Let there be proper shader layouts n stuff \o/

This commit is contained in:
abux 2026-07-31 19:25:59 +01:00
parent ccde4427be
commit b03bb1a70a
21 changed files with 418 additions and 709 deletions

View file

@ -21,8 +21,8 @@
.hash = "gtl-0.0.0-e3QoJMRYAADChgXn2sogTxsbUpEBSHjOGZeoEx4ccMMD", .hash = "gtl-0.0.0-e3QoJMRYAADChgXn2sogTxsbUpEBSHjOGZeoEx4ccMMD",
}, },
.NYXMath = .{ .NYXMath = .{
.url = "git+http://192.168.0.11:25569/abux/NYXMath.git#58d57b61f3fa25e234af1af3e1e56bb656f4d77d", .url = "git+http://192.168.0.11:25569/abux/NYXMath.git#cb7b54995bdb46321f112bba2a704d58cf53aeb1",
.hash = "NYXMath-0.0.0-BD6ST8zOAAB1dgmaGKShXtKmGTuYqgiDpAY2faHM_HnV", .hash = "NYXMath-0.0.0-BD6ST9rOAAA0uSpPK_woZrta3nTh0f5w7hFewJ-e2Kv1",
}, },
}, },

View file

@ -1,83 +0,0 @@
// ----------------------------------------------------
// ----------------------------------------------------
struct VertexInput {
@location(0) position: vec3f,
@location(1) normal: vec3f,
@location(2) uv: vec2f,
};
// ----------------------------------------------------
// ----------------------------------------------------
struct VertexOutput {
@builtin(position) position: vec4f,
@location(0) uv: vec2f,
@location(1) normal: vec3f,
};
// ----------------------------------------------------
// ----------------------------------------------------
struct Material {
albedo: vec4f,
metallic: f32,
roughness: f32,
_pad0: vec2f,
};
// ----------------------------------------------------
// ----------------------------------------------------
struct CameraUniform {
view: mat4x4<f32>,
projection: mat4x4<f32>,
view_projection: mat4x4<f32>,
camera_position: vec3<f32>,
_pad: f32,
};
// ----------------------------------------------------
// ----------------------------------------------------
struct ModelUniform {
model: mat4x4<f32>,
};
//
// UNIFORMS
//
@group(0) @binding(0)
var<uniform> camera: CameraUniform;
@group(1) @binding(0)
var<uniform> model: ModelUniform;
@group(2) @binding(0)
var<uniform> material: Material;
//
// VERTEX
//
@vertex
fn vs_main(in: VertexInput) -> VertexOutput {
// --- # ---
var out: VertexOutput;
// --- # ---
out.position = vec4f(in.position, 1.0);
out.uv = in.uv;
out.normal = in.normal;
// --- # ---
return out;
}
//
// FRAGMENT
//
@fragment
fn fs_main(out: VertexOutput) -> @location(0) vec4f {
var final_color: vec4f = material.albedo;
final_color.x += out.uv.x * 0.2;
final_color.y += out.uv.y * 0.2;
return vec4f(final_color);
}

View file

@ -127,6 +127,38 @@ pub fn getOrCreate(
}, },
}, },
.cube => &.{
.{ .position = .{ -0.5, -0.5, 0.5 }, .normal = .{ 0, 0, 1 }, .uv = .{ 0, 1 } },
.{ .position = .{ 0.5, -0.5, 0.5 }, .normal = .{ 0, 0, 1 }, .uv = .{ 1, 1 } },
.{ .position = .{ 0.5, 0.5, 0.5 }, .normal = .{ 0, 0, 1 }, .uv = .{ 1, 0 } },
.{ .position = .{ -0.5, 0.5, 0.5 }, .normal = .{ 0, 0, 1 }, .uv = .{ 0, 0 } },
.{ .position = .{ 0.5, -0.5, -0.5 }, .normal = .{ 0, 0, -1 }, .uv = .{ 0, 1 } },
.{ .position = .{ -0.5, -0.5, -0.5 }, .normal = .{ 0, 0, -1 }, .uv = .{ 1, 1 } },
.{ .position = .{ -0.5, 0.5, -0.5 }, .normal = .{ 0, 0, -1 }, .uv = .{ 1, 0 } },
.{ .position = .{ 0.5, 0.5, -0.5 }, .normal = .{ 0, 0, -1 }, .uv = .{ 0, 0 } },
.{ .position = .{ -0.5, -0.5, -0.5 }, .normal = .{ -1, 0, 0 }, .uv = .{ 0, 1 } },
.{ .position = .{ -0.5, -0.5, 0.5 }, .normal = .{ -1, 0, 0 }, .uv = .{ 1, 1 } },
.{ .position = .{ -0.5, 0.5, 0.5 }, .normal = .{ -1, 0, 0 }, .uv = .{ 1, 0 } },
.{ .position = .{ -0.5, 0.5, -0.5 }, .normal = .{ -1, 0, 0 }, .uv = .{ 0, 0 } },
.{ .position = .{ 0.5, -0.5, 0.5 }, .normal = .{ 1, 0, 0 }, .uv = .{ 0, 1 } },
.{ .position = .{ 0.5, -0.5, -0.5 }, .normal = .{ 1, 0, 0 }, .uv = .{ 1, 1 } },
.{ .position = .{ 0.5, 0.5, -0.5 }, .normal = .{ 1, 0, 0 }, .uv = .{ 1, 0 } },
.{ .position = .{ 0.5, 0.5, 0.5 }, .normal = .{ 1, 0, 0 }, .uv = .{ 0, 0 } },
.{ .position = .{ -0.5, 0.5, 0.5 }, .normal = .{ 0, 1, 0 }, .uv = .{ 0, 1 } },
.{ .position = .{ 0.5, 0.5, 0.5 }, .normal = .{ 0, 1, 0 }, .uv = .{ 1, 1 } },
.{ .position = .{ 0.5, 0.5, -0.5 }, .normal = .{ 0, 1, 0 }, .uv = .{ 1, 0 } },
.{ .position = .{ -0.5, 0.5, -0.5 }, .normal = .{ 0, 1, 0 }, .uv = .{ 0, 0 } },
.{ .position = .{ -0.5, -0.5, -0.5 }, .normal = .{ 0, -1, 0 }, .uv = .{ 0, 1 } },
.{ .position = .{ 0.5, -0.5, -0.5 }, .normal = .{ 0, -1, 0 }, .uv = .{ 1, 1 } },
.{ .position = .{ 0.5, -0.5, 0.5 }, .normal = .{ 0, -1, 0 }, .uv = .{ 1, 0 } },
.{ .position = .{ -0.5, -0.5, 0.5 }, .normal = .{ 0, -1, 0 }, .uv = .{ 0, 0 } },
},
else => &.{}, else => &.{},
}; };
@ -141,6 +173,15 @@ pub fn getOrCreate(
0, 1, 2, 0, 1, 2,
}, },
.cube => &.{
0, 1, 2, 0, 2, 3,
4, 5, 6, 4, 6, 7,
8, 9, 10, 8, 10, 11,
12, 13, 14, 12, 14, 15,
16, 17, 18, 16, 18, 19,
20, 21, 22, 20, 22, 23,
},
else => &.{}, else => &.{},
}; };

