From 5e62d2c2684a1186001936be074a444675a70244 Mon Sep 17 00:00:00 2001 From: abux Date: Tue, 28 Jul 2026 22:37:53 +0100 Subject: [PATCH] Added Plugins [Renderer, Cache, Common] --- build.zig | 12 ++ src/main.zig | 5 + .../cache/resource/embeded/shaders/pbr.wgsl | 19 +++ src/plugins/cache/resource/pipeline_cache.zig | 131 ++++++++++++++++++ src/plugins/cache/resource/shader_cache.zig | 103 ++++++++++++++ src/plugins/cache/root.zig | 33 +++++ src/plugins/cache/system/lifetime.zig | 92 ++++++++++++ src/plugins/common/component/material.zig | 52 +++++++ src/plugins/common/root.zig | 8 ++ src/plugins/renderer/root.zig | 28 ++++ 10 files changed, 483 insertions(+) create mode 100644 src/plugins/cache/resource/embeded/shaders/pbr.wgsl create mode 100644 src/plugins/cache/resource/pipeline_cache.zig create mode 100644 src/plugins/cache/resource/shader_cache.zig create mode 100644 src/plugins/cache/root.zig create mode 100644 src/plugins/cache/system/lifetime.zig create mode 100644 src/plugins/common/component/material.zig create mode 100644 src/plugins/common/root.zig create mode 100644 src/plugins/renderer/root.zig diff --git a/build.zig b/build.zig index 67729c5..28e25ea 100644 --- a/build.zig +++ b/build.zig @@ -48,6 +48,18 @@ pub fn build(b: *std.Build) void { }); exe.root_module.addImport("app", app.module("app")); + const gtl = b.dependency("gtl", .{ + .target = target, + .optimize = optimize, + }); + exe.root_module.addImport("gtl", gtl.module("gtl")); + + const zla = b.dependency("zla", .{ + .target = target, + .optimize = optimize, + }); + exe.root_module.addImport("zla", zla.module("zla")); + // // SYSTEM DEPENDENCIES // diff --git a/src/main.zig b/src/main.zig index 30c323e..c2c567f 100644 --- a/src/main.zig +++ b/src/main.zig @@ -13,9 +13,12 @@ const App = @import("app"); // const template = @import("plugins/template/root.zig"); +const common = @import("plugins/common/root.zig"); const platform = @import("plugins/platform/root.zig"); const device = @import("plugins/device/root.zig"); const window = @import("plugins/window/root.zig"); +const cache = @import("plugins/cache/root.zig"); +const renderer = @import("plugins/renderer/root.zig"); /// ---------------------------------------------------- /// ---------------------------------------------------- @@ -29,6 +32,8 @@ pub fn main(init: std.process.Init) !void { platform.plugin, device.plugin, window.plugin, + cache.plugin, + renderer.plugin, }); try app.plugins.build(&app); diff --git a/src/plugins/cache/resource/embeded/shaders/pbr.wgsl b/src/plugins/cache/resource/embeded/shaders/pbr.wgsl new file mode 100644 index 0000000..574617b --- /dev/null +++ b/src/plugins/cache/resource/embeded/shaders/pbr.wgsl @@ -0,0 +1,19 @@ +@vertex +fn vs_main( + @builtin(vertex_index) in_vertex_index: u32 +) -> @builtin(position) vec4f { + var p = vec2f(0.0, 0.0); + if in_vertex_index == 0u { + p = vec2f(-0.5, -0.5); + } else if in_vertex_index == 1u { + p = vec2f(0.5, -0.5); + } else { + p = vec2f(0.0, 0.5); + } + return vec4f(p, 0.0, 1.0); +} + +@fragment +fn fs_main() -> @location(0) vec4f { + return vec4f(0.0, 0.4, 1.0, 1.0); +} diff --git a/src/plugins/cache/resource/pipeline_cache.zig b/src/plugins/cache/resource/pipeline_cache.zig new file mode 100644 index 0000000..0f6cee2 --- /dev/null +++ b/src/plugins/cache/resource/pipeline_cache.zig @@ -0,0 +1,131 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +const std = @import("std"); +const gtl = @import("gtl"); +const wgpu = @import("wgpu"); +const Self = @This(); + +const Material = @import("../../common/component/material.zig"); +const ShaderCache = @import("shader_cache.zig"); + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub const GPUPipeline = struct { + raw: *wgpu.WGPURenderPipelineImpl, +}; + +// +// FIELDS +// + +pipelines: gtl.KeyedSlotMap(Material.Unique, GPUPipeline), +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), + .shader_cache = shader_cache, + .queue = queue, + .device = device, + .alloc = alloc, + }; +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn deinit(self: *Self) void { + // --- FREE PIPELINES --- + for (self.pipelines.items()) |entry| { + wgpu.wgpuRenderPipelineRelease(entry.value.raw); + } + + // --- # --- + self.pipelines.deinit(); +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn getOrCreate( + self: *Self, + key: Material.Unique, +) !*GPUPipeline { + return try self.pipelines.getOrPut(key, .{ + .raw = try self.createPipeline(key), + }); +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +fn createPipeline( + self: *Self, + key: Material.Unique, +) !*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 }, + .vertex = .{ + .module = shader_module.raw, + .entryPoint = .{ .data = "vs_main", .length = wgpu.WGPU_STRLEN }, + }, + .fragment = &wgpu.WGPUFragmentState{ + .module = shader_module.raw, + .entryPoint = .{ .data = "fs_main", .length = wgpu.WGPU_STRLEN }, + .targetCount = 1, + .targets = &wgpu.WGPUColorTargetState{ + .format = 28, + .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; + }; +} diff --git a/src/plugins/cache/resource/shader_cache.zig b/src/plugins/cache/resource/shader_cache.zig new file mode 100644 index 0000000..3fd5e3c --- /dev/null +++ b/src/plugins/cache/resource/shader_cache.zig @@ -0,0 +1,103 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +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; + }; +} diff --git a/src/plugins/cache/root.zig b/src/plugins/cache/root.zig new file mode 100644 index 0000000..8b17205 --- /dev/null +++ b/src/plugins/cache/root.zig @@ -0,0 +1,33 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +// +// PRIVATE +// + +const App = @import("app"); +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 plugin: App.plugin.Plugin = .{ + .name = "Cache", + .build = build, +}; + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +fn build( + _: *App.plugin.Plugin, + app: *App, +) !void { + try app.schedules.add(App.stage.init, lifetime.init); + try app.schedules.add(App.stage.deinit, lifetime.deinit); +} diff --git a/src/plugins/cache/system/lifetime.zig b/src/plugins/cache/system/lifetime.zig new file mode 100644 index 0000000..8ec9066 --- /dev/null +++ b/src/plugins/cache/system/lifetime.zig @@ -0,0 +1,92 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +const std = @import("std"); +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"); + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn init(app: *App) !void { + // --- # --- + const res = try app.resources.getMany(struct { + 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: Remove or place it somewhere else, idk + + // --- TEST --- + _ = try pipelines.getOrCreate(.{}); + _ = try pipelines.getOrCreate(.{}); + + _ = try pipelines.getOrCreate(.{ .shader = .unlit }); + _ = try pipelines.getOrCreate(.{ .shader = .unlit }); + + { // DEBUG + std.debug.print("{s}┏━{s} PIPELINES\n", .{ gtl.ansi.blue, gtl.ansi.reset }); + for (pipelines.pipelines.items(), 0..) |entry, i| { + // std.debug.print("\x1b[33m\x1b[0m {s:<12} {s}\n", .{ plugin.name, plugin.description }); + std.debug.print( + \\{s}┃ ┏━{s} {} + \\{s}┃ ┃ {s}Shader: {s} + \\{s}┃ ┃ {s}Cull Mode: {s} + \\{s}┃ ┃ {s}Blend Mode: {s} + \\{s}┃ ┃ {s}Front Face: {s} + \\{s}┃ ┃ {s}Topology: {s} + \\ + , .{ + gtl.ansi.blue, + gtl.ansi.reset, + i, + gtl.ansi.blue, + gtl.ansi.reset, + @tagName(entry.key.shader), + gtl.ansi.blue, + gtl.ansi.reset, + @tagName(entry.key.cull_mode), + gtl.ansi.blue, + gtl.ansi.reset, + @tagName(entry.key.blend_mode), + gtl.ansi.blue, + gtl.ansi.reset, + @tagName(entry.key.front_face), + gtl.ansi.blue, + gtl.ansi.reset, + @tagName(entry.key.topology), + }); + std.debug.print("{s}┃ ┗━{s}\n", .{ gtl.ansi.blue, gtl.ansi.reset }); + } + std.debug.print("{s}┗━{s}\n", .{ gtl.ansi.blue, gtl.ansi.reset }); + } +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn deinit(app: *App) !void { + // --- # --- + const res = try app.resources.getMany(struct { + shaders: *ShaderCache, + pipelines: *PipelineCache, + }); + + // --- # --- + res.shaders.deinit(); + res.pipelines.deinit(); +} diff --git a/src/plugins/common/component/material.zig b/src/plugins/common/component/material.zig new file mode 100644 index 0000000..20521e7 --- /dev/null +++ b/src/plugins/common/component/material.zig @@ -0,0 +1,52 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +const gtl = @import("gtl"); +const ShaderID = @import("../../cache/resource/shader_cache.zig").ShaderID; +const Handle = gtl.Handle; + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +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 = .{}, diff --git a/src/plugins/common/root.zig b/src/plugins/common/root.zig new file mode 100644 index 0000000..04a5598 --- /dev/null +++ b/src/plugins/common/root.zig @@ -0,0 +1,8 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +// +// COMPONENTS +// + +pub const Material = @import("component/material.zig"); diff --git a/src/plugins/renderer/root.zig b/src/plugins/renderer/root.zig new file mode 100644 index 0000000..b5719d7 --- /dev/null +++ b/src/plugins/renderer/root.zig @@ -0,0 +1,28 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +// +// PRIVATE +// + +const App = @import("app"); + +// +// PUBLIC +// + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub const plugin: App.plugin.Plugin = .{ + .name = "Renderer", + .build = build, +}; + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +fn build( + _: *App.plugin.Plugin, + app: *App, +) !void { + _ = app; +}