From 6fab7b5ead0f49b5bbb3f6cda77c4fb01f0975a8 Mon Sep 17 00:00:00 2001 From: abux Date: Tue, 4 Aug 2026 12:21:52 +0100 Subject: [PATCH] Major update | Replaced previous renderer with a proper clean one --- README.md | 8 +- build.zig | 6 + build.zig.zon | 12 +- src/main.zig | 23 +- .../device/resource/gpu_device.zig | 10 + src/plugins.old/device/root.zig | 30 + .../platform/resource/wgpu_instance.zig | 3 + src/plugins.old/platform/root.zig | 64 ++ .../render/resource/render_graph.zig | 17 + src/plugins.old/render/root.zig | 119 ++++ src/plugins.old/render/template/edge.zig | 14 + src/plugins.old/render/template/node.zig | 16 + .../render/template/render_ctx.zig | 14 + src/plugins.old/render/template/slot.zig | 19 + src/plugins.old/template/root.zig | 28 + .../window/resource/gpu_window.zig | 6 + src/plugins.old/window/resource/window.zig | 8 + src/plugins.old/window/root.zig | 33 + src/plugins.old/window/system/lifetime.zig | 50 ++ src/plugins/cache/resource/mesh_cache.zig | 7 +- src/plugins/common/component/camera.zig | 2 + src/plugins/common/component/material.zig | 4 +- src/plugins/common/component/mesh.zig | 8 +- src/plugins/device/root.zig | 4 +- src/plugins/input/root.zig | 4 +- src/plugins/input/system/input.zig | 12 +- src/plugins/lua/root.zig | 4 +- src/plugins/platform/root.zig | 12 +- src/plugins/playground/root.zig | 129 ++-- .../resource/embeded/shaders/pbr.wgsl | 80 +++ .../resource/render_graph.zig | 17 + .../resource/render_layout.zig | 0 .../resource/render_world.zig | 11 +- .../resource/renderer.zig | 8 +- src/plugins/renderer.depricated/root.zig | 62 ++ .../system/frame.zig | 8 +- .../system/lifetime.zig | 18 +- .../renderer.depricated/system/prepare.zig | 515 ++++++++++++++++ .../renderer.depricated/system/present.zig | 160 +++++ .../renderer.depricated/template/edge.zig | 14 + .../template/frame_object.zig | 4 +- .../template/frame_view.zig | 4 +- .../template/gpu_camera.zig | 0 .../template/gpu_material.zig | 0 .../template/gpu_object.zig | 0 .../template/gpu_pipeline.zig | 0 .../template/gpu_shader.zig | 0 .../template/material.zig | 0 .../renderer.depricated/template/node.zig | 16 + .../template/render_ctx.zig | 14 + .../template/shader_id.zig | 0 .../renderer.depricated/template/slot.zig | 19 + .../renderer/resource/bind_group_cache.zig | 54 ++ .../resource/embeded/shaders/pbr.wgsl | 87 +-- .../resource/embeded/shaders/pbr.wgsl.old | 80 +++ src/plugins/renderer/resource/gpu_frame.zig | 13 + .../renderer/resource/material_assets.zig | 89 +++ .../renderer/resource/material_cache.zig | 41 ++ src/plugins/renderer/resource/mesh_assets.zig | 52 ++ .../renderer/resource/pipeline_cache.zig | 47 ++ src/plugins/renderer/resource/render_data.zig | 47 ++ .../renderer/resource/render_graph.zig | 359 +++++++++++ .../renderer/resource/render_phase.zig | 33 + .../renderer/resource/sampler_cache.zig | 35 ++ .../renderer/resource/shader_cache.zig | 54 ++ .../renderer/resource/texture_cache.zig | 26 + src/plugins/renderer/root.zig | 201 ++++++- src/plugins/renderer/system/extract.zig | 100 +++ src/plugins/renderer/system/prepare.zig | 569 +++++------------- src/plugins/renderer/system/present.zig | 156 ++--- src/plugins/renderer/system/queue.zig | 38 ++ src/plugins/template/root.zig | 4 +- src/plugins/window/resource/gpu_window.zig | 5 + src/plugins/window/resource/window.zig | 8 +- src/plugins/window/root.zig | 28 +- src/plugins/world/root.zig | 40 ++ 76 files changed, 2999 insertions(+), 783 deletions(-) create mode 100644 src/plugins.old/device/resource/gpu_device.zig create mode 100644 src/plugins.old/device/root.zig create mode 100644 src/plugins.old/platform/resource/wgpu_instance.zig create mode 100644 src/plugins.old/platform/root.zig create mode 100644 src/plugins.old/render/resource/render_graph.zig create mode 100644 src/plugins.old/render/root.zig create mode 100644 src/plugins.old/render/template/edge.zig create mode 100644 src/plugins.old/render/template/node.zig create mode 100644 src/plugins.old/render/template/render_ctx.zig create mode 100644 src/plugins.old/render/template/slot.zig create mode 100644 src/plugins.old/template/root.zig create mode 100644 src/plugins.old/window/resource/gpu_window.zig create mode 100644 src/plugins.old/window/resource/window.zig create mode 100644 src/plugins.old/window/root.zig create mode 100644 src/plugins.old/window/system/lifetime.zig create mode 100644 src/plugins/renderer.depricated/resource/embeded/shaders/pbr.wgsl create mode 100644 src/plugins/renderer.depricated/resource/render_graph.zig rename src/plugins/{renderer => renderer.depricated}/resource/render_layout.zig (100%) rename src/plugins/{renderer => renderer.depricated}/resource/render_world.zig (83%) rename src/plugins/{renderer => renderer.depricated}/resource/renderer.zig (92%) create mode 100644 src/plugins/renderer.depricated/root.zig rename src/plugins/{renderer => renderer.depricated}/system/frame.zig (86%) rename src/plugins/{renderer => renderer.depricated}/system/lifetime.zig (91%) create mode 100644 src/plugins/renderer.depricated/system/prepare.zig create mode 100644 src/plugins/renderer.depricated/system/present.zig create mode 100644 src/plugins/renderer.depricated/template/edge.zig rename src/plugins/{renderer => renderer.depricated}/template/frame_object.zig (87%) rename src/plugins/{renderer => renderer.depricated}/template/frame_view.zig (54%) rename src/plugins/{renderer => renderer.depricated}/template/gpu_camera.zig (100%) rename src/plugins/{renderer => renderer.depricated}/template/gpu_material.zig (100%) rename src/plugins/{renderer => renderer.depricated}/template/gpu_object.zig (100%) rename src/plugins/{renderer => renderer.depricated}/template/gpu_pipeline.zig (100%) rename src/plugins/{renderer => renderer.depricated}/template/gpu_shader.zig (100%) rename src/plugins/{renderer => renderer.depricated}/template/material.zig (100%) create mode 100644 src/plugins/renderer.depricated/template/node.zig create mode 100644 src/plugins/renderer.depricated/template/render_ctx.zig rename src/plugins/{renderer => renderer.depricated}/template/shader_id.zig (100%) create mode 100644 src/plugins/renderer.depricated/template/slot.zig create mode 100644 src/plugins/renderer/resource/bind_group_cache.zig create mode 100644 src/plugins/renderer/resource/embeded/shaders/pbr.wgsl.old create mode 100644 src/plugins/renderer/resource/gpu_frame.zig create mode 100644 src/plugins/renderer/resource/material_assets.zig create mode 100644 src/plugins/renderer/resource/material_cache.zig create mode 100644 src/plugins/renderer/resource/mesh_assets.zig create mode 100644 src/plugins/renderer/resource/pipeline_cache.zig create mode 100644 src/plugins/renderer/resource/render_data.zig create mode 100644 src/plugins/renderer/resource/render_graph.zig create mode 100644 src/plugins/renderer/resource/render_phase.zig create mode 100644 src/plugins/renderer/resource/sampler_cache.zig create mode 100644 src/plugins/renderer/resource/shader_cache.zig create mode 100644 src/plugins/renderer/resource/texture_cache.zig create mode 100644 src/plugins/renderer/system/extract.zig create mode 100644 src/plugins/renderer/system/queue.zig create mode 100644 src/plugins/window/resource/gpu_window.zig create mode 100644 src/plugins/world/root.zig diff --git a/README.md b/README.md index f6a0894..8c9908f 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,8 @@ paru -S --needed sdl3 wgpu-native ## Architecture ```bash -[ENTITY] -| mesh: Handle(Mesh) -| material: Handle(Material) -| transform: Transform2D or Transform3D +RenderData -> RenderPhase -> RenderGraph +Extract -> Prepare -> Queue -> Present ``` ## TODO @@ -21,7 +19,7 @@ paru -S --needed sdl3 wgpu-native - [x] Window - [x] Device - [x] Cache -- [ ] Renderer +- [x] RenderGraph - [ ] Hot Reloading ### Engine diff --git a/build.zig b/build.zig index 9691674..7d66dae 100644 --- a/build.zig +++ b/build.zig @@ -66,6 +66,12 @@ pub fn build(b: *std.Build) void { }); exe.root_module.addImport("math", math.module("NYXMath")); + const world = b.dependency("NYXWorld", .{ + .target = target, + .optimize = optimize, + }); + exe.root_module.addImport("world", world.module("NYXWorld")); + // // SYSTEM DEPENDENCIES // diff --git a/build.zig.zon b/build.zig.zon index 6a47079..ce0dc6e 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -13,17 +13,21 @@ .hash = "zla-0.0.0-z62_impQAABHbrWUmoFLsl_HuD8CNS-k91z6laMJEQ62", }, .app = .{ - .url = "git+http://2.120.146.66:25569/abux/NYXA.git#9cbd0804a6008243b4cff387285a5e09cf1e03ed", - .hash = "app-0.0.0-AOV9_YDvAAAjjuCldX-9p7mt3Oswbv8bTB2rvcZ67Fn1", + .url = "git+http://192.168.0.11:25569/abux/NYXA.git#97c629a78b5a99860cd16405a8741ed91c2411ce", + .hash = "app-0.0.0-AOV9_dxlAADNgU13ahJF7Qn934LLC-5M1KWAxVwDgaoW", }, .gtl = .{ - .url = "git+http://192.168.0.11:25569/abux/GTL.git#f9772950b5097bbbda39cd68d86f6b6062c41fec", - .hash = "gtl-0.0.0-e3QoJPNxAABpkY1nvYNN7r02CI4z2aki1_FHDPzdWi9P", + .url = "git+http://192.168.0.11:25569/abux/GTL.git#3c411f25b9a60d4633fa283a076a1cc141eb9902", + .hash = "gtl-0.0.0-e3QoJM_pAADNA1GlFiA-h7cRWuLH5rAspOCDy4aMXLd_", }, .NYXMath = .{ .url = "git+http://192.168.0.11:25569/abux/NYXMath.git#cb7b54995bdb46321f112bba2a704d58cf53aeb1", .hash = "NYXMath-0.0.0-BD6ST9rOAAA0uSpPK_woZrta3nTh0f5w7hFewJ-e2Kv1", }, + .NYXWorld = .{ + .url = "git+http://192.168.0.11:25569/abux/NYXWorld.git#e6c46e23716614473500931dbad16de9c7b36f3c", + .hash = "NYXWorld-0.0.0-jIP7UtZ3AAB4FoXBjV1iAO-0EWFssFr-cxAnIK4Y7_LR", + }, }, .paths = .{ diff --git a/src/main.zig b/src/main.zig index 3ff10db..b0b5b07 100644 --- a/src/main.zig +++ b/src/main.zig @@ -15,6 +15,7 @@ const App = @import("app"); // const template = @import("plugins/template/root.zig"); +const world = @import("plugins/world/root.zig"); const playground = @import("plugins/playground/root.zig"); const lua = @import("plugins/lua/root.zig"); const common = @import("plugins/common/root.zig"); @@ -32,8 +33,15 @@ pub fn main(init: std.process.Init) !void { var app: App = .init(init.gpa); defer app.deinit(); + // --- # --- + errdefer { + app.schedules.runReversed(App.stage.deinit, &app) catch unreachable; + app.deinit(); + } + // --- ADD PLUGINS --- try app.plugins.addMany(&.{ + world.plugin, lua.plugin, platform.plugin, device.plugin, @@ -45,25 +53,10 @@ pub fn main(init: std.process.Init) !void { }); try app.plugins.build(&app); - // DEBUG - if (gtl.log.print(.debug, "PLUGINS", gtl.ansi.yellow)) |v| { - defer v.end(); - - for (app.plugins.data.items) |plugin| { - v.write("{s}", .{plugin.name}); - } - } - // --- LIFETIME --- try app.schedules.run(App.stage.init, &app); defer app.schedules.runReversed(App.stage.deinit, &app) catch unreachable; - // --- # --- - errdefer { - app.schedules.runReversed(App.stage.deinit, &app) catch unreachable; - app.deinit(); - } - // --- MAIN LOOP --- while (true) { // --- # --- diff --git a/src/plugins.old/device/resource/gpu_device.zig b/src/plugins.old/device/resource/gpu_device.zig new file mode 100644 index 0000000..2d631dd --- /dev/null +++ b/src/plugins.old/device/resource/gpu_device.zig @@ -0,0 +1,10 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +const wgpu = @import("wgpu"); + +// +// FIELDS +// + +raw: *wgpu.WGPUDeviceImpl, diff --git a/src/plugins.old/device/root.zig b/src/plugins.old/device/root.zig new file mode 100644 index 0000000..30f1a04 --- /dev/null +++ b/src/plugins.old/device/root.zig @@ -0,0 +1,30 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +// +// PRIVATE +// + +const App = @import("app"); + +// +// PUBLIC +// + +pub const GPUDevice = @import("resource/gpu_device.zig"); + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub const plugin: App.Plugin = .{ + .name = "Device", + .build = build, +}; + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +fn build( + _: *App.Plugin, + app: *App, +) !void { + _ = app; +} diff --git a/src/plugins.old/platform/resource/wgpu_instance.zig b/src/plugins.old/platform/resource/wgpu_instance.zig new file mode 100644 index 0000000..d9f6e23 --- /dev/null +++ b/src/plugins.old/platform/resource/wgpu_instance.zig @@ -0,0 +1,3 @@ +const wgpu = @import("wgpu"); + +raw: *wgpu.WGPUInstanceImpl, diff --git a/src/plugins.old/platform/root.zig b/src/plugins.old/platform/root.zig new file mode 100644 index 0000000..53b6639 --- /dev/null +++ b/src/plugins.old/platform/root.zig @@ -0,0 +1,64 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +// +// PRIVATE +// + +const std = @import("std"); +const sdl = @import("sdl"); +const wgpu = @import("wgpu"); +const App = @import("app"); + +// +// PUBLIC +// + +pub const WGPUInstance = @import("resource/wgpu_instance.zig"); + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub const plugin: App.plugin.Plugin = .{ + .name = "Platform", + .build = build, +}; + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +fn build( + _: *App.plugin.Plugin, + app: *App, +) !void { + try app.schedules.add(App.stage.init, init); + try app.schedules.add(App.stage.deinit, deinit); +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn init(app: *App) !void { + // --- SDL3 INIT --- + if (!sdl.SDL_Init(sdl.SDL_INIT_VIDEO)) { + std.debug.print("[SDL_ERROR] SDL_Init: {s}\n", .{sdl.SDL_GetError()}); + return error.SDL_INIT; + } + + // --- WGPU INIT --- + try app.resources.set(WGPUInstance{ + .raw = wgpu.wgpuCreateInstance(&wgpu.WGPUInstanceDescriptor{}) orelse return { + return error.WGPU_Init; + }, + }); +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn deinit(app: *App) !void { + // --- # --- + const res = try app.resources.getMany(struct { + instance: *WGPUInstance, + }); + + // --- # --- + wgpu.wgpuInstanceRelease(res.instance.raw); + sdl.SDL_Quit(); +} diff --git a/src/plugins.old/render/resource/render_graph.zig b/src/plugins.old/render/resource/render_graph.zig new file mode 100644 index 0000000..6fc33e5 --- /dev/null +++ b/src/plugins.old/render/resource/render_graph.zig @@ -0,0 +1,17 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +const gtl = @import("gtl"); +const Node = @import("../template/node.zig"); +const Slot = @import("../template/slot.zig"); +const Edge = @import("../template/edge.zig").Edge; +const Handle = gtl.Handle; + +// +// FIELDS +// + +nodes: gtl.SlotMap(Node), +edges: gtl.SlotMap(Edge), +slots: gtl.SlotMap(Slot), +order: ?[]Handle(Node) = null, diff --git a/src/plugins.old/render/root.zig b/src/plugins.old/render/root.zig new file mode 100644 index 0000000..e9828e4 --- /dev/null +++ b/src/plugins.old/render/root.zig @@ -0,0 +1,119 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +// +// PRIVATE +// + +const std = @import("std"); +const gtl = @import("gtl"); +const App = @import("app"); + +// +// PUBLIC +// + +pub const RenderGraph = @import("resource/render_graph.zig"); +pub const Node = @import("template/node.zig"); +pub const Edge = @import("template/edge.zig").Edge; +pub const Slot = @import("template/slot.zig"); +pub const RenderCTX = @import("template/render_ctx.zig"); + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub const plugin: App.Plugin = .{ + .name = "Render", + .build = build, +}; + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +fn build( + _: *App.Plugin, + app: *App, +) !void { + try app.schedules.add(App.stage.init, init); + try app.schedules.add(App.stage.deinit, deinit); +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +fn init(app: *App) !void { + const graph = try app.resources.getOrSet(RenderGraph{ + .edges = .init(app.alloc), + .nodes = .init(app.alloc), + .slots = .init(app.alloc), + }); + + const main = try graph.nodes.put(.{ + .name = "main", + .run = mainNode, + }); + + const present = try graph.nodes.put(.{ + .name = "present", + .run = presentNode, + }); + + _ = try graph.edges.put( + .{ + .order = .{ + .from = main, + .to = present, + }, + }, + ); + + var render_ctx: RenderCTX = .{ + .graph = graph, + .device = undefined, + .encoder = undefined, + }; + + if (graph.order == null) { + var order: std.ArrayList(gtl.Handle(Node)) = .empty; + defer order.deinit(app.alloc); + + try order.append(app.alloc, main); + try order.append(app.alloc, present); + + graph.order = try order.toOwnedSlice(app.alloc); + } + + if (graph.order) |order| { + for (order) |node_id| { + const node = graph.nodes.get( + node_id, + ) orelse return error.MissingNode; + + try node.run(&render_ctx); + } + } +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +fn deinit(app: *App) !void { + const graph = app.resources.getPtr( + RenderGraph, + ) orelse return error.RenderGraphNotSet; + + if (graph.order) |order| app.alloc.free(order); + graph.nodes.deinit(); + graph.edges.deinit(); + graph.slots.deinit(); +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +fn mainNode(ctx: *RenderCTX) !void { + std.debug.print("[MAIN_PASS]\n", .{}); + _ = ctx; +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +fn presentNode(ctx: *RenderCTX) !void { + std.debug.print("[PRESENT_PASS]\n", .{}); + _ = ctx; +} diff --git a/src/plugins.old/render/template/edge.zig b/src/plugins.old/render/template/edge.zig new file mode 100644 index 0000000..2733630 --- /dev/null +++ b/src/plugins.old/render/template/edge.zig @@ -0,0 +1,14 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +const gtl = @import("gtl"); +const Node = @import("node.zig"); +const Slot = @import("slot.zig"); +const Handle = gtl.Handle; + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub const Edge = union(enum) { + data: struct { producer: Handle(Slot), consumer: Handle(Slot) }, + order: struct { from: Handle(Node), to: Handle(Node) }, +}; diff --git a/src/plugins.old/render/template/node.zig b/src/plugins.old/render/template/node.zig new file mode 100644 index 0000000..276206b --- /dev/null +++ b/src/plugins.old/render/template/node.zig @@ -0,0 +1,16 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +const gtl = @import("gtl"); +const Slot = @import("slot.zig"); +const RenderCTX = @import("render_ctx.zig"); +const Handle = gtl.Handle; + +// +// FIELDS +// + +name: []const u8, +inputs: []const Handle(Slot) = &.{}, +outputs: []const Handle(Slot) = &.{}, +run: *const fn (*RenderCTX) anyerror!void, diff --git a/src/plugins.old/render/template/render_ctx.zig b/src/plugins.old/render/template/render_ctx.zig new file mode 100644 index 0000000..5520c17 --- /dev/null +++ b/src/plugins.old/render/template/render_ctx.zig @@ -0,0 +1,14 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +const wgpu = @import("wgpu"); +const RenderGraph = @import("../resource/render_graph.zig"); + +// +// FIELDS +// + +device: *wgpu.WGPUDeviceImpl, +encoder: *wgpu.WGPUCommandEncoderImpl, + +graph: *const RenderGraph, diff --git a/src/plugins.old/render/template/slot.zig b/src/plugins.old/render/template/slot.zig new file mode 100644 index 0000000..27746eb --- /dev/null +++ b/src/plugins.old/render/template/slot.zig @@ -0,0 +1,19 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +const wgpu = @import("wgpu"); + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub const Value = union(enum) { + buffer: *wgpu.WGPUBufferImpl, + bind_group: *wgpu.WGPUBindGroupImpl, + texture_view: *wgpu.WGPUTextureViewImpl, +}; + +// +// FIELDS +// + +name: []const u8, +value: ?Value = null, diff --git a/src/plugins.old/template/root.zig b/src/plugins.old/template/root.zig new file mode 100644 index 0000000..5213e78 --- /dev/null +++ b/src/plugins.old/template/root.zig @@ -0,0 +1,28 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +// +// PRIVATE +// + +const App = @import("app"); + +// +// PUBLIC +// + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub const plugin: App.Plugin = .{ + .name = "Template", + .build = build, +}; + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +fn build( + _: *App.Plugin, + app: *App, +) !void { + _ = app; +} diff --git a/src/plugins.old/window/resource/gpu_window.zig b/src/plugins.old/window/resource/gpu_window.zig new file mode 100644 index 0000000..abd5aa7 --- /dev/null +++ b/src/plugins.old/window/resource/gpu_window.zig @@ -0,0 +1,6 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +const sdl = @import("sdl"); + +raw: *sdl.SDL_Window, diff --git a/src/plugins.old/window/resource/window.zig b/src/plugins.old/window/resource/window.zig new file mode 100644 index 0000000..94974c3 --- /dev/null +++ b/src/plugins.old/window/resource/window.zig @@ -0,0 +1,8 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +title: []const u8 = "NYXEngine", +width: u32 = 800, +height: u32 = 600, + +resizeable: bool = true, diff --git a/src/plugins.old/window/root.zig b/src/plugins.old/window/root.zig new file mode 100644 index 0000000..183791a --- /dev/null +++ b/src/plugins.old/window/root.zig @@ -0,0 +1,33 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +// +// PRIVATE +// + +const App = @import("app"); +const lifetime = @import("system/lifetime.zig"); + +// +// PUBLIC +// + +pub const Window = @import("resource/window.zig"); +pub const GPUWindow = @import("resource/gpu_window.zig"); + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub const plugin: App.Plugin = .{ + .name = "Window", + .build = build, +}; + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +fn build( + _: *App.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.old/window/system/lifetime.zig b/src/plugins.old/window/system/lifetime.zig new file mode 100644 index 0000000..f3c472e --- /dev/null +++ b/src/plugins.old/window/system/lifetime.zig @@ -0,0 +1,50 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +const sdl = @import("sdl"); +const gtl = @import("gtl"); + +const App = @import("app"); +const Window = @import("../resource/window.zig"); +const GPUWindow = @import("../resource/gpu_window.zig"); + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn init(app: *App) !void { + // --- ENSURE WINDOW --- + const window = try app.resources.getOrSet(Window{}); + + // --- CREATE GPU WINDOW --- + try app.resources.set(GPUWindow{ + .raw = sdl.SDL_CreateWindow( + window.title.ptr, + @intCast(window.width), + @intCast(window.height), + 0, + ) orelse { + gtl.log.err("{s}", .{sdl.SDL_GetError()}, @src()); + return error.SDLWindow; + }, + }); + + // --- DEBUG --- + if (gtl.log.print(.debug, "Window", gtl.ansi.blue)) |v| { + defer v.end(); + + v.write("title: \"{s}\"", .{window.title}); + v.write("width: {}", .{window.width}); + v.write("height: {}", .{window.height}); + } +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn deinit(app: *App) !void { + // --- GET GPU WINDOW --- + const gpu_window = app.resources.getPtr( + GPUWindow, + ) orelse return error.GPUWindowNotSet; + + // --- # --- + sdl.SDL_DestroyWindow(gpu_window.raw); +} diff --git a/src/plugins/cache/resource/mesh_cache.zig b/src/plugins/cache/resource/mesh_cache.zig index a14501d..d76f1e9 100644 --- a/src/plugins/cache/resource/mesh_cache.zig +++ b/src/plugins/cache/resource/mesh_cache.zig @@ -62,10 +62,11 @@ pub fn init( /// ---------------------------------------------------- pub fn deinit(self: *Self) void { // --- FREE BUFFERS --- - for (self.data.items()) |entry| { - wgpu.wgpuBufferRelease(entry.value.vbuf); + var it = self.data.valueIterator(); + while (it.next()) |v| { + wgpu.wgpuBufferRelease(v.vbuf); - if (entry.value.ibuf) |ibuf| { + if (v.ibuf) |ibuf| { wgpu.wgpuBufferRelease(ibuf); } } diff --git a/src/plugins/common/component/camera.zig b/src/plugins/common/component/camera.zig index 260c265..d3013a3 100644 --- a/src/plugins/common/component/camera.zig +++ b/src/plugins/common/component/camera.zig @@ -20,3 +20,5 @@ target: union(enum) { order: u32 = 0, priority: u32 = 0, + +clear_color: math.Color = .black, diff --git a/src/plugins/common/component/material.zig b/src/plugins/common/component/material.zig index 91f8efa..c97c3e7 100644 --- a/src/plugins/common/component/material.zig +++ b/src/plugins/common/component/material.zig @@ -1,5 +1,5 @@ const gtl = @import("gtl"); -const Material = @import("../../renderer/template/material.zig"); +const MaterialAsset = @import("../../renderer/resource/material_assets.zig").Asset; const Handle = gtl.Handle; -value: Handle(Material), +value: Handle(MaterialAsset), diff --git a/src/plugins/common/component/mesh.zig b/src/plugins/common/component/mesh.zig index 00a25f9..93d5644 100644 --- a/src/plugins/common/component/mesh.zig +++ b/src/plugins/common/component/mesh.zig @@ -1,3 +1,7 @@ -const MeshID = @import("../../cache/resource/mesh_cache.zig").MeshID; +const gtl = @import("gtl"); +const MeshAssets = @import("../../renderer/resource/mesh_assets.zig"); -value: MeshID, +value: gtl.Handle(MeshAssets.Asset), + +// const MeshID = @import("../../cache/resource/mesh_cache.zig").MeshID; +// value: MeshID, diff --git a/src/plugins/device/root.zig b/src/plugins/device/root.zig index 434a575..e3159f6 100644 --- a/src/plugins/device/root.zig +++ b/src/plugins/device/root.zig @@ -17,7 +17,7 @@ pub const WGPUDevice = @import("resource/wgpu_device.zig"); /// ---------------------------------------------------- /// ---------------------------------------------------- -pub const plugin: App.plugin.Plugin = .{ +pub const plugin: App.Plugin = .{ .name = "Device", .build = build, }; @@ -25,7 +25,7 @@ pub const plugin: App.plugin.Plugin = .{ /// ---------------------------------------------------- /// ---------------------------------------------------- fn build( - _: *App.plugin.Plugin, + _: *App.Plugin, app: *App, ) !void { try app.schedules.add(App.stage.init, lifetime.init); diff --git a/src/plugins/input/root.zig b/src/plugins/input/root.zig index a23f30a..91b2168 100644 --- a/src/plugins/input/root.zig +++ b/src/plugins/input/root.zig @@ -14,7 +14,7 @@ const input = @import("system/input.zig"); /// ---------------------------------------------------- /// ---------------------------------------------------- -pub const plugin: App.plugin.Plugin = .{ +pub const plugin: App.Plugin = .{ .name = "Input", .build = build, }; @@ -22,7 +22,7 @@ pub const plugin: App.plugin.Plugin = .{ /// ---------------------------------------------------- /// ---------------------------------------------------- fn build( - _: *App.plugin.Plugin, + _: *App.Plugin, app: *App, ) !void { try app.schedules.add(App.stage.inputs, input.pollEvents); diff --git a/src/plugins/input/system/input.zig b/src/plugins/input/system/input.zig index 73ff355..9c88e0a 100644 --- a/src/plugins/input/system/input.zig +++ b/src/plugins/input/system/input.zig @@ -8,6 +8,7 @@ const wgpu = @import("wgpu"); const App = @import("app"); const Window = @import("../../window/resource/window.zig"); +const GPUWindow = @import("../../window/resource/gpu_window.zig"); const Resized = @import("../../window/event/resized.zig"); const WGPUInstance = @import("../../platform/resource/wgpu_instance.zig"); const WGPUDevice = @import("../../device/resource/wgpu_device.zig"); @@ -18,8 +19,9 @@ pub fn pollEvents(app: *App) !void { // --- # --- const res = try app.resources.getMany(struct { instance: *WGPUInstance, - window: *Window, device: *WGPUDevice, + window: *Window, + gpu_window: *GPUWindow, }); var event: sdl.SDL_Event = undefined; @@ -33,12 +35,16 @@ pub fn pollEvents(app: *App) !void { .height = @intCast(event.window.data2), }); + // --- SET SIZE --- + res.window.width = @intCast(event.window.data1); + res.window.height = @intCast(event.window.data2); + // --- RELEASE --- - sdl_glue.wgpuSurfaceUnconfigure(res.window.surface); + sdl_glue.wgpuSurfaceUnconfigure(res.gpu_window.surface); // --- CONFIGURE SURFACE --- sdl_glue.wgpuSurfaceConfigure( - res.window.surface, + res.gpu_window.surface, &sdl_glue.WGPUSurfaceConfiguration{ .device = @ptrCast(res.device.raw), .format = 28, // TODO: Get format properly, idk, store it somewhere diff --git a/src/plugins/lua/root.zig b/src/plugins/lua/root.zig index d6a0acb..1b4e95c 100644 --- a/src/plugins/lua/root.zig +++ b/src/plugins/lua/root.zig @@ -14,7 +14,7 @@ const Zla = @import("zla"); /// ---------------------------------------------------- /// ---------------------------------------------------- -pub const plugin: App.plugin.Plugin = .{ +pub const plugin: App.Plugin = .{ .name = "LUA", .build = build, }; @@ -22,7 +22,7 @@ pub const plugin: App.plugin.Plugin = .{ /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn build( - _: *App.plugin.Plugin, + _: *App.Plugin, app: *App, ) !void { // --- LIFETIME --- diff --git a/src/plugins/platform/root.zig b/src/plugins/platform/root.zig index 53b6639..e76ae52 100644 --- a/src/plugins/platform/root.zig +++ b/src/plugins/platform/root.zig @@ -18,7 +18,7 @@ pub const WGPUInstance = @import("resource/wgpu_instance.zig"); /// ---------------------------------------------------- /// ---------------------------------------------------- -pub const plugin: App.plugin.Plugin = .{ +pub const plugin: App.Plugin = .{ .name = "Platform", .build = build, }; @@ -26,7 +26,7 @@ pub const plugin: App.plugin.Plugin = .{ /// ---------------------------------------------------- /// ---------------------------------------------------- fn build( - _: *App.plugin.Plugin, + _: *App.Plugin, app: *App, ) !void { try app.schedules.add(App.stage.init, init); @@ -54,11 +54,11 @@ pub fn init(app: *App) !void { /// ---------------------------------------------------- pub fn deinit(app: *App) !void { // --- # --- - const res = try app.resources.getMany(struct { - instance: *WGPUInstance, - }); + const instance = try app.resources.require( + WGPUInstance, + ); // --- # --- - wgpu.wgpuInstanceRelease(res.instance.raw); + wgpu.wgpuInstanceRelease(instance.raw); sdl.SDL_Quit(); } diff --git a/src/plugins/playground/root.zig b/src/plugins/playground/root.zig index 58aadda..3007217 100644 --- a/src/plugins/playground/root.zig +++ b/src/plugins/playground/root.zig @@ -7,13 +7,17 @@ const std = @import("std"); const App = @import("app"); +const World = @import("world"); + +const MeshAssets = @import("../renderer/resource/mesh_assets.zig"); +const MaterialAssets = @import("../renderer/resource/material_assets.zig"); 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 RenderWorld = @import("../renderer/resource/render_world.zig"); +// const RenderWorld = @import("../renderer/resource/render_world.zig"); // // PUBLIC @@ -21,7 +25,7 @@ const RenderWorld = @import("../renderer/resource/render_world.zig"); /// ---------------------------------------------------- /// ---------------------------------------------------- -pub const plugin: App.plugin.Plugin = .{ +pub const plugin: App.Plugin = .{ .name = "Playground", .build = build, }; @@ -29,10 +33,14 @@ pub const plugin: App.plugin.Plugin = .{ /// ---------------------------------------------------- /// ---------------------------------------------------- fn build( - _: *App.plugin.Plugin, + _: *App.Plugin, app: *App, ) !void { - 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 {}; @@ -41,79 +49,62 @@ const FlyCamera = struct {}; /// ---------------------------------------------------- fn start(app: *App) !void { // --- # --- - const render_world = app.resources.getPtr( - RenderWorld, - ).?; + const res = try app.resources.getMany(struct { + world: *World, + mesh_assets: *MeshAssets, + material_assets: *MaterialAssets, + }); - // --- CAMERA --- - try app.world.spawn(.{ - FlyCamera{}, + try res.world.spawn(.{ Camera{ .projection = .{ .perspective = .{}, }, }, - Transform3D{ - .position = .new(.{ 0, -2.5, -6.5 }), - .rotation = .fromEuler(.init( - -std.math.degreesToRadians(30), - 0, - 0, - )), - }, - }); - - // --- # --- - try app.world.spawn(.{ - Mesh{ .value = .cube }, - Material{ - .value = try render_world.materials.put(.{ - .style = .{ - .albedo = .{ 0.8, 0.2, 0.2, 1.0 }, - }, - .unique = .{ - .cull_mode = .none, - }, - }), - }, Transform3D{}, }); - const count: usize = 200; - const radius: f32 = 5.0; + try res.world.spawn(.{ + Mesh{ .value = try res.mesh_assets.put(.{}) }, + Material{ .value = try res.material_assets.put(.{}) }, + Transform3D{}, + }); - for (0..count) |i| { - const angle = (@as(f32, @floatFromInt(i)) / @as(f32, @floatFromInt(count))) * std.math.tau; - - try app.world.spawn(.{ - Mesh{ .value = .cube }, - Material{ - .value = try render_world.materials.put(.{ - .style = .{ - .albedo = .{ - 0.2, - 0.0 + (angle * std.math.pi) * 0.01, - 0.0 + (angle * std.math.pi) * 0.02, - 1, - }, - }, - .unique = .{ - .cull_mode = .none, - }, - }), - }, - Transform3D{ - .position = .new(.{ - @cos(angle) * radius, - 0, - @sin(angle) * radius, - }), - .rotation = .fromEuler(.init( - std.math.degreesToRadians(180), - 0, - 0, - )), - }, - }); - } + // const count: usize = 200; + // const radius: f32 = 5.0; + // + // for (0..count) |i| { + // const angle = (@as(f32, @floatFromInt(i)) / @as(f32, @floatFromInt(count))) * std.math.tau; + // + // try res.world.spawn(.{ + // Mesh{ .value = .cube }, + // Material{ + // .value = try res.render_world.materials.put(.{ + // .style = .{ + // .albedo = .{ + // 0.2, + // 0.0 + (angle * std.math.pi) * 0.01, + // 0.0 + (angle * std.math.pi) * 0.02, + // 1, + // }, + // }, + // .unique = .{ + // .cull_mode = .none, + // }, + // }), + // }, + // Transform3D{ + // .position = .new(.{ + // @cos(angle) * radius, + // 0, + // @sin(angle) * radius, + // }), + // .rotation = .fromEuler(.init( + // std.math.degreesToRadians(180), + // 0, + // 0, + // )), + // }, + // }); + // } } diff --git a/src/plugins/renderer.depricated/resource/embeded/shaders/pbr.wgsl b/src/plugins/renderer.depricated/resource/embeded/shaders/pbr.wgsl new file mode 100644 index 0000000..cb094f9 --- /dev/null +++ b/src/plugins/renderer.depricated/resource/embeded/shaders/pbr.wgsl @@ -0,0 +1,80 @@ +// ---------------------------------------------------- +// ---------------------------------------------------- +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 CameraUniform { + view: mat4x4, + projection: mat4x4, +}; + +// ---------------------------------------------------- +// ---------------------------------------------------- +struct ModelUniform { + data: mat4x4, +}; + +// ---------------------------------------------------- +// ---------------------------------------------------- +struct Material { + albedo: vec4f, + metallic: f32, + roughness: f32, + _pad0: vec2f, +}; + +// +// UNIFORMS +// + +@group(0) @binding(0) +var camera: CameraUniform; + +@group(1) @binding(0) +var model: ModelUniform; + +@group(2) @binding(0) +var material: Material; + +// +// VERTEX +// + +@vertex +fn vs_main(in: VertexInput) -> VertexOutput { + // --- # --- + var out: VertexOutput; + + // --- # --- + out.position = camera.projection * camera.view * model.data * 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); +} diff --git a/src/plugins/renderer.depricated/resource/render_graph.zig b/src/plugins/renderer.depricated/resource/render_graph.zig new file mode 100644 index 0000000..353aa1f --- /dev/null +++ b/src/plugins/renderer.depricated/resource/render_graph.zig @@ -0,0 +1,17 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +const gtl = @import("gtl"); +pub const Node = @import("../template/node.zig"); +pub const Slot = @import("../template/slot.zig"); +pub const Edge = @import("../template/edge.zig").Edge; +const Handle = gtl.Handle; + +// +// FIELDS +// + +nodes: gtl.FixedSlotMap(Node, 24), +edges: gtl.FixedSlotMap(Edge, 24), +slots: gtl.FixedSlotMap(Slot, 24), +order: ?[]Handle(Node) = null, diff --git a/src/plugins/renderer/resource/render_layout.zig b/src/plugins/renderer.depricated/resource/render_layout.zig similarity index 100% rename from src/plugins/renderer/resource/render_layout.zig rename to src/plugins/renderer.depricated/resource/render_layout.zig diff --git a/src/plugins/renderer/resource/render_world.zig b/src/plugins/renderer.depricated/resource/render_world.zig similarity index 83% rename from src/plugins/renderer/resource/render_world.zig rename to src/plugins/renderer.depricated/resource/render_world.zig index e347288..9efb460 100644 --- a/src/plugins/renderer/resource/render_world.zig +++ b/src/plugins/renderer.depricated/resource/render_world.zig @@ -5,7 +5,8 @@ const std = @import("std"); const gtl = @import("gtl"); const wgpu = @import("wgpu"); -pub const Entity = u64; // TODO: You lazy retard +const App = @import("app"); +const World = @import("world"); // --- CAMERA --- pub const GPUCamera = @import("../template/gpu_camera.zig"); @@ -39,12 +40,12 @@ pub const UnlitCode = @embedFile("embeded/shaders/pbr.wgsl"); // FIELDS // -gpu_cameras: gtl.KeyedSlotMap(Entity, GPUCamera), -gpu_objects: gtl.KeyedSlotMap(Entity, GPUObject), +gpu_cameras: gtl.KeyedSlotMap(World.Entity, GPUCamera), +gpu_objects: gtl.KeyedSlotMap(World.Entity, GPUObject), 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), +frame_views: gtl.FixedArrayList(FrameView, 8), +frame_objects: gtl.FixedArrayList(FrameObject, 256), materials: gtl.SlotMap(Material), // TODO: Move this to assets resource or sum diff --git a/src/plugins/renderer/resource/renderer.zig b/src/plugins/renderer.depricated/resource/renderer.zig similarity index 92% rename from src/plugins/renderer/resource/renderer.zig rename to src/plugins/renderer.depricated/resource/renderer.zig index 0d53a9d..e83e045 100644 --- a/src/plugins/renderer/resource/renderer.zig +++ b/src/plugins/renderer.depricated/resource/renderer.zig @@ -3,6 +3,8 @@ const std = @import("std"); const gtl = @import("gtl"); +const App = @import("app"); +const World = @import("world"); const Material = @import("../../common/component/material.zig"); const Mesh = @import("../../cache/resource/mesh_cache.zig").MeshID; const Handle = gtl.Handle; @@ -13,18 +15,18 @@ const Self = @This(); pub const DrawCMD = union(enum) { draw: struct { mesh: Mesh, - entity: u64, + entity: World.Entity, material: Handle(Material), }, draw_indexed: struct { mesh: Mesh, - entity: u64, + entity: World.Entity, material: Handle(Material), }, begin_pass: struct { - camera: u64, // TODO: Change it to Entity type instead + camera: World.Entity, color: [4]f32 = .{ 0, 0, 0, 255 }, load_op: enum { load, diff --git a/src/plugins/renderer.depricated/root.zig b/src/plugins/renderer.depricated/root.zig new file mode 100644 index 0000000..8af2f74 --- /dev/null +++ b/src/plugins/renderer.depricated/root.zig @@ -0,0 +1,62 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +// +// PRIVATE +// + +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"); + +// +// PUBLIC +// + +const Renderer = @import("resource/renderer.zig"); +const RenderWorld = @import("resource/render_world.zig"); +const RenderLayout = @import("resource/render_layout.zig"); + +pub const RenderGraph = @import("resource/render_graph.zig"); +pub const Node = @import("template/node.zig"); +pub const Slot = @import("template/slot.zig"); +pub const Edge = @import("template/edge.zig").Edge; +pub const RenderCTX = @import("template/render_ctx.zig"); + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub const plugin: App.Plugin = .{ + .name = "Renderer", + .build = build, +}; + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +fn build( + _: *App.Plugin, + app: *App, +) !void { + // --- LIFETIME --- + try app.schedules.add(App.stage.init, lifetime.init); + try app.schedules.add(App.stage.deinit, lifetime.deinit); + + // --- FRAME --- + 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.shaders, + prepare.pipelines, + + prepare.cameras, + prepare.objects, + prepare.materials, + }); + + // --- DRAW --- + try app.schedules.add(App.stage.draw, present.draw); +} diff --git a/src/plugins/renderer/system/frame.zig b/src/plugins/renderer.depricated/system/frame.zig similarity index 86% rename from src/plugins/renderer/system/frame.zig rename to src/plugins/renderer.depricated/system/frame.zig index c011e33..e1823cf 100644 --- a/src/plugins/renderer/system/frame.zig +++ b/src/plugins/renderer.depricated/system/frame.zig @@ -23,7 +23,7 @@ pub fn begin(app: *App) !void { res.renderer.clear(); // --- # --- - for (res.render_world.frame_views.items) |view| { + for (res.render_world.frame_views.items()) |view| { // --- NEW PASS --- try res.renderer.draw(.{ .begin_pass = .{ @@ -34,7 +34,7 @@ pub fn begin(app: *App) !void { 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(.{ .draw = .{ .material = obj.material, @@ -46,8 +46,8 @@ pub fn begin(app: *App) !void { } // --- YES --- - res.render_world.frame_views.clearRetainingCapacity(); - res.render_world.frame_objects.clearRetainingCapacity(); + res.render_world.frame_views.clear(); + res.render_world.frame_objects.clear(); } /// ---------------------------------------------------- diff --git a/src/plugins/renderer/system/lifetime.zig b/src/plugins/renderer.depricated/system/lifetime.zig similarity index 91% rename from src/plugins/renderer/system/lifetime.zig rename to src/plugins/renderer.depricated/system/lifetime.zig index e4f8e66..21417e2 100644 --- a/src/plugins/renderer/system/lifetime.zig +++ b/src/plugins/renderer.depricated/system/lifetime.zig @@ -6,6 +6,7 @@ const App = @import("app"); const Zla = @import("zla"); const WGPUDevice = @import("../../device/resource/wgpu_device.zig"); +const RenderGraph = @import("../resource/render_graph.zig"); const Renderer = @import("../resource/renderer.zig"); const RenderWorld = @import("../resource/render_world.zig"); const RenderLayout = @import("../resource/render_layout.zig"); @@ -23,6 +24,14 @@ pub fn init(app: *App) !void { device: *WGPUDevice, }); + // --- # --- + try app.resources.set(RenderGraph{ + .nodes = .empty, + .edges = .empty, + .slots = .empty, + .order = null, + }); + // --- # --- try app.resources.set(Renderer.init( app.alloc, @@ -97,10 +106,17 @@ pub fn deinit(app: *App) !void { // --- # --- const res = try app.resources.getMany(struct { ren: *Renderer, + ren_graph: *RenderGraph, ren_world: *RenderWorld, ren_layout: *RenderLayout, }); + // --- FREE RENDER GRAPH --- + // res.ren_graph.nodes.deinit(); + // res.ren_graph.edges.deinit(); + // res.ren_graph.slots.deinit(); + if (res.ren_graph.order) |v| app.alloc.free(v); + // --- FREE SHADERS --- for (res.ren_world.gpu_shaders.items()) |entry| { wgpu.wgpuShaderModuleRelease(entry.value.raw); @@ -137,8 +153,6 @@ pub fn deinit(app: *App) !void { // --- # --- 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_objects.deinit(); res.ren_world.gpu_shaders.deinit(); diff --git a/src/plugins/renderer.depricated/system/prepare.zig b/src/plugins/renderer.depricated/system/prepare.zig new file mode 100644 index 0000000..926534d --- /dev/null +++ b/src/plugins/renderer.depricated/system/prepare.zig @@ -0,0 +1,515 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +// --- # --- +const std = @import("std"); +const gtl = @import("gtl"); +const math = @import("math"); +const wgpu = @import("wgpu"); +const App = @import("app"); +const World = @import("world"); + +// --- # --- +const Vertex = @import("../../cache/resource/mesh_cache.zig").Vertex; +const Window = @import("../../window/resource/window.zig"); +const WGPUDevice = @import("../../device/resource/wgpu_device.zig"); +const RenderWorld = @import("../resource/render_world.zig"); +const RenderLayout = @import("../resource/render_layout.zig"); +const RenderGraph = @import("../resource/render_graph.zig"); + +// --- # --- +const Transform2D = @import("../../common/component/transform2D.zig"); +const Transform3D = @import("../../common/component/transform3D.zig"); + +// --- MESH --- +const CMesh = @import("../../common/component/mesh.zig"); +const TMesh = @import("../template/gpu_object.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 ensureGraph(app: *App) !void { + // --- # --- + const res = try app.resources.getMany(struct { + world: *World, + device: *WGPUDevice, + render_graph: *RenderGraph, + }); + + // --- # --- + if (res.render_graph.order == null) { + const order: std.ArrayList(gtl.Handle(RenderGraph.Node)) = .empty; + defer order.deinit(app.alloc); + } +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn cameras(app: *App) !void { + // --- # --- + const res = try app.resources.getMany(struct { + world: *World, + render_world: *RenderWorld, + render_layout: *RenderLayout, + device: *WGPUDevice, + }); + + // --- # --- + var query = res.world.query(struct { + camera: *const CCamera, + transform2D: ?*const Transform2D, + transform3D: ?*const Transform3D, + }, .{}); + 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.*; + + // --- UNIFORM --- + const uniform = wgpu.wgpuDeviceCreateBuffer(res.device.raw, &wgpu.WGPUBufferDescriptor{ + .usage = wgpu.WGPUBufferUsage_CopyDst | wgpu.WGPUBufferUsage_Uniform, + .size = @sizeOf(TCamera.Uniform), + }) orelse return error.Buffer; + + // --- VIEW --- + const view: math.Mat4x4 = if (e.transform3D) |t3| cmp: { // 3D + const eye = t3.position; + const forward = t3.rotation.rotate(math.Vec3f.new(.{ 0, 0, 1 })); + const up = t3.rotation.rotate(math.Vec3f.new(.{ 0, 1, 0 })); + 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) { + .perspective => |v| .perspective(std.math.degreesToRadians(v.fov), 800.0 / 600.0, v.near, v.far), + .ortho => |v| .orthographic(800.0, 0, 600.0, 0, v.near, v.far), + }; + + // --- WRITE TO UNIFORM --- + wgpu.wgpuQueueWriteBuffer( + res.device.queue, + uniform, + 0, + &TCamera.Uniform{ + .view = view.transpose(), + .projection = projection.transpose(), + }, + @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(.{ + .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 { + world: *World, + render_world: *RenderWorld, + render_layout: *RenderLayout, + device: *WGPUDevice, + }); + + // --- # --- + var query = res.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(.{ + .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" = .{} }; + }, + }); + } +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn shaders(app: *App) !void { + // --- # --- + const res = try app.resources.getMany(struct { + world: *World, + render_world: *RenderWorld, + device: *WGPUDevice, + }); + + // --- # --- + var query = res.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 { + world: *World, + render_world: *RenderWorld, + render_layout: *RenderLayout, + device: *WGPUDevice, + }); + + // --- # --- + var query = res.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; + + // --- LAYOUT --- + var bind_group_layouts = [_]wgpu.WGPUBindGroupLayout{ + res.render_layout.camera, + res.render_layout.model, + res.render_layout.material, + }; + + // --- 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, + }; + }); + } +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn materials(app: *App) !void { + // --- # --- + const res = try app.resources.getMany(struct { + world: *World, + render_world: *RenderWorld, + render_layout: *RenderLayout, + device: *WGPUDevice, + }); + + // --- # --- + var query = res.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; + + // --- 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 = res.render_layout.material, + .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, + }, + // .depthStencil = &wgpu.WGPUDepthStencilState{ + // .format = wgpu.WGPUTextureFormat_Depth24Plus, + // .depthCompare = wgpu.WGPUCompareFunction_Less, + // .depthWriteEnabled = 1, + // + // .stencilReadMask = 0, + // .stencilWriteMask = 0, + // }, + }) orelse { + return error.PipelineCreationFailed; + }; +} diff --git a/src/plugins/renderer.depricated/system/present.zig b/src/plugins/renderer.depricated/system/present.zig new file mode 100644 index 0000000..25c14f6 --- /dev/null +++ b/src/plugins/renderer.depricated/system/present.zig @@ -0,0 +1,160 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +const std = @import("std"); +const wgpu = @import("wgpu"); +const App = @import("app"); +const World = @import("world"); + +const GPUWindow = @import("../../window/resource/gpu_window.zig"); +const Resized = @import("../../window/event/resized.zig"); +const Device = @import("../../device/resource/wgpu_device.zig"); +const Renderer = @import("../resource/renderer.zig"); +const RenderWorld = @import("../resource/render_world.zig"); +const MeshCache = @import("../../cache/resource/mesh_cache.zig"); + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn draw(app: *App) !void { + // --- # --- + const res = try app.resources.getMany(struct { + window: *GPUWindow, + device: *Device, + renderer: *Renderer, + render_world: *RenderWorld, + mesh_cache: *MeshCache, + }); + + // --- DO NONE ON WINDOW RESIZE --- + if (app.events.consume(Resized)) |_| { + return; + } + + // --- SURFACE TEXTURE --- + var surface_texture: wgpu.WGPUSurfaceTexture = .{}; + defer wgpu.wgpuTextureRelease(surface_texture.texture); + + // --- GET SURFACE TEXTURE --- + wgpu.wgpuSurfaceGetCurrentTexture( + @ptrCast(res.window.surface), + &surface_texture, + ); + + // --- DEBUG --- + switch (surface_texture.status) { + wgpu.WGPUSurfaceGetCurrentTextureStatus_Error => { + std.debug.print("\x1b[31m[SURFACE_TEXTURE_ERROR]\x1b[0m\n", .{}); + }, + wgpu.WGPUSurfaceGetCurrentTextureStatus_Outdated => { + std.debug.print("\x1b[31m[SURFACE_TEXTURE_OUTDATED]\x1b[0m\n", .{}); + }, + else => {}, + } + + // --- GET SURFACE COLOR VIEW --- + const target_view = wgpu.wgpuTextureCreateView(surface_texture.texture, &wgpu.WGPUTextureViewDescriptor{ + .format = wgpu.wgpuTextureGetFormat(surface_texture.texture), + .dimension = wgpu.WGPUTextureViewDimension_2D, + .baseMipLevel = 0, + .mipLevelCount = 1, + .baseArrayLayer = 0, + .arrayLayerCount = 1, + .aspect = wgpu.WGPUTextureAspect_All, + }); + defer wgpu.wgpuTextureViewRelease(target_view); + + // --- ENCODER --- + const encoder = wgpu.wgpuDeviceCreateCommandEncoder( + res.device.raw, + &wgpu.WGPUCommandEncoderDescriptor{}, + ); + defer wgpu.wgpuCommandEncoderRelease(encoder); + + // --- FRAME --- + var render_pass: ?*wgpu.WGPURenderPassEncoderImpl = null; + var camera: ?World.Entity = null; + + for (res.renderer.commands.items) |cmd| { + switch (cmd) { + .begin_pass => |v| { + // --- END PREV PASS --- + if (render_pass) |rp| { + wgpu.wgpuRenderPassEncoderEnd(rp); + wgpu.wgpuRenderPassEncoderRelease(rp); + } + + // --- NEW RENDER PASS --- + render_pass = wgpu.wgpuCommandEncoderBeginRenderPass( + encoder, + &wgpu.WGPURenderPassDescriptor{ + .colorAttachmentCount = 1, + .colorAttachments = &wgpu.WGPURenderPassColorAttachment{ + .view = target_view, + .resolveTarget = null, + .loadOp = switch (v.load_op) { + .load => wgpu.WGPULoadOp_Load, + .clear => wgpu.WGPULoadOp_Clear, + }, + .storeOp = switch (v.store_op) { + .store => wgpu.WGPUStoreOp_Store, + .discard => wgpu.WGPUStoreOp_Discard, + }, + .clearValue = .{ + .r = v.color[0], + .g = v.color[1], + .b = v.color[2], + .a = v.color[3], + }, + .depthSlice = wgpu.WGPU_DEPTH_SLICE_UNDEFINED, + }, + }, + ); + + // --- SET CAMERA --- + camera = v.camera; + }, + .end_pass => { + if (render_pass) |rp| { + wgpu.wgpuRenderPassEncoderEnd(rp); + wgpu.wgpuRenderPassEncoderRelease(rp); + render_pass = null; + } + }, + .draw => |v| { + // --- ENSURE --- + 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_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 gpu_mesh = res.render_world.gpu_objects.getByKey(v.entity) orelse return error.GPUMeshNotFound; + 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_camera.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); + + // --- DRAW --- + if (mesh.ibuf) |ibuf| { + wgpu.wgpuRenderPassEncoderSetIndexBuffer(render_pass, ibuf, wgpu.WGPUIndexFormat_Uint32, 0, wgpu.wgpuBufferGetSize(ibuf)); + wgpu.wgpuRenderPassEncoderDrawIndexed(render_pass, mesh.index_count, 1, 0, 0, 0); + } else { + wgpu.wgpuRenderPassEncoderDraw(render_pass, mesh.vertex_count, 1, 0, 0); + } + }, + else => {}, + } + } + + // --- FINISH ENCODER | CMD BUFFER --- + const cmd_buff = wgpu.wgpuCommandEncoderFinish(encoder, &.{}); + defer wgpu.wgpuCommandBufferRelease(cmd_buff); + + // --- SUBMIT AND PRESENT --- + wgpu.wgpuQueueSubmit(res.device.queue, 1, &cmd_buff); + _ = wgpu.wgpuSurfacePresent(@ptrCast(res.window.surface)); +} diff --git a/src/plugins/renderer.depricated/template/edge.zig b/src/plugins/renderer.depricated/template/edge.zig new file mode 100644 index 0000000..2733630 --- /dev/null +++ b/src/plugins/renderer.depricated/template/edge.zig @@ -0,0 +1,14 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +const gtl = @import("gtl"); +const Node = @import("node.zig"); +const Slot = @import("slot.zig"); +const Handle = gtl.Handle; + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub const Edge = union(enum) { + data: struct { producer: Handle(Slot), consumer: Handle(Slot) }, + order: struct { from: Handle(Node), to: Handle(Node) }, +}; diff --git a/src/plugins/renderer/template/frame_object.zig b/src/plugins/renderer.depricated/template/frame_object.zig similarity index 87% rename from src/plugins/renderer/template/frame_object.zig rename to src/plugins/renderer.depricated/template/frame_object.zig index 84e5571..f724859 100644 --- a/src/plugins/renderer/template/frame_object.zig +++ b/src/plugins/renderer.depricated/template/frame_object.zig @@ -2,6 +2,8 @@ //! ---------------------------------------------------- const gtl = @import("gtl"); +const App = @import("app"); +const World = @import("world"); const Material = @import("material.zig"); const Transform2D = @import("../../common/component/transform2D.zig"); const Transform3D = @import("../../common/component/transform3D.zig"); @@ -13,7 +15,7 @@ const Handle = gtl.Handle; // mesh: MeshID, -entity: u64, // TODO: Change to Entity type +entity: World.Entity, material: Handle(Material), transform: union(enum) { @"2D": Transform2D, diff --git a/src/plugins/renderer/template/frame_view.zig b/src/plugins/renderer.depricated/template/frame_view.zig similarity index 54% rename from src/plugins/renderer/template/frame_view.zig rename to src/plugins/renderer.depricated/template/frame_view.zig index 9ce723e..0cb1c8b 100644 --- a/src/plugins/renderer/template/frame_view.zig +++ b/src/plugins/renderer.depricated/template/frame_view.zig @@ -1,6 +1,8 @@ +const App = @import("app"); const GPUCamera = @import("gpu_camera.zig"); +const World = @import("world"); raw: *GPUCamera, order: u32 = 0, priority: u32 = 0, -camera: u64, // TODO: Change to ENTITY Type +camera: World.Entity, diff --git a/src/plugins/renderer/template/gpu_camera.zig b/src/plugins/renderer.depricated/template/gpu_camera.zig similarity index 100% rename from src/plugins/renderer/template/gpu_camera.zig rename to src/plugins/renderer.depricated/template/gpu_camera.zig diff --git a/src/plugins/renderer/template/gpu_material.zig b/src/plugins/renderer.depricated/template/gpu_material.zig similarity index 100% rename from src/plugins/renderer/template/gpu_material.zig rename to src/plugins/renderer.depricated/template/gpu_material.zig diff --git a/src/plugins/renderer/template/gpu_object.zig b/src/plugins/renderer.depricated/template/gpu_object.zig similarity index 100% rename from src/plugins/renderer/template/gpu_object.zig rename to src/plugins/renderer.depricated/template/gpu_object.zig diff --git a/src/plugins/renderer/template/gpu_pipeline.zig b/src/plugins/renderer.depricated/template/gpu_pipeline.zig similarity index 100% rename from src/plugins/renderer/template/gpu_pipeline.zig rename to src/plugins/renderer.depricated/template/gpu_pipeline.zig diff --git a/src/plugins/renderer/template/gpu_shader.zig b/src/plugins/renderer.depricated/template/gpu_shader.zig similarity index 100% rename from src/plugins/renderer/template/gpu_shader.zig rename to src/plugins/renderer.depricated/template/gpu_shader.zig diff --git a/src/plugins/renderer/template/material.zig b/src/plugins/renderer.depricated/template/material.zig similarity index 100% rename from src/plugins/renderer/template/material.zig rename to src/plugins/renderer.depricated/template/material.zig diff --git a/src/plugins/renderer.depricated/template/node.zig b/src/plugins/renderer.depricated/template/node.zig new file mode 100644 index 0000000..276206b --- /dev/null +++ b/src/plugins/renderer.depricated/template/node.zig @@ -0,0 +1,16 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +const gtl = @import("gtl"); +const Slot = @import("slot.zig"); +const RenderCTX = @import("render_ctx.zig"); +const Handle = gtl.Handle; + +// +// FIELDS +// + +name: []const u8, +inputs: []const Handle(Slot) = &.{}, +outputs: []const Handle(Slot) = &.{}, +run: *const fn (*RenderCTX) anyerror!void, diff --git a/src/plugins/renderer.depricated/template/render_ctx.zig b/src/plugins/renderer.depricated/template/render_ctx.zig new file mode 100644 index 0000000..5520c17 --- /dev/null +++ b/src/plugins/renderer.depricated/template/render_ctx.zig @@ -0,0 +1,14 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +const wgpu = @import("wgpu"); +const RenderGraph = @import("../resource/render_graph.zig"); + +// +// FIELDS +// + +device: *wgpu.WGPUDeviceImpl, +encoder: *wgpu.WGPUCommandEncoderImpl, + +graph: *const RenderGraph, diff --git a/src/plugins/renderer/template/shader_id.zig b/src/plugins/renderer.depricated/template/shader_id.zig similarity index 100% rename from src/plugins/renderer/template/shader_id.zig rename to src/plugins/renderer.depricated/template/shader_id.zig diff --git a/src/plugins/renderer.depricated/template/slot.zig b/src/plugins/renderer.depricated/template/slot.zig new file mode 100644 index 0000000..27746eb --- /dev/null +++ b/src/plugins/renderer.depricated/template/slot.zig @@ -0,0 +1,19 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +const wgpu = @import("wgpu"); + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub const Value = union(enum) { + buffer: *wgpu.WGPUBufferImpl, + bind_group: *wgpu.WGPUBindGroupImpl, + texture_view: *wgpu.WGPUTextureViewImpl, +}; + +// +// FIELDS +// + +name: []const u8, +value: ?Value = null, diff --git a/src/plugins/renderer/resource/bind_group_cache.zig b/src/plugins/renderer/resource/bind_group_cache.zig new file mode 100644 index 0000000..9f73a9c --- /dev/null +++ b/src/plugins/renderer/resource/bind_group_cache.zig @@ -0,0 +1,54 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +const std = @import("std"); +const wgpu = @import("wgpu"); +const MaterialAsset = @import("material_assets.zig").Asset; +const Self = @This(); + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub const GPUBindGroup = struct { + raw: *wgpu.WGPUBindGroupImpl, +}; + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +const BindingRef = union(enum) { + buffer: struct {}, + texture_view: u64, + sampler: u64, +}; + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub const BindGroup = struct { + layout: u64, + bindings: []const BindingRef, +}; + +// +// FIELDS +// + +groups: std.AutoHashMap(BindGroup, GPUBindGroup), +alloc: std.mem.Allocator, + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn init(alloc: std.mem.Allocator) Self { + return .{ + .groups = .init(alloc), + .alloc = alloc, + }; +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn deinit(self: *Self) void { + var it = self.groups.valueIterator(); + while (it.next()) |group| { + wgpu.wgpuBindGroupRelease(group.raw); + } + self.groups.deinit(); +} diff --git a/src/plugins/renderer/resource/embeded/shaders/pbr.wgsl b/src/plugins/renderer/resource/embeded/shaders/pbr.wgsl index cb094f9..1dfc82c 100644 --- a/src/plugins/renderer/resource/embeded/shaders/pbr.wgsl +++ b/src/plugins/renderer/resource/embeded/shaders/pbr.wgsl @@ -1,80 +1,17 @@ -// ---------------------------------------------------- -// ---------------------------------------------------- -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 CameraUniform { - view: mat4x4, - projection: mat4x4, -}; - -// ---------------------------------------------------- -// ---------------------------------------------------- -struct ModelUniform { - data: mat4x4, -}; - -// ---------------------------------------------------- -// ---------------------------------------------------- -struct Material { - albedo: vec4f, - metallic: f32, - roughness: f32, - _pad0: vec2f, -}; - -// -// UNIFORMS -// - -@group(0) @binding(0) -var camera: CameraUniform; - -@group(1) @binding(0) -var model: ModelUniform; - -@group(2) @binding(0) -var material: Material; - -// -// VERTEX -// - @vertex -fn vs_main(in: VertexInput) -> VertexOutput { - // --- # --- - var out: VertexOutput; - - // --- # --- - out.position = camera.projection * camera.view * model.data * vec4f(in.position, 1.0); - out.uv = in.uv; - out.normal = in.normal; - - // --- # --- - return out; +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 -// - @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); +fn fs_main() -> @location(0) vec4f { + return vec4f(0.0, 0.4, 1.0, 1.0); } diff --git a/src/plugins/renderer/resource/embeded/shaders/pbr.wgsl.old b/src/plugins/renderer/resource/embeded/shaders/pbr.wgsl.old new file mode 100644 index 0000000..cb094f9 --- /dev/null +++ b/src/plugins/renderer/resource/embeded/shaders/pbr.wgsl.old @@ -0,0 +1,80 @@ +// ---------------------------------------------------- +// ---------------------------------------------------- +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 CameraUniform { + view: mat4x4, + projection: mat4x4, +}; + +// ---------------------------------------------------- +// ---------------------------------------------------- +struct ModelUniform { + data: mat4x4, +}; + +// ---------------------------------------------------- +// ---------------------------------------------------- +struct Material { + albedo: vec4f, + metallic: f32, + roughness: f32, + _pad0: vec2f, +}; + +// +// UNIFORMS +// + +@group(0) @binding(0) +var camera: CameraUniform; + +@group(1) @binding(0) +var model: ModelUniform; + +@group(2) @binding(0) +var material: Material; + +// +// VERTEX +// + +@vertex +fn vs_main(in: VertexInput) -> VertexOutput { + // --- # --- + var out: VertexOutput; + + // --- # --- + out.position = camera.projection * camera.view * model.data * 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); +} diff --git a/src/plugins/renderer/resource/gpu_frame.zig b/src/plugins/renderer/resource/gpu_frame.zig new file mode 100644 index 0000000..68d4b58 --- /dev/null +++ b/src/plugins/renderer/resource/gpu_frame.zig @@ -0,0 +1,13 @@ +//! ---------------------------------------------------- +//! GPU Frame data +//! ---------------------------------------------------- + +const wgpu = @import("wgpu"); + +// +// FIELDS +// + +surface_texture: wgpu.WGPUSurfaceTexture = .{}, +target_view: *wgpu.WGPUTextureViewImpl = undefined, +encoder: *wgpu.WGPUCommandEncoderImpl = undefined, diff --git a/src/plugins/renderer/resource/material_assets.zig b/src/plugins/renderer/resource/material_assets.zig new file mode 100644 index 0000000..96e2631 --- /dev/null +++ b/src/plugins/renderer/resource/material_assets.zig @@ -0,0 +1,89 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +const std = @import("std"); +const gtl = @import("gtl"); +const ShaderID = @import("shader_cache.zig").ShaderID; +const Self = @This(); + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub const Asset = struct { + /// ---------------------------------------------------- + /// ---------------------------------------------------- + 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 = .{}, +}; + +// +// FIELDS +// + +assets: gtl.SlotMap(Asset), + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn init(alloc: std.mem.Allocator) Self { + return .{ + .assets = .init(alloc), + }; +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn deinit(self: *Self) void { + self.assets.deinit(); +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn put(self: *Self, asset: Asset) !gtl.Handle(Asset) { + return try self.assets.put(asset); +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn get(self: *Self, handle: gtl.Handle(Asset)) ?*Asset { + return self.assets.get(handle); +} diff --git a/src/plugins/renderer/resource/material_cache.zig b/src/plugins/renderer/resource/material_cache.zig new file mode 100644 index 0000000..aeb331f --- /dev/null +++ b/src/plugins/renderer/resource/material_cache.zig @@ -0,0 +1,41 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +const std = @import("std"); +const gtl = @import("gtl"); +const wgpu = @import("wgpu"); +const MaterialAsset = @import("material_assets.zig").Asset; +const Self = @This(); + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub const GPUMaterial = struct { + uniform_buffer: u64, + bind_group: *wgpu.WGPUBindGroupImpl, +}; + +// +// FIELDS +// + +map: std.AutoHashMap(gtl.Handle(MaterialAsset), GPUMaterial), +alloc: std.mem.Allocator, + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn init(alloc: std.mem.Allocator) Self { + return .{ + .map = .init(alloc), + .alloc = alloc, + }; +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn deinit(self: *Self) void { + var it = self.map.valueIterator(); + while (it.next()) |gpu| { + wgpu.wgpuBindGroupRelease(gpu.bind_group); + } + self.map.deinit(); +} diff --git a/src/plugins/renderer/resource/mesh_assets.zig b/src/plugins/renderer/resource/mesh_assets.zig new file mode 100644 index 0000000..55326bd --- /dev/null +++ b/src/plugins/renderer/resource/mesh_assets.zig @@ -0,0 +1,52 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +const std = @import("std"); +const gtl = @import("gtl"); +const Self = @This(); + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub const Asset = struct { + pub const MeshID = union(enum) { + cube, + quad, + square, + triangle, + custom: u32, // TODO: idk, someday + }; + + mesh: MeshID = .cube, +}; + +// +// FIELDS +// + +assets: gtl.SlotMap(Asset), + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn init(alloc: std.mem.Allocator) Self { + return .{ + .assets = .init(alloc), + }; +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn deinit(self: *Self) void { + self.assets.deinit(); +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn put(self: *Self, asset: Asset) !gtl.Handle(Asset) { + return try self.assets.put(asset); +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn get(self: *Self, handle: gtl.Handle(Asset)) ?*Asset { + return self.assets.get(handle); +} diff --git a/src/plugins/renderer/resource/pipeline_cache.zig b/src/plugins/renderer/resource/pipeline_cache.zig new file mode 100644 index 0000000..261f45b --- /dev/null +++ b/src/plugins/renderer/resource/pipeline_cache.zig @@ -0,0 +1,47 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +const std = @import("std"); +const wgpu = @import("wgpu"); +const ShaderID = @import("shader_cache.zig").ShaderID; +const MaterialAsset = @import("material_assets.zig").Asset; +const Self = @This(); + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub const GPUPipeline = struct { + raw: *wgpu.WGPURenderPipelineImpl, +}; + +/// ---------------------------------------------------- +/// TODO: Add layout +/// ---------------------------------------------------- +pub const Pipeline = struct { + unique: MaterialAsset.Unique, +}; + +// +// FIELDS +// + +data: std.AutoHashMap(Pipeline, GPUPipeline), +alloc: std.mem.Allocator, + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn init(alloc: std.mem.Allocator) Self { + return .{ + .data = .init(alloc), + .alloc = alloc, + }; +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn deinit(self: *Self) void { + var it = self.data.valueIterator(); + while (it.next()) |pipe| { + wgpu.wgpuRenderPipelineRelease(pipe.raw); + } + self.data.deinit(); +} diff --git a/src/plugins/renderer/resource/render_data.zig b/src/plugins/renderer/resource/render_data.zig new file mode 100644 index 0000000..8f676dd --- /dev/null +++ b/src/plugins/renderer/resource/render_data.zig @@ -0,0 +1,47 @@ +//! ---------------------------------------------------- +//! RenderData -> RenderPhase -> Present +//! ---------------------------------------------------- + +const std = @import("std"); +const gtl = @import("gtl"); +const math = @import("math"); +const World = @import("world"); +const MeshAsset = @import("mesh_assets.zig").Asset; +const MaterialAsset = @import("material_assets.zig").Asset; +const Self = @This(); + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub const ExtractedMesh = struct { + source: World.Entity, + mesh: gtl.Handle(MeshAsset), + material: gtl.Handle(MaterialAsset), + transform: math.Mat4x4, +}; + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub const ExtractedCamera = struct { + source: World.Entity, + + view: math.Mat4x4, + projection: math.Mat4x4, + clear_color: math.Color, + + depth: struct { near: f32, far: f32 } = .{ .near = 0.05, .far = 4000.0 }, + active: bool = true, +}; + +// +// FIELDS +// + +meshes: std.ArrayList(ExtractedMesh) = .empty, +cameras: std.ArrayList(ExtractedCamera) = .empty, + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn clear(self: *Self) void { + self.meshes.clearRetainingCapacity(); + self.cameras.clearRetainingCapacity(); +} diff --git a/src/plugins/renderer/resource/render_graph.zig b/src/plugins/renderer/resource/render_graph.zig new file mode 100644 index 0000000..49328f2 --- /dev/null +++ b/src/plugins/renderer/resource/render_graph.zig @@ -0,0 +1,359 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +const App = @import("app"); +const World = @import("world"); + +const std = @import("std"); +const gtl = @import("gtl"); +const math = @import("math"); +const wgpu = @import("wgpu"); + +const PipelineCache = @import("pipeline_cache.zig"); + +const Handle = gtl.Handle; +const Self = @This(); + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub const Node = struct { + name: []const u8, + inputs: std.ArrayList(Handle(Slot)) = .empty, + outputs: std.ArrayList(Handle(Slot)) = .empty, + run: *const fn (*RenderCTX) anyerror!void, +}; + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub const Edge = union(enum) { + data: struct { + producer: Handle(Slot), + consumer: Handle(Slot), + }, + order: struct { + from: Handle(Node), + to: Handle(Node), + }, +}; + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub const Slot = struct { + pub const Value = union(enum) { + buffer: *wgpu.WGPUBufferImpl, + sampler: *wgpu.WGPUSamplerImpl, + texture_view: *wgpu.WGPUTextureViewImpl, + }; + + name: []const u8, + value: ?Value = null, +}; + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub const RenderCTX = struct { + // --- CORE --- + app: *App, + world: *World, + graph: *Self, + node: ?Handle(Node) = null, + + // --- RAW --- + device: *wgpu.WGPUDeviceImpl, + encoder: *wgpu.WGPUCommandEncoderImpl, + + /// ---------------------------------------------------- + /// ---------------------------------------------------- + pub fn input(self: *RenderCTX, i: usize) !?Slot.Value { + const n = self.node orelse return error.NodeNotSet; + const node = self.graph.nodes.get(n) orelse return error.UnknownNode; + if (i >= node.inputs.items.len) return error.SlotOutOfBounds; + const slot = self.graph.slots.get(node.inputs.items[i]) orelse return error.UnknownSlot; + return slot.value; + } +}; + +/// ---------------------------------------------------- +/// TODO: create Encoder type instead that will return RenderPass +/// ---------------------------------------------------- +pub const RenderPass = struct { + raw: *wgpu.WGPURenderPassEncoderImpl, + + /// ---------------------------------------------------- + /// ---------------------------------------------------- + pub fn begin( + view: *wgpu.WGPUTextureViewImpl, + encoder: *wgpu.WGPUCommandEncoderImpl, + color: math.Color, + ) !RenderPass { + return .{ + .raw = wgpu.wgpuCommandEncoderBeginRenderPass( + encoder, + &wgpu.WGPURenderPassDescriptor{ + .colorAttachmentCount = 1, + .colorAttachments = &wgpu.WGPURenderPassColorAttachment{ + .view = view, + .resolveTarget = null, + .loadOp = wgpu.WGPULoadOp_Clear, + .storeOp = wgpu.WGPUStoreOp_Store, + .clearValue = .{ + .r = color.r, + .g = color.g, + .b = color.b, + .a = color.a, + }, + .depthSlice = wgpu.WGPU_DEPTH_SLICE_UNDEFINED, + }, + }, + ) orelse return error.WGPURenderPassFailed, + }; + } + + /// ---------------------------------------------------- + /// ---------------------------------------------------- + pub fn end(self: *const RenderPass) void { + wgpu.wgpuRenderPassEncoderEnd(self.raw); + wgpu.wgpuRenderPassEncoderRelease(self.raw); + } + + /// ---------------------------------------------------- + /// ---------------------------------------------------- + pub fn bindPipeline(self: *const RenderPass, pipe: *const PipelineCache.GPUPipeline) void { + wgpu.wgpuRenderPassEncoderSetPipeline(self.raw, pipe.raw); + } + + /// ---------------------------------------------------- + /// ---------------------------------------------------- + pub fn draw( + self: *const RenderPass, + vertex_count: u32, + instance_count: u32, + ) void { + wgpu.wgpuRenderPassEncoderDraw(self.raw, vertex_count, instance_count, 0, 0); + } +}; + +// +// FIELDS +// + +nodes: gtl.SlotMap(Node), +edges: gtl.SlotMap(Edge), +slots: gtl.SlotMap(Slot), + +order: ?[]Handle(Node) = null, +alloc: std.mem.Allocator, + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn init(alloc: std.mem.Allocator) Self { + return .{ + .nodes = .init(alloc), + .edges = .init(alloc), + .slots = .init(alloc), + .alloc = alloc, + }; +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn deinit(self: *Self) void { + var it = self.nodes.valueIterator(); + while (it.next()) |node| { + node.inputs.deinit(self.alloc); + node.outputs.deinit(self.alloc); + } + + self.nodes.deinit(); + self.edges.deinit(); + self.slots.deinit(); + + if (self.order) |order| { + self.alloc.free(order); + } +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn run( + self: *Self, + ctx: *RenderCTX, +) !void { + if (self.order) |order| { + for (order) |handle| { + ctx.node = handle; + const node = self.nodes.get(handle) orelse return error.UnknownNode; + try node.run(ctx); + } + } +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn addNode(self: *Self, node: Node) !Handle(Node) { + self.order = null; + return try self.nodes.put(node); +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn addEdge(self: *Self, edge: Edge) !Handle(Edge) { + self.order = null; + return try self.edges.put(edge); +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn addSlot(self: *Self, slot: Slot) !Handle(Slot) { + self.order = null; + return try self.slots.put(slot); +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn addOrderEdge(self: *Self, from: Handle(Node), to: Handle(Node)) !void { + _ = try self.addEdge(.{ .order = .{ .from = from, .to = to } }); +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn addDataEdge(self: *Self, producer: Handle(Slot), consumer: Handle(Slot)) !void { + _ = try self.addEdge(.{ .data = .{ .producer = producer, .consumer = consumer } }); +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn addInput(self: *Self, node: Handle(Node), slot: Slot) !Handle(Slot) { + const h = try self.slots.put(slot); + try self.nodes.get(node).?.inputs.append(self.alloc, h); + return h; +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn compile(self: *Self) !void { + if (self.order != null) return; + + { // --- VALIDATE EDGES --- + var it = self.edges.valueIterator(); + while (it.next()) |edge| { + switch (edge.*) { + .data => |v| { + if (!self.slots.contains(v.producer)) return error.UnknownSlot; + if (!self.slots.contains(v.consumer)) return error.UnknownSlot; + if (v.producer.eql(v.consumer)) return error.SelfLoop; + }, + .order => |v| { + if (!self.nodes.contains(v.from)) return error.UnknownNode; + if (!self.nodes.contains(v.to)) return error.UnknownNode; + }, + } + } + } + + // --- SLOT OWNER --- + var slot_owner = try self.buildSlotOwners(); + defer slot_owner.deinit(); + + // --- TOPO SORT --- + var indegree: std.AutoHashMap(Handle(Node), u32) = .init(self.alloc); + defer indegree.deinit(); + + var adj: std.AutoHashMap(Handle(Node), std.ArrayList(Handle(Node))) = .init(self.alloc); + defer { + var it = adj.valueIterator(); + while (it.next()) |l| l.deinit(self.alloc); + adj.deinit(); + } + + { + var it = self.nodes.handleIterator(); + while (it.next()) |handle| { + try indegree.put(handle, 0); + try adj.put(handle, .empty); + } + } + + { + var it = self.edges.iterator(); + while (it.next()) |entry| { + const from = switch (entry.value.*) { + .data => |v| slot_owner.get(v.producer).?, + .order => |v| v.from, + }; + + const to = switch (entry.value.*) { + .data => |v| slot_owner.get(v.consumer).?, + .order => |v| v.to, + }; + + const d = indegree.getPtr(to).?; + d.* += 1; + try adj.getPtr(from).?.append(self.alloc, to); + } + } + + // --- NERDY SHIT --- + + var order: std.ArrayList(Handle(Node)) = .empty; + defer order.deinit(self.alloc); + + var ready: std.ArrayList(Handle(Node)) = .empty; + defer ready.deinit(self.alloc); + + { + var it = indegree.iterator(); + while (it.next()) |entry| { + if (entry.value_ptr.* == 0) try ready.append(self.alloc, entry.key_ptr.*); + } + } + + while (ready.pop()) |v| { + try order.append(self.alloc, v); + for (adj.get(v).?.items) |m| { + const d = indegree.getPtr(m).?; + d.* -= 1; + if (d.* == 0) try ready.append(self.alloc, m); + } + } + + if (order.items.len != self.nodes.len()) return error.CycleDetected; + + // --- RESULT --- + self.order = try order.toOwnedSlice(self.alloc); +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +fn buildSlotOwners(self: *Self) !std.AutoHashMap(Handle(Slot), Handle(Node)) { + // --- OWNERS --- + var owners: std.AutoHashMap(Handle(Slot), Handle(Node)) = .init(self.alloc); + errdefer owners.deinit(); + + { // --- PORTS CHECK --- + var it = self.nodes.iterator(); + while (it.next()) |entry| { + // --- # --- + const node_handle = entry.handle; + const node: *const Node = entry.value; + + // --- ENSURE STUFF --- + const lists = [_][]const Handle(Slot){ node.inputs.items, node.outputs.items }; + for (lists) |list| for (list) |slot| { + if (!self.slots.contains(slot)) return error.UnknownSlot; + const gop = try owners.getOrPut(slot); + if (gop.found_existing) return error.SlotHasMultipleOwners; + gop.value_ptr.* = node_handle; + }; + } + } + + { // --- ORPHAN CHECK 🥀 --- + var it = self.slots.handleIterator(); + while (it.next()) |handle| { + if (!owners.contains(handle)) return error.OrphanSlot; + } + } + + return owners; +} diff --git a/src/plugins/renderer/resource/render_phase.zig b/src/plugins/renderer/resource/render_phase.zig new file mode 100644 index 0000000..948727f --- /dev/null +++ b/src/plugins/renderer/resource/render_phase.zig @@ -0,0 +1,33 @@ +//! ---------------------------------------------------- +//! Final render data holder 💪 +//! ---------------------------------------------------- + +const std = @import("std"); +const gtl = @import("gtl"); +const wgpu = @import("wgpu"); +const World = @import("world"); +const MaterialAsset = @import("material_assets.zig").Asset; +const Self = @This(); + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub const DrawItem = struct { + source: World.Entity, + material: MaterialAsset.Unique, + + // TODO: vbuf, ibuf, etc +}; + +// +// FIELDS +// + +opaques: std.ArrayList(DrawItem) = .empty, +transparents: std.ArrayList(DrawItem) = .empty, + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn clear(self: *Self) void { + self.opaques.clearRetainingCapacity(); + self.transparents.clearRetainingCapacity(); +} diff --git a/src/plugins/renderer/resource/sampler_cache.zig b/src/plugins/renderer/resource/sampler_cache.zig new file mode 100644 index 0000000..74ef4c1 --- /dev/null +++ b/src/plugins/renderer/resource/sampler_cache.zig @@ -0,0 +1,35 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +const std = @import("std"); +const wgpu = @import("wgpu"); +const Self = @This(); + +pub const GPUSampler = struct { raw: *wgpu.WGPUSamplerImpl }; +pub const Sampler = struct { filter: u8, wrap: u8 }; + +// +// FIELDS +// + +map: std.AutoHashMap(Sampler, GPUSampler), +alloc: std.mem.Allocator, + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn init(alloc: std.mem.Allocator) Self { + return .{ + .map = .init(alloc), + .alloc = alloc, + }; +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn deinit(self: *Self) void { + var it = self.map.valueIterator(); + while (it.next()) |sampler| { + wgpu.wgpuSamplerRelease(sampler.raw); + } + self.map.deinit(); +} diff --git a/src/plugins/renderer/resource/shader_cache.zig b/src/plugins/renderer/resource/shader_cache.zig new file mode 100644 index 0000000..0bead6f --- /dev/null +++ b/src/plugins/renderer/resource/shader_cache.zig @@ -0,0 +1,54 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +const std = @import("std"); +const wgpu = @import("wgpu"); +const Self = @This(); + +// +// EMBEDED +// + +pub const PBRCode = @embedFile("embeded/shaders/pbr.wgsl"); +pub 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: std.AutoHashMap(ShaderID, GPUShader), +alloc: std.mem.Allocator, + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn init(alloc: std.mem.Allocator) Self { + return .{ + .modules = .init(alloc), + .alloc = alloc, + }; +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn deinit(self: *Self) void { + var it = self.modules.valueIterator(); + while (it.next()) |module| { + wgpu.wgpuShaderModuleRelease(module.raw); + } + + self.modules.deinit(); +} diff --git a/src/plugins/renderer/resource/texture_cache.zig b/src/plugins/renderer/resource/texture_cache.zig new file mode 100644 index 0000000..e6c52e7 --- /dev/null +++ b/src/plugins/renderer/resource/texture_cache.zig @@ -0,0 +1,26 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +const std = @import("std"); +const wgpu = @import("wgpu"); +const Self = @This(); + +// +// FIELDS +// + +alloc: std.mem.Allocator, + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn init(alloc: std.mem.Allocator) Self { + return .{ + .alloc = alloc, + }; +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn deinit(self: *Self) void { + _ = self; +} diff --git a/src/plugins/renderer/root.zig b/src/plugins/renderer/root.zig index 85c44f8..2957aac 100644 --- a/src/plugins/renderer/root.zig +++ b/src/plugins/renderer/root.zig @@ -5,24 +5,44 @@ // PRIVATE // -const App = @import("app"); +const std = @import("std"); +const wgpu = @import("wgpu"); -const lifetime = @import("system/lifetime.zig"); -const frame = @import("system/frame.zig"); -const prepare = @import("system/prepare.zig"); -const present = @import("system/present.zig"); +const App = @import("app"); +const World = @import("world"); +const Device = @import("../device/resource/wgpu_device.zig"); +const Window = @import("../window/resource/gpu_window.zig"); + +const extract_sys = @import("system/extract.zig"); +const prepare_sys = @import("system/prepare.zig"); +const present_sys = @import("system/present.zig"); +const queue_sys = @import("system/queue.zig"); // // PUBLIC // -const Renderer = @import("resource/renderer.zig"); -const RenderWorld = @import("resource/render_world.zig"); -const RenderLayout = @import("resource/render_layout.zig"); +// --- ASSETS --- +pub const MeshAssets = @import("resource/mesh_assets.zig"); +pub const MaterialAssets = @import("resource/material_assets.zig"); + +// --- CACHE --- +pub const SamplerCache = @import("resource/sampler_cache.zig"); +pub const TextureCache = @import("resource/texture_cache.zig"); +pub const MaterialCache = @import("resource/material_cache.zig"); +pub const ShaderCache = @import("resource/shader_cache.zig"); +pub const PipelineCache = @import("resource/pipeline_cache.zig"); +pub const BindGroupCache = @import("resource/bind_group_cache.zig"); + +// --- DRAW --- +pub const GPUFrame = @import("resource/gpu_frame.zig"); +pub const RenderData = @import("resource/render_data.zig"); +pub const RenderPhase = @import("resource/render_phase.zig"); +pub const RenderGraph = @import("resource/render_graph.zig"); /// ---------------------------------------------------- /// ---------------------------------------------------- -pub const plugin: App.plugin.Plugin = .{ +pub const plugin: App.Plugin = .{ .name = "Renderer", .build = build, }; @@ -30,27 +50,158 @@ pub const plugin: App.plugin.Plugin = .{ /// ---------------------------------------------------- /// ---------------------------------------------------- fn build( - _: *App.plugin.Plugin, + _: *App.Plugin, app: *App, ) !void { - // --- LIFETIME --- - try app.schedules.add(App.stage.init, lifetime.init); - try app.schedules.add(App.stage.deinit, lifetime.deinit); + try app.schedules.add(App.stage.init, init); + try app.schedules.add(App.stage.deinit, deinit); + try app.schedules.addMany(App.stage.update, &.{ + extract_sys.meshes, + extract_sys.cameras, - // --- FRAME --- - try app.schedules.add(App.stage.begin_frame, frame.begin); - try app.schedules.add(App.stage.end_frame, frame.end); + prepare_sys.shaders, + prepare_sys.pipelines, - // --- PRE DRAW --- - try app.schedules.addManyBefore(App.stage.draw, App.stage.draw, &.{ - prepare.shaders, - prepare.pipelines, + queue_sys.meshes, + }); + try app.schedules.add(App.stage.draw, present_sys.draw); +} - prepare.cameras, - prepare.objects, - prepare.materials, +/// ---------------------------------------------------- +/// ---------------------------------------------------- +fn init(app: *App) !void { + // --- # --- + try app.resources.set(MeshAssets.init(app.alloc)); + try app.resources.set(MaterialAssets.init(app.alloc)); + + // --- # --- + try app.resources.set(SamplerCache.init(app.alloc)); + try app.resources.set(TextureCache.init(app.alloc)); + try app.resources.set(MaterialCache.init(app.alloc)); + try app.resources.set(ShaderCache.init(app.alloc)); + try app.resources.set(PipelineCache.init(app.alloc)); + try app.resources.set(BindGroupCache.init(app.alloc)); + + // --- # --- + try app.resources.set(GPUFrame{}); + try app.resources.set(RenderData{}); + try app.resources.set(RenderPhase{}); + try app.resources.set(RenderGraph.init(app.alloc)); + + // --- # --- + const res = try app.resources.getMany(struct { + world: *World, + device: *Device, + window: *Window, + render_graph: *RenderGraph, + frame: *GPUFrame, }); - // --- DRAW --- - try app.schedules.add(App.stage.draw, present.draw); + const clear = try res.render_graph.addNode(.{ + .name = "clear", + .run = clearNode, + }); + + // const main_color = try res.render_graph.addSlot(.{ .name = "main_color" }); + const main = try res.render_graph.addNode(.{ + .name = "main", + // .outputs = &.{ + // main_color, + // }, + .run = mainNode, + }); + + // const present_target = try res.render_graph.addSlot(.{ .name = "present_target" }); + const present = try res.render_graph.addNode(.{ + .name = "present", + // .inputs = &.{ + // present_target, + // }, + .run = presentNode, + }); + + try res.render_graph.addOrderEdge(clear, main); + // try res.render_graph.addDataEdge(main_color, present_target); + try res.render_graph.addOrderEdge(main, present); + + // --- # --- + try res.render_graph.compile(); +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +fn deinit(app: *App) !void { + // --- # --- + const res = try app.resources.getMany(struct { + mesh_assets: *MeshAssets, + material_assets: *MaterialAssets, + + shader_cache: *ShaderCache, + pipeline_cache: *PipelineCache, + + render_data: *RenderData, + render_phase: *RenderPhase, + render_graph: *RenderGraph, + }); + + // --- # --- + res.mesh_assets.deinit(); + res.material_assets.deinit(); + + // --- # --- + res.shader_cache.deinit(); + res.pipeline_cache.deinit(); + + // --- # --- + res.render_graph.deinit(); + res.render_data.meshes.deinit(app.alloc); + res.render_data.cameras.deinit(app.alloc); + res.render_phase.opaques.deinit(app.alloc); + res.render_phase.transparents.deinit(app.alloc); +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +fn clearNode(ctx: *RenderGraph.RenderCTX) !void { + // --- # --- + const res = try ctx.app.resources.getMany(struct { + frame: *GPUFrame, + pipelines: *PipelineCache, + render_data: *RenderData, + render_phase: *RenderPhase, + }); + + // --- # --- + for (res.render_data.cameras.items) |camera| { + const pass: RenderGraph.RenderPass = try .begin( + res.frame.target_view, + res.frame.encoder, + camera.clear_color, + ); + defer pass.end(); + + // --- # --- + for (res.render_phase.opaques.items) |draw_item| { + // --- GET PIPELINE --- + const pipeline = res.pipelines.data.getPtr(.{ + .unique = draw_item.material, + }) orelse continue; + + // --- DRAW --- + pass.bindPipeline(pipeline); + pass.draw(3, 1); + } + } +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +fn mainNode(ctx: *RenderGraph.RenderCTX) !void { + _ = ctx; +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +fn presentNode(ctx: *RenderGraph.RenderCTX) !void { + _ = ctx; } diff --git a/src/plugins/renderer/system/extract.zig b/src/plugins/renderer/system/extract.zig new file mode 100644 index 0000000..d8a20ca --- /dev/null +++ b/src/plugins/renderer/system/extract.zig @@ -0,0 +1,100 @@ +//! ---------------------------------------------------- +//! Extract -> Prepare -> Present +//! ---------------------------------------------------- + +// --- # --- +const std = @import("std"); +const math = @import("math"); + +// --- RESOURCES --- +const App = @import("app"); +const World = @import("world"); +const MeshAssets = @import("../resource/mesh_assets.zig"); +const MaterialAssets = @import("../resource/material_assets.zig"); +const RenderData = @import("../resource/render_data.zig"); + +// --- COMPONENTS --- +const Mesh = @import("../../common/component/mesh.zig"); +const Camera = @import("../../common/component/camera.zig"); +const Material = @import("../../common/component/material.zig"); +const Transform3D = @import("../../common/component/transform3D.zig"); + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn meshes(app: *App) !void { + // --- # --- + const res = try app.resources.getMany(struct { + world: *World, + mesh_assets: *MeshAssets, + material_assets: *MaterialAssets, + render_data: *RenderData, + }); + + // --- # --- + var query = res.world.query(struct { + mesh: *Mesh, + material: *Material, + transform: *Transform3D, + }, .{}); + while (query.next()) |e| { + + // --- TRANSFORM --- + const transform = math.Mat4x4.translate( + e.transform.position.value[0], + e.transform.position.value[1], + e.transform.position.value[2], + ) + .mul(e.transform.rotation.toMat4()) + .mul(math.Mat4x4.scale( + e.transform.scale.value[0], + e.transform.scale.value[1], + e.transform.scale.value[2], + )); + + // --- RESULT --- + try res.render_data.meshes.append(app.alloc, .{ + .source = query.entity(), + .transform = transform, + + .mesh = e.mesh.value, + .material = e.material.value, + }); + } +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn cameras(app: *App) !void { + // --- # --- + const res = try app.resources.getMany(struct { + world: *World, + render_data: *RenderData, + }); + + // --- # --- + var query = res.world.query(struct { + camera: *Camera, + transform: *Transform3D, + }, .{}); + while (query.next()) |e| { + // --- VIEW --- + const eye = e.transform.position; + const forward = e.transform.rotation.rotate(math.Vec3f.new(.{ 0, 0, 1 })); + const up = e.transform.rotation.rotate(math.Vec3f.new(.{ 0, 1, 0 })); + const view: math.Mat4x4 = .lookAt(eye, eye.add(forward), up); + + // --- PROJECTION --- + const projection: math.Mat4x4 = switch (e.camera.projection) { + .perspective => |v| .perspective(std.math.degreesToRadians(v.fov), 800.0 / 600.0, v.near, v.far), + .ortho => |v| .orthographic(800.0, 0, 600.0, 0, v.near, v.far), + }; + + // --- RESULT --- + try res.render_data.cameras.append(app.alloc, .{ + .source = query.entity(), + .projection = projection, + .view = view, + .clear_color = e.camera.clear_color, + }); + } +} diff --git a/src/plugins/renderer/system/prepare.zig b/src/plugins/renderer/system/prepare.zig index 24747a8..fd13e21 100644 --- a/src/plugins/renderer/system/prepare.zig +++ b/src/plugins/renderer/system/prepare.zig @@ -1,248 +1,51 @@ //! ---------------------------------------------------- +//! Extract -> Prepare -> Present //! ---------------------------------------------------- // --- # --- const std = @import("std"); -const math = @import("math"); +const gtl = @import("gtl"); const wgpu = @import("wgpu"); + +// --- RESOURCES --- 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 RenderLayout = @import("../resource/render_layout.zig"); - -// --- # --- -const Transform2D = @import("../../common/component/transform2D.zig"); -const Transform3D = @import("../../common/component/transform3D.zig"); - -// --- MESH --- -const CMesh = @import("../../common/component/mesh.zig"); -const TMesh = @import("../template/gpu_object.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, - render_layout: *RenderLayout, - device: *WGPUDevice, - }); - - // --- # --- - var query = app.world.query(struct { - camera: *const CCamera, - transform2D: ?*const Transform2D, - transform3D: ?*const Transform3D, - }, .{}); - 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.*; - - // --- UNIFORM --- - const uniform = wgpu.wgpuDeviceCreateBuffer(res.device.raw, &wgpu.WGPUBufferDescriptor{ - .usage = wgpu.WGPUBufferUsage_CopyDst | wgpu.WGPUBufferUsage_Uniform, - .size = @sizeOf(TCamera.Uniform), - }) orelse return error.Buffer; - - // --- VIEW --- - const view: math.Mat4x4 = if (e.transform3D) |t3| cmp: { // 3D - const eye = t3.position; - const forward = t3.rotation.rotate(math.Vec3f.new(.{ 0, 0, 1 })); - const up = t3.rotation.rotate(math.Vec3f.new(.{ 0, 1, 0 })); - 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) { - .perspective => |v| .perspective(std.math.degreesToRadians(v.fov), 800.0 / 600.0, v.near, v.far), - .ortho => |v| .orthographic(800.0, 0, 600.0, 0, v.near, v.far), - }; - - // --- WRITE TO UNIFORM --- - wgpu.wgpuQueueWriteBuffer( - res.device.queue, - uniform, - 0, - &TCamera.Uniform{ - .view = view.transpose(), - .projection = projection.transpose(), - }, - @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" = .{} }; - }, - }); - } -} +const World = @import("world"); +const GPUDevice = @import("../../device/resource/wgpu_device.zig"); +const MeshAssets = @import("../resource/mesh_assets.zig"); +const MaterialAssets = @import("../resource/material_assets.zig"); +const RenderData = @import("../resource/render_data.zig"); +const ShaderCache = @import("../resource/shader_cache.zig"); +const PipelineCache = @import("../resource/pipeline_cache.zig"); /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn shaders(app: *App) !void { // --- # --- const res = try app.resources.getMany(struct { - render_world: *RenderWorld, - device: *WGPUDevice, + world: *World, + device: *GPUDevice, + mesh_assets: *MeshAssets, + material_assets: *MaterialAssets, + render_data: *RenderData, + shader_cache: *ShaderCache, }); - // --- # --- - var query = app.world.query(struct { - material: *const CMaterial, - }, .{}); - while (query.next()) |e| { + for (res.render_data.meshes.items) |extracted_mesh| { // --- GET MATERIAL --- - const material = res.render_world.materials.get( - e.material.value, + const material = res.material_assets.get( + extracted_mesh.material, ) 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; + // --- GOP --- + const entry = try res.shader_cache.modules.getOrPut( + material.unique.shader, + ); + if (!entry.found_existing) { // --- CODE --- const code: []const u8 = switch (material.unique.shader) { - .pbr => RenderWorld.PBRCode, - .unlit => RenderWorld.UnlitCode, + .pbr => ShaderCache.PBRCode, + .unlit => ShaderCache.UnlitCode, .custom => return error.TooLazyToImplement, // TODO: Yes }; @@ -253,7 +56,7 @@ pub fn shaders(app: *App) !void { }; // --- RESULT --- - break :blk .{ + entry.value_ptr.* = .{ .raw = wgpu.wgpuDeviceCreateShaderModule(res.device.raw, &wgpu.WGPUShaderModuleDescriptor{ .label = .{ .data = @tagName(material.unique.shader), .length = wgpu.WGPU_STRLEN }, .nextInChain = &source.chain, @@ -261,7 +64,7 @@ pub fn shaders(app: *App) !void { return error.ShaderModuleCreationFailed; }, }; - }); + } } } @@ -270,221 +73,125 @@ pub fn shaders(app: *App) !void { pub fn pipelines(app: *App) !void { // --- # --- const res = try app.resources.getMany(struct { - render_world: *RenderWorld, - render_layout: *RenderLayout, - device: *WGPUDevice, + world: *World, + device: *GPUDevice, + mesh_assets: *MeshAssets, + material_assets: *MaterialAssets, + render_data: *RenderData, + + shader_cache: *ShaderCache, + pipeline_cache: *PipelineCache, }); - // --- # --- - var query = app.world.query(struct { - material: *const CMaterial, - }, .{}); - while (query.next()) |e| { + for (res.render_data.meshes.items) |extracted_mesh| { // --- GET MATERIAL --- - const material = res.render_world.materials.get( - e.material.value, + const material = res.material_assets.get( + extracted_mesh.material, ) 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; + // --- GET SHADER --- + const shader = res.shader_cache.modules.getPtr( + material.unique.shader, + ) orelse return error.ShaderNotFound; - // --- LAYOUT --- - var bind_group_layouts = [_]wgpu.WGPUBindGroupLayout{ - res.render_layout.camera, - res.render_layout.model, - res.render_layout.material, - }; - - // --- 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, - }; + // --- GOP --- + const entry = try res.pipeline_cache.data.getOrPut(.{ + .unique = material.unique, }); - } -} -/// ---------------------------------------------------- -/// ---------------------------------------------------- -pub fn materials(app: *App) !void { - // --- # --- - const res = try app.resources.getMany(struct { - render_world: *RenderWorld, - render_layout: *RenderLayout, - 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; - - // --- 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 = res.render_layout.material, - .entryCount = 1, - .entries = &[_]wgpu.WGPUBindGroupEntry{ - .{ - .binding = 0, - .buffer = uniform, - .offset = 0, - .size = @sizeOf(TMaterial.Style), + if (!entry.found_existing) { + entry.value_ptr.* = .{ + .raw = wgpu.wgpuDeviceCreateRenderPipeline(res.device.raw, &wgpu.WGPURenderPipelineDescriptor{ + .label = .{ .data = @tagName(material.unique.shader), .length = wgpu.WGPU_STRLEN }, + // .layout = layout, + .vertex = .{ + .module = shader.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.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, }, }, - }) orelse return error.BindGroup; + .primitive = .{ + .topology = switch (material.unique.topology) { + .triangle_list => wgpu.WGPUPrimitiveTopology_TriangleList, + .point_list => wgpu.WGPUPrimitiveTopology_PointList, + .line_list => wgpu.WGPUPrimitiveTopology_LineList, + }, + .frontFace = switch (material.unique.front_face) { + .ccw => wgpu.WGPUFrontFace_CCW, + .cw => wgpu.WGPUFrontFace_CW, + }, + .cullMode = switch (material.unique.cull_mode) { + .back => wgpu.WGPUCullMode_Back, + .front => wgpu.WGPUCullMode_Front, + .none => wgpu.WGPUCullMode_None, + }, + }, + .multisample = .{ + .count = 1, + .mask = ~@as(u32, 0), + .alphaToCoverageEnabled = 0, + }, + // .depthStencil = &wgpu.WGPUDepthStencilState{ + // .format = wgpu.WGPUTextureFormat_Depth24Plus, + // .depthCompare = wgpu.WGPUCompareFunction_Less, + // .depthWriteEnabled = 1, + // + // .stencilReadMask = 0, + // .stencilWriteMask = 0, + // }, + }) orelse { + return error.PipelineCreationFailed; + }, + }; - // --- RESULT --- - break :blk .{ - .buffer = uniform, - .bind_group = bind_group, - .dirty = false, - }; - }, - ); + if (gtl.log.print(.debug, "PIPELINE", gtl.ansi.blue)) |v| { + defer v.end(); + + v.write("shader: {s}", .{@tagName(material.unique.shader)}); + v.write("topology: {s}", .{@tagName(material.unique.topology)}); + } + } } } - -/// ---------------------------------------------------- -/// ---------------------------------------------------- -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, - }, - // .depthStencil = &wgpu.WGPUDepthStencilState{ - // .format = wgpu.WGPUTextureFormat_Depth24Plus, - // .depthCompare = wgpu.WGPUCompareFunction_Less, - // .depthWriteEnabled = 1, - // - // .stencilReadMask = 0, - // .stencilWriteMask = 0, - // }, - }) orelse { - return error.PipelineCreationFailed; - }; -} diff --git a/src/plugins/renderer/system/present.zig b/src/plugins/renderer/system/present.zig index a90a9df..a6f0475 100644 --- a/src/plugins/renderer/system/present.zig +++ b/src/plugins/renderer/system/present.zig @@ -3,44 +3,46 @@ const std = @import("std"); const wgpu = @import("wgpu"); -const App = @import("app"); -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 RenderWorld = @import("../resource/render_world.zig"); -const MeshCache = @import("../../cache/resource/mesh_cache.zig"); +const App = @import("app"); +const World = @import("world"); + +const GPUDevice = @import("../../device/resource/wgpu_device.zig"); +const GPUWindow = @import("../../window/resource/gpu_window.zig"); +const GPUFrame = @import("../resource/gpu_frame.zig"); +const RenderGraph = @import("../resource/render_graph.zig"); + +const RenderData = @import("../resource/render_data.zig"); +const RenderPhase = @import("../resource/render_phase.zig"); /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn draw(app: *App) !void { // --- # --- const res = try app.resources.getMany(struct { - window: *Window, - device: *Device, - renderer: *Renderer, - render_world: *RenderWorld, - mesh_cache: *MeshCache, + world: *World, + window: *GPUWindow, + device: *GPUDevice, + frame: *GPUFrame, + + render_data: *RenderData, + render_phase: *RenderPhase, + render_graph: *RenderGraph, }); - // --- DO NONE ON WINDOW RESIZE --- - if (app.events.consume(Resized)) |_| { - return; - } - // --- SURFACE TEXTURE --- - var surface_texture: wgpu.WGPUSurfaceTexture = .{}; - defer wgpu.wgpuTextureRelease(surface_texture.texture); + defer wgpu.wgpuTextureRelease( + res.frame.surface_texture.texture, + ); // --- GET SURFACE TEXTURE --- wgpu.wgpuSurfaceGetCurrentTexture( @ptrCast(res.window.surface), - &surface_texture, + &res.frame.surface_texture, ); // --- DEBUG --- - switch (surface_texture.status) { + switch (res.frame.surface_texture.status) { wgpu.WGPUSurfaceGetCurrentTextureStatus_Error => { std.debug.print("\x1b[31m[SURFACE_TEXTURE_ERROR]\x1b[0m\n", .{}); }, @@ -51,109 +53,47 @@ pub fn draw(app: *App) !void { } // --- GET SURFACE COLOR VIEW --- - const target_view = wgpu.wgpuTextureCreateView(surface_texture.texture, &wgpu.WGPUTextureViewDescriptor{ - .format = wgpu.wgpuTextureGetFormat(surface_texture.texture), + res.frame.target_view = wgpu.wgpuTextureCreateView(res.frame.surface_texture.texture, &wgpu.WGPUTextureViewDescriptor{ + .format = wgpu.wgpuTextureGetFormat(res.frame.surface_texture.texture), .dimension = wgpu.WGPUTextureViewDimension_2D, .baseMipLevel = 0, .mipLevelCount = 1, .baseArrayLayer = 0, .arrayLayerCount = 1, .aspect = wgpu.WGPUTextureAspect_All, - }); - defer wgpu.wgpuTextureViewRelease(target_view); + }) orelse return error.WGPUTargetView; + defer wgpu.wgpuTextureViewRelease(res.frame.target_view); // --- ENCODER --- - const encoder = wgpu.wgpuDeviceCreateCommandEncoder( + res.frame.encoder = wgpu.wgpuDeviceCreateCommandEncoder( res.device.raw, &wgpu.WGPUCommandEncoderDescriptor{}, + ) orelse return error.WGPUEncoder; + defer wgpu.wgpuCommandEncoderRelease(res.frame.encoder); + + // --- # --- + var render_ctx: RenderGraph.RenderCTX = .{ + .app = app, + .world = res.world, + .graph = res.render_graph, + .device = res.device.raw, + .encoder = res.frame.encoder, + }; + + // --- # --- + try res.render_graph.run( + &render_ctx, ); - defer wgpu.wgpuCommandEncoderRelease(encoder); - - // --- FRAME --- - var render_pass: ?*wgpu.WGPURenderPassEncoderImpl = null; - var camera: ?u64 = null; - - for (res.renderer.commands.items) |cmd| { - switch (cmd) { - .begin_pass => |v| { - // --- END PREV PASS --- - if (render_pass) |rp| { - wgpu.wgpuRenderPassEncoderEnd(rp); - wgpu.wgpuRenderPassEncoderRelease(rp); - } - - // --- NEW RENDER PASS --- - render_pass = wgpu.wgpuCommandEncoderBeginRenderPass( - encoder, - &wgpu.WGPURenderPassDescriptor{ - .colorAttachmentCount = 1, - .colorAttachments = &wgpu.WGPURenderPassColorAttachment{ - .view = target_view, - .resolveTarget = null, - .loadOp = switch (v.load_op) { - .load => wgpu.WGPULoadOp_Load, - .clear => wgpu.WGPULoadOp_Clear, - }, - .storeOp = switch (v.store_op) { - .store => wgpu.WGPUStoreOp_Store, - .discard => wgpu.WGPUStoreOp_Discard, - }, - .clearValue = .{ - .r = v.color[0], - .g = v.color[1], - .b = v.color[2], - .a = v.color[3], - }, - .depthSlice = wgpu.WGPU_DEPTH_SLICE_UNDEFINED, - }, - }, - ); - - // --- SET CAMERA --- - camera = v.camera; - }, - .end_pass => { - if (render_pass) |rp| { - wgpu.wgpuRenderPassEncoderEnd(rp); - wgpu.wgpuRenderPassEncoderRelease(rp); - render_pass = null; - } - }, - .draw => |v| { - // --- ENSURE --- - 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_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 gpu_mesh = res.render_world.gpu_objects.getByKey(v.entity) orelse return error.GPUMeshNotFound; - 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_camera.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); - - // --- DRAW --- - if (mesh.ibuf) |ibuf| { - wgpu.wgpuRenderPassEncoderSetIndexBuffer(render_pass, ibuf, wgpu.WGPUIndexFormat_Uint32, 0, wgpu.wgpuBufferGetSize(ibuf)); - wgpu.wgpuRenderPassEncoderDrawIndexed(render_pass, mesh.index_count, 1, 0, 0, 0); - } else { - wgpu.wgpuRenderPassEncoderDraw(render_pass, mesh.vertex_count, 1, 0, 0); - } - }, - else => {}, - } - } // --- FINISH ENCODER | CMD BUFFER --- - const cmd_buff = wgpu.wgpuCommandEncoderFinish(encoder, &.{}); + const cmd_buff = wgpu.wgpuCommandEncoderFinish(res.frame.encoder, &.{}); defer wgpu.wgpuCommandBufferRelease(cmd_buff); // --- SUBMIT AND PRESENT --- wgpu.wgpuQueueSubmit(res.device.queue, 1, &cmd_buff); _ = wgpu.wgpuSurfacePresent(@ptrCast(res.window.surface)); + + // --- CLEAR --- + res.render_data.clear(); + res.render_phase.clear(); } diff --git a/src/plugins/renderer/system/queue.zig b/src/plugins/renderer/system/queue.zig new file mode 100644 index 0000000..5b4309a --- /dev/null +++ b/src/plugins/renderer/system/queue.zig @@ -0,0 +1,38 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +// --- # --- +const std = @import("std"); +const math = @import("math"); + +// --- RESOURCES --- +const App = @import("app"); +const World = @import("world"); +const RenderPhase = @import("../resource/render_phase.zig"); +const RenderData = @import("../resource/render_data.zig"); +const MaterialAssets = @import("../resource/material_assets.zig"); + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn meshes(app: *App) !void { + // --- # --- + const res = try app.resources.getMany(struct { + world: *World, + render_data: *RenderData, + render_phase: *RenderPhase, + material_assets: *MaterialAssets, + }); + + for (res.render_data.meshes.items) |mesh| { + // --- GET MATERIAL --- + const material = res.material_assets.get( + mesh.material, + ) orelse return error.MaterialNotFound; + + // --- # --- + try res.render_phase.opaques.append(app.alloc, .{ + .source = mesh.source, + .material = material.unique, + }); + } +} diff --git a/src/plugins/template/root.zig b/src/plugins/template/root.zig index 8c9d392..5213e78 100644 --- a/src/plugins/template/root.zig +++ b/src/plugins/template/root.zig @@ -13,7 +13,7 @@ const App = @import("app"); /// ---------------------------------------------------- /// ---------------------------------------------------- -pub const plugin: App.plugin.Plugin = .{ +pub const plugin: App.Plugin = .{ .name = "Template", .build = build, }; @@ -21,7 +21,7 @@ pub const plugin: App.plugin.Plugin = .{ /// ---------------------------------------------------- /// ---------------------------------------------------- fn build( - _: *App.plugin.Plugin, + _: *App.Plugin, app: *App, ) !void { _ = app; diff --git a/src/plugins/window/resource/gpu_window.zig b/src/plugins/window/resource/gpu_window.zig new file mode 100644 index 0000000..67018ed --- /dev/null +++ b/src/plugins/window/resource/gpu_window.zig @@ -0,0 +1,5 @@ +const sdl = @import("sdl"); +const sdl_glue = @import("sdl_glue"); + +raw: *sdl.SDL_Window, +surface: *sdl_glue.WGPUSurfaceImpl, diff --git a/src/plugins/window/resource/window.zig b/src/plugins/window/resource/window.zig index 67018ed..b16a7d4 100644 --- a/src/plugins/window/resource/window.zig +++ b/src/plugins/window/resource/window.zig @@ -1,5 +1,3 @@ -const sdl = @import("sdl"); -const sdl_glue = @import("sdl_glue"); - -raw: *sdl.SDL_Window, -surface: *sdl_glue.WGPUSurfaceImpl, +title: []const u8 = "NYXEngine", +width: u32 = 800, +height: u32 = 600, diff --git a/src/plugins/window/root.zig b/src/plugins/window/root.zig index 44f0692..204bbc8 100644 --- a/src/plugins/window/root.zig +++ b/src/plugins/window/root.zig @@ -5,6 +5,7 @@ // PRIVATE // +const gtl = @import("gtl"); const sdl = @import("sdl"); const sdl_glue = @import("sdl_glue"); @@ -17,6 +18,7 @@ const WGPUDevice = @import("../device/resource/wgpu_device.zig"); // pub const Window = @import("resource/window.zig"); +pub const GPUWindow = @import("resource/gpu_window.zig"); pub const Resized = @import("event/resized.zig"); /// ---------------------------------------------------- @@ -46,11 +48,25 @@ pub fn init(app: *App) !void { WGPUInstance, ) orelse return error.WGPUInstanceNotSet; + // --- ENSURE WINDOW --- + const window = try app.resources.getOrSet( + Window{}, + ); + + // DEBUG + if (gtl.log.print(.debug, "WINDOW", gtl.ansi.yellow)) |v| { + defer v.end(); + + v.write("Title: \"{s}\"", .{window.title}); + v.write("Width: {}", .{window.width}); + v.write("Height: {}", .{window.height}); + } + // --- WINDOW --- const raw_window = sdl.SDL_CreateWindow( - "NYXEngine", - 800, - 600, + window.title.ptr, + @intCast(window.width), + @intCast(window.height), sdl.SDL_WINDOW_RESIZABLE | sdl.SDL_WINDOW_HIGH_PIXEL_DENSITY, ) orelse return error.CreateWindow; @@ -61,7 +77,7 @@ pub fn init(app: *App) !void { ) orelse return error.GetSurface; // --- RESOURCE --- - try app.resources.set(Window{ + try app.resources.set(GPUWindow{ .raw = raw_window, .surface = raw_surface, }); @@ -72,7 +88,7 @@ pub fn init(app: *App) !void { pub fn deinit(app: *App) !void { // --- # --- const res = try app.resources.getMany(struct { - window: *Window, + window: *GPUWindow, }); // --- # --- @@ -86,7 +102,7 @@ pub fn deinit(app: *App) !void { pub fn configure(app: *App) !void { // --- # --- const res = try app.resources.getMany(struct { - window: *Window, + window: *GPUWindow, device: *WGPUDevice, }); diff --git a/src/plugins/world/root.zig b/src/plugins/world/root.zig new file mode 100644 index 0000000..f37b21d --- /dev/null +++ b/src/plugins/world/root.zig @@ -0,0 +1,40 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +// +// PRIVATE +// + +const App = @import("app"); +const World = @import("world"); + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub const plugin: App.Plugin = .{ + .name = "World", + .build = build, +}; + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +fn build( + _: *App.Plugin, + app: *App, +) !void { + try app.schedules.add(App.stage.init, init); + try app.schedules.add(App.stage.deinit, deinit); +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +fn init(app: *App) !void { + try app.resources.set(World.init(app.alloc)); +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +fn deinit(app: *App) !void { + // --- # --- + const world = try app.resources.require(World); + world.deinit(); +}