2026-08-09 14:17:49 +01:00
|
|
|
//! ----------------------------------------------------
|
|
|
|
|
//! Low cortisol graphics
|
|
|
|
|
//! ----------------------------------------------------
|
|
|
|
|
|
2026-08-11 21:06:28 +01:00
|
|
|
// --- # ---
|
2026-08-09 14:17:49 +01:00
|
|
|
const std = @import("std");
|
|
|
|
|
const gtl = @import("gtl");
|
|
|
|
|
const vk = @import("vulkan");
|
2026-08-11 21:06:28 +01:00
|
|
|
const common = @import("rhi/root.zig");
|
|
|
|
|
const Handle = gtl.Handle;
|
|
|
|
|
const Self = @This();
|
|
|
|
|
|
|
|
|
|
// --- CONFIGS ---
|
|
|
|
|
const PipelineConfig = common.PipelineConfig;
|
|
|
|
|
const BufferUsage = common.BufferUsage;
|
2026-08-09 14:17:49 +01:00
|
|
|
|
2026-08-11 21:06:28 +01:00
|
|
|
// --- HANDLES ---
|
2026-08-09 14:17:49 +01:00
|
|
|
const Window = @import("rhi/root.zig").Window;
|
2026-08-11 21:06:28 +01:00
|
|
|
const Adapter = common.Adapter;
|
|
|
|
|
const Device = common.Device;
|
|
|
|
|
const Buffer = common.Buffer;
|
|
|
|
|
const Surface = common.Surface;
|
|
|
|
|
const Swapchain = common.Swapchain;
|
2026-08-09 14:17:49 +01:00
|
|
|
|
2026-08-11 21:06:28 +01:00
|
|
|
const Shader = common.Shader;
|
|
|
|
|
const Pipeline = common.Pipeline;
|
2026-08-09 14:17:49 +01:00
|
|
|
|
2026-08-11 21:06:28 +01:00
|
|
|
// --- RHI ---
|
|
|
|
|
const VulkanRHI = @import("rhi/vulkan/vulkan.zig");
|
2026-08-09 14:17:49 +01:00
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub const Backend = enum { vulkan };
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
const BackendCTX = union(Backend) {
|
|
|
|
|
vulkan: VulkanRHI,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub const Config = struct {
|
|
|
|
|
app_name: []const u8 = "NYXGFX",
|
|
|
|
|
engine_name: []const u8 = "NYX",
|
|
|
|
|
enable_validation: bool = true,
|
|
|
|
|
|
|
|
|
|
window: Window,
|
|
|
|
|
backend: Backend = .vulkan,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// FIELDS
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
ctx: BackendCTX,
|
|
|
|
|
config: Config,
|
2026-08-11 21:06:28 +01:00
|
|
|
|
2026-08-09 14:17:49 +01:00
|
|
|
alloc: std.mem.Allocator,
|
2026-08-11 21:06:28 +01:00
|
|
|
io: std.Io,
|
2026-08-09 14:17:49 +01:00
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub fn init(
|
|
|
|
|
config: Config,
|
|
|
|
|
alloc: std.mem.Allocator,
|
2026-08-11 21:06:28 +01:00
|
|
|
io: std.Io,
|
2026-08-09 14:17:49 +01:00
|
|
|
) !Self {
|
|
|
|
|
const backend_ctx: BackendCTX = switch (config.backend) {
|
2026-08-11 21:06:28 +01:00
|
|
|
.vulkan => .{ .vulkan = try .init(config, alloc, io) },
|
2026-08-09 14:17:49 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return .{
|
|
|
|
|
.config = config,
|
|
|
|
|
.ctx = backend_ctx,
|
|
|
|
|
.alloc = alloc,
|
2026-08-11 21:06:28 +01:00
|
|
|
.io = io,
|
2026-08-09 14:17:49 +01:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub fn deinit(self: *Self) void {
|
|
|
|
|
switch (self.ctx) {
|
|
|
|
|
.vulkan => |*v| v.deinit(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-11 21:06:28 +01:00
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub fn createAdapter(self: *Self) !Handle(Adapter) {
|
|
|
|
|
return try switch (self.ctx) {
|
|
|
|
|
.vulkan => |*v| v.createAdapter(),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub fn createDevice(
|
|
|
|
|
self: *Self,
|
|
|
|
|
adapter: Handle(Adapter),
|
|
|
|
|
surface: Handle(Surface),
|
|
|
|
|
) !Handle(Device) {
|
|
|
|
|
return try switch (self.ctx) {
|
|
|
|
|
.vulkan => |*v| v.createDevice(adapter, surface),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub fn destroyDevice(self: *Self, device: Handle(Device)) !void {
|
|
|
|
|
try switch (self.ctx) {
|
|
|
|
|
.vulkan => |*v| v.destroyDevice(device),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub fn createBuffer(
|
|
|
|
|
self: *Self,
|
|
|
|
|
value: anytype,
|
|
|
|
|
usage: BufferUsage,
|
|
|
|
|
adapter: Handle(Adapter),
|
|
|
|
|
device: Handle(Device),
|
|
|
|
|
) !Buffer {
|
|
|
|
|
const size = @sizeOf(@TypeOf(value));
|
|
|
|
|
|
|
|
|
|
const handle = try switch (self.ctx) {
|
|
|
|
|
.vulkan => |*v| v.createBuffer(size, usage, .exclusive, adapter, device),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var buffer: Buffer = .{
|
|
|
|
|
.handle = handle,
|
|
|
|
|
.device = device,
|
|
|
|
|
.gfx = self,
|
|
|
|
|
};
|
|
|
|
|
try buffer.set(value);
|
|
|
|
|
|
|
|
|
|
return buffer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub fn createSurface(self: *Self) !Handle(Surface) {
|
|
|
|
|
return try switch (self.ctx) {
|
|
|
|
|
.vulkan => |*v| v.createSurface(),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub fn createSwapchain(
|
|
|
|
|
self: *Self,
|
|
|
|
|
adapter: Handle(Adapter),
|
|
|
|
|
device: Handle(Device),
|
|
|
|
|
surface: Handle(Surface),
|
|
|
|
|
) !Handle(Swapchain) {
|
|
|
|
|
return try switch (self.ctx) {
|
|
|
|
|
.vulkan => |*v| v.createSwapchain(adapter, device, surface),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub fn createShader(
|
|
|
|
|
self: *Self,
|
|
|
|
|
vert_spv_path: []const u8,
|
|
|
|
|
frag_spv_path: []const u8,
|
|
|
|
|
device: Handle(Device),
|
|
|
|
|
) !Handle(Shader) {
|
|
|
|
|
return try switch (self.ctx) {
|
|
|
|
|
.vulkan => |*v| v.createShader(vert_spv_path, frag_spv_path, device),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub fn createPipeline(self: *Self, config: PipelineConfig) !Pipeline {
|
|
|
|
|
return .{
|
|
|
|
|
.handle = try switch (self.ctx) {
|
|
|
|
|
.vulkan => |*v| v.createPipeline(config),
|
|
|
|
|
},
|
|
|
|
|
.device = config.device,
|
|
|
|
|
.gfx = self,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub fn beginFrame(
|
|
|
|
|
self: *Self,
|
|
|
|
|
swapchain: Handle(Swapchain),
|
|
|
|
|
device: Handle(Device),
|
|
|
|
|
) !void {
|
|
|
|
|
try switch (self.ctx) {
|
|
|
|
|
.vulkan => |*v| v.beginFrame(swapchain, device),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub fn endFrame(
|
|
|
|
|
self: *Self,
|
|
|
|
|
swapchain: Handle(Swapchain),
|
|
|
|
|
device: Handle(Device),
|
|
|
|
|
) !void {
|
|
|
|
|
try switch (self.ctx) {
|
|
|
|
|
.vulkan => |*v| v.endFrame(swapchain, device),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub fn beginRendering(
|
|
|
|
|
self: *Self,
|
|
|
|
|
swapchain: Handle(Swapchain),
|
|
|
|
|
) !void {
|
|
|
|
|
try switch (self.ctx) {
|
|
|
|
|
.vulkan => |*v| v.beginRendering(swapchain),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub fn endRendering(
|
|
|
|
|
self: *Self,
|
|
|
|
|
swapchain: Handle(Swapchain),
|
|
|
|
|
) !void {
|
|
|
|
|
try switch (self.ctx) {
|
|
|
|
|
.vulkan => |*v| v.endRendering(swapchain),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub fn draw(self: *Self, vertex_count: u32, instance_count: u32, first_vertex: u32, first_instance: u32) !void {
|
|
|
|
|
try switch (self.ctx) {
|
|
|
|
|
.vulkan => |*v| v.draw(vertex_count, instance_count, first_vertex, first_instance),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub fn drawIndexed(self: *Self, index_count: u32, instance_count: u32, first_index: u32, vertex_offset: i32, first_instance: u32) !void {
|
|
|
|
|
switch (self.ctx) {
|
|
|
|
|
.vulkan => |*v| {
|
|
|
|
|
const frame = v.currentFrame();
|
|
|
|
|
if (frame.cmd_buf) |cmd| {
|
|
|
|
|
vk.vkCmdDrawIndexed(cmd, index_count, instance_count, first_index, vertex_offset, first_instance);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|