Proper resource based render | Somewhat proper rendering | Changed stuff n stuff | Now go amimir

This commit is contained in:
abux 2026-07-31 01:11:10 +01:00
parent d213a23ca7
commit ccde4427be
31 changed files with 811 additions and 133 deletions

View file

@ -60,6 +60,12 @@ pub fn build(b: *std.Build) void {
});
exe.root_module.addImport("zla", zla.module("zla"));
const math = b.dependency("NYXMath", .{
.target = target,
.optimize = optimize,
});
exe.root_module.addImport("math", math.module("NYXMath"));
//
// SYSTEM DEPENDENCIES
//

View file

@ -20,6 +20,10 @@
.url = "git+http://2.120.146.66:25569/abux/gtl.git#3c7fcfcb1276b76ec4695ce5ce9dba4d8255a176",
.hash = "gtl-0.0.0-e3QoJMRYAADChgXn2sogTxsbUpEBSHjOGZeoEx4ccMMD",
},
.NYXMath = .{
.url = "git+http://192.168.0.11:25569/abux/NYXMath.git#58d57b61f3fa25e234af1af3e1e56bb656f4d77d",
.hash = "NYXMath-0.0.0-BD6ST8zOAAB1dgmaGKShXtKmGTuYqgiDpAY2faHM_HnV",
},
},
.paths = .{

View file

@ -6,6 +6,7 @@
//
const std = @import("std");
const math = @import("math");
const App = @import("app");
//
@ -61,13 +62,17 @@ pub fn main(init: std.process.Init) !void {
if (app.events.consume(App.event.AppExit)) |_| break;
// --- # ---
try app.schedules.runMany(&.{
app.schedules.runMany(&.{
App.stage.begin_frame,
App.stage.inputs,
App.stage.fixed_update,
App.stage.update,
App.stage.draw,
App.stage.end_frame,
}, &app);
}, &app) catch |err| {
std.debug.print("\x1b[31m[ERROR]\x1b[0m {s}\n", .{
@errorName(err),
});
};
}
}

View file

