diff --git a/engine/core/src/core.zig b/engine/core/src/core.zig index 3f6f0c8..2c20e32 100644 --- a/engine/core/src/core.zig +++ b/engine/core/src/core.zig @@ -169,6 +169,11 @@ pub fn createObject(comptime T: type, params: engine.NeonObjectParams) !*T { return gEngine.createObject(T, params); } +pub fn registerRendererSetup(ctx: *anyopaque, setup: engine.SetupFuncFn) void { + gEngine.rendererCtx = ctx; + gEngine.rendererSetupFunc = setup; +} + pub fn setupEnginePlatform(ctx: *anyopaque, setup: engine.SetupFuncFn, poll: engine.PollFuncFn, proc: engine.ProcEventsFn) void { gEngine.platformCtx = ctx; diff --git a/engine/core/src/engine.zig b/engine/core/src/engine.zig index 23135b3..51e2d8e 100644 --- a/engine/core/src/engine.zig +++ b/engine/core/src/engine.zig @@ -70,6 +70,9 @@ pub const Engine = struct { platformSetupFunc: ?PollFuncFn = null, platformProcEventsFunc: ?ProcEventsFn = null, + rendererCtx: *anyopaque = undefined, + rendererSetupFunc: ?PollFuncFn = null, + engineStartTime: f64 = 0, nfdRuntime: *nfd.NFDRuntime, @@ -280,6 +283,10 @@ pub const Engine = struct { setupFunc(ctx.engine.platformCtx) catch unreachable; } + if (ctx.engine.rendererSetupFunc) |setupFunc| { + setupFunc(ctx.engine.rendererCtx) catch unreachable; + } + while (true) { ctx.engine.tick() catch unreachable; diff --git a/engine/main.zig b/engine/main.zig index ae7acce..c0de943 100644 --- a/engine/main.zig +++ b/engine/main.zig @@ -8,7 +8,7 @@ const realMain = @import("main"); pub const options = @import("BacklogOptions"); pub const std_options = std.Options{ - .enable_segfault_handler = false, + // .enable_segfault_handler = false, }; //pub const build_options = @import("build_options"); @@ -21,6 +21,6 @@ pub const std_options = std.Options{ // } pub fn main() !void { - panickers.attachSegfaultHandler(); + // panickers.attachSegfaultHandler(); try realMain.main(); } diff --git a/engine/platform/src/platform.zig b/engine/platform/src/platform.zig index ba03d93..ad16929 100644 --- a/engine/platform/src/platform.zig +++ b/engine/platform/src/platform.zig @@ -43,6 +43,5 @@ pub fn shutdown_module(allocator: std.mem.Allocator) void { gPlatformInstance.deinit(); - // I have a bug somewhere. need to find out where it is allocator.destroy(gPlatformInstance); } diff --git a/engine/platform/src/windowing.zig b/engine/platform/src/windowing.zig index a47556a..bbe1dbc 100644 --- a/engine/platform/src/windowing.zig +++ b/engine/platform/src/windowing.zig @@ -112,7 +112,7 @@ pub const PlatformInstance = struct { .video = true, .gamepad = true, }) catch return error.BadInit; - self.window = sdl3.c.SDL_CreateWindow(self.windowName, self.windowExtent.x, self.windowExtent.y, 0x20).?; // resizeable + self.window = sdl3.c.SDL_CreateWindow(self.windowName, self.windowExtent.x, self.windowExtent.y, 0).?; // resizeable } pub fn registerSetupFuncs(self: *@This()) !void { diff --git a/engine/rend/src/rend.zig b/engine/rend/src/rend.zig index 12f7d4f..703431f 100644 --- a/engine/rend/src/rend.zig +++ b/engine/rend/src/rend.zig @@ -1,6 +1,6 @@ const std = @import("std"); const core = @import("core"); -const sample_vert = @import("sample.vert"); +const sgpu_renderer = @import("sgpu/renderer.zig"); // controls glfw and general windowing // graphics depends on this one @@ -14,11 +14,10 @@ pub fn start_module(comptime programSpec: anytype, args: anytype, allocator: std _ = args; _ = programSpec; _ = allocator; - core.engine_log("starting up [REND] module...", .{}); - core.engine_log("sample_vert.Scene size = {d}", .{@sizeOf(sample_vert.Scene)}); + try sgpu_renderer.start(); } pub fn shutdown_module(allocator: std.mem.Allocator) void { _ = allocator; - core.engine_log("shutting down [REND] module...", .{}); + sgpu_renderer.shutdown(); } diff --git a/engine/rend/src/sgpu/renderer.zig b/engine/rend/src/sgpu/renderer.zig new file mode 100644 index 0000000..2489089 --- /dev/null +++ b/engine/rend/src/sgpu/renderer.zig @@ -0,0 +1,221 @@ +// sdl3_gpu is like 8 characterso +// so instead of refering to it by it's full name every time +// +// we will just call it sgpu. + +pub const Renderer = struct { + allocator: std.mem.Allocator, + + totalTime: f64 = 0, + + device: *gpu.GPUDevice = undefined, + + shaderType: []const u8 = undefined, + shaderSuffix: []const u8 = undefined, + shaderformat: gpu.GPUShaderFormat = undefined, + entrypoint: []const u8 = "main", + pipeline: *gpu.GPUGraphicsPipeline = undefined, + + scissor: gpu.Rect = .{ .x = 0, .y = 0, .w = 1600, .h = 900 }, + colorBuffer: *gpu.GPUBuffer = undefined, + colorBufferTransfer: *gpu.GPUTransferBuffer = undefined, + window: *sdl3.Window = undefined, + + pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This()); + + pub fn init(allocator: std.mem.Allocator) !*@This() { + const self = try allocator.create(@This()); + + self.* = .{ + .allocator = allocator, + }; + + core.registerRendererSetup(self, engineSetup); + return self; + } + + pub fn engineSetup(p: *anyopaque) core.EngineDataEventError!void { + @as(*@This(), @ptrCast(@alignCast(p))).startRenderer() catch return error.BadInit; + } + + pub fn startRenderer(self: *@This()) !void { + self.device = gpu.createGPUDevice(.{ + .shaderformatSpirv = true, + .shaderformatDxil = true, + .shaderformatMsl = true, + }, false, null); + + self.window = platform.getInstance().window; + + if (!self.device.claimWindowForGPUDevice(self.window)) + return error.UnableToClaimGpu; + + try self.discoverFormats(); + + core.engine_log("sgpu device created, using shader format: {s}", .{self.shaderSuffix}); + + try self.createPipeline(); + } + + pub fn discoverFormats(self: *@This()) !void { + const formats = self.device.getGPUShaderFormats(); + + if (formats.shaderformatSpirv) { + self.shaderType = "spv"; // shaderformatSpirv + self.shaderSuffix = ".spv"; + self.shaderformat = .{ .shaderformatSpirv = true }; + } else if (formats.shaderformatMsl) { + self.shaderType = "msl"; // shaderformatSpirv + self.shaderSuffix = ".msl"; + self.entrypoint = "main0"; + self.shaderformat = .{ .shaderformatMsl = true }; + } else if (formats.shaderformatDxil) { + self.shaderType = "dxil"; // shaderformatSpirv + self.shaderSuffix = ".dxil"; + self.shaderformat = .{ .shaderformatDxil = true }; + } + } + + pub fn createPipeline(self: *@This()) !void { + const vertex = try self.loadShader("sample.vert", 0, 0, 1, 0); + const fragment = try self.loadShader("sample.frag", 0, 0, 0, 0); + + var pci = std.mem.zeroes(gpu.GPUGraphicsPipelineCreateInfo); + pci.fragment_shader = fragment; + pci.vertex_shader = vertex; + + pci.target_info.num_color_targets = 1; + pci.target_info.color_target_descriptions = &[_]gpu.GPUColorTargetDescription{ + .{ + .format = self.device.getGPUSwapchainTextureFormat(self.window), + .blend_state = std.mem.zeroes(gpu.GPUColorTargetBlendState), + }, + }; + + pci.rasterizer_state.fill_mode = .fillmodeFill; + self.pipeline = self.device.createGPUGraphicsPipeline(&pci); + + self.colorBuffer = self.device.createGPUBuffer(&.{ + .usage = .{ .bufferusageVertex = true }, + .size = 8192 * 2, + .props = 0, + }); + + self.colorBufferTransfer = self.device.createGPUTransferBuffer(&.{ + .usage = .transferbufferusageUpload, + .size = 8192 * 2, + .props = 0, + }); + } + + pub fn loadShader( + self: *@This(), + shaderName: []const u8, + num_samplers: u32, // The number of samplers defined in the shader. + num_storage_textures: u32, // The number of storage textures defined in the shader. + num_storage_buffers: u32, // The number of storage buffers defined in the shader. + num_uniform_buffers: u32, // The number of uniform buffers defined in the shader. + ) !*gpu.GPUShader { + const contentPath = try std.fmt.allocPrint(self.allocator, "_shaders/{s}/{s}{s}", .{ self.shaderType, shaderName, self.shaderSuffix }); + + defer self.allocator.free(contentPath); + + const mapping = try core.fs().loadFile(contentPath); + defer core.fs().unmap(mapping); + + var stage: gpu.GPUShaderStage = undefined; + + if (std.mem.endsWith(u8, shaderName, ".vert")) { + stage = .shaderstageVertex; + } else if (std.mem.endsWith(u8, shaderName, ".frag")) { + stage = .shaderstageFragment; + } else { + return error.NotImplemented; + } + + const sci = gpu.GPUShaderCreateInfo{ + .code = @ptrCast(mapping.bytes.ptr), + .entrypoint = @ptrCast(self.entrypoint.ptr), + .format = self.shaderformat, + .code_size = mapping.bytes.len - 1, + .stage = stage, + .num_samplers = num_samplers, + .num_storage_textures = num_storage_textures, // The number of storage textures defined in the shader. + .num_storage_buffers = num_storage_buffers, // The number of storage buffers defined in the shader. + .num_uniform_buffers = num_uniform_buffers, // The number of uniform buffers defined in the shader. + .props = 0, + }; + + const rv = self.device.createGPUShader(&sci); + core.engine_log("creating shader {s} => {s} {any}", .{ shaderName, contentPath, stage }); + return rv; + } + + pub fn tick(self: *@This(), dt: f64) void { + self.totalTime += dt; + + const cmd = self.device.acquireGPUCommandBuffer(); + var swapchain_texture: *gpu.GPUTexture = undefined; + { + const buffer = self.device.mapGPUTransferBuffer(self.colorBufferTransfer, true); + + var b: [*][4]f32 = @ptrCast(@alignCast(buffer)); + + b[0] = .{ @floatCast(std.math.sin(self.totalTime * 3 * 2 + 0.8) * 0.2 + 0.8), 0.4, 0.4, 1.0 }; + b[1] = .{ 0.4, @floatCast(std.math.sin(self.totalTime * 2 * 2 + 0.3) * 0.2 + 0.8), 0.4, 1.0 }; + b[2] = .{ 0.4, 0.4, @floatCast(std.math.sin(self.totalTime * 4 * 2) * 0.2 + 0.8), 1.0 }; + + self.device.unmapGPUTransferBuffer(self.colorBufferTransfer); + } + + { + const copyPass = cmd.beginGPUCopyPass(); + defer copyPass.endGPUCopyPass(); + copyPass.uploadToGPUBuffer(&.{ .transfer_buffer = self.colorBufferTransfer, .offset = 0 }, &.{ + .buffer = self.colorBuffer, + .offset = 0, + .size = 4 * 12, + }, true); + } + + if (cmd.waitAndAcquireGPUSwapchainTexture(self.window, &swapchain_texture, null, null)) { + var targetInfo: gpu.GPUColorTargetInfo = std.mem.zeroes(gpu.GPUColorTargetInfo); + targetInfo.texture = swapchain_texture; + targetInfo.clear_color = .{ .r = 0.1, .g = 0.1, .b = 0.1, .a = 1.0 }; + targetInfo.load_op = .loadopClear; + targetInfo.store_op = .storeopStore; + + const renderpass = cmd.beginGPURenderPass(&targetInfo, 1, null); + renderpass.bindGPUGraphicsPipeline(self.pipeline); + renderpass.bindGPUVertexStorageBuffers(0, &self.colorBuffer, 1); + renderpass.setGPUScissor(&self.scissor); + renderpass.drawGPUPrimitives(3, 1, 0, 0); + renderpass.endGPURenderPass(); + } + + _ = cmd.submitGPUCommandBuffer(); + } + + pub fn deinit(self: *@This()) void { + self.allocator.destroy(self); + } +}; + +pub var gRenderer: *Renderer = undefined; +pub var gAllocator: std.mem.Allocator = undefined; + +pub fn start() !void { + gRenderer = try core.createObject(Renderer, .{ .can_tick = true, .isCore = true }); + gAllocator = gRenderer.allocator; +} + +pub fn shutdown() void {} + +const rend = @import("../rend.zig"); + +const std = @import("std"); +const core = @import("core"); +const platform = @import("platform"); +const sdl3 = @import("sdl3"); +const gpu = sdl3.gpu; +const SSBO_Scene = @import("sample.vert").Scene; diff --git a/engine/rend/src/shaders.zig b/engine/rend/src/shaders.zig new file mode 100644 index 0000000..1e56b09 --- /dev/null +++ b/engine/rend/src/shaders.zig @@ -0,0 +1,10 @@ +// renderer agnostic shader management + +pub const loadShader = renderer.loadShader; + +const renderer = @import("gpu/renderer"); + +const std = @import("std"); +const core = @import("core"); + +const rend = @import("rend.zig"); diff --git a/lib/sdl3/src/samples/hello-triangle.zig b/lib/sdl3/src/samples/hello-triangle.zig index 1859fc3..c90936b 100644 --- a/lib/sdl3/src/samples/hello-triangle.zig +++ b/lib/sdl3/src/samples/hello-triangle.zig @@ -46,7 +46,7 @@ pub fn startContext(self: *@This()) !void { .shaderformatSpirv = true, .shaderformatDxil = true, .shaderformatMsl = true, - }, false, null); + }, true, null); if (!self.device.claimWindowForGPUDevice(self.window)) return error.UnableToClaimGpu;