View file

@ -1,332 +0,0 @@
//! ----------------------------------------------------
//! TODO: Move [materials, materials_gpu and GPUMaterial somewhere else]
//! Depricated
//! ----------------------------------------------------
const std = @import("std");
const gtl = @import("gtl");
const wgpu = @import("wgpu");
const Self = @This();
const Material = @import("../../renderer/template/material.zig");
const ShaderCache = @import("shader_cache.zig");
const ShaderID = ShaderCache.ShaderID;
const Vertex = @import("mesh_cache.zig").Vertex;
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const GPUPipeline = struct {
raw: *wgpu.WGPURenderPipelineImpl,
layout: *wgpu.WGPUPipelineLayoutImpl,
bind_group_layouts: [3]*wgpu.WGPUBindGroupLayoutImpl,
};
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const GPUMaterial = struct {
buffer: *wgpu.WGPUBufferImpl,
bind_group: *wgpu.WGPUBindGroupImpl,
dirty: bool = true,
};
/// ----------------------------------------------------
/// TODO: Mabe mabe switch to it, someday
/// ----------------------------------------------------
pub const GPUUniform = struct {
buffer: wgpu.WGPUBuffer,
bind_group: wgpu.WGPUBindGroup,
dirty: bool = true,
};
//
// FIELDS
//
pipelines: gtl.KeyedSlotMap(Material.Unique, GPUPipeline),
materials: gtl.SlotMap(Material),
materials_gpu: gtl.KeyedSlotMap(gtl.Handle(Material), GPUMaterial),
shader_cache: *ShaderCache,
queue: *wgpu.WGPUQueueImpl,
device: *wgpu.WGPUDeviceImpl,
alloc: std.mem.Allocator,
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn init(
shader_cache: *ShaderCache,
queue: *wgpu.WGPUQueueImpl,
device: *wgpu.WGPUDeviceImpl,
alloc: std.mem.Allocator,
) Self {
return .{
.pipelines = .init(alloc),
.materials = .init(alloc),
.materials_gpu = .init(alloc),
.shader_cache = shader_cache,
.queue = queue,
.device = device,
.alloc = alloc,
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn deinit(self: *Self) void {
// --- FREE PIPELINES ---
for (self.pipelines.items()) |entry| {
wgpu.wgpuPipelineLayoutRelease(entry.value.layout);
wgpu.wgpuRenderPipelineRelease(entry.value.raw);
}
// --- FREE MATERIALS ---
for (self.materials_gpu.items()) |entry| {
wgpu.wgpuBufferRelease(entry.value.buffer);
wgpu.wgpuBindGroupRelease(entry.value.bind_group);
}
// --- # ---
self.pipelines.deinit();
self.materials.deinit();
self.materials_gpu.deinit();
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn getOrCreateMaterial(
self: *Self,
material_handle: gtl.Handle(Material),
) !*GPUMaterial {
return try self.materials_gpu.getOrPut(
material_handle,
blk: {
// --- # ---
if (self.materials_gpu.getByKey(material_handle)) |v| return v;
const material = self.materials.get(material_handle) orelse return error.MaterialNotFound;
// --- ENSURE PIPELINE ---
const pipeline = try self.getOrCreatePipeline(material);
// --- UNIFORM ---
const uniform = wgpu.wgpuDeviceCreateBuffer(self.device, &wgpu.WGPUBufferDescriptor{
.usage = wgpu.WGPUBufferUsage_CopyDst | wgpu.WGPUBufferUsage_Uniform,
.size = @sizeOf(Material.Style),
}) orelse return error.Buffer;
// --- WRITE TO UNIFORM ---
wgpu.wgpuQueueWriteBuffer(
self.queue,
uniform,
0,
&material.style,
@sizeOf(Material.Style),
);
// --- BIND GROUP ---
const bind_group = wgpu.wgpuDeviceCreateBindGroup(self.device, &wgpu.WGPUBindGroupDescriptor{
.layout = pipeline.bind_group_layouts[0],
.entryCount = 1,
.entries = &[_]wgpu.WGPUBindGroupEntry{
.{
.binding = 0,
.buffer = uniform,
.offset = 0,
.size = @sizeOf(Material.Style),
},
},
}) orelse return error.BindGroup;
// --- RESULT ---
break :blk .{
.buffer = uniform,
.bind_group = bind_group,
.dirty = false,
};
},
);
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn getOrCreatePipeline(
self: *Self,
material: *const Material,
) !*GPUPipeline {
return try self.pipelines.getOrPut(material.unique, blk: {
// --- # ---
if (self.pipelines.getByKey(material.unique)) |v| return v;
//
// LAYOUTS
//
// --- CAMERA ---
const camera_layout = wgpu.wgpuDeviceCreateBindGroupLayout(self.device, &wgpu.WGPUBindGroupLayoutDescriptor{
.entryCount = 1,
.entries = &[_]wgpu.WGPUBindGroupLayoutEntry{
.{
.binding = 0,
.visibility = wgpu.WGPUShaderStage_Vertex | wgpu.WGPUShaderStage_Fragment,
.buffer = .{
.type = wgpu.WGPUBufferBindingType_Uniform,
.minBindingSize = @sizeOf(Material.Style),
.hasDynamicOffset = 0,
},
},
},
}) orelse return error.BindGroupLayout;
// --- MODEL ---
const model_layout = wgpu.wgpuDeviceCreateBindGroupLayout(self.device, &wgpu.WGPUBindGroupLayoutDescriptor{
.entryCount = 1,
.entries = &[_]wgpu.WGPUBindGroupLayoutEntry{
.{
.binding = 0,
.visibility = wgpu.WGPUShaderStage_Vertex | wgpu.WGPUShaderStage_Fragment,
.buffer = .{
.type = wgpu.WGPUBufferBindingType_Uniform,
.minBindingSize = @sizeOf(Material.Style),
.hasDynamicOffset = 0,
},
},
},
}) orelse return error.BindGroupLayout;
// --- MATERIAL ---
const material_layout = wgpu.wgpuDeviceCreateBindGroupLayout(self.device, &wgpu.WGPUBindGroupLayoutDescriptor{
.entryCount = 1,
.entries = &[_]wgpu.WGPUBindGroupLayoutEntry{
.{
.binding = 0,
.visibility = wgpu.WGPUShaderStage_Vertex | wgpu.WGPUShaderStage_Fragment,
.buffer = .{
.type = wgpu.WGPUBufferBindingType_Uniform,
.minBindingSize = @sizeOf(Material.Style),
.hasDynamicOffset = 0,
},
},
},
}) orelse return error.BindGroupLayout;
var bind_group_layouts = [_]wgpu.WGPUBindGroupLayout{
camera_layout,
model_layout,
material_layout,
};
// --- PIPELINE LAYOUT ---
const pipeline_layout = wgpu.wgpuDeviceCreatePipelineLayout(
self.device,
&wgpu.WGPUPipelineLayoutDescriptor{
.bindGroupLayoutCount = 3,
.bindGroupLayouts = &bind_group_layouts,
},
) orelse return error.Layout;
// --- CREATE PIPELINE ---
const pipeline = try self.createPipeline(
material.unique,
pipeline_layout,
);
// --- RESULT ---
break :blk .{
.raw = pipeline,
.layout = pipeline_layout,
.bind_group_layouts = .{
camera_layout,
model_layout,
material_layout,
},
};
});
}
/// ----------------------------------------------------
/// ----------------------------------------------------
fn createPipeline(
self: *Self,
key: Material.Unique,
layout: *wgpu.WGPUPipelineLayoutImpl,
) !*wgpu.WGPURenderPipelineImpl {
// --- ENSURE SHADER MODULE ---
const shader_module = try self.shader_cache.getOrCreate(
key.shader,
);
// --- RESULT ---
return wgpu.wgpuDeviceCreateRenderPipeline(self.device, &wgpu.WGPURenderPipelineDescriptor{
.label = .{ .data = @tagName(key.shader), .length = wgpu.WGPU_STRLEN },
.layout = layout,
.vertex = .{
.module = shader_module.raw,
.entryPoint = .{ .data = "vs_main", .length = wgpu.WGPU_STRLEN },
.bufferCount = 1,
.buffers = &wgpu.WGPUVertexBufferLayout{
.attributeCount = 3,
.attributes = &[_]wgpu.WGPUVertexAttribute{ // TODO: Mabe mabe allow for custom, mabe mabe later tho
.{
.format = wgpu.WGPUVertexFormat_Float32x3,
.offset = @offsetOf(Vertex, "position"),
.shaderLocation = 0,
},
.{
.format = wgpu.WGPUVertexFormat_Float32x3,
.offset = @offsetOf(Vertex, "normal"),
.shaderLocation = 1,
},
.{
.format = wgpu.WGPUVertexFormat_Float32x2,
.offset = @offsetOf(Vertex, "uv"),
.shaderLocation = 2,
},
},
.arrayStride = @sizeOf(Vertex),
.stepMode = wgpu.WGPUVertexStepMode_Vertex,
},
},
.fragment = &wgpu.WGPUFragmentState{
.module = shader_module.raw,
.entryPoint = .{ .data = "fs_main", .length = wgpu.WGPU_STRLEN },
.targetCount = 1,
.targets = &wgpu.WGPUColorTargetState{
.format = 28, // FIX: Use proper format
.blend = &wgpu.WGPUBlendState{
.color = .{
.srcFactor = wgpu.WGPUBlendFactor_SrcAlpha,
.dstFactor = wgpu.WGPUBlendFactor_OneMinusSrcAlpha,
.operation = wgpu.WGPUBlendOperation_Add,
},
.alpha = .{
.srcFactor = wgpu.WGPUBlendFactor_Zero,
.dstFactor = wgpu.WGPUBlendFactor_One,
.operation = wgpu.WGPUBlendOperation_Add,
},
},
.writeMask = wgpu.WGPUColorWriteMask_All,
},
},
.primitive = .{
.topology = switch (key.topology) {
.triangle_list => wgpu.WGPUPrimitiveTopology_TriangleList,
.point_list => wgpu.WGPUPrimitiveTopology_PointList,
.line_list => wgpu.WGPUPrimitiveTopology_LineList,
},
.frontFace = switch (key.front_face) {
.ccw => wgpu.WGPUFrontFace_CCW,
.cw => wgpu.WGPUFrontFace_CW,
},
.cullMode = switch (key.cull_mode) {
.back => wgpu.WGPUCullMode_Back,
.front => wgpu.WGPUCullMode_Front,
.none => wgpu.WGPUCullMode_None,
},
},
.multisample = .{
.count = 1,
.mask = ~@as(u32, 0),
.alphaToCoverageEnabled = 0,
},
}) orelse {
return error.PipelineCreationFailed;
};
}

View file

@ -1,103 +0,0 @@
//! ----------------------------------------------------
//! ----------------------------------------------------
const std = @import("std");
const gtl = @import("gtl");
const wgpu = @import("wgpu");
const Self = @This();
//
// EMBEDED
//
const PBRCode = @embedFile("embeded/shaders/pbr.wgsl");
const UnlitCode = @embedFile("embeded/shaders/pbr.wgsl");
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const ShaderID = union(enum) {
pbr,
unlit,
custom: u32,
};
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const GPUShader = struct {
raw: *wgpu.WGPUShaderModuleImpl,
};
//
// FIELDS
//
modules: gtl.KeyedSlotMap(ShaderID, GPUShader),
custom_paths: std.ArrayList([]const u8),
device: *wgpu.WGPUDeviceImpl,
alloc: std.mem.Allocator,
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn init(
device: *wgpu.WGPUDeviceImpl,
alloc: std.mem.Allocator,
) Self {
return .{
.modules = .init(alloc),
.custom_paths = .empty,
.device = device,
.alloc = alloc,
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn deinit(self: *Self) void {
// --- FREE SHADERS ---
for (self.modules.items()) |entry| {
wgpu.wgpuShaderModuleRelease(entry.value.raw);
}
// --- # ---
self.modules.deinit();
self.custom_paths.deinit(self.alloc);
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn getOrCreate(
self: *Self,
key: ShaderID,
) !*GPUShader {
return try self.modules.getOrPut(key, .{
.raw = try self.createShader(key),
});
}
/// ----------------------------------------------------
/// ----------------------------------------------------
fn createShader(
self: *Self,
key: ShaderID,
) !*wgpu.WGPUShaderModuleImpl {
// --- CODE ---
const code: []const u8 = switch (key) {
.pbr => PBRCode,
.unlit => UnlitCode,
.custom => |v| self.custom_paths.items[@intCast(v)], // TODO: Handle out of bounds
};
// --- SOURCE ---
var source: wgpu.WGPUShaderSourceWGSL = .{
.chain = .{ .sType = wgpu.WGPUSType_ShaderSourceWGSL },
.code = .{ .data = code.ptr, .length = wgpu.WGPU_STRLEN },
};
// --- RESULT ---
return wgpu.wgpuDeviceCreateShaderModule(self.device, &wgpu.WGPUShaderModuleDescriptor{
.label = .{ .data = @tagName(key), .length = wgpu.WGPU_STRLEN },
.nextInChain = &source.chain,
}) orelse {
return error.ShaderModuleCreationFailed;
};
}

View file

@ -11,7 +11,7 @@ projection: union(enum) {
far: f32 = 4000.0, far: f32 = 4000.0,
near: f32 = 0.05, near: f32 = 0.05,
}, },
} = .{ .ortho = .{} }, },
target: union(enum) { target: union(enum) {
window, window,

View file

@ -2,4 +2,4 @@ const math = @import("math");
position: math.Vec3f = .as(0), position: math.Vec3f = .as(0),
rotation: math.Quat = .identity(), rotation: math.Quat = .identity(),
scale: math.Vec3f = .as(0), scale: math.Vec3f = .as(1),

View file

@ -5,6 +5,7 @@
// PRIVATE // PRIVATE
// //
const std = @import("std");
const App = @import("app"); const App = @import("app");
const Camera = @import("../common/component/camera.zig"); const Camera = @import("../common/component/camera.zig");
@ -34,6 +35,8 @@ fn build(
try app.schedules.addAfter(App.stage.init, App.stage.init, start); try app.schedules.addAfter(App.stage.init, App.stage.init, start);
} }
const FlyCamera = struct {};
/// ---------------------------------------------------- /// ----------------------------------------------------
/// ---------------------------------------------------- /// ----------------------------------------------------
fn start(app: *App) !void { fn start(app: *App) !void {
@ -44,36 +47,68 @@ fn start(app: *App) !void {
// --- CAMERA --- // --- CAMERA ---
try app.world.spawn(.{ try app.world.spawn(.{
Camera{}, FlyCamera{},
Transform3D{}, Camera{
.projection = .{
.perspective = .{},
},
},
Transform3D{
.position = .new(.{ 0, -2.5, -6.5 }),
.rotation = .fromEuler(.init(
-std.math.degreesToRadians(30),
0,
0,
)),
},
}); });
// --- # --- // --- # ---
try app.world.spawn(.{ try app.world.spawn(.{
Mesh{ .value = .square }, Mesh{ .value = .cube },
Material{ Material{
.value = try render_world.materials.put(.{ .value = try render_world.materials.put(.{
.style = .{ .style = .{
.albedo = .{ 0.8, 0.2, 0.2, 1.0 }, .albedo = .{ 0.8, 0.2, 0.2, 1.0 },
}, },
.unique = .{ .unique = .{
.topology = .line_list, .cull_mode = .none,
}, },
}), }),
}, },
Transform3D{}, Transform3D{},
}); });
// --- # --- const count: usize = 100;
try app.world.spawn(.{ const radius: f32 = 5.0;
Mesh{ .value = .triangle },
Material{ for (0..count) |i| {
.value = try render_world.materials.put(.{ const angle = (@as(f32, @floatFromInt(i)) / @as(f32, @floatFromInt(count))) * std.math.tau;
.style = .{
.albedo = .{ 0.2, 0.2, 0.8, 1 }, try app.world.spawn(.{
}, Mesh{ .value = .cube },
}), Material{
}, .value = try render_world.materials.put(.{
Transform3D{}, .style = .{
}); .albedo = .{ 0.2, 0.2, 0.8, 1 },
},
.unique = .{
.cull_mode = .none,
},
}),
},
Transform3D{
.position = .new(.{
@cos(angle) * radius,
0,
@sin(angle) * radius,
}),
.rotation = .fromEuler(.init(
std.math.degreesToRadians(180),
0,
0,
)),
},
});
}
} }

View file

@ -14,29 +14,26 @@ struct VertexOutput {
@location(1) normal: vec3f, @location(1) normal: vec3f,
}; };
// ----------------------------------------------------
// ----------------------------------------------------
struct Material {
albedo: vec4f,
metallic: f32,
roughness: f32,
_pad0: vec2f,
};
// ---------------------------------------------------- // ----------------------------------------------------
// ---------------------------------------------------- // ----------------------------------------------------
struct CameraUniform { struct CameraUniform {
view: mat4x4<f32>, view: mat4x4<f32>,
projection: mat4x4<f32>, projection: mat4x4<f32>,
view_projection: mat4x4<f32>,
camera_position: vec3<f32>,
_pad: f32,
}; };
// ---------------------------------------------------- // ----------------------------------------------------
// ---------------------------------------------------- // ----------------------------------------------------
struct ModelUniform { struct ModelUniform {
model: mat4x4<f32>, data: mat4x4<f32>,
};
// ----------------------------------------------------
// ----------------------------------------------------
struct Material {
albedo: vec4f,
metallic: f32,
roughness: f32,
_pad0: vec2f,
}; };
// //
@ -62,7 +59,7 @@ fn vs_main(in: VertexInput) -> VertexOutput {
var out: VertexOutput; var out: VertexOutput;
// --- # --- // --- # ---
out.position = vec4f(in.position, 1.0); out.position = camera.projection * camera.view * model.data * vec4f(in.position, 1.0);
out.uv = in.uv; out.uv = in.uv;
out.normal = in.normal; out.normal = in.normal;

View file

@ -0,0 +1,5 @@
const wgpu = @import("wgpu");
camera: *wgpu.WGPUBindGroupLayoutImpl,
model: *wgpu.WGPUBindGroupLayoutImpl,
material: *wgpu.WGPUBindGroupLayoutImpl,

View file

@ -5,6 +5,7 @@
const std = @import("std"); const std = @import("std");
const gtl = @import("gtl"); const gtl = @import("gtl");
const wgpu = @import("wgpu"); const wgpu = @import("wgpu");
pub const Entity = u64; // TODO: You lazy retard
// --- CAMERA --- // --- CAMERA ---
pub const GPUCamera = @import("../template/gpu_camera.zig"); pub const GPUCamera = @import("../template/gpu_camera.zig");
@ -23,7 +24,8 @@ pub const Pipeline = Material.Unique;
pub const GPUPipeline = @import("../template/gpu_pipeline.zig"); pub const GPUPipeline = @import("../template/gpu_pipeline.zig");
// --- OBJECT --- // --- OBJECT ---
pub const Entity = u64; // TODO: You lazy retard pub const MeshID = @import("../../cache/resource/mesh_cache.zig").MeshID;
pub const GPUObject = @import("../template/gpu_object.zig");
pub const FrameObject = @import("../template/frame_object.zig"); pub const FrameObject = @import("../template/frame_object.zig");
// //
@ -38,6 +40,7 @@ pub const UnlitCode = @embedFile("embeded/shaders/pbr.wgsl");
// //
gpu_cameras: gtl.KeyedSlotMap(Entity, GPUCamera), gpu_cameras: gtl.KeyedSlotMap(Entity, GPUCamera),
gpu_objects: gtl.KeyedSlotMap(Entity, GPUObject),
gpu_shaders: gtl.KeyedSlotMap(ShaderID, GPUShader), gpu_shaders: gtl.KeyedSlotMap(ShaderID, GPUShader),
gpu_pipelines: gtl.KeyedSlotMap(Pipeline, GPUPipeline), gpu_pipelines: gtl.KeyedSlotMap(Pipeline, GPUPipeline),
gpu_materials: gtl.KeyedSlotMap(gtl.Handle(Material), GPUMaterial), gpu_materials: gtl.KeyedSlotMap(gtl.Handle(Material), GPUMaterial),

View file

@ -16,6 +16,7 @@ const Camera = struct {};
pub const DrawCMD = union(enum) { pub const DrawCMD = union(enum) {
draw: struct { draw: struct {
mesh: Mesh, mesh: Mesh,
entity: u64,
material: Handle(Material), material: Handle(Material),
}, },

View file

@ -17,6 +17,8 @@ const present = @import("system/present.zig");
// //
const Renderer = @import("resource/renderer.zig"); const Renderer = @import("resource/renderer.zig");
const RenderWorld = @import("resource/render_world.zig");
const RenderLayout = @import("resource/render_layout.zig");
/// ---------------------------------------------------- /// ----------------------------------------------------
/// ---------------------------------------------------- /// ----------------------------------------------------
@ -41,9 +43,11 @@ fn build(
// --- PRE DRAW --- // --- PRE DRAW ---
try app.schedules.addManyBefore(App.stage.draw, App.stage.draw, &.{ try app.schedules.addManyBefore(App.stage.draw, App.stage.draw, &.{
prepare.cameras,
prepare.shaders, prepare.shaders,
prepare.pipelines, prepare.pipelines,
prepare.cameras,
prepare.objects,
prepare.materials, prepare.materials,
}); });

View file

@ -22,7 +22,7 @@ pub fn begin(app: *App) !void {
// --- # --- // --- # ---
res.renderer.clear(); res.renderer.clear();
// --- DRAW --- // --- # ---
for (res.render_world.frame_views.items) |view| { for (res.render_world.frame_views.items) |view| {
// --- NEW PASS --- // --- NEW PASS ---
try res.renderer.draw(.{ try res.renderer.draw(.{
@ -33,11 +33,12 @@ pub fn begin(app: *App) !void {
}); });
defer res.renderer.draw(.end_pass) catch unreachable; defer res.renderer.draw(.end_pass) catch unreachable;
// --- # --- // --- DRAW ---
for (res.render_world.frame_objects.items) |obj| { for (res.render_world.frame_objects.items) |obj| {
try res.renderer.draw(.{ try res.renderer.draw(.{
.draw = .{ .draw = .{
.material = obj.material, .material = obj.material,
.entity = obj.entity,
.mesh = obj.mesh, .mesh = obj.mesh,
}, },
}); });
@ -47,48 +48,6 @@ pub fn begin(app: *App) !void {
// --- YES --- // --- YES ---
res.render_world.frame_views.clearRetainingCapacity(); res.render_world.frame_views.clearRetainingCapacity();
res.render_world.frame_objects.clearRetainingCapacity(); res.render_world.frame_objects.clearRetainingCapacity();
// { // TEST
// // --- DRAW ---
// try res.renderer.draw(.{
// .begin_pass = .{
// .camera = undefined,
// .color = .{ 0.001, 0.001, 0.001, 1.0 },
// },
// });
// defer res.renderer.draw(.end_pass) catch unreachable;
//
// // --- # ---
// try res.renderer.draw(.{
// .draw = .{
// .mesh = .square,
// .material = .{
// .style = .{
// .albedo = .{ 0.2, 0.8, 0.2, 1.0 },
// },
// },
// },
// });
// }
//
// { // TEST
// // --- DRAW ---
// try res.renderer.draw(.{
// .begin_pass = .{
// .camera = undefined,
// .load_op = .load,
// },
// });
// defer res.renderer.draw(.end_pass) catch unreachable;
//
// // --- # ---
// try res.renderer.draw(.{
// .draw = .{
// .mesh = .triangle,
// .material = .{},
// },
// });
// }
} }
/// ---------------------------------------------------- /// ----------------------------------------------------

View file

@ -5,12 +5,24 @@ const wgpu = @import("wgpu");
const App = @import("app"); const App = @import("app");
const Zla = @import("zla"); const Zla = @import("zla");
const WGPUDevice = @import("../../device/resource/wgpu_device.zig");
const Renderer = @import("../resource/renderer.zig"); const Renderer = @import("../resource/renderer.zig");
const RenderWorld = @import("../resource/render_world.zig"); const RenderWorld = @import("../resource/render_world.zig");
const RenderLayout = @import("../resource/render_layout.zig");
// --- TEMPLATES ---
const TMaterial = @import("../template/material.zig");
const TCamera = @import("../template/gpu_camera.zig");
const TMesh = @import("../template/gpu_object.zig");
/// ---------------------------------------------------- /// ----------------------------------------------------
/// ---------------------------------------------------- /// ----------------------------------------------------
pub fn init(app: *App) !void { pub fn init(app: *App) !void {
// --- # ---
const res = try app.resources.getMany(struct {
device: *WGPUDevice,
});
// --- # --- // --- # ---
try app.resources.set(Renderer.init( try app.resources.set(Renderer.init(
app.alloc, app.alloc,
@ -19,6 +31,7 @@ pub fn init(app: *App) !void {
// --- # --- // --- # ---
try app.resources.set(RenderWorld{ try app.resources.set(RenderWorld{
.gpu_cameras = .init(app.alloc), .gpu_cameras = .init(app.alloc),
.gpu_objects = .init(app.alloc),
.gpu_shaders = .init(app.alloc), .gpu_shaders = .init(app.alloc),
.gpu_materials = .init(app.alloc), .gpu_materials = .init(app.alloc),
.gpu_pipelines = .init(app.alloc), .gpu_pipelines = .init(app.alloc),
@ -28,6 +41,54 @@ pub fn init(app: *App) !void {
.materials = .init(app.alloc), .materials = .init(app.alloc),
}); });
// --- # ---
try app.resources.set(RenderLayout{
.camera = wgpu.wgpuDeviceCreateBindGroupLayout(res.device.raw, &wgpu.WGPUBindGroupLayoutDescriptor{
.entryCount = 1,
.entries = &[_]wgpu.WGPUBindGroupLayoutEntry{
.{
.binding = 0,
.visibility = wgpu.WGPUShaderStage_Vertex | wgpu.WGPUShaderStage_Fragment,
.buffer = .{
.type = wgpu.WGPUBufferBindingType_Uniform,
.minBindingSize = @sizeOf(TCamera.Uniform),
.hasDynamicOffset = 0,
},
},
},
}) orelse return error.CameraBindGroupLayout,
.model = wgpu.wgpuDeviceCreateBindGroupLayout(res.device.raw, &wgpu.WGPUBindGroupLayoutDescriptor{
.entryCount = 1,
.entries = &[_]wgpu.WGPUBindGroupLayoutEntry{
.{
.binding = 0,
.visibility = wgpu.WGPUShaderStage_Vertex | wgpu.WGPUShaderStage_Fragment,
.buffer = .{
.type = wgpu.WGPUBufferBindingType_Uniform,
.minBindingSize = @sizeOf(TMesh.Uniform),
.hasDynamicOffset = 0,
},
},
},
}) orelse return error.ModelBindGroupLayout,
.material = wgpu.wgpuDeviceCreateBindGroupLayout(res.device.raw, &wgpu.WGPUBindGroupLayoutDescriptor{
.entryCount = 1,
.entries = &[_]wgpu.WGPUBindGroupLayoutEntry{
.{
.binding = 0,
.visibility = wgpu.WGPUShaderStage_Vertex | wgpu.WGPUShaderStage_Fragment,
.buffer = .{
.type = wgpu.WGPUBufferBindingType_Uniform,
.minBindingSize = @sizeOf(TMaterial.Style),
.hasDynamicOffset = 0,
},
},
},
}) orelse return error.MaterialBindGroupLayout,
});
} }
/// ---------------------------------------------------- /// ----------------------------------------------------
@ -37,6 +98,7 @@ pub fn deinit(app: *App) !void {
const res = try app.resources.getMany(struct { const res = try app.resources.getMany(struct {
ren: *Renderer, ren: *Renderer,
ren_world: *RenderWorld, ren_world: *RenderWorld,
ren_layout: *RenderLayout,
}); });
// --- FREE SHADERS --- // --- FREE SHADERS ---
@ -56,11 +118,29 @@ pub fn deinit(app: *App) !void {
wgpu.wgpuBindGroupRelease(entry.value.bind_group); wgpu.wgpuBindGroupRelease(entry.value.bind_group);
} }
// --- FREE CAMERAS ---
for (res.ren_world.gpu_cameras.items()) |entry| {
wgpu.wgpuBufferRelease(entry.value.buffer);
wgpu.wgpuBindGroupRelease(entry.value.bind_group);
}
// --- FREE OBJECTS ---
for (res.ren_world.gpu_objects.items()) |entry| {
wgpu.wgpuBufferRelease(entry.value.buffer);
wgpu.wgpuBindGroupRelease(entry.value.bind_group);
}
// --- FREE LAYOUTS ---
wgpu.wgpuBindGroupLayoutRelease(res.ren_layout.camera);
wgpu.wgpuBindGroupLayoutRelease(res.ren_layout.model);
wgpu.wgpuBindGroupLayoutRelease(res.ren_layout.material);
// --- # --- // --- # ---
res.ren_world.materials.deinit(); res.ren_world.materials.deinit();
res.ren_world.frame_views.deinit(app.alloc); res.ren_world.frame_views.deinit(app.alloc);
res.ren_world.frame_objects.deinit(app.alloc); res.ren_world.frame_objects.deinit(app.alloc);
res.ren_world.gpu_cameras.deinit(); res.ren_world.gpu_cameras.deinit();
res.ren_world.gpu_objects.deinit();
res.ren_world.gpu_shaders.deinit(); res.ren_world.gpu_shaders.deinit();
res.ren_world.gpu_materials.deinit(); res.ren_world.gpu_materials.deinit();
res.ren_world.gpu_pipelines.deinit(); res.ren_world.gpu_pipelines.deinit();

View file

@ -3,6 +3,7 @@
// --- # --- // --- # ---
const std = @import("std"); const std = @import("std");
const math = @import("math");
const wgpu = @import("wgpu"); const wgpu = @import("wgpu");
const App = @import("app"); const App = @import("app");
const Zla = @import("zla"); const Zla = @import("zla");
@ -11,6 +12,7 @@ const Zla = @import("zla");
const Vertex = @import("../../cache/resource/mesh_cache.zig").Vertex; const Vertex = @import("../../cache/resource/mesh_cache.zig").Vertex;
const WGPUDevice = @import("../../device/resource/wgpu_device.zig"); const WGPUDevice = @import("../../device/resource/wgpu_device.zig");
const RenderWorld = @import("../resource/render_world.zig"); const RenderWorld = @import("../resource/render_world.zig");
const RenderLayout = @import("../resource/render_layout.zig");
// --- # --- // --- # ---
const Transform2D = @import("../../common/component/transform2D.zig"); const Transform2D = @import("../../common/component/transform2D.zig");
@ -18,6 +20,7 @@ const Transform3D = @import("../../common/component/transform3D.zig");
// --- MESH --- // --- MESH ---
const CMesh = @import("../../common/component/mesh.zig"); const CMesh = @import("../../common/component/mesh.zig");
const TMesh = @import("../template/gpu_object.zig");
// --- MATERIAL --- // --- MATERIAL ---
const CMaterial = @import("../../common/component/material.zig"); const CMaterial = @import("../../common/component/material.zig");
@ -33,55 +36,180 @@ pub fn cameras(app: *App) !void {
// --- # --- // --- # ---
const res = try app.resources.getMany(struct { const res = try app.resources.getMany(struct {
render_world: *RenderWorld, render_world: *RenderWorld,
render_layout: *RenderLayout,
device: *WGPUDevice,
}); });
{ // --- # ---
// --- # --- var query = app.world.query(struct {
var query = app.world.query(struct { camera: *const CCamera,
camera: *const CCamera, transform2D: ?*const Transform2D,
}, .{}); transform3D: ?*const Transform3D,
while (query.next()) |e| { }, .{});
// --- GPU --- while (query.next()) |e| {
const raw = try res.render_world.gpu_cameras.getOrPut(query.entity(), blk: { // --- GPU ---
if (res.render_world.gpu_cameras.getByKey(query.entity())) |v| break :blk v.*; const raw = try res.render_world.gpu_cameras.getOrPut(query.entity(), blk: {
// --- # ---
if (res.render_world.gpu_cameras.getByKey(
query.entity(),
)) |v| break :blk v.*;
break :blk .{ // --- UNIFORM ---
.projection = switch (e.camera.projection) { const uniform = wgpu.wgpuDeviceCreateBuffer(res.device.raw, &wgpu.WGPUBufferDescriptor{
.perspective => |v| .perspective(v.fov, 800.0 / 600.0, v.near, v.far), .usage = wgpu.WGPUBufferUsage_CopyDst | wgpu.WGPUBufferUsage_Uniform,
.ortho => |v| .orthographic(800, 0, 600, 0, v.near, v.far), .size = @sizeOf(TCamera.Uniform),
}, }) orelse return error.Buffer;
};
});
// --- FRAME --- // --- VIEW ---
try res.render_world.frame_views.append(app.alloc, .{ const view: math.Mat4x4 = if (e.transform3D) |t3| cmp: { // 3D
.order = e.camera.order, const eye = t3.position;
.priority = e.camera.priority, const forward = t3.rotation.rotate(math.Vec3f.new(.{ 0, 0, 1 }));
.camera = query.entity(), const up = t3.rotation.rotate(math.Vec3f.new(.{ 0, 1, 0 }));
.raw = raw, break :cmp math.Mat4x4.lookAt(eye, eye.add(forward), up);
}); } else if (e.transform2D) |t2| cmp: { // 2D
} const eye: math.Vec3f = .new(.{ t2.position.value[0], t2.position.value[1], 0 });
} break :cmp math.Mat4x4.rotateZ(-t2.angle)
.mul(math.Mat4x4.translate(-eye.value[0], -eye.value[1], 0));
} else math.Mat4x4.identity();
{ // --- PROJECTION ---
// --- # --- const projection: math.Mat4x4 = switch (e.camera.projection) {
var query = app.world.query(struct { .perspective => |v| .perspective(std.math.degreesToRadians(v.fov), 800.0 / 600.0, v.near, v.far),
mesh: *const CMesh, .ortho => |v| .orthographic(800.0, 0, 600.0, 0, v.near, v.far),
material: *const CMaterial, };
transform2D: ?*const Transform2D,
transform3D: ?*const Transform3D, // --- WRITE TO UNIFORM ---
}, .{}); wgpu.wgpuQueueWriteBuffer(
while (query.next()) |e| { res.device.queue,
try res.render_world.frame_objects.append(app.alloc, .{ uniform,
.mesh = e.mesh.value, 0,
.material = e.material.value, &TCamera.Uniform{
.transform = blk: { .view = view.transpose(),
if (e.transform2D) |v| break :blk .{ .@"2D" = v.* }; .projection = projection.transpose(),
if (e.transform3D) |v| break :blk .{ .@"3D" = v.* };
break :blk .{ .@"2D" = .{} };
}, },
}); @sizeOf(TCamera.Uniform),
} );
// --- BIND GROUP ---
const bind_group = wgpu.wgpuDeviceCreateBindGroup(res.device.raw, &wgpu.WGPUBindGroupDescriptor{
.layout = res.render_layout.camera,
.entryCount = 1,
.entries = &[_]wgpu.WGPUBindGroupEntry{
.{
.binding = 0,
.buffer = uniform,
.offset = 0,
.size = @sizeOf(TCamera.Uniform),
},
},
}) orelse return error.BindGroup;
break :blk .{
.buffer = uniform,
.bind_group = bind_group,
.dirty = false,
};
});
// --- FRAME ---
try res.render_world.frame_views.append(app.alloc, .{
.order = e.camera.order,
.priority = e.camera.priority,
.camera = query.entity(),
.raw = raw,
});
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn objects(app: *App) !void {
// --- # ---
const res = try app.resources.getMany(struct {
render_world: *RenderWorld,
render_layout: *RenderLayout,
device: *WGPUDevice,
});
// --- # ---
var query = app.world.query(struct {
mesh: *const CMesh,
material: *const CMaterial,
transform2D: ?*const Transform2D,
transform3D: ?*const Transform3D,
}, .{});
while (query.next()) |e| {
// --- ENSURE TRANSFORM ---
if (e.transform2D == null and
e.transform3D == null) continue;
// --- GPU ---
_ = try res.render_world.gpu_objects.getOrPut(query.entity(), blk: {
// --- # ---
if (res.render_world.gpu_objects.getByKey(
query.entity(),
)) |v| break :blk v.*;
// --- UNIFORM ---
const uniform = wgpu.wgpuDeviceCreateBuffer(res.device.raw, &wgpu.WGPUBufferDescriptor{
.usage = wgpu.WGPUBufferUsage_CopyDst | wgpu.WGPUBufferUsage_Uniform,
.size = @sizeOf(TMesh.Uniform),
}) orelse return error.Buffer;
// --- MODEL ---
const pos: math.Vec3f = e.transform3D.?.position;
const scl: math.Vec3f = e.transform3D.?.scale;
const rot: math.Quat = e.transform3D.?.rotation;
// --- FINAL MODEL ---
const model =
math.Mat4x4.translate(pos.value[0], pos.value[1], pos.value[2])
.mul(rot.toMat4())
.mul(math.Mat4x4.scale(scl.value[0], scl.value[1], scl.value[2]));
// --- WRITE TO UNIFORM ---
wgpu.wgpuQueueWriteBuffer(
res.device.queue,
uniform,
0,
&TMesh.Uniform{
.data = model.transpose(),
},
@sizeOf(TMesh.Uniform),
);
// --- BIND GROUP ---
const bind_group = wgpu.wgpuDeviceCreateBindGroup(res.device.raw, &wgpu.WGPUBindGroupDescriptor{
.layout = res.render_layout.model,
.entryCount = 1,
.entries = &[_]wgpu.WGPUBindGroupEntry{
.{
.binding = 0,
.buffer = uniform,
.offset = 0,
.size = @sizeOf(TMesh.Uniform),
},
},
}) orelse return error.BindGroup;
break :blk .{
.buffer = uniform,
.bind_group = bind_group,
.dirty = false,
};
});
// --- FRAME ---
try res.render_world.frame_objects.append(app.alloc, .{
.mesh = e.mesh.value,
.entity = query.entity(),
.material = e.material.value,
.transform = blk: {
if (e.transform2D) |v| break :blk .{ .@"2D" = v.* };
if (e.transform3D) |v| break :blk .{ .@"3D" = v.* };
break :blk .{ .@"2D" = .{} };
},
});
} }
} }
@ -143,6 +271,7 @@ pub fn pipelines(app: *App) !void {
// --- # --- // --- # ---
const res = try app.resources.getMany(struct { const res = try app.resources.getMany(struct {
render_world: *RenderWorld, render_world: *RenderWorld,
render_layout: *RenderLayout,
device: *WGPUDevice, device: *WGPUDevice,
}); });
@ -163,62 +292,11 @@ pub fn pipelines(app: *App) !void {
material.unique, material.unique,
)) return; )) return;
// // --- LAYOUT ---
// LAYOUTS
//
// --- CAMERA ---
const camera_layout = wgpu.wgpuDeviceCreateBindGroupLayout(res.device.raw, &wgpu.WGPUBindGroupLayoutDescriptor{
.entryCount = 1,
.entries = &[_]wgpu.WGPUBindGroupLayoutEntry{
.{
.binding = 0,
.visibility = wgpu.WGPUShaderStage_Vertex | wgpu.WGPUShaderStage_Fragment,
.buffer = .{
.type = wgpu.WGPUBufferBindingType_Uniform,
.minBindingSize = @sizeOf(TMaterial.Style),
.hasDynamicOffset = 0,
},
},
},
}) orelse return error.BindGroupLayout;
// --- MODEL ---
const model_layout = wgpu.wgpuDeviceCreateBindGroupLayout(res.device.raw, &wgpu.WGPUBindGroupLayoutDescriptor{
.entryCount = 1,
.entries = &[_]wgpu.WGPUBindGroupLayoutEntry{
.{
.binding = 0,
.visibility = wgpu.WGPUShaderStage_Vertex | wgpu.WGPUShaderStage_Fragment,
.buffer = .{
.type = wgpu.WGPUBufferBindingType_Uniform,
.minBindingSize = @sizeOf(TMaterial.Style),
.hasDynamicOffset = 0,
},
},
},
}) orelse return error.BindGroupLayout;
// --- MATERIAL ---
const material_layout = wgpu.wgpuDeviceCreateBindGroupLayout(res.device.raw, &wgpu.WGPUBindGroupLayoutDescriptor{
.entryCount = 1,
.entries = &[_]wgpu.WGPUBindGroupLayoutEntry{
.{
.binding = 0,
.visibility = wgpu.WGPUShaderStage_Vertex | wgpu.WGPUShaderStage_Fragment,
.buffer = .{
.type = wgpu.WGPUBufferBindingType_Uniform,
.minBindingSize = @sizeOf(TMaterial.Style),
.hasDynamicOffset = 0,
},
},
},
}) orelse return error.BindGroupLayout;
var bind_group_layouts = [_]wgpu.WGPUBindGroupLayout{ var bind_group_layouts = [_]wgpu.WGPUBindGroupLayout{
camera_layout, res.render_layout.camera,
model_layout, res.render_layout.model,
material_layout, res.render_layout.material,
}; };
// --- PIPELINE LAYOUT --- // --- PIPELINE LAYOUT ---
@ -234,7 +312,9 @@ pub fn pipelines(app: *App) !void {
const pipeline = try createPipeline( const pipeline = try createPipeline(
material.unique, material.unique,
pipeline_layout, pipeline_layout,
if (res.render_world.gpu_shaders.getByKey(material.unique.shader)) |v| v.raw else return error.ShaderNotFound, if (res.render_world.gpu_shaders.getByKey(
material.unique.shader,
)) |v| v.raw else return error.ShaderNotFound,
res.device.raw, res.device.raw,
); );
@ -242,11 +322,6 @@ pub fn pipelines(app: *App) !void {
break :blk .{ break :blk .{
.raw = pipeline, .raw = pipeline,
.layout = pipeline_layout, .layout = pipeline_layout,
.bind_group_layouts = .{
camera_layout,
model_layout,
material_layout,
},
}; };
}); });
} }
@ -258,6 +333,7 @@ pub fn materials(app: *App) !void {
// --- # --- // --- # ---
const res = try app.resources.getMany(struct { const res = try app.resources.getMany(struct {
render_world: *RenderWorld, render_world: *RenderWorld,
render_layout: *RenderLayout,
device: *WGPUDevice, device: *WGPUDevice,
}); });
@ -276,8 +352,9 @@ pub fn materials(app: *App) !void {
)) return; )) return;
// --- # --- // --- # ---
const material = res.render_world.materials.get(e.material.value) orelse return error.MaterialNotFound; const material = res.render_world.materials.get(
const pipeline = res.render_world.gpu_pipelines.getByKey(material.unique) orelse return error.PipelineNotFound; e.material.value,
) orelse return error.MaterialNotFound;
// --- UNIFORM --- // --- UNIFORM ---
const uniform = wgpu.wgpuDeviceCreateBuffer(res.device.raw, &wgpu.WGPUBufferDescriptor{ const uniform = wgpu.wgpuDeviceCreateBuffer(res.device.raw, &wgpu.WGPUBufferDescriptor{
@ -296,7 +373,7 @@ pub fn materials(app: *App) !void {
// --- BIND GROUP --- // --- BIND GROUP ---
const bind_group = wgpu.wgpuDeviceCreateBindGroup(res.device.raw, &wgpu.WGPUBindGroupDescriptor{ const bind_group = wgpu.wgpuDeviceCreateBindGroup(res.device.raw, &wgpu.WGPUBindGroupDescriptor{
.layout = pipeline.bind_group_layouts[0], .layout = res.render_layout.material,
.entryCount = 1, .entryCount = 1,
.entries = &[_]wgpu.WGPUBindGroupEntry{ .entries = &[_]wgpu.WGPUBindGroupEntry{
.{ .{
@ -399,6 +476,7 @@ fn createPipeline(
.mask = ~@as(u32, 0), .mask = ~@as(u32, 0),
.alphaToCoverageEnabled = 0, .alphaToCoverageEnabled = 0,
}, },
.depthStencil = null,
}) orelse { }) orelse {
return error.PipelineCreationFailed; return error.PipelineCreationFailed;
}; };

View file

@ -71,6 +71,7 @@ pub fn draw(app: *App) !void {
// --- FRAME --- // --- FRAME ---
var render_pass: ?*wgpu.WGPURenderPassEncoderImpl = null; var render_pass: ?*wgpu.WGPURenderPassEncoderImpl = null;
var camera: ?u64 = null;
for (res.renderer.commands.items) |cmd| { for (res.renderer.commands.items) |cmd| {
switch (cmd) { switch (cmd) {
@ -107,6 +108,9 @@ pub fn draw(app: *App) !void {
}, },
}, },
); );
// --- SET CAMERA ---
camera = v.camera;
}, },
.end_pass => { .end_pass => {
if (render_pass) |rp| { if (render_pass) |rp| {
@ -119,7 +123,9 @@ pub fn draw(app: *App) !void {
// --- ENSURE --- // --- ENSURE ---
const material = res.render_world.materials.get(v.material) orelse return error.MaterialNotFound; const material = res.render_world.materials.get(v.material) orelse return error.MaterialNotFound;
const gpu_material = res.render_world.gpu_materials.getByKey(v.material) orelse return error.GPUMaterialNotFound; const gpu_material = res.render_world.gpu_materials.getByKey(v.material) orelse return error.GPUMaterialNotFound;
const gpu_camera = res.render_world.gpu_cameras.getByKey(camera orelse return error.CameraNotSet) orelse return error.GPUCameraNotFound;
const pipeline = res.render_world.gpu_pipelines.getByKey(material.unique) orelse return error.GPUPipelineNotFound; const pipeline = res.render_world.gpu_pipelines.getByKey(material.unique) orelse return error.GPUPipelineNotFound;
const gpu_mesh = res.render_world.gpu_objects.getByKey(v.entity) orelse return error.GPUMeshNotFound;
const mesh = try res.mesh_cache.getOrCreate(v.mesh); const mesh = try res.mesh_cache.getOrCreate(v.mesh);
// --- BIND --- // --- BIND ---
@ -127,8 +133,8 @@ pub fn draw(app: *App) !void {
wgpu.wgpuRenderPassEncoderSetVertexBuffer(render_pass, 0, mesh.vbuf, 0, wgpu.wgpuBufferGetSize(mesh.vbuf)); wgpu.wgpuRenderPassEncoderSetVertexBuffer(render_pass, 0, mesh.vbuf, 0, wgpu.wgpuBufferGetSize(mesh.vbuf));
// --- BIND SHADER GROUPS --- // --- BIND SHADER GROUPS ---
wgpu.wgpuRenderPassEncoderSetBindGroup(render_pass, 0, gpu_material.bind_group, 0, null); wgpu.wgpuRenderPassEncoderSetBindGroup(render_pass, 0, gpu_camera.bind_group, 0, null);
wgpu.wgpuRenderPassEncoderSetBindGroup(render_pass, 1, gpu_material.bind_group, 0, null); wgpu.wgpuRenderPassEncoderSetBindGroup(render_pass, 1, gpu_mesh.bind_group, 0, null);
wgpu.wgpuRenderPassEncoderSetBindGroup(render_pass, 2, gpu_material.bind_group, 0, null); wgpu.wgpuRenderPassEncoderSetBindGroup(render_pass, 2, gpu_material.bind_group, 0, null);
// --- DRAW --- // --- DRAW ---

View file

@ -13,6 +13,7 @@ const Handle = gtl.Handle;
// //
mesh: MeshID, mesh: MeshID,
entity: u64, // TODO: Change to Entity type
material: Handle(Material), material: Handle(Material),
transform: union(enum) { transform: union(enum) {
@"2D": Transform2D, @"2D": Transform2D,

View file

@ -1,3 +1,11 @@
const wgpu = @import("wgpu");
const math = @import("math"); const math = @import("math");
projection: math.Mat4x4, pub const Uniform = struct {
view: math.Mat4x4,
projection: math.Mat4x4,
};
buffer: *wgpu.WGPUBufferImpl,
bind_group: *wgpu.WGPUBindGroupImpl,
dirty: bool = true,

View file

@ -0,0 +1,10 @@
const wgpu = @import("wgpu");
const math = @import("math");
pub const Uniform = struct {
data: math.Mat4x4,
};
buffer: *wgpu.WGPUBufferImpl,
bind_group: *wgpu.WGPUBindGroupImpl,
dirty: bool = true,

View file

@ -2,4 +2,3 @@ const wgpu = @import("wgpu");
raw: *wgpu.WGPURenderPipelineImpl, raw: *wgpu.WGPURenderPipelineImpl,
layout: *wgpu.WGPUPipelineLayoutImpl, layout: *wgpu.WGPUPipelineLayoutImpl,
bind_group_layouts: [3]*wgpu.WGPUBindGroupLayoutImpl,