From ccde4427be781cc364d373332fb68d9def473358 Mon Sep 17 00:00:00 2001 From: abux Date: Fri, 31 Jul 2026 01:11:10 +0100 Subject: [PATCH] Proper resource based render | Somewhat proper rendering | Changed stuff n stuff | Now go amimir --- build.zig | 6 + build.zig.zon | 4 + src/main.zig | 9 +- .../cache/resource/embeded/shaders/pbr.wgsl | 12 +- src/plugins/cache/resource/mesh_cache.zig | 3 + ...eline_cache.zig => pipeline_cache.zig.old} | 64 ++- ...{shader_cache.zig => shader_cache.zig.old} | 0 src/plugins/cache/root.zig | 3 +- src/plugins/cache/system/lifetime.zig | 21 +- src/plugins/common/component/camera.zig | 22 + src/plugins/common/component/material.zig.old | 54 --- src/plugins/common/component/transform2D.zig | 6 +- src/plugins/common/component/transform3D.zig | 8 +- src/plugins/common/root.zig | 3 + src/plugins/playground/root.zig | 27 +- .../resource/embeded/shaders/pbr.wgsl | 83 ++++ .../renderer/resource/render_world.zig | 47 ++ src/plugins/renderer/resource/renderer.zig | 2 +- src/plugins/renderer/root.zig | 9 + src/plugins/renderer/system/frame.zig | 40 +- src/plugins/renderer/system/lifetime.zig | 50 ++- src/plugins/renderer/system/prepare.zig | 405 ++++++++++++++++++ src/plugins/renderer/system/present.zig | 17 +- .../renderer/template/frame_object.zig | 20 + src/plugins/renderer/template/frame_view.zig | 6 + src/plugins/renderer/template/gpu_camera.zig | 3 + .../renderer/template/gpu_material.zig | 5 + .../renderer/template/gpu_pipeline.zig | 5 + src/plugins/renderer/template/gpu_shader.zig | 3 + src/plugins/renderer/template/material.zig | 2 +- src/plugins/renderer/template/shader_id.zig | 5 + 31 files changed, 811 insertions(+), 133 deletions(-) rename src/plugins/cache/resource/{pipeline_cache.zig => pipeline_cache.zig.old} (81%) rename src/plugins/cache/resource/{shader_cache.zig => shader_cache.zig.old} (100%) create mode 100644 src/plugins/common/component/camera.zig delete mode 100644 src/plugins/common/component/material.zig.old create mode 100644 src/plugins/renderer/resource/embeded/shaders/pbr.wgsl create mode 100644 src/plugins/renderer/resource/render_world.zig create mode 100644 src/plugins/renderer/system/prepare.zig create mode 100644 src/plugins/renderer/template/frame_object.zig create mode 100644 src/plugins/renderer/template/frame_view.zig create mode 100644 src/plugins/renderer/template/gpu_camera.zig create mode 100644 src/plugins/renderer/template/gpu_material.zig create mode 100644 src/plugins/renderer/template/gpu_pipeline.zig create mode 100644 src/plugins/renderer/template/gpu_shader.zig create mode 100644 src/plugins/renderer/template/shader_id.zig diff --git a/build.zig b/build.zig index 28e25ea..9691674 100644 --- a/build.zig +++ b/build.zig @@ -60,6 +60,12 @@ pub fn build(b: *std.Build) void { }); exe.root_module.addImport("zla", zla.module("zla")); + const math = b.dependency("NYXMath", .{ + .target = target, + .optimize = optimize, + }); + exe.root_module.addImport("math", math.module("NYXMath")); + // // SYSTEM DEPENDENCIES // diff --git a/build.zig.zon b/build.zig.zon index 1bb5750..5fab478 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -20,6 +20,10 @@ .url = "git+http://2.120.146.66:25569/abux/gtl.git#3c7fcfcb1276b76ec4695ce5ce9dba4d8255a176", .hash = "gtl-0.0.0-e3QoJMRYAADChgXn2sogTxsbUpEBSHjOGZeoEx4ccMMD", }, + .NYXMath = .{ + .url = "git+http://192.168.0.11:25569/abux/NYXMath.git#58d57b61f3fa25e234af1af3e1e56bb656f4d77d", + .hash = "NYXMath-0.0.0-BD6ST8zOAAB1dgmaGKShXtKmGTuYqgiDpAY2faHM_HnV", + }, }, .paths = .{ diff --git a/src/main.zig b/src/main.zig index 69d2de5..f28d327 100644 --- a/src/main.zig +++ b/src/main.zig @@ -6,6 +6,7 @@ // const std = @import("std"); +const math = @import("math"); const App = @import("app"); // @@ -61,13 +62,17 @@ pub fn main(init: std.process.Init) !void { if (app.events.consume(App.event.AppExit)) |_| break; // --- # --- - try app.schedules.runMany(&.{ + app.schedules.runMany(&.{ App.stage.begin_frame, App.stage.inputs, App.stage.fixed_update, App.stage.update, App.stage.draw, App.stage.end_frame, - }, &app); + }, &app) catch |err| { + std.debug.print("\x1b[31m[ERROR]\x1b[0m {s}\n", .{ + @errorName(err), + }); + }; } } diff --git a/src/plugins/cache/resource/embeded/shaders/pbr.wgsl b/src/plugins/cache/resource/embeded/shaders/pbr.wgsl index 38a2d2d..b9dc924 100644 --- a/src/plugins/cache/resource/embeded/shaders/pbr.wgsl +++ b/src/plugins/cache/resource/embeded/shaders/pbr.wgsl @@ -43,13 +43,13 @@ struct ModelUniform { // UNIFORMS // -// @group(0) @binding(0) -// var camera: CameraUniform; - -// @group(1) @binding(0) -// var model: ModelUniform; - @group(0) @binding(0) +var camera: CameraUniform; + +@group(1) @binding(0) +var model: ModelUniform; + +@group(2) @binding(0) var material: Material; // diff --git a/src/plugins/cache/resource/mesh_cache.zig b/src/plugins/cache/resource/mesh_cache.zig index fd7b893..a344079 100644 --- a/src/plugins/cache/resource/mesh_cache.zig +++ b/src/plugins/cache/resource/mesh_cache.zig @@ -81,6 +81,9 @@ pub fn getOrCreate( mesh: MeshID, ) !*GPUMesh { return try self.data.getOrPut(mesh, blk: { + // --- # --- + if (self.data.getByKey(mesh)) |v| return v; + // --- # --- const vertices: []const Vertex = switch (mesh) { .triangle => &.{ diff --git a/src/plugins/cache/resource/pipeline_cache.zig b/src/plugins/cache/resource/pipeline_cache.zig.old similarity index 81% rename from src/plugins/cache/resource/pipeline_cache.zig rename to src/plugins/cache/resource/pipeline_cache.zig.old index db9523a..57a377d 100644 --- a/src/plugins/cache/resource/pipeline_cache.zig +++ b/src/plugins/cache/resource/pipeline_cache.zig.old @@ -1,4 +1,6 @@ //! ---------------------------------------------------- +//! TODO: Move [materials, materials_gpu and GPUMaterial somewhere else] +//! Depricated //! ---------------------------------------------------- const std = @import("std"); @@ -16,8 +18,7 @@ const Vertex = @import("mesh_cache.zig").Vertex; pub const GPUPipeline = struct { raw: *wgpu.WGPURenderPipelineImpl, layout: *wgpu.WGPUPipelineLayoutImpl, - bind_group_layouts: [1]*wgpu.WGPUBindGroupLayoutImpl, - // bind_group_layouts: []const *wgpu.WGPUBindGroupLayoutImpl, + bind_group_layouts: [3]*wgpu.WGPUBindGroupLayoutImpl, }; /// ---------------------------------------------------- @@ -28,6 +29,15 @@ pub const GPUMaterial = struct { dirty: bool = true, }; +/// ---------------------------------------------------- +/// TODO: Mabe mabe switch to it, someday +/// ---------------------------------------------------- +pub const GPUUniform = struct { + buffer: wgpu.WGPUBuffer, + bind_group: wgpu.WGPUBindGroup, + dirty: bool = true, +}; + // // FIELDS // @@ -145,7 +155,43 @@ pub fn getOrCreatePipeline( // --- # --- if (self.pipelines.getByKey(material.unique)) |v| return v; - // --- MATERIAL LAYOUT --- + // + // LAYOUTS + // + + // --- CAMERA --- + const camera_layout = wgpu.wgpuDeviceCreateBindGroupLayout(self.device, &wgpu.WGPUBindGroupLayoutDescriptor{ + .entryCount = 1, + .entries = &[_]wgpu.WGPUBindGroupLayoutEntry{ + .{ + .binding = 0, + .visibility = wgpu.WGPUShaderStage_Vertex | wgpu.WGPUShaderStage_Fragment, + .buffer = .{ + .type = wgpu.WGPUBufferBindingType_Uniform, + .minBindingSize = @sizeOf(Material.Style), + .hasDynamicOffset = 0, + }, + }, + }, + }) orelse return error.BindGroupLayout; + + // --- MODEL --- + const model_layout = wgpu.wgpuDeviceCreateBindGroupLayout(self.device, &wgpu.WGPUBindGroupLayoutDescriptor{ + .entryCount = 1, + .entries = &[_]wgpu.WGPUBindGroupLayoutEntry{ + .{ + .binding = 0, + .visibility = wgpu.WGPUShaderStage_Vertex | wgpu.WGPUShaderStage_Fragment, + .buffer = .{ + .type = wgpu.WGPUBufferBindingType_Uniform, + .minBindingSize = @sizeOf(Material.Style), + .hasDynamicOffset = 0, + }, + }, + }, + }) orelse return error.BindGroupLayout; + + // --- MATERIAL --- const material_layout = wgpu.wgpuDeviceCreateBindGroupLayout(self.device, &wgpu.WGPUBindGroupLayoutDescriptor{ .entryCount = 1, .entries = &[_]wgpu.WGPUBindGroupLayoutEntry{ @@ -161,12 +207,18 @@ pub fn getOrCreatePipeline( }, }) orelse return error.BindGroupLayout; + var bind_group_layouts = [_]wgpu.WGPUBindGroupLayout{ + camera_layout, + model_layout, + material_layout, + }; + // --- PIPELINE LAYOUT --- const pipeline_layout = wgpu.wgpuDeviceCreatePipelineLayout( self.device, &wgpu.WGPUPipelineLayoutDescriptor{ - .bindGroupLayoutCount = 1, - .bindGroupLayouts = &material_layout, + .bindGroupLayoutCount = 3, + .bindGroupLayouts = &bind_group_layouts, }, ) orelse return error.Layout; @@ -181,6 +233,8 @@ pub fn getOrCreatePipeline( .raw = pipeline, .layout = pipeline_layout, .bind_group_layouts = .{ + camera_layout, + model_layout, material_layout, }, }; diff --git a/src/plugins/cache/resource/shader_cache.zig b/src/plugins/cache/resource/shader_cache.zig.old similarity index 100% rename from src/plugins/cache/resource/shader_cache.zig rename to src/plugins/cache/resource/shader_cache.zig.old diff --git a/src/plugins/cache/root.zig b/src/plugins/cache/root.zig index 90761a2..4128428 100644 --- a/src/plugins/cache/root.zig +++ b/src/plugins/cache/root.zig @@ -1,4 +1,5 @@ //! ---------------------------------------------------- +//! DEPRICATED //! ---------------------------------------------------- // @@ -12,8 +13,6 @@ const lifetime = @import("system/lifetime.zig"); // PUBLIC // -pub const ShaderCache = @import("resource/shader_cache.zig"); -pub const PipelineCache = @import("resource/pipeline_cache.zig"); pub const MeshCache = @import("resource/mesh_cache.zig"); /// ---------------------------------------------------- diff --git a/src/plugins/cache/system/lifetime.zig b/src/plugins/cache/system/lifetime.zig index eb36512..67bfe72 100644 --- a/src/plugins/cache/system/lifetime.zig +++ b/src/plugins/cache/system/lifetime.zig @@ -6,8 +6,6 @@ const gtl = @import("gtl"); const App = @import("app"); const WGPUDevice = @import("../../device/resource/wgpu_device.zig"); -const ShaderCache = @import("../resource/shader_cache.zig"); -const PipelineCache = @import("../resource/pipeline_cache.zig"); const MeshCache = @import("../resource/mesh_cache.zig"); /// ---------------------------------------------------- @@ -18,19 +16,7 @@ pub fn init(app: *App) !void { device: *const WGPUDevice, }); - // --- SHADER CACHE --- - const shaders = try app.resources.getOrSet(ShaderCache.init( - res.device.raw, - app.alloc, - )); - - // --- PIPELINE CACHE --- - const pipelines = try app.resources.getOrSet(PipelineCache.init( - shaders, - res.device.queue, - res.device.raw, - app.alloc, - )); + // TODO: Depricated stuff here | Use RenderWorld // --- MESH CACHE --- const meshes = try app.resources.getOrSet(MeshCache.init( @@ -40,7 +26,6 @@ pub fn init(app: *App) !void { )); _ = meshes; - _ = pipelines; // TODO: Remove or place it somewhere else, idk @@ -94,13 +79,9 @@ pub fn init(app: *App) !void { pub fn deinit(app: *App) !void { // --- # --- const res = try app.resources.getMany(struct { - shaders: *ShaderCache, - pipelines: *PipelineCache, meshes: *MeshCache, }); // --- # --- - res.shaders.deinit(); - res.pipelines.deinit(); res.meshes.deinit(); } diff --git a/src/plugins/common/component/camera.zig b/src/plugins/common/component/camera.zig new file mode 100644 index 0000000..c8eb186 --- /dev/null +++ b/src/plugins/common/component/camera.zig @@ -0,0 +1,22 @@ +const math = @import("math"); + +projection: union(enum) { + perspective: struct { + fov: f32 = 80.0, + far: f32 = 4000.0, + near: f32 = 0.05, + }, + ortho: struct { + size: f32 = 1.0, + far: f32 = 4000.0, + near: f32 = 0.05, + }, +} = .{ .ortho = .{} }, + +target: union(enum) { + window, + texture, // TODO: Handle to a texture, someday +} = .window, + +order: u32 = 0, +priority: u32 = 0, diff --git a/src/plugins/common/component/material.zig.old b/src/plugins/common/component/material.zig.old deleted file mode 100644 index d64534b..0000000 --- a/src/plugins/common/component/material.zig.old +++ /dev/null @@ -1,54 +0,0 @@ -//! ---------------------------------------------------- -//! ---------------------------------------------------- - -const gtl = @import("gtl"); -const ShaderID = @import("../../cache/resource/shader_cache.zig").ShaderID; -const Handle = gtl.Handle; -const Self = @This(); - -/// ---------------------------------------------------- -/// ---------------------------------------------------- -pub const Style = struct { - albedo: @Vector(4, f32) = .{ 1, 1, 1, 1 }, - metallic: f32 = 0.0, - roughness: f32 = 0.5, - _pad0: [2]f32 = .{ 0, 0 }, -}; - -/// ---------------------------------------------------- -/// ---------------------------------------------------- -pub const Unique = struct { - shader: ShaderID = .pbr, - - front_face: enum { - cw, - ccw, - } = .ccw, - - cull_mode: enum { - back, - front, - none, - } = .back, - - topology: enum { - triangle_list, - point_list, - line_list, - } = .triangle_list, - - blend_mode: enum { - none, - alpha, - additive, - multiply, - } = .none, -}; - -// -// FIELDS -// - -style: Style = .{}, -unique: Unique = .{}, -value: ?Handle(Self) = null, diff --git a/src/plugins/common/component/transform2D.zig b/src/plugins/common/component/transform2D.zig index 0708cb6..bde6923 100644 --- a/src/plugins/common/component/transform2D.zig +++ b/src/plugins/common/component/transform2D.zig @@ -1,3 +1,5 @@ -position: @Vector(2, f32) = .{ 0, 0 }, -scale: @Vector(2, f32) = .{ 32, 32 }, +const math = @import("math"); + +position: math.Vec2f = .as(0), +scale: math.Vec2f = .as(32), angle: f32 = 0, diff --git a/src/plugins/common/component/transform3D.zig b/src/plugins/common/component/transform3D.zig index e3423f6..23cb847 100644 --- a/src/plugins/common/component/transform3D.zig +++ b/src/plugins/common/component/transform3D.zig @@ -1,3 +1,5 @@ -position: @Vector(3, f32) = .{ 0, 0, 0 }, -rotation: @Vector(3, f32) = .{ 0, 0, 0 }, -scale: @Vector(3, f32) = .{ 1, 1, 1 }, +const math = @import("math"); + +position: math.Vec3f = .as(0), +rotation: math.Quat = .identity(), +scale: math.Vec3f = .as(0), diff --git a/src/plugins/common/root.zig b/src/plugins/common/root.zig index e52a1ae..31968ee 100644 --- a/src/plugins/common/root.zig +++ b/src/plugins/common/root.zig @@ -5,6 +5,9 @@ // COMPONENTS // +// --- VIEW --- +const Camera = @import("component/camera.zig"); + // --- POSITION --- pub const Transform3D = @import("component/transform3D.zig"); pub const Transform2D = @import("component/transform2D.zig"); diff --git a/src/plugins/playground/root.zig b/src/plugins/playground/root.zig index 84eeede..78c7baa 100644 --- a/src/plugins/playground/root.zig +++ b/src/plugins/playground/root.zig @@ -6,11 +6,13 @@ // const App = @import("app"); + +const Camera = @import("../common/component/camera.zig"); const Mesh = @import("../common/component/mesh.zig"); const Material = @import("../common/component/material.zig"); const Transform3D = @import("../common/component/transform3D.zig"); -const PipelineCache = @import("../cache/resource/pipeline_cache.zig"); +const RenderWorld = @import("../renderer/resource/render_world.zig"); // // PUBLIC @@ -36,19 +38,27 @@ fn build( /// ---------------------------------------------------- fn start(app: *App) !void { // --- # --- - const pipeline_cache = app.resources.getPtr( - PipelineCache, - ) orelse return error.PipelineCacheNotSet; + const render_world = app.resources.getPtr( + RenderWorld, + ).?; + + // --- CAMERA --- + try app.world.spawn(.{ + Camera{}, + Transform3D{}, + }); // --- # --- try app.world.spawn(.{ Mesh{ .value = .square }, Material{ - .value = try pipeline_cache.materials.put(.{ + .value = try render_world.materials.put(.{ .style = .{ - .albedo = .{ 0.8, 0.2, 0.2, 1 }, + .albedo = .{ 0.8, 0.2, 0.2, 1.0 }, + }, + .unique = .{ + .topology = .line_list, }, - .unique = .{}, }), }, Transform3D{}, @@ -58,11 +68,10 @@ fn start(app: *App) !void { try app.world.spawn(.{ Mesh{ .value = .triangle }, Material{ - .value = try pipeline_cache.materials.put(.{ + .value = try render_world.materials.put(.{ .style = .{ .albedo = .{ 0.2, 0.2, 0.8, 1 }, }, - .unique = .{}, }), }, Transform3D{}, diff --git a/src/plugins/renderer/resource/embeded/shaders/pbr.wgsl b/src/plugins/renderer/resource/embeded/shaders/pbr.wgsl new file mode 100644 index 0000000..b9dc924 --- /dev/null +++ b/src/plugins/renderer/resource/embeded/shaders/pbr.wgsl @@ -0,0 +1,83 @@ +// ---------------------------------------------------- +// ---------------------------------------------------- +struct VertexInput { + @location(0) position: vec3f, + @location(1) normal: vec3f, + @location(2) uv: vec2f, +}; + +// ---------------------------------------------------- +// ---------------------------------------------------- +struct VertexOutput { + @builtin(position) position: vec4f, + @location(0) uv: vec2f, + @location(1) normal: vec3f, +}; + +// ---------------------------------------------------- +// ---------------------------------------------------- +struct Material { + albedo: vec4f, + metallic: f32, + roughness: f32, + _pad0: vec2f, +}; + +// ---------------------------------------------------- +// ---------------------------------------------------- +struct CameraUniform { + view: mat4x4, + projection: mat4x4, + view_projection: mat4x4, + camera_position: vec3, + _pad: f32, +}; + +// ---------------------------------------------------- +// ---------------------------------------------------- +struct ModelUniform { + model: mat4x4, +}; + +// +// 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 = 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/render_world.zig b/src/plugins/renderer/resource/render_world.zig new file mode 100644 index 0000000..fb625ac --- /dev/null +++ b/src/plugins/renderer/resource/render_world.zig @@ -0,0 +1,47 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +// --- # --- +const std = @import("std"); +const gtl = @import("gtl"); +const wgpu = @import("wgpu"); + +// --- CAMERA --- +pub const GPUCamera = @import("../template/gpu_camera.zig"); +pub const FrameView = @import("../template/frame_view.zig"); + +// --- SHADER --- +pub const ShaderID = @import("../template/shader_id.zig").ShaderID; +pub const GPUShader = @import("../template/gpu_shader.zig"); + +// --- MATERIAL --- +pub const Material = @import("../template/material.zig"); +pub const GPUMaterial = @import("../template/gpu_material.zig"); + +// --- PIPELINE --- +pub const Pipeline = Material.Unique; +pub const GPUPipeline = @import("../template/gpu_pipeline.zig"); + +// --- OBJECT --- +pub const Entity = u64; // TODO: You lazy retard +pub const FrameObject = @import("../template/frame_object.zig"); + +// +// EMBEDED +// + +pub const PBRCode = @embedFile("embeded/shaders/pbr.wgsl"); +pub const UnlitCode = @embedFile("embeded/shaders/pbr.wgsl"); + +// +// FIELDS +// + +gpu_cameras: gtl.KeyedSlotMap(Entity, GPUCamera), +gpu_shaders: gtl.KeyedSlotMap(ShaderID, GPUShader), +gpu_pipelines: gtl.KeyedSlotMap(Pipeline, GPUPipeline), +gpu_materials: gtl.KeyedSlotMap(gtl.Handle(Material), GPUMaterial), + +frame_views: std.ArrayList(FrameView), +frame_objects: std.ArrayList(FrameObject), +materials: gtl.SlotMap(Material), // TODO: Move this to assets resource or sum diff --git a/src/plugins/renderer/resource/renderer.zig b/src/plugins/renderer/resource/renderer.zig index f65191a..7159454 100644 --- a/src/plugins/renderer/resource/renderer.zig +++ b/src/plugins/renderer/resource/renderer.zig @@ -25,7 +25,7 @@ pub const DrawCMD = union(enum) { }, begin_pass: struct { - camera: Handle(Camera), + camera: u64, // TODO: Change it to Entity type instead color: [4]f32 = .{ 0, 0, 0, 255 }, load_op: enum { load, diff --git a/src/plugins/renderer/root.zig b/src/plugins/renderer/root.zig index d58367d..f021bd1 100644 --- a/src/plugins/renderer/root.zig +++ b/src/plugins/renderer/root.zig @@ -9,6 +9,7 @@ const App = @import("app"); const lifetime = @import("system/lifetime.zig"); const frame = @import("system/frame.zig"); +const prepare = @import("system/prepare.zig"); const present = @import("system/present.zig"); // @@ -38,6 +39,14 @@ fn build( try app.schedules.add(App.stage.begin_frame, frame.begin); try app.schedules.add(App.stage.end_frame, frame.end); + // --- PRE DRAW --- + try app.schedules.addManyBefore(App.stage.draw, App.stage.draw, &.{ + prepare.cameras, + prepare.shaders, + prepare.pipelines, + prepare.materials, + }); + // --- DRAW --- try app.schedules.add(App.stage.draw, present.draw); } diff --git a/src/plugins/renderer/system/frame.zig b/src/plugins/renderer/system/frame.zig index df8c9f7..e2c0e3b 100644 --- a/src/plugins/renderer/system/frame.zig +++ b/src/plugins/renderer/system/frame.zig @@ -4,6 +4,7 @@ const std = @import("std"); const App = @import("app"); const Renderer = @import("../resource/renderer.zig"); +const RenderWorld = @import("../resource/render_world.zig"); const Mesh = @import("../../common/component/mesh.zig"); const Material = @import("../../common/component/material.zig"); @@ -15,35 +16,38 @@ pub fn begin(app: *App) !void { // --- # --- const res = try app.resources.getMany(struct { renderer: *Renderer, + render_world: *RenderWorld, }); // --- # --- res.renderer.clear(); // --- DRAW --- - try res.renderer.draw(.{ - .begin_pass = .{ - .camera = undefined, - .color = .{ 0.001, 0.001, 0.001, 1.0 }, - }, - }); - defer res.renderer.draw(.end_pass) catch unreachable; - - // --- QUERY --- - var query = app.world.query(struct { - mesh: *Mesh, - material: *Material, - transform3D: ?*Transform3D, - }, .{}); - while (query.next()) |e| { + for (res.render_world.frame_views.items) |view| { + // --- NEW PASS --- try res.renderer.draw(.{ - .draw = .{ - .material = e.material.value, - .mesh = e.mesh.value, + .begin_pass = .{ + .camera = view.camera, + .color = .{ 0.001, 0.001, 0.001, 1.0 }, }, }); + defer res.renderer.draw(.end_pass) catch unreachable; + + // --- # --- + for (res.render_world.frame_objects.items) |obj| { + try res.renderer.draw(.{ + .draw = .{ + .material = obj.material, + .mesh = obj.mesh, + }, + }); + } } + // --- YES --- + res.render_world.frame_views.clearRetainingCapacity(); + res.render_world.frame_objects.clearRetainingCapacity(); + // { // TEST // // --- DRAW --- // try res.renderer.draw(.{ diff --git a/src/plugins/renderer/system/lifetime.zig b/src/plugins/renderer/system/lifetime.zig index 0a76dc2..7212f0a 100644 --- a/src/plugins/renderer/system/lifetime.zig +++ b/src/plugins/renderer/system/lifetime.zig @@ -1,9 +1,12 @@ //! ---------------------------------------------------- //! ---------------------------------------------------- +const wgpu = @import("wgpu"); const App = @import("app"); const Zla = @import("zla"); + const Renderer = @import("../resource/renderer.zig"); +const RenderWorld = @import("../resource/render_world.zig"); /// ---------------------------------------------------- /// ---------------------------------------------------- @@ -12,11 +15,54 @@ pub fn init(app: *App) !void { try app.resources.set(Renderer.init( app.alloc, )); + + // --- # --- + try app.resources.set(RenderWorld{ + .gpu_cameras = .init(app.alloc), + .gpu_shaders = .init(app.alloc), + .gpu_materials = .init(app.alloc), + .gpu_pipelines = .init(app.alloc), + + .frame_views = .empty, + .frame_objects = .empty, + + .materials = .init(app.alloc), + }); } /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn deinit(app: *App) !void { - const renderer = app.resources.getPtr(Renderer) orelse return error.RendererNotSet; - renderer.deinit(); + // --- # --- + const res = try app.resources.getMany(struct { + ren: *Renderer, + ren_world: *RenderWorld, + }); + + // --- FREE SHADERS --- + for (res.ren_world.gpu_shaders.items()) |entry| { + wgpu.wgpuShaderModuleRelease(entry.value.raw); + } + + // --- FREE PIPELINES --- + for (res.ren_world.gpu_pipelines.items()) |entry| { + wgpu.wgpuPipelineLayoutRelease(entry.value.layout); + wgpu.wgpuRenderPipelineRelease(entry.value.raw); + } + + // --- FREE MATERIALS --- + for (res.ren_world.gpu_materials.items()) |entry| { + wgpu.wgpuBufferRelease(entry.value.buffer); + wgpu.wgpuBindGroupRelease(entry.value.bind_group); + } + + // --- # --- + res.ren_world.materials.deinit(); + res.ren_world.frame_views.deinit(app.alloc); + res.ren_world.frame_objects.deinit(app.alloc); + res.ren_world.gpu_cameras.deinit(); + res.ren_world.gpu_shaders.deinit(); + res.ren_world.gpu_materials.deinit(); + res.ren_world.gpu_pipelines.deinit(); + res.ren.deinit(); } diff --git a/src/plugins/renderer/system/prepare.zig b/src/plugins/renderer/system/prepare.zig new file mode 100644 index 0000000..6113afd --- /dev/null +++ b/src/plugins/renderer/system/prepare.zig @@ -0,0 +1,405 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +// --- # --- +const std = @import("std"); +const wgpu = @import("wgpu"); +const App = @import("app"); +const Zla = @import("zla"); + +// --- # --- +const Vertex = @import("../../cache/resource/mesh_cache.zig").Vertex; +const WGPUDevice = @import("../../device/resource/wgpu_device.zig"); +const RenderWorld = @import("../resource/render_world.zig"); + +// --- # --- +const Transform2D = @import("../../common/component/transform2D.zig"); +const Transform3D = @import("../../common/component/transform3D.zig"); + +// --- MESH --- +const CMesh = @import("../../common/component/mesh.zig"); + +// --- MATERIAL --- +const CMaterial = @import("../../common/component/material.zig"); +const TMaterial = @import("../template/material.zig"); + +// --- CAMERA --- +const CCamera = @import("../../common/component/camera.zig"); +const TCamera = @import("../template/gpu_camera.zig"); + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn cameras(app: *App) !void { + // --- # --- + const res = try app.resources.getMany(struct { + render_world: *RenderWorld, + }); + + { + // --- # --- + var query = app.world.query(struct { + camera: *const CCamera, + }, .{}); + while (query.next()) |e| { + // --- GPU --- + const raw = try res.render_world.gpu_cameras.getOrPut(query.entity(), blk: { + if (res.render_world.gpu_cameras.getByKey(query.entity())) |v| break :blk v.*; + + break :blk .{ + .projection = switch (e.camera.projection) { + .perspective => |v| .perspective(v.fov, 800.0 / 600.0, v.near, v.far), + .ortho => |v| .orthographic(800, 0, 600, 0, v.near, v.far), + }, + }; + }); + + // --- FRAME --- + try res.render_world.frame_views.append(app.alloc, .{ + .order = e.camera.order, + .priority = e.camera.priority, + .camera = query.entity(), + .raw = raw, + }); + } + } + + { + // --- # --- + var query = app.world.query(struct { + mesh: *const CMesh, + material: *const CMaterial, + transform2D: ?*const Transform2D, + transform3D: ?*const Transform3D, + }, .{}); + while (query.next()) |e| { + try res.render_world.frame_objects.append(app.alloc, .{ + .mesh = e.mesh.value, + .material = e.material.value, + .transform = blk: { + if (e.transform2D) |v| break :blk .{ .@"2D" = v.* }; + if (e.transform3D) |v| break :blk .{ .@"3D" = v.* }; + break :blk .{ .@"2D" = .{} }; + }, + }); + } + } +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn shaders(app: *App) !void { + // --- # --- + const res = try app.resources.getMany(struct { + render_world: *RenderWorld, + device: *WGPUDevice, + }); + + // --- # --- + var query = app.world.query(struct { + material: *const CMaterial, + }, .{}); + while (query.next()) |e| { + // --- GET MATERIAL --- + const material = res.render_world.materials.get( + e.material.value, + ) orelse return error.MaterialNotFound; + + // --- ENSURE SHADER --- + _ = try res.render_world.gpu_shaders.getOrPut(material.unique.shader, blk: { + // --- # --- + if (res.render_world.gpu_shaders.contains( + material.unique.shader, + )) return; + + // --- CODE --- + const code: []const u8 = switch (material.unique.shader) { + .pbr => RenderWorld.PBRCode, + .unlit => RenderWorld.UnlitCode, + .custom => return error.TooLazyToImplement, // TODO: Yes + }; + + // --- SOURCE --- + var source: wgpu.WGPUShaderSourceWGSL = .{ + .chain = .{ .sType = wgpu.WGPUSType_ShaderSourceWGSL }, + .code = .{ .data = code.ptr, .length = wgpu.WGPU_STRLEN }, + }; + + // --- RESULT --- + break :blk .{ + .raw = wgpu.wgpuDeviceCreateShaderModule(res.device.raw, &wgpu.WGPUShaderModuleDescriptor{ + .label = .{ .data = @tagName(material.unique.shader), .length = wgpu.WGPU_STRLEN }, + .nextInChain = &source.chain, + }) orelse { + return error.ShaderModuleCreationFailed; + }, + }; + }); + } +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn pipelines(app: *App) !void { + // --- # --- + const res = try app.resources.getMany(struct { + render_world: *RenderWorld, + device: *WGPUDevice, + }); + + // --- # --- + var query = app.world.query(struct { + material: *const CMaterial, + }, .{}); + while (query.next()) |e| { + // --- GET MATERIAL --- + const material = res.render_world.materials.get( + e.material.value, + ) orelse return error.MaterialNotFound; + + // --- ENSURE PIPELINE --- + _ = try res.render_world.gpu_pipelines.getOrPut(material.unique, blk: { + // --- # --- + if (res.render_world.gpu_pipelines.contains( + material.unique, + )) return; + + // + // LAYOUTS + // + + // --- CAMERA --- + const camera_layout = wgpu.wgpuDeviceCreateBindGroupLayout(res.device.raw, &wgpu.WGPUBindGroupLayoutDescriptor{ + .entryCount = 1, + .entries = &[_]wgpu.WGPUBindGroupLayoutEntry{ + .{ + .binding = 0, + .visibility = wgpu.WGPUShaderStage_Vertex | wgpu.WGPUShaderStage_Fragment, + .buffer = .{ + .type = wgpu.WGPUBufferBindingType_Uniform, + .minBindingSize = @sizeOf(TMaterial.Style), + .hasDynamicOffset = 0, + }, + }, + }, + }) orelse return error.BindGroupLayout; + + // --- MODEL --- + const model_layout = wgpu.wgpuDeviceCreateBindGroupLayout(res.device.raw, &wgpu.WGPUBindGroupLayoutDescriptor{ + .entryCount = 1, + .entries = &[_]wgpu.WGPUBindGroupLayoutEntry{ + .{ + .binding = 0, + .visibility = wgpu.WGPUShaderStage_Vertex | wgpu.WGPUShaderStage_Fragment, + .buffer = .{ + .type = wgpu.WGPUBufferBindingType_Uniform, + .minBindingSize = @sizeOf(TMaterial.Style), + .hasDynamicOffset = 0, + }, + }, + }, + }) orelse return error.BindGroupLayout; + + // --- MATERIAL --- + const material_layout = wgpu.wgpuDeviceCreateBindGroupLayout(res.device.raw, &wgpu.WGPUBindGroupLayoutDescriptor{ + .entryCount = 1, + .entries = &[_]wgpu.WGPUBindGroupLayoutEntry{ + .{ + .binding = 0, + .visibility = wgpu.WGPUShaderStage_Vertex | wgpu.WGPUShaderStage_Fragment, + .buffer = .{ + .type = wgpu.WGPUBufferBindingType_Uniform, + .minBindingSize = @sizeOf(TMaterial.Style), + .hasDynamicOffset = 0, + }, + }, + }, + }) orelse return error.BindGroupLayout; + + var bind_group_layouts = [_]wgpu.WGPUBindGroupLayout{ + camera_layout, + model_layout, + material_layout, + }; + + // --- PIPELINE LAYOUT --- + const pipeline_layout = wgpu.wgpuDeviceCreatePipelineLayout( + res.device.raw, + &wgpu.WGPUPipelineLayoutDescriptor{ + .bindGroupLayoutCount = 3, + .bindGroupLayouts = &bind_group_layouts, + }, + ) orelse return error.Layout; + + // --- CREATE PIPELINE --- + const pipeline = try createPipeline( + material.unique, + pipeline_layout, + if (res.render_world.gpu_shaders.getByKey(material.unique.shader)) |v| v.raw else return error.ShaderNotFound, + res.device.raw, + ); + + // --- RESULT --- + break :blk .{ + .raw = pipeline, + .layout = pipeline_layout, + .bind_group_layouts = .{ + camera_layout, + model_layout, + material_layout, + }, + }; + }); + } +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn materials(app: *App) !void { + // --- # --- + const res = try app.resources.getMany(struct { + render_world: *RenderWorld, + device: *WGPUDevice, + }); + + // --- # --- + var query = app.world.query(struct { + material: *const CMaterial, + }, .{}); + while (query.next()) |e| { + // --- ENSURE MATERIAL --- + _ = try res.render_world.gpu_materials.getOrPut( + e.material.value, + blk: { + // --- # --- + if (res.render_world.gpu_materials.contains( + e.material.value, + )) return; + + // --- # --- + const material = res.render_world.materials.get(e.material.value) orelse return error.MaterialNotFound; + const pipeline = res.render_world.gpu_pipelines.getByKey(material.unique) orelse return error.PipelineNotFound; + + // --- UNIFORM --- + const uniform = wgpu.wgpuDeviceCreateBuffer(res.device.raw, &wgpu.WGPUBufferDescriptor{ + .usage = wgpu.WGPUBufferUsage_CopyDst | wgpu.WGPUBufferUsage_Uniform, + .size = @sizeOf(TMaterial.Style), + }) orelse return error.Buffer; + + // --- WRITE TO UNIFORM --- + wgpu.wgpuQueueWriteBuffer( + res.device.queue, + uniform, + 0, + &material.style, + @sizeOf(TMaterial.Style), + ); + + // --- BIND GROUP --- + const bind_group = wgpu.wgpuDeviceCreateBindGroup(res.device.raw, &wgpu.WGPUBindGroupDescriptor{ + .layout = pipeline.bind_group_layouts[0], + .entryCount = 1, + .entries = &[_]wgpu.WGPUBindGroupEntry{ + .{ + .binding = 0, + .buffer = uniform, + .offset = 0, + .size = @sizeOf(TMaterial.Style), + }, + }, + }) orelse return error.BindGroup; + + // --- RESULT --- + break :blk .{ + .buffer = uniform, + .bind_group = bind_group, + .dirty = false, + }; + }, + ); + } +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +fn createPipeline( + key: TMaterial.Unique, + layout: *wgpu.WGPUPipelineLayoutImpl, + shader: *wgpu.WGPUShaderModuleImpl, + device: *wgpu.WGPUDeviceImpl, +) !*wgpu.WGPURenderPipelineImpl { + return wgpu.wgpuDeviceCreateRenderPipeline(device, &wgpu.WGPURenderPipelineDescriptor{ + .label = .{ .data = @tagName(key.shader), .length = wgpu.WGPU_STRLEN }, + .layout = layout, + .vertex = .{ + .module = shader, + .entryPoint = .{ .data = "vs_main", .length = wgpu.WGPU_STRLEN }, + .bufferCount = 1, + .buffers = &wgpu.WGPUVertexBufferLayout{ + .attributeCount = 3, + .attributes = &[_]wgpu.WGPUVertexAttribute{ // TODO: Mabe mabe allow for custom, mabe mabe later tho + .{ + .format = wgpu.WGPUVertexFormat_Float32x3, + .offset = @offsetOf(Vertex, "position"), + .shaderLocation = 0, + }, + .{ + .format = wgpu.WGPUVertexFormat_Float32x3, + .offset = @offsetOf(Vertex, "normal"), + .shaderLocation = 1, + }, + .{ + .format = wgpu.WGPUVertexFormat_Float32x2, + .offset = @offsetOf(Vertex, "uv"), + .shaderLocation = 2, + }, + }, + .arrayStride = @sizeOf(Vertex), + .stepMode = wgpu.WGPUVertexStepMode_Vertex, + }, + }, + .fragment = &wgpu.WGPUFragmentState{ + .module = shader, + .entryPoint = .{ .data = "fs_main", .length = wgpu.WGPU_STRLEN }, + .targetCount = 1, + .targets = &wgpu.WGPUColorTargetState{ + .format = 28, // FIX: Use proper format + .blend = &wgpu.WGPUBlendState{ + .color = .{ + .srcFactor = wgpu.WGPUBlendFactor_SrcAlpha, + .dstFactor = wgpu.WGPUBlendFactor_OneMinusSrcAlpha, + .operation = wgpu.WGPUBlendOperation_Add, + }, + .alpha = .{ + .srcFactor = wgpu.WGPUBlendFactor_Zero, + .dstFactor = wgpu.WGPUBlendFactor_One, + .operation = wgpu.WGPUBlendOperation_Add, + }, + }, + .writeMask = wgpu.WGPUColorWriteMask_All, + }, + }, + .primitive = .{ + .topology = switch (key.topology) { + .triangle_list => wgpu.WGPUPrimitiveTopology_TriangleList, + .point_list => wgpu.WGPUPrimitiveTopology_PointList, + .line_list => wgpu.WGPUPrimitiveTopology_LineList, + }, + .frontFace = switch (key.front_face) { + .ccw => wgpu.WGPUFrontFace_CCW, + .cw => wgpu.WGPUFrontFace_CW, + }, + .cullMode = switch (key.cull_mode) { + .back => wgpu.WGPUCullMode_Back, + .front => wgpu.WGPUCullMode_Front, + .none => wgpu.WGPUCullMode_None, + }, + }, + .multisample = .{ + .count = 1, + .mask = ~@as(u32, 0), + .alphaToCoverageEnabled = 0, + }, + }) orelse { + return error.PipelineCreationFailed; + }; +} diff --git a/src/plugins/renderer/system/present.zig b/src/plugins/renderer/system/present.zig index b1291c0..8abbdd5 100644 --- a/src/plugins/renderer/system/present.zig +++ b/src/plugins/renderer/system/present.zig @@ -9,7 +9,7 @@ const Window = @import("../../window/resource/window.zig"); const Resized = @import("../../window/event/resized.zig"); const Device = @import("../../device/resource/wgpu_device.zig"); const Renderer = @import("../resource/renderer.zig"); -const PipelineCache = @import("../../cache/resource/pipeline_cache.zig"); +const RenderWorld = @import("../resource/render_world.zig"); const MeshCache = @import("../../cache/resource/mesh_cache.zig"); /// ---------------------------------------------------- @@ -20,13 +20,10 @@ pub fn draw(app: *App) !void { window: *Window, device: *Device, renderer: *Renderer, - pipeline_cache: *PipelineCache, + render_world: *RenderWorld, mesh_cache: *MeshCache, }); - { // TODO: Move this | Gather Materials - } - // --- DO NONE ON WINDOW RESIZE --- if (app.events.consume(Resized)) |_| { return; @@ -120,15 +117,19 @@ pub fn draw(app: *App) !void { }, .draw => |v| { // --- ENSURE --- - const material = res.pipeline_cache.materials.get(v.material) orelse return error.MaterialNotFound; - const gpu_material = try res.pipeline_cache.getOrCreateMaterial(v.material); - const pipeline = try res.pipeline_cache.getOrCreatePipeline(material); + const material = res.render_world.materials.get(v.material) orelse return error.MaterialNotFound; + const gpu_material = res.render_world.gpu_materials.getByKey(v.material) orelse return error.GPUMaterialNotFound; + const pipeline = res.render_world.gpu_pipelines.getByKey(material.unique) orelse return error.GPUPipelineNotFound; const mesh = try res.mesh_cache.getOrCreate(v.mesh); // --- BIND --- wgpu.wgpuRenderPassEncoderSetPipeline(render_pass, pipeline.raw); wgpu.wgpuRenderPassEncoderSetVertexBuffer(render_pass, 0, mesh.vbuf, 0, wgpu.wgpuBufferGetSize(mesh.vbuf)); + + // --- BIND SHADER GROUPS --- wgpu.wgpuRenderPassEncoderSetBindGroup(render_pass, 0, gpu_material.bind_group, 0, null); + wgpu.wgpuRenderPassEncoderSetBindGroup(render_pass, 1, gpu_material.bind_group, 0, null); + wgpu.wgpuRenderPassEncoderSetBindGroup(render_pass, 2, gpu_material.bind_group, 0, null); // --- DRAW --- if (mesh.ibuf) |ibuf| { diff --git a/src/plugins/renderer/template/frame_object.zig b/src/plugins/renderer/template/frame_object.zig new file mode 100644 index 0000000..c89a3be --- /dev/null +++ b/src/plugins/renderer/template/frame_object.zig @@ -0,0 +1,20 @@ +//! ---------------------------------------------------- +//! ---------------------------------------------------- + +const gtl = @import("gtl"); +const Material = @import("material.zig"); +const Transform2D = @import("../../common/component/transform2D.zig"); +const Transform3D = @import("../../common/component/transform3D.zig"); +const MeshID = @import("../../cache/resource/mesh_cache.zig").MeshID; +const Handle = gtl.Handle; + +// +// FIELDS +// + +mesh: MeshID, +material: Handle(Material), +transform: union(enum) { + @"2D": Transform2D, + @"3D": Transform3D, +}, diff --git a/src/plugins/renderer/template/frame_view.zig b/src/plugins/renderer/template/frame_view.zig new file mode 100644 index 0000000..9ce723e --- /dev/null +++ b/src/plugins/renderer/template/frame_view.zig @@ -0,0 +1,6 @@ +const GPUCamera = @import("gpu_camera.zig"); + +raw: *GPUCamera, +order: u32 = 0, +priority: u32 = 0, +camera: u64, // TODO: Change to ENTITY Type diff --git a/src/plugins/renderer/template/gpu_camera.zig b/src/plugins/renderer/template/gpu_camera.zig new file mode 100644 index 0000000..5ec6048 --- /dev/null +++ b/src/plugins/renderer/template/gpu_camera.zig @@ -0,0 +1,3 @@ +const math = @import("math"); + +projection: math.Mat4x4, diff --git a/src/plugins/renderer/template/gpu_material.zig b/src/plugins/renderer/template/gpu_material.zig new file mode 100644 index 0000000..8cf76fb --- /dev/null +++ b/src/plugins/renderer/template/gpu_material.zig @@ -0,0 +1,5 @@ +const wgpu = @import("wgpu"); + +buffer: *wgpu.WGPUBufferImpl, +bind_group: *wgpu.WGPUBindGroupImpl, +dirty: bool = true, diff --git a/src/plugins/renderer/template/gpu_pipeline.zig b/src/plugins/renderer/template/gpu_pipeline.zig new file mode 100644 index 0000000..7e2e123 --- /dev/null +++ b/src/plugins/renderer/template/gpu_pipeline.zig @@ -0,0 +1,5 @@ +const wgpu = @import("wgpu"); + +raw: *wgpu.WGPURenderPipelineImpl, +layout: *wgpu.WGPUPipelineLayoutImpl, +bind_group_layouts: [3]*wgpu.WGPUBindGroupLayoutImpl, diff --git a/src/plugins/renderer/template/gpu_shader.zig b/src/plugins/renderer/template/gpu_shader.zig new file mode 100644 index 0000000..0c26654 --- /dev/null +++ b/src/plugins/renderer/template/gpu_shader.zig @@ -0,0 +1,3 @@ +const wgpu = @import("wgpu"); + +raw: *wgpu.WGPUShaderModuleImpl, diff --git a/src/plugins/renderer/template/material.zig b/src/plugins/renderer/template/material.zig index 9decce6..4568d68 100644 --- a/src/plugins/renderer/template/material.zig +++ b/src/plugins/renderer/template/material.zig @@ -2,7 +2,7 @@ //! ---------------------------------------------------- const gtl = @import("gtl"); -const ShaderID = @import("../../cache/resource/shader_cache.zig").ShaderID; +const ShaderID = @import("../template/shader_id.zig").ShaderID; const Handle = gtl.Handle; const Self = @This(); diff --git a/src/plugins/renderer/template/shader_id.zig b/src/plugins/renderer/template/shader_id.zig new file mode 100644 index 0000000..7e9e1ed --- /dev/null +++ b/src/plugins/renderer/template/shader_id.zig @@ -0,0 +1,5 @@ +pub const ShaderID = union(enum) { + pbr, + unlit, + custom: u32, +};