@ -43,13 +43,13 @@ struct ModelUniform {
// UNIFORMS
//
// @group(0) @binding(0)
// var<uniform> camera: CameraUniform;
// @group(1) @binding(0)
// var<uniform> model: ModelUniform;
@group(0) @binding(0)
var<uniform> camera: CameraUniform;
@group(1) @binding(0)
var<uniform> model: ModelUniform;
@group(2) @binding(0)
var<uniform> material: Material;
//

View file

@ -81,6 +81,9 @@ pub fn getOrCreate(
mesh: MeshID,
) !*GPUMesh {
return try self.data.getOrPut(mesh, blk: {
// --- # ---
if (self.data.getByKey(mesh)) |v| return v;
// --- # ---
const vertices: []const Vertex = switch (mesh) {
.triangle => &.{

View file

@ -1,4 +1,6 @@
//! ----------------------------------------------------
//! TODO: Move [materials, materials_gpu and GPUMaterial somewhere else]
//! Depricated
//! ----------------------------------------------------
const std = @import("std");
@ -16,8 +18,7 @@ const Vertex = @import("mesh_cache.zig").Vertex;
pub const GPUPipeline = struct {
raw: *wgpu.WGPURenderPipelineImpl,
layout: *wgpu.WGPUPipelineLayoutImpl,
bind_group_layouts: [1]*wgpu.WGPUBindGroupLayoutImpl,
// bind_group_layouts: []const *wgpu.WGPUBindGroupLayoutImpl,
bind_group_layouts: [3]*wgpu.WGPUBindGroupLayoutImpl,
};
/// ----------------------------------------------------
@ -28,6 +29,15 @@ pub const GPUMaterial = struct {
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
//
@ -145,7 +155,43 @@ pub fn getOrCreatePipeline(
// --- # ---
if (self.pipelines.getByKey(material.unique)) |v| return v;
// --- MATERIAL LAYOUT ---
//
// 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{
@ -161,12 +207,18 @@ pub fn getOrCreatePipeline(
},
}) 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 = 1,
.bindGroupLayouts = &material_layout,
.bindGroupLayoutCount = 3,
.bindGroupLayouts = &bind_group_layouts,
},
) orelse return error.Layout;
@ -181,6 +233,8 @@ pub fn getOrCreatePipeline(
.raw = pipeline,
.layout = pipeline_layout,
.bind_group_layouts = .{
camera_layout,
model_layout,
material_layout,
},
};

View file

@ -1,4 +1,5 @@
//! ----------------------------------------------------
//! DEPRICATED
//! ----------------------------------------------------
//
@ -12,8 +13,6 @@ const lifetime = @import("system/lifetime.zig");
// PUBLIC
//
pub const ShaderCache = @import("resource/shader_cache.zig");
pub const PipelineCache = @import("resource/pipeline_cache.zig");
pub const MeshCache = @import("resource/mesh_cache.zig");
/// ----------------------------------------------------

View file

@ -6,8 +6,6 @@ const gtl = @import("gtl");
const App = @import("app");
const WGPUDevice = @import("../../device/resource/wgpu_device.zig");
const ShaderCache = @import("../resource/shader_cache.zig");
const PipelineCache = @import("../resource/pipeline_cache.zig");
const MeshCache = @import("../resource/mesh_cache.zig");
/// ----------------------------------------------------
@ -18,19 +16,7 @@ pub fn init(app: *App) !void {
device: *const WGPUDevice,
});
// --- SHADER CACHE ---
const shaders = try app.resources.getOrSet(ShaderCache.init(
res.device.raw,
app.alloc,
));
// --- PIPELINE CACHE ---
const pipelines = try app.resources.getOrSet(PipelineCache.init(
shaders,
res.device.queue,
res.device.raw,
app.alloc,
));
// TODO: Depricated stuff here | Use RenderWorld
// --- MESH CACHE ---
const meshes = try app.resources.getOrSet(MeshCache.init(
@ -40,7 +26,6 @@ pub fn init(app: *App) !void {
));
_ = meshes;
_ = pipelines;
// TODO: Remove or place it somewhere else, idk
@ -94,13 +79,9 @@ pub fn init(app: *App) !void {
pub fn deinit(app: *App) !void {
// --- # ---
const res = try app.resources.getMany(struct {
shaders: *ShaderCache,
pipelines: *PipelineCache,
meshes: *MeshCache,
});
// --- # ---
res.shaders.deinit();
res.pipelines.deinit();
res.meshes.deinit();
}

View file

@ -0,0 +1,22 @@
const math = @import("math");
projection: union(enum) {
perspective: struct {
fov: f32 = 80.0,
far: f32 = 4000.0,
near: f32 = 0.05,
},
ortho: struct {
size: f32 = 1.0,
far: f32 = 4000.0,
near: f32 = 0.05,
},
} = .{ .ortho = .{} },
target: union(enum) {
window,
texture, // TODO: Handle to a texture, someday
} = .window,
order: u32 = 0,
priority: u32 = 0,

View file

@ -1,54 +0,0 @@
//! ----------------------------------------------------
//! ----------------------------------------------------
const gtl = @import("gtl");
const ShaderID = @import("../../cache/resource/shader_cache.zig").ShaderID;
const Handle = gtl.Handle;
const Self = @This();
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const Style = struct {
albedo: @Vector(4, f32) = .{ 1, 1, 1, 1 },
metallic: f32 = 0.0,
roughness: f32 = 0.5,
_pad0: [2]f32 = .{ 0, 0 },
};
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const Unique = struct {
shader: ShaderID = .pbr,
front_face: enum {
cw,
ccw,
} = .ccw,
cull_mode: enum {
back,
front,
none,
} = .back,
topology: enum {
triangle_list,
point_list,
line_list,
} = .triangle_list,
blend_mode: enum {
none,
alpha,
additive,
multiply,
} = .none,
};
//
// FIELDS
//
style: Style = .{},
unique: Unique = .{},
value: ?Handle(Self) = null,

View file

@ -1,3 +1,5 @@
position: @Vector(2, f32) = .{ 0, 0 },
scale: @Vector(2, f32) = .{ 32, 32 },
const math = @import("math");
position: math.Vec2f = .as(0),
scale: math.Vec2f = .as(32),
angle: f32 = 0,

View file

@ -1,3 +1,5 @@
position: @Vector(3, f32) = .{ 0, 0, 0 },
rotation: @Vector(3, f32) = .{ 0, 0, 0 },
scale: @Vector(3, f32) = .{ 1, 1, 1 },
const math = @import("math");
position: math.Vec3f = .as(0),
rotation: math.Quat = .identity(),
scale: math.Vec3f = .as(0),

View file

@ -5,6 +5,9 @@
// COMPONENTS
//
// --- VIEW ---
const Camera = @import("component/camera.zig");
// --- POSITION ---
pub const Transform3D = @import("component/transform3D.zig");
pub const Transform2D = @import("component/transform2D.zig");

View file

@ -6,11 +6,13 @@
//
const App = @import("app");
const Camera = @import("../common/component/camera.zig");
const Mesh = @import("../common/component/mesh.zig");
const Material = @import("../common/component/material.zig");
const Transform3D = @import("../common/component/transform3D.zig");
const PipelineCache = @import("../cache/resource/pipeline_cache.zig");
const RenderWorld = @import("../renderer/resource/render_world.zig");
//
// PUBLIC
@ -36,19 +38,27 @@ fn build(
/// ----------------------------------------------------
fn start(app: *App) !void {
// --- # ---
const pipeline_cache = app.resources.getPtr(
PipelineCache,
) orelse return error.PipelineCacheNotSet;
const render_world = app.resources.getPtr(
RenderWorld,
).?;
// --- CAMERA ---
try app.world.spawn(.{
Camera{},
Transform3D{},
});
// --- # ---
try app.world.spawn(.{
Mesh{ .value = .square },
Material{
.value = try pipeline_cache.materials.put(.{
.value = try render_world.materials.put(.{
.style = .{
.albedo = .{ 0.8, 0.2, 0.2, 1 },
.albedo = .{ 0.8, 0.2, 0.2, 1.0 },
},
.unique = .{
.topology = .line_list,
},
.unique = .{},
}),
},
Transform3D{},
@ -58,11 +68,10 @@ fn start(app: *App) !void {
try app.world.spawn(.{
Mesh{ .value = .triangle },
Material{
.value = try pipeline_cache.materials.put(.{
.value = try render_world.materials.put(.{
.style = .{
.albedo = .{ 0.2, 0.2, 0.8, 1 },
},
.unique = .{},
}),
},
Transform3D{},

View file

@ -0,0 +1,83 @@
// ----------------------------------------------------
// ----------------------------------------------------
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

@ -0,0 +1,47 @@
//! ----------------------------------------------------
//! ----------------------------------------------------
// --- # ---
const std = @import("std");
const gtl = @import("gtl");
const wgpu = @import("wgpu");
// --- CAMERA ---
pub const GPUCamera = @import("../template/gpu_camera.zig");
pub const FrameView = @import("../template/frame_view.zig");
// --- SHADER ---
pub const ShaderID = @import("../template/shader_id.zig").ShaderID;
pub const GPUShader = @import("../template/gpu_shader.zig");
// --- MATERIAL ---
pub const Material = @import("../template/material.zig");
pub const GPUMaterial = @import("../template/gpu_material.zig");
// --- PIPELINE ---
pub const Pipeline = Material.Unique;
pub const GPUPipeline = @import("../template/gpu_pipeline.zig");
// --- OBJECT ---
pub const Entity = u64; // TODO: You lazy retard
pub const FrameObject = @import("../template/frame_object.zig");
//
// EMBEDED
//
pub const PBRCode = @embedFile("embeded/shaders/pbr.wgsl");
pub const UnlitCode = @embedFile("embeded/shaders/pbr.wgsl");
//
// FIELDS
//
gpu_cameras: gtl.KeyedSlotMap(Entity, GPUCamera),
gpu_shaders: gtl.KeyedSlotMap(ShaderID, GPUShader),
gpu_pipelines: gtl.KeyedSlotMap(Pipeline, GPUPipeline),
gpu_materials: gtl.KeyedSlotMap(gtl.Handle(Material), GPUMaterial),
frame_views: std.ArrayList(FrameView),
frame_objects: std.ArrayList(FrameObject),
materials: gtl.SlotMap(Material), // TODO: Move this to assets resource or sum

View file

@ -25,7 +25,7 @@ pub const DrawCMD = union(enum) {
},
begin_pass: struct {
camera: Handle(Camera),
camera: u64, // TODO: Change it to Entity type instead
color: [4]f32 = .{ 0, 0, 0, 255 },
load_op: enum {
load,

View file

@ -9,6 +9,7 @@ const App = @import("app");
const lifetime = @import("system/lifetime.zig");
const frame = @import("system/frame.zig");
const prepare = @import("system/prepare.zig");
const present = @import("system/present.zig");
//
@ -38,6 +39,14 @@ fn build(
try app.schedules.add(App.stage.begin_frame, frame.begin);
try app.schedules.add(App.stage.end_frame, frame.end);
// --- PRE DRAW ---
try app.schedules.addManyBefore(App.stage.draw, App.stage.draw, &.{
prepare.cameras,
prepare.shaders,
prepare.pipelines,
prepare.materials,
});
// --- DRAW ---
try app.schedules.add(App.stage.draw, present.draw);
}

View file

@ -4,6 +4,7 @@
const std = @import("std");
const App = @import("app");
const Renderer = @import("../resource/renderer.zig");
const RenderWorld = @import("../resource/render_world.zig");
const Mesh = @import("../../common/component/mesh.zig");
const Material = @import("../../common/component/material.zig");
@ -15,34 +16,37 @@ pub fn begin(app: *App) !void {
// --- # ---
const res = try app.resources.getMany(struct {
renderer: *Renderer,
render_world: *RenderWorld,
});
// --- # ---
res.renderer.clear();
// --- DRAW ---
for (res.render_world.frame_views.items) |view| {
// --- NEW PASS ---
try res.renderer.draw(.{
.begin_pass = .{
.camera = undefined,
.camera = view.camera,
.color = .{ 0.001, 0.001, 0.001, 1.0 },
},
});
defer res.renderer.draw(.end_pass) catch unreachable;
// --- QUERY ---
var query = app.world.query(struct {
mesh: *Mesh,
material: *Material,
transform3D: ?*Transform3D,
}, .{});
while (query.next()) |e| {
// --- # ---
for (res.render_world.frame_objects.items) |obj| {
try res.renderer.draw(.{
.draw = .{
.material = e.material.value,
.mesh = e.mesh.value,
.material = obj.material,
.mesh = obj.mesh,
},
});
}
}
// --- YES ---
res.render_world.frame_views.clearRetainingCapacity();
res.render_world.frame_objects.clearRetainingCapacity();
// { // TEST
// // --- DRAW ---

View file

@ -1,9 +1,12 @@
//! ----------------------------------------------------
//! ----------------------------------------------------
const wgpu = @import("wgpu");
const App = @import("app");
const Zla = @import("zla");
const Renderer = @import("../resource/renderer.zig");
const RenderWorld = @import("../resource/render_world.zig");
/// ----------------------------------------------------
/// ----------------------------------------------------
@ -12,11 +15,54 @@ pub fn init(app: *App) !void {
try app.resources.set(Renderer.init(
app.alloc,
));
// --- # ---
try app.resources.set(RenderWorld{
.gpu_cameras = .init(app.alloc),
.gpu_shaders = .init(app.alloc),
.gpu_materials = .init(app.alloc),
.gpu_pipelines = .init(app.alloc),
.frame_views = .empty,
.frame_objects = .empty,
.materials = .init(app.alloc),
});
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn deinit(app: *App) !void {
const renderer = app.resources.getPtr(Renderer) orelse return error.RendererNotSet;
renderer.deinit();
// --- # ---
const res = try app.resources.getMany(struct {
ren: *Renderer,
ren_world: *RenderWorld,
});
// --- FREE SHADERS ---
for (res.ren_world.gpu_shaders.items()) |entry| {
wgpu.wgpuShaderModuleRelease(entry.value.raw);
}
// --- FREE PIPELINES ---
for (res.ren_world.gpu_pipelines.items()) |entry| {
wgpu.wgpuPipelineLayoutRelease(entry.value.layout);
wgpu.wgpuRenderPipelineRelease(entry.value.raw);
}
// --- FREE MATERIALS ---
for (res.ren_world.gpu_materials.items()) |entry| {
wgpu.wgpuBufferRelease(entry.value.buffer);
wgpu.wgpuBindGroupRelease(entry.value.bind_group);
}
// --- # ---
res.ren_world.materials.deinit();
res.ren_world.frame_views.deinit(app.alloc);
res.ren_world.frame_objects.deinit(app.alloc);
res.ren_world.gpu_cameras.deinit();
res.ren_world.gpu_shaders.deinit();
res.ren_world.gpu_materials.deinit();
res.ren_world.gpu_pipelines.deinit();
res.ren.deinit();
}

View file

@ -0,0 +1,405 @@
//! ----------------------------------------------------
//! ----------------------------------------------------
// --- # ---
const std = @import("std");
const wgpu = @import("wgpu");
const App = @import("app");
const Zla = @import("zla");
// --- # ---
const Vertex = @import("../../cache/resource/mesh_cache.zig").Vertex;
const WGPUDevice = @import("../../device/resource/wgpu_device.zig");
const RenderWorld = @import("../resource/render_world.zig");
// --- # ---
const Transform2D = @import("../../common/component/transform2D.zig");
const Transform3D = @import("../../common/component/transform3D.zig");
// --- MESH ---
const CMesh = @import("../../common/component/mesh.zig");
// --- MATERIAL ---
const CMaterial = @import("../../common/component/material.zig");
const TMaterial = @import("../template/material.zig");
// --- CAMERA ---
const CCamera = @import("../../common/component/camera.zig");
const TCamera = @import("../template/gpu_camera.zig");
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn cameras(app: *App) !void {
// --- # ---
const res = try app.resources.getMany(struct {
render_world: *RenderWorld,
});
{
// --- # ---
var query = app.world.query(struct {
camera: *const CCamera,
}, .{});
while (query.next()) |e| {
// --- GPU ---
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 .{
.projection = switch (e.camera.projection) {
.perspective => |v| .perspective(v.fov, 800.0 / 600.0, v.near, v.far),
.ortho => |v| .orthographic(800, 0, 600, 0, v.near, v.far),
},
};
});
// --- FRAME ---
try res.render_world.frame_views.append(app.alloc, .{
.order = e.camera.order,
.priority = e.camera.priority,
.camera = query.entity(),
.raw = raw,
});
}
}
{
// --- # ---
var query = app.world.query(struct {
mesh: *const CMesh,
material: *const CMaterial,
transform2D: ?*const Transform2D,
transform3D: ?*const Transform3D,
}, .{});
while (query.next()) |e| {
try res.render_world.frame_objects.append(app.alloc, .{
.mesh = e.mesh.value,
.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" = .{} };
},
});
}
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn shaders(app: *App) !void {
// --- # ---
const res = try app.resources.getMany(struct {
render_world: *RenderWorld,
device: *WGPUDevice,
});
// --- # ---
var query = app.world.query(struct {
material: *const CMaterial,
}, .{});
while (query.next()) |e| {
// --- GET MATERIAL ---
const material = res.render_world.materials.get(
e.material.value,
) orelse return error.MaterialNotFound;
// --- ENSURE SHADER ---
_ = try res.render_world.gpu_shaders.getOrPut(material.unique.shader, blk: {
// --- # ---
if (res.render_world.gpu_shaders.contains(
material.unique.shader,
)) return;
// --- CODE ---
const code: []const u8 = switch (material.unique.shader) {
.pbr => RenderWorld.PBRCode,
.unlit => RenderWorld.UnlitCode,
.custom => return error.TooLazyToImplement, // TODO: Yes
};
// --- SOURCE ---
var source: wgpu.WGPUShaderSourceWGSL = .{
.chain = .{ .sType = wgpu.WGPUSType_ShaderSourceWGSL },
.code = .{ .data = code.ptr, .length = wgpu.WGPU_STRLEN },
};
// --- RESULT ---
break :blk .{
.raw = wgpu.wgpuDeviceCreateShaderModule(res.device.raw, &wgpu.WGPUShaderModuleDescriptor{
.label = .{ .data = @tagName(material.unique.shader), .length = wgpu.WGPU_STRLEN },
.nextInChain = &source.chain,
}) orelse {
return error.ShaderModuleCreationFailed;
},
};
});
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn pipelines(app: *App) !void {
// --- # ---
const res = try app.resources.getMany(struct {
render_world: *RenderWorld,
device: *WGPUDevice,
});
// --- # ---
var query = app.world.query(struct {
material: *const CMaterial,
}, .{});
while (query.next()) |e| {
// --- GET MATERIAL ---
const material = res.render_world.materials.get(
e.material.value,
) orelse return error.MaterialNotFound;
// --- ENSURE PIPELINE ---
_ = try res.render_world.gpu_pipelines.getOrPut(material.unique, blk: {
// --- # ---
if (res.render_world.gpu_pipelines.contains(
material.unique,
)) return;
//
// 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{
camera_layout,
model_layout,
material_layout,
};
// --- PIPELINE LAYOUT ---
const pipeline_layout = wgpu.wgpuDeviceCreatePipelineLayout(
res.device.raw,
&wgpu.WGPUPipelineLayoutDescriptor{
.bindGroupLayoutCount = 3,
.bindGroupLayouts = &bind_group_layouts,
},
) orelse return error.Layout;
// --- CREATE PIPELINE ---
const pipeline = try createPipeline(
material.unique,
pipeline_layout,
if (res.render_world.gpu_shaders.getByKey(material.unique.shader)) |v| v.raw else return error.ShaderNotFound,
res.device.raw,
);
// --- RESULT ---
break :blk .{
.raw = pipeline,
.layout = pipeline_layout,
.bind_group_layouts = .{
camera_layout,
model_layout,
material_layout,
},
};
});
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn materials(app: *App) !void {
// --- # ---
const res = try app.resources.getMany(struct {
render_world: *RenderWorld,
device: *WGPUDevice,
});
// --- # ---
var query = app.world.query(struct {
material: *const CMaterial,
}, .{});
while (query.next()) |e| {
// --- ENSURE MATERIAL ---
_ = try res.render_world.gpu_materials.getOrPut(
e.material.value,
blk: {
// --- # ---
if (res.render_world.gpu_materials.contains(
e.material.value,
)) return;
// --- # ---
const material = res.render_world.materials.get(e.material.value) orelse return error.MaterialNotFound;
const pipeline = res.render_world.gpu_pipelines.getByKey(material.unique) orelse return error.PipelineNotFound;
// --- UNIFORM ---
const uniform = wgpu.wgpuDeviceCreateBuffer(res.device.raw, &wgpu.WGPUBufferDescriptor{
.usage = wgpu.WGPUBufferUsage_CopyDst | wgpu.WGPUBufferUsage_Uniform,
.size = @sizeOf(TMaterial.Style),
}) orelse return error.Buffer;
// --- WRITE TO UNIFORM ---
wgpu.wgpuQueueWriteBuffer(
res.device.queue,
uniform,
0,
&material.style,
@sizeOf(TMaterial.Style),
);
// --- BIND GROUP ---
const bind_group = wgpu.wgpuDeviceCreateBindGroup(res.device.raw, &wgpu.WGPUBindGroupDescriptor{
.layout = pipeline.bind_group_layouts[0],
.entryCount = 1,
.entries = &[_]wgpu.WGPUBindGroupEntry{
.{
.binding = 0,
.buffer = uniform,
.offset = 0,
.size = @sizeOf(TMaterial.Style),
},
},
}) orelse return error.BindGroup;
// --- RESULT ---
break :blk .{
.buffer = uniform,
.bind_group = bind_group,
.dirty = false,
};
},
);
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
fn createPipeline(
key: TMaterial.Unique,
layout: *wgpu.WGPUPipelineLayoutImpl,
shader: *wgpu.WGPUShaderModuleImpl,
device: *wgpu.WGPUDeviceImpl,
) !*wgpu.WGPURenderPipelineImpl {
return wgpu.wgpuDeviceCreateRenderPipeline(device, &wgpu.WGPURenderPipelineDescriptor{
.label = .{ .data = @tagName(key.shader), .length = wgpu.WGPU_STRLEN },
.layout = layout,
.vertex = .{
.module = shader,
.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,
.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

@ -9,7 +9,7 @@ const Window = @import("../../window/resource/window.zig");
const Resized = @import("../../window/event/resized.zig");
const Device = @import("../../device/resource/wgpu_device.zig");
const Renderer = @import("../resource/renderer.zig");
const PipelineCache = @import("../../cache/resource/pipeline_cache.zig");
const RenderWorld = @import("../resource/render_world.zig");
const MeshCache = @import("../../cache/resource/mesh_cache.zig");
/// ----------------------------------------------------
@ -20,13 +20,10 @@ pub fn draw(app: *App) !void {
window: *Window,
device: *Device,
renderer: *Renderer,
pipeline_cache: *PipelineCache,
render_world: *RenderWorld,
mesh_cache: *MeshCache,
});
{ // TODO: Move this | Gather Materials
}
// --- DO NONE ON WINDOW RESIZE ---
if (app.events.consume(Resized)) |_| {
return;
@ -120,15 +117,19 @@ pub fn draw(app: *App) !void {
},
.draw => |v| {
// --- ENSURE ---
const material = res.pipeline_cache.materials.get(v.material) orelse return error.MaterialNotFound;
const gpu_material = try res.pipeline_cache.getOrCreateMaterial(v.material);
const pipeline = try res.pipeline_cache.getOrCreatePipeline(material);
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 pipeline = res.render_world.gpu_pipelines.getByKey(material.unique) orelse return error.GPUPipelineNotFound;
const mesh = try res.mesh_cache.getOrCreate(v.mesh);
// --- BIND ---
wgpu.wgpuRenderPassEncoderSetPipeline(render_pass, pipeline.raw);
wgpu.wgpuRenderPassEncoderSetVertexBuffer(render_pass, 0, mesh.vbuf, 0, wgpu.wgpuBufferGetSize(mesh.vbuf));
// --- BIND SHADER GROUPS ---
wgpu.wgpuRenderPassEncoderSetBindGroup(render_pass, 0, gpu_material.bind_group, 0, null);
wgpu.wgpuRenderPassEncoderSetBindGroup(render_pass, 1, gpu_material.bind_group, 0, null);
wgpu.wgpuRenderPassEncoderSetBindGroup(render_pass, 2, gpu_material.bind_group, 0, null);
// --- DRAW ---
if (mesh.ibuf) |ibuf| {

View file

@ -0,0 +1,20 @@
//! ----------------------------------------------------
//! ----------------------------------------------------
const gtl = @import("gtl");
const Material = @import("material.zig");
const Transform2D = @import("../../common/component/transform2D.zig");
const Transform3D = @import("../../common/component/transform3D.zig");
const MeshID = @import("../../cache/resource/mesh_cache.zig").MeshID;
const Handle = gtl.Handle;
//
// FIELDS
//
mesh: MeshID,
material: Handle(Material),
transform: union(enum) {
@"2D": Transform2D,
@"3D": Transform3D,
},

View file

@ -0,0 +1,6 @@
const GPUCamera = @import("gpu_camera.zig");
raw: *GPUCamera,
order: u32 = 0,
priority: u32 = 0,
camera: u64, // TODO: Change to ENTITY Type

View file

@ -0,0 +1,3 @@
const math = @import("math");
projection: math.Mat4x4,

View file

@ -0,0 +1,5 @@
const wgpu = @import("wgpu");
buffer: *wgpu.WGPUBufferImpl,
bind_group: *wgpu.WGPUBindGroupImpl,
dirty: bool = true,

View file

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

View file

@ -0,0 +1,3 @@
const wgpu = @import("wgpu");
raw: *wgpu.WGPUShaderModuleImpl,

View file

@ -2,7 +2,7 @@
//! ----------------------------------------------------
const gtl = @import("gtl");
const ShaderID = @import("../../cache/resource/shader_cache.zig").ShaderID;
const ShaderID = @import("../template/shader_id.zig").ShaderID;
const Handle = gtl.Handle;
const Self = @This();

View file

@ -0,0 +1,5 @@
pub const ShaderID = union(enum) {
pbr,
unlit,
custom: u32,
};