Hello Triangle!
This commit is contained in:
parent
c16eb13ba1
commit
7700000bd7
11 changed files with 414 additions and 3 deletions
|
|
@ -7,8 +7,14 @@ paru -S --needed sdl3 wgpu-native
|
|||
```
|
||||
|
||||
## TODO
|
||||
|
||||
### Core
|
||||
- [x] Platform
|
||||
- [x] Window
|
||||
- [x] Device
|
||||
- [x] Cache
|
||||
- [ ] Renderer
|
||||
|
||||
### Engine
|
||||
- [ ] ClassDB
|
||||
- [ ] UI
|
||||
|
|
|
|||
18
src/main.zig
18
src/main.zig
|
|
@ -18,6 +18,7 @@ const platform = @import("plugins/platform/root.zig");
|
|||
const device = @import("plugins/device/root.zig");
|
||||
const window = @import("plugins/window/root.zig");
|
||||
const cache = @import("plugins/cache/root.zig");
|
||||
const input = @import("plugins/input/root.zig");
|
||||
const renderer = @import("plugins/renderer/root.zig");
|
||||
|
||||
/// ----------------------------------------------------
|
||||
|
|
@ -33,6 +34,7 @@ pub fn main(init: std.process.Init) !void {
|
|||
device.plugin,
|
||||
window.plugin,
|
||||
cache.plugin,
|
||||
input.plugin,
|
||||
renderer.plugin,
|
||||
});
|
||||
try app.plugins.build(&app);
|
||||
|
|
@ -48,4 +50,20 @@ pub fn main(init: std.process.Init) !void {
|
|||
// --- LIFETIME ---
|
||||
try app.schedules.run(App.stage.init, &app);
|
||||
defer app.schedules.runReversed(App.stage.deinit, &app) catch unreachable;
|
||||
|
||||
// --- MAIN LOOP ---
|
||||
while (true) {
|
||||
// --- # ---
|
||||
if (app.events.consume(App.event.AppExit)) |_| break;
|
||||
|
||||
// --- # ---
|
||||
try 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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
29
src/plugins/input/root.zig
Normal file
29
src/plugins/input/root.zig
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
//! ----------------------------------------------------
|
||||
//! ----------------------------------------------------
|
||||
|
||||
//
|
||||
// PRIVATE
|
||||
//
|
||||
|
||||
const App = @import("app");
|
||||
const input = @import("system/input.zig");
|
||||
|
||||
//
|
||||
// PUBLIC
|
||||
//
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub const plugin: App.plugin.Plugin = .{
|
||||
.name = "Input",
|
||||
.build = build,
|
||||
};
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
fn build(
|
||||
_: *App.plugin.Plugin,
|
||||
app: *App,
|
||||
) !void {
|
||||
try app.schedules.add(App.stage.inputs, input.pollEvents);
|
||||
}
|
||||
17
src/plugins/input/system/input.zig
Normal file
17
src/plugins/input/system/input.zig
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
//! ----------------------------------------------------
|
||||
//! ----------------------------------------------------
|
||||
|
||||
const sdl = @import("sdl");
|
||||
const App = @import("app");
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub fn pollEvents(app: *App) !void {
|
||||
var event: sdl.SDL_Event = undefined;
|
||||
while (sdl.SDL_PollEvent(&event)) {
|
||||
switch (event.type) {
|
||||
sdl.SDL_EVENT_QUIT => try app.events.send(App.event.AppExit{}),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
73
src/plugins/renderer/resource/renderer.zig
Normal file
73
src/plugins/renderer/resource/renderer.zig
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
//! ----------------------------------------------------
|
||||
//! ----------------------------------------------------
|
||||
|
||||
const std = @import("std");
|
||||
const gtl = @import("gtl");
|
||||
const Material = @import("../../common/component/material.zig");
|
||||
const Handle = gtl.Handle;
|
||||
const Self = @This();
|
||||
|
||||
// TODO:
|
||||
const Mesh = struct {};
|
||||
const Camera = struct {};
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub const DrawCMD = union(enum) {
|
||||
draw: struct {
|
||||
mesh: Handle(Mesh),
|
||||
material: Material.Unique,
|
||||
},
|
||||
|
||||
draw_indexed: struct {
|
||||
mesh: Handle(Mesh),
|
||||
material: Material.Unique,
|
||||
},
|
||||
|
||||
begin_pass: struct {
|
||||
camera: Handle(Camera),
|
||||
color: [4]f32 = .{ 0, 0, 0, 255 },
|
||||
},
|
||||
|
||||
end_pass,
|
||||
};
|
||||
|
||||
//
|
||||
// FIELDS
|
||||
//
|
||||
|
||||
commands: std.ArrayList(DrawCMD),
|
||||
alloc: std.mem.Allocator,
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub fn init(alloc: std.mem.Allocator) Self {
|
||||
return .{
|
||||
.commands = .empty,
|
||||
.alloc = alloc,
|
||||
};
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.commands.deinit(self.alloc);
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub fn clear(self: *Self) void {
|
||||
self.commands.clearRetainingCapacity();
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub fn draw(
|
||||
self: *Self,
|
||||
cmd: DrawCMD,
|
||||
) !void {
|
||||
try self.commands.append(
|
||||
self.alloc,
|
||||
cmd,
|
||||
);
|
||||
}
|
||||
|
|
@ -7,10 +7,16 @@
|
|||
|
||||
const App = @import("app");
|
||||
|
||||
const lifetime = @import("system/lifetime.zig");
|
||||
const frame = @import("system/frame.zig");
|
||||
const present = @import("system/present.zig");
|
||||
|
||||
//
|
||||
// PUBLIC
|
||||
//
|
||||
|
||||
const Renderer = @import("resource/renderer.zig");
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub const plugin: App.plugin.Plugin = .{
|
||||
|
|
@ -24,5 +30,14 @@ fn build(
|
|||
_: *App.plugin.Plugin,
|
||||
app: *App,
|
||||
) !void {
|
||||
_ = app;
|
||||
// --- 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);
|
||||
|
||||
// --- DRAW ---
|
||||
try app.schedules.add(App.stage.draw, present.draw);
|
||||
}
|
||||
|
|
|
|||
39
src/plugins/renderer/system/frame.zig
Normal file
39
src/plugins/renderer/system/frame.zig
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
//! ----------------------------------------------------
|
||||
//! ----------------------------------------------------
|
||||
|
||||
const App = @import("app");
|
||||
const Renderer = @import("../resource/renderer.zig");
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub fn begin(app: *App) !void {
|
||||
// --- # ---
|
||||
const res = try app.resources.getMany(struct {
|
||||
renderer: *Renderer,
|
||||
});
|
||||
|
||||
// --- # ---
|
||||
res.renderer.clear();
|
||||
|
||||
{ // TEST
|
||||
try res.renderer.draw(.{
|
||||
.begin_pass = .{
|
||||
.camera = undefined,
|
||||
.color = .{ 0.2, 0.8, 0.2, 1.0 },
|
||||
},
|
||||
});
|
||||
try res.renderer.draw(.{
|
||||
.draw = .{
|
||||
.mesh = undefined,
|
||||
.material = .{},
|
||||
},
|
||||
});
|
||||
try res.renderer.draw(.end_pass);
|
||||
}
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub fn end(app: *App) !void {
|
||||
_ = app;
|
||||
}
|
||||
20
src/plugins/renderer/system/lifetime.zig
Normal file
20
src/plugins/renderer/system/lifetime.zig
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
//! ----------------------------------------------------
|
||||
//! ----------------------------------------------------
|
||||
|
||||
const App = @import("app");
|
||||
const Renderer = @import("../resource/renderer.zig");
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub fn init(app: *App) !void {
|
||||
try app.resources.set(Renderer.init(
|
||||
app.alloc,
|
||||
));
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub fn deinit(app: *App) !void {
|
||||
const renderer = app.resources.getPtr(Renderer) orelse return error.RendererNotSet;
|
||||
renderer.deinit();
|
||||
}
|
||||
114
src/plugins/renderer/system/present.zig
Normal file
114
src/plugins/renderer/system/present.zig
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
//! ----------------------------------------------------
|
||||
//! ----------------------------------------------------
|
||||
|
||||
const std = @import("std");
|
||||
const wgpu = @import("wgpu");
|
||||
const App = @import("app");
|
||||
|
||||
const Window = @import("../../window/resource/window.zig");
|
||||
const Device = @import("../../device/resource/wgpu_device.zig");
|
||||
const Renderer = @import("../resource/renderer.zig");
|
||||
const PipelineCache = @import("../../cache/resource/pipeline_cache.zig");
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub fn draw(app: *App) !void {
|
||||
// --- # ---
|
||||
const res = try app.resources.getMany(struct {
|
||||
window: *Window,
|
||||
device: *Device,
|
||||
renderer: *Renderer,
|
||||
pipeline_cache: *PipelineCache,
|
||||
});
|
||||
|
||||
// --- SURFACE TEXTURE ---
|
||||
var surface_texture: wgpu.WGPUSurfaceTexture = .{};
|
||||
defer wgpu.wgpuTextureRelease(surface_texture.texture);
|
||||
|
||||
// --- GET SURFACE TEXTURE ---
|
||||
wgpu.wgpuSurfaceGetCurrentTexture(
|
||||
@ptrCast(res.window.surface),
|
||||
&surface_texture,
|
||||
);
|
||||
|
||||
// --- 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;
|
||||
|
||||
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 = wgpu.WGPULoadOp_Clear,
|
||||
.storeOp = wgpu.WGPUStoreOp_Store,
|
||||
.clearValue = .{
|
||||
.r = v.color[0],
|
||||
.g = v.color[1],
|
||||
.b = v.color[2],
|
||||
.a = v.color[3],
|
||||
},
|
||||
.depthSlice = wgpu.WGPU_DEPTH_SLICE_UNDEFINED,
|
||||
},
|
||||
},
|
||||
);
|
||||
},
|
||||
.end_pass => {
|
||||
if (render_pass) |rp| {
|
||||
wgpu.wgpuRenderPassEncoderEnd(rp);
|
||||
wgpu.wgpuRenderPassEncoderRelease(rp);
|
||||
render_pass = null;
|
||||
}
|
||||
},
|
||||
.draw => |v| {
|
||||
// --- ENSURE PIPELINE ---
|
||||
const pipeline = try res.pipeline_cache.getOrCreate(
|
||||
v.material,
|
||||
);
|
||||
|
||||
// --- DRAW ---
|
||||
wgpu.wgpuRenderPassEncoderSetPipeline(render_pass, pipeline.raw);
|
||||
wgpu.wgpuRenderPassEncoderDraw(render_pass, 3, 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));
|
||||
}
|
||||
5
src/plugins/window/resource/window.zig
Normal file
5
src/plugins/window/resource/window.zig
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
const sdl = @import("sdl");
|
||||
const sdl_glue = @import("sdl_glue");
|
||||
|
||||
raw: *sdl.SDL_Window,
|
||||
surface: *sdl_glue.WGPUSurfaceImpl,
|
||||
|
|
@ -5,12 +5,19 @@
|
|||
// PRIVATE
|
||||
//
|
||||
|
||||
const sdl = @import("sdl");
|
||||
const sdl_glue = @import("sdl_glue");
|
||||
|
||||
const App = @import("app");
|
||||
const WGPUInstance = @import("../platform/resource/wgpu_instance.zig");
|
||||
const WGPUDevice = @import("../device/resource/wgpu_device.zig");
|
||||
|
||||
//
|
||||
// PUBLIC
|
||||
//
|
||||
|
||||
pub const Window = @import("resource/window.zig");
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub const plugin: App.plugin.Plugin = .{
|
||||
|
|
@ -24,5 +31,73 @@ fn build(
|
|||
_: *App.plugin.Plugin,
|
||||
app: *App,
|
||||
) !void {
|
||||
_ = app;
|
||||
try app.schedules.add(App.stage.init, init);
|
||||
try app.schedules.add(App.stage.deinit, deinit);
|
||||
|
||||
try app.schedules.addAfter(App.stage.init, App.stage.init, configure);
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub fn init(app: *App) !void {
|
||||
// --- GET WGPU INSTANCE ---
|
||||
const instance = app.resources.getPtr(
|
||||
WGPUInstance,
|
||||
) orelse return error.WGPUInstanceNotSet;
|
||||
|
||||
// --- WINDOW ---
|
||||
const raw_window = sdl.SDL_CreateWindow(
|
||||
"NYXEngine",
|
||||
800,
|
||||
600,
|
||||
0,
|
||||
) orelse return error.CreateWindow;
|
||||
|
||||
// --- SURFACE ---
|
||||
const raw_surface = sdl_glue.SDL_GetWGPUSurface(
|
||||
@ptrCast(instance.raw),
|
||||
@ptrCast(raw_window),
|
||||
) orelse return error.GetSurface;
|
||||
|
||||
// --- RESOURCE ---
|
||||
try app.resources.set(Window{
|
||||
.raw = raw_window,
|
||||
.surface = raw_surface,
|
||||
});
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub fn deinit(app: *App) !void {
|
||||
// --- # ---
|
||||
const res = try app.resources.getMany(struct {
|
||||
window: *Window,
|
||||
});
|
||||
|
||||
// --- # ---
|
||||
sdl_glue.wgpuSurfaceUnconfigure(res.window.surface);
|
||||
sdl_glue.wgpuSurfaceRelease(res.window.surface);
|
||||
sdl.SDL_DestroyWindow(res.window.raw);
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub fn configure(app: *App) !void {
|
||||
// --- # ---
|
||||
const res = try app.resources.getMany(struct {
|
||||
window: *Window,
|
||||
device: *WGPUDevice,
|
||||
});
|
||||
|
||||
// --- CONFIGURE SURFACE ---
|
||||
sdl_glue.wgpuSurfaceConfigure(
|
||||
res.window.surface,
|
||||
&sdl_glue.WGPUSurfaceConfiguration{
|
||||
.device = @ptrCast(res.device.raw),
|
||||
.format = 28, // TODO: Get format properly, idk, store it somewhere
|
||||
.width = 800,
|
||||
.height = 600,
|
||||
.usage = sdl_glue.WGPUTextureUsage_RenderAttachment,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue