core: remove all examples
These examples were great at showing how to use the WebGPU and sysgpu APIs but otherwise had poor code quality, depended on zmath instead of mach.math, etc. and so are being removed. Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
parent
2385b93a2b
commit
e6cecc3ebe
113 changed files with 0 additions and 15104 deletions
|
|
@ -1,26 +0,0 @@
|
||||||
Copyright 2019 WebGPU Samples Contributors
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
3. Neither the name of the copyright holder nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from this
|
|
||||||
software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
@ -1,265 +0,0 @@
|
||||||
/// A port of Austin Eng's "computeBoids" webgpu sample.
|
|
||||||
/// https://github.com/austinEng/webgpu-samples/blob/main/src/sample/computeBoids/main.ts
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
title_timer: core.Timer,
|
|
||||||
timer: core.Timer,
|
|
||||||
compute_pipeline: *gpu.ComputePipeline,
|
|
||||||
render_pipeline: *gpu.RenderPipeline,
|
|
||||||
sprite_vertex_buffer: *gpu.Buffer,
|
|
||||||
particle_buffers: [2]*gpu.Buffer,
|
|
||||||
particle_bind_groups: [2]*gpu.BindGroup,
|
|
||||||
sim_param_buffer: *gpu.Buffer,
|
|
||||||
frame_counter: usize,
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
|
|
||||||
const num_particle = 1500;
|
|
||||||
|
|
||||||
var sim_params = [_]f32{
|
|
||||||
0.04, // .delta_T
|
|
||||||
0.1, // .rule_1_distance
|
|
||||||
0.025, // .rule_2_distance
|
|
||||||
0.025, // .rule_3_distance
|
|
||||||
0.02, // .rule_1_scale
|
|
||||||
0.05, // .rule_2_scale
|
|
||||||
0.005, // .rule_3_scale
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{});
|
|
||||||
|
|
||||||
const sprite_shader_module = core.device.createShaderModuleWGSL(
|
|
||||||
"sprite.wgsl",
|
|
||||||
@embedFile("sprite.wgsl"),
|
|
||||||
);
|
|
||||||
defer sprite_shader_module.release();
|
|
||||||
|
|
||||||
const update_sprite_shader_module = core.device.createShaderModuleWGSL(
|
|
||||||
"updateSprites.wgsl",
|
|
||||||
@embedFile("updateSprites.wgsl"),
|
|
||||||
);
|
|
||||||
defer update_sprite_shader_module.release();
|
|
||||||
|
|
||||||
const instanced_particles_attributes = [_]gpu.VertexAttribute{
|
|
||||||
.{
|
|
||||||
// instance position
|
|
||||||
.shader_location = 0,
|
|
||||||
.offset = 0,
|
|
||||||
.format = .float32x2,
|
|
||||||
},
|
|
||||||
.{
|
|
||||||
// instance velocity
|
|
||||||
.shader_location = 1,
|
|
||||||
.offset = 2 * 4,
|
|
||||||
.format = .float32x2,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const vertex_buffer_attributes = [_]gpu.VertexAttribute{
|
|
||||||
.{
|
|
||||||
// vertex positions
|
|
||||||
.shader_location = 2,
|
|
||||||
.offset = 0,
|
|
||||||
.format = .float32x2,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const render_pipeline = core.device.createRenderPipeline(&gpu.RenderPipeline.Descriptor{
|
|
||||||
.vertex = gpu.VertexState.init(.{
|
|
||||||
.module = sprite_shader_module,
|
|
||||||
.entry_point = "vert_main",
|
|
||||||
.buffers = &.{
|
|
||||||
gpu.VertexBufferLayout.init(.{
|
|
||||||
// instanced particles buffer
|
|
||||||
.array_stride = 4 * 4,
|
|
||||||
.step_mode = .instance,
|
|
||||||
.attributes = &instanced_particles_attributes,
|
|
||||||
}),
|
|
||||||
gpu.VertexBufferLayout.init(.{
|
|
||||||
// vertex buffer
|
|
||||||
.array_stride = 2 * 4,
|
|
||||||
.step_mode = .vertex,
|
|
||||||
.attributes = &vertex_buffer_attributes,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
.fragment = &gpu.FragmentState.init(.{
|
|
||||||
.module = sprite_shader_module,
|
|
||||||
.entry_point = "frag_main",
|
|
||||||
.targets = &[_]gpu.ColorTargetState{.{
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
}},
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
const compute_pipeline = core.device.createComputePipeline(&gpu.ComputePipeline.Descriptor{ .compute = gpu.ProgrammableStageDescriptor{
|
|
||||||
.module = update_sprite_shader_module,
|
|
||||||
.entry_point = "main",
|
|
||||||
} });
|
|
||||||
|
|
||||||
const vert_buffer_data = [_]f32{
|
|
||||||
-0.01, -0.02, 0.01,
|
|
||||||
-0.02, 0.0, 0.02,
|
|
||||||
};
|
|
||||||
|
|
||||||
const sprite_vertex_buffer = core.device.createBuffer(&gpu.Buffer.Descriptor{
|
|
||||||
.label = "sprite_vertex_buffer",
|
|
||||||
.usage = .{ .vertex = true },
|
|
||||||
.mapped_at_creation = .true,
|
|
||||||
.size = vert_buffer_data.len * @sizeOf(f32),
|
|
||||||
});
|
|
||||||
const vertex_mapped = sprite_vertex_buffer.getMappedRange(f32, 0, vert_buffer_data.len);
|
|
||||||
@memcpy(vertex_mapped.?, vert_buffer_data[0..]);
|
|
||||||
sprite_vertex_buffer.unmap();
|
|
||||||
|
|
||||||
const sim_param_buffer = core.device.createBuffer(&gpu.Buffer.Descriptor{
|
|
||||||
.label = "sim_param_buffer",
|
|
||||||
.usage = .{ .uniform = true, .copy_dst = true },
|
|
||||||
.size = sim_params.len * @sizeOf(f32),
|
|
||||||
});
|
|
||||||
core.queue.writeBuffer(sim_param_buffer, 0, sim_params[0..]);
|
|
||||||
|
|
||||||
var initial_particle_data: [num_particle * 4]f32 = undefined;
|
|
||||||
var rng = std.rand.DefaultPrng.init(0);
|
|
||||||
const random = rng.random();
|
|
||||||
var i: usize = 0;
|
|
||||||
while (i < num_particle) : (i += 1) {
|
|
||||||
initial_particle_data[4 * i + 0] = 2 * (random.float(f32) - 0.5);
|
|
||||||
initial_particle_data[4 * i + 1] = 2 * (random.float(f32) - 0.5);
|
|
||||||
initial_particle_data[4 * i + 2] = 2 * (random.float(f32) - 0.5) * 0.1;
|
|
||||||
initial_particle_data[4 * i + 3] = 2 * (random.float(f32) - 0.5) * 0.1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var particle_buffers: [2]*gpu.Buffer = undefined;
|
|
||||||
var particle_bind_groups: [2]*gpu.BindGroup = undefined;
|
|
||||||
i = 0;
|
|
||||||
while (i < 2) : (i += 1) {
|
|
||||||
particle_buffers[i] = core.device.createBuffer(&gpu.Buffer.Descriptor{
|
|
||||||
.label = "particle_buffer",
|
|
||||||
.mapped_at_creation = .true,
|
|
||||||
.usage = .{
|
|
||||||
.vertex = true,
|
|
||||||
.storage = true,
|
|
||||||
},
|
|
||||||
.size = initial_particle_data.len * @sizeOf(f32),
|
|
||||||
});
|
|
||||||
const mapped = particle_buffers[i].getMappedRange(f32, 0, initial_particle_data.len);
|
|
||||||
@memcpy(mapped.?, initial_particle_data[0..]);
|
|
||||||
particle_buffers[i].unmap();
|
|
||||||
}
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
while (i < 2) : (i += 1) {
|
|
||||||
const layout = compute_pipeline.getBindGroupLayout(0);
|
|
||||||
defer layout.release();
|
|
||||||
|
|
||||||
particle_bind_groups[i] = core.device.createBindGroup(&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = layout,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.buffer(0, sim_param_buffer, 0, sim_params.len * @sizeOf(f32)),
|
|
||||||
gpu.BindGroup.Entry.buffer(1, particle_buffers[i], 0, initial_particle_data.len * @sizeOf(f32)),
|
|
||||||
gpu.BindGroup.Entry.buffer(2, particle_buffers[(i + 1) % 2], 0, initial_particle_data.len * @sizeOf(f32)),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
app.* = .{
|
|
||||||
.timer = try core.Timer.start(),
|
|
||||||
.title_timer = try core.Timer.start(),
|
|
||||||
.compute_pipeline = compute_pipeline,
|
|
||||||
.render_pipeline = render_pipeline,
|
|
||||||
.sprite_vertex_buffer = sprite_vertex_buffer,
|
|
||||||
.particle_buffers = particle_buffers,
|
|
||||||
.particle_bind_groups = particle_bind_groups,
|
|
||||||
.sim_param_buffer = sim_param_buffer,
|
|
||||||
.frame_counter = 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
defer _ = gpa.deinit();
|
|
||||||
defer core.deinit();
|
|
||||||
|
|
||||||
app.compute_pipeline.release();
|
|
||||||
app.render_pipeline.release();
|
|
||||||
app.sprite_vertex_buffer.release();
|
|
||||||
for (app.particle_buffers) |particle_buffer| particle_buffer.release();
|
|
||||||
for (app.particle_bind_groups) |particle_bind_group| particle_bind_group.release();
|
|
||||||
app.sim_param_buffer.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(app: *App) !bool {
|
|
||||||
const delta_time = app.timer.lap();
|
|
||||||
|
|
||||||
var iter = core.pollEvents();
|
|
||||||
while (iter.next()) |event| {
|
|
||||||
if (event == .close) return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = back_buffer_view,
|
|
||||||
.clear_value = std.mem.zeroes(gpu.Color),
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
|
|
||||||
const render_pass_descriptor = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{
|
|
||||||
color_attachment,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
sim_params[0] = @as(f32, @floatCast(delta_time));
|
|
||||||
core.queue.writeBuffer(app.sim_param_buffer, 0, sim_params[0..]);
|
|
||||||
|
|
||||||
const command_encoder = core.device.createCommandEncoder(null);
|
|
||||||
{
|
|
||||||
const pass_encoder = command_encoder.beginComputePass(null);
|
|
||||||
pass_encoder.setPipeline(app.compute_pipeline);
|
|
||||||
pass_encoder.setBindGroup(0, app.particle_bind_groups[app.frame_counter % 2], null);
|
|
||||||
pass_encoder.dispatchWorkgroups(@as(u32, @intFromFloat(@ceil(@as(f32, num_particle) / 64))), 1, 1);
|
|
||||||
pass_encoder.end();
|
|
||||||
pass_encoder.release();
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const pass_encoder = command_encoder.beginRenderPass(&render_pass_descriptor);
|
|
||||||
pass_encoder.setPipeline(app.render_pipeline);
|
|
||||||
pass_encoder.setVertexBuffer(0, app.particle_buffers[(app.frame_counter + 1) % 2], 0, num_particle * 4 * @sizeOf(f32));
|
|
||||||
pass_encoder.setVertexBuffer(1, app.sprite_vertex_buffer, 0, 6 * @sizeOf(f32));
|
|
||||||
pass_encoder.draw(3, num_particle, 0, 0);
|
|
||||||
pass_encoder.end();
|
|
||||||
pass_encoder.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
app.frame_counter += 1;
|
|
||||||
if (app.frame_counter % 60 == 0) {
|
|
||||||
std.log.info("Frame {}", .{app.frame_counter});
|
|
||||||
}
|
|
||||||
|
|
||||||
var command = command_encoder.finish(null);
|
|
||||||
command_encoder.release();
|
|
||||||
core.queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
command.release();
|
|
||||||
|
|
||||||
core.swap_chain.present();
|
|
||||||
back_buffer_view.release();
|
|
||||||
|
|
||||||
// update the window title every second
|
|
||||||
if (app.title_timer.read() >= 1.0) {
|
|
||||||
app.title_timer.reset();
|
|
||||||
try core.printTitle("Boids [ {d}fps ] [ Input {d}hz ]", .{
|
|
||||||
core.frameRate(),
|
|
||||||
core.inputRate(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
@vertex
|
|
||||||
fn vert_main(@location(0) a_particlePos : vec2<f32>,
|
|
||||||
@location(1) a_particleVel : vec2<f32>,
|
|
||||||
@location(2) a_pos : vec2<f32>) -> @builtin(position) vec4<f32> {
|
|
||||||
let angle = -atan2(a_particleVel.x, a_particleVel.y);
|
|
||||||
let pos = vec2<f32>(
|
|
||||||
(a_pos.x * cos(angle)) - (a_pos.y * sin(angle)),
|
|
||||||
(a_pos.x * sin(angle)) + (a_pos.y * cos(angle)));
|
|
||||||
return vec4<f32>(pos + a_particlePos, 0.0, 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@fragment
|
|
||||||
fn frag_main() -> @location(0) vec4<f32> {
|
|
||||||
return vec4<f32>(1.0, 1.0, 1.0, 1.0);
|
|
||||||
}
|
|
||||||
|
|
@ -1,90 +0,0 @@
|
||||||
struct Particle {
|
|
||||||
pos : vec2<f32>,
|
|
||||||
vel : vec2<f32>,
|
|
||||||
};
|
|
||||||
struct SimParams {
|
|
||||||
deltaT : f32,
|
|
||||||
rule1Distance : f32,
|
|
||||||
rule2Distance : f32,
|
|
||||||
rule3Distance : f32,
|
|
||||||
rule1Scale : f32,
|
|
||||||
rule2Scale : f32,
|
|
||||||
rule3Scale : f32,
|
|
||||||
};
|
|
||||||
struct Particles {
|
|
||||||
particles : array<Particle>,
|
|
||||||
};
|
|
||||||
@binding(0) @group(0) var<uniform> params : SimParams;
|
|
||||||
@binding(1) @group(0) var<storage, read> particlesA : Particles;
|
|
||||||
@binding(2) @group(0) var<storage, read_write> particlesB : Particles;
|
|
||||||
|
|
||||||
// https://github.com/austinEng/Project6-Vulkan-Flocking/blob/master/data/shaders/computeparticles/particle.comp
|
|
||||||
@compute @workgroup_size(64)
|
|
||||||
fn main(@builtin(global_invocation_id) GlobalInvocationID : vec3<u32>) {
|
|
||||||
var index : u32 = GlobalInvocationID.x;
|
|
||||||
|
|
||||||
if (index >= arrayLength(&particlesA.particles)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var vPos = particlesA.particles[index].pos;
|
|
||||||
var vVel = particlesA.particles[index].vel;
|
|
||||||
var cMass = vec2<f32>(0.0, 0.0);
|
|
||||||
var cVel = vec2<f32>(0.0, 0.0);
|
|
||||||
var colVel = vec2<f32>(0.0, 0.0);
|
|
||||||
var cMassCount : u32 = 0u;
|
|
||||||
var cVelCount : u32 = 0u;
|
|
||||||
var pos : vec2<f32>;
|
|
||||||
var vel : vec2<f32>;
|
|
||||||
|
|
||||||
for (var i : u32 = 0u; i < arrayLength(&particlesA.particles); i = i + 1u) {
|
|
||||||
if (i == index) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
pos = particlesA.particles[i].pos.xy;
|
|
||||||
vel = particlesA.particles[i].vel.xy;
|
|
||||||
if (distance(pos, vPos) < params.rule1Distance) {
|
|
||||||
cMass = cMass + pos;
|
|
||||||
cMassCount = cMassCount + 1u;
|
|
||||||
}
|
|
||||||
if (distance(pos, vPos) < params.rule2Distance) {
|
|
||||||
colVel = colVel - (pos - vPos);
|
|
||||||
}
|
|
||||||
if (distance(pos, vPos) < params.rule3Distance) {
|
|
||||||
cVel = cVel + vel;
|
|
||||||
cVelCount = cVelCount + 1u;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (cMassCount > 0u) {
|
|
||||||
var temp = f32(cMassCount);
|
|
||||||
cMass = (cMass / vec2<f32>(temp, temp)) - vPos;
|
|
||||||
}
|
|
||||||
if (cVelCount > 0u) {
|
|
||||||
var temp = f32(cVelCount);
|
|
||||||
cVel = cVel / vec2<f32>(temp, temp);
|
|
||||||
}
|
|
||||||
vVel = vVel + (cMass * params.rule1Scale) + (colVel * params.rule2Scale) +
|
|
||||||
(cVel * params.rule3Scale);
|
|
||||||
|
|
||||||
// clamp velocity for a more pleasing simulation
|
|
||||||
vVel = normalize(vVel) * clamp(length(vVel), 0.0, 0.1);
|
|
||||||
// kinematic update
|
|
||||||
vPos = vPos + (vVel * params.deltaT);
|
|
||||||
// Wrap around boundary
|
|
||||||
if (vPos.x < -1.0) {
|
|
||||||
vPos.x = 1.0;
|
|
||||||
}
|
|
||||||
if (vPos.x > 1.0) {
|
|
||||||
vPos.x = -1.0;
|
|
||||||
}
|
|
||||||
if (vPos.y < -1.0) {
|
|
||||||
vPos.y = 1.0;
|
|
||||||
}
|
|
||||||
if (vPos.y > 1.0) {
|
|
||||||
vPos.y = -1.0;
|
|
||||||
}
|
|
||||||
// Write back
|
|
||||||
particlesB.particles[index].pos = vPos;
|
|
||||||
particlesB.particles[index].vel = vVel;
|
|
||||||
}
|
|
||||||
|
|
@ -1,81 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
const renderer = @import("renderer.zig");
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
|
|
||||||
title_timer: core.Timer,
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{});
|
|
||||||
app.* = .{
|
|
||||||
.title_timer = try core.Timer.start(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
_ = app;
|
|
||||||
defer _ = gpa.deinit();
|
|
||||||
defer core.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(app: *App) !bool {
|
|
||||||
var iter = core.pollEvents();
|
|
||||||
while (iter.next()) |event| {
|
|
||||||
switch (event) {
|
|
||||||
.key_press => |ev| {
|
|
||||||
if (ev.key == .space) return true;
|
|
||||||
},
|
|
||||||
.close => return true,
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
app.render();
|
|
||||||
|
|
||||||
// update the window title every second
|
|
||||||
if (app.title_timer.read() >= 1.0) {
|
|
||||||
app.title_timer.reset();
|
|
||||||
try core.printTitle("Clear Color [ {d}fps ] [ Input {d}hz ]", .{
|
|
||||||
core.frameRate(),
|
|
||||||
core.inputRate(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(app: *App) void {
|
|
||||||
_ = app;
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = back_buffer_view,
|
|
||||||
.clear_value = gpu.Color{ .r = 0.0, .g = 0.0, .b = 1.0, .a = 1.0 },
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
const render_pass_info = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{color_attachment},
|
|
||||||
});
|
|
||||||
|
|
||||||
const pass = encoder.beginRenderPass(&render_pass_info);
|
|
||||||
pass.end();
|
|
||||||
pass.release();
|
|
||||||
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
encoder.release();
|
|
||||||
|
|
||||||
var queue = core.queue;
|
|
||||||
queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
command.release();
|
|
||||||
core.swap_chain.present();
|
|
||||||
back_buffer_view.release();
|
|
||||||
}
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
pub const Renderer = @This();
|
|
||||||
|
|
||||||
pub fn RenderUpdate() void {
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = back_buffer_view,
|
|
||||||
.clear_value = gpu.Color{ .r = 0.0, .g = 0.0, .b = 1.0, .a = 1.0 },
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
const render_pass_info = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{color_attachment},
|
|
||||||
});
|
|
||||||
|
|
||||||
const pass = encoder.beginRenderPass(&render_pass_info);
|
|
||||||
pass.end();
|
|
||||||
pass.release();
|
|
||||||
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
encoder.release();
|
|
||||||
|
|
||||||
const queue = core.queue;
|
|
||||||
queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
command.release();
|
|
||||||
core.swap_chain.present();
|
|
||||||
back_buffer_view.release();
|
|
||||||
}
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
pub const Vertex = extern struct {
|
|
||||||
pos: @Vector(4, f32),
|
|
||||||
col: @Vector(4, f32),
|
|
||||||
uv: @Vector(2, f32),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const vertices = [_]Vertex{
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
};
|
|
||||||
|
|
@ -1,395 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
const zm = @import("zmath");
|
|
||||||
const zigimg = @import("zigimg");
|
|
||||||
const Vertex = @import("cube_mesh.zig").Vertex;
|
|
||||||
const vertices = @import("cube_mesh.zig").vertices;
|
|
||||||
const assets = @import("assets");
|
|
||||||
|
|
||||||
const UniformBufferObject = struct {
|
|
||||||
mat: zm.Mat,
|
|
||||||
};
|
|
||||||
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
|
|
||||||
title_timer: core.Timer,
|
|
||||||
timer: core.Timer,
|
|
||||||
pipeline: *gpu.RenderPipeline,
|
|
||||||
vertex_buffer: *gpu.Buffer,
|
|
||||||
uniform_buffer: *gpu.Buffer,
|
|
||||||
bind_group: *gpu.BindGroup,
|
|
||||||
depth_texture: *gpu.Texture,
|
|
||||||
depth_texture_view: *gpu.TextureView,
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{});
|
|
||||||
|
|
||||||
const allocator = gpa.allocator();
|
|
||||||
|
|
||||||
const shader_module = core.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.wgsl"));
|
|
||||||
defer shader_module.release();
|
|
||||||
|
|
||||||
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.init(.{
|
|
||||||
.array_stride = @sizeOf(Vertex),
|
|
||||||
.step_mode = .vertex,
|
|
||||||
.attributes = &vertex_attributes,
|
|
||||||
});
|
|
||||||
|
|
||||||
const blend = gpu.BlendState{
|
|
||||||
.color = .{
|
|
||||||
.operation = .add,
|
|
||||||
.src_factor = .src_alpha,
|
|
||||||
.dst_factor = .one_minus_src_alpha,
|
|
||||||
},
|
|
||||||
.alpha = .{
|
|
||||||
.operation = .add,
|
|
||||||
.src_factor = .one,
|
|
||||||
.dst_factor = .zero,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
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{
|
|
||||||
.fragment = &fragment,
|
|
||||||
// Enable depth testing so that the fragment closest to the camera
|
|
||||||
// is rendered in front.
|
|
||||||
.depth_stencil = &.{
|
|
||||||
.format = .depth24_plus,
|
|
||||||
.depth_write_enabled = .true,
|
|
||||||
.depth_compare = .less,
|
|
||||||
},
|
|
||||||
.vertex = gpu.VertexState.init(.{
|
|
||||||
.module = shader_module,
|
|
||||||
.entry_point = "vertex_main",
|
|
||||||
.buffers = &.{vertex_buffer_layout},
|
|
||||||
}),
|
|
||||||
.primitive = .{
|
|
||||||
// Since the cube has its face pointing outwards, cull_mode must be
|
|
||||||
// set to .front or .none here since we are inside the cube looking out.
|
|
||||||
// Ideally you would set this to .back and have a custom cube primitive
|
|
||||||
// with the faces pointing towards the inside of the cube.
|
|
||||||
.cull_mode = .none,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const pipeline = core.device.createRenderPipeline(&pipeline_descriptor);
|
|
||||||
|
|
||||||
const vertex_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .vertex = true },
|
|
||||||
.size = @sizeOf(Vertex) * vertices.len,
|
|
||||||
.mapped_at_creation = .true,
|
|
||||||
});
|
|
||||||
const vertex_mapped = vertex_buffer.getMappedRange(Vertex, 0, vertices.len);
|
|
||||||
@memcpy(vertex_mapped.?, vertices[0..]);
|
|
||||||
vertex_buffer.unmap();
|
|
||||||
|
|
||||||
const uniform_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .copy_dst = true, .uniform = true },
|
|
||||||
.size = @sizeOf(UniformBufferObject),
|
|
||||||
.mapped_at_creation = .false,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create a sampler with linear filtering for smooth interpolation.
|
|
||||||
const sampler = core.device.createSampler(&.{
|
|
||||||
.mag_filter = .linear,
|
|
||||||
.min_filter = .linear,
|
|
||||||
});
|
|
||||||
|
|
||||||
const queue = core.queue;
|
|
||||||
|
|
||||||
// WebGPU expects the cubemap textures in this order: (+X,-X,+Y,-Y,+Z,-Z)
|
|
||||||
var images: [6]zigimg.Image = undefined;
|
|
||||||
images[0] = try zigimg.Image.fromMemory(allocator, assets.skybox_posx_png);
|
|
||||||
defer images[0].deinit();
|
|
||||||
images[1] = try zigimg.Image.fromMemory(allocator, assets.skybox_negx_png);
|
|
||||||
defer images[1].deinit();
|
|
||||||
images[2] = try zigimg.Image.fromMemory(allocator, assets.skybox_posy_png);
|
|
||||||
defer images[2].deinit();
|
|
||||||
images[3] = try zigimg.Image.fromMemory(allocator, assets.skybox_negy_png);
|
|
||||||
defer images[3].deinit();
|
|
||||||
images[4] = try zigimg.Image.fromMemory(allocator, assets.skybox_posz_png);
|
|
||||||
defer images[4].deinit();
|
|
||||||
images[5] = try zigimg.Image.fromMemory(allocator, assets.skybox_negz_png);
|
|
||||||
defer images[5].deinit();
|
|
||||||
|
|
||||||
// Use the first image of the set for sizing
|
|
||||||
const img_size = gpu.Extent3D{
|
|
||||||
.width = @as(u32, @intCast(images[0].width)),
|
|
||||||
.height = @as(u32, @intCast(images[0].height)),
|
|
||||||
};
|
|
||||||
|
|
||||||
// We set depth_or_array_layers to 6 here to indicate there are 6 images in this texture
|
|
||||||
const tex_size = gpu.Extent3D{
|
|
||||||
.width = @as(u32, @intCast(images[0].width)),
|
|
||||||
.height = @as(u32, @intCast(images[0].height)),
|
|
||||||
.depth_or_array_layers = 6,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Same as a regular texture, but with a Z of 6 (defined in tex_size)
|
|
||||||
const cube_texture = core.device.createTexture(&.{
|
|
||||||
.size = tex_size,
|
|
||||||
.format = .rgba8_unorm,
|
|
||||||
.dimension = .dimension_2d,
|
|
||||||
.usage = .{
|
|
||||||
.texture_binding = true,
|
|
||||||
.copy_dst = true,
|
|
||||||
.render_attachment = false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const data_layout = gpu.Texture.DataLayout{
|
|
||||||
.bytes_per_row = @as(u32, @intCast(images[0].width * 4)),
|
|
||||||
.rows_per_image = @as(u32, @intCast(images[0].height)),
|
|
||||||
};
|
|
||||||
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
|
|
||||||
// We have to create a staging buffer, copy all the image data into the
|
|
||||||
// staging buffer at the correct Z offset, encode a command to copy
|
|
||||||
// the buffer to the texture for each image, then push it to the command
|
|
||||||
// queue
|
|
||||||
var staging_buff: [6]*gpu.Buffer = undefined;
|
|
||||||
var i: u32 = 0;
|
|
||||||
while (i < 6) : (i += 1) {
|
|
||||||
staging_buff[i] = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .copy_src = true, .map_write = true },
|
|
||||||
.size = @as(u64, @intCast(images[0].width)) * @as(u64, @intCast(images[0].height)) * @sizeOf(u32),
|
|
||||||
.mapped_at_creation = .true,
|
|
||||||
});
|
|
||||||
switch (images[i].pixels) {
|
|
||||||
.rgba32 => |pixels| {
|
|
||||||
// Map a section of the staging buffer
|
|
||||||
const staging_map = staging_buff[i].getMappedRange(u32, 0, @as(u64, @intCast(images[i].width)) * @as(u64, @intCast(images[i].height)));
|
|
||||||
// Copy the image data into the mapped buffer
|
|
||||||
@memcpy(staging_map.?, @as([]u32, @ptrCast(@alignCast(pixels))));
|
|
||||||
// And release the mapping
|
|
||||||
staging_buff[i].unmap();
|
|
||||||
},
|
|
||||||
.rgb24 => |pixels| {
|
|
||||||
const staging_map = staging_buff[i].getMappedRange(u32, 0, @as(u64, @intCast(images[i].width)) * @as(u64, @intCast(images[i].height)));
|
|
||||||
// In this case, we have to convert the data to rgba32 first
|
|
||||||
const data = try rgb24ToRgba32(allocator, pixels);
|
|
||||||
defer data.deinit(allocator);
|
|
||||||
@memcpy(staging_map.?, @as([]u32, @ptrCast(@alignCast(data.rgba32))));
|
|
||||||
staging_buff[i].unmap();
|
|
||||||
},
|
|
||||||
else => @panic("unsupported image color format"),
|
|
||||||
}
|
|
||||||
|
|
||||||
// These define the source and target for the buffer to texture copy command
|
|
||||||
const copy_buff = gpu.ImageCopyBuffer{
|
|
||||||
.layout = data_layout,
|
|
||||||
.buffer = staging_buff[i],
|
|
||||||
};
|
|
||||||
const copy_tex = gpu.ImageCopyTexture{
|
|
||||||
.texture = cube_texture,
|
|
||||||
.origin = gpu.Origin3D{ .x = 0, .y = 0, .z = i },
|
|
||||||
};
|
|
||||||
|
|
||||||
// Encode the copy command, we do this for every image in the texture.
|
|
||||||
encoder.copyBufferToTexture(©_buff, ©_tex, &img_size);
|
|
||||||
staging_buff[i].release();
|
|
||||||
}
|
|
||||||
// Now that the commands to copy our buffer data to the texture is filled,
|
|
||||||
// push the encoded commands over to the queue and execute to get the
|
|
||||||
// texture filled with the image data.
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
encoder.release();
|
|
||||||
queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
command.release();
|
|
||||||
|
|
||||||
// The textureView in the bind group needs dimension defined as "dimension_cube".
|
|
||||||
const cube_texture_view = cube_texture.createView(&gpu.TextureView.Descriptor{ .dimension = .dimension_cube });
|
|
||||||
cube_texture.release();
|
|
||||||
|
|
||||||
const bind_group_layout = pipeline.getBindGroupLayout(0);
|
|
||||||
const bind_group = core.device.createBindGroup(
|
|
||||||
&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = bind_group_layout,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.buffer(0, uniform_buffer, 0, @sizeOf(UniformBufferObject)),
|
|
||||||
gpu.BindGroup.Entry.sampler(1, sampler),
|
|
||||||
gpu.BindGroup.Entry.textureView(2, cube_texture_view),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
sampler.release();
|
|
||||||
cube_texture_view.release();
|
|
||||||
bind_group_layout.release();
|
|
||||||
|
|
||||||
const depth_texture = core.device.createTexture(&gpu.Texture.Descriptor{
|
|
||||||
.size = gpu.Extent3D{
|
|
||||||
.width = core.descriptor.width,
|
|
||||||
.height = core.descriptor.height,
|
|
||||||
},
|
|
||||||
.format = .depth24_plus,
|
|
||||||
.usage = .{
|
|
||||||
.render_attachment = true,
|
|
||||||
.texture_binding = true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const depth_texture_view = depth_texture.createView(&gpu.TextureView.Descriptor{
|
|
||||||
.format = .depth24_plus,
|
|
||||||
.dimension = .dimension_2d,
|
|
||||||
.array_layer_count = 1,
|
|
||||||
.mip_level_count = 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
app.timer = try core.Timer.start();
|
|
||||||
app.title_timer = try core.Timer.start();
|
|
||||||
app.pipeline = pipeline;
|
|
||||||
app.vertex_buffer = vertex_buffer;
|
|
||||||
app.uniform_buffer = uniform_buffer;
|
|
||||||
app.bind_group = bind_group;
|
|
||||||
app.depth_texture = depth_texture;
|
|
||||||
app.depth_texture_view = depth_texture_view;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
defer _ = gpa.deinit();
|
|
||||||
defer core.deinit();
|
|
||||||
|
|
||||||
app.pipeline.release();
|
|
||||||
app.vertex_buffer.release();
|
|
||||||
app.uniform_buffer.release();
|
|
||||||
app.bind_group.release();
|
|
||||||
app.depth_texture.release();
|
|
||||||
app.depth_texture_view.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(app: *App) !bool {
|
|
||||||
var iter = core.pollEvents();
|
|
||||||
while (iter.next()) |event| {
|
|
||||||
switch (event) {
|
|
||||||
.key_press => |ev| {
|
|
||||||
if (ev.key == .space) return true;
|
|
||||||
},
|
|
||||||
.close => return true,
|
|
||||||
.framebuffer_resize => |ev| {
|
|
||||||
// If window is resized, recreate depth buffer otherwise we cannot use it.
|
|
||||||
app.depth_texture.release();
|
|
||||||
app.depth_texture = core.device.createTexture(&gpu.Texture.Descriptor{
|
|
||||||
.size = gpu.Extent3D{
|
|
||||||
.width = ev.width,
|
|
||||||
.height = ev.height,
|
|
||||||
},
|
|
||||||
.format = .depth24_plus,
|
|
||||||
.usage = .{
|
|
||||||
.render_attachment = true,
|
|
||||||
.texture_binding = true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
app.depth_texture_view.release();
|
|
||||||
app.depth_texture_view = app.depth_texture.createView(&gpu.TextureView.Descriptor{
|
|
||||||
.format = .depth24_plus,
|
|
||||||
.dimension = .dimension_2d,
|
|
||||||
.array_layer_count = 1,
|
|
||||||
.mip_level_count = 1,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = back_buffer_view,
|
|
||||||
.clear_value = .{ .r = 0.5, .g = 0.5, .b = 0.5, .a = 0.0 },
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
const render_pass_info = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{color_attachment},
|
|
||||||
.depth_stencil_attachment = &.{
|
|
||||||
.view = app.depth_texture_view,
|
|
||||||
.depth_clear_value = 1.0,
|
|
||||||
.depth_load_op = .clear,
|
|
||||||
.depth_store_op = .store,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
{
|
|
||||||
const time = app.timer.read();
|
|
||||||
const aspect = @as(f32, @floatFromInt(core.descriptor.width)) / @as(f32, @floatFromInt(core.descriptor.height));
|
|
||||||
const proj = zm.perspectiveFovRh((2 * std.math.pi) / 5.0, aspect, 0.1, 3000);
|
|
||||||
const model = zm.mul(
|
|
||||||
zm.scaling(1000, 1000, 1000),
|
|
||||||
zm.rotationX(std.math.pi / 2.0 * 3.0),
|
|
||||||
);
|
|
||||||
const view = zm.mul(
|
|
||||||
zm.mul(
|
|
||||||
zm.lookAtRh(
|
|
||||||
zm.Vec{ 0, 0, 0, 1 },
|
|
||||||
zm.Vec{ 1, 0, 0, 1 },
|
|
||||||
zm.Vec{ 0, 0, 1, 0 },
|
|
||||||
),
|
|
||||||
zm.rotationY(time * 0.2),
|
|
||||||
),
|
|
||||||
zm.rotationX((std.math.pi / 10.0) * std.math.sin(time)),
|
|
||||||
);
|
|
||||||
|
|
||||||
const mvp = zm.mul(zm.mul(zm.transpose(model), view), proj);
|
|
||||||
const ubo = UniformBufferObject{ .mat = mvp };
|
|
||||||
|
|
||||||
encoder.writeBuffer(app.uniform_buffer, 0, &[_]UniformBufferObject{ubo});
|
|
||||||
}
|
|
||||||
|
|
||||||
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_group, &.{});
|
|
||||||
pass.draw(vertices.len, 1, 0, 0);
|
|
||||||
pass.end();
|
|
||||||
pass.release();
|
|
||||||
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
encoder.release();
|
|
||||||
|
|
||||||
const queue = core.queue;
|
|
||||||
queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
command.release();
|
|
||||||
core.swap_chain.present();
|
|
||||||
back_buffer_view.release();
|
|
||||||
|
|
||||||
// update the window title every second
|
|
||||||
if (app.title_timer.read() >= 1.0) {
|
|
||||||
app.title_timer.reset();
|
|
||||||
try core.printTitle("Cube Map [ {d}fps ] [ Input {d}hz ]", .{
|
|
||||||
core.frameRate(),
|
|
||||||
core.inputRate(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rgb24ToRgba32(allocator: std.mem.Allocator, in: []zigimg.color.Rgb24) !zigimg.color.PixelStorage {
|
|
||||||
const out = try zigimg.color.PixelStorage.init(allocator, .rgba32, in.len);
|
|
||||||
var i: usize = 0;
|
|
||||||
while (i < in.len) : (i += 1) {
|
|
||||||
out.rgba32[i] = zigimg.color.Rgba32{ .r = in[i].r, .g = in[i].g, .b = in[i].b, .a = 255 };
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
struct Uniforms {
|
|
||||||
modelViewProjectionMatrix : mat4x4<f32>,
|
|
||||||
}
|
|
||||||
@binding(0) @group(0) var<uniform> uniforms : Uniforms;
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) Position : vec4<f32>,
|
|
||||||
@location(0) fragUV : vec2<f32>,
|
|
||||||
@location(1) fragPosition: vec4<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
@vertex
|
|
||||||
fn vertex_main(
|
|
||||||
@location(0) position : vec4<f32>,
|
|
||||||
@location(1) uv : vec2<f32>
|
|
||||||
) -> VertexOutput {
|
|
||||||
var output : VertexOutput;
|
|
||||||
output.Position = uniforms.modelViewProjectionMatrix * position;
|
|
||||||
output.fragUV = uv;
|
|
||||||
output.fragPosition = 0.5 * (position + vec4<f32>(1.0, 1.0, 1.0, 1.0));
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
@group(0) @binding(1) var mySampler: sampler;
|
|
||||||
@group(0) @binding(2) var myTexture: texture_cube<f32>;
|
|
||||||
|
|
||||||
@fragment
|
|
||||||
fn frag_main(
|
|
||||||
@location(0) fragUV: vec2<f32>,
|
|
||||||
@location(1) fragPosition: vec4<f32>
|
|
||||||
) -> @location(0) vec4<f32> {
|
|
||||||
var cubemapVec = fragPosition.xyz - vec3<f32>(0.5, 0.5, 0.5);
|
|
||||||
return textureSample(myTexture, mySampler, cubemapVec);
|
|
||||||
}
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
pub const Vertex = extern struct {
|
|
||||||
pos: @Vector(4, f32),
|
|
||||||
col: @Vector(4, f32),
|
|
||||||
uv: @Vector(2, f32),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const vertices = [_]Vertex{
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
};
|
|
||||||
|
|
@ -1,374 +0,0 @@
|
||||||
//! To get the effect we want, we need a texture on which to render;
|
|
||||||
//! we can't use the swapchain texture directly, but we can get the effect
|
|
||||||
//! by doing the same render pass twice, on the texture and the swapchain.
|
|
||||||
//! We also need a second texture to use on the cube (after the render pass
|
|
||||||
//! it needs to copy the other texture.) We can't use the same texture since
|
|
||||||
//! it would interfere with the synchronization on the gpu during the render pass.
|
|
||||||
//! This demo currently does not work on opengl, because core.descriptor.width/height,
|
|
||||||
//! are set to 0 after core.init() and because webgpu does not implement copyTextureToTexture,
|
|
||||||
//! for opengl
|
|
||||||
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
const zm = @import("zmath");
|
|
||||||
const Vertex = @import("cube_mesh.zig").Vertex;
|
|
||||||
const vertices = @import("cube_mesh.zig").vertices;
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
const UniformBufferObject = struct {
|
|
||||||
mat: zm.Mat,
|
|
||||||
};
|
|
||||||
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
|
|
||||||
title_timer: core.Timer,
|
|
||||||
timer: core.Timer,
|
|
||||||
pipeline: *gpu.RenderPipeline,
|
|
||||||
vertex_buffer: *gpu.Buffer,
|
|
||||||
uniform_buffer: *gpu.Buffer,
|
|
||||||
bind_group: *gpu.BindGroup,
|
|
||||||
depth_texture: ?*gpu.Texture,
|
|
||||||
depth_texture_view: *gpu.TextureView,
|
|
||||||
cube_texture: *gpu.Texture,
|
|
||||||
cube_texture_view: *gpu.TextureView,
|
|
||||||
cube_texture_render: *gpu.Texture,
|
|
||||||
cube_texture_view_render: *gpu.TextureView,
|
|
||||||
sampler: *gpu.Sampler,
|
|
||||||
bgl: *gpu.BindGroupLayout,
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{});
|
|
||||||
|
|
||||||
const shader_module = core.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.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.init(.{
|
|
||||||
.array_stride = @sizeOf(Vertex),
|
|
||||||
.attributes = &vertex_attributes,
|
|
||||||
});
|
|
||||||
|
|
||||||
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 bgle_buffer = gpu.BindGroupLayout.Entry.buffer(0, .{ .vertex = true }, .uniform, true, 0);
|
|
||||||
const bgle_sampler = gpu.BindGroupLayout.Entry.sampler(1, .{ .fragment = true }, .filtering);
|
|
||||||
const bgle_textureview = gpu.BindGroupLayout.Entry.texture(2, .{ .fragment = true }, .float, .dimension_2d, false);
|
|
||||||
const bgl = core.device.createBindGroupLayout(
|
|
||||||
&gpu.BindGroupLayout.Descriptor.init(.{
|
|
||||||
.entries = &.{ bgle_buffer, bgle_sampler, bgle_textureview },
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
const bind_group_layouts = [_]*gpu.BindGroupLayout{bgl};
|
|
||||||
const pipeline_layout = core.device.createPipelineLayout(&gpu.PipelineLayout.Descriptor.init(.{
|
|
||||||
.bind_group_layouts = &bind_group_layouts,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const pipeline_descriptor = gpu.RenderPipeline.Descriptor{
|
|
||||||
.fragment = &fragment,
|
|
||||||
.layout = pipeline_layout,
|
|
||||||
.depth_stencil = &.{
|
|
||||||
.format = .depth24_plus,
|
|
||||||
.depth_write_enabled = .true,
|
|
||||||
.depth_compare = .less,
|
|
||||||
},
|
|
||||||
.vertex = gpu.VertexState.init(.{
|
|
||||||
.module = shader_module,
|
|
||||||
.entry_point = "vertex_main",
|
|
||||||
.buffers = &.{vertex_buffer_layout},
|
|
||||||
}),
|
|
||||||
.primitive = .{
|
|
||||||
.cull_mode = .back,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const vertex_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .vertex = true },
|
|
||||||
.size = @sizeOf(Vertex) * vertices.len,
|
|
||||||
.mapped_at_creation = .true,
|
|
||||||
});
|
|
||||||
const vertex_mapped = vertex_buffer.getMappedRange(Vertex, 0, vertices.len);
|
|
||||||
@memcpy(vertex_mapped.?, vertices[0..]);
|
|
||||||
vertex_buffer.unmap();
|
|
||||||
|
|
||||||
const uniform_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .copy_dst = true, .uniform = true },
|
|
||||||
.size = @sizeOf(UniformBufferObject),
|
|
||||||
.mapped_at_creation = .false,
|
|
||||||
});
|
|
||||||
|
|
||||||
// The texture to put on the cube
|
|
||||||
const cube_texture = core.device.createTexture(&gpu.Texture.Descriptor{
|
|
||||||
.usage = .{ .texture_binding = true, .copy_dst = true },
|
|
||||||
.size = .{ .width = core.descriptor.width, .height = core.descriptor.height },
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
});
|
|
||||||
// The texture on which we render
|
|
||||||
const cube_texture_render = core.device.createTexture(&gpu.Texture.Descriptor{
|
|
||||||
.usage = .{ .render_attachment = true, .copy_src = true },
|
|
||||||
.size = .{ .width = core.descriptor.width, .height = core.descriptor.height },
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
});
|
|
||||||
|
|
||||||
const sampler = core.device.createSampler(&gpu.Sampler.Descriptor{
|
|
||||||
.mag_filter = .linear,
|
|
||||||
.min_filter = .linear,
|
|
||||||
});
|
|
||||||
|
|
||||||
const cube_texture_view = cube_texture.createView(&gpu.TextureView.Descriptor{
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
.dimension = .dimension_2d,
|
|
||||||
.mip_level_count = 1,
|
|
||||||
.array_layer_count = 1,
|
|
||||||
});
|
|
||||||
const cube_texture_view_render = cube_texture_render.createView(&gpu.TextureView.Descriptor{
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
.dimension = .dimension_2d,
|
|
||||||
.mip_level_count = 1,
|
|
||||||
.array_layer_count = 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
const bind_group = core.device.createBindGroup(
|
|
||||||
&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = bgl,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.buffer(0, uniform_buffer, 0, @sizeOf(UniformBufferObject)),
|
|
||||||
gpu.BindGroup.Entry.sampler(1, sampler),
|
|
||||||
gpu.BindGroup.Entry.textureView(2, cube_texture_view),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
const depth_texture = core.device.createTexture(&gpu.Texture.Descriptor{
|
|
||||||
.usage = .{ .render_attachment = true },
|
|
||||||
.size = .{ .width = core.descriptor.width, .height = core.descriptor.height },
|
|
||||||
.format = .depth24_plus,
|
|
||||||
});
|
|
||||||
const depth_texture_view = depth_texture.createView(&gpu.TextureView.Descriptor{
|
|
||||||
.format = .depth24_plus,
|
|
||||||
.dimension = .dimension_2d,
|
|
||||||
.array_layer_count = 1,
|
|
||||||
.mip_level_count = 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
app.timer = try core.Timer.start();
|
|
||||||
app.title_timer = try core.Timer.start();
|
|
||||||
app.pipeline = core.device.createRenderPipeline(&pipeline_descriptor);
|
|
||||||
app.vertex_buffer = vertex_buffer;
|
|
||||||
app.uniform_buffer = uniform_buffer;
|
|
||||||
app.bind_group = bind_group;
|
|
||||||
app.depth_texture = depth_texture;
|
|
||||||
app.depth_texture_view = depth_texture_view;
|
|
||||||
app.cube_texture = cube_texture;
|
|
||||||
app.cube_texture_view = cube_texture_view;
|
|
||||||
app.cube_texture_render = cube_texture_render;
|
|
||||||
app.cube_texture_view_render = cube_texture_view_render;
|
|
||||||
app.sampler = sampler;
|
|
||||||
app.bgl = bgl;
|
|
||||||
|
|
||||||
shader_module.release();
|
|
||||||
pipeline_layout.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
defer _ = gpa.deinit();
|
|
||||||
defer core.deinit();
|
|
||||||
|
|
||||||
app.pipeline.release();
|
|
||||||
app.bgl.release();
|
|
||||||
app.vertex_buffer.release();
|
|
||||||
app.uniform_buffer.release();
|
|
||||||
app.cube_texture.release();
|
|
||||||
app.cube_texture_render.release();
|
|
||||||
app.sampler.release();
|
|
||||||
app.cube_texture_view.release();
|
|
||||||
app.cube_texture_view_render.release();
|
|
||||||
app.bind_group.release();
|
|
||||||
app.depth_texture.?.release();
|
|
||||||
app.depth_texture_view.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(app: *App) !bool {
|
|
||||||
var iter = core.pollEvents();
|
|
||||||
while (iter.next()) |event| {
|
|
||||||
switch (event) {
|
|
||||||
.key_press => |ev| {
|
|
||||||
if (ev.key == .space) return true;
|
|
||||||
},
|
|
||||||
.close => return true,
|
|
||||||
.framebuffer_resize => |ev| {
|
|
||||||
app.depth_texture.?.release();
|
|
||||||
app.depth_texture = core.device.createTexture(&gpu.Texture.Descriptor{
|
|
||||||
.usage = .{ .render_attachment = true },
|
|
||||||
.size = .{ .width = ev.width, .height = ev.height },
|
|
||||||
.format = .depth24_plus,
|
|
||||||
});
|
|
||||||
|
|
||||||
app.cube_texture.release();
|
|
||||||
app.cube_texture = core.device.createTexture(&gpu.Texture.Descriptor{
|
|
||||||
.usage = .{ .texture_binding = true, .copy_dst = true },
|
|
||||||
.size = .{ .width = ev.width, .height = ev.height },
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
});
|
|
||||||
app.cube_texture_render.release();
|
|
||||||
app.cube_texture_render = core.device.createTexture(&gpu.Texture.Descriptor{
|
|
||||||
.usage = .{ .render_attachment = true, .copy_src = true },
|
|
||||||
.size = .{ .width = ev.width, .height = ev.height },
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
});
|
|
||||||
|
|
||||||
app.depth_texture_view.release();
|
|
||||||
app.depth_texture_view = app.depth_texture.?.createView(&gpu.TextureView.Descriptor{
|
|
||||||
.format = .depth24_plus,
|
|
||||||
.dimension = .dimension_2d,
|
|
||||||
.array_layer_count = 1,
|
|
||||||
.mip_level_count = 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
app.cube_texture_view.release();
|
|
||||||
app.cube_texture_view = app.cube_texture.createView(&gpu.TextureView.Descriptor{
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
.dimension = .dimension_2d,
|
|
||||||
.mip_level_count = 1,
|
|
||||||
.array_layer_count = 1,
|
|
||||||
});
|
|
||||||
app.cube_texture_view_render.release();
|
|
||||||
app.cube_texture_view_render = app.cube_texture_render.createView(&gpu.TextureView.Descriptor{
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
.dimension = .dimension_2d,
|
|
||||||
.mip_level_count = 1,
|
|
||||||
.array_layer_count = 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
app.bind_group.release();
|
|
||||||
app.bind_group = core.device.createBindGroup(
|
|
||||||
&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = app.bgl,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.buffer(0, app.uniform_buffer, 0, @sizeOf(UniformBufferObject)),
|
|
||||||
gpu.BindGroup.Entry.sampler(1, app.sampler),
|
|
||||||
gpu.BindGroup.Entry.textureView(2, app.cube_texture_view),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const cube_view = app.cube_texture_view_render;
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
|
|
||||||
const cube_color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = cube_view,
|
|
||||||
.clear_value = gpu.Color{ .r = 0.5, .g = 0.5, .b = 0.5, .a = 1 },
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = back_buffer_view,
|
|
||||||
.clear_value = gpu.Color{ .r = 0.5, .g = 0.5, .b = 0.5, .a = 1 },
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
|
|
||||||
const depth_stencil_attachment = gpu.RenderPassDepthStencilAttachment{
|
|
||||||
.view = app.depth_texture_view,
|
|
||||||
.depth_load_op = .clear,
|
|
||||||
.depth_store_op = .store,
|
|
||||||
.depth_clear_value = 1.0,
|
|
||||||
};
|
|
||||||
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
const cube_render_pass_info = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{cube_color_attachment},
|
|
||||||
.depth_stencil_attachment = &depth_stencil_attachment,
|
|
||||||
});
|
|
||||||
const render_pass_info = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{color_attachment},
|
|
||||||
.depth_stencil_attachment = &depth_stencil_attachment,
|
|
||||||
});
|
|
||||||
|
|
||||||
{
|
|
||||||
const time = app.timer.read();
|
|
||||||
const model = zm.mul(zm.rotationX(time * (std.math.pi / 2.0)), zm.rotationZ(time * (std.math.pi / 2.0)));
|
|
||||||
const view = zm.lookAtRh(
|
|
||||||
zm.Vec{ 0, -4, 0, 1 },
|
|
||||||
zm.Vec{ 0, 0, 0, 1 },
|
|
||||||
zm.Vec{ 0, 0, 1, 0 },
|
|
||||||
);
|
|
||||||
const proj = zm.perspectiveFovRh(
|
|
||||||
(std.math.pi * 2.0 / 5.0),
|
|
||||||
@as(f32, @floatFromInt(core.descriptor.width)) / @as(f32, @floatFromInt(core.descriptor.height)),
|
|
||||||
1,
|
|
||||||
100,
|
|
||||||
);
|
|
||||||
const ubo = UniformBufferObject{
|
|
||||||
.mat = zm.transpose(zm.mul(zm.mul(model, view), proj)),
|
|
||||||
};
|
|
||||||
encoder.writeBuffer(app.uniform_buffer, 0, &[_]UniformBufferObject{ubo});
|
|
||||||
}
|
|
||||||
|
|
||||||
const pass = encoder.beginRenderPass(&render_pass_info);
|
|
||||||
pass.setPipeline(app.pipeline);
|
|
||||||
pass.setBindGroup(0, app.bind_group, &.{0});
|
|
||||||
pass.setVertexBuffer(0, app.vertex_buffer, 0, @sizeOf(Vertex) * vertices.len);
|
|
||||||
pass.draw(vertices.len, 1, 0, 0);
|
|
||||||
pass.end();
|
|
||||||
pass.release();
|
|
||||||
|
|
||||||
encoder.copyTextureToTexture(
|
|
||||||
&gpu.ImageCopyTexture{
|
|
||||||
.texture = app.cube_texture_render,
|
|
||||||
},
|
|
||||||
&gpu.ImageCopyTexture{
|
|
||||||
.texture = app.cube_texture,
|
|
||||||
},
|
|
||||||
&.{ .width = core.descriptor.width, .height = core.descriptor.height },
|
|
||||||
);
|
|
||||||
|
|
||||||
const cube_pass = encoder.beginRenderPass(&cube_render_pass_info);
|
|
||||||
cube_pass.setPipeline(app.pipeline);
|
|
||||||
cube_pass.setBindGroup(0, app.bind_group, &.{0});
|
|
||||||
cube_pass.setVertexBuffer(0, app.vertex_buffer, 0, @sizeOf(Vertex) * vertices.len);
|
|
||||||
cube_pass.draw(vertices.len, 1, 0, 0);
|
|
||||||
cube_pass.end();
|
|
||||||
cube_pass.release();
|
|
||||||
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
encoder.release();
|
|
||||||
|
|
||||||
const queue = core.queue;
|
|
||||||
queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
command.release();
|
|
||||||
core.swap_chain.present();
|
|
||||||
back_buffer_view.release();
|
|
||||||
|
|
||||||
// update the window title every second
|
|
||||||
if (app.title_timer.read() >= 1.0) {
|
|
||||||
app.title_timer.reset();
|
|
||||||
try core.printTitle("Fractal Cube [ {d}fps ] [ Input {d}hz ]", .{
|
|
||||||
core.frameRate(),
|
|
||||||
core.inputRate(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
struct Uniforms {
|
|
||||||
matrix : mat4x4<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
@binding(0) @group(0) var<uniform> ubo : Uniforms;
|
|
||||||
|
|
||||||
struct VertexOut {
|
|
||||||
@builtin(position) Position : vec4<f32>,
|
|
||||||
@location(0) fragUV : vec2<f32>,
|
|
||||||
@location(1) fragPosition: vec4<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
@vertex fn vertex_main(
|
|
||||||
@location(0) position : vec4<f32>,
|
|
||||||
@location(1) uv: vec2<f32>
|
|
||||||
) -> VertexOut {
|
|
||||||
var output : VertexOut;
|
|
||||||
output.Position = position * ubo.matrix;
|
|
||||||
output.fragUV = uv;
|
|
||||||
output.fragPosition = 0.5 * (position + vec4<f32>(1.0, 1.0, 1.0, 1.0));
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
@binding(1) @group(0) var mySampler: sampler;
|
|
||||||
@binding(2) @group(0) var myTexture: texture_2d<f32>;
|
|
||||||
|
|
||||||
@fragment fn frag_main(
|
|
||||||
@location(0) fragUV: vec2<f32>,
|
|
||||||
@location(1) fragPosition: vec4<f32>
|
|
||||||
) -> @location(0) vec4<f32> {
|
|
||||||
let texColor = textureSample(myTexture, mySampler, fragUV * 0.8 + vec2<f32>(0.1, 0.1));
|
|
||||||
let f = f32(length(texColor.rgb - vec3<f32>(0.5, 0.5, 0.5)) < 0.01);
|
|
||||||
return (1.0 - f) * texColor + f * fragPosition;
|
|
||||||
// return vec4<f32>(texColor.rgb,1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
struct CameraUniform {
|
|
||||||
pos: vec4<f32>,
|
|
||||||
view_proj: mat4x4<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct InstanceInput {
|
|
||||||
@location(3) model_matrix_0: vec4<f32>,
|
|
||||||
@location(4) model_matrix_1: vec4<f32>,
|
|
||||||
@location(5) model_matrix_2: vec4<f32>,
|
|
||||||
@location(6) model_matrix_3: vec4<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VertexInput {
|
|
||||||
@location(0) position: vec3<f32>,
|
|
||||||
@location(1) normal: vec3<f32>,
|
|
||||||
@location(2) tex_coords: vec2<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) clip_position: vec4<f32>,
|
|
||||||
@location(0) tex_coords: vec2<f32>,
|
|
||||||
@location(1) normal: vec3<f32>,
|
|
||||||
@location(2) position: vec3<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Light {
|
|
||||||
position: vec4<f32>,
|
|
||||||
color: vec4<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
@group(0) @binding(0) var<uniform> camera: CameraUniform;
|
|
||||||
@group(1) @binding(0) var t_diffuse: texture_2d<f32>;
|
|
||||||
@group(1) @binding(1) var s_diffuse: sampler;
|
|
||||||
@group(2) @binding(0) var<uniform> light: Light;
|
|
||||||
|
|
||||||
@vertex
|
|
||||||
fn vs_main(model: VertexInput, instance: InstanceInput) -> VertexOutput {
|
|
||||||
let model_matrix = mat4x4<f32>(
|
|
||||||
instance.model_matrix_0,
|
|
||||||
instance.model_matrix_1,
|
|
||||||
instance.model_matrix_2,
|
|
||||||
instance.model_matrix_3,
|
|
||||||
);
|
|
||||||
var out: VertexOutput;
|
|
||||||
let world_pos = model_matrix * vec4<f32>(model.position, 1.0);
|
|
||||||
out.position = world_pos.xyz;
|
|
||||||
out.normal = (model_matrix * vec4<f32>(model.normal, 0.0)).xyz;
|
|
||||||
out.clip_position = camera.view_proj * world_pos;
|
|
||||||
out.tex_coords = model.tex_coords;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@fragment
|
|
||||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
|
||||||
let object_color = textureSample(t_diffuse, s_diffuse, in.tex_coords);
|
|
||||||
|
|
||||||
let ambient = 0.1;
|
|
||||||
let ambient_color = light.color.rbg * ambient;
|
|
||||||
|
|
||||||
let light_dir = normalize(light.position.xyz - in.position);
|
|
||||||
let diffuse = max(dot(in.normal, light_dir), 0.0);
|
|
||||||
let diffuse_color = light.color.rgb * diffuse;
|
|
||||||
|
|
||||||
let view_dir = normalize(camera.pos.xyz - in.position);
|
|
||||||
let half_dir = normalize(view_dir + light_dir);
|
|
||||||
let specular = pow(max(dot(in.normal, half_dir), 0.0), 32.0);
|
|
||||||
let specular_color = light.color.rbg * specular;
|
|
||||||
|
|
||||||
let all = ambient_color + diffuse_color + specular_color;
|
|
||||||
|
|
||||||
let result = all * object_color.rgb;
|
|
||||||
|
|
||||||
return vec4<f32>(result, object_color.a);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
struct CameraUniform {
|
|
||||||
view_pos: vec4<f32>,
|
|
||||||
view_proj: mat4x4<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VertexInput {
|
|
||||||
@location(0) position: vec3<f32>,
|
|
||||||
@location(1) normal: vec3<f32>,
|
|
||||||
@location(2) tex_coords: vec2<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) clip_position: vec4<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Light {
|
|
||||||
position: vec4<f32>,
|
|
||||||
color: vec4<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
@group(0) @binding(0) var<uniform> camera: CameraUniform;
|
|
||||||
@group(1) @binding(0) var<uniform> light: Light;
|
|
||||||
|
|
||||||
@vertex
|
|
||||||
fn vs_main(model: VertexInput) -> VertexOutput {
|
|
||||||
var out: VertexOutput;
|
|
||||||
let world_pos = vec4<f32>(model.position + light.position.xyz, 1.0);
|
|
||||||
out.clip_position = camera.view_proj * world_pos;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@fragment
|
|
||||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
|
||||||
return vec4<f32>(1.0, 1.0, 1.0, 0.5);
|
|
||||||
}
|
|
||||||
|
|
@ -1,893 +0,0 @@
|
||||||
// in this example:
|
|
||||||
// - comptime generated image data for texture
|
|
||||||
// - Blinn-Phong lighting
|
|
||||||
// - several pipelines
|
|
||||||
//
|
|
||||||
// quit with escape, q or space
|
|
||||||
// move camera with arrows or wasd
|
|
||||||
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
const zm = @import("zmath");
|
|
||||||
const Vec = zm.Vec;
|
|
||||||
const Mat = zm.Mat;
|
|
||||||
const Quat = zm.Quat;
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
|
|
||||||
title_timer: core.Timer,
|
|
||||||
timer: core.Timer,
|
|
||||||
cube: Cube,
|
|
||||||
camera: Camera,
|
|
||||||
light: Light,
|
|
||||||
depth: Texture,
|
|
||||||
keys: u8,
|
|
||||||
|
|
||||||
const Dir = struct {
|
|
||||||
const up: u8 = 0b0001;
|
|
||||||
const down: u8 = 0b0010;
|
|
||||||
const left: u8 = 0b0100;
|
|
||||||
const right: u8 = 0b1000;
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{});
|
|
||||||
|
|
||||||
app.title_timer = try core.Timer.start();
|
|
||||||
app.timer = try core.Timer.start();
|
|
||||||
|
|
||||||
const eye = Vec{ 5.0, 7.0, 5.0, 0.0 };
|
|
||||||
const target = Vec{ 0.0, 0.0, 0.0, 0.0 };
|
|
||||||
|
|
||||||
const framebuffer = core.descriptor;
|
|
||||||
const aspect_ratio = @as(f32, @floatFromInt(framebuffer.width)) / @as(f32, @floatFromInt(framebuffer.height));
|
|
||||||
|
|
||||||
app.cube = Cube.init();
|
|
||||||
app.light = Light.init();
|
|
||||||
app.depth = Texture.depth(core.device, framebuffer.width, framebuffer.height);
|
|
||||||
app.camera = Camera.init(core.device, eye, target, zm.Vec{ 0.0, 1.0, 0.0, 0.0 }, aspect_ratio, 45.0, 0.1, 100.0);
|
|
||||||
app.keys = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
defer _ = gpa.deinit();
|
|
||||||
defer core.deinit();
|
|
||||||
|
|
||||||
app.cube.deinit();
|
|
||||||
app.camera.deinit();
|
|
||||||
app.light.deinit();
|
|
||||||
app.depth.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(app: *App) !bool {
|
|
||||||
const delta_time = app.timer.lap();
|
|
||||||
|
|
||||||
var iter = core.pollEvents();
|
|
||||||
while (iter.next()) |event| {
|
|
||||||
switch (event) {
|
|
||||||
.key_press => |ev| switch (ev.key) {
|
|
||||||
.q, .escape, .space => return true,
|
|
||||||
.w, .up => {
|
|
||||||
app.keys |= Dir.up;
|
|
||||||
},
|
|
||||||
.s, .down => {
|
|
||||||
app.keys |= Dir.down;
|
|
||||||
},
|
|
||||||
.a, .left => {
|
|
||||||
app.keys |= Dir.left;
|
|
||||||
},
|
|
||||||
.d, .right => {
|
|
||||||
app.keys |= Dir.right;
|
|
||||||
},
|
|
||||||
.one => core.setDisplayMode(.windowed),
|
|
||||||
.two => core.setDisplayMode(.fullscreen),
|
|
||||||
.three => core.setDisplayMode(.borderless),
|
|
||||||
else => {},
|
|
||||||
},
|
|
||||||
.key_release => |ev| switch (ev.key) {
|
|
||||||
.w, .up => {
|
|
||||||
app.keys &= ~Dir.up;
|
|
||||||
},
|
|
||||||
.s, .down => {
|
|
||||||
app.keys &= ~Dir.down;
|
|
||||||
},
|
|
||||||
.a, .left => {
|
|
||||||
app.keys &= ~Dir.left;
|
|
||||||
},
|
|
||||||
.d, .right => {
|
|
||||||
app.keys &= ~Dir.right;
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
},
|
|
||||||
.framebuffer_resize => |ev| {
|
|
||||||
// recreates the sampler, which is a waste, but for an example it's ok
|
|
||||||
app.depth.release();
|
|
||||||
app.depth = Texture.depth(core.device, ev.width, ev.height);
|
|
||||||
},
|
|
||||||
.close => return true,
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// move camera
|
|
||||||
const speed = zm.Vec{ delta_time * 5, delta_time * 5, delta_time * 5, delta_time * 5 };
|
|
||||||
const fwd = zm.normalize3(app.camera.target - app.camera.eye);
|
|
||||||
const right = zm.normalize3(zm.cross3(fwd, app.camera.up));
|
|
||||||
|
|
||||||
if (app.keys & Dir.up != 0)
|
|
||||||
app.camera.eye += fwd * speed;
|
|
||||||
|
|
||||||
if (app.keys & Dir.down != 0)
|
|
||||||
app.camera.eye -= fwd * speed;
|
|
||||||
|
|
||||||
if (app.keys & Dir.right != 0)
|
|
||||||
app.camera.eye += right * speed
|
|
||||||
else if (app.keys & Dir.left != 0)
|
|
||||||
app.camera.eye -= right * speed
|
|
||||||
else
|
|
||||||
app.camera.eye += right * (speed * @Vector(4, f32){ 0.5, 0.5, 0.5, 0.5 });
|
|
||||||
|
|
||||||
const queue = core.queue;
|
|
||||||
app.camera.update(queue);
|
|
||||||
|
|
||||||
// move light
|
|
||||||
const light_speed = delta_time * 2.5;
|
|
||||||
app.light.update(queue, light_speed);
|
|
||||||
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
defer back_buffer_view.release();
|
|
||||||
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
defer encoder.release();
|
|
||||||
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = back_buffer_view,
|
|
||||||
.clear_value = gpu.Color{ .r = 0.0, .g = 0.0, .b = 0.4, .a = 1.0 },
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
|
|
||||||
const render_pass_descriptor = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{color_attachment},
|
|
||||||
.depth_stencil_attachment = &.{
|
|
||||||
.view = app.depth.view,
|
|
||||||
.depth_load_op = .clear,
|
|
||||||
.depth_store_op = .store,
|
|
||||||
.depth_clear_value = 1.0,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const pass = encoder.beginRenderPass(&render_pass_descriptor);
|
|
||||||
defer pass.release();
|
|
||||||
|
|
||||||
// brick cubes
|
|
||||||
pass.setPipeline(app.cube.pipeline);
|
|
||||||
pass.setBindGroup(0, app.camera.bind_group, &.{});
|
|
||||||
pass.setBindGroup(1, app.cube.texture.bind_group.?, &.{});
|
|
||||||
pass.setBindGroup(2, app.light.bind_group, &.{});
|
|
||||||
pass.setVertexBuffer(0, app.cube.mesh.buffer, 0, app.cube.mesh.size);
|
|
||||||
pass.setVertexBuffer(1, app.cube.instance.buffer, 0, app.cube.instance.size);
|
|
||||||
pass.draw(4, app.cube.instance.len, 0, 0);
|
|
||||||
pass.draw(4, app.cube.instance.len, 4, 0);
|
|
||||||
pass.draw(4, app.cube.instance.len, 8, 0);
|
|
||||||
pass.draw(4, app.cube.instance.len, 12, 0);
|
|
||||||
pass.draw(4, app.cube.instance.len, 16, 0);
|
|
||||||
pass.draw(4, app.cube.instance.len, 20, 0);
|
|
||||||
|
|
||||||
// light source
|
|
||||||
pass.setPipeline(app.light.pipeline);
|
|
||||||
pass.setBindGroup(0, app.camera.bind_group, &.{});
|
|
||||||
pass.setBindGroup(1, app.light.bind_group, &.{});
|
|
||||||
pass.setVertexBuffer(0, app.cube.mesh.buffer, 0, app.cube.mesh.size);
|
|
||||||
pass.draw(4, 1, 0, 0);
|
|
||||||
pass.draw(4, 1, 4, 0);
|
|
||||||
pass.draw(4, 1, 8, 0);
|
|
||||||
pass.draw(4, 1, 12, 0);
|
|
||||||
pass.draw(4, 1, 16, 0);
|
|
||||||
pass.draw(4, 1, 20, 0);
|
|
||||||
|
|
||||||
pass.end();
|
|
||||||
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
defer command.release();
|
|
||||||
|
|
||||||
queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
core.swap_chain.present();
|
|
||||||
|
|
||||||
// update the window title every second
|
|
||||||
if (app.title_timer.read() >= 1.0) {
|
|
||||||
app.title_timer.reset();
|
|
||||||
try core.printTitle("Gen Texture Light [ {d}fps ] [ Input {d}hz ]", .{
|
|
||||||
core.frameRate(),
|
|
||||||
core.inputRate(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Camera = struct {
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
eye: Vec,
|
|
||||||
target: Vec,
|
|
||||||
up: Vec,
|
|
||||||
aspect: f32,
|
|
||||||
fovy: f32,
|
|
||||||
near: f32,
|
|
||||||
far: f32,
|
|
||||||
bind_group: *gpu.BindGroup,
|
|
||||||
buffer: Buffer,
|
|
||||||
|
|
||||||
const Uniform = extern struct {
|
|
||||||
pos: Vec,
|
|
||||||
mat: Mat,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn init(device: *gpu.Device, eye: Vec, target: Vec, up: Vec, aspect: f32, fovy: f32, near: f32, far: f32) Self {
|
|
||||||
var self: Self = .{
|
|
||||||
.eye = eye,
|
|
||||||
.target = target,
|
|
||||||
.up = up,
|
|
||||||
.aspect = aspect,
|
|
||||||
.near = near,
|
|
||||||
.far = far,
|
|
||||||
.fovy = fovy,
|
|
||||||
.buffer = undefined,
|
|
||||||
.bind_group = undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
const view = self.buildViewProjMatrix();
|
|
||||||
|
|
||||||
const uniform = Uniform{
|
|
||||||
.pos = self.eye,
|
|
||||||
.mat = view,
|
|
||||||
};
|
|
||||||
|
|
||||||
const buffer = .{
|
|
||||||
.buffer = initBuffer(device, .{ .uniform = true }, &@as([20]f32, @bitCast(uniform))),
|
|
||||||
.size = @sizeOf(@TypeOf(uniform)),
|
|
||||||
};
|
|
||||||
|
|
||||||
const layout = Self.bindGroupLayout(device);
|
|
||||||
const bind_group = device.createBindGroup(&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = layout,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.buffer(0, buffer.buffer, 0, buffer.size),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
layout.release();
|
|
||||||
|
|
||||||
self.buffer = buffer;
|
|
||||||
self.bind_group = bind_group;
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deinit(self: *Self) void {
|
|
||||||
self.bind_group.release();
|
|
||||||
self.buffer.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(self: *Self, queue: *gpu.Queue) void {
|
|
||||||
const mat = self.buildViewProjMatrix();
|
|
||||||
const uniform = .{
|
|
||||||
.pos = self.eye,
|
|
||||||
.mat = mat,
|
|
||||||
};
|
|
||||||
|
|
||||||
queue.writeBuffer(self.buffer.buffer, 0, &[_]Uniform{uniform});
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fn buildViewProjMatrix(s: *const Camera) Mat {
|
|
||||||
const view = zm.lookAtRh(s.eye, s.target, s.up);
|
|
||||||
const proj = zm.perspectiveFovRh(s.fovy, s.aspect, s.near, s.far);
|
|
||||||
return zm.mul(view, proj);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fn bindGroupLayout(device: *gpu.Device) *gpu.BindGroupLayout {
|
|
||||||
const visibility = .{ .vertex = true, .fragment = true };
|
|
||||||
return device.createBindGroupLayout(&gpu.BindGroupLayout.Descriptor.init(.{
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroupLayout.Entry.buffer(0, visibility, .uniform, false, 0),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const Buffer = struct {
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
buffer: *gpu.Buffer,
|
|
||||||
size: usize,
|
|
||||||
len: u32 = 0,
|
|
||||||
|
|
||||||
fn release(self: *Self) void {
|
|
||||||
self.buffer.release();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const Cube = struct {
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
pipeline: *gpu.RenderPipeline,
|
|
||||||
mesh: Buffer,
|
|
||||||
instance: Buffer,
|
|
||||||
texture: Texture,
|
|
||||||
|
|
||||||
const IPR = 20; // instances per row
|
|
||||||
const SPACING = 2; // spacing between cubes
|
|
||||||
const DISPLACEMENT = vec3u(IPR * SPACING / 2, 0, IPR * SPACING / 2);
|
|
||||||
|
|
||||||
fn init() Self {
|
|
||||||
const device = core.device;
|
|
||||||
|
|
||||||
const texture = Brick.texture(device);
|
|
||||||
|
|
||||||
// instance buffer
|
|
||||||
var ibuf: [IPR * IPR * 16]f32 = undefined;
|
|
||||||
|
|
||||||
var z: usize = 0;
|
|
||||||
while (z < IPR) : (z += 1) {
|
|
||||||
var x: usize = 0;
|
|
||||||
while (x < IPR) : (x += 1) {
|
|
||||||
const pos = vec3u(x * SPACING, 0, z * SPACING) - DISPLACEMENT;
|
|
||||||
const rot = blk: {
|
|
||||||
if (pos[0] == 0 and pos[2] == 0) {
|
|
||||||
break :blk zm.rotationZ(0.0);
|
|
||||||
} else {
|
|
||||||
break :blk zm.mul(zm.rotationX(zm.clamp(zm.abs(pos[0]), 0, 45.0)), zm.rotationZ(zm.clamp(zm.abs(pos[2]), 0, 45.0)));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const index = z * IPR + x;
|
|
||||||
const inst = Instance{
|
|
||||||
.position = pos,
|
|
||||||
.rotation = rot,
|
|
||||||
};
|
|
||||||
zm.storeMat(ibuf[index * 16 ..], inst.toMat());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const instance = Buffer{
|
|
||||||
.buffer = initBuffer(device, .{ .vertex = true }, &ibuf),
|
|
||||||
.len = IPR * IPR,
|
|
||||||
.size = @sizeOf(@TypeOf(ibuf)),
|
|
||||||
};
|
|
||||||
|
|
||||||
return Self{
|
|
||||||
.mesh = mesh(device),
|
|
||||||
.texture = texture,
|
|
||||||
.instance = instance,
|
|
||||||
.pipeline = pipeline(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deinit(self: *Self) void {
|
|
||||||
self.pipeline.release();
|
|
||||||
self.mesh.release();
|
|
||||||
self.instance.release();
|
|
||||||
self.texture.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pipeline() *gpu.RenderPipeline {
|
|
||||||
const device = core.device;
|
|
||||||
|
|
||||||
const camera_layout = Camera.bindGroupLayout(device);
|
|
||||||
const texture_layout = Texture.bindGroupLayout(device);
|
|
||||||
const light_layout = Light.bindGroupLayout(device);
|
|
||||||
const layout_descriptor = gpu.PipelineLayout.Descriptor.init(.{
|
|
||||||
.bind_group_layouts = &.{
|
|
||||||
camera_layout,
|
|
||||||
texture_layout,
|
|
||||||
light_layout,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
defer camera_layout.release();
|
|
||||||
defer texture_layout.release();
|
|
||||||
defer light_layout.release();
|
|
||||||
|
|
||||||
const layout = device.createPipelineLayout(&layout_descriptor);
|
|
||||||
defer layout.release();
|
|
||||||
|
|
||||||
const shader = device.createShaderModuleWGSL("cube.wgsl", @embedFile("cube.wgsl"));
|
|
||||||
defer shader.release();
|
|
||||||
|
|
||||||
const blend = gpu.BlendState{};
|
|
||||||
const color_target = gpu.ColorTargetState{
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
.blend = &blend,
|
|
||||||
};
|
|
||||||
|
|
||||||
const fragment = gpu.FragmentState.init(.{
|
|
||||||
.module = shader,
|
|
||||||
.entry_point = "fs_main",
|
|
||||||
.targets = &.{color_target},
|
|
||||||
});
|
|
||||||
|
|
||||||
const descriptor = gpu.RenderPipeline.Descriptor{
|
|
||||||
.layout = layout,
|
|
||||||
.fragment = &fragment,
|
|
||||||
.vertex = gpu.VertexState.init(.{
|
|
||||||
.module = shader,
|
|
||||||
.entry_point = "vs_main",
|
|
||||||
.buffers = &.{
|
|
||||||
Self.vertexBufferLayout(),
|
|
||||||
Self.instanceLayout(),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
.depth_stencil = &.{
|
|
||||||
.format = Texture.DEPTH_FORMAT,
|
|
||||||
.depth_write_enabled = .true,
|
|
||||||
.depth_compare = .less,
|
|
||||||
},
|
|
||||||
.primitive = .{
|
|
||||||
.cull_mode = .back,
|
|
||||||
.topology = .triangle_strip,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return device.createRenderPipeline(&descriptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mesh(device: *gpu.Device) Buffer {
|
|
||||||
// generated texture has aspect ratio of 1:2
|
|
||||||
// `h` reflects that ratio
|
|
||||||
// `v` sets how many times texture repeats across surface
|
|
||||||
const v = 2;
|
|
||||||
const h = v * 2;
|
|
||||||
const buf = asFloats(.{
|
|
||||||
// z+ face
|
|
||||||
0, 0, 1, 0, 0, 1, 0, h,
|
|
||||||
1, 0, 1, 0, 0, 1, v, h,
|
|
||||||
0, 1, 1, 0, 0, 1, 0, 0,
|
|
||||||
1, 1, 1, 0, 0, 1, v, 0,
|
|
||||||
// z- face
|
|
||||||
1, 0, 0, 0, 0, -1, 0, h,
|
|
||||||
0, 0, 0, 0, 0, -1, v, h,
|
|
||||||
1, 1, 0, 0, 0, -1, 0, 0,
|
|
||||||
0, 1, 0, 0, 0, -1, v, 0,
|
|
||||||
// x+ face
|
|
||||||
1, 0, 1, 1, 0, 0, 0, h,
|
|
||||||
1, 0, 0, 1, 0, 0, v, h,
|
|
||||||
1, 1, 1, 1, 0, 0, 0, 0,
|
|
||||||
1, 1, 0, 1, 0, 0, v, 0,
|
|
||||||
// x- face
|
|
||||||
0, 0, 0, -1, 0, 0, 0, h,
|
|
||||||
0, 0, 1, -1, 0, 0, v, h,
|
|
||||||
0, 1, 0, -1, 0, 0, 0, 0,
|
|
||||||
0, 1, 1, -1, 0, 0, v, 0,
|
|
||||||
// y+ face
|
|
||||||
1, 1, 0, 0, 1, 0, 0, h,
|
|
||||||
0, 1, 0, 0, 1, 0, v, h,
|
|
||||||
1, 1, 1, 0, 1, 0, 0, 0,
|
|
||||||
0, 1, 1, 0, 1, 0, v, 0,
|
|
||||||
// y- face
|
|
||||||
0, 0, 0, 0, -1, 0, 0, h,
|
|
||||||
1, 0, 0, 0, -1, 0, v, h,
|
|
||||||
0, 0, 1, 0, -1, 0, 0, 0,
|
|
||||||
1, 0, 1, 0, -1, 0, v, 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
return Buffer{
|
|
||||||
.buffer = initBuffer(device, .{ .vertex = true }, &buf),
|
|
||||||
.size = @sizeOf(@TypeOf(buf)),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn vertexBufferLayout() gpu.VertexBufferLayout {
|
|
||||||
const attributes = [_]gpu.VertexAttribute{
|
|
||||||
.{
|
|
||||||
.format = .float32x3,
|
|
||||||
.offset = 0,
|
|
||||||
.shader_location = 0,
|
|
||||||
},
|
|
||||||
.{
|
|
||||||
.format = .float32x3,
|
|
||||||
.offset = @sizeOf([3]f32),
|
|
||||||
.shader_location = 1,
|
|
||||||
},
|
|
||||||
.{
|
|
||||||
.format = .float32x2,
|
|
||||||
.offset = @sizeOf([6]f32),
|
|
||||||
.shader_location = 2,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
return gpu.VertexBufferLayout.init(.{
|
|
||||||
.array_stride = @sizeOf([8]f32),
|
|
||||||
.attributes = &attributes,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn instanceLayout() gpu.VertexBufferLayout {
|
|
||||||
const attributes = [_]gpu.VertexAttribute{
|
|
||||||
.{
|
|
||||||
.format = .float32x4,
|
|
||||||
.offset = 0,
|
|
||||||
.shader_location = 3,
|
|
||||||
},
|
|
||||||
.{
|
|
||||||
.format = .float32x4,
|
|
||||||
.offset = @sizeOf([4]f32),
|
|
||||||
.shader_location = 4,
|
|
||||||
},
|
|
||||||
.{
|
|
||||||
.format = .float32x4,
|
|
||||||
.offset = @sizeOf([8]f32),
|
|
||||||
.shader_location = 5,
|
|
||||||
},
|
|
||||||
.{
|
|
||||||
.format = .float32x4,
|
|
||||||
.offset = @sizeOf([12]f32),
|
|
||||||
.shader_location = 6,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return gpu.VertexBufferLayout.init(.{
|
|
||||||
.array_stride = @sizeOf([16]f32),
|
|
||||||
.step_mode = .instance,
|
|
||||||
.attributes = &attributes,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
fn asFloats(comptime arr: anytype) [arr.len]f32 {
|
|
||||||
const len = arr.len;
|
|
||||||
comptime var out: [len]f32 = undefined;
|
|
||||||
comptime var i = 0;
|
|
||||||
inline while (i < len) : (i += 1) {
|
|
||||||
out[i] = @as(f32, @floatFromInt(arr[i]));
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Brick = struct {
|
|
||||||
const W = 12;
|
|
||||||
const H = 6;
|
|
||||||
|
|
||||||
fn texture(device: *gpu.Device) Texture {
|
|
||||||
const slice: []const u8 = &data();
|
|
||||||
return Texture.fromData(device, W, H, u8, slice);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn data() [W * H * 4]u8 {
|
|
||||||
comptime var out: [W * H * 4]u8 = undefined;
|
|
||||||
|
|
||||||
// fill all the texture with brick color
|
|
||||||
comptime var i = 0;
|
|
||||||
inline while (i < H) : (i += 1) {
|
|
||||||
comptime var j = 0;
|
|
||||||
inline while (j < W * 4) : (j += 4) {
|
|
||||||
out[i * W * 4 + j + 0] = 210;
|
|
||||||
out[i * W * 4 + j + 1] = 30;
|
|
||||||
out[i * W * 4 + j + 2] = 30;
|
|
||||||
out[i * W * 4 + j + 3] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const f = 10;
|
|
||||||
|
|
||||||
// fill the cement lines
|
|
||||||
inline for ([_]comptime_int{ 0, 1 }) |k| {
|
|
||||||
inline for ([_]comptime_int{ 5 * 4, 11 * 4 }) |m| {
|
|
||||||
out[k * W * 4 + m + 0] = f;
|
|
||||||
out[k * W * 4 + m + 1] = f;
|
|
||||||
out[k * W * 4 + m + 2] = f;
|
|
||||||
out[k * W * 4 + m + 3] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline for ([_]comptime_int{ 3, 4 }) |k| {
|
|
||||||
inline for ([_]comptime_int{ 2 * 4, 8 * 4 }) |m| {
|
|
||||||
out[k * W * 4 + m + 0] = f;
|
|
||||||
out[k * W * 4 + m + 1] = f;
|
|
||||||
out[k * W * 4 + m + 2] = f;
|
|
||||||
out[k * W * 4 + m + 3] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline for ([_]comptime_int{ 2, 5 }) |k| {
|
|
||||||
comptime var m = 0;
|
|
||||||
inline while (m < W * 4) : (m += 4) {
|
|
||||||
out[k * W * 4 + m + 0] = f;
|
|
||||||
out[k * W * 4 + m + 1] = f;
|
|
||||||
out[k * W * 4 + m + 2] = f;
|
|
||||||
out[k * W * 4 + m + 3] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// don't confuse with gpu.Texture
|
|
||||||
const Texture = struct {
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
texture: *gpu.Texture,
|
|
||||||
view: *gpu.TextureView,
|
|
||||||
sampler: *gpu.Sampler,
|
|
||||||
bind_group: ?*gpu.BindGroup,
|
|
||||||
|
|
||||||
const DEPTH_FORMAT = .depth32_float;
|
|
||||||
const FORMAT = .rgba8_unorm;
|
|
||||||
|
|
||||||
fn release(self: *Self) void {
|
|
||||||
self.texture.release();
|
|
||||||
self.view.release();
|
|
||||||
self.sampler.release();
|
|
||||||
if (self.bind_group) |bind_group| bind_group.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fromData(device: *gpu.Device, width: u32, height: u32, comptime T: type, data: []const T) Self {
|
|
||||||
const extent = gpu.Extent3D{
|
|
||||||
.width = width,
|
|
||||||
.height = height,
|
|
||||||
};
|
|
||||||
|
|
||||||
const texture = device.createTexture(&gpu.Texture.Descriptor{
|
|
||||||
.size = extent,
|
|
||||||
.format = FORMAT,
|
|
||||||
.usage = .{ .copy_dst = true, .texture_binding = true },
|
|
||||||
});
|
|
||||||
|
|
||||||
const view = texture.createView(&gpu.TextureView.Descriptor{
|
|
||||||
.format = FORMAT,
|
|
||||||
.dimension = .dimension_2d,
|
|
||||||
.array_layer_count = 1,
|
|
||||||
.mip_level_count = 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
const sampler = device.createSampler(&gpu.Sampler.Descriptor{
|
|
||||||
.address_mode_u = .repeat,
|
|
||||||
.address_mode_v = .repeat,
|
|
||||||
.address_mode_w = .repeat,
|
|
||||||
.mag_filter = .linear,
|
|
||||||
.min_filter = .linear,
|
|
||||||
.mipmap_filter = .linear,
|
|
||||||
.max_anisotropy = 1, // 1,2,4,8,16
|
|
||||||
});
|
|
||||||
|
|
||||||
core.queue.writeTexture(
|
|
||||||
&gpu.ImageCopyTexture{
|
|
||||||
.texture = texture,
|
|
||||||
},
|
|
||||||
&gpu.Texture.DataLayout{
|
|
||||||
.bytes_per_row = 4 * width,
|
|
||||||
.rows_per_image = height,
|
|
||||||
},
|
|
||||||
&extent,
|
|
||||||
data,
|
|
||||||
);
|
|
||||||
|
|
||||||
const bind_group_layout = Self.bindGroupLayout(device);
|
|
||||||
const bind_group = device.createBindGroup(&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = bind_group_layout,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.textureView(0, view),
|
|
||||||
gpu.BindGroup.Entry.sampler(1, sampler),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
bind_group_layout.release();
|
|
||||||
|
|
||||||
return Self{
|
|
||||||
.view = view,
|
|
||||||
.texture = texture,
|
|
||||||
.sampler = sampler,
|
|
||||||
.bind_group = bind_group,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn depth(device: *gpu.Device, width: u32, height: u32) Self {
|
|
||||||
const extent = gpu.Extent3D{
|
|
||||||
.width = width,
|
|
||||||
.height = height,
|
|
||||||
};
|
|
||||||
|
|
||||||
const texture = device.createTexture(&gpu.Texture.Descriptor{
|
|
||||||
.size = extent,
|
|
||||||
.format = DEPTH_FORMAT,
|
|
||||||
.usage = .{
|
|
||||||
.render_attachment = true,
|
|
||||||
.texture_binding = true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const view = texture.createView(&gpu.TextureView.Descriptor{
|
|
||||||
.dimension = .dimension_2d,
|
|
||||||
.array_layer_count = 1,
|
|
||||||
.mip_level_count = 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
const sampler = device.createSampler(&gpu.Sampler.Descriptor{
|
|
||||||
.mag_filter = .linear,
|
|
||||||
.compare = .less_equal,
|
|
||||||
});
|
|
||||||
|
|
||||||
return Self{
|
|
||||||
.texture = texture,
|
|
||||||
.view = view,
|
|
||||||
.sampler = sampler,
|
|
||||||
.bind_group = null, // not used
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fn bindGroupLayout(device: *gpu.Device) *gpu.BindGroupLayout {
|
|
||||||
const visibility = .{ .fragment = true };
|
|
||||||
const Entry = gpu.BindGroupLayout.Entry;
|
|
||||||
return device.createBindGroupLayout(&gpu.BindGroupLayout.Descriptor.init(.{
|
|
||||||
.entries = &.{
|
|
||||||
Entry.texture(0, visibility, .float, .dimension_2d, false),
|
|
||||||
Entry.sampler(1, visibility, .filtering),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const Light = struct {
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
uniform: Uniform,
|
|
||||||
buffer: Buffer,
|
|
||||||
bind_group: *gpu.BindGroup,
|
|
||||||
pipeline: *gpu.RenderPipeline,
|
|
||||||
|
|
||||||
const Uniform = extern struct {
|
|
||||||
position: Vec,
|
|
||||||
color: Vec,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn init() Self {
|
|
||||||
const device = core.device;
|
|
||||||
const uniform = Uniform{
|
|
||||||
.color = vec3u(1, 1, 1),
|
|
||||||
.position = vec3u(3, 7, 2),
|
|
||||||
};
|
|
||||||
|
|
||||||
const buffer = .{
|
|
||||||
.buffer = initBuffer(device, .{ .uniform = true }, &@as([8]f32, @bitCast(uniform))),
|
|
||||||
.size = @sizeOf(@TypeOf(uniform)),
|
|
||||||
};
|
|
||||||
|
|
||||||
const layout = Self.bindGroupLayout(device);
|
|
||||||
const bind_group = device.createBindGroup(&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = layout,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.buffer(0, buffer.buffer, 0, buffer.size),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
layout.release();
|
|
||||||
|
|
||||||
return Self{
|
|
||||||
.buffer = buffer,
|
|
||||||
.uniform = uniform,
|
|
||||||
.bind_group = bind_group,
|
|
||||||
.pipeline = Self.pipeline(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deinit(self: *Self) void {
|
|
||||||
self.buffer.release();
|
|
||||||
self.bind_group.release();
|
|
||||||
self.pipeline.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(self: *Self, queue: *gpu.Queue, delta: f32) void {
|
|
||||||
const old = self.uniform;
|
|
||||||
const new = Light.Uniform{
|
|
||||||
.position = zm.qmul(zm.quatFromAxisAngle(vec3u(0, 1, 0), delta), old.position),
|
|
||||||
.color = old.color,
|
|
||||||
};
|
|
||||||
queue.writeBuffer(self.buffer.buffer, 0, &[_]Light.Uniform{new});
|
|
||||||
self.uniform = new;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fn bindGroupLayout(device: *gpu.Device) *gpu.BindGroupLayout {
|
|
||||||
const visibility = .{ .vertex = true, .fragment = true };
|
|
||||||
const Entry = gpu.BindGroupLayout.Entry;
|
|
||||||
return device.createBindGroupLayout(&gpu.BindGroupLayout.Descriptor.init(.{
|
|
||||||
.entries = &.{
|
|
||||||
Entry.buffer(0, visibility, .uniform, false, 0),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pipeline() *gpu.RenderPipeline {
|
|
||||||
const device = core.device;
|
|
||||||
|
|
||||||
const camera_layout = Camera.bindGroupLayout(device);
|
|
||||||
const light_layout = Light.bindGroupLayout(device);
|
|
||||||
const layout_descriptor = gpu.PipelineLayout.Descriptor.init(.{
|
|
||||||
.bind_group_layouts = &.{
|
|
||||||
camera_layout,
|
|
||||||
light_layout,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
defer camera_layout.release();
|
|
||||||
defer light_layout.release();
|
|
||||||
|
|
||||||
const layout = device.createPipelineLayout(&layout_descriptor);
|
|
||||||
defer layout.release();
|
|
||||||
|
|
||||||
const shader = core.device.createShaderModuleWGSL("light.wgsl", @embedFile("light.wgsl"));
|
|
||||||
defer shader.release();
|
|
||||||
|
|
||||||
const blend = gpu.BlendState{};
|
|
||||||
const color_target = gpu.ColorTargetState{
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
.blend = &blend,
|
|
||||||
};
|
|
||||||
|
|
||||||
const fragment = gpu.FragmentState.init(.{
|
|
||||||
.module = shader,
|
|
||||||
.entry_point = "fs_main",
|
|
||||||
.targets = &.{color_target},
|
|
||||||
});
|
|
||||||
|
|
||||||
const descriptor = gpu.RenderPipeline.Descriptor{
|
|
||||||
.layout = layout,
|
|
||||||
.fragment = &fragment,
|
|
||||||
.vertex = gpu.VertexState.init(.{
|
|
||||||
.module = shader,
|
|
||||||
.entry_point = "vs_main",
|
|
||||||
.buffers = &.{
|
|
||||||
Cube.vertexBufferLayout(),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
.depth_stencil = &.{
|
|
||||||
.format = Texture.DEPTH_FORMAT,
|
|
||||||
.depth_write_enabled = .true,
|
|
||||||
.depth_compare = .less,
|
|
||||||
},
|
|
||||||
.primitive = .{
|
|
||||||
.cull_mode = .back,
|
|
||||||
.topology = .triangle_strip,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return device.createRenderPipeline(&descriptor);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline fn initBuffer(device: *gpu.Device, usage: gpu.Buffer.UsageFlags, data: anytype) *gpu.Buffer {
|
|
||||||
std.debug.assert(@typeInfo(@TypeOf(data)) == .Pointer);
|
|
||||||
const T = std.meta.Elem(@TypeOf(data));
|
|
||||||
|
|
||||||
var u = usage;
|
|
||||||
u.copy_dst = true;
|
|
||||||
const buffer = device.createBuffer(&.{
|
|
||||||
.size = @sizeOf(T) * data.len,
|
|
||||||
.usage = u,
|
|
||||||
.mapped_at_creation = .true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const mapped = buffer.getMappedRange(T, 0, data.len);
|
|
||||||
@memcpy(mapped.?, data);
|
|
||||||
buffer.unmap();
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn vec3i(x: isize, y: isize, z: isize) Vec {
|
|
||||||
return Vec{ @floatFromInt(x), @floatFromInt(y), @floatFromInt(z), 0.0 };
|
|
||||||
}
|
|
||||||
|
|
||||||
fn vec3u(x: usize, y: usize, z: usize) Vec {
|
|
||||||
return zm.Vec{ @floatFromInt(x), @floatFromInt(y), @floatFromInt(z), 0.0 };
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo indside Cube
|
|
||||||
const Instance = struct {
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
position: Vec,
|
|
||||||
rotation: Mat,
|
|
||||||
|
|
||||||
fn toMat(self: *const Self) Mat {
|
|
||||||
return zm.mul(self.rotation, zm.translationV(self.position));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,81 +0,0 @@
|
||||||
struct Params {
|
|
||||||
filterDim : i32,
|
|
||||||
blockDim : u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
@group(0) @binding(0) var samp : sampler;
|
|
||||||
@group(0) @binding(1) var<uniform> params : Params;
|
|
||||||
@group(1) @binding(1) var inputTex : texture_2d<f32>;
|
|
||||||
@group(1) @binding(2) var outputTex : texture_storage_2d<rgba8unorm, write>;
|
|
||||||
|
|
||||||
struct Flip {
|
|
||||||
value : u32,
|
|
||||||
}
|
|
||||||
@group(1) @binding(3) var<uniform> flip : Flip;
|
|
||||||
|
|
||||||
// This shader blurs the input texture in one direction, depending on whether
|
|
||||||
// |flip.value| is 0 or 1.
|
|
||||||
// It does so by running (128 / 4) threads per workgroup to load 128
|
|
||||||
// texels into 4 rows of shared memory. Each thread loads a
|
|
||||||
// 4 x 4 block of texels to take advantage of the texture sampling
|
|
||||||
// hardware.
|
|
||||||
// Then, each thread computes the blur result by averaging the adjacent texel values
|
|
||||||
// in shared memory.
|
|
||||||
// Because we're operating on a subset of the texture, we cannot compute all of the
|
|
||||||
// results since not all of the neighbors are available in shared memory.
|
|
||||||
// Specifically, with 128 x 128 tiles, we can only compute and write out
|
|
||||||
// square blocks of size 128 - (filterSize - 1). We compute the number of blocks
|
|
||||||
// needed in Javascript and dispatch that amount.
|
|
||||||
|
|
||||||
var<workgroup> tile : array<array<vec3<f32>, 128>, 4>;
|
|
||||||
|
|
||||||
@compute @workgroup_size(32, 1, 1)
|
|
||||||
fn main(
|
|
||||||
@builtin(workgroup_id) WorkGroupID : vec3<u32>,
|
|
||||||
@builtin(local_invocation_id) LocalInvocationID : vec3<u32>
|
|
||||||
) {
|
|
||||||
let filterOffset = (params.filterDim - 1) / 2;
|
|
||||||
let dims = vec2<i32>(textureDimensions(inputTex, 0));
|
|
||||||
let baseIndex = vec2<i32>(WorkGroupID.xy * vec2(params.blockDim, 4) +
|
|
||||||
LocalInvocationID.xy * vec2(4, 1))
|
|
||||||
- vec2(filterOffset, 0);
|
|
||||||
|
|
||||||
for (var r = 0; r < 4; r++) {
|
|
||||||
for (var c = 0; c < 4; c++) {
|
|
||||||
var loadIndex = baseIndex + vec2(c, r);
|
|
||||||
if (flip.value != 0u) {
|
|
||||||
loadIndex = loadIndex.yx;
|
|
||||||
}
|
|
||||||
|
|
||||||
tile[r][4 * LocalInvocationID.x + u32(c)] = textureSampleLevel(
|
|
||||||
inputTex,
|
|
||||||
samp,
|
|
||||||
(vec2<f32>(loadIndex) + vec2<f32>(0.25, 0.25)) / vec2<f32>(dims),
|
|
||||||
0.0
|
|
||||||
).rgb;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
workgroupBarrier();
|
|
||||||
|
|
||||||
for (var r = 0; r < 4; r++) {
|
|
||||||
for (var c = 0; c < 4; c++) {
|
|
||||||
var writeIndex = baseIndex + vec2(c, r);
|
|
||||||
if (flip.value != 0) {
|
|
||||||
writeIndex = writeIndex.yx;
|
|
||||||
}
|
|
||||||
|
|
||||||
let center = i32(4 * LocalInvocationID.x) + c;
|
|
||||||
if (center >= filterOffset &&
|
|
||||||
center < 128 - filterOffset &&
|
|
||||||
all(writeIndex < dims)) {
|
|
||||||
var acc = vec3(0.0, 0.0, 0.0);
|
|
||||||
for (var f = 0; f < params.filterDim; f++) {
|
|
||||||
var i = center + f - filterOffset;
|
|
||||||
acc = acc + (1.0 / f32(params.filterDim)) * tile[r][i];
|
|
||||||
}
|
|
||||||
textureStore(outputTex, writeIndex, vec4(acc, 1.0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
@group(0) @binding(0) var mySampler : sampler;
|
|
||||||
@group(0) @binding(1) var myTexture : texture_2d<f32>;
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) Position : vec4<f32>,
|
|
||||||
@location(0) fragUV : vec2<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
@vertex
|
|
||||||
fn vert_main(@builtin(vertex_index) VertexIndex : u32) -> VertexOutput {
|
|
||||||
var pos = array<vec2<f32>, 6>(
|
|
||||||
vec2<f32>( 1.0, 1.0),
|
|
||||||
vec2<f32>( 1.0, -1.0),
|
|
||||||
vec2<f32>(-1.0, -1.0),
|
|
||||||
vec2<f32>( 1.0, 1.0),
|
|
||||||
vec2<f32>(-1.0, -1.0),
|
|
||||||
vec2<f32>(-1.0, 1.0)
|
|
||||||
);
|
|
||||||
|
|
||||||
var uv = array<vec2<f32>, 6>(
|
|
||||||
vec2<f32>(1.0, 0.0),
|
|
||||||
vec2<f32>(1.0, 1.0),
|
|
||||||
vec2<f32>(0.0, 1.0),
|
|
||||||
vec2<f32>(1.0, 0.0),
|
|
||||||
vec2<f32>(0.0, 1.0),
|
|
||||||
vec2<f32>(0.0, 0.0)
|
|
||||||
);
|
|
||||||
|
|
||||||
var output : VertexOutput;
|
|
||||||
output.Position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
|
|
||||||
output.fragUV = uv[VertexIndex];
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
@fragment
|
|
||||||
fn frag_main(@location(0) fragUV : vec2<f32>) -> @location(0) vec4<f32> {
|
|
||||||
return textureSample(myTexture, mySampler, fragUV);
|
|
||||||
}
|
|
||||||
|
|
@ -1,329 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
const zigimg = @import("zigimg");
|
|
||||||
const assets = @import("assets");
|
|
||||||
|
|
||||||
title_timer: core.Timer,
|
|
||||||
blur_pipeline: *gpu.ComputePipeline,
|
|
||||||
fullscreen_quad_pipeline: *gpu.RenderPipeline,
|
|
||||||
cube_texture: *gpu.Texture,
|
|
||||||
textures: [2]*gpu.Texture,
|
|
||||||
blur_params_buffer: *gpu.Buffer,
|
|
||||||
compute_constants: *gpu.BindGroup,
|
|
||||||
compute_bind_group_0: *gpu.BindGroup,
|
|
||||||
compute_bind_group_1: *gpu.BindGroup,
|
|
||||||
compute_bind_group_2: *gpu.BindGroup,
|
|
||||||
show_result_bind_group: *gpu.BindGroup,
|
|
||||||
img_size: gpu.Extent3D,
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
// Constants from the blur.wgsl shader
|
|
||||||
const tile_dimension: u32 = 128;
|
|
||||||
const batch: [2]u32 = .{ 4, 4 };
|
|
||||||
|
|
||||||
// Currently hardcoded
|
|
||||||
const filter_size: u32 = 15;
|
|
||||||
const iterations: u32 = 2;
|
|
||||||
var block_dimension: u32 = tile_dimension - (filter_size - 1);
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{});
|
|
||||||
const allocator = gpa.allocator();
|
|
||||||
|
|
||||||
const queue = core.queue;
|
|
||||||
|
|
||||||
const blur_shader_module = core.device.createShaderModuleWGSL("blur.wgsl", @embedFile("blur.wgsl"));
|
|
||||||
|
|
||||||
const blur_pipeline_descriptor = gpu.ComputePipeline.Descriptor{
|
|
||||||
.compute = gpu.ProgrammableStageDescriptor{
|
|
||||||
.module = blur_shader_module,
|
|
||||||
.entry_point = "main",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const blur_pipeline = core.device.createComputePipeline(&blur_pipeline_descriptor);
|
|
||||||
blur_shader_module.release();
|
|
||||||
|
|
||||||
const fullscreen_quad_vs_module = core.device.createShaderModuleWGSL(
|
|
||||||
"fullscreen_textured_quad.wgsl",
|
|
||||||
@embedFile("fullscreen_textured_quad.wgsl"),
|
|
||||||
);
|
|
||||||
|
|
||||||
const fullscreen_quad_fs_module = core.device.createShaderModuleWGSL(
|
|
||||||
"fullscreen_textured_quad.wgsl",
|
|
||||||
@embedFile("fullscreen_textured_quad.wgsl"),
|
|
||||||
);
|
|
||||||
|
|
||||||
const blend = gpu.BlendState{};
|
|
||||||
const color_target = gpu.ColorTargetState{
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
.blend = &blend,
|
|
||||||
.write_mask = gpu.ColorWriteMaskFlags.all,
|
|
||||||
};
|
|
||||||
|
|
||||||
const fragment_state = gpu.FragmentState.init(.{
|
|
||||||
.module = fullscreen_quad_fs_module,
|
|
||||||
.entry_point = "frag_main",
|
|
||||||
.targets = &.{color_target},
|
|
||||||
});
|
|
||||||
|
|
||||||
const fullscreen_quad_pipeline_descriptor = gpu.RenderPipeline.Descriptor{
|
|
||||||
.fragment = &fragment_state,
|
|
||||||
.vertex = .{
|
|
||||||
.module = fullscreen_quad_vs_module,
|
|
||||||
.entry_point = "vert_main",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const fullscreen_quad_pipeline = core.device.createRenderPipeline(&fullscreen_quad_pipeline_descriptor);
|
|
||||||
fullscreen_quad_vs_module.release();
|
|
||||||
fullscreen_quad_fs_module.release();
|
|
||||||
|
|
||||||
const sampler = core.device.createSampler(&.{
|
|
||||||
.mag_filter = .linear,
|
|
||||||
.min_filter = .linear,
|
|
||||||
});
|
|
||||||
|
|
||||||
var img = try zigimg.Image.fromMemory(allocator, assets.gotta_go_fast_png);
|
|
||||||
defer img.deinit();
|
|
||||||
|
|
||||||
const img_size = gpu.Extent3D{ .width = @as(u32, @intCast(img.width)), .height = @as(u32, @intCast(img.height)) };
|
|
||||||
|
|
||||||
const cube_texture = core.device.createTexture(&.{
|
|
||||||
.size = img_size,
|
|
||||||
.format = .rgba8_unorm,
|
|
||||||
.usage = .{
|
|
||||||
.texture_binding = true,
|
|
||||||
.copy_dst = true,
|
|
||||||
.render_attachment = true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const data_layout = gpu.Texture.DataLayout{
|
|
||||||
.bytes_per_row = @as(u32, @intCast(img.width * 4)),
|
|
||||||
.rows_per_image = @as(u32, @intCast(img.height)),
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (img.pixels) {
|
|
||||||
.rgba32 => |pixels| queue.writeTexture(&.{ .texture = cube_texture }, &data_layout, &img_size, pixels),
|
|
||||||
.rgb24 => |pixels| {
|
|
||||||
const data = try rgb24ToRgba32(allocator, pixels);
|
|
||||||
defer data.deinit(allocator);
|
|
||||||
queue.writeTexture(&.{ .texture = cube_texture }, &data_layout, &img_size, data.rgba32);
|
|
||||||
},
|
|
||||||
else => @panic("unsupported image color format"),
|
|
||||||
}
|
|
||||||
|
|
||||||
var textures: [2]*gpu.Texture = undefined;
|
|
||||||
for (textures, 0..) |_, i| {
|
|
||||||
textures[i] = core.device.createTexture(&.{
|
|
||||||
.size = img_size,
|
|
||||||
.format = .rgba8_unorm,
|
|
||||||
.usage = .{
|
|
||||||
.storage_binding = true,
|
|
||||||
.texture_binding = true,
|
|
||||||
.copy_dst = true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// the shader blurs the input texture in one direction,
|
|
||||||
// depending on whether flip value is 0 or 1
|
|
||||||
var flip: [2]*gpu.Buffer = undefined;
|
|
||||||
for (flip, 0..) |_, i| {
|
|
||||||
const buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .uniform = true },
|
|
||||||
.size = @sizeOf(u32),
|
|
||||||
.mapped_at_creation = .true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const buffer_mapped = buffer.getMappedRange(u32, 0, 1);
|
|
||||||
buffer_mapped.?[0] = @as(u32, @intCast(i));
|
|
||||||
buffer.unmap();
|
|
||||||
|
|
||||||
flip[i] = buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
const blur_params_buffer = core.device.createBuffer(&.{
|
|
||||||
.size = 8,
|
|
||||||
.usage = .{ .copy_dst = true, .uniform = true },
|
|
||||||
});
|
|
||||||
|
|
||||||
const blur_bind_group_layout0 = blur_pipeline.getBindGroupLayout(0);
|
|
||||||
const blur_bind_group_layout1 = blur_pipeline.getBindGroupLayout(1);
|
|
||||||
const fullscreen_bind_group_layout = fullscreen_quad_pipeline.getBindGroupLayout(0);
|
|
||||||
const cube_texture_view = cube_texture.createView(&gpu.TextureView.Descriptor{});
|
|
||||||
const texture0_view = textures[0].createView(&gpu.TextureView.Descriptor{});
|
|
||||||
const texture1_view = textures[1].createView(&gpu.TextureView.Descriptor{});
|
|
||||||
|
|
||||||
const compute_constants = core.device.createBindGroup(&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = blur_bind_group_layout0,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.sampler(0, sampler),
|
|
||||||
gpu.BindGroup.Entry.buffer(1, blur_params_buffer, 0, 8),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const compute_bind_group_0 = core.device.createBindGroup(&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = blur_bind_group_layout1,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.textureView(1, cube_texture_view),
|
|
||||||
gpu.BindGroup.Entry.textureView(2, texture0_view),
|
|
||||||
gpu.BindGroup.Entry.buffer(3, flip[0], 0, 4),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const compute_bind_group_1 = core.device.createBindGroup(&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = blur_bind_group_layout1,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.textureView(1, texture0_view),
|
|
||||||
gpu.BindGroup.Entry.textureView(2, texture1_view),
|
|
||||||
gpu.BindGroup.Entry.buffer(3, flip[1], 0, 4),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const compute_bind_group_2 = core.device.createBindGroup(&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = blur_bind_group_layout1,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.textureView(1, texture1_view),
|
|
||||||
gpu.BindGroup.Entry.textureView(2, texture0_view),
|
|
||||||
gpu.BindGroup.Entry.buffer(3, flip[0], 0, 4),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const show_result_bind_group = core.device.createBindGroup(&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = fullscreen_bind_group_layout,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.sampler(0, sampler),
|
|
||||||
gpu.BindGroup.Entry.textureView(1, texture1_view),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
blur_bind_group_layout0.release();
|
|
||||||
blur_bind_group_layout1.release();
|
|
||||||
fullscreen_bind_group_layout.release();
|
|
||||||
sampler.release();
|
|
||||||
flip[0].release();
|
|
||||||
flip[1].release();
|
|
||||||
cube_texture_view.release();
|
|
||||||
texture0_view.release();
|
|
||||||
texture1_view.release();
|
|
||||||
|
|
||||||
const blur_params_buffer_data = [_]u32{ filter_size, block_dimension };
|
|
||||||
queue.writeBuffer(blur_params_buffer, 0, &blur_params_buffer_data);
|
|
||||||
|
|
||||||
app.title_timer = try core.Timer.start();
|
|
||||||
app.blur_pipeline = blur_pipeline;
|
|
||||||
app.fullscreen_quad_pipeline = fullscreen_quad_pipeline;
|
|
||||||
app.cube_texture = cube_texture;
|
|
||||||
app.textures = textures;
|
|
||||||
app.blur_params_buffer = blur_params_buffer;
|
|
||||||
app.compute_constants = compute_constants;
|
|
||||||
app.compute_bind_group_0 = compute_bind_group_0;
|
|
||||||
app.compute_bind_group_1 = compute_bind_group_1;
|
|
||||||
app.compute_bind_group_2 = compute_bind_group_2;
|
|
||||||
app.show_result_bind_group = show_result_bind_group;
|
|
||||||
app.img_size = img_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
defer _ = gpa.deinit();
|
|
||||||
defer core.deinit();
|
|
||||||
|
|
||||||
app.blur_pipeline.release();
|
|
||||||
app.fullscreen_quad_pipeline.release();
|
|
||||||
app.cube_texture.release();
|
|
||||||
app.textures[0].release();
|
|
||||||
app.textures[1].release();
|
|
||||||
app.blur_params_buffer.release();
|
|
||||||
app.compute_constants.release();
|
|
||||||
app.compute_bind_group_0.release();
|
|
||||||
app.compute_bind_group_1.release();
|
|
||||||
app.compute_bind_group_2.release();
|
|
||||||
app.show_result_bind_group.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(app: *App) !bool {
|
|
||||||
var iter = core.pollEvents();
|
|
||||||
while (iter.next()) |event| {
|
|
||||||
if (event == .close) return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
|
|
||||||
const compute_pass = encoder.beginComputePass(null);
|
|
||||||
compute_pass.setPipeline(app.blur_pipeline);
|
|
||||||
compute_pass.setBindGroup(0, app.compute_constants, &.{});
|
|
||||||
|
|
||||||
const width: u32 = @as(u32, @intCast(app.img_size.width));
|
|
||||||
const height: u32 = @as(u32, @intCast(app.img_size.height));
|
|
||||||
compute_pass.setBindGroup(1, app.compute_bind_group_0, &.{});
|
|
||||||
compute_pass.dispatchWorkgroups(try std.math.divCeil(u32, width, block_dimension), try std.math.divCeil(u32, height, batch[1]), 1);
|
|
||||||
|
|
||||||
compute_pass.setBindGroup(1, app.compute_bind_group_1, &.{});
|
|
||||||
compute_pass.dispatchWorkgroups(try std.math.divCeil(u32, height, block_dimension), try std.math.divCeil(u32, width, batch[1]), 1);
|
|
||||||
|
|
||||||
var i: u32 = 0;
|
|
||||||
while (i < iterations - 1) : (i += 1) {
|
|
||||||
compute_pass.setBindGroup(1, app.compute_bind_group_2, &.{});
|
|
||||||
compute_pass.dispatchWorkgroups(try std.math.divCeil(u32, width, block_dimension), try std.math.divCeil(u32, height, batch[1]), 1);
|
|
||||||
|
|
||||||
compute_pass.setBindGroup(1, app.compute_bind_group_1, &.{});
|
|
||||||
compute_pass.dispatchWorkgroups(try std.math.divCeil(u32, height, block_dimension), try std.math.divCeil(u32, width, batch[1]), 1);
|
|
||||||
}
|
|
||||||
compute_pass.end();
|
|
||||||
compute_pass.release();
|
|
||||||
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = back_buffer_view,
|
|
||||||
.clear_value = std.mem.zeroes(gpu.Color),
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
|
|
||||||
const render_pass_descriptor = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{color_attachment},
|
|
||||||
});
|
|
||||||
|
|
||||||
const render_pass = encoder.beginRenderPass(&render_pass_descriptor);
|
|
||||||
render_pass.setPipeline(app.fullscreen_quad_pipeline);
|
|
||||||
render_pass.setBindGroup(0, app.show_result_bind_group, &.{});
|
|
||||||
render_pass.draw(6, 1, 0, 0);
|
|
||||||
render_pass.end();
|
|
||||||
render_pass.release();
|
|
||||||
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
encoder.release();
|
|
||||||
const queue = core.queue;
|
|
||||||
queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
command.release();
|
|
||||||
core.swap_chain.present();
|
|
||||||
back_buffer_view.release();
|
|
||||||
|
|
||||||
// update the window title every second
|
|
||||||
if (app.title_timer.read() >= 1.0) {
|
|
||||||
app.title_timer.reset();
|
|
||||||
try core.printTitle("Image Blur [ {d}fps ] [ Input {d}hz ]", .{
|
|
||||||
core.frameRate(),
|
|
||||||
core.inputRate(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rgb24ToRgba32(allocator: std.mem.Allocator, in: []zigimg.color.Rgb24) !zigimg.color.PixelStorage {
|
|
||||||
const out = try zigimg.color.PixelStorage.init(allocator, .rgba32, in.len);
|
|
||||||
var i: usize = 0;
|
|
||||||
while (i < in.len) : (i += 1) {
|
|
||||||
out.rgba32[i] = zigimg.color.Rgba32{ .r = in[i].r, .g = in[i].g, .b = in[i].b, .a = 255 };
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
@group(0) @binding(0) var mySampler : sampler;
|
|
||||||
@group(0) @binding(1) var myTexture : texture_2d<f32>;
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) Position : vec4<f32>,
|
|
||||||
@location(0) fragUV : vec2<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
@vertex
|
|
||||||
fn vert_main(@builtin(vertex_index) VertexIndex : u32) -> VertexOutput {
|
|
||||||
// Draw a fullscreen quad using two triangles, with UV coordinates (normalized pixel coordinates)
|
|
||||||
// that would have the full texture be displayed.
|
|
||||||
var pos = array<vec2<f32>, 6>(
|
|
||||||
vec2<f32>( 1.0, 1.0), // right, top
|
|
||||||
vec2<f32>( 1.0, -1.0), // right, bottom
|
|
||||||
vec2<f32>(-1.0, -1.0), // left, bottom
|
|
||||||
vec2<f32>( 1.0, 1.0), // right, top
|
|
||||||
vec2<f32>(-1.0, -1.0), // left, bottom
|
|
||||||
vec2<f32>(-1.0, 1.0) // left, top
|
|
||||||
);
|
|
||||||
var uv = array<vec2<f32>, 6>(
|
|
||||||
vec2<f32>(1.0, 0.0),
|
|
||||||
vec2<f32>(1.0, 1.0),
|
|
||||||
vec2<f32>(0.0, 1.0),
|
|
||||||
vec2<f32>(1.0, 0.0),
|
|
||||||
vec2<f32>(0.0, 1.0),
|
|
||||||
vec2<f32>(0.0, 0.0)
|
|
||||||
);
|
|
||||||
|
|
||||||
var output : VertexOutput;
|
|
||||||
output.Position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
|
|
||||||
output.fragUV = uv[VertexIndex];
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
@fragment
|
|
||||||
fn frag_main(@location(0) fragUV : vec2<f32>) -> @location(0) vec4<f32> {
|
|
||||||
return textureSample(myTexture, mySampler, fragUV);
|
|
||||||
}
|
|
||||||
|
|
@ -1,187 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
const zigimg = @import("zigimg");
|
|
||||||
const assets = @import("assets");
|
|
||||||
|
|
||||||
title_timer: core.Timer,
|
|
||||||
pipeline: *gpu.RenderPipeline,
|
|
||||||
texture: *gpu.Texture,
|
|
||||||
bind_group: *gpu.BindGroup,
|
|
||||||
img_size: gpu.Extent3D,
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{});
|
|
||||||
const allocator = gpa.allocator();
|
|
||||||
|
|
||||||
// Load our shader that will render a fullscreen textured quad using two triangles, needed to
|
|
||||||
// get the image on screen.
|
|
||||||
const fullscreen_quad_vs_module = core.device.createShaderModuleWGSL(
|
|
||||||
"fullscreen_textured_quad.wgsl",
|
|
||||||
@embedFile("fullscreen_textured_quad.wgsl"),
|
|
||||||
);
|
|
||||||
defer fullscreen_quad_vs_module.release();
|
|
||||||
const fullscreen_quad_fs_module = core.device.createShaderModuleWGSL(
|
|
||||||
"fullscreen_textured_quad.wgsl",
|
|
||||||
@embedFile("fullscreen_textured_quad.wgsl"),
|
|
||||||
);
|
|
||||||
defer fullscreen_quad_fs_module.release();
|
|
||||||
|
|
||||||
// Create our render pipeline
|
|
||||||
const blend = gpu.BlendState{};
|
|
||||||
const color_target = gpu.ColorTargetState{
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
.blend = &blend,
|
|
||||||
.write_mask = gpu.ColorWriteMaskFlags.all,
|
|
||||||
};
|
|
||||||
const fragment_state = gpu.FragmentState.init(.{
|
|
||||||
.module = fullscreen_quad_fs_module,
|
|
||||||
.entry_point = "frag_main",
|
|
||||||
.targets = &.{color_target},
|
|
||||||
});
|
|
||||||
const pipeline_descriptor = gpu.RenderPipeline.Descriptor{
|
|
||||||
.fragment = &fragment_state,
|
|
||||||
.vertex = .{
|
|
||||||
.module = fullscreen_quad_vs_module,
|
|
||||||
.entry_point = "vert_main",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const pipeline = core.device.createRenderPipeline(&pipeline_descriptor);
|
|
||||||
|
|
||||||
// Create a texture sampler. This determines what happens when the texture doesn't match the
|
|
||||||
// dimensions of the screen it's being displayed on. If the image needs to be magnified or
|
|
||||||
// minified to fit, it can be linearly interpolated (i.e. 'blurred', .linear) or the nearest
|
|
||||||
// pixel may be used (i.e. 'pixelated', .nearest)
|
|
||||||
const sampler = core.device.createSampler(&.{
|
|
||||||
.mag_filter = .linear,
|
|
||||||
.min_filter = .linear,
|
|
||||||
});
|
|
||||||
defer sampler.release();
|
|
||||||
|
|
||||||
// Load the pixels of the image
|
|
||||||
var img = try zigimg.Image.fromMemory(allocator, assets.gotta_go_fast_png);
|
|
||||||
defer img.deinit();
|
|
||||||
const img_size = gpu.Extent3D{ .width = @as(u32, @intCast(img.width)), .height = @as(u32, @intCast(img.height)) };
|
|
||||||
|
|
||||||
// Create a texture
|
|
||||||
const texture = core.device.createTexture(&.{
|
|
||||||
.size = img_size,
|
|
||||||
.format = .rgba8_unorm,
|
|
||||||
.usage = .{
|
|
||||||
.texture_binding = true,
|
|
||||||
.copy_dst = true,
|
|
||||||
.render_attachment = true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Upload the pixels (from the CPU) to the GPU. You could e.g. do this once per frame if you
|
|
||||||
// wanted the image to be updated dynamically.
|
|
||||||
const data_layout = gpu.Texture.DataLayout{
|
|
||||||
.bytes_per_row = @as(u32, @intCast(img.width * 4)),
|
|
||||||
.rows_per_image = @as(u32, @intCast(img.height)),
|
|
||||||
};
|
|
||||||
switch (img.pixels) {
|
|
||||||
.rgba32 => |pixels| core.queue.writeTexture(&.{ .texture = texture }, &data_layout, &img_size, pixels),
|
|
||||||
.rgb24 => |pixels| {
|
|
||||||
const data = try rgb24ToRgba32(allocator, pixels);
|
|
||||||
defer data.deinit(allocator);
|
|
||||||
core.queue.writeTexture(&.{ .texture = texture }, &data_layout, &img_size, data.rgba32);
|
|
||||||
},
|
|
||||||
else => @panic("unsupported image color format"),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Describe which data we will pass to our shader (GPU program)
|
|
||||||
const bind_group_layout = pipeline.getBindGroupLayout(0);
|
|
||||||
defer bind_group_layout.release();
|
|
||||||
const texture_view = texture.createView(&gpu.TextureView.Descriptor{});
|
|
||||||
defer texture_view.release();
|
|
||||||
const bind_group = core.device.createBindGroup(&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = bind_group_layout,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.sampler(0, sampler),
|
|
||||||
gpu.BindGroup.Entry.textureView(1, texture_view),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
app.* = .{
|
|
||||||
.title_timer = try core.Timer.start(),
|
|
||||||
.pipeline = pipeline,
|
|
||||||
.texture = texture,
|
|
||||||
.bind_group = bind_group,
|
|
||||||
.img_size = img_size,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
defer _ = gpa.deinit();
|
|
||||||
defer core.deinit();
|
|
||||||
app.pipeline.release();
|
|
||||||
app.texture.release();
|
|
||||||
app.bind_group.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(app: *App) !bool {
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
defer back_buffer_view.release();
|
|
||||||
|
|
||||||
// Poll for events (keyboard input, etc.)
|
|
||||||
var iter = core.pollEvents();
|
|
||||||
while (iter.next()) |event| {
|
|
||||||
if (event == .close) return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
defer encoder.release();
|
|
||||||
|
|
||||||
// Begin our render pass by clearing the pixels that were on the screen from the previous frame.
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = back_buffer_view,
|
|
||||||
.clear_value = std.mem.zeroes(gpu.Color),
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
const render_pass_descriptor = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{color_attachment},
|
|
||||||
});
|
|
||||||
const render_pass = encoder.beginRenderPass(&render_pass_descriptor);
|
|
||||||
defer render_pass.release();
|
|
||||||
|
|
||||||
// Render using our pipeline
|
|
||||||
render_pass.setPipeline(app.pipeline);
|
|
||||||
render_pass.setBindGroup(0, app.bind_group, &.{});
|
|
||||||
render_pass.draw(6, 1, 0, 0); // Tell the GPU to draw 6 vertices, one object
|
|
||||||
render_pass.end();
|
|
||||||
|
|
||||||
// Submit all the commands to the GPU and render the frame.
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
defer command.release();
|
|
||||||
core.queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
core.swap_chain.present();
|
|
||||||
|
|
||||||
// update the window title every second to have the FPS
|
|
||||||
if (app.title_timer.read() >= 1.0) {
|
|
||||||
app.title_timer.reset();
|
|
||||||
try core.printTitle("Image [ {d}fps ] [ Input {d}hz ]", .{
|
|
||||||
core.frameRate(),
|
|
||||||
core.inputRate(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rgb24ToRgba32(allocator: std.mem.Allocator, in: []zigimg.color.Rgb24) !zigimg.color.PixelStorage {
|
|
||||||
const out = try zigimg.color.PixelStorage.init(allocator, .rgba32, in.len);
|
|
||||||
var i: usize = 0;
|
|
||||||
while (i < in.len) : (i += 1) {
|
|
||||||
out.rgba32[i] = zigimg.color.Rgba32{ .r = in[i].r, .g = in[i].g, .b = in[i].b, .a = 255 };
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
pub const Vertex = extern struct {
|
|
||||||
pos: @Vector(4, f32),
|
|
||||||
col: @Vector(4, f32),
|
|
||||||
uv: @Vector(2, f32),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const vertices = [_]Vertex{
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
};
|
|
||||||
|
|
@ -1,208 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
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 gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
|
|
||||||
title_timer: core.Timer,
|
|
||||||
|
|
||||||
timer: core.Timer,
|
|
||||||
pipeline: *gpu.RenderPipeline,
|
|
||||||
vertex_buffer: *gpu.Buffer,
|
|
||||||
uniform_buffer: *gpu.Buffer,
|
|
||||||
bind_group: *gpu.BindGroup,
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{});
|
|
||||||
app.timer = try core.Timer.start();
|
|
||||||
|
|
||||||
const shader_module = core.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.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.init(.{
|
|
||||||
.array_stride = @sizeOf(Vertex),
|
|
||||||
.step_mode = .vertex,
|
|
||||||
.attributes = &vertex_attributes,
|
|
||||||
});
|
|
||||||
|
|
||||||
const color_target = gpu.ColorTargetState{
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
.write_mask = gpu.ColorWriteMaskFlags.all,
|
|
||||||
};
|
|
||||||
const fragment = gpu.FragmentState.init(.{
|
|
||||||
.module = shader_module,
|
|
||||||
.entry_point = "frag_main",
|
|
||||||
.targets = &.{color_target},
|
|
||||||
});
|
|
||||||
|
|
||||||
const bgle = gpu.BindGroupLayout.Entry.buffer(0, .{ .vertex = true }, .uniform, true, 0);
|
|
||||||
const bgl = core.device.createBindGroupLayout(
|
|
||||||
&gpu.BindGroupLayout.Descriptor.init(.{
|
|
||||||
.entries = &.{bgle},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
const bind_group_layouts = [_]*gpu.BindGroupLayout{bgl};
|
|
||||||
const pipeline_layout = core.device.createPipelineLayout(&gpu.PipelineLayout.Descriptor.init(.{
|
|
||||||
.bind_group_layouts = &bind_group_layouts,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const pipeline_descriptor = gpu.RenderPipeline.Descriptor{
|
|
||||||
.fragment = &fragment,
|
|
||||||
.layout = pipeline_layout,
|
|
||||||
.vertex = gpu.VertexState.init(.{
|
|
||||||
.module = shader_module,
|
|
||||||
.entry_point = "vertex_main",
|
|
||||||
.buffers = &.{vertex_buffer_layout},
|
|
||||||
}),
|
|
||||||
.primitive = .{
|
|
||||||
.cull_mode = .back,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const vertex_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .vertex = true },
|
|
||||||
.size = @sizeOf(Vertex) * vertices.len,
|
|
||||||
.mapped_at_creation = .true,
|
|
||||||
});
|
|
||||||
const vertex_mapped = vertex_buffer.getMappedRange(Vertex, 0, vertices.len);
|
|
||||||
@memcpy(vertex_mapped.?, vertices[0..]);
|
|
||||||
vertex_buffer.unmap();
|
|
||||||
|
|
||||||
const x_count = 4;
|
|
||||||
const y_count = 4;
|
|
||||||
const num_instances = x_count * y_count;
|
|
||||||
|
|
||||||
const uniform_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .copy_dst = true, .uniform = true },
|
|
||||||
.size = @sizeOf(UniformBufferObject) * num_instances,
|
|
||||||
.mapped_at_creation = .false,
|
|
||||||
});
|
|
||||||
const bind_group = core.device.createBindGroup(
|
|
||||||
&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = bgl,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.buffer(0, uniform_buffer, 0, @sizeOf(UniformBufferObject) * num_instances),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
app.title_timer = try core.Timer.start();
|
|
||||||
app.pipeline = core.device.createRenderPipeline(&pipeline_descriptor);
|
|
||||||
app.vertex_buffer = vertex_buffer;
|
|
||||||
app.uniform_buffer = uniform_buffer;
|
|
||||||
app.bind_group = bind_group;
|
|
||||||
|
|
||||||
shader_module.release();
|
|
||||||
pipeline_layout.release();
|
|
||||||
bgl.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
defer _ = gpa.deinit();
|
|
||||||
defer core.deinit();
|
|
||||||
|
|
||||||
app.pipeline.release();
|
|
||||||
app.vertex_buffer.release();
|
|
||||||
app.bind_group.release();
|
|
||||||
app.uniform_buffer.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(app: *App) !bool {
|
|
||||||
var iter = core.pollEvents();
|
|
||||||
while (iter.next()) |event| {
|
|
||||||
switch (event) {
|
|
||||||
.key_press => |ev| {
|
|
||||||
if (ev.key == .space) return true;
|
|
||||||
},
|
|
||||||
.close => return true,
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = back_buffer_view,
|
|
||||||
.clear_value = std.mem.zeroes(gpu.Color),
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
const render_pass_info = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{color_attachment},
|
|
||||||
});
|
|
||||||
|
|
||||||
{
|
|
||||||
const proj = zm.perspectiveFovRh(
|
|
||||||
(std.math.pi / 3.0),
|
|
||||||
@as(f32, @floatFromInt(core.descriptor.width)) / @as(f32, @floatFromInt(core.descriptor.height)),
|
|
||||||
10,
|
|
||||||
30,
|
|
||||||
);
|
|
||||||
|
|
||||||
var ubos: [16]UniformBufferObject = undefined;
|
|
||||||
const time = app.timer.read();
|
|
||||||
const step: f32 = 4.0;
|
|
||||||
var m: u8 = 0;
|
|
||||||
var x: u8 = 0;
|
|
||||||
while (x < 4) : (x += 1) {
|
|
||||||
var y: u8 = 0;
|
|
||||||
while (y < 4) : (y += 1) {
|
|
||||||
const trans = zm.translation(step * (@as(f32, @floatFromInt(x)) - 2.0 + 0.5), step * (@as(f32, @floatFromInt(y)) - 2.0 + 0.5), -20);
|
|
||||||
const localTime = time + @as(f32, @floatFromInt(m)) * 0.5;
|
|
||||||
const model = zm.mul(zm.mul(zm.mul(zm.rotationX(localTime * (std.math.pi / 2.1)), zm.rotationY(localTime * (std.math.pi / 0.9))), zm.rotationZ(localTime * (std.math.pi / 1.3))), trans);
|
|
||||||
const mvp = zm.mul(model, proj);
|
|
||||||
const ubo = UniformBufferObject{
|
|
||||||
.mat = mvp,
|
|
||||||
};
|
|
||||||
ubos[m] = ubo;
|
|
||||||
m += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
encoder.writeBuffer(app.uniform_buffer, 0, &ubos);
|
|
||||||
}
|
|
||||||
|
|
||||||
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_group, &.{0});
|
|
||||||
pass.draw(vertices.len, 16, 0, 0);
|
|
||||||
pass.end();
|
|
||||||
pass.release();
|
|
||||||
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
encoder.release();
|
|
||||||
|
|
||||||
const queue = core.queue;
|
|
||||||
queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
command.release();
|
|
||||||
core.swap_chain.present();
|
|
||||||
back_buffer_view.release();
|
|
||||||
|
|
||||||
// update the window title every second
|
|
||||||
if (app.title_timer.read() >= 1.0) {
|
|
||||||
app.title_timer.reset();
|
|
||||||
try core.printTitle("Instanced Cube [ {d}fps ] [ Input {d}hz ]", .{
|
|
||||||
core.frameRate(),
|
|
||||||
core.inputRate(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
@binding(0) @group(0) var<uniform> ubos : array<mat4x4<f32>, 16>;
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) position_clip : vec4<f32>,
|
|
||||||
@location(0) fragUV : vec2<f32>,
|
|
||||||
@location(1) fragPosition: vec4<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
@vertex
|
|
||||||
fn vertex_main(@builtin(instance_index) instanceIdx : u32,
|
|
||||||
@location(0) position : vec4<f32>,
|
|
||||||
@location(1) uv : vec2<f32>) -> VertexOutput {
|
|
||||||
var output : VertexOutput;
|
|
||||||
output.position_clip = ubos[instanceIdx] * position;
|
|
||||||
output.fragUV = uv;
|
|
||||||
output.fragPosition = 0.5 * (position + vec4<f32>(1.0, 1.0, 1.0, 1.0));
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
@fragment fn frag_main(
|
|
||||||
@location(0) fragUV: vec2<f32>,
|
|
||||||
@location(1) fragPosition: vec4<f32>
|
|
||||||
) -> @location(0) vec4<f32> {
|
|
||||||
return fragPosition;
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
@group(0) @binding(0) var<storage, read_write> output: array<f32>;
|
|
||||||
|
|
||||||
@compute @workgroup_size(64, 1, 1)
|
|
||||||
fn main(
|
|
||||||
@builtin(global_invocation_id)
|
|
||||||
global_id : vec3<u32>,
|
|
||||||
|
|
||||||
@builtin(local_invocation_id)
|
|
||||||
local_id : vec3<u32>,
|
|
||||||
) {
|
|
||||||
if (global_id.x >= arrayLength(&output)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
output[global_id.x] =
|
|
||||||
f32(global_id.x) * 1000. + f32(local_id.x);
|
|
||||||
}
|
|
||||||
|
|
@ -1,103 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
|
|
||||||
const workgroup_size = 64;
|
|
||||||
const buffer_size = 1000;
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{});
|
|
||||||
app.* = .{};
|
|
||||||
|
|
||||||
const output = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .storage = true, .copy_src = true },
|
|
||||||
.size = buffer_size * @sizeOf(f32),
|
|
||||||
.mapped_at_creation = .false,
|
|
||||||
});
|
|
||||||
defer output.release();
|
|
||||||
|
|
||||||
const staging = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .map_read = true, .copy_dst = true },
|
|
||||||
.size = buffer_size * @sizeOf(f32),
|
|
||||||
.mapped_at_creation = .false,
|
|
||||||
});
|
|
||||||
defer staging.release();
|
|
||||||
|
|
||||||
const compute_module = core.device.createShaderModuleWGSL("main.wgsl", @embedFile("main.wgsl"));
|
|
||||||
|
|
||||||
const compute_pipeline = core.device.createComputePipeline(&gpu.ComputePipeline.Descriptor{ .compute = gpu.ProgrammableStageDescriptor{
|
|
||||||
.module = compute_module,
|
|
||||||
.entry_point = "main",
|
|
||||||
} });
|
|
||||||
defer compute_pipeline.release();
|
|
||||||
|
|
||||||
const layout = compute_pipeline.getBindGroupLayout(0);
|
|
||||||
defer layout.release();
|
|
||||||
|
|
||||||
const compute_bind_group = core.device.createBindGroup(&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = layout,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.buffer(0, output, 0, buffer_size * @sizeOf(f32)),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
defer compute_bind_group.release();
|
|
||||||
|
|
||||||
compute_module.release();
|
|
||||||
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
|
|
||||||
const compute_pass = encoder.beginComputePass(null);
|
|
||||||
compute_pass.setPipeline(compute_pipeline);
|
|
||||||
compute_pass.setBindGroup(0, compute_bind_group, &.{});
|
|
||||||
compute_pass.dispatchWorkgroups(try std.math.divCeil(u32, buffer_size, workgroup_size), 1, 1);
|
|
||||||
compute_pass.end();
|
|
||||||
compute_pass.release();
|
|
||||||
|
|
||||||
encoder.copyBufferToBuffer(output, 0, staging, 0, buffer_size * @sizeOf(f32));
|
|
||||||
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
encoder.release();
|
|
||||||
|
|
||||||
var response: gpu.Buffer.MapAsyncStatus = undefined;
|
|
||||||
const callback = (struct {
|
|
||||||
pub inline fn callback(ctx: *gpu.Buffer.MapAsyncStatus, status: gpu.Buffer.MapAsyncStatus) void {
|
|
||||||
ctx.* = status;
|
|
||||||
}
|
|
||||||
}).callback;
|
|
||||||
|
|
||||||
var queue = core.queue;
|
|
||||||
queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
command.release();
|
|
||||||
|
|
||||||
staging.mapAsync(.{ .read = true }, 0, buffer_size * @sizeOf(f32), &response, callback);
|
|
||||||
while (true) {
|
|
||||||
if (response == gpu.Buffer.MapAsyncStatus.success) {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
core.device.tick();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const staging_mapped = staging.getConstMappedRange(f32, 0, buffer_size);
|
|
||||||
for (staging_mapped.?) |v| {
|
|
||||||
std.debug.print("{d} ", .{v});
|
|
||||||
}
|
|
||||||
std.debug.print("\n", .{});
|
|
||||||
staging.unmap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
_ = app;
|
|
||||||
defer _ = gpa.deinit();
|
|
||||||
core.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(_: *App) !bool {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
pub const Vertex = extern struct {
|
|
||||||
pos: @Vector(3, f32),
|
|
||||||
normal: @Vector(3, f32),
|
|
||||||
uv: @Vector(2, f32),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const vertices = [_]Vertex{
|
|
||||||
.{ .pos = .{ 1, -1, 1 }, .normal = .{ 0, -1, 0 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1 }, .normal = .{ 0, -1, 0 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1 }, .normal = .{ 0, -1, 0 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1 }, .normal = .{ 0, -1, 0 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1 }, .normal = .{ 0, -1, 0 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1 }, .normal = .{ 0, -1, 0 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, 1, 1 }, .normal = .{ 1, 0, 0 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1 }, .normal = .{ 1, 0, 0 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1 }, .normal = .{ 1, 0, 0 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1 }, .normal = .{ 1, 0, 0 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1 }, .normal = .{ 1, 0, 0 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1 }, .normal = .{ 1, 0, 0 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ -1, 1, 1 }, .normal = .{ 0, 1, 0 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1 }, .normal = .{ 0, 1, 0 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1 }, .normal = .{ 0, 1, 0 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1 }, .normal = .{ 0, 1, 0 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1 }, .normal = .{ 0, 1, 0 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1 }, .normal = .{ 0, 1, 0 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ -1, -1, 1 }, .normal = .{ -1, 0, 0 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1 }, .normal = .{ -1, 0, 0 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1 }, .normal = .{ -1, 0, 0 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1 }, .normal = .{ -1, 0, 0 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1 }, .normal = .{ -1, 0, 0 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1 }, .normal = .{ -1, 0, 0 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, 1, 1 }, .normal = .{ 0, 0, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1 }, .normal = .{ 0, 0, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1 }, .normal = .{ 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1 }, .normal = .{ 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1 }, .normal = .{ 0, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1 }, .normal = .{ 0, 0, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, -1, -1 }, .normal = .{ 0, 0, -1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1 }, .normal = .{ 0, 0, -1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1 }, .normal = .{ 0, 0, -1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1 }, .normal = .{ 0, 0, -1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1 }, .normal = .{ 0, 0, -1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1 }, .normal = .{ 0, 0, -1 }, .uv = .{ 0, 0 } },
|
|
||||||
};
|
|
||||||
|
|
@ -1,464 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
const zm = @import("zmath");
|
|
||||||
|
|
||||||
const Vertex = @import("cube_mesh.zig").Vertex;
|
|
||||||
const vertices = @import("cube_mesh.zig").vertices;
|
|
||||||
const Quad = @import("quad_mesh.zig").Quad;
|
|
||||||
const quad = @import("quad_mesh.zig").quad;
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
const pixel_size = 8;
|
|
||||||
|
|
||||||
const UniformBufferObject = struct {
|
|
||||||
mat: zm.Mat,
|
|
||||||
};
|
|
||||||
const PostUniformBufferObject = extern struct {
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
pixel_size: u32 = pixel_size,
|
|
||||||
};
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
|
|
||||||
title_timer: core.Timer,
|
|
||||||
timer: core.Timer,
|
|
||||||
|
|
||||||
pipeline: *gpu.RenderPipeline,
|
|
||||||
normal_pipeline: *gpu.RenderPipeline,
|
|
||||||
vertex_buffer: *gpu.Buffer,
|
|
||||||
uniform_buffer: *gpu.Buffer,
|
|
||||||
bind_group: *gpu.BindGroup,
|
|
||||||
|
|
||||||
post_pipeline: *gpu.RenderPipeline,
|
|
||||||
post_vertex_buffer: *gpu.Buffer,
|
|
||||||
post_uniform_buffer: *gpu.Buffer,
|
|
||||||
post_bind_group: *gpu.BindGroup,
|
|
||||||
|
|
||||||
draw_texture_view: *gpu.TextureView,
|
|
||||||
depth_texture_view: *gpu.TextureView,
|
|
||||||
normal_texture_view: *gpu.TextureView,
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{});
|
|
||||||
app.title_timer = try core.Timer.start();
|
|
||||||
app.timer = try core.Timer.start();
|
|
||||||
|
|
||||||
try app.createRenderTextures();
|
|
||||||
app.createDrawPipeline();
|
|
||||||
app.createPostPipeline();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
defer _ = gpa.deinit();
|
|
||||||
defer core.deinit();
|
|
||||||
|
|
||||||
app.cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(app: *App) !bool {
|
|
||||||
var iter = core.pollEvents();
|
|
||||||
while (iter.next()) |event| {
|
|
||||||
switch (event) {
|
|
||||||
.key_press => |ev| {
|
|
||||||
if (ev.key == .space) return true;
|
|
||||||
},
|
|
||||||
.close => return true,
|
|
||||||
.framebuffer_resize => {
|
|
||||||
app.cleanup();
|
|
||||||
try app.createRenderTextures();
|
|
||||||
app.createDrawPipeline();
|
|
||||||
app.createPostPipeline();
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const size = core.size();
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
encoder.writeBuffer(app.post_uniform_buffer, 0, &[_]PostUniformBufferObject{
|
|
||||||
PostUniformBufferObject{
|
|
||||||
.width = size.width,
|
|
||||||
.height = size.height,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
{
|
|
||||||
const time = app.timer.read() * 0.5;
|
|
||||||
const model = zm.mul(zm.rotationX(time * (std.math.pi / 2.0)), zm.rotationZ(time * (std.math.pi / 2.0)));
|
|
||||||
const view = zm.lookAtRh(
|
|
||||||
zm.Vec{ 0, 5, 2, 1 },
|
|
||||||
zm.Vec{ 0, 0, 0, 1 },
|
|
||||||
zm.Vec{ 0, 0, 1, 0 },
|
|
||||||
);
|
|
||||||
const proj = zm.perspectiveFovRh(
|
|
||||||
(std.math.pi / 4.0),
|
|
||||||
@as(f32, @floatFromInt(core.descriptor.width)) / @as(f32, @floatFromInt(core.descriptor.height)),
|
|
||||||
0.1,
|
|
||||||
10,
|
|
||||||
);
|
|
||||||
const mvp = zm.mul(zm.mul(model, view), proj);
|
|
||||||
const ubo = UniformBufferObject{
|
|
||||||
.mat = zm.transpose(mvp),
|
|
||||||
};
|
|
||||||
encoder.writeBuffer(app.uniform_buffer, 0, &[_]UniformBufferObject{ubo});
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// render scene to downscaled texture
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = app.draw_texture_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},
|
|
||||||
.depth_stencil_attachment = &gpu.RenderPassDepthStencilAttachment{
|
|
||||||
.view = app.depth_texture_view,
|
|
||||||
.depth_load_op = .clear,
|
|
||||||
.depth_store_op = .store,
|
|
||||||
.depth_clear_value = 1.0,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
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_group, &.{0});
|
|
||||||
pass.draw(vertices.len, 1, 0, 0);
|
|
||||||
pass.end();
|
|
||||||
pass.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// render scene normals to texture
|
|
||||||
const normal_color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = app.normal_texture_view,
|
|
||||||
.clear_value = .{ .r = 0.5, .b = 0.5, .g = 0.5, .a = 1.0 },
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
const normal_render_pass_info = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{normal_color_attachment},
|
|
||||||
});
|
|
||||||
|
|
||||||
const normal_pass = encoder.beginRenderPass(&normal_render_pass_info);
|
|
||||||
normal_pass.setPipeline(app.normal_pipeline);
|
|
||||||
normal_pass.setVertexBuffer(0, app.vertex_buffer, 0, @sizeOf(Vertex) * vertices.len);
|
|
||||||
normal_pass.setBindGroup(0, app.bind_group, &.{0});
|
|
||||||
normal_pass.draw(vertices.len, 1, 0, 0);
|
|
||||||
normal_pass.end();
|
|
||||||
normal_pass.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
{
|
|
||||||
// render to swap chain using previous passes
|
|
||||||
const post_color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = back_buffer_view,
|
|
||||||
.clear_value = std.mem.zeroes(gpu.Color),
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
const post_render_pass_info = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{post_color_attachment},
|
|
||||||
});
|
|
||||||
|
|
||||||
const draw_pass = encoder.beginRenderPass(&post_render_pass_info);
|
|
||||||
draw_pass.setPipeline(app.post_pipeline);
|
|
||||||
draw_pass.setVertexBuffer(0, app.post_vertex_buffer, 0, @sizeOf(Quad) * quad.len);
|
|
||||||
draw_pass.setBindGroup(0, app.post_bind_group, &.{0});
|
|
||||||
draw_pass.draw(quad.len, 1, 0, 0);
|
|
||||||
draw_pass.end();
|
|
||||||
draw_pass.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
encoder.release();
|
|
||||||
|
|
||||||
const queue = core.queue;
|
|
||||||
queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
command.release();
|
|
||||||
core.swap_chain.present();
|
|
||||||
back_buffer_view.release();
|
|
||||||
|
|
||||||
// update the window title every second
|
|
||||||
if (app.title_timer.read() >= 1.0) {
|
|
||||||
app.title_timer.reset();
|
|
||||||
try core.printTitle("Pixel Post Process [ {d}fps ] [ Input {d}hz ]", .{
|
|
||||||
core.frameRate(),
|
|
||||||
core.inputRate(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cleanup(app: *App) void {
|
|
||||||
app.pipeline.release();
|
|
||||||
app.normal_pipeline.release();
|
|
||||||
app.vertex_buffer.release();
|
|
||||||
app.uniform_buffer.release();
|
|
||||||
app.bind_group.release();
|
|
||||||
|
|
||||||
app.post_pipeline.release();
|
|
||||||
app.post_vertex_buffer.release();
|
|
||||||
app.post_uniform_buffer.release();
|
|
||||||
app.post_bind_group.release();
|
|
||||||
|
|
||||||
app.draw_texture_view.release();
|
|
||||||
app.depth_texture_view.release();
|
|
||||||
app.normal_texture_view.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn createRenderTextures(app: *App) !void {
|
|
||||||
const size = core.size();
|
|
||||||
|
|
||||||
const draw_texture_desc = gpu.Texture.Descriptor.init(.{
|
|
||||||
.size = .{ .width = size.width / pixel_size, .height = size.height / pixel_size },
|
|
||||||
.format = .bgra8_unorm,
|
|
||||||
.usage = .{ .texture_binding = true, .copy_dst = true, .render_attachment = true },
|
|
||||||
});
|
|
||||||
const draw_texture = core.device.createTexture(&draw_texture_desc);
|
|
||||||
app.draw_texture_view = draw_texture.createView(null);
|
|
||||||
draw_texture.release();
|
|
||||||
|
|
||||||
const depth_texture_desc = gpu.Texture.Descriptor.init(.{
|
|
||||||
.size = .{ .width = size.width / pixel_size, .height = size.height / pixel_size },
|
|
||||||
.format = .depth32_float,
|
|
||||||
.usage = .{ .texture_binding = true, .copy_dst = true, .render_attachment = true },
|
|
||||||
});
|
|
||||||
const depth_texture = core.device.createTexture(&depth_texture_desc);
|
|
||||||
app.depth_texture_view = depth_texture.createView(null);
|
|
||||||
depth_texture.release();
|
|
||||||
|
|
||||||
const normal_texture_desc = gpu.Texture.Descriptor.init(.{
|
|
||||||
.size = .{ .width = size.width / pixel_size, .height = size.height / pixel_size },
|
|
||||||
.format = .bgra8_unorm,
|
|
||||||
.usage = .{ .texture_binding = true, .copy_dst = true, .render_attachment = true },
|
|
||||||
});
|
|
||||||
const normal_texture = core.device.createTexture(&normal_texture_desc);
|
|
||||||
app.normal_texture_view = normal_texture.createView(null);
|
|
||||||
normal_texture.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn createDrawPipeline(app: *App) void {
|
|
||||||
const shader_module = core.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.wgsl"));
|
|
||||||
const vertex_attributes = [_]gpu.VertexAttribute{
|
|
||||||
.{ .format = .float32x3, .offset = @offsetOf(Vertex, "pos"), .shader_location = 0 },
|
|
||||||
.{ .format = .float32x3, .offset = @offsetOf(Vertex, "normal"), .shader_location = 1 },
|
|
||||||
.{ .format = .float32x2, .offset = @offsetOf(Vertex, "uv"), .shader_location = 2 },
|
|
||||||
};
|
|
||||||
const vertex_buffer_layout = gpu.VertexBufferLayout.init(.{
|
|
||||||
.array_stride = @sizeOf(Vertex),
|
|
||||||
.step_mode = .vertex,
|
|
||||||
.attributes = &vertex_attributes,
|
|
||||||
});
|
|
||||||
const vertex = gpu.VertexState.init(.{
|
|
||||||
.module = shader_module,
|
|
||||||
.entry_point = "vertex_main",
|
|
||||||
.buffers = &.{vertex_buffer_layout},
|
|
||||||
});
|
|
||||||
|
|
||||||
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 bgle = gpu.BindGroupLayout.Entry.buffer(0, .{ .vertex = true }, .uniform, true, 0);
|
|
||||||
const bgl = core.device.createBindGroupLayout(
|
|
||||||
&gpu.BindGroupLayout.Descriptor.init(.{
|
|
||||||
.entries = &.{bgle},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
const bind_group_layouts = [_]*gpu.BindGroupLayout{bgl};
|
|
||||||
const pipeline_layout = core.device.createPipelineLayout(
|
|
||||||
&gpu.PipelineLayout.Descriptor.init(.{
|
|
||||||
.bind_group_layouts = &bind_group_layouts,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
const vertex_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .vertex = true },
|
|
||||||
.size = @sizeOf(Vertex) * vertices.len,
|
|
||||||
.mapped_at_creation = .true,
|
|
||||||
});
|
|
||||||
const vertex_mapped = vertex_buffer.getMappedRange(Vertex, 0, vertices.len);
|
|
||||||
@memcpy(vertex_mapped.?, vertices[0..]);
|
|
||||||
vertex_buffer.unmap();
|
|
||||||
|
|
||||||
const uniform_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .copy_dst = true, .uniform = true },
|
|
||||||
.size = @sizeOf(UniformBufferObject),
|
|
||||||
.mapped_at_creation = .false,
|
|
||||||
});
|
|
||||||
const bind_group = core.device.createBindGroup(
|
|
||||||
&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = bgl,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.buffer(0, uniform_buffer, 0, @sizeOf(UniformBufferObject)),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
const pipeline_descriptor = gpu.RenderPipeline.Descriptor{
|
|
||||||
.fragment = &fragment,
|
|
||||||
.layout = pipeline_layout,
|
|
||||||
.vertex = vertex,
|
|
||||||
.primitive = .{
|
|
||||||
.cull_mode = .back,
|
|
||||||
},
|
|
||||||
.depth_stencil = &gpu.DepthStencilState{
|
|
||||||
.format = .depth32_float,
|
|
||||||
.depth_write_enabled = .true,
|
|
||||||
.depth_compare = .less,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
{
|
|
||||||
// "same" pipeline, different fragment shader to create a texture with normal information
|
|
||||||
const normal_fs_module = core.device.createShaderModuleWGSL("normal_frag.wgsl", @embedFile("normal_frag.wgsl"));
|
|
||||||
const normal_fragment = gpu.FragmentState.init(.{
|
|
||||||
.module = normal_fs_module,
|
|
||||||
.entry_point = "main",
|
|
||||||
.targets = &.{color_target},
|
|
||||||
});
|
|
||||||
const normal_pipeline_descriptor = gpu.RenderPipeline.Descriptor{
|
|
||||||
.fragment = &normal_fragment,
|
|
||||||
.layout = pipeline_layout,
|
|
||||||
.vertex = vertex,
|
|
||||||
.primitive = .{
|
|
||||||
.cull_mode = .back,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
app.normal_pipeline = core.device.createRenderPipeline(&normal_pipeline_descriptor);
|
|
||||||
|
|
||||||
normal_fs_module.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
app.pipeline = core.device.createRenderPipeline(&pipeline_descriptor);
|
|
||||||
app.vertex_buffer = vertex_buffer;
|
|
||||||
app.uniform_buffer = uniform_buffer;
|
|
||||||
app.bind_group = bind_group;
|
|
||||||
|
|
||||||
shader_module.release();
|
|
||||||
pipeline_layout.release();
|
|
||||||
bgl.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn createPostPipeline(app: *App) void {
|
|
||||||
const vs_module = core.device.createShaderModuleWGSL("pixel_vert.wgsl", @embedFile("pixel_vert.wgsl"));
|
|
||||||
const vertex_attributes = [_]gpu.VertexAttribute{
|
|
||||||
.{ .format = .float32x3, .offset = @offsetOf(Quad, "pos"), .shader_location = 0 },
|
|
||||||
.{ .format = .float32x2, .offset = @offsetOf(Quad, "uv"), .shader_location = 1 },
|
|
||||||
};
|
|
||||||
const vertex_buffer_layout = gpu.VertexBufferLayout.init(.{
|
|
||||||
.array_stride = @sizeOf(Quad),
|
|
||||||
.step_mode = .vertex,
|
|
||||||
.attributes = &vertex_attributes,
|
|
||||||
});
|
|
||||||
const vertex = gpu.VertexState.init(.{
|
|
||||||
.module = vs_module,
|
|
||||||
.entry_point = "main",
|
|
||||||
.buffers = &.{vertex_buffer_layout},
|
|
||||||
});
|
|
||||||
|
|
||||||
const fs_module = core.device.createShaderModuleWGSL("pixel_frag.wgsl", @embedFile("pixel_frag.wgsl"));
|
|
||||||
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 = fs_module,
|
|
||||||
.entry_point = "main",
|
|
||||||
.targets = &.{color_target},
|
|
||||||
});
|
|
||||||
|
|
||||||
const bgl = core.device.createBindGroupLayout(
|
|
||||||
&gpu.BindGroupLayout.Descriptor.init(.{
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroupLayout.Entry.texture(0, .{ .fragment = true }, .float, .dimension_2d, false),
|
|
||||||
gpu.BindGroupLayout.Entry.sampler(1, .{ .fragment = true }, .filtering),
|
|
||||||
gpu.BindGroupLayout.Entry.texture(2, .{ .fragment = true }, .depth, .dimension_2d, false),
|
|
||||||
gpu.BindGroupLayout.Entry.sampler(3, .{ .fragment = true }, .filtering),
|
|
||||||
gpu.BindGroupLayout.Entry.texture(4, .{ .fragment = true }, .float, .dimension_2d, false),
|
|
||||||
gpu.BindGroupLayout.Entry.sampler(5, .{ .fragment = true }, .filtering),
|
|
||||||
gpu.BindGroupLayout.Entry.buffer(6, .{ .fragment = true }, .uniform, true, 0),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
const bind_group_layouts = [_]*gpu.BindGroupLayout{bgl};
|
|
||||||
const pipeline_layout = core.device.createPipelineLayout(&gpu.PipelineLayout.Descriptor.init(.{
|
|
||||||
.bind_group_layouts = &bind_group_layouts,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const vertex_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .vertex = true },
|
|
||||||
.size = @sizeOf(Quad) * quad.len,
|
|
||||||
.mapped_at_creation = .true,
|
|
||||||
});
|
|
||||||
const vertex_mapped = vertex_buffer.getMappedRange(Quad, 0, quad.len);
|
|
||||||
@memcpy(vertex_mapped.?, quad[0..]);
|
|
||||||
vertex_buffer.unmap();
|
|
||||||
|
|
||||||
const draw_sampler = core.device.createSampler(null);
|
|
||||||
const depth_sampler = core.device.createSampler(null);
|
|
||||||
const normal_sampler = core.device.createSampler(null);
|
|
||||||
const uniform_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .copy_dst = true, .uniform = true },
|
|
||||||
.size = @sizeOf(PostUniformBufferObject),
|
|
||||||
.mapped_at_creation = .false,
|
|
||||||
});
|
|
||||||
const bind_group = core.device.createBindGroup(
|
|
||||||
&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = bgl,
|
|
||||||
.entries = &[_]gpu.BindGroup.Entry{
|
|
||||||
gpu.BindGroup.Entry.textureView(0, app.draw_texture_view),
|
|
||||||
gpu.BindGroup.Entry.sampler(1, draw_sampler),
|
|
||||||
gpu.BindGroup.Entry.textureView(2, app.depth_texture_view),
|
|
||||||
gpu.BindGroup.Entry.sampler(3, depth_sampler),
|
|
||||||
gpu.BindGroup.Entry.textureView(4, app.normal_texture_view),
|
|
||||||
gpu.BindGroup.Entry.sampler(5, normal_sampler),
|
|
||||||
gpu.BindGroup.Entry.buffer(6, uniform_buffer, 0, @sizeOf(PostUniformBufferObject)),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
draw_sampler.release();
|
|
||||||
depth_sampler.release();
|
|
||||||
normal_sampler.release();
|
|
||||||
|
|
||||||
const pipeline_descriptor = gpu.RenderPipeline.Descriptor{
|
|
||||||
.fragment = &fragment,
|
|
||||||
.layout = pipeline_layout,
|
|
||||||
.vertex = vertex,
|
|
||||||
.primitive = .{
|
|
||||||
.cull_mode = .back,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
app.post_pipeline = core.device.createRenderPipeline(&pipeline_descriptor);
|
|
||||||
app.post_vertex_buffer = vertex_buffer;
|
|
||||||
app.post_uniform_buffer = uniform_buffer;
|
|
||||||
app.post_bind_group = bind_group;
|
|
||||||
|
|
||||||
vs_module.release();
|
|
||||||
fs_module.release();
|
|
||||||
pipeline_layout.release();
|
|
||||||
bgl.release();
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
@fragment fn main(
|
|
||||||
@location(0) normal: vec3<f32>,
|
|
||||||
@location(1) uv: vec2<f32>,
|
|
||||||
) -> @location(0) vec4<f32> {
|
|
||||||
return vec4<f32>(normal / 2 + 0.5, 1.0);
|
|
||||||
}
|
|
||||||
|
|
@ -1,74 +0,0 @@
|
||||||
@group(0) @binding(0)
|
|
||||||
var draw_texture: texture_2d<f32>;
|
|
||||||
@group(0) @binding(1)
|
|
||||||
var draw_texture_sampler: sampler;
|
|
||||||
|
|
||||||
@group(0) @binding(2)
|
|
||||||
var depth_texture: texture_depth_2d;
|
|
||||||
@group(0) @binding(3)
|
|
||||||
var depth_texture_sampler: sampler;
|
|
||||||
|
|
||||||
@group(0) @binding(4)
|
|
||||||
var normal_texture: texture_2d<f32>;
|
|
||||||
@group(0) @binding(5)
|
|
||||||
var normal_texture_sampler: sampler;
|
|
||||||
|
|
||||||
struct View {
|
|
||||||
@location(0) width: u32,
|
|
||||||
@location(1) height: u32,
|
|
||||||
@location(2) pixel_size: u32,
|
|
||||||
}
|
|
||||||
@group(0) @binding(6)
|
|
||||||
var<uniform> view: View;
|
|
||||||
|
|
||||||
fn sample_depth(uv: vec2<f32>, x: f32, y: f32) -> f32 {
|
|
||||||
return textureSample(
|
|
||||||
depth_texture,
|
|
||||||
depth_texture_sampler,
|
|
||||||
uv + vec2<f32>(x * f32(view.pixel_size) / f32(view.width), y * f32(view.pixel_size) / f32(view.height))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sample_normal(uv: vec2<f32>, x: f32, y: f32) -> vec3<f32> {
|
|
||||||
return textureSample(
|
|
||||||
normal_texture,
|
|
||||||
normal_texture_sampler,
|
|
||||||
uv + vec2<f32>(x * f32(view.pixel_size) / f32(view.width), y * f32(view.pixel_size) / f32(view.height))
|
|
||||||
).xyz;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn normal_indicator(uv: vec2<f32>, x: f32, y: f32) -> f32 {
|
|
||||||
var depth_diff = sample_depth(uv, 0, 0) - sample_depth(uv, x, y);
|
|
||||||
var dx = sample_normal(uv, 0, 0);
|
|
||||||
var dy = sample_normal(uv, x, y);
|
|
||||||
if (depth_diff > 0) {
|
|
||||||
// only sample normals from closest pixel
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return distance(dx, dy);
|
|
||||||
}
|
|
||||||
|
|
||||||
@fragment fn main(
|
|
||||||
@builtin(position) position: vec4<f32>,
|
|
||||||
@location(0) uv: vec2<f32>
|
|
||||||
) -> @location(0) vec4<f32> {
|
|
||||||
var depth = sample_depth(uv, 0, 0);
|
|
||||||
var depth_diff: f32 = 0;
|
|
||||||
depth_diff += abs(depth - sample_depth(uv, -1, 0));
|
|
||||||
depth_diff += abs(depth - sample_depth(uv, 1, 0));
|
|
||||||
depth_diff += abs(depth - sample_depth(uv, 0, -1));
|
|
||||||
depth_diff += abs(depth - sample_depth(uv, 0, 1));
|
|
||||||
|
|
||||||
var normal_diff: f32 = 0;
|
|
||||||
normal_diff += normal_indicator(uv, -1, 0);
|
|
||||||
normal_diff += normal_indicator(uv, 1, 0);
|
|
||||||
normal_diff += normal_indicator(uv, 0, -1);
|
|
||||||
normal_diff += normal_indicator(uv, 0, 1);
|
|
||||||
|
|
||||||
var color = textureSample(draw_texture, draw_texture_sampler, uv);
|
|
||||||
if (depth_diff > 0.007) { // magic number from testing
|
|
||||||
return color * 0.7;
|
|
||||||
}
|
|
||||||
// add instead of multiply so really dark pixels get brighter
|
|
||||||
return color + (vec4<f32>(1) * step(0.1, normal_diff) * 0.7);
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
struct VertexOut {
|
|
||||||
@builtin(position) position_clip: vec4<f32>,
|
|
||||||
@location(0) uv: vec2<f32>
|
|
||||||
}
|
|
||||||
|
|
||||||
@vertex fn main(
|
|
||||||
@location(0) position: vec3<f32>,
|
|
||||||
@location(1) uv: vec2<f32>
|
|
||||||
) -> VertexOut {
|
|
||||||
var output : VertexOut;
|
|
||||||
output.position_clip = vec4<f32>(position.xy, 0.0, 1.0);
|
|
||||||
output.uv = uv;
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
pub const Quad = extern struct {
|
|
||||||
pos: @Vector(3, f32),
|
|
||||||
uv: @Vector(2, f32),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const quad = [_]Quad{
|
|
||||||
.{ .pos = .{ -1.0, 1.0, 0.0 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1.0, -1.0, 0.0 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1.0, 1.0, 0.0 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1.0, 1.0, 0.0 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1.0, -1.0, 0.0 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1.0, -1.0, 0.0 }, .uv = .{ 1, 0 } },
|
|
||||||
};
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
@group(0) @binding(0) var<uniform> ubo: mat4x4<f32>;
|
|
||||||
|
|
||||||
struct VertexOut {
|
|
||||||
@builtin(position) position_clip: vec4<f32>,
|
|
||||||
@location(0) normal: vec3<f32>,
|
|
||||||
@location(1) uv: vec2<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
@vertex fn vertex_main(
|
|
||||||
@location(0) position: vec3<f32>,
|
|
||||||
@location(1) normal: vec3<f32>,
|
|
||||||
@location(2) uv: vec2<f32>
|
|
||||||
) -> VertexOut {
|
|
||||||
var output: VertexOut;
|
|
||||||
output.position_clip = vec4<f32>(position, 1) * ubo;
|
|
||||||
output.normal = (vec4<f32>(normal, 0) * ubo).xyz;
|
|
||||||
output.uv = uv;
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
@fragment fn frag_main(
|
|
||||||
@location(0) normal: vec3<f32>,
|
|
||||||
@location(1) uv: vec2<f32>,
|
|
||||||
) -> @location(0) vec4<f32> {
|
|
||||||
var color = floor((uv * 0.5 + 0.25) * 32) / 32;
|
|
||||||
return vec4<f32>(color, 1, 1);
|
|
||||||
}
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
const renderer = @import("renderer.zig");
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
|
|
||||||
title_timer: core.Timer,
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{ .required_limits = gpu.Limits{
|
|
||||||
.max_vertex_buffers = 1,
|
|
||||||
.max_vertex_attributes = 2,
|
|
||||||
.max_bind_groups = 1,
|
|
||||||
.max_uniform_buffers_per_shader_stage = 1,
|
|
||||||
.max_uniform_buffer_binding_size = 16 * 1 * @sizeOf(f32),
|
|
||||||
} });
|
|
||||||
|
|
||||||
const allocator = gpa.allocator();
|
|
||||||
const timer = try core.Timer.start();
|
|
||||||
try renderer.init(allocator, timer);
|
|
||||||
app.* = .{ .title_timer = try core.Timer.start() };
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
_ = app;
|
|
||||||
defer _ = gpa.deinit();
|
|
||||||
defer core.deinit();
|
|
||||||
defer renderer.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(app: *App) !bool {
|
|
||||||
var iter = core.pollEvents();
|
|
||||||
while (iter.next()) |event| {
|
|
||||||
switch (event) {
|
|
||||||
.key_press => |ev| {
|
|
||||||
if (ev.key == .space) return true;
|
|
||||||
if (ev.key == .right) {
|
|
||||||
renderer.curr_primitive_index += 1;
|
|
||||||
renderer.curr_primitive_index %= 7;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.close => return true,
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
renderer.update();
|
|
||||||
|
|
||||||
// update the window title every second
|
|
||||||
if (app.title_timer.read() >= 1.0) {
|
|
||||||
app.title_timer.reset();
|
|
||||||
try core.printTitle("Procedural Primitives [ {d}fps ] [ Input {d}hz ]", .{
|
|
||||||
core.frameRate(),
|
|
||||||
core.inputRate(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
@ -1,336 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
const zmath = @import("zmath");
|
|
||||||
|
|
||||||
const PI = 3.1415927410125732421875;
|
|
||||||
|
|
||||||
pub const F32x3 = @Vector(3, f32);
|
|
||||||
pub const F32x4 = @Vector(4, f32);
|
|
||||||
pub const VertexData = struct {
|
|
||||||
position: F32x3,
|
|
||||||
normal: F32x3,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const PrimitiveType = enum(u4) { none, triangle, quad, plane, circle, uv_sphere, ico_sphere, cylinder, cone, torus };
|
|
||||||
|
|
||||||
pub const Primitive = struct {
|
|
||||||
vertex_data: std.ArrayList(VertexData),
|
|
||||||
vertex_count: u32,
|
|
||||||
index_data: std.ArrayList(u32),
|
|
||||||
index_count: u32,
|
|
||||||
type: PrimitiveType = .none,
|
|
||||||
};
|
|
||||||
|
|
||||||
// 2D Primitives
|
|
||||||
pub fn createTrianglePrimitive(allocator: std.mem.Allocator, size: f32) !Primitive {
|
|
||||||
const vertex_count = 3;
|
|
||||||
const index_count = 3;
|
|
||||||
var vertex_data = try std.ArrayList(VertexData).initCapacity(allocator, vertex_count);
|
|
||||||
|
|
||||||
const edge = size / 2.0;
|
|
||||||
|
|
||||||
vertex_data.appendSliceAssumeCapacity(&[vertex_count]VertexData{
|
|
||||||
VertexData{ .position = F32x3{ -edge, -edge, 0.0 }, .normal = F32x3{ -edge, -edge, 0.0 } },
|
|
||||||
VertexData{ .position = F32x3{ edge, -edge, 0.0 }, .normal = F32x3{ edge, -edge, 0.0 } },
|
|
||||||
VertexData{ .position = F32x3{ 0.0, edge, 0.0 }, .normal = F32x3{ 0.0, edge, 0.0 } },
|
|
||||||
});
|
|
||||||
|
|
||||||
var index_data = try std.ArrayList(u32).initCapacity(allocator, index_count);
|
|
||||||
index_data.appendSliceAssumeCapacity(&[index_count]u32{ 0, 1, 2 });
|
|
||||||
|
|
||||||
return Primitive{ .vertex_data = vertex_data, .vertex_count = 3, .index_data = index_data, .index_count = 3, .type = .triangle };
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn createQuadPrimitive(allocator: std.mem.Allocator, size: f32) !Primitive {
|
|
||||||
const vertex_count = 4;
|
|
||||||
const index_count = 6;
|
|
||||||
var vertex_data = try std.ArrayList(VertexData).initCapacity(allocator, vertex_count);
|
|
||||||
|
|
||||||
const edge = size / 2.0;
|
|
||||||
|
|
||||||
vertex_data.appendSliceAssumeCapacity(&[vertex_count]VertexData{
|
|
||||||
VertexData{ .position = F32x3{ -edge, -edge, 0.0 }, .normal = F32x3{ -edge, -edge, 0.0 } },
|
|
||||||
VertexData{ .position = F32x3{ edge, -edge, 0.0 }, .normal = F32x3{ edge, -edge, 0.0 } },
|
|
||||||
VertexData{ .position = F32x3{ -edge, edge, 0.0 }, .normal = F32x3{ -edge, edge, 0.0 } },
|
|
||||||
VertexData{ .position = F32x3{ edge, edge, 0.0 }, .normal = F32x3{ edge, edge, 0.0 } },
|
|
||||||
});
|
|
||||||
|
|
||||||
var index_data = try std.ArrayList(u32).initCapacity(allocator, index_count);
|
|
||||||
index_data.appendSliceAssumeCapacity(&[index_count]u32{
|
|
||||||
0, 1, 2,
|
|
||||||
1, 3, 2,
|
|
||||||
});
|
|
||||||
|
|
||||||
return Primitive{ .vertex_data = vertex_data, .vertex_count = 4, .index_data = index_data, .index_count = 6, .type = .quad };
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn createPlanePrimitive(allocator: std.mem.Allocator, x_subdivision: u32, y_subdivision: u32, size: f32) !Primitive {
|
|
||||||
const x_num_vertices = x_subdivision + 1;
|
|
||||||
const y_num_vertices = y_subdivision + 1;
|
|
||||||
const vertex_count = x_num_vertices * y_num_vertices;
|
|
||||||
var vertex_data = try std.ArrayList(VertexData).initCapacity(allocator, vertex_count);
|
|
||||||
|
|
||||||
const vertices_distance_y = (size / @as(f32, @floatFromInt(y_subdivision)));
|
|
||||||
const vertices_distance_x = (size / @as(f32, @floatFromInt(x_subdivision)));
|
|
||||||
var y: u32 = 0;
|
|
||||||
while (y < y_num_vertices) : (y += 1) {
|
|
||||||
var x: u32 = 0;
|
|
||||||
const pos_y = (-size / 2.0) + @as(f32, @floatFromInt(y)) * vertices_distance_y;
|
|
||||||
while (x < x_num_vertices) : (x += 1) {
|
|
||||||
const pos_x = (-size / 2.0) + @as(f32, @floatFromInt(x)) * vertices_distance_x;
|
|
||||||
vertex_data.appendAssumeCapacity(VertexData{ .position = F32x3{ pos_x, pos_y, 0.0 }, .normal = F32x3{ pos_x, pos_y, 0.0 } });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const index_count = x_subdivision * y_subdivision * 2 * 3;
|
|
||||||
var index_data = try std.ArrayList(u32).initCapacity(allocator, index_count);
|
|
||||||
|
|
||||||
y = 0;
|
|
||||||
while (y < y_subdivision) : (y += 1) {
|
|
||||||
var x: u32 = 0;
|
|
||||||
while (x < x_subdivision) : (x += 1) {
|
|
||||||
// First Triangle of Quad
|
|
||||||
index_data.appendAssumeCapacity(x + y * y_num_vertices);
|
|
||||||
index_data.appendAssumeCapacity(x + 1 + y * y_num_vertices);
|
|
||||||
index_data.appendAssumeCapacity(x + (y + 1) * y_num_vertices);
|
|
||||||
|
|
||||||
// Second Triangle of Quad
|
|
||||||
index_data.appendAssumeCapacity(x + 1 + y * y_num_vertices);
|
|
||||||
index_data.appendAssumeCapacity(x + (y + 1) * y_num_vertices + 1);
|
|
||||||
index_data.appendAssumeCapacity(x + (y + 1) * y_num_vertices);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Primitive{ .vertex_data = vertex_data, .vertex_count = vertex_count, .index_data = index_data, .index_count = index_count, .type = .plane };
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn createCirclePrimitive(allocator: std.mem.Allocator, vertices: u32, radius: f32) !Primitive {
|
|
||||||
const vertex_count = vertices + 1;
|
|
||||||
var vertex_data = try std.ArrayList(VertexData).initCapacity(allocator, vertex_count);
|
|
||||||
|
|
||||||
// Mid point of circle
|
|
||||||
vertex_data.appendAssumeCapacity(VertexData{ .position = F32x3{ 0, 0, 0.0 }, .normal = F32x3{ 0, 0, 0.0 } });
|
|
||||||
|
|
||||||
var x: u32 = 0;
|
|
||||||
const angle = 2 * PI / @as(f32, @floatFromInt(vertices));
|
|
||||||
while (x < vertices) : (x += 1) {
|
|
||||||
const x_f = @as(f32, @floatFromInt(x));
|
|
||||||
const pos_x = radius * zmath.cos(angle * x_f);
|
|
||||||
const pos_y = radius * zmath.sin(angle * x_f);
|
|
||||||
|
|
||||||
vertex_data.appendAssumeCapacity(VertexData{ .position = F32x3{ pos_x, pos_y, 0.0 }, .normal = F32x3{ pos_x, pos_y, 0.0 } });
|
|
||||||
}
|
|
||||||
|
|
||||||
const index_count = (vertices + 1) * 3;
|
|
||||||
var index_data = try std.ArrayList(u32).initCapacity(allocator, index_count);
|
|
||||||
|
|
||||||
x = 1;
|
|
||||||
while (x <= vertices) : (x += 1) {
|
|
||||||
index_data.appendAssumeCapacity(0);
|
|
||||||
index_data.appendAssumeCapacity(x);
|
|
||||||
index_data.appendAssumeCapacity(x + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
index_data.appendAssumeCapacity(0);
|
|
||||||
index_data.appendAssumeCapacity(vertices);
|
|
||||||
index_data.appendAssumeCapacity(1);
|
|
||||||
|
|
||||||
return Primitive{ .vertex_data = vertex_data, .vertex_count = vertex_count, .index_data = index_data, .index_count = index_count, .type = .plane };
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3D Primitives
|
|
||||||
pub fn createCubePrimitive(allocator: std.mem.Allocator, size: f32) !Primitive {
|
|
||||||
const vertex_count = 8;
|
|
||||||
const index_count = 36;
|
|
||||||
var vertex_data = try std.ArrayList(VertexData).initCapacity(allocator, vertex_count);
|
|
||||||
|
|
||||||
const edge = size / 2.0;
|
|
||||||
|
|
||||||
vertex_data.appendSliceAssumeCapacity(&[vertex_count]VertexData{
|
|
||||||
// Front positions
|
|
||||||
VertexData{ .position = F32x3{ -edge, -edge, edge }, .normal = F32x3{ -edge, -edge, edge } },
|
|
||||||
VertexData{ .position = F32x3{ edge, -edge, edge }, .normal = F32x3{ edge, -edge, edge } },
|
|
||||||
VertexData{ .position = F32x3{ edge, edge, edge }, .normal = F32x3{ edge, edge, edge } },
|
|
||||||
VertexData{ .position = F32x3{ -edge, edge, edge }, .normal = F32x3{ -edge, edge, edge } },
|
|
||||||
// Back positions
|
|
||||||
VertexData{ .position = F32x3{ -edge, -edge, -edge }, .normal = F32x3{ -edge, -edge, -edge } },
|
|
||||||
VertexData{ .position = F32x3{ edge, -edge, -edge }, .normal = F32x3{ edge, -edge, -edge } },
|
|
||||||
VertexData{ .position = F32x3{ edge, edge, -edge }, .normal = F32x3{ edge, edge, -edge } },
|
|
||||||
VertexData{ .position = F32x3{ -edge, edge, -edge }, .normal = F32x3{ -edge, edge, -edge } },
|
|
||||||
});
|
|
||||||
|
|
||||||
var index_data = try std.ArrayList(u32).initCapacity(allocator, index_count);
|
|
||||||
|
|
||||||
index_data.appendSliceAssumeCapacity(&[index_count]u32{
|
|
||||||
// front quad
|
|
||||||
0, 1, 2,
|
|
||||||
2, 3, 0,
|
|
||||||
// right quad
|
|
||||||
1, 5, 6,
|
|
||||||
6, 2, 1,
|
|
||||||
// back quad
|
|
||||||
7, 6, 5,
|
|
||||||
5, 4, 7,
|
|
||||||
// left quad
|
|
||||||
4, 0, 3,
|
|
||||||
3, 7, 4,
|
|
||||||
// bottom quad
|
|
||||||
4, 5, 1,
|
|
||||||
1, 0, 4,
|
|
||||||
// top quad
|
|
||||||
3, 2, 6,
|
|
||||||
6, 7, 3,
|
|
||||||
});
|
|
||||||
|
|
||||||
return Primitive{ .vertex_data = vertex_data, .vertex_count = vertex_count, .index_data = index_data, .index_count = index_count, .type = .quad };
|
|
||||||
}
|
|
||||||
|
|
||||||
const VertexDataMAL = std.MultiArrayList(VertexData);
|
|
||||||
|
|
||||||
pub fn createCylinderPrimitive(allocator: std.mem.Allocator, radius: f32, height: f32, num_sides: u32) !Primitive {
|
|
||||||
const alloc_amt_vert: u32 = num_sides * 2 + 2;
|
|
||||||
const alloc_amt_idx: u32 = num_sides * 12;
|
|
||||||
|
|
||||||
var vertex_data = VertexDataMAL{};
|
|
||||||
try vertex_data.ensureTotalCapacity(allocator, alloc_amt_vert);
|
|
||||||
defer vertex_data.deinit(allocator);
|
|
||||||
|
|
||||||
var out_vertex_data = try std.ArrayList(VertexData).initCapacity(allocator, alloc_amt_vert);
|
|
||||||
var index_data = try std.ArrayList(u32).initCapacity(allocator, alloc_amt_idx);
|
|
||||||
|
|
||||||
vertex_data.appendAssumeCapacity(VertexData{ .position = F32x3{ 0.0, (height / 2.0), 0.0 }, .normal = undefined });
|
|
||||||
vertex_data.appendAssumeCapacity(VertexData{ .position = F32x3{ 0.0, -(height / 2.0), 0.0 }, .normal = undefined });
|
|
||||||
|
|
||||||
const angle = 2.0 * PI / @as(f32, @floatFromInt(num_sides));
|
|
||||||
|
|
||||||
for (1..num_sides + 1) |i| {
|
|
||||||
const float_i = @as(f32, @floatFromInt(i));
|
|
||||||
|
|
||||||
const x: f32 = radius * zmath.sin(angle * float_i);
|
|
||||||
const y: f32 = radius * zmath.cos(angle * float_i);
|
|
||||||
|
|
||||||
vertex_data.appendAssumeCapacity(VertexData{ .position = F32x3{ x, (height / 2.0), y }, .normal = undefined });
|
|
||||||
vertex_data.appendAssumeCapacity(VertexData{ .position = F32x3{ x, -(height / 2.0), y }, .normal = undefined });
|
|
||||||
}
|
|
||||||
|
|
||||||
var group1: u32 = 1;
|
|
||||||
var group2: u32 = 3;
|
|
||||||
|
|
||||||
for (0..num_sides) |_| {
|
|
||||||
if (group2 >= num_sides * 2) group2 = 1;
|
|
||||||
index_data.appendSliceAssumeCapacity(&[_]u32{
|
|
||||||
0, group1 + 1, group2 + 1,
|
|
||||||
group1 + 1, group1 + 2, group2 + 1,
|
|
||||||
group1 + 2, group2 + 2, group2 + 1,
|
|
||||||
group2 + 2, group1 + 2, 1,
|
|
||||||
});
|
|
||||||
group1 += 2;
|
|
||||||
group2 += 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var i: u32 = 0;
|
|
||||||
while (i < alloc_amt_idx) : (i += 3) {
|
|
||||||
const indexA: u32 = index_data.items[i];
|
|
||||||
const indexB: u32 = index_data.items[i + 1];
|
|
||||||
const indexC: u32 = index_data.items[i + 2];
|
|
||||||
|
|
||||||
const vert1: F32x4 = F32x4{ vertex_data.get(indexA).position[0], vertex_data.get(indexA).position[1], vertex_data.get(indexA).position[2], 1.0 };
|
|
||||||
const vert2: F32x4 = F32x4{ vertex_data.get(indexB).position[0], vertex_data.get(indexB).position[1], vertex_data.get(indexB).position[2], 1.0 };
|
|
||||||
const vert3: F32x4 = F32x4{ vertex_data.get(indexC).position[0], vertex_data.get(indexC).position[1], vertex_data.get(indexC).position[2], 1.0 };
|
|
||||||
|
|
||||||
const edgeAB: F32x4 = vert2 - vert1;
|
|
||||||
const edgeAC: F32x4 = vert3 - vert1;
|
|
||||||
|
|
||||||
const cross = zmath.cross3(edgeAB, edgeAC);
|
|
||||||
|
|
||||||
vertex_data.items(.normal)[indexA][0] += cross[0];
|
|
||||||
vertex_data.items(.normal)[indexA][1] += cross[1];
|
|
||||||
vertex_data.items(.normal)[indexA][2] += cross[2];
|
|
||||||
vertex_data.items(.normal)[indexB][0] += cross[0];
|
|
||||||
vertex_data.items(.normal)[indexB][1] += cross[1];
|
|
||||||
vertex_data.items(.normal)[indexB][2] += cross[2];
|
|
||||||
vertex_data.items(.normal)[indexC][0] += cross[0];
|
|
||||||
vertex_data.items(.normal)[indexC][1] += cross[1];
|
|
||||||
vertex_data.items(.normal)[indexC][2] += cross[2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (vertex_data.items(.position), vertex_data.items(.normal)) |pos, nor| {
|
|
||||||
out_vertex_data.appendAssumeCapacity(VertexData{ .position = pos, .normal = nor });
|
|
||||||
}
|
|
||||||
|
|
||||||
return Primitive{ .vertex_data = out_vertex_data, .vertex_count = alloc_amt_vert, .index_data = index_data, .index_count = alloc_amt_idx, .type = .cylinder };
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn createConePrimitive(allocator: std.mem.Allocator, radius: f32, height: f32, num_sides: u32) !Primitive {
|
|
||||||
const alloc_amt_vert: u32 = num_sides + 2;
|
|
||||||
const alloc_amt_idx: u32 = num_sides * 6;
|
|
||||||
|
|
||||||
var vertex_data = VertexDataMAL{};
|
|
||||||
try vertex_data.ensureTotalCapacity(allocator, alloc_amt_vert);
|
|
||||||
defer vertex_data.deinit(allocator);
|
|
||||||
|
|
||||||
var out_vertex_data = try std.ArrayList(VertexData).initCapacity(allocator, alloc_amt_vert);
|
|
||||||
var index_data = try std.ArrayList(u32).initCapacity(allocator, alloc_amt_idx);
|
|
||||||
|
|
||||||
vertex_data.appendAssumeCapacity(VertexData{ .position = F32x3{ 0.0, (height / 2.0), 0.0 }, .normal = undefined });
|
|
||||||
vertex_data.appendAssumeCapacity(VertexData{ .position = F32x3{ 0.0, -(height / 2.0), 0.0 }, .normal = undefined });
|
|
||||||
|
|
||||||
const angle = 2.0 * PI / @as(f32, @floatFromInt(num_sides));
|
|
||||||
|
|
||||||
for (1..num_sides + 1) |i| {
|
|
||||||
const float_i = @as(f32, @floatFromInt(i));
|
|
||||||
|
|
||||||
const x: f32 = radius * zmath.sin(angle * float_i);
|
|
||||||
const y: f32 = radius * zmath.cos(angle * float_i);
|
|
||||||
|
|
||||||
vertex_data.appendAssumeCapacity(VertexData{ .position = F32x3{ x, -(height / 2.0), y }, .normal = undefined });
|
|
||||||
}
|
|
||||||
|
|
||||||
var group1: u32 = 1;
|
|
||||||
var group2: u32 = 2;
|
|
||||||
|
|
||||||
for (0..num_sides) |_| {
|
|
||||||
if (group2 >= num_sides + 1) group2 = 1;
|
|
||||||
index_data.appendSliceAssumeCapacity(&[_]u32{
|
|
||||||
0, group1 + 1, group2 + 1,
|
|
||||||
group2 + 1, group1 + 1, 1,
|
|
||||||
});
|
|
||||||
group1 += 1;
|
|
||||||
group2 += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var i: u32 = 0;
|
|
||||||
while (i < alloc_amt_idx) : (i += 3) {
|
|
||||||
const indexA: u32 = index_data.items[i];
|
|
||||||
const indexB: u32 = index_data.items[i + 1];
|
|
||||||
const indexC: u32 = index_data.items[i + 2];
|
|
||||||
|
|
||||||
const vert1: F32x4 = F32x4{ vertex_data.get(indexA).position[0], vertex_data.get(indexA).position[1], vertex_data.get(indexA).position[2], 1.0 };
|
|
||||||
const vert2: F32x4 = F32x4{ vertex_data.get(indexB).position[0], vertex_data.get(indexB).position[1], vertex_data.get(indexB).position[2], 1.0 };
|
|
||||||
const vert3: F32x4 = F32x4{ vertex_data.get(indexC).position[0], vertex_data.get(indexC).position[1], vertex_data.get(indexC).position[2], 1.0 };
|
|
||||||
|
|
||||||
const edgeAB: F32x4 = vert2 - vert1;
|
|
||||||
const edgeAC: F32x4 = vert3 - vert1;
|
|
||||||
|
|
||||||
const cross = zmath.cross3(edgeAB, edgeAC);
|
|
||||||
|
|
||||||
vertex_data.items(.normal)[indexA][0] += cross[0];
|
|
||||||
vertex_data.items(.normal)[indexA][1] += cross[1];
|
|
||||||
vertex_data.items(.normal)[indexA][2] += cross[2];
|
|
||||||
vertex_data.items(.normal)[indexB][0] += cross[0];
|
|
||||||
vertex_data.items(.normal)[indexB][1] += cross[1];
|
|
||||||
vertex_data.items(.normal)[indexB][2] += cross[2];
|
|
||||||
vertex_data.items(.normal)[indexC][0] += cross[0];
|
|
||||||
vertex_data.items(.normal)[indexC][1] += cross[1];
|
|
||||||
vertex_data.items(.normal)[indexC][2] += cross[2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (vertex_data.items(.position), vertex_data.items(.normal)) |pos, nor| {
|
|
||||||
out_vertex_data.appendAssumeCapacity(VertexData{ .position = pos, .normal = nor });
|
|
||||||
}
|
|
||||||
|
|
||||||
return Primitive{ .vertex_data = out_vertex_data, .vertex_count = alloc_amt_vert, .index_data = index_data, .index_count = alloc_amt_idx, .type = .cone };
|
|
||||||
}
|
|
||||||
|
|
@ -1,328 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
const zm = @import("zmath");
|
|
||||||
const primitives = @import("procedural-primitives.zig");
|
|
||||||
const Primitive = primitives.Primitive;
|
|
||||||
const VertexData = primitives.VertexData;
|
|
||||||
|
|
||||||
pub const Renderer = @This();
|
|
||||||
|
|
||||||
var queue: *gpu.Queue = undefined;
|
|
||||||
var pipeline: *gpu.RenderPipeline = undefined;
|
|
||||||
var app_timer: core.Timer = undefined;
|
|
||||||
var depth_texture: *gpu.Texture = undefined;
|
|
||||||
var depth_texture_view: *gpu.TextureView = undefined;
|
|
||||||
|
|
||||||
const PrimitiveRenderData = struct {
|
|
||||||
vertex_buffer: *gpu.Buffer,
|
|
||||||
index_buffer: *gpu.Buffer,
|
|
||||||
vertex_count: u32,
|
|
||||||
index_count: u32,
|
|
||||||
};
|
|
||||||
|
|
||||||
const UniformBufferObject = struct {
|
|
||||||
mvp_matrix: zm.Mat,
|
|
||||||
};
|
|
||||||
var uniform_buffer: *gpu.Buffer = undefined;
|
|
||||||
var bind_group: *gpu.BindGroup = undefined;
|
|
||||||
|
|
||||||
var primitives_data: [7]PrimitiveRenderData = undefined;
|
|
||||||
|
|
||||||
pub var curr_primitive_index: u4 = 0;
|
|
||||||
|
|
||||||
pub fn init(allocator: std.mem.Allocator, timer: core.Timer) !void {
|
|
||||||
queue = core.queue;
|
|
||||||
app_timer = timer;
|
|
||||||
|
|
||||||
{
|
|
||||||
const triangle_primitive = try primitives.createTrianglePrimitive(allocator, 1);
|
|
||||||
primitives_data[0] = PrimitiveRenderData{ .vertex_buffer = createVertexBuffer(triangle_primitive), .index_buffer = createIndexBuffer(triangle_primitive), .vertex_count = triangle_primitive.vertex_count, .index_count = triangle_primitive.index_count };
|
|
||||||
defer triangle_primitive.vertex_data.deinit();
|
|
||||||
defer triangle_primitive.index_data.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const quad_primitive = try primitives.createQuadPrimitive(allocator, 1.4);
|
|
||||||
primitives_data[1] = PrimitiveRenderData{ .vertex_buffer = createVertexBuffer(quad_primitive), .index_buffer = createIndexBuffer(quad_primitive), .vertex_count = quad_primitive.vertex_count, .index_count = quad_primitive.index_count };
|
|
||||||
defer quad_primitive.vertex_data.deinit();
|
|
||||||
defer quad_primitive.index_data.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const plane_primitive = try primitives.createPlanePrimitive(allocator, 1000, 1000, 1.5);
|
|
||||||
primitives_data[2] = PrimitiveRenderData{ .vertex_buffer = createVertexBuffer(plane_primitive), .index_buffer = createIndexBuffer(plane_primitive), .vertex_count = plane_primitive.vertex_count, .index_count = plane_primitive.index_count };
|
|
||||||
defer plane_primitive.vertex_data.deinit();
|
|
||||||
defer plane_primitive.index_data.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const circle_primitive = try primitives.createCirclePrimitive(allocator, 64, 1);
|
|
||||||
primitives_data[3] = PrimitiveRenderData{ .vertex_buffer = createVertexBuffer(circle_primitive), .index_buffer = createIndexBuffer(circle_primitive), .vertex_count = circle_primitive.vertex_count, .index_count = circle_primitive.index_count };
|
|
||||||
defer circle_primitive.vertex_data.deinit();
|
|
||||||
defer circle_primitive.index_data.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const cube_primitive = try primitives.createCubePrimitive(allocator, 0.5);
|
|
||||||
primitives_data[4] = PrimitiveRenderData{ .vertex_buffer = createVertexBuffer(cube_primitive), .index_buffer = createIndexBuffer(cube_primitive), .vertex_count = cube_primitive.vertex_count, .index_count = cube_primitive.index_count };
|
|
||||||
defer cube_primitive.vertex_data.deinit();
|
|
||||||
defer cube_primitive.index_data.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const cylinder_primitive = try primitives.createCylinderPrimitive(allocator, 1.0, 1.0, 6);
|
|
||||||
primitives_data[5] = PrimitiveRenderData{ .vertex_buffer = createVertexBuffer(cylinder_primitive), .index_buffer = createIndexBuffer(cylinder_primitive), .vertex_count = cylinder_primitive.vertex_count, .index_count = cylinder_primitive.index_count };
|
|
||||||
defer cylinder_primitive.vertex_data.deinit();
|
|
||||||
defer cylinder_primitive.index_data.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const cone_primitive = try primitives.createConePrimitive(allocator, 0.7, 1.0, 15);
|
|
||||||
primitives_data[6] = PrimitiveRenderData{ .vertex_buffer = createVertexBuffer(cone_primitive), .index_buffer = createIndexBuffer(cone_primitive), .vertex_count = cone_primitive.vertex_count, .index_count = cone_primitive.index_count };
|
|
||||||
defer cone_primitive.vertex_data.deinit();
|
|
||||||
defer cone_primitive.index_data.deinit();
|
|
||||||
}
|
|
||||||
var bind_group_layout = createBindGroupLayout();
|
|
||||||
defer bind_group_layout.release();
|
|
||||||
|
|
||||||
createBindBuffer(bind_group_layout);
|
|
||||||
|
|
||||||
createDepthTexture();
|
|
||||||
|
|
||||||
var shader = core.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.wgsl"));
|
|
||||||
defer shader.release();
|
|
||||||
|
|
||||||
pipeline = createPipeline(shader, bind_group_layout);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn createVertexBuffer(primitive: Primitive) *gpu.Buffer {
|
|
||||||
const vertex_buffer_descriptor = gpu.Buffer.Descriptor{
|
|
||||||
.size = primitive.vertex_count * @sizeOf(VertexData),
|
|
||||||
.usage = .{ .vertex = true, .copy_dst = true },
|
|
||||||
.mapped_at_creation = .false,
|
|
||||||
};
|
|
||||||
|
|
||||||
const vertex_buffer = core.device.createBuffer(&vertex_buffer_descriptor);
|
|
||||||
queue.writeBuffer(vertex_buffer, 0, primitive.vertex_data.items[0..]);
|
|
||||||
|
|
||||||
return vertex_buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn createIndexBuffer(primitive: Primitive) *gpu.Buffer {
|
|
||||||
const index_buffer_descriptor = gpu.Buffer.Descriptor{
|
|
||||||
.size = primitive.index_count * @sizeOf(u32),
|
|
||||||
.usage = .{ .index = true, .copy_dst = true },
|
|
||||||
.mapped_at_creation = .false,
|
|
||||||
};
|
|
||||||
const index_buffer = core.device.createBuffer(&index_buffer_descriptor);
|
|
||||||
queue.writeBuffer(index_buffer, 0, primitive.index_data.items[0..]);
|
|
||||||
|
|
||||||
return index_buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn createBindGroupLayout() *gpu.BindGroupLayout {
|
|
||||||
const bgle = gpu.BindGroupLayout.Entry.buffer(0, .{ .vertex = true, .fragment = false }, .uniform, true, 0);
|
|
||||||
return core.device.createBindGroupLayout(
|
|
||||||
&gpu.BindGroupLayout.Descriptor.init(.{
|
|
||||||
.entries = &.{bgle},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn createBindBuffer(bind_group_layout: *gpu.BindGroupLayout) void {
|
|
||||||
uniform_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .copy_dst = true, .uniform = true },
|
|
||||||
.size = @sizeOf(UniformBufferObject),
|
|
||||||
.mapped_at_creation = .false,
|
|
||||||
});
|
|
||||||
|
|
||||||
bind_group = core.device.createBindGroup(
|
|
||||||
&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = bind_group_layout,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.buffer(0, uniform_buffer, 0, @sizeOf(UniformBufferObject)),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn createDepthTexture() void {
|
|
||||||
depth_texture = core.device.createTexture(&gpu.Texture.Descriptor{
|
|
||||||
.usage = .{ .render_attachment = true },
|
|
||||||
.size = .{ .width = core.descriptor.width, .height = core.descriptor.height },
|
|
||||||
.format = .depth24_plus,
|
|
||||||
});
|
|
||||||
|
|
||||||
depth_texture_view = depth_texture.createView(&gpu.TextureView.Descriptor{
|
|
||||||
.format = .depth24_plus,
|
|
||||||
.dimension = .dimension_2d,
|
|
||||||
.array_layer_count = 1,
|
|
||||||
.mip_level_count = 1,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn createPipeline(shader_module: *gpu.ShaderModule, bind_group_layout: *gpu.BindGroupLayout) *gpu.RenderPipeline {
|
|
||||||
const vertex_attributes = [_]gpu.VertexAttribute{
|
|
||||||
.{ .format = .float32x3, .shader_location = 0, .offset = 0 },
|
|
||||||
.{ .format = .float32x3, .shader_location = 1, .offset = @sizeOf(primitives.F32x3) },
|
|
||||||
};
|
|
||||||
|
|
||||||
const vertex_buffer_layout = gpu.VertexBufferLayout.init(.{
|
|
||||||
.array_stride = @sizeOf(VertexData),
|
|
||||||
.step_mode = .vertex,
|
|
||||||
.attributes = &vertex_attributes,
|
|
||||||
});
|
|
||||||
|
|
||||||
const vertex_pipeline_state = gpu.VertexState.init(.{ .module = shader_module, .entry_point = "vertex_main", .buffers = &.{vertex_buffer_layout} });
|
|
||||||
|
|
||||||
const primitive_pipeline_state = gpu.PrimitiveState{
|
|
||||||
.topology = .triangle_list,
|
|
||||||
.front_face = .ccw,
|
|
||||||
.cull_mode = .back,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Fragment Pipeline State
|
|
||||||
const blend = gpu.BlendState{
|
|
||||||
.color = gpu.BlendComponent{ .operation = .add, .src_factor = .src_alpha, .dst_factor = .one_minus_src_alpha },
|
|
||||||
.alpha = gpu.BlendComponent{ .operation = .add, .src_factor = .zero, .dst_factor = .one },
|
|
||||||
};
|
|
||||||
const color_target = gpu.ColorTargetState{
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
.blend = &blend,
|
|
||||||
.write_mask = gpu.ColorWriteMaskFlags.all,
|
|
||||||
};
|
|
||||||
const fragment_pipeline_state = gpu.FragmentState.init(.{
|
|
||||||
.module = shader_module,
|
|
||||||
.entry_point = "frag_main",
|
|
||||||
.targets = &.{color_target},
|
|
||||||
});
|
|
||||||
|
|
||||||
const depth_stencil_state = gpu.DepthStencilState{
|
|
||||||
.format = .depth24_plus,
|
|
||||||
.depth_write_enabled = .true,
|
|
||||||
.depth_compare = .less,
|
|
||||||
};
|
|
||||||
|
|
||||||
const multi_sample_state = gpu.MultisampleState{
|
|
||||||
.count = 1,
|
|
||||||
.mask = 0xFFFFFFFF,
|
|
||||||
.alpha_to_coverage_enabled = .false,
|
|
||||||
};
|
|
||||||
|
|
||||||
const bind_group_layouts = [_]*gpu.BindGroupLayout{bind_group_layout};
|
|
||||||
// Pipeline Layout
|
|
||||||
const pipeline_layout_descriptor = gpu.PipelineLayout.Descriptor.init(.{
|
|
||||||
.bind_group_layouts = &bind_group_layouts,
|
|
||||||
});
|
|
||||||
const pipeline_layout = core.device.createPipelineLayout(&pipeline_layout_descriptor);
|
|
||||||
defer pipeline_layout.release();
|
|
||||||
|
|
||||||
const pipeline_descriptor = gpu.RenderPipeline.Descriptor{
|
|
||||||
.label = "Main Pipeline",
|
|
||||||
.layout = pipeline_layout,
|
|
||||||
.vertex = vertex_pipeline_state,
|
|
||||||
.primitive = primitive_pipeline_state,
|
|
||||||
.depth_stencil = &depth_stencil_state,
|
|
||||||
.multisample = multi_sample_state,
|
|
||||||
.fragment = &fragment_pipeline_state,
|
|
||||||
};
|
|
||||||
|
|
||||||
return core.device.createRenderPipeline(&pipeline_descriptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const F32x1 = @Vector(1, f32);
|
|
||||||
|
|
||||||
pub fn update() void {
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = back_buffer_view,
|
|
||||||
.clear_value = gpu.Color{ .r = 0.2, .g = 0.2, .b = 0.2, .a = 1.0 },
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
|
|
||||||
const depth_stencil_attachment = gpu.RenderPassDepthStencilAttachment{
|
|
||||||
.view = depth_texture_view,
|
|
||||||
.depth_load_op = .clear,
|
|
||||||
.depth_store_op = .store,
|
|
||||||
.depth_clear_value = 1.0,
|
|
||||||
};
|
|
||||||
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
const render_pass_info = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{color_attachment},
|
|
||||||
.depth_stencil_attachment = &depth_stencil_attachment,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (curr_primitive_index >= 4) {
|
|
||||||
const time = app_timer.read() / 5;
|
|
||||||
const model = zm.mul(zm.rotationX(time * (std.math.pi / 2.0)), zm.rotationZ(time * (std.math.pi / 2.0)));
|
|
||||||
const view = zm.lookAtRh(
|
|
||||||
zm.Vec{ 0, 4, 2, 1 },
|
|
||||||
zm.Vec{ 0, 0, 0, 1 },
|
|
||||||
zm.Vec{ 0, 0, 1, 0 },
|
|
||||||
);
|
|
||||||
const proj = zm.perspectiveFovRh(
|
|
||||||
(std.math.pi / 4.0),
|
|
||||||
@as(f32, @floatFromInt(core.descriptor.width)) / @as(f32, @floatFromInt(core.descriptor.height)),
|
|
||||||
0.1,
|
|
||||||
10,
|
|
||||||
);
|
|
||||||
|
|
||||||
const mvp = zm.mul(zm.mul(model, view), proj);
|
|
||||||
|
|
||||||
const ubo = UniformBufferObject{
|
|
||||||
.mvp_matrix = zm.transpose(mvp),
|
|
||||||
};
|
|
||||||
encoder.writeBuffer(uniform_buffer, 0, &[_]UniformBufferObject{ubo});
|
|
||||||
} else {
|
|
||||||
const ubo = UniformBufferObject{
|
|
||||||
.mvp_matrix = zm.identity(),
|
|
||||||
};
|
|
||||||
encoder.writeBuffer(uniform_buffer, 0, &[_]UniformBufferObject{ubo});
|
|
||||||
}
|
|
||||||
|
|
||||||
const pass = encoder.beginRenderPass(&render_pass_info);
|
|
||||||
|
|
||||||
pass.setPipeline(pipeline);
|
|
||||||
|
|
||||||
const vertex_buffer = primitives_data[curr_primitive_index].vertex_buffer;
|
|
||||||
const vertex_count = primitives_data[curr_primitive_index].vertex_count;
|
|
||||||
pass.setVertexBuffer(0, vertex_buffer, 0, @sizeOf(VertexData) * vertex_count);
|
|
||||||
|
|
||||||
pass.setBindGroup(0, bind_group, &.{0});
|
|
||||||
|
|
||||||
const index_buffer = primitives_data[curr_primitive_index].index_buffer;
|
|
||||||
const index_count = primitives_data[curr_primitive_index].index_count;
|
|
||||||
pass.setIndexBuffer(index_buffer, .uint32, 0, @sizeOf(u32) * index_count);
|
|
||||||
pass.drawIndexed(index_count, 1, 0, 0, 0);
|
|
||||||
|
|
||||||
pass.end();
|
|
||||||
pass.release();
|
|
||||||
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
encoder.release();
|
|
||||||
|
|
||||||
queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
command.release();
|
|
||||||
core.swap_chain.present();
|
|
||||||
back_buffer_view.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit() void {
|
|
||||||
var i: u4 = 0;
|
|
||||||
while (i < 7) : (i += 1) {
|
|
||||||
primitives_data[i].vertex_buffer.release();
|
|
||||||
primitives_data[i].index_buffer.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
bind_group.release();
|
|
||||||
uniform_buffer.release();
|
|
||||||
depth_texture.release();
|
|
||||||
depth_texture_view.release();
|
|
||||||
pipeline.release();
|
|
||||||
}
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
struct Uniforms {
|
|
||||||
mvp_matrix : mat4x4<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
@binding(0) @group(0) var<uniform> ubo : Uniforms;
|
|
||||||
|
|
||||||
struct VertexInput {
|
|
||||||
@location(0) position: vec3<f32>,
|
|
||||||
@location(1) normal: vec3<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) position: vec4<f32>,
|
|
||||||
@location(0) normal: vec3<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
@vertex fn vertex_main(in : VertexInput) -> VertexOutput {
|
|
||||||
var out: VertexOutput;
|
|
||||||
out.position = vec4<f32>(in.position, 1.0) * ubo.mvp_matrix;
|
|
||||||
out.normal = in.normal;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FragmentOutput {
|
|
||||||
@location(0) pixel_color: vec4<f32>
|
|
||||||
};
|
|
||||||
|
|
||||||
@fragment fn frag_main(in: VertexOutput) -> FragmentOutput {
|
|
||||||
var out : FragmentOutput;
|
|
||||||
|
|
||||||
out.pixel_color = vec4<f32>((in.normal + 1) / 2, 1.0);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
@ -1,140 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
const Vertex = extern struct {
|
|
||||||
pos: @Vector(2, f32),
|
|
||||||
col: @Vector(3, f32),
|
|
||||||
};
|
|
||||||
const vertices = [_]Vertex{
|
|
||||||
.{ .pos = .{ -0.5, -0.5 }, .col = .{ 1, 0, 0 } },
|
|
||||||
.{ .pos = .{ 0.5, -0.5 }, .col = .{ 0, 1, 0 } },
|
|
||||||
.{ .pos = .{ 0.5, 0.5 }, .col = .{ 0, 0, 1 } },
|
|
||||||
.{ .pos = .{ -0.5, 0.5 }, .col = .{ 1, 1, 1 } },
|
|
||||||
};
|
|
||||||
const index_data = [_]u32{ 0, 1, 2, 2, 3, 0 };
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
|
|
||||||
title_timer: core.Timer,
|
|
||||||
pipeline: *gpu.RenderPipeline,
|
|
||||||
vertex_buffer: *gpu.Buffer,
|
|
||||||
index_buffer: *gpu.Buffer,
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{});
|
|
||||||
|
|
||||||
const shader_module = core.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.wgsl"));
|
|
||||||
defer shader_module.release();
|
|
||||||
|
|
||||||
const vertex_attributes = [_]gpu.VertexAttribute{
|
|
||||||
.{ .format = .float32x2, .offset = @offsetOf(Vertex, "pos"), .shader_location = 0 },
|
|
||||||
.{ .format = .float32x3, .offset = @offsetOf(Vertex, "col"), .shader_location = 1 },
|
|
||||||
};
|
|
||||||
const vertex_buffer_layout = gpu.VertexBufferLayout.init(.{
|
|
||||||
.array_stride = @sizeOf(Vertex),
|
|
||||||
.step_mode = .vertex,
|
|
||||||
.attributes = &vertex_attributes,
|
|
||||||
});
|
|
||||||
|
|
||||||
const color_target = gpu.ColorTargetState{
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
.blend = &.{},
|
|
||||||
.write_mask = gpu.ColorWriteMaskFlags.all,
|
|
||||||
};
|
|
||||||
const fragment = gpu.FragmentState.init(.{
|
|
||||||
.module = shader_module,
|
|
||||||
.entry_point = "frag_main",
|
|
||||||
.targets = &.{color_target},
|
|
||||||
});
|
|
||||||
|
|
||||||
const pipeline_layout = core.device.createPipelineLayout(&gpu.PipelineLayout.Descriptor.init(.{}));
|
|
||||||
defer pipeline_layout.release();
|
|
||||||
|
|
||||||
const pipeline_descriptor = gpu.RenderPipeline.Descriptor{
|
|
||||||
.fragment = &fragment,
|
|
||||||
.layout = pipeline_layout,
|
|
||||||
.vertex = gpu.VertexState.init(.{
|
|
||||||
.module = shader_module,
|
|
||||||
.entry_point = "vertex_main",
|
|
||||||
.buffers = &.{vertex_buffer_layout},
|
|
||||||
}),
|
|
||||||
.primitive = .{ .cull_mode = .back },
|
|
||||||
};
|
|
||||||
|
|
||||||
const vertex_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .vertex = true },
|
|
||||||
.size = @sizeOf(Vertex) * vertices.len,
|
|
||||||
.mapped_at_creation = .true,
|
|
||||||
});
|
|
||||||
const vertex_mapped = vertex_buffer.getMappedRange(Vertex, 0, vertices.len);
|
|
||||||
@memcpy(vertex_mapped.?, vertices[0..]);
|
|
||||||
vertex_buffer.unmap();
|
|
||||||
|
|
||||||
const index_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .index = true },
|
|
||||||
.size = @sizeOf(u32) * index_data.len,
|
|
||||||
.mapped_at_creation = .true,
|
|
||||||
});
|
|
||||||
const index_mapped = index_buffer.getMappedRange(u32, 0, index_data.len);
|
|
||||||
@memcpy(index_mapped.?, index_data[0..]);
|
|
||||||
index_buffer.unmap();
|
|
||||||
|
|
||||||
app.title_timer = try core.Timer.start();
|
|
||||||
app.pipeline = core.device.createRenderPipeline(&pipeline_descriptor);
|
|
||||||
app.vertex_buffer = vertex_buffer;
|
|
||||||
app.index_buffer = index_buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
app.vertex_buffer.release();
|
|
||||||
app.index_buffer.release();
|
|
||||||
app.pipeline.release();
|
|
||||||
core.deinit();
|
|
||||||
_ = gpa.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(app: *App) !bool {
|
|
||||||
var iter = core.pollEvents();
|
|
||||||
while (iter.next()) |event| if (event == .close) return true;
|
|
||||||
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = back_buffer_view,
|
|
||||||
.clear_value = .{ .r = 0, .g = 0, .b = 0, .a = 1 },
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
const render_pass_info = gpu.RenderPassDescriptor.init(.{ .color_attachments = &.{color_attachment} });
|
|
||||||
|
|
||||||
const pass = encoder.beginRenderPass(&render_pass_info);
|
|
||||||
pass.setPipeline(app.pipeline);
|
|
||||||
pass.setVertexBuffer(0, app.vertex_buffer, 0, @sizeOf(Vertex) * vertices.len);
|
|
||||||
pass.setIndexBuffer(app.index_buffer, .uint32, 0, @sizeOf(u32) * index_data.len);
|
|
||||||
pass.drawIndexed(index_data.len, 1, 0, 0, 0);
|
|
||||||
pass.end();
|
|
||||||
pass.release();
|
|
||||||
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
encoder.release();
|
|
||||||
core.queue.submit(&.{command});
|
|
||||||
command.release();
|
|
||||||
core.swap_chain.present();
|
|
||||||
back_buffer_view.release();
|
|
||||||
|
|
||||||
// update the window title every second
|
|
||||||
if (app.title_timer.read() >= 1.0) {
|
|
||||||
app.title_timer.reset();
|
|
||||||
try core.printTitle("RGB Quad [ {d}fps ] [ Input {d}hz ]", .{
|
|
||||||
core.frameRate(),
|
|
||||||
core.inputRate(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
struct Output {
|
|
||||||
@builtin(position) pos: vec4<f32>,
|
|
||||||
@location(0) color: vec3<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
@vertex fn vertex_main(@location(0) pos: vec2<f32>, @location(1) color: vec3<f32>) -> Output {
|
|
||||||
var output: Output;
|
|
||||||
output.pos = vec4(pos, 0, 1);
|
|
||||||
output.color = color;
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
@fragment fn frag_main(@location(0) color: vec3<f32>) -> @location(0) vec4<f32> {
|
|
||||||
return vec4(color, 1);
|
|
||||||
}
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
pub const Vertex = extern struct {
|
|
||||||
pos: @Vector(4, f32),
|
|
||||||
col: @Vector(4, f32),
|
|
||||||
uv: @Vector(2, f32),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const vertices = [_]Vertex{
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
};
|
|
||||||
|
|
@ -1,195 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
const zm = @import("zmath");
|
|
||||||
const Vertex = @import("cube_mesh.zig").Vertex;
|
|
||||||
const vertices = @import("cube_mesh.zig").vertices;
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
const UniformBufferObject = struct {
|
|
||||||
mat: zm.Mat,
|
|
||||||
};
|
|
||||||
|
|
||||||
title_timer: core.Timer,
|
|
||||||
timer: core.Timer,
|
|
||||||
pipeline: *gpu.RenderPipeline,
|
|
||||||
vertex_buffer: *gpu.Buffer,
|
|
||||||
uniform_buffer: *gpu.Buffer,
|
|
||||||
bind_group: *gpu.BindGroup,
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{});
|
|
||||||
|
|
||||||
const shader_module = core.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.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.init(.{
|
|
||||||
.array_stride = @sizeOf(Vertex),
|
|
||||||
.step_mode = .vertex,
|
|
||||||
.attributes = &vertex_attributes,
|
|
||||||
});
|
|
||||||
|
|
||||||
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 bgle = gpu.BindGroupLayout.Entry.buffer(0, .{ .vertex = true }, .uniform, true, 0);
|
|
||||||
const bgl = core.device.createBindGroupLayout(
|
|
||||||
&gpu.BindGroupLayout.Descriptor.init(.{
|
|
||||||
.entries = &.{bgle},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
const bind_group_layouts = [_]*gpu.BindGroupLayout{bgl};
|
|
||||||
const pipeline_layout = core.device.createPipelineLayout(&gpu.PipelineLayout.Descriptor.init(.{
|
|
||||||
.bind_group_layouts = &bind_group_layouts,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const pipeline_descriptor = gpu.RenderPipeline.Descriptor{
|
|
||||||
.fragment = &fragment,
|
|
||||||
.layout = pipeline_layout,
|
|
||||||
.vertex = gpu.VertexState.init(.{
|
|
||||||
.module = shader_module,
|
|
||||||
.entry_point = "vertex_main",
|
|
||||||
.buffers = &.{vertex_buffer_layout},
|
|
||||||
}),
|
|
||||||
.primitive = .{
|
|
||||||
.cull_mode = .back,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const vertex_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .vertex = true },
|
|
||||||
.size = @sizeOf(Vertex) * vertices.len,
|
|
||||||
.mapped_at_creation = .true,
|
|
||||||
});
|
|
||||||
const vertex_mapped = vertex_buffer.getMappedRange(Vertex, 0, vertices.len);
|
|
||||||
@memcpy(vertex_mapped.?, vertices[0..]);
|
|
||||||
vertex_buffer.unmap();
|
|
||||||
|
|
||||||
const uniform_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .copy_dst = true, .uniform = true },
|
|
||||||
.size = @sizeOf(UniformBufferObject),
|
|
||||||
.mapped_at_creation = .false,
|
|
||||||
});
|
|
||||||
const bind_group = core.device.createBindGroup(
|
|
||||||
&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = bgl,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.buffer(0, uniform_buffer, 0, @sizeOf(UniformBufferObject)),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
app.title_timer = try core.Timer.start();
|
|
||||||
app.timer = try core.Timer.start();
|
|
||||||
app.pipeline = core.device.createRenderPipeline(&pipeline_descriptor);
|
|
||||||
app.vertex_buffer = vertex_buffer;
|
|
||||||
app.uniform_buffer = uniform_buffer;
|
|
||||||
app.bind_group = bind_group;
|
|
||||||
|
|
||||||
shader_module.release();
|
|
||||||
pipeline_layout.release();
|
|
||||||
bgl.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
defer _ = gpa.deinit();
|
|
||||||
defer core.deinit();
|
|
||||||
|
|
||||||
app.vertex_buffer.release();
|
|
||||||
app.uniform_buffer.release();
|
|
||||||
app.bind_group.release();
|
|
||||||
app.pipeline.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(app: *App) !bool {
|
|
||||||
var iter = core.pollEvents();
|
|
||||||
while (iter.next()) |event| {
|
|
||||||
switch (event) {
|
|
||||||
.key_press => |ev| {
|
|
||||||
if (ev.key == .space) return true;
|
|
||||||
},
|
|
||||||
.close => return true,
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = back_buffer_view,
|
|
||||||
.clear_value = std.mem.zeroes(gpu.Color),
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
|
|
||||||
const queue = core.queue;
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
const render_pass_info = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{color_attachment},
|
|
||||||
});
|
|
||||||
|
|
||||||
{
|
|
||||||
const time = app.timer.read();
|
|
||||||
const model = zm.mul(zm.rotationX(time * (std.math.pi / 2.0)), zm.rotationZ(time * (std.math.pi / 2.0)));
|
|
||||||
const view = zm.lookAtRh(
|
|
||||||
zm.Vec{ 0, 4, 2, 1 },
|
|
||||||
zm.Vec{ 0, 0, 0, 1 },
|
|
||||||
zm.Vec{ 0, 0, 1, 0 },
|
|
||||||
);
|
|
||||||
const proj = zm.perspectiveFovRh(
|
|
||||||
(std.math.pi / 4.0),
|
|
||||||
@as(f32, @floatFromInt(core.descriptor.width)) / @as(f32, @floatFromInt(core.descriptor.height)),
|
|
||||||
0.1,
|
|
||||||
10,
|
|
||||||
);
|
|
||||||
const mvp = zm.mul(zm.mul(model, view), proj);
|
|
||||||
const ubo = UniformBufferObject{
|
|
||||||
.mat = zm.transpose(mvp),
|
|
||||||
};
|
|
||||||
queue.writeBuffer(app.uniform_buffer, 0, &[_]UniformBufferObject{ubo});
|
|
||||||
}
|
|
||||||
|
|
||||||
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_group, &.{0});
|
|
||||||
pass.draw(vertices.len, 1, 0, 0);
|
|
||||||
pass.end();
|
|
||||||
pass.release();
|
|
||||||
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
encoder.release();
|
|
||||||
|
|
||||||
queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
command.release();
|
|
||||||
core.swap_chain.present();
|
|
||||||
back_buffer_view.release();
|
|
||||||
|
|
||||||
// update the window title every second
|
|
||||||
if (app.title_timer.read() >= 1.0) {
|
|
||||||
app.title_timer.reset();
|
|
||||||
try core.printTitle("Rotating Cube [ {d}fps ] [ Input {d}hz ]", .{
|
|
||||||
core.frameRate(),
|
|
||||||
core.inputRate(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
@group(0) @binding(0) var<uniform> ubo : mat4x4<f32>;
|
|
||||||
struct VertexOut {
|
|
||||||
@builtin(position) position_clip : vec4<f32>,
|
|
||||||
@location(0) fragUV : vec2<f32>,
|
|
||||||
@location(1) fragPosition: vec4<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
@vertex fn vertex_main(
|
|
||||||
@location(0) position : vec4<f32>,
|
|
||||||
@location(1) uv: vec2<f32>
|
|
||||||
) -> VertexOut {
|
|
||||||
var output : VertexOut;
|
|
||||||
output.position_clip = position * ubo;
|
|
||||||
output.fragUV = uv;
|
|
||||||
output.fragPosition = 0.5 * (position + vec4<f32>(1.0, 1.0, 1.0, 1.0));
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
@fragment fn frag_main(
|
|
||||||
@location(0) fragUV: vec2<f32>,
|
|
||||||
@location(1) fragPosition: vec4<f32>
|
|
||||||
) -> @location(0) vec4<f32> {
|
|
||||||
return fragPosition;
|
|
||||||
}
|
|
||||||
|
|
@ -1,357 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
const zm = @import("zmath");
|
|
||||||
const zigimg = @import("zigimg");
|
|
||||||
const assets = @import("assets");
|
|
||||||
const json = std.json;
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
const speed = 2.0 * 100.0; // pixels per second
|
|
||||||
|
|
||||||
const Vec2 = @Vector(2, f32);
|
|
||||||
|
|
||||||
const UniformBufferObject = struct {
|
|
||||||
mat: zm.Mat,
|
|
||||||
};
|
|
||||||
const Sprite = extern struct {
|
|
||||||
pos: Vec2,
|
|
||||||
size: Vec2,
|
|
||||||
world_pos: Vec2,
|
|
||||||
sheet_size: Vec2,
|
|
||||||
};
|
|
||||||
const SpriteFrames = extern struct {
|
|
||||||
up: Vec2,
|
|
||||||
down: Vec2,
|
|
||||||
left: Vec2,
|
|
||||||
right: Vec2,
|
|
||||||
};
|
|
||||||
const JSONFrames = struct {
|
|
||||||
up: []f32,
|
|
||||||
down: []f32,
|
|
||||||
left: []f32,
|
|
||||||
right: []f32,
|
|
||||||
};
|
|
||||||
const JSONSprite = struct {
|
|
||||||
pos: []f32,
|
|
||||||
size: []f32,
|
|
||||||
world_pos: []f32,
|
|
||||||
is_player: bool = false,
|
|
||||||
frames: JSONFrames,
|
|
||||||
};
|
|
||||||
const SpriteSheet = struct {
|
|
||||||
width: f32,
|
|
||||||
height: f32,
|
|
||||||
};
|
|
||||||
const JSONData = struct {
|
|
||||||
sheet: SpriteSheet,
|
|
||||||
sprites: []JSONSprite,
|
|
||||||
};
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
|
|
||||||
title_timer: core.Timer,
|
|
||||||
timer: core.Timer,
|
|
||||||
fps_timer: core.Timer,
|
|
||||||
pipeline: *gpu.RenderPipeline,
|
|
||||||
uniform_buffer: *gpu.Buffer,
|
|
||||||
bind_group: *gpu.BindGroup,
|
|
||||||
sheet: SpriteSheet,
|
|
||||||
sprites_buffer: *gpu.Buffer,
|
|
||||||
sprites: std.ArrayList(Sprite),
|
|
||||||
sprites_frames: std.ArrayList(SpriteFrames),
|
|
||||||
player_pos: Vec2,
|
|
||||||
direction: Vec2,
|
|
||||||
player_sprite_index: usize,
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{});
|
|
||||||
|
|
||||||
const allocator = gpa.allocator();
|
|
||||||
|
|
||||||
const sprites_file = try std.fs.cwd().openFile("../../examples/sprite2d/sprites.json", .{ .mode = .read_only });
|
|
||||||
defer sprites_file.close();
|
|
||||||
const file_size = (try sprites_file.stat()).size;
|
|
||||||
const buffer = try allocator.alloc(u8, file_size);
|
|
||||||
defer allocator.free(buffer);
|
|
||||||
try sprites_file.reader().readNoEof(buffer);
|
|
||||||
const root = try std.json.parseFromSlice(JSONData, allocator, buffer, .{});
|
|
||||||
defer root.deinit();
|
|
||||||
|
|
||||||
app.player_pos = Vec2{ 0, 0 };
|
|
||||||
app.direction = Vec2{ 0, 0 };
|
|
||||||
app.sheet = root.value.sheet;
|
|
||||||
std.log.info("Sheet Dimensions: {} {}", .{ app.sheet.width, app.sheet.height });
|
|
||||||
app.sprites = std.ArrayList(Sprite).init(allocator);
|
|
||||||
app.sprites_frames = std.ArrayList(SpriteFrames).init(allocator);
|
|
||||||
for (root.value.sprites) |sprite| {
|
|
||||||
std.log.info("Sprite World Position: {} {}", .{ sprite.world_pos[0], sprite.world_pos[1] });
|
|
||||||
std.log.info("Sprite Texture Position: {} {}", .{ sprite.pos[0], sprite.pos[1] });
|
|
||||||
std.log.info("Sprite Dimensions: {} {}", .{ sprite.size[0], sprite.size[1] });
|
|
||||||
if (sprite.is_player) {
|
|
||||||
app.player_sprite_index = app.sprites.items.len;
|
|
||||||
}
|
|
||||||
try app.sprites.append(.{
|
|
||||||
.pos = Vec2{ sprite.pos[0], sprite.pos[1] },
|
|
||||||
.size = Vec2{ sprite.size[0], sprite.size[1] },
|
|
||||||
.world_pos = Vec2{ sprite.world_pos[0], sprite.world_pos[1] },
|
|
||||||
.sheet_size = Vec2{ app.sheet.width, app.sheet.height },
|
|
||||||
});
|
|
||||||
try app.sprites_frames.append(.{ .up = Vec2{ sprite.frames.up[0], sprite.frames.up[1] }, .down = Vec2{ sprite.frames.down[0], sprite.frames.down[1] }, .left = Vec2{ sprite.frames.left[0], sprite.frames.left[1] }, .right = Vec2{ sprite.frames.right[0], sprite.frames.right[1] } });
|
|
||||||
}
|
|
||||||
std.log.info("Number of sprites: {}", .{app.sprites.items.len});
|
|
||||||
|
|
||||||
const shader_module = core.device.createShaderModuleWGSL("sprite-shader.wgsl", @embedFile("sprite-shader.wgsl"));
|
|
||||||
|
|
||||||
const blend = gpu.BlendState{
|
|
||||||
.color = .{
|
|
||||||
.operation = .add,
|
|
||||||
.src_factor = .src_alpha,
|
|
||||||
.dst_factor = .one_minus_src_alpha,
|
|
||||||
},
|
|
||||||
.alpha = .{
|
|
||||||
.operation = .add,
|
|
||||||
.src_factor = .one,
|
|
||||||
.dst_factor = .zero,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
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{
|
|
||||||
.fragment = &fragment,
|
|
||||||
.vertex = gpu.VertexState.init(.{
|
|
||||||
.module = shader_module,
|
|
||||||
.entry_point = "vertex_main",
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
const pipeline = core.device.createRenderPipeline(&pipeline_descriptor);
|
|
||||||
|
|
||||||
const sprites_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .storage = true, .copy_dst = true },
|
|
||||||
.size = @sizeOf(Sprite) * app.sprites.items.len,
|
|
||||||
.mapped_at_creation = .true,
|
|
||||||
});
|
|
||||||
const sprites_mapped = sprites_buffer.getMappedRange(Sprite, 0, app.sprites.items.len);
|
|
||||||
@memcpy(sprites_mapped.?, app.sprites.items[0..]);
|
|
||||||
sprites_buffer.unmap();
|
|
||||||
|
|
||||||
// Create a sampler with linear filtering for smooth interpolation.
|
|
||||||
const sampler = core.device.createSampler(&.{
|
|
||||||
.mag_filter = .linear,
|
|
||||||
.min_filter = .linear,
|
|
||||||
});
|
|
||||||
const queue = core.queue;
|
|
||||||
var img = try zigimg.Image.fromMemory(allocator, assets.sprites_sheet_png);
|
|
||||||
defer img.deinit();
|
|
||||||
const img_size = gpu.Extent3D{ .width = @as(u32, @intCast(img.width)), .height = @as(u32, @intCast(img.height)) };
|
|
||||||
std.log.info("Image Dimensions: {} {}", .{ img.width, img.height });
|
|
||||||
const texture = core.device.createTexture(&.{
|
|
||||||
.size = img_size,
|
|
||||||
.format = .rgba8_unorm,
|
|
||||||
.usage = .{
|
|
||||||
.texture_binding = true,
|
|
||||||
.copy_dst = true,
|
|
||||||
.render_attachment = true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const data_layout = gpu.Texture.DataLayout{
|
|
||||||
.bytes_per_row = @as(u32, @intCast(img.width * 4)),
|
|
||||||
.rows_per_image = @as(u32, @intCast(img.height)),
|
|
||||||
};
|
|
||||||
switch (img.pixels) {
|
|
||||||
.rgba32 => |pixels| queue.writeTexture(&.{ .texture = texture }, &data_layout, &img_size, pixels),
|
|
||||||
.rgb24 => |pixels| {
|
|
||||||
const data = try rgb24ToRgba32(allocator, pixels);
|
|
||||||
defer data.deinit(allocator);
|
|
||||||
queue.writeTexture(&.{ .texture = texture }, &data_layout, &img_size, data.rgba32);
|
|
||||||
},
|
|
||||||
else => @panic("unsupported image color format"),
|
|
||||||
}
|
|
||||||
|
|
||||||
const uniform_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .copy_dst = true, .uniform = true },
|
|
||||||
.size = @sizeOf(UniformBufferObject),
|
|
||||||
.mapped_at_creation = .false,
|
|
||||||
});
|
|
||||||
|
|
||||||
const texture_view = texture.createView(&gpu.TextureView.Descriptor{});
|
|
||||||
texture.release();
|
|
||||||
|
|
||||||
const bind_group_layout = pipeline.getBindGroupLayout(0);
|
|
||||||
const bind_group = core.device.createBindGroup(
|
|
||||||
&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = bind_group_layout,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.buffer(0, uniform_buffer, 0, @sizeOf(UniformBufferObject)),
|
|
||||||
gpu.BindGroup.Entry.sampler(1, sampler),
|
|
||||||
gpu.BindGroup.Entry.textureView(2, texture_view),
|
|
||||||
gpu.BindGroup.Entry.buffer(3, sprites_buffer, 0, @sizeOf(Sprite) * app.sprites.items.len),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
texture_view.release();
|
|
||||||
sampler.release();
|
|
||||||
bind_group_layout.release();
|
|
||||||
|
|
||||||
app.title_timer = try core.Timer.start();
|
|
||||||
app.timer = try core.Timer.start();
|
|
||||||
app.fps_timer = try core.Timer.start();
|
|
||||||
app.pipeline = pipeline;
|
|
||||||
app.uniform_buffer = uniform_buffer;
|
|
||||||
app.bind_group = bind_group;
|
|
||||||
app.sprites_buffer = sprites_buffer;
|
|
||||||
|
|
||||||
shader_module.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
defer _ = gpa.deinit();
|
|
||||||
defer core.deinit();
|
|
||||||
|
|
||||||
app.pipeline.release();
|
|
||||||
app.sprites.deinit();
|
|
||||||
app.sprites_frames.deinit();
|
|
||||||
app.uniform_buffer.release();
|
|
||||||
app.bind_group.release();
|
|
||||||
app.sprites_buffer.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(app: *App) !bool {
|
|
||||||
// Handle input by determining the direction the player wants to go.
|
|
||||||
var iter = core.pollEvents();
|
|
||||||
while (iter.next()) |event| {
|
|
||||||
switch (event) {
|
|
||||||
.key_press => |ev| {
|
|
||||||
switch (ev.key) {
|
|
||||||
.space => return true,
|
|
||||||
.left => app.direction[0] += 1,
|
|
||||||
.right => app.direction[0] -= 1,
|
|
||||||
.up => app.direction[1] += 1,
|
|
||||||
.down => app.direction[1] -= 1,
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.key_release => |ev| {
|
|
||||||
switch (ev.key) {
|
|
||||||
.left => app.direction[0] -= 1,
|
|
||||||
.right => app.direction[0] += 1,
|
|
||||||
.up => app.direction[1] -= 1,
|
|
||||||
.down => app.direction[1] += 1,
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.close => return true,
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the player position, by moving in the direction the player wants to go
|
|
||||||
// by the speed amount. Multiply by delta_time to ensure that movement is the same speed
|
|
||||||
// regardless of the frame rate.
|
|
||||||
const delta_time = app.fps_timer.lap();
|
|
||||||
app.player_pos += app.direction * Vec2{ speed, speed } * Vec2{ delta_time, delta_time };
|
|
||||||
|
|
||||||
// Render the frame
|
|
||||||
try app.render();
|
|
||||||
|
|
||||||
// update the window title every second
|
|
||||||
if (app.title_timer.read() >= 1.0) {
|
|
||||||
app.title_timer.reset();
|
|
||||||
try core.printTitle("Sprite2D [ {d}fps ] [ Input {d}hz ]", .{
|
|
||||||
core.frameRate(),
|
|
||||||
core.inputRate(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(app: *App) !void {
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = back_buffer_view,
|
|
||||||
// sky blue background color:
|
|
||||||
.clear_value = .{ .r = 0.52, .g = 0.8, .b = 0.92, .a = 1.0 },
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
const render_pass_info = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{color_attachment},
|
|
||||||
});
|
|
||||||
|
|
||||||
const player_sprite = &app.sprites.items[app.player_sprite_index];
|
|
||||||
const player_sprite_frame = &app.sprites_frames.items[app.player_sprite_index];
|
|
||||||
if (app.direction[0] == -1.0) {
|
|
||||||
player_sprite.pos = player_sprite_frame.left;
|
|
||||||
} else if (app.direction[0] == 1.0) {
|
|
||||||
player_sprite.pos = player_sprite_frame.right;
|
|
||||||
} else if (app.direction[1] == -1.0) {
|
|
||||||
player_sprite.pos = player_sprite_frame.down;
|
|
||||||
} else if (app.direction[1] == 1.0) {
|
|
||||||
player_sprite.pos = player_sprite_frame.up;
|
|
||||||
}
|
|
||||||
player_sprite.world_pos = app.player_pos;
|
|
||||||
|
|
||||||
// One pixel in our scene will equal one window pixel (i.e. be roughly the same size
|
|
||||||
// irrespective of whether the user has a Retina/HDPI display.)
|
|
||||||
const proj = zm.orthographicRh(
|
|
||||||
@as(f32, @floatFromInt(core.size().width)),
|
|
||||||
@as(f32, @floatFromInt(core.size().height)),
|
|
||||||
0.1,
|
|
||||||
1000,
|
|
||||||
);
|
|
||||||
const view = zm.lookAtRh(
|
|
||||||
zm.Vec{ 0, 1000, 0, 1 },
|
|
||||||
zm.Vec{ 0, 0, 0, 1 },
|
|
||||||
zm.Vec{ 0, 0, 1, 0 },
|
|
||||||
);
|
|
||||||
const mvp = zm.mul(view, proj);
|
|
||||||
const ubo = UniformBufferObject{
|
|
||||||
.mat = zm.transpose(mvp),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Pass the latest uniform values & sprite values to the shader program.
|
|
||||||
encoder.writeBuffer(app.uniform_buffer, 0, &[_]UniformBufferObject{ubo});
|
|
||||||
encoder.writeBuffer(app.sprites_buffer, 0, app.sprites.items);
|
|
||||||
|
|
||||||
// Draw the sprite batch
|
|
||||||
const total_vertices = @as(u32, @intCast(app.sprites.items.len * 6));
|
|
||||||
const pass = encoder.beginRenderPass(&render_pass_info);
|
|
||||||
pass.setPipeline(app.pipeline);
|
|
||||||
pass.setBindGroup(0, app.bind_group, &.{});
|
|
||||||
pass.draw(total_vertices, 1, 0, 0);
|
|
||||||
pass.end();
|
|
||||||
pass.release();
|
|
||||||
|
|
||||||
// Submit the frame.
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
encoder.release();
|
|
||||||
const queue = core.queue;
|
|
||||||
queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
command.release();
|
|
||||||
core.swap_chain.present();
|
|
||||||
back_buffer_view.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rgb24ToRgba32(allocator: std.mem.Allocator, in: []zigimg.color.Rgb24) !zigimg.color.PixelStorage {
|
|
||||||
const out = try zigimg.color.PixelStorage.init(allocator, .rgba32, in.len);
|
|
||||||
var i: usize = 0;
|
|
||||||
while (i < in.len) : (i += 1) {
|
|
||||||
out.rgba32[i] = zigimg.color.Rgba32{ .r = in[i].r, .g = in[i].g, .b = in[i].b, .a = 255 };
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
struct Uniforms {
|
|
||||||
modelViewProjectionMatrix : mat4x4<f32>,
|
|
||||||
};
|
|
||||||
@binding(0) @group(0) var<uniform> uniforms : Uniforms;
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) Position : vec4<f32>,
|
|
||||||
@location(0) fragUV : vec2<f32>,
|
|
||||||
@location(1) spriteIndex : f32,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Sprite {
|
|
||||||
pos: vec2<f32>,
|
|
||||||
size: vec2<f32>,
|
|
||||||
world_pos: vec2<f32>,
|
|
||||||
sheet_size: vec2<f32>,
|
|
||||||
};
|
|
||||||
@binding(3) @group(0) var<storage, read> sprites: array<Sprite>;
|
|
||||||
|
|
||||||
@vertex
|
|
||||||
fn vertex_main(
|
|
||||||
@builtin(vertex_index) VertexIndex : u32
|
|
||||||
) -> VertexOutput {
|
|
||||||
var sprite = sprites[VertexIndex / 6];
|
|
||||||
|
|
||||||
// Calculate the vertex position
|
|
||||||
var positions = array<vec2<f32>, 6>(
|
|
||||||
vec2<f32>(0.0, 0.0), // bottom-left
|
|
||||||
vec2<f32>(0.0, 1.0), // top-left
|
|
||||||
vec2<f32>(1.0, 0.0), // bottom-right
|
|
||||||
vec2<f32>(1.0, 0.0), // bottom-right
|
|
||||||
vec2<f32>(0.0, 1.0), // top-left
|
|
||||||
vec2<f32>(1.0, 1.0), // top-right
|
|
||||||
);
|
|
||||||
var pos = positions[VertexIndex % 6];
|
|
||||||
pos.x *= sprite.size.x;
|
|
||||||
pos.y *= sprite.size.y;
|
|
||||||
pos.x += sprite.world_pos.x;
|
|
||||||
pos.y += sprite.world_pos.y;
|
|
||||||
|
|
||||||
// Calculate the UV coordinate
|
|
||||||
var uvs = array<vec2<f32>, 6>(
|
|
||||||
vec2<f32>(0.0, 1.0), // bottom-left
|
|
||||||
vec2<f32>(0.0, 0.0), // top-left
|
|
||||||
vec2<f32>(1.0, 1.0), // bottom-right
|
|
||||||
vec2<f32>(1.0, 1.0), // bottom-right
|
|
||||||
vec2<f32>(0.0, 0.0), // top-left
|
|
||||||
vec2<f32>(1.0, 0.0), // top-right
|
|
||||||
);
|
|
||||||
var uv = uvs[VertexIndex % 6];
|
|
||||||
uv.x *= sprite.size.x / sprite.sheet_size.x;
|
|
||||||
uv.y *= sprite.size.y / sprite.sheet_size.y;
|
|
||||||
uv.x += sprite.pos.x / sprite.sheet_size.x;
|
|
||||||
uv.y += sprite.pos.y / sprite.sheet_size.y;
|
|
||||||
|
|
||||||
var output : VertexOutput;
|
|
||||||
output.Position = vec4<f32>(pos.x, 0.0, pos.y, 1.0) * uniforms.modelViewProjectionMatrix;
|
|
||||||
output.fragUV = uv;
|
|
||||||
output.spriteIndex = f32(VertexIndex / 6);
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
@group(0) @binding(1) var spriteSampler: sampler;
|
|
||||||
@group(0) @binding(2) var spriteTexture: texture_2d<f32>;
|
|
||||||
|
|
||||||
@fragment
|
|
||||||
fn frag_main(
|
|
||||||
@location(0) fragUV: vec2<f32>,
|
|
||||||
@location(1) spriteIndex: f32
|
|
||||||
) -> @location(0) vec4<f32> {
|
|
||||||
var color = textureSample(spriteTexture, spriteSampler, fragUV);
|
|
||||||
if (spriteIndex == 0.0) {
|
|
||||||
if (color[3] > 0.0) {
|
|
||||||
color[0] = 0.3;
|
|
||||||
color[1] = 0.2;
|
|
||||||
color[2] = 0.5;
|
|
||||||
color[3] = 1.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
{
|
|
||||||
"sheet": {
|
|
||||||
"width": 352.0,
|
|
||||||
"height": 32.0
|
|
||||||
},
|
|
||||||
"sprites": [
|
|
||||||
{
|
|
||||||
"pos": [ 0.0, 0.0 ],
|
|
||||||
"size": [ 32.0, 32.0 ],
|
|
||||||
"world_pos": [ 0.0, 0.0 ],
|
|
||||||
"is_player": true,
|
|
||||||
"frames": {
|
|
||||||
"down": [ 0.0, 0.0 ],
|
|
||||||
"left": [ 32.0, 0.0 ],
|
|
||||||
"right": [ 64.0, 0.0 ],
|
|
||||||
"up": [ 96.0, 0.0 ]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"pos": [ 128.0, 0.0 ],
|
|
||||||
"size": [ 32.0, 32.0 ],
|
|
||||||
"world_pos": [ 32.0, 32.0 ],
|
|
||||||
"frames": {
|
|
||||||
"down": [ 128.0, 0.0 ],
|
|
||||||
"left": [ 160.0, 0.0 ],
|
|
||||||
"right": [ 192.0, 0.0 ],
|
|
||||||
"up": [ 224.0, 0.0 ]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"pos": [ 128.0, 0.0 ],
|
|
||||||
"size": [ 32.0, 32.0 ],
|
|
||||||
"world_pos": [ 64.0, 64.0 ],
|
|
||||||
"frames": {
|
|
||||||
"down": [ 0.0, 0.0 ],
|
|
||||||
"left": [ 0.0, 0.0 ],
|
|
||||||
"right": [ 0.0, 0.0 ],
|
|
||||||
"up": [ 0.0, 0.0 ]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"pos": [ 256.0, 0.0 ],
|
|
||||||
"size": [ 32.0, 32.0 ],
|
|
||||||
"world_pos": [ 96.0, 96.0 ],
|
|
||||||
"frames": {
|
|
||||||
"down": [ 0.0, 0.0 ],
|
|
||||||
"left": [ 0.0, 0.0 ],
|
|
||||||
"right": [ 0.0, 0.0 ],
|
|
||||||
"up": [ 0.0, 0.0 ]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
@ -1,268 +0,0 @@
|
||||||
/// A port of Austin Eng's "computeBoids" webgpu sample.
|
|
||||||
/// https://github.com/austinEng/webgpu-samples/blob/main/src/sample/computeBoids/main.ts
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
title_timer: core.Timer,
|
|
||||||
timer: core.Timer,
|
|
||||||
compute_pipeline: *gpu.ComputePipeline,
|
|
||||||
render_pipeline: *gpu.RenderPipeline,
|
|
||||||
sprite_vertex_buffer: *gpu.Buffer,
|
|
||||||
particle_buffers: [2]*gpu.Buffer,
|
|
||||||
particle_bind_groups: [2]*gpu.BindGroup,
|
|
||||||
sim_param_buffer: *gpu.Buffer,
|
|
||||||
frame_counter: usize,
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
// Use experimental sysgpu graphics API
|
|
||||||
pub const use_sysgpu = true;
|
|
||||||
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
|
|
||||||
const num_particle = 1500;
|
|
||||||
|
|
||||||
var sim_params = [_]f32{
|
|
||||||
0.04, // .delta_T
|
|
||||||
0.1, // .rule_1_distance
|
|
||||||
0.025, // .rule_2_distance
|
|
||||||
0.025, // .rule_3_distance
|
|
||||||
0.02, // .rule_1_scale
|
|
||||||
0.05, // .rule_2_scale
|
|
||||||
0.005, // .rule_3_scale
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{});
|
|
||||||
|
|
||||||
const sprite_shader_module = core.device.createShaderModuleWGSL(
|
|
||||||
"sprite.wgsl",
|
|
||||||
@embedFile("sprite.wgsl"),
|
|
||||||
);
|
|
||||||
defer sprite_shader_module.release();
|
|
||||||
|
|
||||||
const update_sprite_shader_module = core.device.createShaderModuleWGSL(
|
|
||||||
"updateSprites.wgsl",
|
|
||||||
@embedFile("updateSprites.wgsl"),
|
|
||||||
);
|
|
||||||
defer update_sprite_shader_module.release();
|
|
||||||
|
|
||||||
const instanced_particles_attributes = [_]gpu.VertexAttribute{
|
|
||||||
.{
|
|
||||||
// instance position
|
|
||||||
.shader_location = 0,
|
|
||||||
.offset = 0,
|
|
||||||
.format = .float32x2,
|
|
||||||
},
|
|
||||||
.{
|
|
||||||
// instance velocity
|
|
||||||
.shader_location = 1,
|
|
||||||
.offset = 2 * 4,
|
|
||||||
.format = .float32x2,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const vertex_buffer_attributes = [_]gpu.VertexAttribute{
|
|
||||||
.{
|
|
||||||
// vertex positions
|
|
||||||
.shader_location = 2,
|
|
||||||
.offset = 0,
|
|
||||||
.format = .float32x2,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const render_pipeline = core.device.createRenderPipeline(&gpu.RenderPipeline.Descriptor{
|
|
||||||
.vertex = gpu.VertexState.init(.{
|
|
||||||
.module = sprite_shader_module,
|
|
||||||
.entry_point = "vert_main",
|
|
||||||
.buffers = &.{
|
|
||||||
gpu.VertexBufferLayout.init(.{
|
|
||||||
// instanced particles buffer
|
|
||||||
.array_stride = 4 * 4,
|
|
||||||
.step_mode = .instance,
|
|
||||||
.attributes = &instanced_particles_attributes,
|
|
||||||
}),
|
|
||||||
gpu.VertexBufferLayout.init(.{
|
|
||||||
// vertex buffer
|
|
||||||
.array_stride = 2 * 4,
|
|
||||||
.step_mode = .vertex,
|
|
||||||
.attributes = &vertex_buffer_attributes,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
.fragment = &gpu.FragmentState.init(.{
|
|
||||||
.module = sprite_shader_module,
|
|
||||||
.entry_point = "frag_main",
|
|
||||||
.targets = &[_]gpu.ColorTargetState{.{
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
}},
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
const compute_pipeline = core.device.createComputePipeline(&gpu.ComputePipeline.Descriptor{ .compute = gpu.ProgrammableStageDescriptor{
|
|
||||||
.module = update_sprite_shader_module,
|
|
||||||
.entry_point = "main",
|
|
||||||
} });
|
|
||||||
|
|
||||||
const vert_buffer_data = [_]f32{
|
|
||||||
-0.01, -0.02, 0.01,
|
|
||||||
-0.02, 0.0, 0.02,
|
|
||||||
};
|
|
||||||
|
|
||||||
const sprite_vertex_buffer = core.device.createBuffer(&gpu.Buffer.Descriptor{
|
|
||||||
.label = "sprite_vertex_buffer",
|
|
||||||
.usage = .{ .vertex = true },
|
|
||||||
.mapped_at_creation = .true,
|
|
||||||
.size = vert_buffer_data.len * @sizeOf(f32),
|
|
||||||
});
|
|
||||||
const vertex_mapped = sprite_vertex_buffer.getMappedRange(f32, 0, vert_buffer_data.len);
|
|
||||||
@memcpy(vertex_mapped.?, vert_buffer_data[0..]);
|
|
||||||
sprite_vertex_buffer.unmap();
|
|
||||||
|
|
||||||
const sim_param_buffer = core.device.createBuffer(&gpu.Buffer.Descriptor{
|
|
||||||
.label = "sim_param_buffer",
|
|
||||||
.usage = .{ .uniform = true, .copy_dst = true },
|
|
||||||
.size = sim_params.len * @sizeOf(f32),
|
|
||||||
});
|
|
||||||
core.queue.writeBuffer(sim_param_buffer, 0, sim_params[0..]);
|
|
||||||
|
|
||||||
var initial_particle_data: [num_particle * 4]f32 = undefined;
|
|
||||||
var rng = std.rand.DefaultPrng.init(0);
|
|
||||||
const random = rng.random();
|
|
||||||
var i: usize = 0;
|
|
||||||
while (i < num_particle) : (i += 1) {
|
|
||||||
initial_particle_data[4 * i + 0] = 2 * (random.float(f32) - 0.5);
|
|
||||||
initial_particle_data[4 * i + 1] = 2 * (random.float(f32) - 0.5);
|
|
||||||
initial_particle_data[4 * i + 2] = 2 * (random.float(f32) - 0.5) * 0.1;
|
|
||||||
initial_particle_data[4 * i + 3] = 2 * (random.float(f32) - 0.5) * 0.1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var particle_buffers: [2]*gpu.Buffer = undefined;
|
|
||||||
var particle_bind_groups: [2]*gpu.BindGroup = undefined;
|
|
||||||
i = 0;
|
|
||||||
while (i < 2) : (i += 1) {
|
|
||||||
particle_buffers[i] = core.device.createBuffer(&gpu.Buffer.Descriptor{
|
|
||||||
.label = "particle_buffer",
|
|
||||||
.mapped_at_creation = .true,
|
|
||||||
.usage = .{
|
|
||||||
.vertex = true,
|
|
||||||
.storage = true,
|
|
||||||
},
|
|
||||||
.size = initial_particle_data.len * @sizeOf(f32),
|
|
||||||
});
|
|
||||||
const mapped = particle_buffers[i].getMappedRange(f32, 0, initial_particle_data.len);
|
|
||||||
@memcpy(mapped.?, initial_particle_data[0..]);
|
|
||||||
particle_buffers[i].unmap();
|
|
||||||
}
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
while (i < 2) : (i += 1) {
|
|
||||||
const layout = compute_pipeline.getBindGroupLayout(0);
|
|
||||||
defer layout.release();
|
|
||||||
|
|
||||||
particle_bind_groups[i] = core.device.createBindGroup(&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = layout,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.buffer(0, sim_param_buffer, 0, sim_params.len * @sizeOf(f32), sim_params.len * @sizeOf(f32)),
|
|
||||||
gpu.BindGroup.Entry.buffer(1, particle_buffers[i], 0, initial_particle_data.len * @sizeOf(f32), 4 * @sizeOf(f32)),
|
|
||||||
gpu.BindGroup.Entry.buffer(2, particle_buffers[(i + 1) % 2], 0, initial_particle_data.len * @sizeOf(f32), 4 * @sizeOf(f32)),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
app.* = .{
|
|
||||||
.timer = try core.Timer.start(),
|
|
||||||
.title_timer = try core.Timer.start(),
|
|
||||||
.compute_pipeline = compute_pipeline,
|
|
||||||
.render_pipeline = render_pipeline,
|
|
||||||
.sprite_vertex_buffer = sprite_vertex_buffer,
|
|
||||||
.particle_buffers = particle_buffers,
|
|
||||||
.particle_bind_groups = particle_bind_groups,
|
|
||||||
.sim_param_buffer = sim_param_buffer,
|
|
||||||
.frame_counter = 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
defer _ = gpa.deinit();
|
|
||||||
defer core.deinit();
|
|
||||||
|
|
||||||
app.compute_pipeline.release();
|
|
||||||
app.render_pipeline.release();
|
|
||||||
app.sprite_vertex_buffer.release();
|
|
||||||
for (app.particle_buffers) |particle_buffer| particle_buffer.release();
|
|
||||||
for (app.particle_bind_groups) |particle_bind_group| particle_bind_group.release();
|
|
||||||
app.sim_param_buffer.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(app: *App) !bool {
|
|
||||||
const delta_time = app.timer.lap();
|
|
||||||
|
|
||||||
var iter = core.pollEvents();
|
|
||||||
while (iter.next()) |event| {
|
|
||||||
if (event == .close) return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = back_buffer_view,
|
|
||||||
.clear_value = std.mem.zeroes(gpu.Color),
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
|
|
||||||
const render_pass_descriptor = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{
|
|
||||||
color_attachment,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
sim_params[0] = @as(f32, @floatCast(delta_time));
|
|
||||||
core.queue.writeBuffer(app.sim_param_buffer, 0, sim_params[0..]);
|
|
||||||
|
|
||||||
const command_encoder = core.device.createCommandEncoder(null);
|
|
||||||
{
|
|
||||||
const pass_encoder = command_encoder.beginComputePass(null);
|
|
||||||
pass_encoder.setPipeline(app.compute_pipeline);
|
|
||||||
pass_encoder.setBindGroup(0, app.particle_bind_groups[app.frame_counter % 2], null);
|
|
||||||
pass_encoder.dispatchWorkgroups(@as(u32, @intFromFloat(@ceil(@as(f32, num_particle) / 64))), 1, 1);
|
|
||||||
pass_encoder.end();
|
|
||||||
pass_encoder.release();
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const pass_encoder = command_encoder.beginRenderPass(&render_pass_descriptor);
|
|
||||||
pass_encoder.setPipeline(app.render_pipeline);
|
|
||||||
pass_encoder.setVertexBuffer(0, app.particle_buffers[(app.frame_counter + 1) % 2], 0, num_particle * 4 * @sizeOf(f32));
|
|
||||||
pass_encoder.setVertexBuffer(1, app.sprite_vertex_buffer, 0, 6 * @sizeOf(f32));
|
|
||||||
pass_encoder.draw(3, num_particle, 0, 0);
|
|
||||||
pass_encoder.end();
|
|
||||||
pass_encoder.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
app.frame_counter += 1;
|
|
||||||
if (app.frame_counter % 60 == 0) {
|
|
||||||
std.log.info("Frame {}", .{app.frame_counter});
|
|
||||||
}
|
|
||||||
|
|
||||||
var command = command_encoder.finish(null);
|
|
||||||
command_encoder.release();
|
|
||||||
core.queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
command.release();
|
|
||||||
|
|
||||||
core.swap_chain.present();
|
|
||||||
back_buffer_view.release();
|
|
||||||
|
|
||||||
// update the window title every second
|
|
||||||
if (app.title_timer.read() >= 1.0) {
|
|
||||||
app.title_timer.reset();
|
|
||||||
try core.printTitle("Boids [ {d}fps ] [ Input {d}hz ]", .{
|
|
||||||
core.frameRate(),
|
|
||||||
core.inputRate(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
@vertex
|
|
||||||
fn vert_main(@location(0) a_particlePos : vec2<f32>,
|
|
||||||
@location(1) a_particleVel : vec2<f32>,
|
|
||||||
@location(2) a_pos : vec2<f32>) -> @builtin(position) vec4<f32> {
|
|
||||||
let angle = -atan2(a_particleVel.x, a_particleVel.y);
|
|
||||||
let pos = vec2<f32>(
|
|
||||||
(a_pos.x * cos(angle)) - (a_pos.y * sin(angle)),
|
|
||||||
(a_pos.x * sin(angle)) + (a_pos.y * cos(angle)));
|
|
||||||
return vec4<f32>(pos + a_particlePos, 0.0, 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@fragment
|
|
||||||
fn frag_main() -> @location(0) vec4<f32> {
|
|
||||||
return vec4<f32>(1.0, 1.0, 1.0, 1.0);
|
|
||||||
}
|
|
||||||
|
|
@ -1,90 +0,0 @@
|
||||||
struct Particle {
|
|
||||||
pos : vec2<f32>,
|
|
||||||
vel : vec2<f32>,
|
|
||||||
};
|
|
||||||
struct SimParams {
|
|
||||||
deltaT : f32,
|
|
||||||
rule1Distance : f32,
|
|
||||||
rule2Distance : f32,
|
|
||||||
rule3Distance : f32,
|
|
||||||
rule1Scale : f32,
|
|
||||||
rule2Scale : f32,
|
|
||||||
rule3Scale : f32,
|
|
||||||
};
|
|
||||||
struct Particles {
|
|
||||||
particles : array<Particle>,
|
|
||||||
};
|
|
||||||
@binding(0) @group(0) var<uniform> params : SimParams;
|
|
||||||
@binding(1) @group(0) var<storage, read> particlesA : Particles;
|
|
||||||
@binding(2) @group(0) var<storage, read_write> particlesB : Particles;
|
|
||||||
|
|
||||||
// https://github.com/austinEng/Project6-Vulkan-Flocking/blob/master/data/shaders/computeparticles/particle.comp
|
|
||||||
@compute @workgroup_size(64)
|
|
||||||
fn main(@builtin(global_invocation_id) GlobalInvocationID : vec3<u32>) {
|
|
||||||
var index : u32 = GlobalInvocationID.x;
|
|
||||||
|
|
||||||
if (index >= arrayLength(&particlesA.particles)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var vPos = particlesA.particles[index].pos;
|
|
||||||
var vVel = particlesA.particles[index].vel;
|
|
||||||
var cMass = vec2<f32>(0.0, 0.0);
|
|
||||||
var cVel = vec2<f32>(0.0, 0.0);
|
|
||||||
var colVel = vec2<f32>(0.0, 0.0);
|
|
||||||
var cMassCount : u32 = 0u;
|
|
||||||
var cVelCount : u32 = 0u;
|
|
||||||
var pos : vec2<f32>;
|
|
||||||
var vel : vec2<f32>;
|
|
||||||
|
|
||||||
for (var i : u32 = 0u; i < arrayLength(&particlesA.particles); i = i + 1u) {
|
|
||||||
if (i == index) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
pos = particlesA.particles[i].pos.xy;
|
|
||||||
vel = particlesA.particles[i].vel.xy;
|
|
||||||
if (distance(pos, vPos) < params.rule1Distance) {
|
|
||||||
cMass = cMass + pos;
|
|
||||||
cMassCount = cMassCount + 1u;
|
|
||||||
}
|
|
||||||
if (distance(pos, vPos) < params.rule2Distance) {
|
|
||||||
colVel = colVel - (pos - vPos);
|
|
||||||
}
|
|
||||||
if (distance(pos, vPos) < params.rule3Distance) {
|
|
||||||
cVel = cVel + vel;
|
|
||||||
cVelCount = cVelCount + 1u;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (cMassCount > 0u) {
|
|
||||||
var temp = f32(cMassCount);
|
|
||||||
cMass = (cMass / vec2<f32>(temp, temp)) - vPos;
|
|
||||||
}
|
|
||||||
if (cVelCount > 0u) {
|
|
||||||
var temp = f32(cVelCount);
|
|
||||||
cVel = cVel / vec2<f32>(temp, temp);
|
|
||||||
}
|
|
||||||
vVel = vVel + (cMass * params.rule1Scale) + (colVel * params.rule2Scale) +
|
|
||||||
(cVel * params.rule3Scale);
|
|
||||||
|
|
||||||
// clamp velocity for a more pleasing simulation
|
|
||||||
vVel = normalize(vVel) * clamp(length(vVel), 0.0, 0.1);
|
|
||||||
// kinematic update
|
|
||||||
vPos = vPos + (vVel * params.deltaT);
|
|
||||||
// Wrap around boundary
|
|
||||||
if (vPos.x < -1.0) {
|
|
||||||
vPos.x = 1.0;
|
|
||||||
}
|
|
||||||
if (vPos.x > 1.0) {
|
|
||||||
vPos.x = -1.0;
|
|
||||||
}
|
|
||||||
if (vPos.y < -1.0) {
|
|
||||||
vPos.y = 1.0;
|
|
||||||
}
|
|
||||||
if (vPos.y > 1.0) {
|
|
||||||
vPos.y = -1.0;
|
|
||||||
}
|
|
||||||
// Write back
|
|
||||||
particlesB.particles[index].pos = vPos;
|
|
||||||
particlesB.particles[index].vel = vVel;
|
|
||||||
}
|
|
||||||
|
|
@ -1,84 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
const renderer = @import("renderer.zig");
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
// Use experimental sysgpu graphics API
|
|
||||||
pub const use_sysgpu = true;
|
|
||||||
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
|
|
||||||
title_timer: core.Timer,
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{});
|
|
||||||
app.* = .{
|
|
||||||
.title_timer = try core.Timer.start(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
_ = app;
|
|
||||||
defer _ = gpa.deinit();
|
|
||||||
defer core.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(app: *App) !bool {
|
|
||||||
var iter = core.pollEvents();
|
|
||||||
while (iter.next()) |event| {
|
|
||||||
switch (event) {
|
|
||||||
.key_press => |ev| {
|
|
||||||
if (ev.key == .space) return true;
|
|
||||||
},
|
|
||||||
.close => return true,
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
app.render();
|
|
||||||
|
|
||||||
// update the window title every second
|
|
||||||
if (app.title_timer.read() >= 1.0) {
|
|
||||||
app.title_timer.reset();
|
|
||||||
try core.printTitle("Clear Color [ {d}fps ] [ Input {d}hz ]", .{
|
|
||||||
core.frameRate(),
|
|
||||||
core.inputRate(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(app: *App) void {
|
|
||||||
_ = app;
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = back_buffer_view,
|
|
||||||
.clear_value = gpu.Color{ .r = 0.0, .g = 0.0, .b = 1.0, .a = 1.0 },
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
const render_pass_info = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{color_attachment},
|
|
||||||
});
|
|
||||||
|
|
||||||
const pass = encoder.beginRenderPass(&render_pass_info);
|
|
||||||
pass.end();
|
|
||||||
pass.release();
|
|
||||||
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
encoder.release();
|
|
||||||
|
|
||||||
var queue = core.queue;
|
|
||||||
queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
command.release();
|
|
||||||
core.swap_chain.present();
|
|
||||||
back_buffer_view.release();
|
|
||||||
}
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
pub const Renderer = @This();
|
|
||||||
|
|
||||||
pub fn RenderUpdate() void {
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = back_buffer_view,
|
|
||||||
.clear_value = gpu.Color{ .r = 0.0, .g = 0.0, .b = 1.0, .a = 1.0 },
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
const render_pass_info = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{color_attachment},
|
|
||||||
});
|
|
||||||
|
|
||||||
const pass = encoder.beginRenderPass(&render_pass_info);
|
|
||||||
pass.end();
|
|
||||||
pass.release();
|
|
||||||
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
encoder.release();
|
|
||||||
|
|
||||||
const queue = core.queue;
|
|
||||||
queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
command.release();
|
|
||||||
core.swap_chain.present();
|
|
||||||
back_buffer_view.release();
|
|
||||||
}
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
pub const Vertex = extern struct {
|
|
||||||
pos: @Vector(4, f32),
|
|
||||||
col: @Vector(4, f32),
|
|
||||||
uv: @Vector(2, f32),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const vertices = [_]Vertex{
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
};
|
|
||||||
|
|
@ -1,398 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
const zm = @import("zmath");
|
|
||||||
const zigimg = @import("zigimg");
|
|
||||||
const Vertex = @import("cube_mesh.zig").Vertex;
|
|
||||||
const vertices = @import("cube_mesh.zig").vertices;
|
|
||||||
const assets = @import("assets");
|
|
||||||
|
|
||||||
const UniformBufferObject = struct {
|
|
||||||
mat: zm.Mat,
|
|
||||||
};
|
|
||||||
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
|
|
||||||
title_timer: core.Timer,
|
|
||||||
timer: core.Timer,
|
|
||||||
pipeline: *gpu.RenderPipeline,
|
|
||||||
vertex_buffer: *gpu.Buffer,
|
|
||||||
uniform_buffer: *gpu.Buffer,
|
|
||||||
bind_group: *gpu.BindGroup,
|
|
||||||
depth_texture: *gpu.Texture,
|
|
||||||
depth_texture_view: *gpu.TextureView,
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
// Use experimental sysgpu graphics API
|
|
||||||
pub const use_sysgpu = true;
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{});
|
|
||||||
|
|
||||||
const allocator = gpa.allocator();
|
|
||||||
|
|
||||||
const shader_module = core.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.wgsl"));
|
|
||||||
defer shader_module.release();
|
|
||||||
|
|
||||||
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.init(.{
|
|
||||||
.array_stride = @sizeOf(Vertex),
|
|
||||||
.step_mode = .vertex,
|
|
||||||
.attributes = &vertex_attributes,
|
|
||||||
});
|
|
||||||
|
|
||||||
const blend = gpu.BlendState{
|
|
||||||
.color = .{
|
|
||||||
.operation = .add,
|
|
||||||
.src_factor = .src_alpha,
|
|
||||||
.dst_factor = .one_minus_src_alpha,
|
|
||||||
},
|
|
||||||
.alpha = .{
|
|
||||||
.operation = .add,
|
|
||||||
.src_factor = .one,
|
|
||||||
.dst_factor = .zero,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
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{
|
|
||||||
.fragment = &fragment,
|
|
||||||
// Enable depth testing so that the fragment closest to the camera
|
|
||||||
// is rendered in front.
|
|
||||||
.depth_stencil = &.{
|
|
||||||
.format = .depth24_plus,
|
|
||||||
.depth_write_enabled = .true,
|
|
||||||
.depth_compare = .less,
|
|
||||||
},
|
|
||||||
.vertex = gpu.VertexState.init(.{
|
|
||||||
.module = shader_module,
|
|
||||||
.entry_point = "vertex_main",
|
|
||||||
.buffers = &.{vertex_buffer_layout},
|
|
||||||
}),
|
|
||||||
.primitive = .{
|
|
||||||
// Since the cube has its face pointing outwards, cull_mode must be
|
|
||||||
// set to .front or .none here since we are inside the cube looking out.
|
|
||||||
// Ideally you would set this to .back and have a custom cube primitive
|
|
||||||
// with the faces pointing towards the inside of the cube.
|
|
||||||
.cull_mode = .none,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const pipeline = core.device.createRenderPipeline(&pipeline_descriptor);
|
|
||||||
|
|
||||||
const vertex_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .vertex = true },
|
|
||||||
.size = @sizeOf(Vertex) * vertices.len,
|
|
||||||
.mapped_at_creation = .true,
|
|
||||||
});
|
|
||||||
const vertex_mapped = vertex_buffer.getMappedRange(Vertex, 0, vertices.len);
|
|
||||||
@memcpy(vertex_mapped.?, vertices[0..]);
|
|
||||||
vertex_buffer.unmap();
|
|
||||||
|
|
||||||
const uniform_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .copy_dst = true, .uniform = true },
|
|
||||||
.size = @sizeOf(UniformBufferObject),
|
|
||||||
.mapped_at_creation = .false,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create a sampler with linear filtering for smooth interpolation.
|
|
||||||
const sampler = core.device.createSampler(&.{
|
|
||||||
.mag_filter = .linear,
|
|
||||||
.min_filter = .linear,
|
|
||||||
});
|
|
||||||
|
|
||||||
const queue = core.queue;
|
|
||||||
|
|
||||||
// WebGPU expects the cubemap textures in this order: (+X,-X,+Y,-Y,+Z,-Z)
|
|
||||||
var images: [6]zigimg.Image = undefined;
|
|
||||||
images[0] = try zigimg.Image.fromMemory(allocator, assets.skybox_posx_png);
|
|
||||||
defer images[0].deinit();
|
|
||||||
images[1] = try zigimg.Image.fromMemory(allocator, assets.skybox_negx_png);
|
|
||||||
defer images[1].deinit();
|
|
||||||
images[2] = try zigimg.Image.fromMemory(allocator, assets.skybox_posy_png);
|
|
||||||
defer images[2].deinit();
|
|
||||||
images[3] = try zigimg.Image.fromMemory(allocator, assets.skybox_negy_png);
|
|
||||||
defer images[3].deinit();
|
|
||||||
images[4] = try zigimg.Image.fromMemory(allocator, assets.skybox_posz_png);
|
|
||||||
defer images[4].deinit();
|
|
||||||
images[5] = try zigimg.Image.fromMemory(allocator, assets.skybox_negz_png);
|
|
||||||
defer images[5].deinit();
|
|
||||||
|
|
||||||
// Use the first image of the set for sizing
|
|
||||||
const img_size = gpu.Extent3D{
|
|
||||||
.width = @as(u32, @intCast(images[0].width)),
|
|
||||||
.height = @as(u32, @intCast(images[0].height)),
|
|
||||||
};
|
|
||||||
|
|
||||||
// We set depth_or_array_layers to 6 here to indicate there are 6 images in this texture
|
|
||||||
const tex_size = gpu.Extent3D{
|
|
||||||
.width = @as(u32, @intCast(images[0].width)),
|
|
||||||
.height = @as(u32, @intCast(images[0].height)),
|
|
||||||
.depth_or_array_layers = 6,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Same as a regular texture, but with a Z of 6 (defined in tex_size)
|
|
||||||
const cube_texture = core.device.createTexture(&.{
|
|
||||||
.size = tex_size,
|
|
||||||
.format = .rgba8_unorm,
|
|
||||||
.dimension = .dimension_2d,
|
|
||||||
.usage = .{
|
|
||||||
.texture_binding = true,
|
|
||||||
.copy_dst = true,
|
|
||||||
.render_attachment = false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const data_layout = gpu.Texture.DataLayout{
|
|
||||||
.bytes_per_row = @as(u32, @intCast(images[0].width * 4)),
|
|
||||||
.rows_per_image = @as(u32, @intCast(images[0].height)),
|
|
||||||
};
|
|
||||||
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
|
|
||||||
// We have to create a staging buffer, copy all the image data into the
|
|
||||||
// staging buffer at the correct Z offset, encode a command to copy
|
|
||||||
// the buffer to the texture for each image, then push it to the command
|
|
||||||
// queue
|
|
||||||
var staging_buff: [6]*gpu.Buffer = undefined;
|
|
||||||
var i: u32 = 0;
|
|
||||||
while (i < 6) : (i += 1) {
|
|
||||||
staging_buff[i] = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .copy_src = true, .map_write = true },
|
|
||||||
.size = @as(u64, @intCast(images[0].width)) * @as(u64, @intCast(images[0].height)) * @sizeOf(u32),
|
|
||||||
.mapped_at_creation = .true,
|
|
||||||
});
|
|
||||||
switch (images[i].pixels) {
|
|
||||||
.rgba32 => |pixels| {
|
|
||||||
// Map a section of the staging buffer
|
|
||||||
const staging_map = staging_buff[i].getMappedRange(u32, 0, @as(u64, @intCast(images[i].width)) * @as(u64, @intCast(images[i].height)));
|
|
||||||
// Copy the image data into the mapped buffer
|
|
||||||
@memcpy(staging_map.?, @as([]u32, @ptrCast(@alignCast(pixels))));
|
|
||||||
// And release the mapping
|
|
||||||
staging_buff[i].unmap();
|
|
||||||
},
|
|
||||||
.rgb24 => |pixels| {
|
|
||||||
const staging_map = staging_buff[i].getMappedRange(u32, 0, @as(u64, @intCast(images[i].width)) * @as(u64, @intCast(images[i].height)));
|
|
||||||
// In this case, we have to convert the data to rgba32 first
|
|
||||||
const data = try rgb24ToRgba32(allocator, pixels);
|
|
||||||
defer data.deinit(allocator);
|
|
||||||
@memcpy(staging_map.?, @as([]u32, @ptrCast(@alignCast(data.rgba32))));
|
|
||||||
staging_buff[i].unmap();
|
|
||||||
},
|
|
||||||
else => @panic("unsupported image color format"),
|
|
||||||
}
|
|
||||||
|
|
||||||
// These define the source and target for the buffer to texture copy command
|
|
||||||
const copy_buff = gpu.ImageCopyBuffer{
|
|
||||||
.layout = data_layout,
|
|
||||||
.buffer = staging_buff[i],
|
|
||||||
};
|
|
||||||
const copy_tex = gpu.ImageCopyTexture{
|
|
||||||
.texture = cube_texture,
|
|
||||||
.origin = gpu.Origin3D{ .x = 0, .y = 0, .z = i },
|
|
||||||
};
|
|
||||||
|
|
||||||
// Encode the copy command, we do this for every image in the texture.
|
|
||||||
encoder.copyBufferToTexture(©_buff, ©_tex, &img_size);
|
|
||||||
staging_buff[i].release();
|
|
||||||
}
|
|
||||||
// Now that the commands to copy our buffer data to the texture is filled,
|
|
||||||
// push the encoded commands over to the queue and execute to get the
|
|
||||||
// texture filled with the image data.
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
encoder.release();
|
|
||||||
queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
command.release();
|
|
||||||
|
|
||||||
// The textureView in the bind group needs dimension defined as "dimension_cube".
|
|
||||||
const cube_texture_view = cube_texture.createView(&gpu.TextureView.Descriptor{ .dimension = .dimension_cube });
|
|
||||||
cube_texture.release();
|
|
||||||
|
|
||||||
const bind_group_layout = pipeline.getBindGroupLayout(0);
|
|
||||||
const bind_group = core.device.createBindGroup(
|
|
||||||
&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = bind_group_layout,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.buffer(0, uniform_buffer, 0, @sizeOf(UniformBufferObject), @sizeOf(UniformBufferObject)),
|
|
||||||
gpu.BindGroup.Entry.sampler(1, sampler),
|
|
||||||
gpu.BindGroup.Entry.textureView(2, cube_texture_view),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
sampler.release();
|
|
||||||
cube_texture_view.release();
|
|
||||||
bind_group_layout.release();
|
|
||||||
|
|
||||||
const depth_texture = core.device.createTexture(&gpu.Texture.Descriptor{
|
|
||||||
.size = gpu.Extent3D{
|
|
||||||
.width = core.descriptor.width,
|
|
||||||
.height = core.descriptor.height,
|
|
||||||
},
|
|
||||||
.format = .depth24_plus,
|
|
||||||
.usage = .{
|
|
||||||
.render_attachment = true,
|
|
||||||
.texture_binding = true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const depth_texture_view = depth_texture.createView(&gpu.TextureView.Descriptor{
|
|
||||||
.format = .depth24_plus,
|
|
||||||
.dimension = .dimension_2d,
|
|
||||||
.array_layer_count = 1,
|
|
||||||
.mip_level_count = 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
app.timer = try core.Timer.start();
|
|
||||||
app.title_timer = try core.Timer.start();
|
|
||||||
app.pipeline = pipeline;
|
|
||||||
app.vertex_buffer = vertex_buffer;
|
|
||||||
app.uniform_buffer = uniform_buffer;
|
|
||||||
app.bind_group = bind_group;
|
|
||||||
app.depth_texture = depth_texture;
|
|
||||||
app.depth_texture_view = depth_texture_view;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
defer _ = gpa.deinit();
|
|
||||||
defer core.deinit();
|
|
||||||
|
|
||||||
app.pipeline.release();
|
|
||||||
app.vertex_buffer.release();
|
|
||||||
app.uniform_buffer.release();
|
|
||||||
app.bind_group.release();
|
|
||||||
app.depth_texture.release();
|
|
||||||
app.depth_texture_view.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(app: *App) !bool {
|
|
||||||
var iter = core.pollEvents();
|
|
||||||
while (iter.next()) |event| {
|
|
||||||
switch (event) {
|
|
||||||
.key_press => |ev| {
|
|
||||||
if (ev.key == .space) return true;
|
|
||||||
},
|
|
||||||
.close => return true,
|
|
||||||
.framebuffer_resize => |ev| {
|
|
||||||
// If window is resized, recreate depth buffer otherwise we cannot use it.
|
|
||||||
app.depth_texture.release();
|
|
||||||
app.depth_texture = core.device.createTexture(&gpu.Texture.Descriptor{
|
|
||||||
.size = gpu.Extent3D{
|
|
||||||
.width = ev.width,
|
|
||||||
.height = ev.height,
|
|
||||||
},
|
|
||||||
.format = .depth24_plus,
|
|
||||||
.usage = .{
|
|
||||||
.render_attachment = true,
|
|
||||||
.texture_binding = true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
app.depth_texture_view.release();
|
|
||||||
app.depth_texture_view = app.depth_texture.createView(&gpu.TextureView.Descriptor{
|
|
||||||
.format = .depth24_plus,
|
|
||||||
.dimension = .dimension_2d,
|
|
||||||
.array_layer_count = 1,
|
|
||||||
.mip_level_count = 1,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = back_buffer_view,
|
|
||||||
.clear_value = .{ .r = 0.5, .g = 0.5, .b = 0.5, .a = 0.0 },
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
const render_pass_info = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{color_attachment},
|
|
||||||
.depth_stencil_attachment = &.{
|
|
||||||
.view = app.depth_texture_view,
|
|
||||||
.depth_clear_value = 1.0,
|
|
||||||
.depth_load_op = .clear,
|
|
||||||
.depth_store_op = .store,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
{
|
|
||||||
const time = app.timer.read();
|
|
||||||
const aspect = @as(f32, @floatFromInt(core.descriptor.width)) / @as(f32, @floatFromInt(core.descriptor.height));
|
|
||||||
const proj = zm.perspectiveFovRh((2 * std.math.pi) / 5.0, aspect, 0.1, 3000);
|
|
||||||
const model = zm.mul(
|
|
||||||
zm.scaling(1000, 1000, 1000),
|
|
||||||
zm.rotationX(std.math.pi / 2.0 * 3.0),
|
|
||||||
);
|
|
||||||
const view = zm.mul(
|
|
||||||
zm.mul(
|
|
||||||
zm.lookAtRh(
|
|
||||||
zm.Vec{ 0, 0, 0, 1 },
|
|
||||||
zm.Vec{ 1, 0, 0, 1 },
|
|
||||||
zm.Vec{ 0, 0, 1, 0 },
|
|
||||||
),
|
|
||||||
zm.rotationY(time * 0.2),
|
|
||||||
),
|
|
||||||
zm.rotationX((std.math.pi / 10.0) * std.math.sin(time)),
|
|
||||||
);
|
|
||||||
|
|
||||||
const mvp = zm.mul(zm.mul(zm.transpose(model), view), proj);
|
|
||||||
const ubo = UniformBufferObject{ .mat = mvp };
|
|
||||||
|
|
||||||
encoder.writeBuffer(app.uniform_buffer, 0, &[_]UniformBufferObject{ubo});
|
|
||||||
}
|
|
||||||
|
|
||||||
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_group, &.{});
|
|
||||||
pass.draw(vertices.len, 1, 0, 0);
|
|
||||||
pass.end();
|
|
||||||
pass.release();
|
|
||||||
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
encoder.release();
|
|
||||||
|
|
||||||
const queue = core.queue;
|
|
||||||
queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
command.release();
|
|
||||||
core.swap_chain.present();
|
|
||||||
back_buffer_view.release();
|
|
||||||
|
|
||||||
// update the window title every second
|
|
||||||
if (app.title_timer.read() >= 1.0) {
|
|
||||||
app.title_timer.reset();
|
|
||||||
try core.printTitle("Cube Map [ {d}fps ] [ Input {d}hz ]", .{
|
|
||||||
core.frameRate(),
|
|
||||||
core.inputRate(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rgb24ToRgba32(allocator: std.mem.Allocator, in: []zigimg.color.Rgb24) !zigimg.color.PixelStorage {
|
|
||||||
const out = try zigimg.color.PixelStorage.init(allocator, .rgba32, in.len);
|
|
||||||
var i: usize = 0;
|
|
||||||
while (i < in.len) : (i += 1) {
|
|
||||||
out.rgba32[i] = zigimg.color.Rgba32{ .r = in[i].r, .g = in[i].g, .b = in[i].b, .a = 255 };
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
struct Uniforms {
|
|
||||||
modelViewProjectionMatrix : mat4x4<f32>,
|
|
||||||
}
|
|
||||||
@binding(0) @group(0) var<uniform> uniforms : Uniforms;
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) Position : vec4<f32>,
|
|
||||||
@location(0) fragUV : vec2<f32>,
|
|
||||||
@location(1) fragPosition: vec4<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
@vertex
|
|
||||||
fn vertex_main(
|
|
||||||
@location(0) position : vec4<f32>,
|
|
||||||
@location(1) uv : vec2<f32>
|
|
||||||
) -> VertexOutput {
|
|
||||||
var output : VertexOutput;
|
|
||||||
output.Position = uniforms.modelViewProjectionMatrix * position;
|
|
||||||
output.fragUV = uv;
|
|
||||||
output.fragPosition = 0.5 * (position + vec4<f32>(1.0, 1.0, 1.0, 1.0));
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
@group(0) @binding(1) var mySampler: sampler;
|
|
||||||
@group(0) @binding(2) var myTexture: texture_cube<f32>;
|
|
||||||
|
|
||||||
@fragment
|
|
||||||
fn frag_main(
|
|
||||||
@location(0) fragUV: vec2<f32>,
|
|
||||||
@location(1) fragPosition: vec4<f32>
|
|
||||||
) -> @location(0) vec4<f32> {
|
|
||||||
var cubemapVec = fragPosition.xyz - vec3<f32>(0.5, 0.5, 0.5);
|
|
||||||
return textureSample(myTexture, mySampler, cubemapVec);
|
|
||||||
}
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
pub const Vertex = extern struct {
|
|
||||||
pos: @Vector(4, f32),
|
|
||||||
col: @Vector(4, f32),
|
|
||||||
uv: @Vector(2, f32),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const vertices = [_]Vertex{
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
};
|
|
||||||
|
|
@ -1,377 +0,0 @@
|
||||||
//! To get the effect we want, we need a texture on which to render;
|
|
||||||
//! we can't use the swapchain texture directly, but we can get the effect
|
|
||||||
//! by doing the same render pass twice, on the texture and the swapchain.
|
|
||||||
//! We also need a second texture to use on the cube (after the render pass
|
|
||||||
//! it needs to copy the other texture.) We can't use the same texture since
|
|
||||||
//! it would interfere with the synchronization on the gpu during the render pass.
|
|
||||||
//! This demo currently does not work on opengl, because core.descriptor.width/height,
|
|
||||||
//! are set to 0 after core.init() and because webgpu does not implement copyTextureToTexture,
|
|
||||||
//! for opengl
|
|
||||||
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
const zm = @import("zmath");
|
|
||||||
const Vertex = @import("cube_mesh.zig").Vertex;
|
|
||||||
const vertices = @import("cube_mesh.zig").vertices;
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
// Use experimental sysgpu graphics API
|
|
||||||
pub const use_sysgpu = true;
|
|
||||||
|
|
||||||
const UniformBufferObject = struct {
|
|
||||||
mat: zm.Mat,
|
|
||||||
};
|
|
||||||
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
|
|
||||||
title_timer: core.Timer,
|
|
||||||
timer: core.Timer,
|
|
||||||
pipeline: *gpu.RenderPipeline,
|
|
||||||
vertex_buffer: *gpu.Buffer,
|
|
||||||
uniform_buffer: *gpu.Buffer,
|
|
||||||
bind_group: *gpu.BindGroup,
|
|
||||||
depth_texture: ?*gpu.Texture,
|
|
||||||
depth_texture_view: *gpu.TextureView,
|
|
||||||
cube_texture: *gpu.Texture,
|
|
||||||
cube_texture_view: *gpu.TextureView,
|
|
||||||
cube_texture_render: *gpu.Texture,
|
|
||||||
cube_texture_view_render: *gpu.TextureView,
|
|
||||||
sampler: *gpu.Sampler,
|
|
||||||
bgl: *gpu.BindGroupLayout,
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{});
|
|
||||||
|
|
||||||
const shader_module = core.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.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.init(.{
|
|
||||||
.array_stride = @sizeOf(Vertex),
|
|
||||||
.attributes = &vertex_attributes,
|
|
||||||
});
|
|
||||||
|
|
||||||
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 bgle_buffer = gpu.BindGroupLayout.Entry.buffer(0, .{ .vertex = true }, .uniform, true, 0);
|
|
||||||
const bgle_sampler = gpu.BindGroupLayout.Entry.sampler(1, .{ .fragment = true }, .filtering);
|
|
||||||
const bgle_textureview = gpu.BindGroupLayout.Entry.texture(2, .{ .fragment = true }, .float, .dimension_2d, false);
|
|
||||||
const bgl = core.device.createBindGroupLayout(
|
|
||||||
&gpu.BindGroupLayout.Descriptor.init(.{
|
|
||||||
.entries = &.{ bgle_buffer, bgle_sampler, bgle_textureview },
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
const bind_group_layouts = [_]*gpu.BindGroupLayout{bgl};
|
|
||||||
const pipeline_layout = core.device.createPipelineLayout(&gpu.PipelineLayout.Descriptor.init(.{
|
|
||||||
.bind_group_layouts = &bind_group_layouts,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const pipeline_descriptor = gpu.RenderPipeline.Descriptor{
|
|
||||||
.fragment = &fragment,
|
|
||||||
.layout = pipeline_layout,
|
|
||||||
.depth_stencil = &.{
|
|
||||||
.format = .depth24_plus,
|
|
||||||
.depth_write_enabled = .true,
|
|
||||||
.depth_compare = .less,
|
|
||||||
},
|
|
||||||
.vertex = gpu.VertexState.init(.{
|
|
||||||
.module = shader_module,
|
|
||||||
.entry_point = "vertex_main",
|
|
||||||
.buffers = &.{vertex_buffer_layout},
|
|
||||||
}),
|
|
||||||
.primitive = .{
|
|
||||||
.cull_mode = .back,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const vertex_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .vertex = true },
|
|
||||||
.size = @sizeOf(Vertex) * vertices.len,
|
|
||||||
.mapped_at_creation = .true,
|
|
||||||
});
|
|
||||||
const vertex_mapped = vertex_buffer.getMappedRange(Vertex, 0, vertices.len);
|
|
||||||
@memcpy(vertex_mapped.?, vertices[0..]);
|
|
||||||
vertex_buffer.unmap();
|
|
||||||
|
|
||||||
const uniform_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .copy_dst = true, .uniform = true },
|
|
||||||
.size = @sizeOf(UniformBufferObject),
|
|
||||||
.mapped_at_creation = .false,
|
|
||||||
});
|
|
||||||
|
|
||||||
// The texture to put on the cube
|
|
||||||
const cube_texture = core.device.createTexture(&gpu.Texture.Descriptor{
|
|
||||||
.usage = .{ .texture_binding = true, .copy_dst = true },
|
|
||||||
.size = .{ .width = core.descriptor.width, .height = core.descriptor.height },
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
});
|
|
||||||
// The texture on which we render
|
|
||||||
const cube_texture_render = core.device.createTexture(&gpu.Texture.Descriptor{
|
|
||||||
.usage = .{ .render_attachment = true, .copy_src = true },
|
|
||||||
.size = .{ .width = core.descriptor.width, .height = core.descriptor.height },
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
});
|
|
||||||
|
|
||||||
const sampler = core.device.createSampler(&gpu.Sampler.Descriptor{
|
|
||||||
.mag_filter = .linear,
|
|
||||||
.min_filter = .linear,
|
|
||||||
});
|
|
||||||
|
|
||||||
const cube_texture_view = cube_texture.createView(&gpu.TextureView.Descriptor{
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
.dimension = .dimension_2d,
|
|
||||||
.mip_level_count = 1,
|
|
||||||
.array_layer_count = 1,
|
|
||||||
});
|
|
||||||
const cube_texture_view_render = cube_texture_render.createView(&gpu.TextureView.Descriptor{
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
.dimension = .dimension_2d,
|
|
||||||
.mip_level_count = 1,
|
|
||||||
.array_layer_count = 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
const bind_group = core.device.createBindGroup(
|
|
||||||
&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = bgl,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.buffer(0, uniform_buffer, 0, @sizeOf(UniformBufferObject), @sizeOf(UniformBufferObject)),
|
|
||||||
gpu.BindGroup.Entry.sampler(1, sampler),
|
|
||||||
gpu.BindGroup.Entry.textureView(2, cube_texture_view),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
const depth_texture = core.device.createTexture(&gpu.Texture.Descriptor{
|
|
||||||
.usage = .{ .render_attachment = true },
|
|
||||||
.size = .{ .width = core.descriptor.width, .height = core.descriptor.height },
|
|
||||||
.format = .depth24_plus,
|
|
||||||
});
|
|
||||||
const depth_texture_view = depth_texture.createView(&gpu.TextureView.Descriptor{
|
|
||||||
.format = .depth24_plus,
|
|
||||||
.dimension = .dimension_2d,
|
|
||||||
.array_layer_count = 1,
|
|
||||||
.mip_level_count = 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
app.timer = try core.Timer.start();
|
|
||||||
app.title_timer = try core.Timer.start();
|
|
||||||
app.pipeline = core.device.createRenderPipeline(&pipeline_descriptor);
|
|
||||||
app.vertex_buffer = vertex_buffer;
|
|
||||||
app.uniform_buffer = uniform_buffer;
|
|
||||||
app.bind_group = bind_group;
|
|
||||||
app.depth_texture = depth_texture;
|
|
||||||
app.depth_texture_view = depth_texture_view;
|
|
||||||
app.cube_texture = cube_texture;
|
|
||||||
app.cube_texture_view = cube_texture_view;
|
|
||||||
app.cube_texture_render = cube_texture_render;
|
|
||||||
app.cube_texture_view_render = cube_texture_view_render;
|
|
||||||
app.sampler = sampler;
|
|
||||||
app.bgl = bgl;
|
|
||||||
|
|
||||||
shader_module.release();
|
|
||||||
pipeline_layout.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
defer _ = gpa.deinit();
|
|
||||||
defer core.deinit();
|
|
||||||
|
|
||||||
app.pipeline.release();
|
|
||||||
app.bgl.release();
|
|
||||||
app.vertex_buffer.release();
|
|
||||||
app.uniform_buffer.release();
|
|
||||||
app.cube_texture.release();
|
|
||||||
app.cube_texture_render.release();
|
|
||||||
app.sampler.release();
|
|
||||||
app.cube_texture_view.release();
|
|
||||||
app.cube_texture_view_render.release();
|
|
||||||
app.bind_group.release();
|
|
||||||
app.depth_texture.?.release();
|
|
||||||
app.depth_texture_view.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(app: *App) !bool {
|
|
||||||
var iter = core.pollEvents();
|
|
||||||
while (iter.next()) |event| {
|
|
||||||
switch (event) {
|
|
||||||
.key_press => |ev| {
|
|
||||||
if (ev.key == .space) return true;
|
|
||||||
},
|
|
||||||
.close => return true,
|
|
||||||
.framebuffer_resize => |ev| {
|
|
||||||
app.depth_texture.?.release();
|
|
||||||
app.depth_texture = core.device.createTexture(&gpu.Texture.Descriptor{
|
|
||||||
.usage = .{ .render_attachment = true },
|
|
||||||
.size = .{ .width = ev.width, .height = ev.height },
|
|
||||||
.format = .depth24_plus,
|
|
||||||
});
|
|
||||||
|
|
||||||
app.cube_texture.release();
|
|
||||||
app.cube_texture = core.device.createTexture(&gpu.Texture.Descriptor{
|
|
||||||
.usage = .{ .texture_binding = true, .copy_dst = true },
|
|
||||||
.size = .{ .width = ev.width, .height = ev.height },
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
});
|
|
||||||
app.cube_texture_render.release();
|
|
||||||
app.cube_texture_render = core.device.createTexture(&gpu.Texture.Descriptor{
|
|
||||||
.usage = .{ .render_attachment = true, .copy_src = true },
|
|
||||||
.size = .{ .width = ev.width, .height = ev.height },
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
});
|
|
||||||
|
|
||||||
app.depth_texture_view.release();
|
|
||||||
app.depth_texture_view = app.depth_texture.?.createView(&gpu.TextureView.Descriptor{
|
|
||||||
.format = .depth24_plus,
|
|
||||||
.dimension = .dimension_2d,
|
|
||||||
.array_layer_count = 1,
|
|
||||||
.mip_level_count = 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
app.cube_texture_view.release();
|
|
||||||
app.cube_texture_view = app.cube_texture.createView(&gpu.TextureView.Descriptor{
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
.dimension = .dimension_2d,
|
|
||||||
.mip_level_count = 1,
|
|
||||||
.array_layer_count = 1,
|
|
||||||
});
|
|
||||||
app.cube_texture_view_render.release();
|
|
||||||
app.cube_texture_view_render = app.cube_texture_render.createView(&gpu.TextureView.Descriptor{
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
.dimension = .dimension_2d,
|
|
||||||
.mip_level_count = 1,
|
|
||||||
.array_layer_count = 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
app.bind_group.release();
|
|
||||||
app.bind_group = core.device.createBindGroup(
|
|
||||||
&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = app.bgl,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.buffer(0, app.uniform_buffer, 0, @sizeOf(UniformBufferObject), @sizeOf(UniformBufferObject)),
|
|
||||||
gpu.BindGroup.Entry.sampler(1, app.sampler),
|
|
||||||
gpu.BindGroup.Entry.textureView(2, app.cube_texture_view),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const cube_view = app.cube_texture_view_render;
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
|
|
||||||
const cube_color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = cube_view,
|
|
||||||
.clear_value = gpu.Color{ .r = 0.5, .g = 0.5, .b = 0.5, .a = 1 },
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = back_buffer_view,
|
|
||||||
.clear_value = gpu.Color{ .r = 0.5, .g = 0.5, .b = 0.5, .a = 1 },
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
|
|
||||||
const depth_stencil_attachment = gpu.RenderPassDepthStencilAttachment{
|
|
||||||
.view = app.depth_texture_view,
|
|
||||||
.depth_load_op = .clear,
|
|
||||||
.depth_store_op = .store,
|
|
||||||
.depth_clear_value = 1.0,
|
|
||||||
};
|
|
||||||
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
const cube_render_pass_info = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{cube_color_attachment},
|
|
||||||
.depth_stencil_attachment = &depth_stencil_attachment,
|
|
||||||
});
|
|
||||||
const render_pass_info = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{color_attachment},
|
|
||||||
.depth_stencil_attachment = &depth_stencil_attachment,
|
|
||||||
});
|
|
||||||
|
|
||||||
{
|
|
||||||
const time = app.timer.read();
|
|
||||||
const model = zm.mul(zm.rotationX(time * (std.math.pi / 2.0)), zm.rotationZ(time * (std.math.pi / 2.0)));
|
|
||||||
const view = zm.lookAtRh(
|
|
||||||
zm.Vec{ 0, -4, 0, 1 },
|
|
||||||
zm.Vec{ 0, 0, 0, 1 },
|
|
||||||
zm.Vec{ 0, 0, 1, 0 },
|
|
||||||
);
|
|
||||||
const proj = zm.perspectiveFovRh(
|
|
||||||
(std.math.pi * 2.0 / 5.0),
|
|
||||||
@as(f32, @floatFromInt(core.descriptor.width)) / @as(f32, @floatFromInt(core.descriptor.height)),
|
|
||||||
1,
|
|
||||||
100,
|
|
||||||
);
|
|
||||||
const ubo = UniformBufferObject{
|
|
||||||
.mat = zm.transpose(zm.mul(zm.mul(model, view), proj)),
|
|
||||||
};
|
|
||||||
encoder.writeBuffer(app.uniform_buffer, 0, &[_]UniformBufferObject{ubo});
|
|
||||||
}
|
|
||||||
|
|
||||||
const pass = encoder.beginRenderPass(&render_pass_info);
|
|
||||||
pass.setPipeline(app.pipeline);
|
|
||||||
pass.setBindGroup(0, app.bind_group, &.{0});
|
|
||||||
pass.setVertexBuffer(0, app.vertex_buffer, 0, @sizeOf(Vertex) * vertices.len);
|
|
||||||
pass.draw(vertices.len, 1, 0, 0);
|
|
||||||
pass.end();
|
|
||||||
pass.release();
|
|
||||||
|
|
||||||
encoder.copyTextureToTexture(
|
|
||||||
&gpu.ImageCopyTexture{
|
|
||||||
.texture = app.cube_texture_render,
|
|
||||||
},
|
|
||||||
&gpu.ImageCopyTexture{
|
|
||||||
.texture = app.cube_texture,
|
|
||||||
},
|
|
||||||
&.{ .width = core.descriptor.width, .height = core.descriptor.height },
|
|
||||||
);
|
|
||||||
|
|
||||||
const cube_pass = encoder.beginRenderPass(&cube_render_pass_info);
|
|
||||||
cube_pass.setPipeline(app.pipeline);
|
|
||||||
cube_pass.setBindGroup(0, app.bind_group, &.{0});
|
|
||||||
cube_pass.setVertexBuffer(0, app.vertex_buffer, 0, @sizeOf(Vertex) * vertices.len);
|
|
||||||
cube_pass.draw(vertices.len, 1, 0, 0);
|
|
||||||
cube_pass.end();
|
|
||||||
cube_pass.release();
|
|
||||||
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
encoder.release();
|
|
||||||
|
|
||||||
const queue = core.queue;
|
|
||||||
queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
command.release();
|
|
||||||
core.swap_chain.present();
|
|
||||||
back_buffer_view.release();
|
|
||||||
|
|
||||||
// update the window title every second
|
|
||||||
if (app.title_timer.read() >= 1.0) {
|
|
||||||
app.title_timer.reset();
|
|
||||||
try core.printTitle("Fractal Cube [ {d}fps ] [ Input {d}hz ]", .{
|
|
||||||
core.frameRate(),
|
|
||||||
core.inputRate(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
struct Uniforms {
|
|
||||||
matrix : mat4x4<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
@binding(0) @group(0) var<uniform> ubo : Uniforms;
|
|
||||||
|
|
||||||
struct VertexOut {
|
|
||||||
@builtin(position) Position : vec4<f32>,
|
|
||||||
@location(0) fragUV : vec2<f32>,
|
|
||||||
@location(1) fragPosition: vec4<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
@vertex fn vertex_main(
|
|
||||||
@location(0) position : vec4<f32>,
|
|
||||||
@location(1) uv: vec2<f32>
|
|
||||||
) -> VertexOut {
|
|
||||||
var output : VertexOut;
|
|
||||||
output.Position = position * ubo.matrix;
|
|
||||||
output.fragUV = uv;
|
|
||||||
output.fragPosition = 0.5 * (position + vec4<f32>(1.0, 1.0, 1.0, 1.0));
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
@binding(1) @group(0) var mySampler: sampler;
|
|
||||||
@binding(2) @group(0) var myTexture: texture_2d<f32>;
|
|
||||||
|
|
||||||
@fragment fn frag_main(
|
|
||||||
@location(0) fragUV: vec2<f32>,
|
|
||||||
@location(1) fragPosition: vec4<f32>
|
|
||||||
) -> @location(0) vec4<f32> {
|
|
||||||
let texColor = textureSample(myTexture, mySampler, fragUV * 0.8 + vec2<f32>(0.1, 0.1));
|
|
||||||
let f = f32(length(texColor.rgb - vec3<f32>(0.5, 0.5, 0.5)) < 0.01);
|
|
||||||
return (1.0 - f) * texColor + f * fragPosition;
|
|
||||||
// return vec4<f32>(texColor.rgb,1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
||||||
struct CameraUniform {
|
|
||||||
pos: vec4<f32>,
|
|
||||||
view_proj: mat4x4<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) clip_position: vec4<f32>,
|
|
||||||
@location(0) tex_coords: vec2<f32>,
|
|
||||||
@location(1) normal: vec3<f32>,
|
|
||||||
@location(2) position: vec3<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Light {
|
|
||||||
position: vec4<f32>,
|
|
||||||
color: vec4<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
@group(0) @binding(0) var<uniform> camera: CameraUniform;
|
|
||||||
@group(1) @binding(0) var t_diffuse: texture_2d<f32>;
|
|
||||||
@group(1) @binding(1) var s_diffuse: sampler;
|
|
||||||
@group(2) @binding(0) var<uniform> light: Light;
|
|
||||||
|
|
||||||
@vertex
|
|
||||||
fn vs_main(
|
|
||||||
// TODO - struct input
|
|
||||||
@location(0) model_position: vec3<f32>,
|
|
||||||
@location(1) model_normal: vec3<f32>,
|
|
||||||
@location(2) model_tex_coords: vec2<f32>,
|
|
||||||
@location(3) instance_model_matrix_0: vec4<f32>,
|
|
||||||
@location(4) instance_model_matrix_1: vec4<f32>,
|
|
||||||
@location(5) instance_model_matrix_2: vec4<f32>,
|
|
||||||
@location(6) instance_model_matrix_3: vec4<f32>)
|
|
||||||
-> VertexOutput {
|
|
||||||
let model_matrix = mat4x4<f32>(
|
|
||||||
instance_model_matrix_0,
|
|
||||||
instance_model_matrix_1,
|
|
||||||
instance_model_matrix_2,
|
|
||||||
instance_model_matrix_3,
|
|
||||||
);
|
|
||||||
var out: VertexOutput;
|
|
||||||
let world_pos = model_matrix * vec4<f32>(model_position, 1.0);
|
|
||||||
out.position = world_pos.xyz;
|
|
||||||
out.normal = (model_matrix * vec4<f32>(model_normal, 0.0)).xyz;
|
|
||||||
out.clip_position = camera.view_proj * world_pos;
|
|
||||||
out.tex_coords = model_tex_coords;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@fragment
|
|
||||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
|
||||||
let object_color = textureSample(t_diffuse, s_diffuse, in.tex_coords);
|
|
||||||
|
|
||||||
let ambient = 0.1;
|
|
||||||
let ambient_color = light.color.rbg * ambient;
|
|
||||||
|
|
||||||
let light_dir = normalize(light.position.xyz - in.position);
|
|
||||||
let diffuse = max(dot(in.normal, light_dir), 0.0);
|
|
||||||
let diffuse_color = light.color.rgb * diffuse;
|
|
||||||
|
|
||||||
let view_dir = normalize(camera.pos.xyz - in.position);
|
|
||||||
let half_dir = normalize(view_dir + light_dir);
|
|
||||||
let specular = pow(max(dot(in.normal, half_dir), 0.0), 32.0);
|
|
||||||
let specular_color = light.color.rbg * specular;
|
|
||||||
|
|
||||||
let all = ambient_color + diffuse_color + specular_color;
|
|
||||||
|
|
||||||
let result = all * object_color.rgb;
|
|
||||||
|
|
||||||
return vec4<f32>(result, object_color.a);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
struct CameraUniform {
|
|
||||||
view_pos: vec4<f32>,
|
|
||||||
view_proj: mat4x4<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) clip_position: vec4<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Light {
|
|
||||||
position: vec4<f32>,
|
|
||||||
color: vec4<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
@group(0) @binding(0) var<uniform> camera: CameraUniform;
|
|
||||||
@group(1) @binding(0) var<uniform> light: Light;
|
|
||||||
|
|
||||||
@vertex
|
|
||||||
fn vs_main(
|
|
||||||
// TODO - struct input
|
|
||||||
@location(0) model_position: vec3<f32>,
|
|
||||||
@location(1) model_normal: vec3<f32>,
|
|
||||||
@location(2) model_tex_coords: vec2<f32>,
|
|
||||||
) -> VertexOutput {
|
|
||||||
var out: VertexOutput;
|
|
||||||
let world_pos = vec4<f32>(model_position + light.position.xyz, 1.0);
|
|
||||||
out.clip_position = camera.view_proj * world_pos;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@fragment
|
|
||||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
|
||||||
return vec4<f32>(1.0, 1.0, 1.0, 0.5);
|
|
||||||
}
|
|
||||||
|
|
@ -1,896 +0,0 @@
|
||||||
// in this example:
|
|
||||||
// - comptime generated image data for texture
|
|
||||||
// - Blinn-Phong lighting
|
|
||||||
// - several pipelines
|
|
||||||
//
|
|
||||||
// quit with escape, q or space
|
|
||||||
// move camera with arrows or wasd
|
|
||||||
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
const zm = @import("zmath");
|
|
||||||
const Vec = zm.Vec;
|
|
||||||
const Mat = zm.Mat;
|
|
||||||
const Quat = zm.Quat;
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
// Use experimental sysgpu graphics API
|
|
||||||
pub const use_sysgpu = true;
|
|
||||||
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
|
|
||||||
title_timer: core.Timer,
|
|
||||||
timer: core.Timer,
|
|
||||||
cube: Cube,
|
|
||||||
camera: Camera,
|
|
||||||
light: Light,
|
|
||||||
depth: Texture,
|
|
||||||
keys: u8,
|
|
||||||
|
|
||||||
const Dir = struct {
|
|
||||||
const up: u8 = 0b0001;
|
|
||||||
const down: u8 = 0b0010;
|
|
||||||
const left: u8 = 0b0100;
|
|
||||||
const right: u8 = 0b1000;
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{});
|
|
||||||
|
|
||||||
app.title_timer = try core.Timer.start();
|
|
||||||
app.timer = try core.Timer.start();
|
|
||||||
|
|
||||||
const eye = Vec{ 5.0, 7.0, 5.0, 0.0 };
|
|
||||||
const target = Vec{ 0.0, 0.0, 0.0, 0.0 };
|
|
||||||
|
|
||||||
const framebuffer = core.descriptor;
|
|
||||||
const aspect_ratio = @as(f32, @floatFromInt(framebuffer.width)) / @as(f32, @floatFromInt(framebuffer.height));
|
|
||||||
|
|
||||||
app.cube = Cube.init();
|
|
||||||
app.light = Light.init();
|
|
||||||
app.depth = Texture.depth(core.device, framebuffer.width, framebuffer.height);
|
|
||||||
app.camera = Camera.init(core.device, eye, target, zm.Vec{ 0.0, 1.0, 0.0, 0.0 }, aspect_ratio, 45.0, 0.1, 100.0);
|
|
||||||
app.keys = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
defer _ = gpa.deinit();
|
|
||||||
defer core.deinit();
|
|
||||||
|
|
||||||
app.cube.deinit();
|
|
||||||
app.camera.deinit();
|
|
||||||
app.light.deinit();
|
|
||||||
app.depth.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(app: *App) !bool {
|
|
||||||
const delta_time = app.timer.lap();
|
|
||||||
|
|
||||||
var iter = core.pollEvents();
|
|
||||||
while (iter.next()) |event| {
|
|
||||||
switch (event) {
|
|
||||||
.key_press => |ev| switch (ev.key) {
|
|
||||||
.q, .escape, .space => return true,
|
|
||||||
.w, .up => {
|
|
||||||
app.keys |= Dir.up;
|
|
||||||
},
|
|
||||||
.s, .down => {
|
|
||||||
app.keys |= Dir.down;
|
|
||||||
},
|
|
||||||
.a, .left => {
|
|
||||||
app.keys |= Dir.left;
|
|
||||||
},
|
|
||||||
.d, .right => {
|
|
||||||
app.keys |= Dir.right;
|
|
||||||
},
|
|
||||||
.one => core.setDisplayMode(.windowed),
|
|
||||||
.two => core.setDisplayMode(.fullscreen),
|
|
||||||
.three => core.setDisplayMode(.borderless),
|
|
||||||
else => {},
|
|
||||||
},
|
|
||||||
.key_release => |ev| switch (ev.key) {
|
|
||||||
.w, .up => {
|
|
||||||
app.keys &= ~Dir.up;
|
|
||||||
},
|
|
||||||
.s, .down => {
|
|
||||||
app.keys &= ~Dir.down;
|
|
||||||
},
|
|
||||||
.a, .left => {
|
|
||||||
app.keys &= ~Dir.left;
|
|
||||||
},
|
|
||||||
.d, .right => {
|
|
||||||
app.keys &= ~Dir.right;
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
},
|
|
||||||
.framebuffer_resize => |ev| {
|
|
||||||
// recreates the sampler, which is a waste, but for an example it's ok
|
|
||||||
app.depth.release();
|
|
||||||
app.depth = Texture.depth(core.device, ev.width, ev.height);
|
|
||||||
},
|
|
||||||
.close => return true,
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// move camera
|
|
||||||
const speed = zm.Vec{ delta_time * 5, delta_time * 5, delta_time * 5, delta_time * 5 };
|
|
||||||
const fwd = zm.normalize3(app.camera.target - app.camera.eye);
|
|
||||||
const right = zm.normalize3(zm.cross3(fwd, app.camera.up));
|
|
||||||
|
|
||||||
if (app.keys & Dir.up != 0)
|
|
||||||
app.camera.eye += fwd * speed;
|
|
||||||
|
|
||||||
if (app.keys & Dir.down != 0)
|
|
||||||
app.camera.eye -= fwd * speed;
|
|
||||||
|
|
||||||
if (app.keys & Dir.right != 0)
|
|
||||||
app.camera.eye += right * speed
|
|
||||||
else if (app.keys & Dir.left != 0)
|
|
||||||
app.camera.eye -= right * speed
|
|
||||||
else
|
|
||||||
app.camera.eye += right * (speed * @Vector(4, f32){ 0.5, 0.5, 0.5, 0.5 });
|
|
||||||
|
|
||||||
const queue = core.queue;
|
|
||||||
app.camera.update(queue);
|
|
||||||
|
|
||||||
// move light
|
|
||||||
const light_speed = delta_time * 2.5;
|
|
||||||
app.light.update(queue, light_speed);
|
|
||||||
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
defer back_buffer_view.release();
|
|
||||||
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
defer encoder.release();
|
|
||||||
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = back_buffer_view,
|
|
||||||
.clear_value = gpu.Color{ .r = 0.0, .g = 0.0, .b = 0.4, .a = 1.0 },
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
|
|
||||||
const render_pass_descriptor = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{color_attachment},
|
|
||||||
.depth_stencil_attachment = &.{
|
|
||||||
.view = app.depth.view,
|
|
||||||
.depth_load_op = .clear,
|
|
||||||
.depth_store_op = .store,
|
|
||||||
.depth_clear_value = 1.0,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const pass = encoder.beginRenderPass(&render_pass_descriptor);
|
|
||||||
defer pass.release();
|
|
||||||
|
|
||||||
// brick cubes
|
|
||||||
pass.setPipeline(app.cube.pipeline);
|
|
||||||
pass.setBindGroup(0, app.camera.bind_group, &.{});
|
|
||||||
pass.setBindGroup(1, app.cube.texture.bind_group.?, &.{});
|
|
||||||
pass.setBindGroup(2, app.light.bind_group, &.{});
|
|
||||||
pass.setVertexBuffer(0, app.cube.mesh.buffer, 0, app.cube.mesh.size);
|
|
||||||
pass.setVertexBuffer(1, app.cube.instance.buffer, 0, app.cube.instance.size);
|
|
||||||
pass.draw(4, app.cube.instance.len, 0, 0);
|
|
||||||
pass.draw(4, app.cube.instance.len, 4, 0);
|
|
||||||
pass.draw(4, app.cube.instance.len, 8, 0);
|
|
||||||
pass.draw(4, app.cube.instance.len, 12, 0);
|
|
||||||
pass.draw(4, app.cube.instance.len, 16, 0);
|
|
||||||
pass.draw(4, app.cube.instance.len, 20, 0);
|
|
||||||
|
|
||||||
// light source
|
|
||||||
pass.setPipeline(app.light.pipeline);
|
|
||||||
pass.setBindGroup(0, app.camera.bind_group, &.{});
|
|
||||||
pass.setBindGroup(1, app.light.bind_group, &.{});
|
|
||||||
pass.setVertexBuffer(0, app.cube.mesh.buffer, 0, app.cube.mesh.size);
|
|
||||||
pass.draw(4, 1, 0, 0);
|
|
||||||
pass.draw(4, 1, 4, 0);
|
|
||||||
pass.draw(4, 1, 8, 0);
|
|
||||||
pass.draw(4, 1, 12, 0);
|
|
||||||
pass.draw(4, 1, 16, 0);
|
|
||||||
pass.draw(4, 1, 20, 0);
|
|
||||||
|
|
||||||
pass.end();
|
|
||||||
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
defer command.release();
|
|
||||||
|
|
||||||
queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
core.swap_chain.present();
|
|
||||||
|
|
||||||
// update the window title every second
|
|
||||||
if (app.title_timer.read() >= 1.0) {
|
|
||||||
app.title_timer.reset();
|
|
||||||
try core.printTitle("Gen Texture Light [ {d}fps ] [ Input {d}hz ]", .{
|
|
||||||
core.frameRate(),
|
|
||||||
core.inputRate(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Camera = struct {
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
eye: Vec,
|
|
||||||
target: Vec,
|
|
||||||
up: Vec,
|
|
||||||
aspect: f32,
|
|
||||||
fovy: f32,
|
|
||||||
near: f32,
|
|
||||||
far: f32,
|
|
||||||
bind_group: *gpu.BindGroup,
|
|
||||||
buffer: Buffer,
|
|
||||||
|
|
||||||
const Uniform = extern struct {
|
|
||||||
pos: Vec,
|
|
||||||
mat: Mat,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn init(device: *gpu.Device, eye: Vec, target: Vec, up: Vec, aspect: f32, fovy: f32, near: f32, far: f32) Self {
|
|
||||||
var self: Self = .{
|
|
||||||
.eye = eye,
|
|
||||||
.target = target,
|
|
||||||
.up = up,
|
|
||||||
.aspect = aspect,
|
|
||||||
.near = near,
|
|
||||||
.far = far,
|
|
||||||
.fovy = fovy,
|
|
||||||
.buffer = undefined,
|
|
||||||
.bind_group = undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
const view = self.buildViewProjMatrix();
|
|
||||||
|
|
||||||
const uniform = Uniform{
|
|
||||||
.pos = self.eye,
|
|
||||||
.mat = view,
|
|
||||||
};
|
|
||||||
|
|
||||||
const buffer = .{
|
|
||||||
.buffer = initBuffer(device, .{ .uniform = true }, &@as([20]f32, @bitCast(uniform))),
|
|
||||||
.size = @sizeOf(@TypeOf(uniform)),
|
|
||||||
};
|
|
||||||
|
|
||||||
const layout = Self.bindGroupLayout(device);
|
|
||||||
const bind_group = device.createBindGroup(&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = layout,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.buffer(0, buffer.buffer, 0, buffer.size, buffer.size),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
layout.release();
|
|
||||||
|
|
||||||
self.buffer = buffer;
|
|
||||||
self.bind_group = bind_group;
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deinit(self: *Self) void {
|
|
||||||
self.bind_group.release();
|
|
||||||
self.buffer.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(self: *Self, queue: *gpu.Queue) void {
|
|
||||||
const mat = self.buildViewProjMatrix();
|
|
||||||
const uniform = .{
|
|
||||||
.pos = self.eye,
|
|
||||||
.mat = mat,
|
|
||||||
};
|
|
||||||
|
|
||||||
queue.writeBuffer(self.buffer.buffer, 0, &[_]Uniform{uniform});
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fn buildViewProjMatrix(s: *const Camera) Mat {
|
|
||||||
const view = zm.lookAtRh(s.eye, s.target, s.up);
|
|
||||||
const proj = zm.perspectiveFovRh(s.fovy, s.aspect, s.near, s.far);
|
|
||||||
return zm.mul(view, proj);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fn bindGroupLayout(device: *gpu.Device) *gpu.BindGroupLayout {
|
|
||||||
const visibility = .{ .vertex = true, .fragment = true };
|
|
||||||
return device.createBindGroupLayout(&gpu.BindGroupLayout.Descriptor.init(.{
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroupLayout.Entry.buffer(0, visibility, .uniform, false, 0),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const Buffer = struct {
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
buffer: *gpu.Buffer,
|
|
||||||
size: usize,
|
|
||||||
len: u32 = 0,
|
|
||||||
|
|
||||||
fn release(self: *Self) void {
|
|
||||||
self.buffer.release();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const Cube = struct {
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
pipeline: *gpu.RenderPipeline,
|
|
||||||
mesh: Buffer,
|
|
||||||
instance: Buffer,
|
|
||||||
texture: Texture,
|
|
||||||
|
|
||||||
const IPR = 20; // instances per row
|
|
||||||
const SPACING = 2; // spacing between cubes
|
|
||||||
const DISPLACEMENT = vec3u(IPR * SPACING / 2, 0, IPR * SPACING / 2);
|
|
||||||
|
|
||||||
fn init() Self {
|
|
||||||
const device = core.device;
|
|
||||||
|
|
||||||
const texture = Brick.texture(device);
|
|
||||||
|
|
||||||
// instance buffer
|
|
||||||
var ibuf: [IPR * IPR * 16]f32 = undefined;
|
|
||||||
|
|
||||||
var z: usize = 0;
|
|
||||||
while (z < IPR) : (z += 1) {
|
|
||||||
var x: usize = 0;
|
|
||||||
while (x < IPR) : (x += 1) {
|
|
||||||
const pos = vec3u(x * SPACING, 0, z * SPACING) - DISPLACEMENT;
|
|
||||||
const rot = blk: {
|
|
||||||
if (pos[0] == 0 and pos[2] == 0) {
|
|
||||||
break :blk zm.rotationZ(0.0);
|
|
||||||
} else {
|
|
||||||
break :blk zm.mul(zm.rotationX(zm.clamp(zm.abs(pos[0]), 0, 45.0)), zm.rotationZ(zm.clamp(zm.abs(pos[2]), 0, 45.0)));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const index = z * IPR + x;
|
|
||||||
const inst = Instance{
|
|
||||||
.position = pos,
|
|
||||||
.rotation = rot,
|
|
||||||
};
|
|
||||||
zm.storeMat(ibuf[index * 16 ..], inst.toMat());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const instance = Buffer{
|
|
||||||
.buffer = initBuffer(device, .{ .vertex = true }, &ibuf),
|
|
||||||
.len = IPR * IPR,
|
|
||||||
.size = @sizeOf(@TypeOf(ibuf)),
|
|
||||||
};
|
|
||||||
|
|
||||||
return Self{
|
|
||||||
.mesh = mesh(device),
|
|
||||||
.texture = texture,
|
|
||||||
.instance = instance,
|
|
||||||
.pipeline = pipeline(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deinit(self: *Self) void {
|
|
||||||
self.pipeline.release();
|
|
||||||
self.mesh.release();
|
|
||||||
self.instance.release();
|
|
||||||
self.texture.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pipeline() *gpu.RenderPipeline {
|
|
||||||
const device = core.device;
|
|
||||||
|
|
||||||
const camera_layout = Camera.bindGroupLayout(device);
|
|
||||||
const texture_layout = Texture.bindGroupLayout(device);
|
|
||||||
const light_layout = Light.bindGroupLayout(device);
|
|
||||||
const layout_descriptor = gpu.PipelineLayout.Descriptor.init(.{
|
|
||||||
.bind_group_layouts = &.{
|
|
||||||
camera_layout,
|
|
||||||
texture_layout,
|
|
||||||
light_layout,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
defer camera_layout.release();
|
|
||||||
defer texture_layout.release();
|
|
||||||
defer light_layout.release();
|
|
||||||
|
|
||||||
const layout = device.createPipelineLayout(&layout_descriptor);
|
|
||||||
defer layout.release();
|
|
||||||
|
|
||||||
const shader = device.createShaderModuleWGSL("cube.wgsl", @embedFile("cube.wgsl"));
|
|
||||||
defer shader.release();
|
|
||||||
|
|
||||||
const blend = gpu.BlendState{};
|
|
||||||
const color_target = gpu.ColorTargetState{
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
.blend = &blend,
|
|
||||||
};
|
|
||||||
|
|
||||||
const fragment = gpu.FragmentState.init(.{
|
|
||||||
.module = shader,
|
|
||||||
.entry_point = "fs_main",
|
|
||||||
.targets = &.{color_target},
|
|
||||||
});
|
|
||||||
|
|
||||||
const descriptor = gpu.RenderPipeline.Descriptor{
|
|
||||||
.layout = layout,
|
|
||||||
.fragment = &fragment,
|
|
||||||
.vertex = gpu.VertexState.init(.{
|
|
||||||
.module = shader,
|
|
||||||
.entry_point = "vs_main",
|
|
||||||
.buffers = &.{
|
|
||||||
Self.vertexBufferLayout(),
|
|
||||||
Self.instanceLayout(),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
.depth_stencil = &.{
|
|
||||||
.format = Texture.DEPTH_FORMAT,
|
|
||||||
.depth_write_enabled = .true,
|
|
||||||
.depth_compare = .less,
|
|
||||||
},
|
|
||||||
.primitive = .{
|
|
||||||
.cull_mode = .back,
|
|
||||||
.topology = .triangle_strip,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return device.createRenderPipeline(&descriptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mesh(device: *gpu.Device) Buffer {
|
|
||||||
// generated texture has aspect ratio of 1:2
|
|
||||||
// `h` reflects that ratio
|
|
||||||
// `v` sets how many times texture repeats across surface
|
|
||||||
const v = 2;
|
|
||||||
const h = v * 2;
|
|
||||||
const buf = asFloats(.{
|
|
||||||
// z+ face
|
|
||||||
0, 0, 1, 0, 0, 1, 0, h,
|
|
||||||
1, 0, 1, 0, 0, 1, v, h,
|
|
||||||
0, 1, 1, 0, 0, 1, 0, 0,
|
|
||||||
1, 1, 1, 0, 0, 1, v, 0,
|
|
||||||
// z- face
|
|
||||||
1, 0, 0, 0, 0, -1, 0, h,
|
|
||||||
0, 0, 0, 0, 0, -1, v, h,
|
|
||||||
1, 1, 0, 0, 0, -1, 0, 0,
|
|
||||||
0, 1, 0, 0, 0, -1, v, 0,
|
|
||||||
// x+ face
|
|
||||||
1, 0, 1, 1, 0, 0, 0, h,
|
|
||||||
1, 0, 0, 1, 0, 0, v, h,
|
|
||||||
1, 1, 1, 1, 0, 0, 0, 0,
|
|
||||||
1, 1, 0, 1, 0, 0, v, 0,
|
|
||||||
// x- face
|
|
||||||
0, 0, 0, -1, 0, 0, 0, h,
|
|
||||||
0, 0, 1, -1, 0, 0, v, h,
|
|
||||||
0, 1, 0, -1, 0, 0, 0, 0,
|
|
||||||
0, 1, 1, -1, 0, 0, v, 0,
|
|
||||||
// y+ face
|
|
||||||
1, 1, 0, 0, 1, 0, 0, h,
|
|
||||||
0, 1, 0, 0, 1, 0, v, h,
|
|
||||||
1, 1, 1, 0, 1, 0, 0, 0,
|
|
||||||
0, 1, 1, 0, 1, 0, v, 0,
|
|
||||||
// y- face
|
|
||||||
0, 0, 0, 0, -1, 0, 0, h,
|
|
||||||
1, 0, 0, 0, -1, 0, v, h,
|
|
||||||
0, 0, 1, 0, -1, 0, 0, 0,
|
|
||||||
1, 0, 1, 0, -1, 0, v, 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
return Buffer{
|
|
||||||
.buffer = initBuffer(device, .{ .vertex = true }, &buf),
|
|
||||||
.size = @sizeOf(@TypeOf(buf)),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn vertexBufferLayout() gpu.VertexBufferLayout {
|
|
||||||
const attributes = [_]gpu.VertexAttribute{
|
|
||||||
.{
|
|
||||||
.format = .float32x3,
|
|
||||||
.offset = 0,
|
|
||||||
.shader_location = 0,
|
|
||||||
},
|
|
||||||
.{
|
|
||||||
.format = .float32x3,
|
|
||||||
.offset = @sizeOf([3]f32),
|
|
||||||
.shader_location = 1,
|
|
||||||
},
|
|
||||||
.{
|
|
||||||
.format = .float32x2,
|
|
||||||
.offset = @sizeOf([6]f32),
|
|
||||||
.shader_location = 2,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
return gpu.VertexBufferLayout.init(.{
|
|
||||||
.array_stride = @sizeOf([8]f32),
|
|
||||||
.attributes = &attributes,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn instanceLayout() gpu.VertexBufferLayout {
|
|
||||||
const attributes = [_]gpu.VertexAttribute{
|
|
||||||
.{
|
|
||||||
.format = .float32x4,
|
|
||||||
.offset = 0,
|
|
||||||
.shader_location = 3,
|
|
||||||
},
|
|
||||||
.{
|
|
||||||
.format = .float32x4,
|
|
||||||
.offset = @sizeOf([4]f32),
|
|
||||||
.shader_location = 4,
|
|
||||||
},
|
|
||||||
.{
|
|
||||||
.format = .float32x4,
|
|
||||||
.offset = @sizeOf([8]f32),
|
|
||||||
.shader_location = 5,
|
|
||||||
},
|
|
||||||
.{
|
|
||||||
.format = .float32x4,
|
|
||||||
.offset = @sizeOf([12]f32),
|
|
||||||
.shader_location = 6,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return gpu.VertexBufferLayout.init(.{
|
|
||||||
.array_stride = @sizeOf([16]f32),
|
|
||||||
.step_mode = .instance,
|
|
||||||
.attributes = &attributes,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
fn asFloats(comptime arr: anytype) [arr.len]f32 {
|
|
||||||
const len = arr.len;
|
|
||||||
comptime var out: [len]f32 = undefined;
|
|
||||||
comptime var i = 0;
|
|
||||||
inline while (i < len) : (i += 1) {
|
|
||||||
out[i] = @as(f32, @floatFromInt(arr[i]));
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Brick = struct {
|
|
||||||
const W = 12;
|
|
||||||
const H = 6;
|
|
||||||
|
|
||||||
fn texture(device: *gpu.Device) Texture {
|
|
||||||
const slice: []const u8 = &data();
|
|
||||||
return Texture.fromData(device, W, H, u8, slice);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn data() [W * H * 4]u8 {
|
|
||||||
comptime var out: [W * H * 4]u8 = undefined;
|
|
||||||
|
|
||||||
// fill all the texture with brick color
|
|
||||||
comptime var i = 0;
|
|
||||||
inline while (i < H) : (i += 1) {
|
|
||||||
comptime var j = 0;
|
|
||||||
inline while (j < W * 4) : (j += 4) {
|
|
||||||
out[i * W * 4 + j + 0] = 210;
|
|
||||||
out[i * W * 4 + j + 1] = 30;
|
|
||||||
out[i * W * 4 + j + 2] = 30;
|
|
||||||
out[i * W * 4 + j + 3] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const f = 10;
|
|
||||||
|
|
||||||
// fill the cement lines
|
|
||||||
inline for ([_]comptime_int{ 0, 1 }) |k| {
|
|
||||||
inline for ([_]comptime_int{ 5 * 4, 11 * 4 }) |m| {
|
|
||||||
out[k * W * 4 + m + 0] = f;
|
|
||||||
out[k * W * 4 + m + 1] = f;
|
|
||||||
out[k * W * 4 + m + 2] = f;
|
|
||||||
out[k * W * 4 + m + 3] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline for ([_]comptime_int{ 3, 4 }) |k| {
|
|
||||||
inline for ([_]comptime_int{ 2 * 4, 8 * 4 }) |m| {
|
|
||||||
out[k * W * 4 + m + 0] = f;
|
|
||||||
out[k * W * 4 + m + 1] = f;
|
|
||||||
out[k * W * 4 + m + 2] = f;
|
|
||||||
out[k * W * 4 + m + 3] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline for ([_]comptime_int{ 2, 5 }) |k| {
|
|
||||||
comptime var m = 0;
|
|
||||||
inline while (m < W * 4) : (m += 4) {
|
|
||||||
out[k * W * 4 + m + 0] = f;
|
|
||||||
out[k * W * 4 + m + 1] = f;
|
|
||||||
out[k * W * 4 + m + 2] = f;
|
|
||||||
out[k * W * 4 + m + 3] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// don't confuse with gpu.Texture
|
|
||||||
const Texture = struct {
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
texture: *gpu.Texture,
|
|
||||||
view: *gpu.TextureView,
|
|
||||||
sampler: *gpu.Sampler,
|
|
||||||
bind_group: ?*gpu.BindGroup,
|
|
||||||
|
|
||||||
const DEPTH_FORMAT = .depth32_float;
|
|
||||||
const FORMAT = .rgba8_unorm;
|
|
||||||
|
|
||||||
fn release(self: *Self) void {
|
|
||||||
self.texture.release();
|
|
||||||
self.view.release();
|
|
||||||
self.sampler.release();
|
|
||||||
if (self.bind_group) |bind_group| bind_group.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fromData(device: *gpu.Device, width: u32, height: u32, comptime T: type, data: []const T) Self {
|
|
||||||
const extent = gpu.Extent3D{
|
|
||||||
.width = width,
|
|
||||||
.height = height,
|
|
||||||
};
|
|
||||||
|
|
||||||
const texture = device.createTexture(&gpu.Texture.Descriptor{
|
|
||||||
.size = extent,
|
|
||||||
.format = FORMAT,
|
|
||||||
.usage = .{ .copy_dst = true, .texture_binding = true },
|
|
||||||
});
|
|
||||||
|
|
||||||
const view = texture.createView(&gpu.TextureView.Descriptor{
|
|
||||||
.format = FORMAT,
|
|
||||||
.dimension = .dimension_2d,
|
|
||||||
.array_layer_count = 1,
|
|
||||||
.mip_level_count = 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
const sampler = device.createSampler(&gpu.Sampler.Descriptor{
|
|
||||||
.address_mode_u = .repeat,
|
|
||||||
.address_mode_v = .repeat,
|
|
||||||
.address_mode_w = .repeat,
|
|
||||||
.mag_filter = .linear,
|
|
||||||
.min_filter = .linear,
|
|
||||||
.mipmap_filter = .linear,
|
|
||||||
.max_anisotropy = 1, // 1,2,4,8,16
|
|
||||||
});
|
|
||||||
|
|
||||||
core.queue.writeTexture(
|
|
||||||
&gpu.ImageCopyTexture{
|
|
||||||
.texture = texture,
|
|
||||||
},
|
|
||||||
&gpu.Texture.DataLayout{
|
|
||||||
.bytes_per_row = 4 * width,
|
|
||||||
.rows_per_image = height,
|
|
||||||
},
|
|
||||||
&extent,
|
|
||||||
data,
|
|
||||||
);
|
|
||||||
|
|
||||||
const bind_group_layout = Self.bindGroupLayout(device);
|
|
||||||
const bind_group = device.createBindGroup(&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = bind_group_layout,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.textureView(0, view),
|
|
||||||
gpu.BindGroup.Entry.sampler(1, sampler),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
bind_group_layout.release();
|
|
||||||
|
|
||||||
return Self{
|
|
||||||
.view = view,
|
|
||||||
.texture = texture,
|
|
||||||
.sampler = sampler,
|
|
||||||
.bind_group = bind_group,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn depth(device: *gpu.Device, width: u32, height: u32) Self {
|
|
||||||
const extent = gpu.Extent3D{
|
|
||||||
.width = width,
|
|
||||||
.height = height,
|
|
||||||
};
|
|
||||||
|
|
||||||
const texture = device.createTexture(&gpu.Texture.Descriptor{
|
|
||||||
.size = extent,
|
|
||||||
.format = DEPTH_FORMAT,
|
|
||||||
.usage = .{
|
|
||||||
.render_attachment = true,
|
|
||||||
.texture_binding = true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const view = texture.createView(&gpu.TextureView.Descriptor{
|
|
||||||
.dimension = .dimension_2d,
|
|
||||||
.array_layer_count = 1,
|
|
||||||
.mip_level_count = 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
const sampler = device.createSampler(&gpu.Sampler.Descriptor{
|
|
||||||
.mag_filter = .linear,
|
|
||||||
.compare = .less_equal,
|
|
||||||
});
|
|
||||||
|
|
||||||
return Self{
|
|
||||||
.texture = texture,
|
|
||||||
.view = view,
|
|
||||||
.sampler = sampler,
|
|
||||||
.bind_group = null, // not used
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fn bindGroupLayout(device: *gpu.Device) *gpu.BindGroupLayout {
|
|
||||||
const visibility = .{ .fragment = true };
|
|
||||||
const Entry = gpu.BindGroupLayout.Entry;
|
|
||||||
return device.createBindGroupLayout(&gpu.BindGroupLayout.Descriptor.init(.{
|
|
||||||
.entries = &.{
|
|
||||||
Entry.texture(0, visibility, .float, .dimension_2d, false),
|
|
||||||
Entry.sampler(1, visibility, .filtering),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const Light = struct {
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
uniform: Uniform,
|
|
||||||
buffer: Buffer,
|
|
||||||
bind_group: *gpu.BindGroup,
|
|
||||||
pipeline: *gpu.RenderPipeline,
|
|
||||||
|
|
||||||
const Uniform = extern struct {
|
|
||||||
position: Vec,
|
|
||||||
color: Vec,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn init() Self {
|
|
||||||
const device = core.device;
|
|
||||||
const uniform = Uniform{
|
|
||||||
.color = vec3u(1, 1, 1),
|
|
||||||
.position = vec3u(3, 7, 2),
|
|
||||||
};
|
|
||||||
|
|
||||||
const buffer = .{
|
|
||||||
.buffer = initBuffer(device, .{ .uniform = true }, &@as([8]f32, @bitCast(uniform))),
|
|
||||||
.size = @sizeOf(@TypeOf(uniform)),
|
|
||||||
};
|
|
||||||
|
|
||||||
const layout = Self.bindGroupLayout(device);
|
|
||||||
const bind_group = device.createBindGroup(&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = layout,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.buffer(0, buffer.buffer, 0, buffer.size, buffer.size),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
layout.release();
|
|
||||||
|
|
||||||
return Self{
|
|
||||||
.buffer = buffer,
|
|
||||||
.uniform = uniform,
|
|
||||||
.bind_group = bind_group,
|
|
||||||
.pipeline = Self.pipeline(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deinit(self: *Self) void {
|
|
||||||
self.buffer.release();
|
|
||||||
self.bind_group.release();
|
|
||||||
self.pipeline.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(self: *Self, queue: *gpu.Queue, delta: f32) void {
|
|
||||||
const old = self.uniform;
|
|
||||||
const new = Light.Uniform{
|
|
||||||
.position = zm.qmul(zm.quatFromAxisAngle(vec3u(0, 1, 0), delta), old.position),
|
|
||||||
.color = old.color,
|
|
||||||
};
|
|
||||||
queue.writeBuffer(self.buffer.buffer, 0, &[_]Light.Uniform{new});
|
|
||||||
self.uniform = new;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fn bindGroupLayout(device: *gpu.Device) *gpu.BindGroupLayout {
|
|
||||||
const visibility = .{ .vertex = true, .fragment = true };
|
|
||||||
const Entry = gpu.BindGroupLayout.Entry;
|
|
||||||
return device.createBindGroupLayout(&gpu.BindGroupLayout.Descriptor.init(.{
|
|
||||||
.entries = &.{
|
|
||||||
Entry.buffer(0, visibility, .uniform, false, 0),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pipeline() *gpu.RenderPipeline {
|
|
||||||
const device = core.device;
|
|
||||||
|
|
||||||
const camera_layout = Camera.bindGroupLayout(device);
|
|
||||||
const light_layout = Light.bindGroupLayout(device);
|
|
||||||
const layout_descriptor = gpu.PipelineLayout.Descriptor.init(.{
|
|
||||||
.bind_group_layouts = &.{
|
|
||||||
camera_layout,
|
|
||||||
light_layout,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
defer camera_layout.release();
|
|
||||||
defer light_layout.release();
|
|
||||||
|
|
||||||
const layout = device.createPipelineLayout(&layout_descriptor);
|
|
||||||
defer layout.release();
|
|
||||||
|
|
||||||
const shader = core.device.createShaderModuleWGSL("light.wgsl", @embedFile("light.wgsl"));
|
|
||||||
defer shader.release();
|
|
||||||
|
|
||||||
const blend = gpu.BlendState{};
|
|
||||||
const color_target = gpu.ColorTargetState{
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
.blend = &blend,
|
|
||||||
};
|
|
||||||
|
|
||||||
const fragment = gpu.FragmentState.init(.{
|
|
||||||
.module = shader,
|
|
||||||
.entry_point = "fs_main",
|
|
||||||
.targets = &.{color_target},
|
|
||||||
});
|
|
||||||
|
|
||||||
const descriptor = gpu.RenderPipeline.Descriptor{
|
|
||||||
.layout = layout,
|
|
||||||
.fragment = &fragment,
|
|
||||||
.vertex = gpu.VertexState.init(.{
|
|
||||||
.module = shader,
|
|
||||||
.entry_point = "vs_main",
|
|
||||||
.buffers = &.{
|
|
||||||
Cube.vertexBufferLayout(),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
.depth_stencil = &.{
|
|
||||||
.format = Texture.DEPTH_FORMAT,
|
|
||||||
.depth_write_enabled = .true,
|
|
||||||
.depth_compare = .less,
|
|
||||||
},
|
|
||||||
.primitive = .{
|
|
||||||
.cull_mode = .back,
|
|
||||||
.topology = .triangle_strip,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return device.createRenderPipeline(&descriptor);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline fn initBuffer(device: *gpu.Device, usage: gpu.Buffer.UsageFlags, data: anytype) *gpu.Buffer {
|
|
||||||
std.debug.assert(@typeInfo(@TypeOf(data)) == .Pointer);
|
|
||||||
const T = std.meta.Elem(@TypeOf(data));
|
|
||||||
|
|
||||||
var u = usage;
|
|
||||||
u.copy_dst = true;
|
|
||||||
const buffer = device.createBuffer(&.{
|
|
||||||
.size = @sizeOf(T) * data.len,
|
|
||||||
.usage = u,
|
|
||||||
.mapped_at_creation = .true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const mapped = buffer.getMappedRange(T, 0, data.len);
|
|
||||||
@memcpy(mapped.?, data);
|
|
||||||
buffer.unmap();
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn vec3i(x: isize, y: isize, z: isize) Vec {
|
|
||||||
return Vec{ @floatFromInt(x), @floatFromInt(y), @floatFromInt(z), 0.0 };
|
|
||||||
}
|
|
||||||
|
|
||||||
fn vec3u(x: usize, y: usize, z: usize) Vec {
|
|
||||||
return zm.Vec{ @floatFromInt(x), @floatFromInt(y), @floatFromInt(z), 0.0 };
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo indside Cube
|
|
||||||
const Instance = struct {
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
position: Vec,
|
|
||||||
rotation: Mat,
|
|
||||||
|
|
||||||
fn toMat(self: *const Self) Mat {
|
|
||||||
return zm.mul(self.rotation, zm.translationV(self.position));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
struct Params {
|
|
||||||
filterDim : i32,
|
|
||||||
blockDim : u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
@group(0) @binding(0) var samp : sampler;
|
|
||||||
@group(0) @binding(1) var<uniform> params : Params;
|
|
||||||
@group(1) @binding(1) var inputTex : texture_2d<f32>;
|
|
||||||
@group(1) @binding(2) var outputTex : texture_storage_2d<rgba8unorm, write>;
|
|
||||||
|
|
||||||
struct Flip {
|
|
||||||
value : u32,
|
|
||||||
}
|
|
||||||
@group(1) @binding(3) var<uniform> flip : Flip;
|
|
||||||
|
|
||||||
// This shader blurs the input texture in one direction, depending on whether
|
|
||||||
// |flip.value| is 0 or 1.
|
|
||||||
// It does so by running (128 / 4) threads per workgroup to load 128
|
|
||||||
// texels into 4 rows of shared memory. Each thread loads a
|
|
||||||
// 4 x 4 block of texels to take advantage of the texture sampling
|
|
||||||
// hardware.
|
|
||||||
// Then, each thread computes the blur result by averaging the adjacent texel values
|
|
||||||
// in shared memory.
|
|
||||||
// Because we're operating on a subset of the texture, we cannot compute all of the
|
|
||||||
// results since not all of the neighbors are available in shared memory.
|
|
||||||
// Specifically, with 128 x 128 tiles, we can only compute and write out
|
|
||||||
// square blocks of size 128 - (filterSize - 1). We compute the number of blocks
|
|
||||||
// needed in Javascript and dispatch that amount.
|
|
||||||
|
|
||||||
var<workgroup> tile : array<array<vec3<f32>, 128>, 4>;
|
|
||||||
|
|
||||||
@compute @workgroup_size(32, 1, 1)
|
|
||||||
fn main(
|
|
||||||
@builtin(workgroup_id) WorkGroupID : vec3<u32>,
|
|
||||||
@builtin(local_invocation_id) LocalInvocationID : vec3<u32>
|
|
||||||
) {
|
|
||||||
// TODO - mixed vector arithmetic (vec2<u32> and vec2<i32>)
|
|
||||||
let filterOffset = (params.filterDim - 1) / 2;
|
|
||||||
let dims = vec2<u32>(textureDimensions(inputTex, 0));
|
|
||||||
let baseIndex = vec2<u32>(WorkGroupID.xy * vec2(params.blockDim, 4) +
|
|
||||||
LocalInvocationID.xy * vec2<u32>(4, 1))
|
|
||||||
- vec2<u32>(filterOffset, 0);
|
|
||||||
|
|
||||||
for (var r = 0; r < 4; r++) {
|
|
||||||
for (var c = 0; c < 4; c++) {
|
|
||||||
var loadIndex = baseIndex + vec2<u32>(c, r);
|
|
||||||
if (flip.value != 0u) {
|
|
||||||
loadIndex = loadIndex.yx;
|
|
||||||
}
|
|
||||||
|
|
||||||
tile[r][4 * LocalInvocationID.x + u32(c)] = textureSampleLevel(
|
|
||||||
inputTex,
|
|
||||||
samp,
|
|
||||||
(vec2<f32>(loadIndex) + vec2<f32>(0.25, 0.25)) / vec2<f32>(dims),
|
|
||||||
0.0
|
|
||||||
).rgb;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
workgroupBarrier();
|
|
||||||
|
|
||||||
for (var r = 0; r < 4; r++) {
|
|
||||||
for (var c = 0; c < 4; c++) {
|
|
||||||
var writeIndex = baseIndex + vec2<u32>(c, r);
|
|
||||||
if (flip.value != 0) {
|
|
||||||
writeIndex = writeIndex.yx;
|
|
||||||
}
|
|
||||||
|
|
||||||
let center = u32(4 * LocalInvocationID.x) + c;
|
|
||||||
if (center >= filterOffset &&
|
|
||||||
center < 128 - filterOffset &&
|
|
||||||
all(writeIndex < dims)) {
|
|
||||||
var acc = vec3(0.0, 0.0, 0.0);
|
|
||||||
for (var f = 0; f < params.filterDim; f++) {
|
|
||||||
var i = center + f - filterOffset;
|
|
||||||
acc = acc + (1.0 / f32(params.filterDim)) * tile[r][i];
|
|
||||||
}
|
|
||||||
textureStore(outputTex, writeIndex, vec4(acc, 1.0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
@group(0) @binding(0) var mySampler : sampler;
|
|
||||||
@group(0) @binding(1) var myTexture : texture_2d<f32>;
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) Position : vec4<f32>,
|
|
||||||
@location(0) fragUV : vec2<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
@vertex
|
|
||||||
fn vert_main(@builtin(vertex_index) VertexIndex : u32) -> VertexOutput {
|
|
||||||
var pos = array<vec2<f32>, 6>(
|
|
||||||
vec2<f32>( 1.0, 1.0),
|
|
||||||
vec2<f32>( 1.0, -1.0),
|
|
||||||
vec2<f32>(-1.0, -1.0),
|
|
||||||
vec2<f32>( 1.0, 1.0),
|
|
||||||
vec2<f32>(-1.0, -1.0),
|
|
||||||
vec2<f32>(-1.0, 1.0)
|
|
||||||
);
|
|
||||||
|
|
||||||
var uv = array<vec2<f32>, 6>(
|
|
||||||
vec2<f32>(1.0, 0.0),
|
|
||||||
vec2<f32>(1.0, 1.0),
|
|
||||||
vec2<f32>(0.0, 1.0),
|
|
||||||
vec2<f32>(1.0, 0.0),
|
|
||||||
vec2<f32>(0.0, 1.0),
|
|
||||||
vec2<f32>(0.0, 0.0)
|
|
||||||
);
|
|
||||||
|
|
||||||
var output : VertexOutput;
|
|
||||||
output.Position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
|
|
||||||
output.fragUV = uv[VertexIndex];
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
@fragment
|
|
||||||
fn frag_main(@location(0) fragUV : vec2<f32>) -> @location(0) vec4<f32> {
|
|
||||||
return textureSample(myTexture, mySampler, fragUV);
|
|
||||||
}
|
|
||||||
|
|
@ -1,332 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
const zigimg = @import("zigimg");
|
|
||||||
const assets = @import("assets");
|
|
||||||
|
|
||||||
title_timer: core.Timer,
|
|
||||||
blur_pipeline: *gpu.ComputePipeline,
|
|
||||||
fullscreen_quad_pipeline: *gpu.RenderPipeline,
|
|
||||||
cube_texture: *gpu.Texture,
|
|
||||||
textures: [2]*gpu.Texture,
|
|
||||||
blur_params_buffer: *gpu.Buffer,
|
|
||||||
compute_constants: *gpu.BindGroup,
|
|
||||||
compute_bind_group_0: *gpu.BindGroup,
|
|
||||||
compute_bind_group_1: *gpu.BindGroup,
|
|
||||||
compute_bind_group_2: *gpu.BindGroup,
|
|
||||||
show_result_bind_group: *gpu.BindGroup,
|
|
||||||
img_size: gpu.Extent3D,
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
// Use experimental sysgpu graphics API
|
|
||||||
pub const use_sysgpu = true;
|
|
||||||
|
|
||||||
// Constants from the blur.wgsl shader
|
|
||||||
const tile_dimension: u32 = 128;
|
|
||||||
const batch: [2]u32 = .{ 4, 4 };
|
|
||||||
|
|
||||||
// Currently hardcoded
|
|
||||||
const filter_size: u32 = 15;
|
|
||||||
const iterations: u32 = 2;
|
|
||||||
var block_dimension: u32 = tile_dimension - (filter_size - 1);
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{});
|
|
||||||
const allocator = gpa.allocator();
|
|
||||||
|
|
||||||
const queue = core.queue;
|
|
||||||
|
|
||||||
const blur_shader_module = core.device.createShaderModuleWGSL("blur.wgsl", @embedFile("blur.wgsl"));
|
|
||||||
|
|
||||||
const blur_pipeline_descriptor = gpu.ComputePipeline.Descriptor{
|
|
||||||
.compute = gpu.ProgrammableStageDescriptor{
|
|
||||||
.module = blur_shader_module,
|
|
||||||
.entry_point = "main",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const blur_pipeline = core.device.createComputePipeline(&blur_pipeline_descriptor);
|
|
||||||
blur_shader_module.release();
|
|
||||||
|
|
||||||
const fullscreen_quad_vs_module = core.device.createShaderModuleWGSL(
|
|
||||||
"fullscreen_textured_quad.wgsl",
|
|
||||||
@embedFile("fullscreen_textured_quad.wgsl"),
|
|
||||||
);
|
|
||||||
|
|
||||||
const fullscreen_quad_fs_module = core.device.createShaderModuleWGSL(
|
|
||||||
"fullscreen_textured_quad.wgsl",
|
|
||||||
@embedFile("fullscreen_textured_quad.wgsl"),
|
|
||||||
);
|
|
||||||
|
|
||||||
const blend = gpu.BlendState{};
|
|
||||||
const color_target = gpu.ColorTargetState{
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
.blend = &blend,
|
|
||||||
.write_mask = gpu.ColorWriteMaskFlags.all,
|
|
||||||
};
|
|
||||||
|
|
||||||
const fragment_state = gpu.FragmentState.init(.{
|
|
||||||
.module = fullscreen_quad_fs_module,
|
|
||||||
.entry_point = "frag_main",
|
|
||||||
.targets = &.{color_target},
|
|
||||||
});
|
|
||||||
|
|
||||||
const fullscreen_quad_pipeline_descriptor = gpu.RenderPipeline.Descriptor{
|
|
||||||
.fragment = &fragment_state,
|
|
||||||
.vertex = .{
|
|
||||||
.module = fullscreen_quad_vs_module,
|
|
||||||
.entry_point = "vert_main",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const fullscreen_quad_pipeline = core.device.createRenderPipeline(&fullscreen_quad_pipeline_descriptor);
|
|
||||||
fullscreen_quad_vs_module.release();
|
|
||||||
fullscreen_quad_fs_module.release();
|
|
||||||
|
|
||||||
const sampler = core.device.createSampler(&.{
|
|
||||||
.mag_filter = .linear,
|
|
||||||
.min_filter = .linear,
|
|
||||||
});
|
|
||||||
|
|
||||||
var img = try zigimg.Image.fromMemory(allocator, assets.gotta_go_fast_png);
|
|
||||||
defer img.deinit();
|
|
||||||
|
|
||||||
const img_size = gpu.Extent3D{ .width = @as(u32, @intCast(img.width)), .height = @as(u32, @intCast(img.height)) };
|
|
||||||
|
|
||||||
const cube_texture = core.device.createTexture(&.{
|
|
||||||
.size = img_size,
|
|
||||||
.format = .rgba8_unorm,
|
|
||||||
.usage = .{
|
|
||||||
.texture_binding = true,
|
|
||||||
.copy_dst = true,
|
|
||||||
.render_attachment = true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const data_layout = gpu.Texture.DataLayout{
|
|
||||||
.bytes_per_row = @as(u32, @intCast(img.width * 4)),
|
|
||||||
.rows_per_image = @as(u32, @intCast(img.height)),
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (img.pixels) {
|
|
||||||
.rgba32 => |pixels| queue.writeTexture(&.{ .texture = cube_texture }, &data_layout, &img_size, pixels),
|
|
||||||
.rgb24 => |pixels| {
|
|
||||||
const data = try rgb24ToRgba32(allocator, pixels);
|
|
||||||
defer data.deinit(allocator);
|
|
||||||
queue.writeTexture(&.{ .texture = cube_texture }, &data_layout, &img_size, data.rgba32);
|
|
||||||
},
|
|
||||||
else => @panic("unsupported image color format"),
|
|
||||||
}
|
|
||||||
|
|
||||||
var textures: [2]*gpu.Texture = undefined;
|
|
||||||
for (textures, 0..) |_, i| {
|
|
||||||
textures[i] = core.device.createTexture(&.{
|
|
||||||
.size = img_size,
|
|
||||||
.format = .rgba8_unorm,
|
|
||||||
.usage = .{
|
|
||||||
.storage_binding = true,
|
|
||||||
.texture_binding = true,
|
|
||||||
.copy_dst = true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// the shader blurs the input texture in one direction,
|
|
||||||
// depending on whether flip value is 0 or 1
|
|
||||||
var flip: [2]*gpu.Buffer = undefined;
|
|
||||||
for (flip, 0..) |_, i| {
|
|
||||||
const buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .uniform = true },
|
|
||||||
.size = @sizeOf(u32),
|
|
||||||
.mapped_at_creation = .true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const buffer_mapped = buffer.getMappedRange(u32, 0, 1);
|
|
||||||
buffer_mapped.?[0] = @as(u32, @intCast(i));
|
|
||||||
buffer.unmap();
|
|
||||||
|
|
||||||
flip[i] = buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
const blur_params_buffer = core.device.createBuffer(&.{
|
|
||||||
.size = 8,
|
|
||||||
.usage = .{ .copy_dst = true, .uniform = true },
|
|
||||||
});
|
|
||||||
|
|
||||||
const blur_bind_group_layout0 = blur_pipeline.getBindGroupLayout(0);
|
|
||||||
const blur_bind_group_layout1 = blur_pipeline.getBindGroupLayout(1);
|
|
||||||
const fullscreen_bind_group_layout = fullscreen_quad_pipeline.getBindGroupLayout(0);
|
|
||||||
const cube_texture_view = cube_texture.createView(&gpu.TextureView.Descriptor{});
|
|
||||||
const texture0_view = textures[0].createView(&gpu.TextureView.Descriptor{});
|
|
||||||
const texture1_view = textures[1].createView(&gpu.TextureView.Descriptor{});
|
|
||||||
|
|
||||||
const compute_constants = core.device.createBindGroup(&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = blur_bind_group_layout0,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.sampler(0, sampler),
|
|
||||||
gpu.BindGroup.Entry.buffer(1, blur_params_buffer, 0, 8, 8),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const compute_bind_group_0 = core.device.createBindGroup(&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = blur_bind_group_layout1,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.textureView(1, cube_texture_view),
|
|
||||||
gpu.BindGroup.Entry.textureView(2, texture0_view),
|
|
||||||
gpu.BindGroup.Entry.buffer(3, flip[0], 0, 4, 4),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const compute_bind_group_1 = core.device.createBindGroup(&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = blur_bind_group_layout1,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.textureView(1, texture0_view),
|
|
||||||
gpu.BindGroup.Entry.textureView(2, texture1_view),
|
|
||||||
gpu.BindGroup.Entry.buffer(3, flip[1], 0, 4, 4),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const compute_bind_group_2 = core.device.createBindGroup(&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = blur_bind_group_layout1,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.textureView(1, texture1_view),
|
|
||||||
gpu.BindGroup.Entry.textureView(2, texture0_view),
|
|
||||||
gpu.BindGroup.Entry.buffer(3, flip[0], 0, 4, 4),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const show_result_bind_group = core.device.createBindGroup(&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = fullscreen_bind_group_layout,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.sampler(0, sampler),
|
|
||||||
gpu.BindGroup.Entry.textureView(1, texture1_view),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
blur_bind_group_layout0.release();
|
|
||||||
blur_bind_group_layout1.release();
|
|
||||||
fullscreen_bind_group_layout.release();
|
|
||||||
sampler.release();
|
|
||||||
flip[0].release();
|
|
||||||
flip[1].release();
|
|
||||||
cube_texture_view.release();
|
|
||||||
texture0_view.release();
|
|
||||||
texture1_view.release();
|
|
||||||
|
|
||||||
const blur_params_buffer_data = [_]u32{ filter_size, block_dimension };
|
|
||||||
queue.writeBuffer(blur_params_buffer, 0, &blur_params_buffer_data);
|
|
||||||
|
|
||||||
app.title_timer = try core.Timer.start();
|
|
||||||
app.blur_pipeline = blur_pipeline;
|
|
||||||
app.fullscreen_quad_pipeline = fullscreen_quad_pipeline;
|
|
||||||
app.cube_texture = cube_texture;
|
|
||||||
app.textures = textures;
|
|
||||||
app.blur_params_buffer = blur_params_buffer;
|
|
||||||
app.compute_constants = compute_constants;
|
|
||||||
app.compute_bind_group_0 = compute_bind_group_0;
|
|
||||||
app.compute_bind_group_1 = compute_bind_group_1;
|
|
||||||
app.compute_bind_group_2 = compute_bind_group_2;
|
|
||||||
app.show_result_bind_group = show_result_bind_group;
|
|
||||||
app.img_size = img_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
defer _ = gpa.deinit();
|
|
||||||
defer core.deinit();
|
|
||||||
|
|
||||||
app.blur_pipeline.release();
|
|
||||||
app.fullscreen_quad_pipeline.release();
|
|
||||||
app.cube_texture.release();
|
|
||||||
app.textures[0].release();
|
|
||||||
app.textures[1].release();
|
|
||||||
app.blur_params_buffer.release();
|
|
||||||
app.compute_constants.release();
|
|
||||||
app.compute_bind_group_0.release();
|
|
||||||
app.compute_bind_group_1.release();
|
|
||||||
app.compute_bind_group_2.release();
|
|
||||||
app.show_result_bind_group.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(app: *App) !bool {
|
|
||||||
var iter = core.pollEvents();
|
|
||||||
while (iter.next()) |event| {
|
|
||||||
if (event == .close) return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
|
|
||||||
const compute_pass = encoder.beginComputePass(null);
|
|
||||||
compute_pass.setPipeline(app.blur_pipeline);
|
|
||||||
compute_pass.setBindGroup(0, app.compute_constants, &.{});
|
|
||||||
|
|
||||||
const width: u32 = @as(u32, @intCast(app.img_size.width));
|
|
||||||
const height: u32 = @as(u32, @intCast(app.img_size.height));
|
|
||||||
compute_pass.setBindGroup(1, app.compute_bind_group_0, &.{});
|
|
||||||
compute_pass.dispatchWorkgroups(try std.math.divCeil(u32, width, block_dimension), try std.math.divCeil(u32, height, batch[1]), 1);
|
|
||||||
|
|
||||||
compute_pass.setBindGroup(1, app.compute_bind_group_1, &.{});
|
|
||||||
compute_pass.dispatchWorkgroups(try std.math.divCeil(u32, height, block_dimension), try std.math.divCeil(u32, width, batch[1]), 1);
|
|
||||||
|
|
||||||
var i: u32 = 0;
|
|
||||||
while (i < iterations - 1) : (i += 1) {
|
|
||||||
compute_pass.setBindGroup(1, app.compute_bind_group_2, &.{});
|
|
||||||
compute_pass.dispatchWorkgroups(try std.math.divCeil(u32, width, block_dimension), try std.math.divCeil(u32, height, batch[1]), 1);
|
|
||||||
|
|
||||||
compute_pass.setBindGroup(1, app.compute_bind_group_1, &.{});
|
|
||||||
compute_pass.dispatchWorkgroups(try std.math.divCeil(u32, height, block_dimension), try std.math.divCeil(u32, width, batch[1]), 1);
|
|
||||||
}
|
|
||||||
compute_pass.end();
|
|
||||||
compute_pass.release();
|
|
||||||
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = back_buffer_view,
|
|
||||||
.clear_value = std.mem.zeroes(gpu.Color),
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
|
|
||||||
const render_pass_descriptor = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{color_attachment},
|
|
||||||
});
|
|
||||||
|
|
||||||
const render_pass = encoder.beginRenderPass(&render_pass_descriptor);
|
|
||||||
render_pass.setPipeline(app.fullscreen_quad_pipeline);
|
|
||||||
render_pass.setBindGroup(0, app.show_result_bind_group, &.{});
|
|
||||||
render_pass.draw(6, 1, 0, 0);
|
|
||||||
render_pass.end();
|
|
||||||
render_pass.release();
|
|
||||||
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
encoder.release();
|
|
||||||
const queue = core.queue;
|
|
||||||
queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
command.release();
|
|
||||||
core.swap_chain.present();
|
|
||||||
back_buffer_view.release();
|
|
||||||
|
|
||||||
// update the window title every second
|
|
||||||
if (app.title_timer.read() >= 1.0) {
|
|
||||||
app.title_timer.reset();
|
|
||||||
try core.printTitle("Image Blur [ {d}fps ] [ Input {d}hz ]", .{
|
|
||||||
core.frameRate(),
|
|
||||||
core.inputRate(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rgb24ToRgba32(allocator: std.mem.Allocator, in: []zigimg.color.Rgb24) !zigimg.color.PixelStorage {
|
|
||||||
const out = try zigimg.color.PixelStorage.init(allocator, .rgba32, in.len);
|
|
||||||
var i: usize = 0;
|
|
||||||
while (i < in.len) : (i += 1) {
|
|
||||||
out.rgba32[i] = zigimg.color.Rgba32{ .r = in[i].r, .g = in[i].g, .b = in[i].b, .a = 255 };
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
@group(0) @binding(0) var mySampler : sampler;
|
|
||||||
@group(0) @binding(1) var myTexture : texture_2d<f32>;
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) Position : vec4<f32>,
|
|
||||||
@location(0) fragUV : vec2<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
@vertex
|
|
||||||
fn vert_main(@builtin(vertex_index) VertexIndex : u32) -> VertexOutput {
|
|
||||||
// Draw a fullscreen quad using two triangles, with UV coordinates (normalized pixel coordinates)
|
|
||||||
// that would have the full texture be displayed.
|
|
||||||
var pos = array<vec2<f32>, 6>(
|
|
||||||
vec2<f32>( 1.0, 1.0), // right, top
|
|
||||||
vec2<f32>( 1.0, -1.0), // right, bottom
|
|
||||||
vec2<f32>(-1.0, -1.0), // left, bottom
|
|
||||||
vec2<f32>( 1.0, 1.0), // right, top
|
|
||||||
vec2<f32>(-1.0, -1.0), // left, bottom
|
|
||||||
vec2<f32>(-1.0, 1.0) // left, top
|
|
||||||
);
|
|
||||||
var uv = array<vec2<f32>, 6>(
|
|
||||||
vec2<f32>(1.0, 0.0),
|
|
||||||
vec2<f32>(1.0, 1.0),
|
|
||||||
vec2<f32>(0.0, 1.0),
|
|
||||||
vec2<f32>(1.0, 0.0),
|
|
||||||
vec2<f32>(0.0, 1.0),
|
|
||||||
vec2<f32>(0.0, 0.0)
|
|
||||||
);
|
|
||||||
|
|
||||||
var output : VertexOutput;
|
|
||||||
output.Position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
|
|
||||||
output.fragUV = uv[VertexIndex];
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
@fragment
|
|
||||||
fn frag_main(@location(0) fragUV : vec2<f32>) -> @location(0) vec4<f32> {
|
|
||||||
return textureSample(myTexture, mySampler, fragUV);
|
|
||||||
}
|
|
||||||
|
|
@ -1,190 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
const zigimg = @import("zigimg");
|
|
||||||
const assets = @import("assets");
|
|
||||||
|
|
||||||
// Use experimental sysgpu graphics API
|
|
||||||
pub const use_sysgpu = true;
|
|
||||||
|
|
||||||
title_timer: core.Timer,
|
|
||||||
pipeline: *gpu.RenderPipeline,
|
|
||||||
texture: *gpu.Texture,
|
|
||||||
bind_group: *gpu.BindGroup,
|
|
||||||
img_size: gpu.Extent3D,
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{});
|
|
||||||
const allocator = gpa.allocator();
|
|
||||||
|
|
||||||
// Load our shader that will render a fullscreen textured quad using two triangles, needed to
|
|
||||||
// get the image on screen.
|
|
||||||
const fullscreen_quad_vs_module = core.device.createShaderModuleWGSL(
|
|
||||||
"fullscreen_textured_quad.wgsl",
|
|
||||||
@embedFile("fullscreen_textured_quad.wgsl"),
|
|
||||||
);
|
|
||||||
defer fullscreen_quad_vs_module.release();
|
|
||||||
const fullscreen_quad_fs_module = core.device.createShaderModuleWGSL(
|
|
||||||
"fullscreen_textured_quad.wgsl",
|
|
||||||
@embedFile("fullscreen_textured_quad.wgsl"),
|
|
||||||
);
|
|
||||||
defer fullscreen_quad_fs_module.release();
|
|
||||||
|
|
||||||
// Create our render pipeline
|
|
||||||
const blend = gpu.BlendState{};
|
|
||||||
const color_target = gpu.ColorTargetState{
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
.blend = &blend,
|
|
||||||
.write_mask = gpu.ColorWriteMaskFlags.all,
|
|
||||||
};
|
|
||||||
const fragment_state = gpu.FragmentState.init(.{
|
|
||||||
.module = fullscreen_quad_fs_module,
|
|
||||||
.entry_point = "frag_main",
|
|
||||||
.targets = &.{color_target},
|
|
||||||
});
|
|
||||||
const pipeline_descriptor = gpu.RenderPipeline.Descriptor{
|
|
||||||
.fragment = &fragment_state,
|
|
||||||
.vertex = .{
|
|
||||||
.module = fullscreen_quad_vs_module,
|
|
||||||
.entry_point = "vert_main",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const pipeline = core.device.createRenderPipeline(&pipeline_descriptor);
|
|
||||||
|
|
||||||
// Create a texture sampler. This determines what happens when the texture doesn't match the
|
|
||||||
// dimensions of the screen it's being displayed on. If the image needs to be magnified or
|
|
||||||
// minified to fit, it can be linearly interpolated (i.e. 'blurred', .linear) or the nearest
|
|
||||||
// pixel may be used (i.e. 'pixelated', .nearest)
|
|
||||||
const sampler = core.device.createSampler(&.{
|
|
||||||
.mag_filter = .linear,
|
|
||||||
.min_filter = .linear,
|
|
||||||
});
|
|
||||||
defer sampler.release();
|
|
||||||
|
|
||||||
// Load the pixels of the image
|
|
||||||
var img = try zigimg.Image.fromMemory(allocator, assets.gotta_go_fast_png);
|
|
||||||
defer img.deinit();
|
|
||||||
const img_size = gpu.Extent3D{ .width = @as(u32, @intCast(img.width)), .height = @as(u32, @intCast(img.height)) };
|
|
||||||
|
|
||||||
// Create a texture
|
|
||||||
const texture = core.device.createTexture(&.{
|
|
||||||
.size = img_size,
|
|
||||||
.format = .rgba8_unorm,
|
|
||||||
.usage = .{
|
|
||||||
.texture_binding = true,
|
|
||||||
.copy_dst = true,
|
|
||||||
.render_attachment = true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Upload the pixels (from the CPU) to the GPU. You could e.g. do this once per frame if you
|
|
||||||
// wanted the image to be updated dynamically.
|
|
||||||
const data_layout = gpu.Texture.DataLayout{
|
|
||||||
.bytes_per_row = @as(u32, @intCast(img.width * 4)),
|
|
||||||
.rows_per_image = @as(u32, @intCast(img.height)),
|
|
||||||
};
|
|
||||||
switch (img.pixels) {
|
|
||||||
.rgba32 => |pixels| core.queue.writeTexture(&.{ .texture = texture }, &data_layout, &img_size, pixels),
|
|
||||||
.rgb24 => |pixels| {
|
|
||||||
const data = try rgb24ToRgba32(allocator, pixels);
|
|
||||||
defer data.deinit(allocator);
|
|
||||||
core.queue.writeTexture(&.{ .texture = texture }, &data_layout, &img_size, data.rgba32);
|
|
||||||
},
|
|
||||||
else => @panic("unsupported image color format"),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Describe which data we will pass to our shader (GPU program)
|
|
||||||
const bind_group_layout = pipeline.getBindGroupLayout(0);
|
|
||||||
defer bind_group_layout.release();
|
|
||||||
const texture_view = texture.createView(&gpu.TextureView.Descriptor{});
|
|
||||||
defer texture_view.release();
|
|
||||||
const bind_group = core.device.createBindGroup(&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = bind_group_layout,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.sampler(0, sampler),
|
|
||||||
gpu.BindGroup.Entry.textureView(1, texture_view),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
app.* = .{
|
|
||||||
.title_timer = try core.Timer.start(),
|
|
||||||
.pipeline = pipeline,
|
|
||||||
.texture = texture,
|
|
||||||
.bind_group = bind_group,
|
|
||||||
.img_size = img_size,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
defer _ = gpa.deinit();
|
|
||||||
defer core.deinit();
|
|
||||||
app.pipeline.release();
|
|
||||||
app.texture.release();
|
|
||||||
app.bind_group.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(app: *App) !bool {
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
defer back_buffer_view.release();
|
|
||||||
|
|
||||||
// Poll for events (keyboard input, etc.)
|
|
||||||
var iter = core.pollEvents();
|
|
||||||
while (iter.next()) |event| {
|
|
||||||
if (event == .close) return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
defer encoder.release();
|
|
||||||
|
|
||||||
// Begin our render pass by clearing the pixels that were on the screen from the previous frame.
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = back_buffer_view,
|
|
||||||
.clear_value = std.mem.zeroes(gpu.Color),
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
const render_pass_descriptor = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{color_attachment},
|
|
||||||
});
|
|
||||||
const render_pass = encoder.beginRenderPass(&render_pass_descriptor);
|
|
||||||
defer render_pass.release();
|
|
||||||
|
|
||||||
// Render using our pipeline
|
|
||||||
render_pass.setPipeline(app.pipeline);
|
|
||||||
render_pass.setBindGroup(0, app.bind_group, &.{});
|
|
||||||
render_pass.draw(6, 1, 0, 0); // Tell the GPU to draw 6 vertices, one object
|
|
||||||
render_pass.end();
|
|
||||||
|
|
||||||
// Submit all the commands to the GPU and render the frame.
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
defer command.release();
|
|
||||||
core.queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
core.swap_chain.present();
|
|
||||||
|
|
||||||
// update the window title every second to have the FPS
|
|
||||||
if (app.title_timer.read() >= 1.0) {
|
|
||||||
app.title_timer.reset();
|
|
||||||
try core.printTitle("Image [ {d}fps ] [ Input {d}hz ]", .{
|
|
||||||
core.frameRate(),
|
|
||||||
core.inputRate(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rgb24ToRgba32(allocator: std.mem.Allocator, in: []zigimg.color.Rgb24) !zigimg.color.PixelStorage {
|
|
||||||
const out = try zigimg.color.PixelStorage.init(allocator, .rgba32, in.len);
|
|
||||||
var i: usize = 0;
|
|
||||||
while (i < in.len) : (i += 1) {
|
|
||||||
out.rgba32[i] = zigimg.color.Rgba32{ .r = in[i].r, .g = in[i].g, .b = in[i].b, .a = 255 };
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
pub const Vertex = extern struct {
|
|
||||||
pos: @Vector(4, f32),
|
|
||||||
col: @Vector(4, f32),
|
|
||||||
uv: @Vector(2, f32),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const vertices = [_]Vertex{
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
};
|
|
||||||
|
|
@ -1,211 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
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 gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
|
|
||||||
title_timer: core.Timer,
|
|
||||||
|
|
||||||
timer: core.Timer,
|
|
||||||
pipeline: *gpu.RenderPipeline,
|
|
||||||
vertex_buffer: *gpu.Buffer,
|
|
||||||
uniform_buffer: *gpu.Buffer,
|
|
||||||
bind_group: *gpu.BindGroup,
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
// Use experimental sysgpu graphics API
|
|
||||||
pub const use_sysgpu = true;
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{});
|
|
||||||
app.timer = try core.Timer.start();
|
|
||||||
|
|
||||||
const shader_module = core.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.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.init(.{
|
|
||||||
.array_stride = @sizeOf(Vertex),
|
|
||||||
.step_mode = .vertex,
|
|
||||||
.attributes = &vertex_attributes,
|
|
||||||
});
|
|
||||||
|
|
||||||
const color_target = gpu.ColorTargetState{
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
.write_mask = gpu.ColorWriteMaskFlags.all,
|
|
||||||
};
|
|
||||||
const fragment = gpu.FragmentState.init(.{
|
|
||||||
.module = shader_module,
|
|
||||||
.entry_point = "frag_main",
|
|
||||||
.targets = &.{color_target},
|
|
||||||
});
|
|
||||||
|
|
||||||
const bgle = gpu.BindGroupLayout.Entry.buffer(0, .{ .vertex = true }, .uniform, true, 0);
|
|
||||||
const bgl = core.device.createBindGroupLayout(
|
|
||||||
&gpu.BindGroupLayout.Descriptor.init(.{
|
|
||||||
.entries = &.{bgle},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
const bind_group_layouts = [_]*gpu.BindGroupLayout{bgl};
|
|
||||||
const pipeline_layout = core.device.createPipelineLayout(&gpu.PipelineLayout.Descriptor.init(.{
|
|
||||||
.bind_group_layouts = &bind_group_layouts,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const pipeline_descriptor = gpu.RenderPipeline.Descriptor{
|
|
||||||
.fragment = &fragment,
|
|
||||||
.layout = pipeline_layout,
|
|
||||||
.vertex = gpu.VertexState.init(.{
|
|
||||||
.module = shader_module,
|
|
||||||
.entry_point = "vertex_main",
|
|
||||||
.buffers = &.{vertex_buffer_layout},
|
|
||||||
}),
|
|
||||||
.primitive = .{
|
|
||||||
.cull_mode = .back,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const vertex_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .vertex = true },
|
|
||||||
.size = @sizeOf(Vertex) * vertices.len,
|
|
||||||
.mapped_at_creation = .true,
|
|
||||||
});
|
|
||||||
const vertex_mapped = vertex_buffer.getMappedRange(Vertex, 0, vertices.len);
|
|
||||||
@memcpy(vertex_mapped.?, vertices[0..]);
|
|
||||||
vertex_buffer.unmap();
|
|
||||||
|
|
||||||
const x_count = 4;
|
|
||||||
const y_count = 4;
|
|
||||||
const num_instances = x_count * y_count;
|
|
||||||
|
|
||||||
const uniform_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .copy_dst = true, .uniform = true },
|
|
||||||
.size = @sizeOf(UniformBufferObject) * num_instances,
|
|
||||||
.mapped_at_creation = .false,
|
|
||||||
});
|
|
||||||
const bind_group = core.device.createBindGroup(
|
|
||||||
&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = bgl,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.buffer(0, uniform_buffer, 0, @sizeOf(UniformBufferObject) * num_instances, @sizeOf(UniformBufferObject)),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
app.title_timer = try core.Timer.start();
|
|
||||||
app.pipeline = core.device.createRenderPipeline(&pipeline_descriptor);
|
|
||||||
app.vertex_buffer = vertex_buffer;
|
|
||||||
app.uniform_buffer = uniform_buffer;
|
|
||||||
app.bind_group = bind_group;
|
|
||||||
|
|
||||||
shader_module.release();
|
|
||||||
pipeline_layout.release();
|
|
||||||
bgl.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
defer _ = gpa.deinit();
|
|
||||||
defer core.deinit();
|
|
||||||
|
|
||||||
app.pipeline.release();
|
|
||||||
app.vertex_buffer.release();
|
|
||||||
app.bind_group.release();
|
|
||||||
app.uniform_buffer.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(app: *App) !bool {
|
|
||||||
var iter = core.pollEvents();
|
|
||||||
while (iter.next()) |event| {
|
|
||||||
switch (event) {
|
|
||||||
.key_press => |ev| {
|
|
||||||
if (ev.key == .space) return true;
|
|
||||||
},
|
|
||||||
.close => return true,
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = back_buffer_view,
|
|
||||||
.clear_value = std.mem.zeroes(gpu.Color),
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
const render_pass_info = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{color_attachment},
|
|
||||||
});
|
|
||||||
|
|
||||||
{
|
|
||||||
const proj = zm.perspectiveFovRh(
|
|
||||||
(std.math.pi / 3.0),
|
|
||||||
@as(f32, @floatFromInt(core.descriptor.width)) / @as(f32, @floatFromInt(core.descriptor.height)),
|
|
||||||
10,
|
|
||||||
30,
|
|
||||||
);
|
|
||||||
|
|
||||||
var ubos: [16]UniformBufferObject = undefined;
|
|
||||||
const time = app.timer.read();
|
|
||||||
const step: f32 = 4.0;
|
|
||||||
var m: u8 = 0;
|
|
||||||
var x: u8 = 0;
|
|
||||||
while (x < 4) : (x += 1) {
|
|
||||||
var y: u8 = 0;
|
|
||||||
while (y < 4) : (y += 1) {
|
|
||||||
const trans = zm.translation(step * (@as(f32, @floatFromInt(x)) - 2.0 + 0.5), step * (@as(f32, @floatFromInt(y)) - 2.0 + 0.5), -20);
|
|
||||||
const localTime = time + @as(f32, @floatFromInt(m)) * 0.5;
|
|
||||||
const model = zm.mul(zm.mul(zm.mul(zm.rotationX(localTime * (std.math.pi / 2.1)), zm.rotationY(localTime * (std.math.pi / 0.9))), zm.rotationZ(localTime * (std.math.pi / 1.3))), trans);
|
|
||||||
const mvp = zm.mul(model, proj);
|
|
||||||
const ubo = UniformBufferObject{
|
|
||||||
.mat = mvp,
|
|
||||||
};
|
|
||||||
ubos[m] = ubo;
|
|
||||||
m += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
encoder.writeBuffer(app.uniform_buffer, 0, &ubos);
|
|
||||||
}
|
|
||||||
|
|
||||||
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_group, &.{0});
|
|
||||||
pass.draw(vertices.len, 16, 0, 0);
|
|
||||||
pass.end();
|
|
||||||
pass.release();
|
|
||||||
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
encoder.release();
|
|
||||||
|
|
||||||
const queue = core.queue;
|
|
||||||
queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
command.release();
|
|
||||||
core.swap_chain.present();
|
|
||||||
back_buffer_view.release();
|
|
||||||
|
|
||||||
// update the window title every second
|
|
||||||
if (app.title_timer.read() >= 1.0) {
|
|
||||||
app.title_timer.reset();
|
|
||||||
try core.printTitle("Instanced Cube [ {d}fps ] [ Input {d}hz ]", .{
|
|
||||||
core.frameRate(),
|
|
||||||
core.inputRate(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
@binding(0) @group(0) var<uniform> ubos : array<mat4x4<f32>, 16>;
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) position_clip : vec4<f32>,
|
|
||||||
@location(0) fragUV : vec2<f32>,
|
|
||||||
@location(1) fragPosition: vec4<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
@vertex
|
|
||||||
fn vertex_main(@builtin(instance_index) instanceIdx : u32,
|
|
||||||
@location(0) position : vec4<f32>,
|
|
||||||
@location(1) uv : vec2<f32>) -> VertexOutput {
|
|
||||||
var output : VertexOutput;
|
|
||||||
output.position_clip = ubos[instanceIdx] * position;
|
|
||||||
output.fragUV = uv;
|
|
||||||
output.fragPosition = 0.5 * (position + vec4<f32>(1.0, 1.0, 1.0, 1.0));
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
@fragment fn frag_main(
|
|
||||||
@location(0) fragUV: vec2<f32>,
|
|
||||||
@location(1) fragPosition: vec4<f32>
|
|
||||||
) -> @location(0) vec4<f32> {
|
|
||||||
return fragPosition;
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
@group(0) @binding(0) var<storage, read_write> output: array<f32>;
|
|
||||||
|
|
||||||
@compute @workgroup_size(64, 1, 1)
|
|
||||||
fn main(
|
|
||||||
@builtin(global_invocation_id)
|
|
||||||
global_id : vec3<u32>,
|
|
||||||
|
|
||||||
@builtin(local_invocation_id)
|
|
||||||
local_id : vec3<u32>,
|
|
||||||
) {
|
|
||||||
if (global_id.x >= arrayLength(&output)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
output[global_id.x] =
|
|
||||||
f32(global_id.x) * 1000. + f32(local_id.x);
|
|
||||||
}
|
|
||||||
|
|
@ -1,106 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
// Use experimental sysgpu graphics API
|
|
||||||
pub const use_sysgpu = true;
|
|
||||||
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
|
|
||||||
const workgroup_size = 64;
|
|
||||||
const buffer_size = 1000;
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{});
|
|
||||||
app.* = .{};
|
|
||||||
|
|
||||||
const output = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .storage = true, .copy_src = true },
|
|
||||||
.size = buffer_size * @sizeOf(f32),
|
|
||||||
.mapped_at_creation = .false,
|
|
||||||
});
|
|
||||||
defer output.release();
|
|
||||||
|
|
||||||
const staging = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .map_read = true, .copy_dst = true },
|
|
||||||
.size = buffer_size * @sizeOf(f32),
|
|
||||||
.mapped_at_creation = .false,
|
|
||||||
});
|
|
||||||
defer staging.release();
|
|
||||||
|
|
||||||
const compute_module = core.device.createShaderModuleWGSL("main.wgsl", @embedFile("main.wgsl"));
|
|
||||||
|
|
||||||
const compute_pipeline = core.device.createComputePipeline(&gpu.ComputePipeline.Descriptor{ .compute = gpu.ProgrammableStageDescriptor{
|
|
||||||
.module = compute_module,
|
|
||||||
.entry_point = "main",
|
|
||||||
} });
|
|
||||||
defer compute_pipeline.release();
|
|
||||||
|
|
||||||
const layout = compute_pipeline.getBindGroupLayout(0);
|
|
||||||
defer layout.release();
|
|
||||||
|
|
||||||
const compute_bind_group = core.device.createBindGroup(&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = layout,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.buffer(0, output, 0, buffer_size * @sizeOf(f32), @sizeOf(f32)),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
defer compute_bind_group.release();
|
|
||||||
|
|
||||||
compute_module.release();
|
|
||||||
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
|
|
||||||
const compute_pass = encoder.beginComputePass(null);
|
|
||||||
compute_pass.setPipeline(compute_pipeline);
|
|
||||||
compute_pass.setBindGroup(0, compute_bind_group, &.{});
|
|
||||||
compute_pass.dispatchWorkgroups(try std.math.divCeil(u32, buffer_size, workgroup_size), 1, 1);
|
|
||||||
compute_pass.end();
|
|
||||||
compute_pass.release();
|
|
||||||
|
|
||||||
encoder.copyBufferToBuffer(output, 0, staging, 0, buffer_size * @sizeOf(f32));
|
|
||||||
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
encoder.release();
|
|
||||||
|
|
||||||
var response: gpu.Buffer.MapAsyncStatus = undefined;
|
|
||||||
const callback = (struct {
|
|
||||||
pub inline fn callback(ctx: *gpu.Buffer.MapAsyncStatus, status: gpu.Buffer.MapAsyncStatus) void {
|
|
||||||
ctx.* = status;
|
|
||||||
}
|
|
||||||
}).callback;
|
|
||||||
|
|
||||||
var queue = core.queue;
|
|
||||||
queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
command.release();
|
|
||||||
|
|
||||||
staging.mapAsync(.{ .read = true }, 0, buffer_size * @sizeOf(f32), &response, callback);
|
|
||||||
while (true) {
|
|
||||||
if (response == gpu.Buffer.MapAsyncStatus.success) {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
core.device.tick();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const staging_mapped = staging.getConstMappedRange(f32, 0, buffer_size);
|
|
||||||
for (staging_mapped.?) |v| {
|
|
||||||
std.debug.print("{d} ", .{v});
|
|
||||||
}
|
|
||||||
std.debug.print("\n", .{});
|
|
||||||
staging.unmap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
_ = app;
|
|
||||||
defer _ = gpa.deinit();
|
|
||||||
core.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(_: *App) !bool {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
pub const Vertex = extern struct {
|
|
||||||
pos: @Vector(3, f32),
|
|
||||||
normal: @Vector(3, f32),
|
|
||||||
uv: @Vector(2, f32),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const vertices = [_]Vertex{
|
|
||||||
.{ .pos = .{ 1, -1, 1 }, .normal = .{ 0, -1, 0 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1 }, .normal = .{ 0, -1, 0 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1 }, .normal = .{ 0, -1, 0 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1 }, .normal = .{ 0, -1, 0 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1 }, .normal = .{ 0, -1, 0 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1 }, .normal = .{ 0, -1, 0 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, 1, 1 }, .normal = .{ 1, 0, 0 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1 }, .normal = .{ 1, 0, 0 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1 }, .normal = .{ 1, 0, 0 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1 }, .normal = .{ 1, 0, 0 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1 }, .normal = .{ 1, 0, 0 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1 }, .normal = .{ 1, 0, 0 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ -1, 1, 1 }, .normal = .{ 0, 1, 0 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1 }, .normal = .{ 0, 1, 0 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1 }, .normal = .{ 0, 1, 0 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1 }, .normal = .{ 0, 1, 0 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1 }, .normal = .{ 0, 1, 0 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1 }, .normal = .{ 0, 1, 0 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ -1, -1, 1 }, .normal = .{ -1, 0, 0 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1 }, .normal = .{ -1, 0, 0 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1 }, .normal = .{ -1, 0, 0 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1 }, .normal = .{ -1, 0, 0 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1 }, .normal = .{ -1, 0, 0 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1 }, .normal = .{ -1, 0, 0 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, 1, 1 }, .normal = .{ 0, 0, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1 }, .normal = .{ 0, 0, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1 }, .normal = .{ 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1 }, .normal = .{ 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1 }, .normal = .{ 0, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1 }, .normal = .{ 0, 0, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, -1, -1 }, .normal = .{ 0, 0, -1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1 }, .normal = .{ 0, 0, -1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1 }, .normal = .{ 0, 0, -1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1 }, .normal = .{ 0, 0, -1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1 }, .normal = .{ 0, 0, -1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1 }, .normal = .{ 0, 0, -1 }, .uv = .{ 0, 0 } },
|
|
||||||
};
|
|
||||||
|
|
@ -1,467 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
const zm = @import("zmath");
|
|
||||||
|
|
||||||
const Vertex = @import("cube_mesh.zig").Vertex;
|
|
||||||
const vertices = @import("cube_mesh.zig").vertices;
|
|
||||||
const Quad = @import("quad_mesh.zig").Quad;
|
|
||||||
const quad = @import("quad_mesh.zig").quad;
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
// Use experimental sysgpu graphics API
|
|
||||||
pub const use_sysgpu = true;
|
|
||||||
|
|
||||||
const pixel_size = 8;
|
|
||||||
|
|
||||||
const UniformBufferObject = struct {
|
|
||||||
mat: zm.Mat,
|
|
||||||
};
|
|
||||||
const PostUniformBufferObject = extern struct {
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
pixel_size: u32 = pixel_size,
|
|
||||||
};
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
|
|
||||||
title_timer: core.Timer,
|
|
||||||
timer: core.Timer,
|
|
||||||
|
|
||||||
pipeline: *gpu.RenderPipeline,
|
|
||||||
normal_pipeline: *gpu.RenderPipeline,
|
|
||||||
vertex_buffer: *gpu.Buffer,
|
|
||||||
uniform_buffer: *gpu.Buffer,
|
|
||||||
bind_group: *gpu.BindGroup,
|
|
||||||
|
|
||||||
post_pipeline: *gpu.RenderPipeline,
|
|
||||||
post_vertex_buffer: *gpu.Buffer,
|
|
||||||
post_uniform_buffer: *gpu.Buffer,
|
|
||||||
post_bind_group: *gpu.BindGroup,
|
|
||||||
|
|
||||||
draw_texture_view: *gpu.TextureView,
|
|
||||||
depth_texture_view: *gpu.TextureView,
|
|
||||||
normal_texture_view: *gpu.TextureView,
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{});
|
|
||||||
app.title_timer = try core.Timer.start();
|
|
||||||
app.timer = try core.Timer.start();
|
|
||||||
|
|
||||||
try app.createRenderTextures();
|
|
||||||
app.createDrawPipeline();
|
|
||||||
app.createPostPipeline();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
defer _ = gpa.deinit();
|
|
||||||
defer core.deinit();
|
|
||||||
|
|
||||||
app.cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(app: *App) !bool {
|
|
||||||
var iter = core.pollEvents();
|
|
||||||
while (iter.next()) |event| {
|
|
||||||
switch (event) {
|
|
||||||
.key_press => |ev| {
|
|
||||||
if (ev.key == .space) return true;
|
|
||||||
},
|
|
||||||
.close => return true,
|
|
||||||
.framebuffer_resize => {
|
|
||||||
app.cleanup();
|
|
||||||
try app.createRenderTextures();
|
|
||||||
app.createDrawPipeline();
|
|
||||||
app.createPostPipeline();
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const size = core.size();
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
encoder.writeBuffer(app.post_uniform_buffer, 0, &[_]PostUniformBufferObject{
|
|
||||||
PostUniformBufferObject{
|
|
||||||
.width = size.width,
|
|
||||||
.height = size.height,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
{
|
|
||||||
const time = app.timer.read() * 0.5;
|
|
||||||
const model = zm.mul(zm.rotationX(time * (std.math.pi / 2.0)), zm.rotationZ(time * (std.math.pi / 2.0)));
|
|
||||||
const view = zm.lookAtRh(
|
|
||||||
zm.Vec{ 0, 5, 2, 1 },
|
|
||||||
zm.Vec{ 0, 0, 0, 1 },
|
|
||||||
zm.Vec{ 0, 0, 1, 0 },
|
|
||||||
);
|
|
||||||
const proj = zm.perspectiveFovRh(
|
|
||||||
(std.math.pi / 4.0),
|
|
||||||
@as(f32, @floatFromInt(core.descriptor.width)) / @as(f32, @floatFromInt(core.descriptor.height)),
|
|
||||||
0.1,
|
|
||||||
10,
|
|
||||||
);
|
|
||||||
const mvp = zm.mul(zm.mul(model, view), proj);
|
|
||||||
const ubo = UniformBufferObject{
|
|
||||||
.mat = zm.transpose(mvp),
|
|
||||||
};
|
|
||||||
encoder.writeBuffer(app.uniform_buffer, 0, &[_]UniformBufferObject{ubo});
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// render scene to downscaled texture
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = app.draw_texture_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},
|
|
||||||
.depth_stencil_attachment = &gpu.RenderPassDepthStencilAttachment{
|
|
||||||
.view = app.depth_texture_view,
|
|
||||||
.depth_load_op = .clear,
|
|
||||||
.depth_store_op = .store,
|
|
||||||
.depth_clear_value = 1.0,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
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_group, &.{0});
|
|
||||||
pass.draw(vertices.len, 1, 0, 0);
|
|
||||||
pass.end();
|
|
||||||
pass.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// render scene normals to texture
|
|
||||||
const normal_color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = app.normal_texture_view,
|
|
||||||
.clear_value = .{ .r = 0.5, .b = 0.5, .g = 0.5, .a = 1.0 },
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
const normal_render_pass_info = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{normal_color_attachment},
|
|
||||||
});
|
|
||||||
|
|
||||||
const normal_pass = encoder.beginRenderPass(&normal_render_pass_info);
|
|
||||||
normal_pass.setPipeline(app.normal_pipeline);
|
|
||||||
normal_pass.setVertexBuffer(0, app.vertex_buffer, 0, @sizeOf(Vertex) * vertices.len);
|
|
||||||
normal_pass.setBindGroup(0, app.bind_group, &.{0});
|
|
||||||
normal_pass.draw(vertices.len, 1, 0, 0);
|
|
||||||
normal_pass.end();
|
|
||||||
normal_pass.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
{
|
|
||||||
// render to swap chain using previous passes
|
|
||||||
const post_color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = back_buffer_view,
|
|
||||||
.clear_value = std.mem.zeroes(gpu.Color),
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
const post_render_pass_info = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{post_color_attachment},
|
|
||||||
});
|
|
||||||
|
|
||||||
const draw_pass = encoder.beginRenderPass(&post_render_pass_info);
|
|
||||||
draw_pass.setPipeline(app.post_pipeline);
|
|
||||||
draw_pass.setVertexBuffer(0, app.post_vertex_buffer, 0, @sizeOf(Quad) * quad.len);
|
|
||||||
draw_pass.setBindGroup(0, app.post_bind_group, &.{0});
|
|
||||||
draw_pass.draw(quad.len, 1, 0, 0);
|
|
||||||
draw_pass.end();
|
|
||||||
draw_pass.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
encoder.release();
|
|
||||||
|
|
||||||
const queue = core.queue;
|
|
||||||
queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
command.release();
|
|
||||||
core.swap_chain.present();
|
|
||||||
back_buffer_view.release();
|
|
||||||
|
|
||||||
// update the window title every second
|
|
||||||
if (app.title_timer.read() >= 1.0) {
|
|
||||||
app.title_timer.reset();
|
|
||||||
try core.printTitle("Pixel Post Process [ {d}fps ] [ Input {d}hz ]", .{
|
|
||||||
core.frameRate(),
|
|
||||||
core.inputRate(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cleanup(app: *App) void {
|
|
||||||
app.pipeline.release();
|
|
||||||
app.normal_pipeline.release();
|
|
||||||
app.vertex_buffer.release();
|
|
||||||
app.uniform_buffer.release();
|
|
||||||
app.bind_group.release();
|
|
||||||
|
|
||||||
app.post_pipeline.release();
|
|
||||||
app.post_vertex_buffer.release();
|
|
||||||
app.post_uniform_buffer.release();
|
|
||||||
app.post_bind_group.release();
|
|
||||||
|
|
||||||
app.draw_texture_view.release();
|
|
||||||
app.depth_texture_view.release();
|
|
||||||
app.normal_texture_view.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn createRenderTextures(app: *App) !void {
|
|
||||||
const size = core.size();
|
|
||||||
|
|
||||||
const draw_texture_desc = gpu.Texture.Descriptor.init(.{
|
|
||||||
.size = .{ .width = size.width / pixel_size, .height = size.height / pixel_size },
|
|
||||||
.format = .bgra8_unorm,
|
|
||||||
.usage = .{ .texture_binding = true, .copy_dst = true, .render_attachment = true },
|
|
||||||
});
|
|
||||||
const draw_texture = core.device.createTexture(&draw_texture_desc);
|
|
||||||
app.draw_texture_view = draw_texture.createView(null);
|
|
||||||
draw_texture.release();
|
|
||||||
|
|
||||||
const depth_texture_desc = gpu.Texture.Descriptor.init(.{
|
|
||||||
.size = .{ .width = size.width / pixel_size, .height = size.height / pixel_size },
|
|
||||||
.format = .depth32_float,
|
|
||||||
.usage = .{ .texture_binding = true, .copy_dst = true, .render_attachment = true },
|
|
||||||
});
|
|
||||||
const depth_texture = core.device.createTexture(&depth_texture_desc);
|
|
||||||
app.depth_texture_view = depth_texture.createView(null);
|
|
||||||
depth_texture.release();
|
|
||||||
|
|
||||||
const normal_texture_desc = gpu.Texture.Descriptor.init(.{
|
|
||||||
.size = .{ .width = size.width / pixel_size, .height = size.height / pixel_size },
|
|
||||||
.format = .bgra8_unorm,
|
|
||||||
.usage = .{ .texture_binding = true, .copy_dst = true, .render_attachment = true },
|
|
||||||
});
|
|
||||||
const normal_texture = core.device.createTexture(&normal_texture_desc);
|
|
||||||
app.normal_texture_view = normal_texture.createView(null);
|
|
||||||
normal_texture.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn createDrawPipeline(app: *App) void {
|
|
||||||
const shader_module = core.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.wgsl"));
|
|
||||||
const vertex_attributes = [_]gpu.VertexAttribute{
|
|
||||||
.{ .format = .float32x3, .offset = @offsetOf(Vertex, "pos"), .shader_location = 0 },
|
|
||||||
.{ .format = .float32x3, .offset = @offsetOf(Vertex, "normal"), .shader_location = 1 },
|
|
||||||
.{ .format = .float32x2, .offset = @offsetOf(Vertex, "uv"), .shader_location = 2 },
|
|
||||||
};
|
|
||||||
const vertex_buffer_layout = gpu.VertexBufferLayout.init(.{
|
|
||||||
.array_stride = @sizeOf(Vertex),
|
|
||||||
.step_mode = .vertex,
|
|
||||||
.attributes = &vertex_attributes,
|
|
||||||
});
|
|
||||||
const vertex = gpu.VertexState.init(.{
|
|
||||||
.module = shader_module,
|
|
||||||
.entry_point = "vertex_main",
|
|
||||||
.buffers = &.{vertex_buffer_layout},
|
|
||||||
});
|
|
||||||
|
|
||||||
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 bgle = gpu.BindGroupLayout.Entry.buffer(0, .{ .vertex = true }, .uniform, true, 0);
|
|
||||||
const bgl = core.device.createBindGroupLayout(
|
|
||||||
&gpu.BindGroupLayout.Descriptor.init(.{
|
|
||||||
.entries = &.{bgle},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
const bind_group_layouts = [_]*gpu.BindGroupLayout{bgl};
|
|
||||||
const pipeline_layout = core.device.createPipelineLayout(
|
|
||||||
&gpu.PipelineLayout.Descriptor.init(.{
|
|
||||||
.bind_group_layouts = &bind_group_layouts,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
const vertex_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .vertex = true },
|
|
||||||
.size = @sizeOf(Vertex) * vertices.len,
|
|
||||||
.mapped_at_creation = .true,
|
|
||||||
});
|
|
||||||
const vertex_mapped = vertex_buffer.getMappedRange(Vertex, 0, vertices.len);
|
|
||||||
@memcpy(vertex_mapped.?, vertices[0..]);
|
|
||||||
vertex_buffer.unmap();
|
|
||||||
|
|
||||||
const uniform_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .copy_dst = true, .uniform = true },
|
|
||||||
.size = @sizeOf(UniformBufferObject),
|
|
||||||
.mapped_at_creation = .false,
|
|
||||||
});
|
|
||||||
const bind_group = core.device.createBindGroup(
|
|
||||||
&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = bgl,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.buffer(0, uniform_buffer, 0, @sizeOf(UniformBufferObject), @sizeOf(UniformBufferObject)),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
const pipeline_descriptor = gpu.RenderPipeline.Descriptor{
|
|
||||||
.fragment = &fragment,
|
|
||||||
.layout = pipeline_layout,
|
|
||||||
.vertex = vertex,
|
|
||||||
.primitive = .{
|
|
||||||
.cull_mode = .back,
|
|
||||||
},
|
|
||||||
.depth_stencil = &gpu.DepthStencilState{
|
|
||||||
.format = .depth32_float,
|
|
||||||
.depth_write_enabled = .true,
|
|
||||||
.depth_compare = .less,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
{
|
|
||||||
// "same" pipeline, different fragment shader to create a texture with normal information
|
|
||||||
const normal_fs_module = core.device.createShaderModuleWGSL("normal_frag.wgsl", @embedFile("normal_frag.wgsl"));
|
|
||||||
const normal_fragment = gpu.FragmentState.init(.{
|
|
||||||
.module = normal_fs_module,
|
|
||||||
.entry_point = "main",
|
|
||||||
.targets = &.{color_target},
|
|
||||||
});
|
|
||||||
const normal_pipeline_descriptor = gpu.RenderPipeline.Descriptor{
|
|
||||||
.fragment = &normal_fragment,
|
|
||||||
.layout = pipeline_layout,
|
|
||||||
.vertex = vertex,
|
|
||||||
.primitive = .{
|
|
||||||
.cull_mode = .back,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
app.normal_pipeline = core.device.createRenderPipeline(&normal_pipeline_descriptor);
|
|
||||||
|
|
||||||
normal_fs_module.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
app.pipeline = core.device.createRenderPipeline(&pipeline_descriptor);
|
|
||||||
app.vertex_buffer = vertex_buffer;
|
|
||||||
app.uniform_buffer = uniform_buffer;
|
|
||||||
app.bind_group = bind_group;
|
|
||||||
|
|
||||||
shader_module.release();
|
|
||||||
pipeline_layout.release();
|
|
||||||
bgl.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn createPostPipeline(app: *App) void {
|
|
||||||
const vs_module = core.device.createShaderModuleWGSL("pixel_vert.wgsl", @embedFile("pixel_vert.wgsl"));
|
|
||||||
const vertex_attributes = [_]gpu.VertexAttribute{
|
|
||||||
.{ .format = .float32x3, .offset = @offsetOf(Quad, "pos"), .shader_location = 0 },
|
|
||||||
.{ .format = .float32x2, .offset = @offsetOf(Quad, "uv"), .shader_location = 1 },
|
|
||||||
};
|
|
||||||
const vertex_buffer_layout = gpu.VertexBufferLayout.init(.{
|
|
||||||
.array_stride = @sizeOf(Quad),
|
|
||||||
.step_mode = .vertex,
|
|
||||||
.attributes = &vertex_attributes,
|
|
||||||
});
|
|
||||||
const vertex = gpu.VertexState.init(.{
|
|
||||||
.module = vs_module,
|
|
||||||
.entry_point = "main",
|
|
||||||
.buffers = &.{vertex_buffer_layout},
|
|
||||||
});
|
|
||||||
|
|
||||||
const fs_module = core.device.createShaderModuleWGSL("pixel_frag.wgsl", @embedFile("pixel_frag.wgsl"));
|
|
||||||
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 = fs_module,
|
|
||||||
.entry_point = "main",
|
|
||||||
.targets = &.{color_target},
|
|
||||||
});
|
|
||||||
|
|
||||||
const bgl = core.device.createBindGroupLayout(
|
|
||||||
&gpu.BindGroupLayout.Descriptor.init(.{
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroupLayout.Entry.texture(0, .{ .fragment = true }, .float, .dimension_2d, false),
|
|
||||||
gpu.BindGroupLayout.Entry.sampler(1, .{ .fragment = true }, .filtering),
|
|
||||||
gpu.BindGroupLayout.Entry.texture(2, .{ .fragment = true }, .depth, .dimension_2d, false),
|
|
||||||
gpu.BindGroupLayout.Entry.sampler(3, .{ .fragment = true }, .filtering),
|
|
||||||
gpu.BindGroupLayout.Entry.texture(4, .{ .fragment = true }, .float, .dimension_2d, false),
|
|
||||||
gpu.BindGroupLayout.Entry.sampler(5, .{ .fragment = true }, .filtering),
|
|
||||||
gpu.BindGroupLayout.Entry.buffer(6, .{ .fragment = true }, .uniform, true, 0),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
const bind_group_layouts = [_]*gpu.BindGroupLayout{bgl};
|
|
||||||
const pipeline_layout = core.device.createPipelineLayout(&gpu.PipelineLayout.Descriptor.init(.{
|
|
||||||
.bind_group_layouts = &bind_group_layouts,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const vertex_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .vertex = true },
|
|
||||||
.size = @sizeOf(Quad) * quad.len,
|
|
||||||
.mapped_at_creation = .true,
|
|
||||||
});
|
|
||||||
const vertex_mapped = vertex_buffer.getMappedRange(Quad, 0, quad.len);
|
|
||||||
@memcpy(vertex_mapped.?, quad[0..]);
|
|
||||||
vertex_buffer.unmap();
|
|
||||||
|
|
||||||
const draw_sampler = core.device.createSampler(null);
|
|
||||||
const depth_sampler = core.device.createSampler(null);
|
|
||||||
const normal_sampler = core.device.createSampler(null);
|
|
||||||
const uniform_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .copy_dst = true, .uniform = true },
|
|
||||||
.size = @sizeOf(PostUniformBufferObject),
|
|
||||||
.mapped_at_creation = .false,
|
|
||||||
});
|
|
||||||
const bind_group = core.device.createBindGroup(
|
|
||||||
&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = bgl,
|
|
||||||
.entries = &[_]gpu.BindGroup.Entry{
|
|
||||||
gpu.BindGroup.Entry.textureView(0, app.draw_texture_view),
|
|
||||||
gpu.BindGroup.Entry.sampler(1, draw_sampler),
|
|
||||||
gpu.BindGroup.Entry.textureView(2, app.depth_texture_view),
|
|
||||||
gpu.BindGroup.Entry.sampler(3, depth_sampler),
|
|
||||||
gpu.BindGroup.Entry.textureView(4, app.normal_texture_view),
|
|
||||||
gpu.BindGroup.Entry.sampler(5, normal_sampler),
|
|
||||||
gpu.BindGroup.Entry.buffer(6, uniform_buffer, 0, @sizeOf(PostUniformBufferObject), @sizeOf(PostUniformBufferObject)),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
draw_sampler.release();
|
|
||||||
depth_sampler.release();
|
|
||||||
normal_sampler.release();
|
|
||||||
|
|
||||||
const pipeline_descriptor = gpu.RenderPipeline.Descriptor{
|
|
||||||
.fragment = &fragment,
|
|
||||||
.layout = pipeline_layout,
|
|
||||||
.vertex = vertex,
|
|
||||||
.primitive = .{
|
|
||||||
.cull_mode = .back,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
app.post_pipeline = core.device.createRenderPipeline(&pipeline_descriptor);
|
|
||||||
app.post_vertex_buffer = vertex_buffer;
|
|
||||||
app.post_uniform_buffer = uniform_buffer;
|
|
||||||
app.post_bind_group = bind_group;
|
|
||||||
|
|
||||||
vs_module.release();
|
|
||||||
fs_module.release();
|
|
||||||
pipeline_layout.release();
|
|
||||||
bgl.release();
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
@fragment fn main(
|
|
||||||
@location(0) normal: vec3<f32>,
|
|
||||||
@location(1) uv: vec2<f32>,
|
|
||||||
) -> @location(0) vec4<f32> {
|
|
||||||
return vec4<f32>(normal / 2 + 0.5, 1.0);
|
|
||||||
}
|
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
@group(0) @binding(0)
|
|
||||||
var draw_texture: texture_2d<f32>;
|
|
||||||
@group(0) @binding(1)
|
|
||||||
var draw_texture_sampler: sampler;
|
|
||||||
|
|
||||||
@group(0) @binding(2)
|
|
||||||
var depth_texture: texture_depth_2d;
|
|
||||||
@group(0) @binding(3)
|
|
||||||
var depth_texture_sampler: sampler;
|
|
||||||
|
|
||||||
@group(0) @binding(4)
|
|
||||||
var normal_texture: texture_2d<f32>;
|
|
||||||
@group(0) @binding(5)
|
|
||||||
var normal_texture_sampler: sampler;
|
|
||||||
|
|
||||||
struct View {
|
|
||||||
@location(0) width: u32,
|
|
||||||
@location(1) height: u32,
|
|
||||||
@location(2) pixel_size: u32,
|
|
||||||
}
|
|
||||||
@group(0) @binding(6)
|
|
||||||
var<uniform> view: View;
|
|
||||||
|
|
||||||
fn sample_depth(uv: vec2<f32>, x: f32, y: f32) -> f32 {
|
|
||||||
return textureSample(
|
|
||||||
depth_texture,
|
|
||||||
depth_texture_sampler,
|
|
||||||
uv + vec2<f32>(x * f32(view.pixel_size) / f32(view.width), y * f32(view.pixel_size) / f32(view.height))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sample_normal(uv: vec2<f32>, x: f32, y: f32) -> vec3<f32> {
|
|
||||||
return textureSample(
|
|
||||||
normal_texture,
|
|
||||||
normal_texture_sampler,
|
|
||||||
uv + vec2<f32>(x * f32(view.pixel_size) / f32(view.width), y * f32(view.pixel_size) / f32(view.height))
|
|
||||||
).xyz;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn normal_indicator(uv: vec2<f32>, x: f32, y: f32) -> f32 {
|
|
||||||
// TODO - integer promotion to float argument
|
|
||||||
var depth_diff = sample_depth(uv, 0.0, 0.0) - sample_depth(uv, x, y);
|
|
||||||
var dx = sample_normal(uv, 0.0, 0.0);
|
|
||||||
var dy = sample_normal(uv, x, y);
|
|
||||||
if (depth_diff > 0) {
|
|
||||||
// only sample normals from closest pixel
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return distance(dx, dy);
|
|
||||||
}
|
|
||||||
|
|
||||||
@fragment fn main(
|
|
||||||
// TODO - vertex/fragment linkage
|
|
||||||
@location(0) uv: vec2<f32>,
|
|
||||||
@builtin(position) position: vec4<f32>
|
|
||||||
) -> @location(0) vec4<f32> {
|
|
||||||
// TODO - integer promotion to float argument
|
|
||||||
var depth = sample_depth(uv, 0.0, 0.0);
|
|
||||||
var depth_diff: f32 = 0;
|
|
||||||
depth_diff += abs(depth - sample_depth(uv, -1.0, 0.0));
|
|
||||||
depth_diff += abs(depth - sample_depth(uv, 1.0, 0.0));
|
|
||||||
depth_diff += abs(depth - sample_depth(uv, 0.0, -1.0));
|
|
||||||
depth_diff += abs(depth - sample_depth(uv, 0.0, 1.0));
|
|
||||||
|
|
||||||
var normal_diff: f32 = 0;
|
|
||||||
normal_diff += normal_indicator(uv, -1.0, 0.0);
|
|
||||||
normal_diff += normal_indicator(uv, 1.0, 0.0);
|
|
||||||
normal_diff += normal_indicator(uv, 0.0, -1.0);
|
|
||||||
normal_diff += normal_indicator(uv, 0.0, 1.0);
|
|
||||||
|
|
||||||
var color = textureSample(draw_texture, draw_texture_sampler, uv);
|
|
||||||
if (depth_diff > 0.007) { // magic number from testing
|
|
||||||
return color * 0.7;
|
|
||||||
}
|
|
||||||
// add instead of multiply so really dark pixels get brighter
|
|
||||||
return color + (vec4<f32>(1) * step(0.1, normal_diff) * 0.7);
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
struct VertexOut {
|
|
||||||
@builtin(position) position_clip: vec4<f32>,
|
|
||||||
@location(0) uv: vec2<f32>
|
|
||||||
}
|
|
||||||
|
|
||||||
@vertex fn main(
|
|
||||||
@location(0) position: vec3<f32>,
|
|
||||||
@location(1) uv: vec2<f32>
|
|
||||||
) -> VertexOut {
|
|
||||||
var output : VertexOut;
|
|
||||||
output.position_clip = vec4<f32>(position.xy, 0.0, 1.0);
|
|
||||||
output.uv = uv;
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
pub const Quad = extern struct {
|
|
||||||
pos: @Vector(3, f32),
|
|
||||||
uv: @Vector(2, f32),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const quad = [_]Quad{
|
|
||||||
.{ .pos = .{ -1.0, 1.0, 0.0 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1.0, -1.0, 0.0 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1.0, 1.0, 0.0 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1.0, 1.0, 0.0 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1.0, -1.0, 0.0 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1.0, -1.0, 0.0 }, .uv = .{ 1, 0 } },
|
|
||||||
};
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
@group(0) @binding(0) var<uniform> ubo: mat4x4<f32>;
|
|
||||||
|
|
||||||
struct VertexOut {
|
|
||||||
@builtin(position) position_clip: vec4<f32>,
|
|
||||||
@location(0) normal: vec3<f32>,
|
|
||||||
@location(1) uv: vec2<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
@vertex fn vertex_main(
|
|
||||||
@location(0) position: vec3<f32>,
|
|
||||||
@location(1) normal: vec3<f32>,
|
|
||||||
@location(2) uv: vec2<f32>
|
|
||||||
) -> VertexOut {
|
|
||||||
var output: VertexOut;
|
|
||||||
output.position_clip = vec4<f32>(position, 1) * ubo;
|
|
||||||
output.normal = (vec4<f32>(normal, 0) * ubo).xyz;
|
|
||||||
output.uv = uv;
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
@fragment fn frag_main(
|
|
||||||
@location(0) normal: vec3<f32>,
|
|
||||||
@location(1) uv: vec2<f32>,
|
|
||||||
) -> @location(0) vec4<f32> {
|
|
||||||
var color = floor((uv * 0.5 + 0.25) * 32) / 32;
|
|
||||||
return vec4<f32>(color, 1, 1);
|
|
||||||
}
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
const renderer = @import("renderer.zig");
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
// Use experimental sysgpu graphics API
|
|
||||||
pub const use_sysgpu = true;
|
|
||||||
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
|
|
||||||
title_timer: core.Timer,
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{ .required_limits = gpu.Limits{
|
|
||||||
.max_vertex_buffers = 1,
|
|
||||||
.max_vertex_attributes = 2,
|
|
||||||
.max_bind_groups = 1,
|
|
||||||
.max_uniform_buffers_per_shader_stage = 1,
|
|
||||||
.max_uniform_buffer_binding_size = 16 * 1 * @sizeOf(f32),
|
|
||||||
} });
|
|
||||||
|
|
||||||
const allocator = gpa.allocator();
|
|
||||||
const timer = try core.Timer.start();
|
|
||||||
try renderer.init(allocator, timer);
|
|
||||||
app.* = .{ .title_timer = try core.Timer.start() };
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
_ = app;
|
|
||||||
defer _ = gpa.deinit();
|
|
||||||
defer core.deinit();
|
|
||||||
defer renderer.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(app: *App) !bool {
|
|
||||||
var iter = core.pollEvents();
|
|
||||||
while (iter.next()) |event| {
|
|
||||||
switch (event) {
|
|
||||||
.key_press => |ev| {
|
|
||||||
if (ev.key == .space) return true;
|
|
||||||
if (ev.key == .right) {
|
|
||||||
renderer.curr_primitive_index += 1;
|
|
||||||
renderer.curr_primitive_index %= 7;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.close => return true,
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
renderer.update();
|
|
||||||
|
|
||||||
// update the window title every second
|
|
||||||
if (app.title_timer.read() >= 1.0) {
|
|
||||||
app.title_timer.reset();
|
|
||||||
try core.printTitle("Procedural Primitives [ {d}fps ] [ Input {d}hz ]", .{
|
|
||||||
core.frameRate(),
|
|
||||||
core.inputRate(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
@ -1,336 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
const zmath = @import("zmath");
|
|
||||||
|
|
||||||
const PI = 3.1415927410125732421875;
|
|
||||||
|
|
||||||
pub const F32x3 = @Vector(3, f32);
|
|
||||||
pub const F32x4 = @Vector(4, f32);
|
|
||||||
pub const VertexData = struct {
|
|
||||||
position: F32x3,
|
|
||||||
normal: F32x3,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const PrimitiveType = enum(u4) { none, triangle, quad, plane, circle, uv_sphere, ico_sphere, cylinder, cone, torus };
|
|
||||||
|
|
||||||
pub const Primitive = struct {
|
|
||||||
vertex_data: std.ArrayList(VertexData),
|
|
||||||
vertex_count: u32,
|
|
||||||
index_data: std.ArrayList(u32),
|
|
||||||
index_count: u32,
|
|
||||||
type: PrimitiveType = .none,
|
|
||||||
};
|
|
||||||
|
|
||||||
// 2D Primitives
|
|
||||||
pub fn createTrianglePrimitive(allocator: std.mem.Allocator, size: f32) !Primitive {
|
|
||||||
const vertex_count = 3;
|
|
||||||
const index_count = 3;
|
|
||||||
var vertex_data = try std.ArrayList(VertexData).initCapacity(allocator, vertex_count);
|
|
||||||
|
|
||||||
const edge = size / 2.0;
|
|
||||||
|
|
||||||
vertex_data.appendSliceAssumeCapacity(&[vertex_count]VertexData{
|
|
||||||
VertexData{ .position = F32x3{ -edge, -edge, 0.0 }, .normal = F32x3{ -edge, -edge, 0.0 } },
|
|
||||||
VertexData{ .position = F32x3{ edge, -edge, 0.0 }, .normal = F32x3{ edge, -edge, 0.0 } },
|
|
||||||
VertexData{ .position = F32x3{ 0.0, edge, 0.0 }, .normal = F32x3{ 0.0, edge, 0.0 } },
|
|
||||||
});
|
|
||||||
|
|
||||||
var index_data = try std.ArrayList(u32).initCapacity(allocator, index_count);
|
|
||||||
index_data.appendSliceAssumeCapacity(&[index_count]u32{ 0, 1, 2 });
|
|
||||||
|
|
||||||
return Primitive{ .vertex_data = vertex_data, .vertex_count = 3, .index_data = index_data, .index_count = 3, .type = .triangle };
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn createQuadPrimitive(allocator: std.mem.Allocator, size: f32) !Primitive {
|
|
||||||
const vertex_count = 4;
|
|
||||||
const index_count = 6;
|
|
||||||
var vertex_data = try std.ArrayList(VertexData).initCapacity(allocator, vertex_count);
|
|
||||||
|
|
||||||
const edge = size / 2.0;
|
|
||||||
|
|
||||||
vertex_data.appendSliceAssumeCapacity(&[vertex_count]VertexData{
|
|
||||||
VertexData{ .position = F32x3{ -edge, -edge, 0.0 }, .normal = F32x3{ -edge, -edge, 0.0 } },
|
|
||||||
VertexData{ .position = F32x3{ edge, -edge, 0.0 }, .normal = F32x3{ edge, -edge, 0.0 } },
|
|
||||||
VertexData{ .position = F32x3{ -edge, edge, 0.0 }, .normal = F32x3{ -edge, edge, 0.0 } },
|
|
||||||
VertexData{ .position = F32x3{ edge, edge, 0.0 }, .normal = F32x3{ edge, edge, 0.0 } },
|
|
||||||
});
|
|
||||||
|
|
||||||
var index_data = try std.ArrayList(u32).initCapacity(allocator, index_count);
|
|
||||||
index_data.appendSliceAssumeCapacity(&[index_count]u32{
|
|
||||||
0, 1, 2,
|
|
||||||
1, 3, 2,
|
|
||||||
});
|
|
||||||
|
|
||||||
return Primitive{ .vertex_data = vertex_data, .vertex_count = 4, .index_data = index_data, .index_count = 6, .type = .quad };
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn createPlanePrimitive(allocator: std.mem.Allocator, x_subdivision: u32, y_subdivision: u32, size: f32) !Primitive {
|
|
||||||
const x_num_vertices = x_subdivision + 1;
|
|
||||||
const y_num_vertices = y_subdivision + 1;
|
|
||||||
const vertex_count = x_num_vertices * y_num_vertices;
|
|
||||||
var vertex_data = try std.ArrayList(VertexData).initCapacity(allocator, vertex_count);
|
|
||||||
|
|
||||||
const vertices_distance_y = (size / @as(f32, @floatFromInt(y_subdivision)));
|
|
||||||
const vertices_distance_x = (size / @as(f32, @floatFromInt(x_subdivision)));
|
|
||||||
var y: u32 = 0;
|
|
||||||
while (y < y_num_vertices) : (y += 1) {
|
|
||||||
var x: u32 = 0;
|
|
||||||
const pos_y = (-size / 2.0) + @as(f32, @floatFromInt(y)) * vertices_distance_y;
|
|
||||||
while (x < x_num_vertices) : (x += 1) {
|
|
||||||
const pos_x = (-size / 2.0) + @as(f32, @floatFromInt(x)) * vertices_distance_x;
|
|
||||||
vertex_data.appendAssumeCapacity(VertexData{ .position = F32x3{ pos_x, pos_y, 0.0 }, .normal = F32x3{ pos_x, pos_y, 0.0 } });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const index_count = x_subdivision * y_subdivision * 2 * 3;
|
|
||||||
var index_data = try std.ArrayList(u32).initCapacity(allocator, index_count);
|
|
||||||
|
|
||||||
y = 0;
|
|
||||||
while (y < y_subdivision) : (y += 1) {
|
|
||||||
var x: u32 = 0;
|
|
||||||
while (x < x_subdivision) : (x += 1) {
|
|
||||||
// First Triangle of Quad
|
|
||||||
index_data.appendAssumeCapacity(x + y * y_num_vertices);
|
|
||||||
index_data.appendAssumeCapacity(x + 1 + y * y_num_vertices);
|
|
||||||
index_data.appendAssumeCapacity(x + (y + 1) * y_num_vertices);
|
|
||||||
|
|
||||||
// Second Triangle of Quad
|
|
||||||
index_data.appendAssumeCapacity(x + 1 + y * y_num_vertices);
|
|
||||||
index_data.appendAssumeCapacity(x + (y + 1) * y_num_vertices + 1);
|
|
||||||
index_data.appendAssumeCapacity(x + (y + 1) * y_num_vertices);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Primitive{ .vertex_data = vertex_data, .vertex_count = vertex_count, .index_data = index_data, .index_count = index_count, .type = .plane };
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn createCirclePrimitive(allocator: std.mem.Allocator, vertices: u32, radius: f32) !Primitive {
|
|
||||||
const vertex_count = vertices + 1;
|
|
||||||
var vertex_data = try std.ArrayList(VertexData).initCapacity(allocator, vertex_count);
|
|
||||||
|
|
||||||
// Mid point of circle
|
|
||||||
vertex_data.appendAssumeCapacity(VertexData{ .position = F32x3{ 0, 0, 0.0 }, .normal = F32x3{ 0, 0, 0.0 } });
|
|
||||||
|
|
||||||
var x: u32 = 0;
|
|
||||||
const angle = 2 * PI / @as(f32, @floatFromInt(vertices));
|
|
||||||
while (x < vertices) : (x += 1) {
|
|
||||||
const x_f = @as(f32, @floatFromInt(x));
|
|
||||||
const pos_x = radius * zmath.cos(angle * x_f);
|
|
||||||
const pos_y = radius * zmath.sin(angle * x_f);
|
|
||||||
|
|
||||||
vertex_data.appendAssumeCapacity(VertexData{ .position = F32x3{ pos_x, pos_y, 0.0 }, .normal = F32x3{ pos_x, pos_y, 0.0 } });
|
|
||||||
}
|
|
||||||
|
|
||||||
const index_count = (vertices + 1) * 3;
|
|
||||||
var index_data = try std.ArrayList(u32).initCapacity(allocator, index_count);
|
|
||||||
|
|
||||||
x = 1;
|
|
||||||
while (x <= vertices) : (x += 1) {
|
|
||||||
index_data.appendAssumeCapacity(0);
|
|
||||||
index_data.appendAssumeCapacity(x);
|
|
||||||
index_data.appendAssumeCapacity(x + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
index_data.appendAssumeCapacity(0);
|
|
||||||
index_data.appendAssumeCapacity(vertices);
|
|
||||||
index_data.appendAssumeCapacity(1);
|
|
||||||
|
|
||||||
return Primitive{ .vertex_data = vertex_data, .vertex_count = vertex_count, .index_data = index_data, .index_count = index_count, .type = .plane };
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3D Primitives
|
|
||||||
pub fn createCubePrimitive(allocator: std.mem.Allocator, size: f32) !Primitive {
|
|
||||||
const vertex_count = 8;
|
|
||||||
const index_count = 36;
|
|
||||||
var vertex_data = try std.ArrayList(VertexData).initCapacity(allocator, vertex_count);
|
|
||||||
|
|
||||||
const edge = size / 2.0;
|
|
||||||
|
|
||||||
vertex_data.appendSliceAssumeCapacity(&[vertex_count]VertexData{
|
|
||||||
// Front positions
|
|
||||||
VertexData{ .position = F32x3{ -edge, -edge, edge }, .normal = F32x3{ -edge, -edge, edge } },
|
|
||||||
VertexData{ .position = F32x3{ edge, -edge, edge }, .normal = F32x3{ edge, -edge, edge } },
|
|
||||||
VertexData{ .position = F32x3{ edge, edge, edge }, .normal = F32x3{ edge, edge, edge } },
|
|
||||||
VertexData{ .position = F32x3{ -edge, edge, edge }, .normal = F32x3{ -edge, edge, edge } },
|
|
||||||
// Back positions
|
|
||||||
VertexData{ .position = F32x3{ -edge, -edge, -edge }, .normal = F32x3{ -edge, -edge, -edge } },
|
|
||||||
VertexData{ .position = F32x3{ edge, -edge, -edge }, .normal = F32x3{ edge, -edge, -edge } },
|
|
||||||
VertexData{ .position = F32x3{ edge, edge, -edge }, .normal = F32x3{ edge, edge, -edge } },
|
|
||||||
VertexData{ .position = F32x3{ -edge, edge, -edge }, .normal = F32x3{ -edge, edge, -edge } },
|
|
||||||
});
|
|
||||||
|
|
||||||
var index_data = try std.ArrayList(u32).initCapacity(allocator, index_count);
|
|
||||||
|
|
||||||
index_data.appendSliceAssumeCapacity(&[index_count]u32{
|
|
||||||
// front quad
|
|
||||||
0, 1, 2,
|
|
||||||
2, 3, 0,
|
|
||||||
// right quad
|
|
||||||
1, 5, 6,
|
|
||||||
6, 2, 1,
|
|
||||||
// back quad
|
|
||||||
7, 6, 5,
|
|
||||||
5, 4, 7,
|
|
||||||
// left quad
|
|
||||||
4, 0, 3,
|
|
||||||
3, 7, 4,
|
|
||||||
// bottom quad
|
|
||||||
4, 5, 1,
|
|
||||||
1, 0, 4,
|
|
||||||
// top quad
|
|
||||||
3, 2, 6,
|
|
||||||
6, 7, 3,
|
|
||||||
});
|
|
||||||
|
|
||||||
return Primitive{ .vertex_data = vertex_data, .vertex_count = vertex_count, .index_data = index_data, .index_count = index_count, .type = .quad };
|
|
||||||
}
|
|
||||||
|
|
||||||
const VertexDataMAL = std.MultiArrayList(VertexData);
|
|
||||||
|
|
||||||
pub fn createCylinderPrimitive(allocator: std.mem.Allocator, radius: f32, height: f32, num_sides: u32) !Primitive {
|
|
||||||
const alloc_amt_vert: u32 = num_sides * 2 + 2;
|
|
||||||
const alloc_amt_idx: u32 = num_sides * 12;
|
|
||||||
|
|
||||||
var vertex_data = VertexDataMAL{};
|
|
||||||
try vertex_data.ensureTotalCapacity(allocator, alloc_amt_vert);
|
|
||||||
defer vertex_data.deinit(allocator);
|
|
||||||
|
|
||||||
var out_vertex_data = try std.ArrayList(VertexData).initCapacity(allocator, alloc_amt_vert);
|
|
||||||
var index_data = try std.ArrayList(u32).initCapacity(allocator, alloc_amt_idx);
|
|
||||||
|
|
||||||
vertex_data.appendAssumeCapacity(VertexData{ .position = F32x3{ 0.0, (height / 2.0), 0.0 }, .normal = undefined });
|
|
||||||
vertex_data.appendAssumeCapacity(VertexData{ .position = F32x3{ 0.0, -(height / 2.0), 0.0 }, .normal = undefined });
|
|
||||||
|
|
||||||
const angle = 2.0 * PI / @as(f32, @floatFromInt(num_sides));
|
|
||||||
|
|
||||||
for (1..num_sides + 1) |i| {
|
|
||||||
const float_i = @as(f32, @floatFromInt(i));
|
|
||||||
|
|
||||||
const x: f32 = radius * zmath.sin(angle * float_i);
|
|
||||||
const y: f32 = radius * zmath.cos(angle * float_i);
|
|
||||||
|
|
||||||
vertex_data.appendAssumeCapacity(VertexData{ .position = F32x3{ x, (height / 2.0), y }, .normal = undefined });
|
|
||||||
vertex_data.appendAssumeCapacity(VertexData{ .position = F32x3{ x, -(height / 2.0), y }, .normal = undefined });
|
|
||||||
}
|
|
||||||
|
|
||||||
var group1: u32 = 1;
|
|
||||||
var group2: u32 = 3;
|
|
||||||
|
|
||||||
for (0..num_sides) |_| {
|
|
||||||
if (group2 >= num_sides * 2) group2 = 1;
|
|
||||||
index_data.appendSliceAssumeCapacity(&[_]u32{
|
|
||||||
0, group1 + 1, group2 + 1,
|
|
||||||
group1 + 1, group1 + 2, group2 + 1,
|
|
||||||
group1 + 2, group2 + 2, group2 + 1,
|
|
||||||
group2 + 2, group1 + 2, 1,
|
|
||||||
});
|
|
||||||
group1 += 2;
|
|
||||||
group2 += 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var i: u32 = 0;
|
|
||||||
while (i < alloc_amt_idx) : (i += 3) {
|
|
||||||
const indexA: u32 = index_data.items[i];
|
|
||||||
const indexB: u32 = index_data.items[i + 1];
|
|
||||||
const indexC: u32 = index_data.items[i + 2];
|
|
||||||
|
|
||||||
const vert1: F32x4 = F32x4{ vertex_data.get(indexA).position[0], vertex_data.get(indexA).position[1], vertex_data.get(indexA).position[2], 1.0 };
|
|
||||||
const vert2: F32x4 = F32x4{ vertex_data.get(indexB).position[0], vertex_data.get(indexB).position[1], vertex_data.get(indexB).position[2], 1.0 };
|
|
||||||
const vert3: F32x4 = F32x4{ vertex_data.get(indexC).position[0], vertex_data.get(indexC).position[1], vertex_data.get(indexC).position[2], 1.0 };
|
|
||||||
|
|
||||||
const edgeAB: F32x4 = vert2 - vert1;
|
|
||||||
const edgeAC: F32x4 = vert3 - vert1;
|
|
||||||
|
|
||||||
const cross = zmath.cross3(edgeAB, edgeAC);
|
|
||||||
|
|
||||||
vertex_data.items(.normal)[indexA][0] += cross[0];
|
|
||||||
vertex_data.items(.normal)[indexA][1] += cross[1];
|
|
||||||
vertex_data.items(.normal)[indexA][2] += cross[2];
|
|
||||||
vertex_data.items(.normal)[indexB][0] += cross[0];
|
|
||||||
vertex_data.items(.normal)[indexB][1] += cross[1];
|
|
||||||
vertex_data.items(.normal)[indexB][2] += cross[2];
|
|
||||||
vertex_data.items(.normal)[indexC][0] += cross[0];
|
|
||||||
vertex_data.items(.normal)[indexC][1] += cross[1];
|
|
||||||
vertex_data.items(.normal)[indexC][2] += cross[2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (vertex_data.items(.position), vertex_data.items(.normal)) |pos, nor| {
|
|
||||||
out_vertex_data.appendAssumeCapacity(VertexData{ .position = pos, .normal = nor });
|
|
||||||
}
|
|
||||||
|
|
||||||
return Primitive{ .vertex_data = out_vertex_data, .vertex_count = alloc_amt_vert, .index_data = index_data, .index_count = alloc_amt_idx, .type = .cylinder };
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn createConePrimitive(allocator: std.mem.Allocator, radius: f32, height: f32, num_sides: u32) !Primitive {
|
|
||||||
const alloc_amt_vert: u32 = num_sides + 2;
|
|
||||||
const alloc_amt_idx: u32 = num_sides * 6;
|
|
||||||
|
|
||||||
var vertex_data = VertexDataMAL{};
|
|
||||||
try vertex_data.ensureTotalCapacity(allocator, alloc_amt_vert);
|
|
||||||
defer vertex_data.deinit(allocator);
|
|
||||||
|
|
||||||
var out_vertex_data = try std.ArrayList(VertexData).initCapacity(allocator, alloc_amt_vert);
|
|
||||||
var index_data = try std.ArrayList(u32).initCapacity(allocator, alloc_amt_idx);
|
|
||||||
|
|
||||||
vertex_data.appendAssumeCapacity(VertexData{ .position = F32x3{ 0.0, (height / 2.0), 0.0 }, .normal = undefined });
|
|
||||||
vertex_data.appendAssumeCapacity(VertexData{ .position = F32x3{ 0.0, -(height / 2.0), 0.0 }, .normal = undefined });
|
|
||||||
|
|
||||||
const angle = 2.0 * PI / @as(f32, @floatFromInt(num_sides));
|
|
||||||
|
|
||||||
for (1..num_sides + 1) |i| {
|
|
||||||
const float_i = @as(f32, @floatFromInt(i));
|
|
||||||
|
|
||||||
const x: f32 = radius * zmath.sin(angle * float_i);
|
|
||||||
const y: f32 = radius * zmath.cos(angle * float_i);
|
|
||||||
|
|
||||||
vertex_data.appendAssumeCapacity(VertexData{ .position = F32x3{ x, -(height / 2.0), y }, .normal = undefined });
|
|
||||||
}
|
|
||||||
|
|
||||||
var group1: u32 = 1;
|
|
||||||
var group2: u32 = 2;
|
|
||||||
|
|
||||||
for (0..num_sides) |_| {
|
|
||||||
if (group2 >= num_sides + 1) group2 = 1;
|
|
||||||
index_data.appendSliceAssumeCapacity(&[_]u32{
|
|
||||||
0, group1 + 1, group2 + 1,
|
|
||||||
group2 + 1, group1 + 1, 1,
|
|
||||||
});
|
|
||||||
group1 += 1;
|
|
||||||
group2 += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
var i: u32 = 0;
|
|
||||||
while (i < alloc_amt_idx) : (i += 3) {
|
|
||||||
const indexA: u32 = index_data.items[i];
|
|
||||||
const indexB: u32 = index_data.items[i + 1];
|
|
||||||
const indexC: u32 = index_data.items[i + 2];
|
|
||||||
|
|
||||||
const vert1: F32x4 = F32x4{ vertex_data.get(indexA).position[0], vertex_data.get(indexA).position[1], vertex_data.get(indexA).position[2], 1.0 };
|
|
||||||
const vert2: F32x4 = F32x4{ vertex_data.get(indexB).position[0], vertex_data.get(indexB).position[1], vertex_data.get(indexB).position[2], 1.0 };
|
|
||||||
const vert3: F32x4 = F32x4{ vertex_data.get(indexC).position[0], vertex_data.get(indexC).position[1], vertex_data.get(indexC).position[2], 1.0 };
|
|
||||||
|
|
||||||
const edgeAB: F32x4 = vert2 - vert1;
|
|
||||||
const edgeAC: F32x4 = vert3 - vert1;
|
|
||||||
|
|
||||||
const cross = zmath.cross3(edgeAB, edgeAC);
|
|
||||||
|
|
||||||
vertex_data.items(.normal)[indexA][0] += cross[0];
|
|
||||||
vertex_data.items(.normal)[indexA][1] += cross[1];
|
|
||||||
vertex_data.items(.normal)[indexA][2] += cross[2];
|
|
||||||
vertex_data.items(.normal)[indexB][0] += cross[0];
|
|
||||||
vertex_data.items(.normal)[indexB][1] += cross[1];
|
|
||||||
vertex_data.items(.normal)[indexB][2] += cross[2];
|
|
||||||
vertex_data.items(.normal)[indexC][0] += cross[0];
|
|
||||||
vertex_data.items(.normal)[indexC][1] += cross[1];
|
|
||||||
vertex_data.items(.normal)[indexC][2] += cross[2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (vertex_data.items(.position), vertex_data.items(.normal)) |pos, nor| {
|
|
||||||
out_vertex_data.appendAssumeCapacity(VertexData{ .position = pos, .normal = nor });
|
|
||||||
}
|
|
||||||
|
|
||||||
return Primitive{ .vertex_data = out_vertex_data, .vertex_count = alloc_amt_vert, .index_data = index_data, .index_count = alloc_amt_idx, .type = .cone };
|
|
||||||
}
|
|
||||||
|
|
@ -1,328 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
const zm = @import("zmath");
|
|
||||||
const primitives = @import("procedural-primitives.zig");
|
|
||||||
const Primitive = primitives.Primitive;
|
|
||||||
const VertexData = primitives.VertexData;
|
|
||||||
|
|
||||||
pub const Renderer = @This();
|
|
||||||
|
|
||||||
var queue: *gpu.Queue = undefined;
|
|
||||||
var pipeline: *gpu.RenderPipeline = undefined;
|
|
||||||
var app_timer: core.Timer = undefined;
|
|
||||||
var depth_texture: *gpu.Texture = undefined;
|
|
||||||
var depth_texture_view: *gpu.TextureView = undefined;
|
|
||||||
|
|
||||||
const PrimitiveRenderData = struct {
|
|
||||||
vertex_buffer: *gpu.Buffer,
|
|
||||||
index_buffer: *gpu.Buffer,
|
|
||||||
vertex_count: u32,
|
|
||||||
index_count: u32,
|
|
||||||
};
|
|
||||||
|
|
||||||
const UniformBufferObject = struct {
|
|
||||||
mvp_matrix: zm.Mat,
|
|
||||||
};
|
|
||||||
var uniform_buffer: *gpu.Buffer = undefined;
|
|
||||||
var bind_group: *gpu.BindGroup = undefined;
|
|
||||||
|
|
||||||
var primitives_data: [7]PrimitiveRenderData = undefined;
|
|
||||||
|
|
||||||
pub var curr_primitive_index: u4 = 0;
|
|
||||||
|
|
||||||
pub fn init(allocator: std.mem.Allocator, timer: core.Timer) !void {
|
|
||||||
queue = core.queue;
|
|
||||||
app_timer = timer;
|
|
||||||
|
|
||||||
{
|
|
||||||
const triangle_primitive = try primitives.createTrianglePrimitive(allocator, 1);
|
|
||||||
primitives_data[0] = PrimitiveRenderData{ .vertex_buffer = createVertexBuffer(triangle_primitive), .index_buffer = createIndexBuffer(triangle_primitive), .vertex_count = triangle_primitive.vertex_count, .index_count = triangle_primitive.index_count };
|
|
||||||
defer triangle_primitive.vertex_data.deinit();
|
|
||||||
defer triangle_primitive.index_data.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const quad_primitive = try primitives.createQuadPrimitive(allocator, 1.4);
|
|
||||||
primitives_data[1] = PrimitiveRenderData{ .vertex_buffer = createVertexBuffer(quad_primitive), .index_buffer = createIndexBuffer(quad_primitive), .vertex_count = quad_primitive.vertex_count, .index_count = quad_primitive.index_count };
|
|
||||||
defer quad_primitive.vertex_data.deinit();
|
|
||||||
defer quad_primitive.index_data.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const plane_primitive = try primitives.createPlanePrimitive(allocator, 1000, 1000, 1.5);
|
|
||||||
primitives_data[2] = PrimitiveRenderData{ .vertex_buffer = createVertexBuffer(plane_primitive), .index_buffer = createIndexBuffer(plane_primitive), .vertex_count = plane_primitive.vertex_count, .index_count = plane_primitive.index_count };
|
|
||||||
defer plane_primitive.vertex_data.deinit();
|
|
||||||
defer plane_primitive.index_data.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const circle_primitive = try primitives.createCirclePrimitive(allocator, 64, 1);
|
|
||||||
primitives_data[3] = PrimitiveRenderData{ .vertex_buffer = createVertexBuffer(circle_primitive), .index_buffer = createIndexBuffer(circle_primitive), .vertex_count = circle_primitive.vertex_count, .index_count = circle_primitive.index_count };
|
|
||||||
defer circle_primitive.vertex_data.deinit();
|
|
||||||
defer circle_primitive.index_data.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const cube_primitive = try primitives.createCubePrimitive(allocator, 0.5);
|
|
||||||
primitives_data[4] = PrimitiveRenderData{ .vertex_buffer = createVertexBuffer(cube_primitive), .index_buffer = createIndexBuffer(cube_primitive), .vertex_count = cube_primitive.vertex_count, .index_count = cube_primitive.index_count };
|
|
||||||
defer cube_primitive.vertex_data.deinit();
|
|
||||||
defer cube_primitive.index_data.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const cylinder_primitive = try primitives.createCylinderPrimitive(allocator, 1.0, 1.0, 6);
|
|
||||||
primitives_data[5] = PrimitiveRenderData{ .vertex_buffer = createVertexBuffer(cylinder_primitive), .index_buffer = createIndexBuffer(cylinder_primitive), .vertex_count = cylinder_primitive.vertex_count, .index_count = cylinder_primitive.index_count };
|
|
||||||
defer cylinder_primitive.vertex_data.deinit();
|
|
||||||
defer cylinder_primitive.index_data.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const cone_primitive = try primitives.createConePrimitive(allocator, 0.7, 1.0, 15);
|
|
||||||
primitives_data[6] = PrimitiveRenderData{ .vertex_buffer = createVertexBuffer(cone_primitive), .index_buffer = createIndexBuffer(cone_primitive), .vertex_count = cone_primitive.vertex_count, .index_count = cone_primitive.index_count };
|
|
||||||
defer cone_primitive.vertex_data.deinit();
|
|
||||||
defer cone_primitive.index_data.deinit();
|
|
||||||
}
|
|
||||||
var bind_group_layout = createBindGroupLayout();
|
|
||||||
defer bind_group_layout.release();
|
|
||||||
|
|
||||||
createBindBuffer(bind_group_layout);
|
|
||||||
|
|
||||||
createDepthTexture();
|
|
||||||
|
|
||||||
var shader = core.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.wgsl"));
|
|
||||||
defer shader.release();
|
|
||||||
|
|
||||||
pipeline = createPipeline(shader, bind_group_layout);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn createVertexBuffer(primitive: Primitive) *gpu.Buffer {
|
|
||||||
const vertex_buffer_descriptor = gpu.Buffer.Descriptor{
|
|
||||||
.size = primitive.vertex_count * @sizeOf(VertexData),
|
|
||||||
.usage = .{ .vertex = true, .copy_dst = true },
|
|
||||||
.mapped_at_creation = .false,
|
|
||||||
};
|
|
||||||
|
|
||||||
const vertex_buffer = core.device.createBuffer(&vertex_buffer_descriptor);
|
|
||||||
queue.writeBuffer(vertex_buffer, 0, primitive.vertex_data.items[0..]);
|
|
||||||
|
|
||||||
return vertex_buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn createIndexBuffer(primitive: Primitive) *gpu.Buffer {
|
|
||||||
const index_buffer_descriptor = gpu.Buffer.Descriptor{
|
|
||||||
.size = primitive.index_count * @sizeOf(u32),
|
|
||||||
.usage = .{ .index = true, .copy_dst = true },
|
|
||||||
.mapped_at_creation = .false,
|
|
||||||
};
|
|
||||||
const index_buffer = core.device.createBuffer(&index_buffer_descriptor);
|
|
||||||
queue.writeBuffer(index_buffer, 0, primitive.index_data.items[0..]);
|
|
||||||
|
|
||||||
return index_buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn createBindGroupLayout() *gpu.BindGroupLayout {
|
|
||||||
const bgle = gpu.BindGroupLayout.Entry.buffer(0, .{ .vertex = true, .fragment = false }, .uniform, true, 0);
|
|
||||||
return core.device.createBindGroupLayout(
|
|
||||||
&gpu.BindGroupLayout.Descriptor.init(.{
|
|
||||||
.entries = &.{bgle},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn createBindBuffer(bind_group_layout: *gpu.BindGroupLayout) void {
|
|
||||||
uniform_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .copy_dst = true, .uniform = true },
|
|
||||||
.size = @sizeOf(UniformBufferObject),
|
|
||||||
.mapped_at_creation = .false,
|
|
||||||
});
|
|
||||||
|
|
||||||
bind_group = core.device.createBindGroup(
|
|
||||||
&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = bind_group_layout,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.buffer(0, uniform_buffer, 0, @sizeOf(UniformBufferObject), @sizeOf(UniformBufferObject)),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn createDepthTexture() void {
|
|
||||||
depth_texture = core.device.createTexture(&gpu.Texture.Descriptor{
|
|
||||||
.usage = .{ .render_attachment = true },
|
|
||||||
.size = .{ .width = core.descriptor.width, .height = core.descriptor.height },
|
|
||||||
.format = .depth24_plus,
|
|
||||||
});
|
|
||||||
|
|
||||||
depth_texture_view = depth_texture.createView(&gpu.TextureView.Descriptor{
|
|
||||||
.format = .depth24_plus,
|
|
||||||
.dimension = .dimension_2d,
|
|
||||||
.array_layer_count = 1,
|
|
||||||
.mip_level_count = 1,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn createPipeline(shader_module: *gpu.ShaderModule, bind_group_layout: *gpu.BindGroupLayout) *gpu.RenderPipeline {
|
|
||||||
const vertex_attributes = [_]gpu.VertexAttribute{
|
|
||||||
.{ .format = .float32x3, .shader_location = 0, .offset = 0 },
|
|
||||||
.{ .format = .float32x3, .shader_location = 1, .offset = @sizeOf(primitives.F32x3) },
|
|
||||||
};
|
|
||||||
|
|
||||||
const vertex_buffer_layout = gpu.VertexBufferLayout.init(.{
|
|
||||||
.array_stride = @sizeOf(VertexData),
|
|
||||||
.step_mode = .vertex,
|
|
||||||
.attributes = &vertex_attributes,
|
|
||||||
});
|
|
||||||
|
|
||||||
const vertex_pipeline_state = gpu.VertexState.init(.{ .module = shader_module, .entry_point = "vertex_main", .buffers = &.{vertex_buffer_layout} });
|
|
||||||
|
|
||||||
const primitive_pipeline_state = gpu.PrimitiveState{
|
|
||||||
.topology = .triangle_list,
|
|
||||||
.front_face = .ccw,
|
|
||||||
.cull_mode = .back,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Fragment Pipeline State
|
|
||||||
const blend = gpu.BlendState{
|
|
||||||
.color = gpu.BlendComponent{ .operation = .add, .src_factor = .src_alpha, .dst_factor = .one_minus_src_alpha },
|
|
||||||
.alpha = gpu.BlendComponent{ .operation = .add, .src_factor = .zero, .dst_factor = .one },
|
|
||||||
};
|
|
||||||
const color_target = gpu.ColorTargetState{
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
.blend = &blend,
|
|
||||||
.write_mask = gpu.ColorWriteMaskFlags.all,
|
|
||||||
};
|
|
||||||
const fragment_pipeline_state = gpu.FragmentState.init(.{
|
|
||||||
.module = shader_module,
|
|
||||||
.entry_point = "frag_main",
|
|
||||||
.targets = &.{color_target},
|
|
||||||
});
|
|
||||||
|
|
||||||
const depth_stencil_state = gpu.DepthStencilState{
|
|
||||||
.format = .depth24_plus,
|
|
||||||
.depth_write_enabled = .true,
|
|
||||||
.depth_compare = .less,
|
|
||||||
};
|
|
||||||
|
|
||||||
const multi_sample_state = gpu.MultisampleState{
|
|
||||||
.count = 1,
|
|
||||||
.mask = 0xFFFFFFFF,
|
|
||||||
.alpha_to_coverage_enabled = .false,
|
|
||||||
};
|
|
||||||
|
|
||||||
const bind_group_layouts = [_]*gpu.BindGroupLayout{bind_group_layout};
|
|
||||||
// Pipeline Layout
|
|
||||||
const pipeline_layout_descriptor = gpu.PipelineLayout.Descriptor.init(.{
|
|
||||||
.bind_group_layouts = &bind_group_layouts,
|
|
||||||
});
|
|
||||||
const pipeline_layout = core.device.createPipelineLayout(&pipeline_layout_descriptor);
|
|
||||||
defer pipeline_layout.release();
|
|
||||||
|
|
||||||
const pipeline_descriptor = gpu.RenderPipeline.Descriptor{
|
|
||||||
.label = "Main Pipeline",
|
|
||||||
.layout = pipeline_layout,
|
|
||||||
.vertex = vertex_pipeline_state,
|
|
||||||
.primitive = primitive_pipeline_state,
|
|
||||||
.depth_stencil = &depth_stencil_state,
|
|
||||||
.multisample = multi_sample_state,
|
|
||||||
.fragment = &fragment_pipeline_state,
|
|
||||||
};
|
|
||||||
|
|
||||||
return core.device.createRenderPipeline(&pipeline_descriptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const F32x1 = @Vector(1, f32);
|
|
||||||
|
|
||||||
pub fn update() void {
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = back_buffer_view,
|
|
||||||
.clear_value = gpu.Color{ .r = 0.2, .g = 0.2, .b = 0.2, .a = 1.0 },
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
|
|
||||||
const depth_stencil_attachment = gpu.RenderPassDepthStencilAttachment{
|
|
||||||
.view = depth_texture_view,
|
|
||||||
.depth_load_op = .clear,
|
|
||||||
.depth_store_op = .store,
|
|
||||||
.depth_clear_value = 1.0,
|
|
||||||
};
|
|
||||||
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
const render_pass_info = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{color_attachment},
|
|
||||||
.depth_stencil_attachment = &depth_stencil_attachment,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (curr_primitive_index >= 4) {
|
|
||||||
const time = app_timer.read() / 5;
|
|
||||||
const model = zm.mul(zm.rotationX(time * (std.math.pi / 2.0)), zm.rotationZ(time * (std.math.pi / 2.0)));
|
|
||||||
const view = zm.lookAtRh(
|
|
||||||
zm.Vec{ 0, 4, 2, 1 },
|
|
||||||
zm.Vec{ 0, 0, 0, 1 },
|
|
||||||
zm.Vec{ 0, 0, 1, 0 },
|
|
||||||
);
|
|
||||||
const proj = zm.perspectiveFovRh(
|
|
||||||
(std.math.pi / 4.0),
|
|
||||||
@as(f32, @floatFromInt(core.descriptor.width)) / @as(f32, @floatFromInt(core.descriptor.height)),
|
|
||||||
0.1,
|
|
||||||
10,
|
|
||||||
);
|
|
||||||
|
|
||||||
const mvp = zm.mul(zm.mul(model, view), proj);
|
|
||||||
|
|
||||||
const ubo = UniformBufferObject{
|
|
||||||
.mvp_matrix = zm.transpose(mvp),
|
|
||||||
};
|
|
||||||
encoder.writeBuffer(uniform_buffer, 0, &[_]UniformBufferObject{ubo});
|
|
||||||
} else {
|
|
||||||
const ubo = UniformBufferObject{
|
|
||||||
.mvp_matrix = zm.identity(),
|
|
||||||
};
|
|
||||||
encoder.writeBuffer(uniform_buffer, 0, &[_]UniformBufferObject{ubo});
|
|
||||||
}
|
|
||||||
|
|
||||||
const pass = encoder.beginRenderPass(&render_pass_info);
|
|
||||||
|
|
||||||
pass.setPipeline(pipeline);
|
|
||||||
|
|
||||||
const vertex_buffer = primitives_data[curr_primitive_index].vertex_buffer;
|
|
||||||
const vertex_count = primitives_data[curr_primitive_index].vertex_count;
|
|
||||||
pass.setVertexBuffer(0, vertex_buffer, 0, @sizeOf(VertexData) * vertex_count);
|
|
||||||
|
|
||||||
pass.setBindGroup(0, bind_group, &.{0});
|
|
||||||
|
|
||||||
const index_buffer = primitives_data[curr_primitive_index].index_buffer;
|
|
||||||
const index_count = primitives_data[curr_primitive_index].index_count;
|
|
||||||
pass.setIndexBuffer(index_buffer, .uint32, 0, @sizeOf(u32) * index_count);
|
|
||||||
pass.drawIndexed(index_count, 1, 0, 0, 0);
|
|
||||||
|
|
||||||
pass.end();
|
|
||||||
pass.release();
|
|
||||||
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
encoder.release();
|
|
||||||
|
|
||||||
queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
command.release();
|
|
||||||
core.swap_chain.present();
|
|
||||||
back_buffer_view.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit() void {
|
|
||||||
var i: u4 = 0;
|
|
||||||
while (i < 7) : (i += 1) {
|
|
||||||
primitives_data[i].vertex_buffer.release();
|
|
||||||
primitives_data[i].index_buffer.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
bind_group.release();
|
|
||||||
uniform_buffer.release();
|
|
||||||
depth_texture.release();
|
|
||||||
depth_texture_view.release();
|
|
||||||
pipeline.release();
|
|
||||||
}
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
struct Uniforms {
|
|
||||||
mvp_matrix : mat4x4<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
@binding(0) @group(0) var<uniform> ubo : Uniforms;
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) position: vec4<f32>,
|
|
||||||
@location(0) normal: vec3<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
@vertex fn vertex_main(
|
|
||||||
// TODO - struct input
|
|
||||||
@location(0) position: vec3<f32>,
|
|
||||||
@location(1) normal: vec3<f32>,
|
|
||||||
) -> VertexOutput {
|
|
||||||
var out: VertexOutput;
|
|
||||||
out.position = vec4<f32>(position, 1.0) * ubo.mvp_matrix;
|
|
||||||
out.normal = normal;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FragmentOutput {
|
|
||||||
@location(0) pixel_color: vec4<f32>
|
|
||||||
};
|
|
||||||
|
|
||||||
@fragment fn frag_main(in: VertexOutput) -> FragmentOutput {
|
|
||||||
var out : FragmentOutput;
|
|
||||||
|
|
||||||
out.pixel_color = vec4<f32>((in.normal + 1) / 2, 1.0);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
@ -1,143 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
const Vertex = extern struct {
|
|
||||||
pos: @Vector(2, f32),
|
|
||||||
col: @Vector(3, f32),
|
|
||||||
};
|
|
||||||
const vertices = [_]Vertex{
|
|
||||||
.{ .pos = .{ -0.5, -0.5 }, .col = .{ 1, 0, 0 } },
|
|
||||||
.{ .pos = .{ 0.5, -0.5 }, .col = .{ 0, 1, 0 } },
|
|
||||||
.{ .pos = .{ 0.5, 0.5 }, .col = .{ 0, 0, 1 } },
|
|
||||||
.{ .pos = .{ -0.5, 0.5 }, .col = .{ 1, 1, 1 } },
|
|
||||||
};
|
|
||||||
const index_data = [_]u32{ 0, 1, 2, 2, 3, 0 };
|
|
||||||
|
|
||||||
// Use experimental sysgpu graphics API
|
|
||||||
pub const use_sysgpu = true;
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
|
|
||||||
title_timer: core.Timer,
|
|
||||||
pipeline: *gpu.RenderPipeline,
|
|
||||||
vertex_buffer: *gpu.Buffer,
|
|
||||||
index_buffer: *gpu.Buffer,
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{});
|
|
||||||
|
|
||||||
const shader_module = core.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.wgsl"));
|
|
||||||
defer shader_module.release();
|
|
||||||
|
|
||||||
const vertex_attributes = [_]gpu.VertexAttribute{
|
|
||||||
.{ .format = .float32x2, .offset = @offsetOf(Vertex, "pos"), .shader_location = 0 },
|
|
||||||
.{ .format = .float32x3, .offset = @offsetOf(Vertex, "col"), .shader_location = 1 },
|
|
||||||
};
|
|
||||||
const vertex_buffer_layout = gpu.VertexBufferLayout.init(.{
|
|
||||||
.array_stride = @sizeOf(Vertex),
|
|
||||||
.step_mode = .vertex,
|
|
||||||
.attributes = &vertex_attributes,
|
|
||||||
});
|
|
||||||
|
|
||||||
const color_target = gpu.ColorTargetState{
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
.blend = &.{},
|
|
||||||
.write_mask = gpu.ColorWriteMaskFlags.all,
|
|
||||||
};
|
|
||||||
const fragment = gpu.FragmentState.init(.{
|
|
||||||
.module = shader_module,
|
|
||||||
.entry_point = "frag_main",
|
|
||||||
.targets = &.{color_target},
|
|
||||||
});
|
|
||||||
|
|
||||||
const pipeline_layout = core.device.createPipelineLayout(&gpu.PipelineLayout.Descriptor.init(.{}));
|
|
||||||
defer pipeline_layout.release();
|
|
||||||
|
|
||||||
const pipeline_descriptor = gpu.RenderPipeline.Descriptor{
|
|
||||||
.fragment = &fragment,
|
|
||||||
.layout = pipeline_layout,
|
|
||||||
.vertex = gpu.VertexState.init(.{
|
|
||||||
.module = shader_module,
|
|
||||||
.entry_point = "vertex_main",
|
|
||||||
.buffers = &.{vertex_buffer_layout},
|
|
||||||
}),
|
|
||||||
.primitive = .{ .cull_mode = .back },
|
|
||||||
};
|
|
||||||
|
|
||||||
const vertex_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .vertex = true },
|
|
||||||
.size = @sizeOf(Vertex) * vertices.len,
|
|
||||||
.mapped_at_creation = .true,
|
|
||||||
});
|
|
||||||
const vertex_mapped = vertex_buffer.getMappedRange(Vertex, 0, vertices.len);
|
|
||||||
@memcpy(vertex_mapped.?, vertices[0..]);
|
|
||||||
vertex_buffer.unmap();
|
|
||||||
|
|
||||||
const index_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .index = true },
|
|
||||||
.size = @sizeOf(u32) * index_data.len,
|
|
||||||
.mapped_at_creation = .true,
|
|
||||||
});
|
|
||||||
const index_mapped = index_buffer.getMappedRange(u32, 0, index_data.len);
|
|
||||||
@memcpy(index_mapped.?, index_data[0..]);
|
|
||||||
index_buffer.unmap();
|
|
||||||
|
|
||||||
app.title_timer = try core.Timer.start();
|
|
||||||
app.pipeline = core.device.createRenderPipeline(&pipeline_descriptor);
|
|
||||||
app.vertex_buffer = vertex_buffer;
|
|
||||||
app.index_buffer = index_buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
app.vertex_buffer.release();
|
|
||||||
app.index_buffer.release();
|
|
||||||
app.pipeline.release();
|
|
||||||
core.deinit();
|
|
||||||
_ = gpa.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(app: *App) !bool {
|
|
||||||
var iter = core.pollEvents();
|
|
||||||
while (iter.next()) |event| if (event == .close) return true;
|
|
||||||
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = back_buffer_view,
|
|
||||||
.clear_value = .{ .r = 0, .g = 0, .b = 0, .a = 1 },
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
const render_pass_info = gpu.RenderPassDescriptor.init(.{ .color_attachments = &.{color_attachment} });
|
|
||||||
|
|
||||||
const pass = encoder.beginRenderPass(&render_pass_info);
|
|
||||||
pass.setPipeline(app.pipeline);
|
|
||||||
pass.setVertexBuffer(0, app.vertex_buffer, 0, @sizeOf(Vertex) * vertices.len);
|
|
||||||
pass.setIndexBuffer(app.index_buffer, .uint32, 0, @sizeOf(u32) * index_data.len);
|
|
||||||
pass.drawIndexed(index_data.len, 1, 0, 0, 0);
|
|
||||||
pass.end();
|
|
||||||
pass.release();
|
|
||||||
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
encoder.release();
|
|
||||||
core.queue.submit(&.{command});
|
|
||||||
command.release();
|
|
||||||
core.swap_chain.present();
|
|
||||||
back_buffer_view.release();
|
|
||||||
|
|
||||||
// update the window title every second
|
|
||||||
if (app.title_timer.read() >= 1.0) {
|
|
||||||
app.title_timer.reset();
|
|
||||||
try core.printTitle("RGB Quad [ {d}fps ] [ Input {d}hz ]", .{
|
|
||||||
core.frameRate(),
|
|
||||||
core.inputRate(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
struct Output {
|
|
||||||
@builtin(position) pos: vec4<f32>,
|
|
||||||
@location(0) color: vec3<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
@vertex fn vertex_main(@location(0) pos: vec2<f32>, @location(1) color: vec3<f32>) -> Output {
|
|
||||||
var output: Output;
|
|
||||||
output.pos = vec4(pos, 0, 1);
|
|
||||||
output.color = color;
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
@fragment fn frag_main(@location(0) color: vec3<f32>) -> @location(0) vec4<f32> {
|
|
||||||
return vec4(color, 1);
|
|
||||||
}
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
pub const Vertex = extern struct {
|
|
||||||
pos: @Vector(4, f32),
|
|
||||||
col: @Vector(4, f32),
|
|
||||||
uv: @Vector(2, f32),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const vertices = [_]Vertex{
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
};
|
|
||||||
|
|
@ -1,198 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
const zm = @import("zmath");
|
|
||||||
const Vertex = @import("cube_mesh.zig").Vertex;
|
|
||||||
const vertices = @import("cube_mesh.zig").vertices;
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
// Use experimental sysgpu graphics API
|
|
||||||
pub const use_sysgpu = true;
|
|
||||||
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
const UniformBufferObject = struct {
|
|
||||||
mat: zm.Mat,
|
|
||||||
};
|
|
||||||
|
|
||||||
title_timer: core.Timer,
|
|
||||||
timer: core.Timer,
|
|
||||||
pipeline: *gpu.RenderPipeline,
|
|
||||||
vertex_buffer: *gpu.Buffer,
|
|
||||||
uniform_buffer: *gpu.Buffer,
|
|
||||||
bind_group: *gpu.BindGroup,
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{});
|
|
||||||
|
|
||||||
const shader_module = core.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.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.init(.{
|
|
||||||
.array_stride = @sizeOf(Vertex),
|
|
||||||
.step_mode = .vertex,
|
|
||||||
.attributes = &vertex_attributes,
|
|
||||||
});
|
|
||||||
|
|
||||||
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 bgle = gpu.BindGroupLayout.Entry.buffer(0, .{ .vertex = true }, .uniform, true, 0);
|
|
||||||
const bgl = core.device.createBindGroupLayout(
|
|
||||||
&gpu.BindGroupLayout.Descriptor.init(.{
|
|
||||||
.entries = &.{bgle},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
const bind_group_layouts = [_]*gpu.BindGroupLayout{bgl};
|
|
||||||
const pipeline_layout = core.device.createPipelineLayout(&gpu.PipelineLayout.Descriptor.init(.{
|
|
||||||
.bind_group_layouts = &bind_group_layouts,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const pipeline_descriptor = gpu.RenderPipeline.Descriptor{
|
|
||||||
.fragment = &fragment,
|
|
||||||
.layout = pipeline_layout,
|
|
||||||
.vertex = gpu.VertexState.init(.{
|
|
||||||
.module = shader_module,
|
|
||||||
.entry_point = "vertex_main",
|
|
||||||
.buffers = &.{vertex_buffer_layout},
|
|
||||||
}),
|
|
||||||
.primitive = .{
|
|
||||||
.cull_mode = .back,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const vertex_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .vertex = true },
|
|
||||||
.size = @sizeOf(Vertex) * vertices.len,
|
|
||||||
.mapped_at_creation = .true,
|
|
||||||
});
|
|
||||||
const vertex_mapped = vertex_buffer.getMappedRange(Vertex, 0, vertices.len);
|
|
||||||
@memcpy(vertex_mapped.?, vertices[0..]);
|
|
||||||
vertex_buffer.unmap();
|
|
||||||
|
|
||||||
const uniform_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .copy_dst = true, .uniform = true },
|
|
||||||
.size = @sizeOf(UniformBufferObject),
|
|
||||||
.mapped_at_creation = .false,
|
|
||||||
});
|
|
||||||
const bind_group = core.device.createBindGroup(
|
|
||||||
&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = bgl,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.buffer(0, uniform_buffer, 0, @sizeOf(UniformBufferObject), @sizeOf(UniformBufferObject)),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
app.title_timer = try core.Timer.start();
|
|
||||||
app.timer = try core.Timer.start();
|
|
||||||
app.pipeline = core.device.createRenderPipeline(&pipeline_descriptor);
|
|
||||||
app.vertex_buffer = vertex_buffer;
|
|
||||||
app.uniform_buffer = uniform_buffer;
|
|
||||||
app.bind_group = bind_group;
|
|
||||||
|
|
||||||
shader_module.release();
|
|
||||||
pipeline_layout.release();
|
|
||||||
bgl.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
defer _ = gpa.deinit();
|
|
||||||
defer core.deinit();
|
|
||||||
|
|
||||||
app.vertex_buffer.release();
|
|
||||||
app.uniform_buffer.release();
|
|
||||||
app.bind_group.release();
|
|
||||||
app.pipeline.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(app: *App) !bool {
|
|
||||||
var iter = core.pollEvents();
|
|
||||||
while (iter.next()) |event| {
|
|
||||||
switch (event) {
|
|
||||||
.key_press => |ev| {
|
|
||||||
if (ev.key == .space) return true;
|
|
||||||
},
|
|
||||||
.close => return true,
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = back_buffer_view,
|
|
||||||
.clear_value = std.mem.zeroes(gpu.Color),
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
|
|
||||||
const queue = core.queue;
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
const render_pass_info = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{color_attachment},
|
|
||||||
});
|
|
||||||
|
|
||||||
{
|
|
||||||
const time = app.timer.read();
|
|
||||||
const model = zm.mul(zm.rotationX(time * (std.math.pi / 2.0)), zm.rotationZ(time * (std.math.pi / 2.0)));
|
|
||||||
const view = zm.lookAtRh(
|
|
||||||
zm.Vec{ 0, 4, 2, 1 },
|
|
||||||
zm.Vec{ 0, 0, 0, 1 },
|
|
||||||
zm.Vec{ 0, 0, 1, 0 },
|
|
||||||
);
|
|
||||||
const proj = zm.perspectiveFovRh(
|
|
||||||
(std.math.pi / 4.0),
|
|
||||||
@as(f32, @floatFromInt(core.descriptor.width)) / @as(f32, @floatFromInt(core.descriptor.height)),
|
|
||||||
0.1,
|
|
||||||
10,
|
|
||||||
);
|
|
||||||
const mvp = zm.mul(zm.mul(model, view), proj);
|
|
||||||
const ubo = UniformBufferObject{
|
|
||||||
.mat = zm.transpose(mvp),
|
|
||||||
};
|
|
||||||
queue.writeBuffer(app.uniform_buffer, 0, &[_]UniformBufferObject{ubo});
|
|
||||||
}
|
|
||||||
|
|
||||||
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_group, &.{0});
|
|
||||||
pass.draw(vertices.len, 1, 0, 0);
|
|
||||||
pass.end();
|
|
||||||
pass.release();
|
|
||||||
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
encoder.release();
|
|
||||||
|
|
||||||
queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
command.release();
|
|
||||||
core.swap_chain.present();
|
|
||||||
back_buffer_view.release();
|
|
||||||
|
|
||||||
// update the window title every second
|
|
||||||
if (app.title_timer.read() >= 1.0) {
|
|
||||||
app.title_timer.reset();
|
|
||||||
try core.printTitle("Rotating Cube [ {d}fps ] [ Input {d}hz ]", .{
|
|
||||||
core.frameRate(),
|
|
||||||
core.inputRate(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
@group(0) @binding(0) var<uniform> ubo : mat4x4<f32>;
|
|
||||||
struct VertexOut {
|
|
||||||
@builtin(position) position_clip : vec4<f32>,
|
|
||||||
@location(0) fragUV : vec2<f32>,
|
|
||||||
@location(1) fragPosition: vec4<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
@vertex fn vertex_main(
|
|
||||||
@location(0) position : vec4<f32>,
|
|
||||||
@location(1) uv: vec2<f32>
|
|
||||||
) -> VertexOut {
|
|
||||||
var output : VertexOut;
|
|
||||||
output.position_clip = position * ubo;
|
|
||||||
output.fragUV = uv;
|
|
||||||
output.fragPosition = 0.5 * (position + vec4<f32>(1.0, 1.0, 1.0, 1.0));
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
@fragment fn frag_main(
|
|
||||||
@location(0) fragUV: vec2<f32>,
|
|
||||||
@location(1) fragPosition: vec4<f32>
|
|
||||||
) -> @location(0) vec4<f32> {
|
|
||||||
return fragPosition;
|
|
||||||
}
|
|
||||||
|
|
@ -1,360 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
const zm = @import("zmath");
|
|
||||||
const zigimg = @import("zigimg");
|
|
||||||
const assets = @import("assets");
|
|
||||||
const json = std.json;
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
// Use experimental sysgpu graphics API
|
|
||||||
pub const use_sysgpu = true;
|
|
||||||
|
|
||||||
const speed = 2.0 * 100.0; // pixels per second
|
|
||||||
|
|
||||||
const Vec2 = @Vector(2, f32);
|
|
||||||
|
|
||||||
const UniformBufferObject = struct {
|
|
||||||
mat: zm.Mat,
|
|
||||||
};
|
|
||||||
const Sprite = extern struct {
|
|
||||||
pos: Vec2,
|
|
||||||
size: Vec2,
|
|
||||||
world_pos: Vec2,
|
|
||||||
sheet_size: Vec2,
|
|
||||||
};
|
|
||||||
const SpriteFrames = extern struct {
|
|
||||||
up: Vec2,
|
|
||||||
down: Vec2,
|
|
||||||
left: Vec2,
|
|
||||||
right: Vec2,
|
|
||||||
};
|
|
||||||
const JSONFrames = struct {
|
|
||||||
up: []f32,
|
|
||||||
down: []f32,
|
|
||||||
left: []f32,
|
|
||||||
right: []f32,
|
|
||||||
};
|
|
||||||
const JSONSprite = struct {
|
|
||||||
pos: []f32,
|
|
||||||
size: []f32,
|
|
||||||
world_pos: []f32,
|
|
||||||
is_player: bool = false,
|
|
||||||
frames: JSONFrames,
|
|
||||||
};
|
|
||||||
const SpriteSheet = struct {
|
|
||||||
width: f32,
|
|
||||||
height: f32,
|
|
||||||
};
|
|
||||||
const JSONData = struct {
|
|
||||||
sheet: SpriteSheet,
|
|
||||||
sprites: []JSONSprite,
|
|
||||||
};
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
|
|
||||||
title_timer: core.Timer,
|
|
||||||
timer: core.Timer,
|
|
||||||
fps_timer: core.Timer,
|
|
||||||
pipeline: *gpu.RenderPipeline,
|
|
||||||
uniform_buffer: *gpu.Buffer,
|
|
||||||
bind_group: *gpu.BindGroup,
|
|
||||||
sheet: SpriteSheet,
|
|
||||||
sprites_buffer: *gpu.Buffer,
|
|
||||||
sprites: std.ArrayList(Sprite),
|
|
||||||
sprites_frames: std.ArrayList(SpriteFrames),
|
|
||||||
player_pos: Vec2,
|
|
||||||
direction: Vec2,
|
|
||||||
player_sprite_index: usize,
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{});
|
|
||||||
|
|
||||||
const allocator = gpa.allocator();
|
|
||||||
|
|
||||||
const sprites_file = try std.fs.cwd().openFile("../../examples/sprite2d/sprites.json", .{ .mode = .read_only });
|
|
||||||
defer sprites_file.close();
|
|
||||||
const file_size = (try sprites_file.stat()).size;
|
|
||||||
const buffer = try allocator.alloc(u8, file_size);
|
|
||||||
defer allocator.free(buffer);
|
|
||||||
try sprites_file.reader().readNoEof(buffer);
|
|
||||||
const root = try std.json.parseFromSlice(JSONData, allocator, buffer, .{});
|
|
||||||
defer root.deinit();
|
|
||||||
|
|
||||||
app.player_pos = Vec2{ 0, 0 };
|
|
||||||
app.direction = Vec2{ 0, 0 };
|
|
||||||
app.sheet = root.value.sheet;
|
|
||||||
std.log.info("Sheet Dimensions: {} {}", .{ app.sheet.width, app.sheet.height });
|
|
||||||
app.sprites = std.ArrayList(Sprite).init(allocator);
|
|
||||||
app.sprites_frames = std.ArrayList(SpriteFrames).init(allocator);
|
|
||||||
for (root.value.sprites) |sprite| {
|
|
||||||
std.log.info("Sprite World Position: {} {}", .{ sprite.world_pos[0], sprite.world_pos[1] });
|
|
||||||
std.log.info("Sprite Texture Position: {} {}", .{ sprite.pos[0], sprite.pos[1] });
|
|
||||||
std.log.info("Sprite Dimensions: {} {}", .{ sprite.size[0], sprite.size[1] });
|
|
||||||
if (sprite.is_player) {
|
|
||||||
app.player_sprite_index = app.sprites.items.len;
|
|
||||||
}
|
|
||||||
try app.sprites.append(.{
|
|
||||||
.pos = Vec2{ sprite.pos[0], sprite.pos[1] },
|
|
||||||
.size = Vec2{ sprite.size[0], sprite.size[1] },
|
|
||||||
.world_pos = Vec2{ sprite.world_pos[0], sprite.world_pos[1] },
|
|
||||||
.sheet_size = Vec2{ app.sheet.width, app.sheet.height },
|
|
||||||
});
|
|
||||||
try app.sprites_frames.append(.{ .up = Vec2{ sprite.frames.up[0], sprite.frames.up[1] }, .down = Vec2{ sprite.frames.down[0], sprite.frames.down[1] }, .left = Vec2{ sprite.frames.left[0], sprite.frames.left[1] }, .right = Vec2{ sprite.frames.right[0], sprite.frames.right[1] } });
|
|
||||||
}
|
|
||||||
std.log.info("Number of sprites: {}", .{app.sprites.items.len});
|
|
||||||
|
|
||||||
const shader_module = core.device.createShaderModuleWGSL("sprite-shader.wgsl", @embedFile("sprite-shader.wgsl"));
|
|
||||||
|
|
||||||
const blend = gpu.BlendState{
|
|
||||||
.color = .{
|
|
||||||
.operation = .add,
|
|
||||||
.src_factor = .src_alpha,
|
|
||||||
.dst_factor = .one_minus_src_alpha,
|
|
||||||
},
|
|
||||||
.alpha = .{
|
|
||||||
.operation = .add,
|
|
||||||
.src_factor = .one,
|
|
||||||
.dst_factor = .zero,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
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{
|
|
||||||
.fragment = &fragment,
|
|
||||||
.vertex = gpu.VertexState.init(.{
|
|
||||||
.module = shader_module,
|
|
||||||
.entry_point = "vertex_main",
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
const pipeline = core.device.createRenderPipeline(&pipeline_descriptor);
|
|
||||||
|
|
||||||
const sprites_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .storage = true, .copy_dst = true },
|
|
||||||
.size = @sizeOf(Sprite) * app.sprites.items.len,
|
|
||||||
.mapped_at_creation = .true,
|
|
||||||
});
|
|
||||||
const sprites_mapped = sprites_buffer.getMappedRange(Sprite, 0, app.sprites.items.len);
|
|
||||||
@memcpy(sprites_mapped.?, app.sprites.items[0..]);
|
|
||||||
sprites_buffer.unmap();
|
|
||||||
|
|
||||||
// Create a sampler with linear filtering for smooth interpolation.
|
|
||||||
const sampler = core.device.createSampler(&.{
|
|
||||||
.mag_filter = .linear,
|
|
||||||
.min_filter = .linear,
|
|
||||||
});
|
|
||||||
const queue = core.queue;
|
|
||||||
var img = try zigimg.Image.fromMemory(allocator, assets.sprites_sheet_png);
|
|
||||||
defer img.deinit();
|
|
||||||
const img_size = gpu.Extent3D{ .width = @as(u32, @intCast(img.width)), .height = @as(u32, @intCast(img.height)) };
|
|
||||||
std.log.info("Image Dimensions: {} {}", .{ img.width, img.height });
|
|
||||||
const texture = core.device.createTexture(&.{
|
|
||||||
.size = img_size,
|
|
||||||
.format = .rgba8_unorm,
|
|
||||||
.usage = .{
|
|
||||||
.texture_binding = true,
|
|
||||||
.copy_dst = true,
|
|
||||||
.render_attachment = true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const data_layout = gpu.Texture.DataLayout{
|
|
||||||
.bytes_per_row = @as(u32, @intCast(img.width * 4)),
|
|
||||||
.rows_per_image = @as(u32, @intCast(img.height)),
|
|
||||||
};
|
|
||||||
switch (img.pixels) {
|
|
||||||
.rgba32 => |pixels| queue.writeTexture(&.{ .texture = texture }, &data_layout, &img_size, pixels),
|
|
||||||
.rgb24 => |pixels| {
|
|
||||||
const data = try rgb24ToRgba32(allocator, pixels);
|
|
||||||
defer data.deinit(allocator);
|
|
||||||
queue.writeTexture(&.{ .texture = texture }, &data_layout, &img_size, data.rgba32);
|
|
||||||
},
|
|
||||||
else => @panic("unsupported image color format"),
|
|
||||||
}
|
|
||||||
|
|
||||||
const uniform_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .copy_dst = true, .uniform = true },
|
|
||||||
.size = @sizeOf(UniformBufferObject),
|
|
||||||
.mapped_at_creation = .false,
|
|
||||||
});
|
|
||||||
|
|
||||||
const texture_view = texture.createView(&gpu.TextureView.Descriptor{});
|
|
||||||
texture.release();
|
|
||||||
|
|
||||||
const bind_group_layout = pipeline.getBindGroupLayout(0);
|
|
||||||
const bind_group = core.device.createBindGroup(
|
|
||||||
&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = bind_group_layout,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.buffer(0, uniform_buffer, 0, @sizeOf(UniformBufferObject), @sizeOf(UniformBufferObject)),
|
|
||||||
gpu.BindGroup.Entry.sampler(1, sampler),
|
|
||||||
gpu.BindGroup.Entry.textureView(2, texture_view),
|
|
||||||
gpu.BindGroup.Entry.buffer(3, sprites_buffer, 0, @sizeOf(Sprite) * app.sprites.items.len, @sizeOf(Sprite)),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
texture_view.release();
|
|
||||||
sampler.release();
|
|
||||||
bind_group_layout.release();
|
|
||||||
|
|
||||||
app.title_timer = try core.Timer.start();
|
|
||||||
app.timer = try core.Timer.start();
|
|
||||||
app.fps_timer = try core.Timer.start();
|
|
||||||
app.pipeline = pipeline;
|
|
||||||
app.uniform_buffer = uniform_buffer;
|
|
||||||
app.bind_group = bind_group;
|
|
||||||
app.sprites_buffer = sprites_buffer;
|
|
||||||
|
|
||||||
shader_module.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
defer _ = gpa.deinit();
|
|
||||||
defer core.deinit();
|
|
||||||
|
|
||||||
app.pipeline.release();
|
|
||||||
app.sprites.deinit();
|
|
||||||
app.sprites_frames.deinit();
|
|
||||||
app.uniform_buffer.release();
|
|
||||||
app.bind_group.release();
|
|
||||||
app.sprites_buffer.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(app: *App) !bool {
|
|
||||||
// Handle input by determining the direction the player wants to go.
|
|
||||||
var iter = core.pollEvents();
|
|
||||||
while (iter.next()) |event| {
|
|
||||||
switch (event) {
|
|
||||||
.key_press => |ev| {
|
|
||||||
switch (ev.key) {
|
|
||||||
.space => return true,
|
|
||||||
.left => app.direction[0] += 1,
|
|
||||||
.right => app.direction[0] -= 1,
|
|
||||||
.up => app.direction[1] += 1,
|
|
||||||
.down => app.direction[1] -= 1,
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.key_release => |ev| {
|
|
||||||
switch (ev.key) {
|
|
||||||
.left => app.direction[0] -= 1,
|
|
||||||
.right => app.direction[0] += 1,
|
|
||||||
.up => app.direction[1] -= 1,
|
|
||||||
.down => app.direction[1] += 1,
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.close => return true,
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the player position, by moving in the direction the player wants to go
|
|
||||||
// by the speed amount. Multiply by delta_time to ensure that movement is the same speed
|
|
||||||
// regardless of the frame rate.
|
|
||||||
const delta_time = app.fps_timer.lap();
|
|
||||||
app.player_pos += app.direction * Vec2{ speed, speed } * Vec2{ delta_time, delta_time };
|
|
||||||
|
|
||||||
// Render the frame
|
|
||||||
try app.render();
|
|
||||||
|
|
||||||
// update the window title every second
|
|
||||||
if (app.title_timer.read() >= 1.0) {
|
|
||||||
app.title_timer.reset();
|
|
||||||
try core.printTitle("Sprite2D [ {d}fps ] [ Input {d}hz ]", .{
|
|
||||||
core.frameRate(),
|
|
||||||
core.inputRate(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(app: *App) !void {
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = back_buffer_view,
|
|
||||||
// sky blue background color:
|
|
||||||
.clear_value = .{ .r = 0.52, .g = 0.8, .b = 0.92, .a = 1.0 },
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
const render_pass_info = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{color_attachment},
|
|
||||||
});
|
|
||||||
|
|
||||||
const player_sprite = &app.sprites.items[app.player_sprite_index];
|
|
||||||
const player_sprite_frame = &app.sprites_frames.items[app.player_sprite_index];
|
|
||||||
if (app.direction[0] == -1.0) {
|
|
||||||
player_sprite.pos = player_sprite_frame.left;
|
|
||||||
} else if (app.direction[0] == 1.0) {
|
|
||||||
player_sprite.pos = player_sprite_frame.right;
|
|
||||||
} else if (app.direction[1] == -1.0) {
|
|
||||||
player_sprite.pos = player_sprite_frame.down;
|
|
||||||
} else if (app.direction[1] == 1.0) {
|
|
||||||
player_sprite.pos = player_sprite_frame.up;
|
|
||||||
}
|
|
||||||
player_sprite.world_pos = app.player_pos;
|
|
||||||
|
|
||||||
// One pixel in our scene will equal one window pixel (i.e. be roughly the same size
|
|
||||||
// irrespective of whether the user has a Retina/HDPI display.)
|
|
||||||
const proj = zm.orthographicRh(
|
|
||||||
@as(f32, @floatFromInt(core.size().width)),
|
|
||||||
@as(f32, @floatFromInt(core.size().height)),
|
|
||||||
0.1,
|
|
||||||
1000,
|
|
||||||
);
|
|
||||||
const view = zm.lookAtRh(
|
|
||||||
zm.Vec{ 0, 1000, 0, 1 },
|
|
||||||
zm.Vec{ 0, 0, 0, 1 },
|
|
||||||
zm.Vec{ 0, 0, 1, 0 },
|
|
||||||
);
|
|
||||||
const mvp = zm.mul(view, proj);
|
|
||||||
const ubo = UniformBufferObject{
|
|
||||||
.mat = zm.transpose(mvp),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Pass the latest uniform values & sprite values to the shader program.
|
|
||||||
encoder.writeBuffer(app.uniform_buffer, 0, &[_]UniformBufferObject{ubo});
|
|
||||||
encoder.writeBuffer(app.sprites_buffer, 0, app.sprites.items);
|
|
||||||
|
|
||||||
// Draw the sprite batch
|
|
||||||
const total_vertices = @as(u32, @intCast(app.sprites.items.len * 6));
|
|
||||||
const pass = encoder.beginRenderPass(&render_pass_info);
|
|
||||||
pass.setPipeline(app.pipeline);
|
|
||||||
pass.setBindGroup(0, app.bind_group, &.{});
|
|
||||||
pass.draw(total_vertices, 1, 0, 0);
|
|
||||||
pass.end();
|
|
||||||
pass.release();
|
|
||||||
|
|
||||||
// Submit the frame.
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
encoder.release();
|
|
||||||
const queue = core.queue;
|
|
||||||
queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
command.release();
|
|
||||||
core.swap_chain.present();
|
|
||||||
back_buffer_view.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rgb24ToRgba32(allocator: std.mem.Allocator, in: []zigimg.color.Rgb24) !zigimg.color.PixelStorage {
|
|
||||||
const out = try zigimg.color.PixelStorage.init(allocator, .rgba32, in.len);
|
|
||||||
var i: usize = 0;
|
|
||||||
while (i < in.len) : (i += 1) {
|
|
||||||
out.rgba32[i] = zigimg.color.Rgba32{ .r = in[i].r, .g = in[i].g, .b = in[i].b, .a = 255 };
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
struct Uniforms {
|
|
||||||
modelViewProjectionMatrix : mat4x4<f32>,
|
|
||||||
};
|
|
||||||
@binding(0) @group(0) var<uniform> uniforms : Uniforms;
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) Position : vec4<f32>,
|
|
||||||
@location(0) fragUV : vec2<f32>,
|
|
||||||
@location(1) spriteIndex : f32,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Sprite {
|
|
||||||
pos: vec2<f32>,
|
|
||||||
size: vec2<f32>,
|
|
||||||
world_pos: vec2<f32>,
|
|
||||||
sheet_size: vec2<f32>,
|
|
||||||
};
|
|
||||||
@binding(3) @group(0) var<storage, read> sprites: array<Sprite>;
|
|
||||||
|
|
||||||
@vertex
|
|
||||||
fn vertex_main(
|
|
||||||
@builtin(vertex_index) VertexIndex : u32
|
|
||||||
) -> VertexOutput {
|
|
||||||
var sprite = sprites[VertexIndex / 6];
|
|
||||||
|
|
||||||
// Calculate the vertex position
|
|
||||||
var positions = array<vec2<f32>, 6>(
|
|
||||||
vec2<f32>(0.0, 0.0), // bottom-left
|
|
||||||
vec2<f32>(0.0, 1.0), // top-left
|
|
||||||
vec2<f32>(1.0, 0.0), // bottom-right
|
|
||||||
vec2<f32>(1.0, 0.0), // bottom-right
|
|
||||||
vec2<f32>(0.0, 1.0), // top-left
|
|
||||||
vec2<f32>(1.0, 1.0), // top-right
|
|
||||||
);
|
|
||||||
var pos = positions[VertexIndex % 6];
|
|
||||||
pos.x *= sprite.size.x;
|
|
||||||
pos.y *= sprite.size.y;
|
|
||||||
pos.x += sprite.world_pos.x;
|
|
||||||
pos.y += sprite.world_pos.y;
|
|
||||||
|
|
||||||
// Calculate the UV coordinate
|
|
||||||
var uvs = array<vec2<f32>, 6>(
|
|
||||||
vec2<f32>(0.0, 1.0), // bottom-left
|
|
||||||
vec2<f32>(0.0, 0.0), // top-left
|
|
||||||
vec2<f32>(1.0, 1.0), // bottom-right
|
|
||||||
vec2<f32>(1.0, 1.0), // bottom-right
|
|
||||||
vec2<f32>(0.0, 0.0), // top-left
|
|
||||||
vec2<f32>(1.0, 0.0), // top-right
|
|
||||||
);
|
|
||||||
var uv = uvs[VertexIndex % 6];
|
|
||||||
uv.x *= sprite.size.x / sprite.sheet_size.x;
|
|
||||||
uv.y *= sprite.size.y / sprite.sheet_size.y;
|
|
||||||
uv.x += sprite.pos.x / sprite.sheet_size.x;
|
|
||||||
uv.y += sprite.pos.y / sprite.sheet_size.y;
|
|
||||||
|
|
||||||
var output : VertexOutput;
|
|
||||||
output.Position = vec4<f32>(pos.x, 0.0, pos.y, 1.0) * uniforms.modelViewProjectionMatrix;
|
|
||||||
output.fragUV = uv;
|
|
||||||
output.spriteIndex = f32(VertexIndex / 6);
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
@group(0) @binding(1) var spriteSampler: sampler;
|
|
||||||
@group(0) @binding(2) var spriteTexture: texture_2d<f32>;
|
|
||||||
|
|
||||||
@fragment
|
|
||||||
fn frag_main(
|
|
||||||
@location(0) fragUV: vec2<f32>,
|
|
||||||
@location(1) spriteIndex: f32
|
|
||||||
) -> @location(0) vec4<f32> {
|
|
||||||
var color = textureSample(spriteTexture, spriteSampler, fragUV);
|
|
||||||
if (spriteIndex == 0.0) {
|
|
||||||
if (color[3] > 0.0) {
|
|
||||||
color[0] = 0.3;
|
|
||||||
color[1] = 0.2;
|
|
||||||
color[2] = 0.5;
|
|
||||||
color[3] = 1.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
{
|
|
||||||
"sheet": {
|
|
||||||
"width": 352.0,
|
|
||||||
"height": 32.0
|
|
||||||
},
|
|
||||||
"sprites": [
|
|
||||||
{
|
|
||||||
"pos": [ 0.0, 0.0 ],
|
|
||||||
"size": [ 32.0, 32.0 ],
|
|
||||||
"world_pos": [ 0.0, 0.0 ],
|
|
||||||
"is_player": true,
|
|
||||||
"frames": {
|
|
||||||
"down": [ 0.0, 0.0 ],
|
|
||||||
"left": [ 32.0, 0.0 ],
|
|
||||||
"right": [ 64.0, 0.0 ],
|
|
||||||
"up": [ 96.0, 0.0 ]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"pos": [ 128.0, 0.0 ],
|
|
||||||
"size": [ 32.0, 32.0 ],
|
|
||||||
"world_pos": [ 32.0, 32.0 ],
|
|
||||||
"frames": {
|
|
||||||
"down": [ 128.0, 0.0 ],
|
|
||||||
"left": [ 160.0, 0.0 ],
|
|
||||||
"right": [ 192.0, 0.0 ],
|
|
||||||
"up": [ 224.0, 0.0 ]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"pos": [ 128.0, 0.0 ],
|
|
||||||
"size": [ 32.0, 32.0 ],
|
|
||||||
"world_pos": [ 64.0, 64.0 ],
|
|
||||||
"frames": {
|
|
||||||
"down": [ 0.0, 0.0 ],
|
|
||||||
"left": [ 0.0, 0.0 ],
|
|
||||||
"right": [ 0.0, 0.0 ],
|
|
||||||
"up": [ 0.0, 0.0 ]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"pos": [ 256.0, 0.0 ],
|
|
||||||
"size": [ 32.0, 32.0 ],
|
|
||||||
"world_pos": [ 96.0, 96.0 ],
|
|
||||||
"frames": {
|
|
||||||
"down": [ 0.0, 0.0 ],
|
|
||||||
"left": [ 0.0, 0.0 ],
|
|
||||||
"right": [ 0.0, 0.0 ],
|
|
||||||
"up": [ 0.0, 0.0 ]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
pub const Vertex = extern struct {
|
|
||||||
pos: @Vector(4, f32),
|
|
||||||
col: @Vector(4, f32),
|
|
||||||
uv: @Vector(2, f32),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const vertices = [_]Vertex{
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
};
|
|
||||||
|
|
@ -1,319 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
const zm = @import("zmath");
|
|
||||||
const zigimg = @import("zigimg");
|
|
||||||
const Vertex = @import("cube_mesh.zig").Vertex;
|
|
||||||
const vertices = @import("cube_mesh.zig").vertices;
|
|
||||||
const assets = @import("assets");
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
// Use experimental sysgpu graphics API
|
|
||||||
pub const use_sysgpu = true;
|
|
||||||
|
|
||||||
const UniformBufferObject = struct {
|
|
||||||
mat: zm.Mat,
|
|
||||||
};
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
|
|
||||||
title_timer: core.Timer,
|
|
||||||
timer: core.Timer,
|
|
||||||
pipeline: *gpu.RenderPipeline,
|
|
||||||
vertex_buffer: *gpu.Buffer,
|
|
||||||
uniform_buffer: *gpu.Buffer,
|
|
||||||
bind_group: *gpu.BindGroup,
|
|
||||||
depth_texture: *gpu.Texture,
|
|
||||||
depth_texture_view: *gpu.TextureView,
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{});
|
|
||||||
const allocator = gpa.allocator();
|
|
||||||
|
|
||||||
const shader_module = core.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.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.init(.{
|
|
||||||
.array_stride = @sizeOf(Vertex),
|
|
||||||
.step_mode = .vertex,
|
|
||||||
.attributes = &vertex_attributes,
|
|
||||||
});
|
|
||||||
|
|
||||||
const blend = gpu.BlendState{
|
|
||||||
.color = .{
|
|
||||||
.operation = .add,
|
|
||||||
.src_factor = .src_alpha,
|
|
||||||
.dst_factor = .one_minus_src_alpha,
|
|
||||||
},
|
|
||||||
.alpha = .{
|
|
||||||
.operation = .add,
|
|
||||||
.src_factor = .one,
|
|
||||||
.dst_factor = .zero,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
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{
|
|
||||||
.fragment = &fragment,
|
|
||||||
// Enable depth testing so that the fragment closest to the camera
|
|
||||||
// is rendered in front.
|
|
||||||
.depth_stencil = &.{
|
|
||||||
.format = .depth24_plus,
|
|
||||||
.depth_write_enabled = .true,
|
|
||||||
.depth_compare = .less,
|
|
||||||
},
|
|
||||||
.vertex = gpu.VertexState.init(.{
|
|
||||||
.module = shader_module,
|
|
||||||
.entry_point = "vertex_main",
|
|
||||||
.buffers = &.{vertex_buffer_layout},
|
|
||||||
}),
|
|
||||||
.primitive = .{
|
|
||||||
// Backface culling since the cube is solid piece of geometry.
|
|
||||||
// Faces pointing away from the camera will be occluded by faces
|
|
||||||
// pointing toward the camera.
|
|
||||||
.cull_mode = .back,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const pipeline = core.device.createRenderPipeline(&pipeline_descriptor);
|
|
||||||
shader_module.release();
|
|
||||||
|
|
||||||
const vertex_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .vertex = true },
|
|
||||||
.size = @sizeOf(Vertex) * vertices.len,
|
|
||||||
.mapped_at_creation = .true,
|
|
||||||
});
|
|
||||||
const vertex_mapped = vertex_buffer.getMappedRange(Vertex, 0, vertices.len);
|
|
||||||
@memcpy(vertex_mapped.?, vertices[0..]);
|
|
||||||
vertex_buffer.unmap();
|
|
||||||
|
|
||||||
// Create a sampler with linear filtering for smooth interpolation.
|
|
||||||
const sampler = core.device.createSampler(&.{
|
|
||||||
.mag_filter = .linear,
|
|
||||||
.min_filter = .linear,
|
|
||||||
});
|
|
||||||
const queue = core.queue;
|
|
||||||
var img = try zigimg.Image.fromMemory(allocator, assets.gotta_go_fast_png);
|
|
||||||
defer img.deinit();
|
|
||||||
const img_size = gpu.Extent3D{ .width = @as(u32, @intCast(img.width)), .height = @as(u32, @intCast(img.height)) };
|
|
||||||
const cube_texture = core.device.createTexture(&.{
|
|
||||||
.size = img_size,
|
|
||||||
.format = .rgba8_unorm,
|
|
||||||
.usage = .{
|
|
||||||
.texture_binding = true,
|
|
||||||
.copy_dst = true,
|
|
||||||
.render_attachment = true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const data_layout = gpu.Texture.DataLayout{
|
|
||||||
.bytes_per_row = @as(u32, @intCast(img.width * 4)),
|
|
||||||
.rows_per_image = @as(u32, @intCast(img.height)),
|
|
||||||
};
|
|
||||||
switch (img.pixels) {
|
|
||||||
.rgba32 => |pixels| queue.writeTexture(&.{ .texture = cube_texture }, &data_layout, &img_size, pixels),
|
|
||||||
.rgb24 => |pixels| {
|
|
||||||
const data = try rgb24ToRgba32(allocator, pixels);
|
|
||||||
defer data.deinit(allocator);
|
|
||||||
queue.writeTexture(&.{ .texture = cube_texture }, &data_layout, &img_size, data.rgba32);
|
|
||||||
},
|
|
||||||
else => @panic("unsupported image color format"),
|
|
||||||
}
|
|
||||||
|
|
||||||
const uniform_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .copy_dst = true, .uniform = true },
|
|
||||||
.size = @sizeOf(UniformBufferObject),
|
|
||||||
.mapped_at_creation = .false,
|
|
||||||
});
|
|
||||||
|
|
||||||
const texture_view = cube_texture.createView(&gpu.TextureView.Descriptor{});
|
|
||||||
cube_texture.release();
|
|
||||||
|
|
||||||
const bind_group_layout = pipeline.getBindGroupLayout(0);
|
|
||||||
const bind_group = core.device.createBindGroup(
|
|
||||||
&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = bind_group_layout,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.buffer(0, uniform_buffer, 0, @sizeOf(UniformBufferObject), @sizeOf(UniformBufferObject)),
|
|
||||||
gpu.BindGroup.Entry.sampler(1, sampler),
|
|
||||||
gpu.BindGroup.Entry.textureView(2, texture_view),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
sampler.release();
|
|
||||||
texture_view.release();
|
|
||||||
bind_group_layout.release();
|
|
||||||
|
|
||||||
const depth_texture = core.device.createTexture(&gpu.Texture.Descriptor{
|
|
||||||
.size = gpu.Extent3D{
|
|
||||||
.width = core.descriptor.width,
|
|
||||||
.height = core.descriptor.height,
|
|
||||||
},
|
|
||||||
.format = .depth24_plus,
|
|
||||||
.usage = .{
|
|
||||||
.render_attachment = true,
|
|
||||||
.texture_binding = true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const depth_texture_view = depth_texture.createView(&gpu.TextureView.Descriptor{
|
|
||||||
.format = .depth24_plus,
|
|
||||||
.dimension = .dimension_2d,
|
|
||||||
.array_layer_count = 1,
|
|
||||||
.mip_level_count = 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
app.timer = try core.Timer.start();
|
|
||||||
app.title_timer = try core.Timer.start();
|
|
||||||
app.pipeline = pipeline;
|
|
||||||
app.vertex_buffer = vertex_buffer;
|
|
||||||
app.uniform_buffer = uniform_buffer;
|
|
||||||
app.bind_group = bind_group;
|
|
||||||
app.depth_texture = depth_texture;
|
|
||||||
app.depth_texture_view = depth_texture_view;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
defer _ = gpa.deinit();
|
|
||||||
defer core.deinit();
|
|
||||||
|
|
||||||
app.pipeline.release();
|
|
||||||
app.vertex_buffer.release();
|
|
||||||
app.uniform_buffer.release();
|
|
||||||
app.bind_group.release();
|
|
||||||
app.depth_texture.release();
|
|
||||||
app.depth_texture_view.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(app: *App) !bool {
|
|
||||||
var iter = core.pollEvents();
|
|
||||||
while (iter.next()) |event| {
|
|
||||||
switch (event) {
|
|
||||||
.key_press => |ev| {
|
|
||||||
switch (ev.key) {
|
|
||||||
.space => return true,
|
|
||||||
.one => core.setVSync(.none),
|
|
||||||
.two => core.setVSync(.double),
|
|
||||||
.three => core.setVSync(.triple),
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
std.debug.print("vsync mode changed to {s}\n", .{@tagName(core.vsync())});
|
|
||||||
},
|
|
||||||
.framebuffer_resize => |ev| {
|
|
||||||
// If window is resized, recreate depth buffer otherwise we cannot use it.
|
|
||||||
app.depth_texture.release();
|
|
||||||
|
|
||||||
app.depth_texture = core.device.createTexture(&gpu.Texture.Descriptor{
|
|
||||||
.size = gpu.Extent3D{
|
|
||||||
.width = ev.width,
|
|
||||||
.height = ev.height,
|
|
||||||
},
|
|
||||||
.format = .depth24_plus,
|
|
||||||
.usage = .{
|
|
||||||
.render_attachment = true,
|
|
||||||
.texture_binding = true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
app.depth_texture_view.release();
|
|
||||||
app.depth_texture_view = app.depth_texture.createView(&gpu.TextureView.Descriptor{
|
|
||||||
.format = .depth24_plus,
|
|
||||||
.dimension = .dimension_2d,
|
|
||||||
.array_layer_count = 1,
|
|
||||||
.mip_level_count = 1,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
.close => return true,
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = back_buffer_view,
|
|
||||||
.clear_value = .{ .r = 0.5, .g = 0.5, .b = 0.5, .a = 0.0 },
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
const render_pass_info = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{color_attachment},
|
|
||||||
.depth_stencil_attachment = &.{
|
|
||||||
.view = app.depth_texture_view,
|
|
||||||
.depth_clear_value = 1.0,
|
|
||||||
.depth_load_op = .clear,
|
|
||||||
.depth_store_op = .store,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
{
|
|
||||||
const time = app.timer.read();
|
|
||||||
const model = zm.mul(zm.rotationX(time * (std.math.pi / 2.0)), zm.rotationZ(time * (std.math.pi / 2.0)));
|
|
||||||
const view = zm.lookAtRh(
|
|
||||||
zm.Vec{ 0, 4, 2, 1 },
|
|
||||||
zm.Vec{ 0, 0, 0, 1 },
|
|
||||||
zm.Vec{ 0, 0, 1, 0 },
|
|
||||||
);
|
|
||||||
const proj = zm.perspectiveFovRh(
|
|
||||||
(std.math.pi / 4.0),
|
|
||||||
@as(f32, @floatFromInt(core.descriptor.width)) / @as(f32, @floatFromInt(core.descriptor.height)),
|
|
||||||
0.1,
|
|
||||||
10,
|
|
||||||
);
|
|
||||||
const mvp = zm.mul(zm.mul(model, view), proj);
|
|
||||||
const ubo = UniformBufferObject{
|
|
||||||
.mat = zm.transpose(mvp),
|
|
||||||
};
|
|
||||||
encoder.writeBuffer(app.uniform_buffer, 0, &[_]UniformBufferObject{ubo});
|
|
||||||
}
|
|
||||||
|
|
||||||
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_group, &.{});
|
|
||||||
pass.draw(vertices.len, 1, 0, 0);
|
|
||||||
pass.end();
|
|
||||||
pass.release();
|
|
||||||
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
encoder.release();
|
|
||||||
|
|
||||||
const queue = core.queue;
|
|
||||||
queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
command.release();
|
|
||||||
core.swap_chain.present();
|
|
||||||
back_buffer_view.release();
|
|
||||||
|
|
||||||
// update the window title every second
|
|
||||||
if (app.title_timer.read() >= 1.0) {
|
|
||||||
app.title_timer.reset();
|
|
||||||
try core.printTitle("Textured cube [ {d}fps ] [ Input {d}hz ]", .{
|
|
||||||
core.frameRate(),
|
|
||||||
core.inputRate(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rgb24ToRgba32(allocator: std.mem.Allocator, in: []zigimg.color.Rgb24) !zigimg.color.PixelStorage {
|
|
||||||
const out = try zigimg.color.PixelStorage.init(allocator, .rgba32, in.len);
|
|
||||||
var i: usize = 0;
|
|
||||||
while (i < in.len) : (i += 1) {
|
|
||||||
out.rgba32[i] = zigimg.color.Rgba32{ .r = in[i].r, .g = in[i].g, .b = in[i].b, .a = 255 };
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
struct Uniforms {
|
|
||||||
modelViewProjectionMatrix : mat4x4<f32>,
|
|
||||||
};
|
|
||||||
@binding(0) @group(0) var<uniform> uniforms : Uniforms;
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) Position : vec4<f32>,
|
|
||||||
@location(0) fragUV : vec2<f32>,
|
|
||||||
@location(1) fragPosition: vec4<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
@vertex
|
|
||||||
fn vertex_main(@location(0) position : vec4<f32>,
|
|
||||||
@location(1) uv : vec2<f32>) -> VertexOutput {
|
|
||||||
var output : VertexOutput;
|
|
||||||
output.Position = position * uniforms.modelViewProjectionMatrix;
|
|
||||||
output.fragUV = uv;
|
|
||||||
output.fragPosition = 0.5 * (position + vec4<f32>(1.0, 1.0, 1.0, 1.0));
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
@group(0) @binding(1) var mySampler: sampler;
|
|
||||||
@group(0) @binding(2) var myTexture: texture_2d<f32>;
|
|
||||||
|
|
||||||
@fragment
|
|
||||||
fn frag_main(@location(0) fragUV: vec2<f32>,
|
|
||||||
@location(1) fragPosition: vec4<f32>) -> @location(0) vec4<f32> {
|
|
||||||
return textureSample(myTexture, mySampler, fragUV);
|
|
||||||
}
|
|
||||||
|
|
@ -1,210 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
const zigimg = @import("zigimg");
|
|
||||||
const assets = @import("assets");
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
const Vertex = extern struct {
|
|
||||||
pos: @Vector(2, f32),
|
|
||||||
uv: @Vector(2, f32),
|
|
||||||
};
|
|
||||||
|
|
||||||
const vertices = [_]Vertex{
|
|
||||||
.{ .pos = .{ -0.5, -0.5 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 0.5, -0.5 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 0.5, 0.5 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -0.5, 0.5 }, .uv = .{ 1, 0 } },
|
|
||||||
};
|
|
||||||
const index_data = [_]u32{ 0, 1, 2, 2, 3, 0 };
|
|
||||||
|
|
||||||
// Use experimental sysgpu graphics API
|
|
||||||
pub const use_sysgpu = true;
|
|
||||||
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
|
|
||||||
title_timer: core.Timer,
|
|
||||||
timer: core.Timer,
|
|
||||||
pipeline: *gpu.RenderPipeline,
|
|
||||||
vertex_buffer: *gpu.Buffer,
|
|
||||||
index_buffer: *gpu.Buffer,
|
|
||||||
bind_group: *gpu.BindGroup,
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{});
|
|
||||||
const allocator = gpa.allocator();
|
|
||||||
|
|
||||||
const shader_module = core.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.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.init(.{
|
|
||||||
.array_stride = @sizeOf(Vertex),
|
|
||||||
.step_mode = .vertex,
|
|
||||||
.attributes = &vertex_attributes,
|
|
||||||
});
|
|
||||||
|
|
||||||
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{
|
|
||||||
.fragment = &fragment,
|
|
||||||
.vertex = gpu.VertexState.init(.{
|
|
||||||
.module = shader_module,
|
|
||||||
.entry_point = "vertex_main",
|
|
||||||
.buffers = &.{vertex_buffer_layout},
|
|
||||||
}),
|
|
||||||
.primitive = .{ .cull_mode = .back },
|
|
||||||
};
|
|
||||||
const pipeline = core.device.createRenderPipeline(&pipeline_descriptor);
|
|
||||||
shader_module.release();
|
|
||||||
|
|
||||||
const vertex_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .vertex = true },
|
|
||||||
.size = @sizeOf(Vertex) * vertices.len,
|
|
||||||
.mapped_at_creation = .true,
|
|
||||||
});
|
|
||||||
const vertex_mapped = vertex_buffer.getMappedRange(Vertex, 0, vertices.len);
|
|
||||||
@memcpy(vertex_mapped.?, vertices[0..]);
|
|
||||||
vertex_buffer.unmap();
|
|
||||||
|
|
||||||
const index_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .index = true },
|
|
||||||
.size = @sizeOf(u32) * index_data.len,
|
|
||||||
.mapped_at_creation = .true,
|
|
||||||
});
|
|
||||||
const index_mapped = index_buffer.getMappedRange(u32, 0, index_data.len);
|
|
||||||
@memcpy(index_mapped.?, index_data[0..]);
|
|
||||||
index_buffer.unmap();
|
|
||||||
|
|
||||||
const sampler = core.device.createSampler(&.{ .mag_filter = .linear, .min_filter = .linear });
|
|
||||||
const queue = core.queue;
|
|
||||||
var img = try zigimg.Image.fromMemory(allocator, assets.gotta_go_fast_png);
|
|
||||||
defer img.deinit();
|
|
||||||
const img_size = gpu.Extent3D{
|
|
||||||
.width = @as(u32, @intCast(img.width)),
|
|
||||||
.height = @as(u32, @intCast(img.height)),
|
|
||||||
};
|
|
||||||
const texture = core.device.createTexture(&.{
|
|
||||||
.size = img_size,
|
|
||||||
.format = .rgba8_unorm,
|
|
||||||
.usage = .{
|
|
||||||
.texture_binding = true,
|
|
||||||
.copy_dst = true,
|
|
||||||
.render_attachment = true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const data_layout = gpu.Texture.DataLayout{
|
|
||||||
.bytes_per_row = @as(u32, @intCast(img.width * 4)),
|
|
||||||
.rows_per_image = @as(u32, @intCast(img.height)),
|
|
||||||
};
|
|
||||||
switch (img.pixels) {
|
|
||||||
.rgba32 => |pixels| queue.writeTexture(&.{ .texture = texture }, &data_layout, &img_size, pixels),
|
|
||||||
.rgb24 => |pixels| {
|
|
||||||
const data = try rgb24ToRgba32(allocator, pixels);
|
|
||||||
defer data.deinit(allocator);
|
|
||||||
queue.writeTexture(&.{ .texture = texture }, &data_layout, &img_size, data.rgba32);
|
|
||||||
},
|
|
||||||
else => @panic("unsupported image color format"),
|
|
||||||
}
|
|
||||||
|
|
||||||
const texture_view = texture.createView(&gpu.TextureView.Descriptor{});
|
|
||||||
texture.release();
|
|
||||||
|
|
||||||
const bind_group_layout = pipeline.getBindGroupLayout(0);
|
|
||||||
const bind_group = core.device.createBindGroup(
|
|
||||||
&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = bind_group_layout,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.sampler(0, sampler),
|
|
||||||
gpu.BindGroup.Entry.textureView(1, texture_view),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
sampler.release();
|
|
||||||
texture_view.release();
|
|
||||||
bind_group_layout.release();
|
|
||||||
|
|
||||||
app.timer = try core.Timer.start();
|
|
||||||
app.title_timer = try core.Timer.start();
|
|
||||||
app.pipeline = pipeline;
|
|
||||||
app.vertex_buffer = vertex_buffer;
|
|
||||||
app.index_buffer = index_buffer;
|
|
||||||
app.bind_group = bind_group;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
app.pipeline.release();
|
|
||||||
app.vertex_buffer.release();
|
|
||||||
app.index_buffer.release();
|
|
||||||
app.bind_group.release();
|
|
||||||
core.deinit();
|
|
||||||
_ = gpa.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(app: *App) !bool {
|
|
||||||
var iter = core.pollEvents();
|
|
||||||
while (iter.next()) |event| if (event == .close) return true;
|
|
||||||
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = back_buffer_view,
|
|
||||||
.clear_value = .{ .r = 0, .g = 0, .b = 0, .a = 0.0 },
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
const render_pass_info = gpu.RenderPassDescriptor.init(.{ .color_attachments = &.{color_attachment} });
|
|
||||||
|
|
||||||
const pass = encoder.beginRenderPass(&render_pass_info);
|
|
||||||
pass.setPipeline(app.pipeline);
|
|
||||||
pass.setVertexBuffer(0, app.vertex_buffer, 0, @sizeOf(Vertex) * vertices.len);
|
|
||||||
pass.setIndexBuffer(app.index_buffer, .uint32, 0, @sizeOf(u32) * index_data.len);
|
|
||||||
pass.setBindGroup(0, app.bind_group, &.{});
|
|
||||||
pass.drawIndexed(index_data.len, 1, 0, 0, 0);
|
|
||||||
pass.end();
|
|
||||||
pass.release();
|
|
||||||
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
encoder.release();
|
|
||||||
|
|
||||||
const queue = core.queue;
|
|
||||||
queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
command.release();
|
|
||||||
core.swap_chain.present();
|
|
||||||
back_buffer_view.release();
|
|
||||||
|
|
||||||
// update the window title every second
|
|
||||||
if (app.title_timer.read() >= 1.0) {
|
|
||||||
app.title_timer.reset();
|
|
||||||
try core.printTitle("Textured Quad [ {d}fps ] [ Input {d}hz ]", .{
|
|
||||||
core.frameRate(),
|
|
||||||
core.inputRate(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rgb24ToRgba32(allocator: std.mem.Allocator, in: []zigimg.color.Rgb24) !zigimg.color.PixelStorage {
|
|
||||||
const out = try zigimg.color.PixelStorage.init(allocator, .rgba32, in.len);
|
|
||||||
var i: usize = 0;
|
|
||||||
while (i < in.len) : (i += 1) {
|
|
||||||
out.rgba32[i] = zigimg.color.Rgba32{ .r = in[i].r, .g = in[i].g, .b = in[i].b, .a = 255 };
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) Position : vec4<f32>,
|
|
||||||
@location(0) fragUV : vec2<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
@vertex
|
|
||||||
fn vertex_main(@location(0) position : vec2<f32>,
|
|
||||||
@location(1) uv : vec2<f32>) -> VertexOutput {
|
|
||||||
var output : VertexOutput;
|
|
||||||
output.Position = vec4(position, 0, 1);
|
|
||||||
output.fragUV = uv;
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
@group(0) @binding(0) var mySampler: sampler;
|
|
||||||
@group(0) @binding(1) var myTexture: texture_2d<f32>;
|
|
||||||
|
|
||||||
@fragment
|
|
||||||
fn frag_main(@location(0) fragUV: vec2<f32>) -> @location(0) vec4<f32> {
|
|
||||||
return textureSample(myTexture, mySampler, fragUV);
|
|
||||||
}
|
|
||||||
|
|
@ -1,133 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
// Use experimental sysgpu graphics API
|
|
||||||
pub const use_sysgpu = true;
|
|
||||||
|
|
||||||
title_timer: core.Timer,
|
|
||||||
pipeline: *gpu.RenderPipeline,
|
|
||||||
texture: *gpu.Texture,
|
|
||||||
texture_view: *gpu.TextureView,
|
|
||||||
|
|
||||||
const sample_count = 4;
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{});
|
|
||||||
app.title_timer = try core.Timer.start();
|
|
||||||
|
|
||||||
const shader_module = core.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.wgsl"));
|
|
||||||
defer shader_module.release();
|
|
||||||
|
|
||||||
// Fragment state
|
|
||||||
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{
|
|
||||||
.fragment = &fragment,
|
|
||||||
.vertex = gpu.VertexState{
|
|
||||||
.module = shader_module,
|
|
||||||
.entry_point = "vertex_main",
|
|
||||||
},
|
|
||||||
.multisample = gpu.MultisampleState{
|
|
||||||
.count = sample_count,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
app.pipeline = core.device.createRenderPipeline(&pipeline_descriptor);
|
|
||||||
|
|
||||||
app.texture = core.device.createTexture(&gpu.Texture.Descriptor{
|
|
||||||
.size = gpu.Extent3D{
|
|
||||||
.width = core.descriptor.width,
|
|
||||||
.height = core.descriptor.height,
|
|
||||||
},
|
|
||||||
.sample_count = sample_count,
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
.usage = .{ .render_attachment = true },
|
|
||||||
});
|
|
||||||
app.texture_view = app.texture.createView(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
defer core.deinit();
|
|
||||||
|
|
||||||
app.pipeline.release();
|
|
||||||
app.texture.release();
|
|
||||||
app.texture_view.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(app: *App) !bool {
|
|
||||||
var iter = core.pollEvents();
|
|
||||||
while (iter.next()) |event| {
|
|
||||||
switch (event) {
|
|
||||||
.framebuffer_resize => |size| {
|
|
||||||
app.texture.release();
|
|
||||||
app.texture = core.device.createTexture(&gpu.Texture.Descriptor{
|
|
||||||
.size = gpu.Extent3D{
|
|
||||||
.width = size.width,
|
|
||||||
.height = size.height,
|
|
||||||
},
|
|
||||||
.sample_count = sample_count,
|
|
||||||
.format = core.descriptor.format,
|
|
||||||
.usage = .{ .render_attachment = true },
|
|
||||||
});
|
|
||||||
|
|
||||||
app.texture_view.release();
|
|
||||||
app.texture_view = app.texture.createView(null);
|
|
||||||
},
|
|
||||||
.close => return true,
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = app.texture_view,
|
|
||||||
.resolve_target = back_buffer_view,
|
|
||||||
.clear_value = std.mem.zeroes(gpu.Color),
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .discard,
|
|
||||||
};
|
|
||||||
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
const render_pass_info = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{color_attachment},
|
|
||||||
});
|
|
||||||
const pass = encoder.beginRenderPass(&render_pass_info);
|
|
||||||
pass.setPipeline(app.pipeline);
|
|
||||||
pass.draw(3, 1, 0, 0);
|
|
||||||
pass.end();
|
|
||||||
pass.release();
|
|
||||||
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
encoder.release();
|
|
||||||
|
|
||||||
const queue = core.queue;
|
|
||||||
queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
command.release();
|
|
||||||
core.swap_chain.present();
|
|
||||||
back_buffer_view.release();
|
|
||||||
|
|
||||||
// update the window title every second
|
|
||||||
if (app.title_timer.read() >= 1.0) {
|
|
||||||
app.title_timer.reset();
|
|
||||||
try core.printTitle("Triangle MSAA [ {d}fps ] [ Input {d}hz ]", .{
|
|
||||||
core.frameRate(),
|
|
||||||
core.inputRate(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
@vertex fn vertex_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);
|
|
||||||
}
|
|
||||||
|
|
||||||
@fragment fn frag_main() -> @location(0) vec4<f32> {
|
|
||||||
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
|
|
||||||
}
|
|
||||||
|
|
@ -1,96 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
// Use experimental sysgpu graphics API
|
|
||||||
pub const use_sysgpu = true;
|
|
||||||
|
|
||||||
title_timer: core.Timer,
|
|
||||||
pipeline: *gpu.RenderPipeline,
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{});
|
|
||||||
|
|
||||||
const shader_module = core.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.wgsl"));
|
|
||||||
defer shader_module.release();
|
|
||||||
|
|
||||||
// Fragment state
|
|
||||||
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{
|
|
||||||
.fragment = &fragment,
|
|
||||||
.vertex = gpu.VertexState{
|
|
||||||
.module = shader_module,
|
|
||||||
.entry_point = "vertex_main",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const pipeline = core.device.createRenderPipeline(&pipeline_descriptor);
|
|
||||||
|
|
||||||
app.* = .{ .title_timer = try core.Timer.start(), .pipeline = pipeline };
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
defer core.deinit();
|
|
||||||
app.pipeline.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(app: *App) !bool {
|
|
||||||
var iter = core.pollEvents();
|
|
||||||
while (iter.next()) |event| {
|
|
||||||
switch (event) {
|
|
||||||
.close => return true,
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const queue = core.queue;
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = back_buffer_view,
|
|
||||||
.clear_value = std.mem.zeroes(gpu.Color),
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
const render_pass_info = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{color_attachment},
|
|
||||||
});
|
|
||||||
const pass = encoder.beginRenderPass(&render_pass_info);
|
|
||||||
pass.setPipeline(app.pipeline);
|
|
||||||
pass.draw(3, 1, 0, 0);
|
|
||||||
pass.end();
|
|
||||||
pass.release();
|
|
||||||
|
|
||||||
var command = encoder.finish(null);
|
|
||||||
encoder.release();
|
|
||||||
|
|
||||||
queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
command.release();
|
|
||||||
core.swap_chain.present();
|
|
||||||
back_buffer_view.release();
|
|
||||||
|
|
||||||
// update the window title every second
|
|
||||||
if (app.title_timer.read() >= 1.0) {
|
|
||||||
app.title_timer.reset();
|
|
||||||
try core.printTitle("Triangle [ {d}fps ] [ Input {d}hz ]", .{
|
|
||||||
core.frameRate(),
|
|
||||||
core.inputRate(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
@vertex fn vertex_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);
|
|
||||||
}
|
|
||||||
|
|
||||||
@fragment fn frag_main() -> @location(0) vec4<f32> {
|
|
||||||
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
|
|
||||||
}
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
pub const Vertex = extern struct {
|
|
||||||
pos: @Vector(4, f32),
|
|
||||||
col: @Vector(4, f32),
|
|
||||||
uv: @Vector(2, f32),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const vertices = [_]Vertex{
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
};
|
|
||||||
|
|
@ -1,229 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
const mach = @import("mach");
|
|
||||||
const core = mach.core;
|
|
||||||
const gpu = mach.gpu;
|
|
||||||
|
|
||||||
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 gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
|
|
||||||
title_timer: core.Timer,
|
|
||||||
timer: core.Timer,
|
|
||||||
pipeline: *gpu.RenderPipeline,
|
|
||||||
vertex_buffer: *gpu.Buffer,
|
|
||||||
uniform_buffer: *gpu.Buffer,
|
|
||||||
bind_group1: *gpu.BindGroup,
|
|
||||||
bind_group2: *gpu.BindGroup,
|
|
||||||
|
|
||||||
pub const App = @This();
|
|
||||||
|
|
||||||
// Use experimental sysgpu graphics API
|
|
||||||
pub const use_sysgpu = true;
|
|
||||||
|
|
||||||
pub fn init(app: *App) !void {
|
|
||||||
try core.init(.{});
|
|
||||||
app.title_timer = try core.Timer.start();
|
|
||||||
app.timer = try core.Timer.start();
|
|
||||||
|
|
||||||
const shader_module = core.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.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.init(.{
|
|
||||||
.array_stride = @sizeOf(Vertex),
|
|
||||||
.step_mode = .vertex,
|
|
||||||
.attributes = &vertex_attributes,
|
|
||||||
});
|
|
||||||
|
|
||||||
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 bgle = gpu.BindGroupLayout.Entry.buffer(0, .{ .vertex = true }, .uniform, true, 0);
|
|
||||||
const bgl = core.device.createBindGroupLayout(
|
|
||||||
&gpu.BindGroupLayout.Descriptor.init(.{
|
|
||||||
.entries = &.{bgle},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
const bind_group_layouts = [_]*gpu.BindGroupLayout{bgl};
|
|
||||||
const pipeline_layout = core.device.createPipelineLayout(&gpu.PipelineLayout.Descriptor.init(.{
|
|
||||||
.bind_group_layouts = &bind_group_layouts,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const pipeline_descriptor = gpu.RenderPipeline.Descriptor{
|
|
||||||
.fragment = &fragment,
|
|
||||||
.layout = pipeline_layout,
|
|
||||||
.vertex = gpu.VertexState.init(.{
|
|
||||||
.module = shader_module,
|
|
||||||
.entry_point = "vertex_main",
|
|
||||||
.buffers = &.{vertex_buffer_layout},
|
|
||||||
}),
|
|
||||||
.primitive = .{
|
|
||||||
.cull_mode = .back,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const vertex_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .vertex = true },
|
|
||||||
.size = @sizeOf(Vertex) * vertices.len,
|
|
||||||
.mapped_at_creation = .true,
|
|
||||||
});
|
|
||||||
const vertex_mapped = vertex_buffer.getMappedRange(Vertex, 0, vertices.len);
|
|
||||||
@memcpy(vertex_mapped.?, vertices[0..]);
|
|
||||||
vertex_buffer.unmap();
|
|
||||||
|
|
||||||
// uniformBindGroup offset must be 256-byte aligned
|
|
||||||
const uniform_offset = 256;
|
|
||||||
const uniform_buffer = core.device.createBuffer(&.{
|
|
||||||
.usage = .{ .uniform = true, .copy_dst = true },
|
|
||||||
.size = @sizeOf(UniformBufferObject) + uniform_offset,
|
|
||||||
.mapped_at_creation = .false,
|
|
||||||
});
|
|
||||||
|
|
||||||
const bind_group1 = core.device.createBindGroup(
|
|
||||||
&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = bgl,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.buffer(0, uniform_buffer, 0, @sizeOf(UniformBufferObject), @sizeOf(UniformBufferObject)),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
const bind_group2 = core.device.createBindGroup(
|
|
||||||
&gpu.BindGroup.Descriptor.init(.{
|
|
||||||
.layout = bgl,
|
|
||||||
.entries = &.{
|
|
||||||
gpu.BindGroup.Entry.buffer(0, uniform_buffer, uniform_offset, @sizeOf(UniformBufferObject), @sizeOf(UniformBufferObject)),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
app.pipeline = core.device.createRenderPipeline(&pipeline_descriptor);
|
|
||||||
app.vertex_buffer = vertex_buffer;
|
|
||||||
app.uniform_buffer = uniform_buffer;
|
|
||||||
app.bind_group1 = bind_group1;
|
|
||||||
app.bind_group2 = bind_group2;
|
|
||||||
|
|
||||||
shader_module.release();
|
|
||||||
pipeline_layout.release();
|
|
||||||
bgl.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(app: *App) void {
|
|
||||||
defer _ = gpa.deinit();
|
|
||||||
defer core.deinit();
|
|
||||||
|
|
||||||
app.pipeline.release();
|
|
||||||
app.vertex_buffer.release();
|
|
||||||
app.uniform_buffer.release();
|
|
||||||
app.bind_group1.release();
|
|
||||||
app.bind_group2.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(app: *App) !bool {
|
|
||||||
var iter = core.pollEvents();
|
|
||||||
while (iter.next()) |event| {
|
|
||||||
switch (event) {
|
|
||||||
.key_press => |ev| {
|
|
||||||
if (ev.key == .space) return true;
|
|
||||||
},
|
|
||||||
.close => return true,
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const back_buffer_view = core.swap_chain.getCurrentTextureView().?;
|
|
||||||
const color_attachment = gpu.RenderPassColorAttachment{
|
|
||||||
.view = back_buffer_view,
|
|
||||||
.clear_value = std.mem.zeroes(gpu.Color),
|
|
||||||
.load_op = .clear,
|
|
||||||
.store_op = .store,
|
|
||||||
};
|
|
||||||
|
|
||||||
const encoder = core.device.createCommandEncoder(null);
|
|
||||||
const render_pass_info = gpu.RenderPassDescriptor.init(.{
|
|
||||||
.color_attachments = &.{color_attachment},
|
|
||||||
});
|
|
||||||
|
|
||||||
{
|
|
||||||
const time = app.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.Vec{ 0, -4, 2, 1 },
|
|
||||||
zm.Vec{ 0, 0, 0, 1 },
|
|
||||||
zm.Vec{ 0, 0, 1, 0 },
|
|
||||||
);
|
|
||||||
const proj = zm.perspectiveFovRh(
|
|
||||||
(2.0 * std.math.pi / 5.0),
|
|
||||||
@as(f32, @floatFromInt(core.descriptor.width)) / @as(f32, @floatFromInt(core.descriptor.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),
|
|
||||||
};
|
|
||||||
|
|
||||||
core.queue.writeBuffer(app.uniform_buffer, 0, &[_]UniformBufferObject{ubo1});
|
|
||||||
|
|
||||||
// bind_group2 offset
|
|
||||||
core.queue.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();
|
|
||||||
|
|
||||||
const queue = core.queue;
|
|
||||||
queue.submit(&[_]*gpu.CommandBuffer{command});
|
|
||||||
command.release();
|
|
||||||
core.swap_chain.present();
|
|
||||||
back_buffer_view.release();
|
|
||||||
|
|
||||||
// update the window title every second
|
|
||||||
if (app.title_timer.read() >= 1.0) {
|
|
||||||
app.title_timer.reset();
|
|
||||||
try core.printTitle("Two Cubes [ {d}fps ] [ Input {d}hz ]", .{
|
|
||||||
core.frameRate(),
|
|
||||||
core.inputRate(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
@group(0) @binding(0) var<uniform> ubo : mat4x4<f32>;
|
|
||||||
struct VertexOut {
|
|
||||||
@builtin(position) position_clip : vec4<f32>,
|
|
||||||
@location(0) fragUV : vec2<f32>,
|
|
||||||
@location(1) fragPosition: vec4<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
@vertex fn vertex_main(
|
|
||||||
@location(0) position : vec4<f32>,
|
|
||||||
@location(1) uv: vec2<f32>
|
|
||||||
) -> VertexOut {
|
|
||||||
var output : VertexOut;
|
|
||||||
output.position_clip = position * ubo;
|
|
||||||
output.fragUV = uv;
|
|
||||||
output.fragPosition = 0.5 * (position + vec4<f32>(1.0, 1.0, 1.0, 1.0));
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
@fragment fn frag_main(
|
|
||||||
@location(0) fragUV: vec2<f32>,
|
|
||||||
@location(1) fragPosition: vec4<f32>
|
|
||||||
) -> @location(0) vec4<f32> {
|
|
||||||
return fragPosition;
|
|
||||||
}
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
pub const Vertex = extern struct {
|
|
||||||
pos: @Vector(4, f32),
|
|
||||||
col: @Vector(4, f32),
|
|
||||||
uv: @Vector(2, f32),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const vertices = [_]Vertex{
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, 1, 1 }, .col = .{ 0, 1, 1, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ -1, -1, 1, 1 }, .col = .{ 0, 0, 1, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, 1, 1 }, .col = .{ 1, 0, 1, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, 1, 1 }, .col = .{ 1, 1, 1, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, -1, -1, 1 }, .col = .{ 0, 0, 0, 1 }, .uv = .{ 1, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
.{ .pos = .{ 1, 1, -1, 1 }, .col = .{ 1, 1, 0, 1 }, .uv = .{ 0, 0 } },
|
|
||||||
.{ .pos = .{ 1, -1, -1, 1 }, .col = .{ 1, 0, 0, 1 }, .uv = .{ 0, 1 } },
|
|
||||||
.{ .pos = .{ -1, 1, -1, 1 }, .col = .{ 0, 1, 0, 1 }, .uv = .{ 1, 0 } },
|
|
||||||
};
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue