2026-08-11 21:06:28 +01:00
|
|
|
//! ----------------------------------------------------
|
2026-08-12 14:52:27 +01:00
|
|
|
//! Shared Handles and Configs across every backend
|
2026-08-11 21:06:28 +01:00
|
|
|
//! ----------------------------------------------------
|
|
|
|
|
|
|
|
|
|
const gtl = @import("gtl");
|
|
|
|
|
const Gfx = @import("../gfx.zig");
|
|
|
|
|
const Handle = gtl.Handle;
|
|
|
|
|
|
2026-08-09 14:17:49 +01:00
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub const Window = union(enum) {
|
|
|
|
|
win32: struct {
|
|
|
|
|
hwnd: *anyopaque,
|
|
|
|
|
hinstance: *anyopaque,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
xcb: struct {
|
|
|
|
|
connection: *anyopaque,
|
|
|
|
|
window: u32,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
xlib: struct {
|
|
|
|
|
display: *anyopaque,
|
|
|
|
|
window: c_ulong,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
wayland: struct {
|
|
|
|
|
display: *anyopaque,
|
|
|
|
|
surface: *anyopaque,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
2026-08-11 21:06:28 +01:00
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
2026-08-12 14:52:27 +01:00
|
|
|
pub const GPUType = enum {
|
2026-08-12 02:11:57 +01:00
|
|
|
any,
|
|
|
|
|
discrete,
|
|
|
|
|
integrated,
|
|
|
|
|
software,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub const AdapterConfig = struct {
|
|
|
|
|
/// Preferred class of GPU
|
|
|
|
|
/// `.any` disables filtering by class
|
2026-08-12 14:52:27 +01:00
|
|
|
gpu_type: GPUType = .discrete,
|
2026-08-12 02:11:57 +01:00
|
|
|
|
|
|
|
|
/// Select the adapter at a specific enumeration index
|
|
|
|
|
/// `null` picks the first suitable one
|
|
|
|
|
index: ?u32 = null,
|
|
|
|
|
};
|
2026-08-11 21:06:28 +01:00
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub const DeviceConfig = struct {
|
|
|
|
|
adapter: Handle(Adapter),
|
|
|
|
|
surface: Handle(Surface),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub const Format = enum {
|
|
|
|
|
int,
|
|
|
|
|
float,
|
|
|
|
|
|
|
|
|
|
vec2,
|
|
|
|
|
vec3,
|
|
|
|
|
vec4,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub const VertexBinding = struct {
|
|
|
|
|
binding: u32 = 0,
|
|
|
|
|
stride: u32,
|
|
|
|
|
input_rate: enum {
|
|
|
|
|
vertex,
|
|
|
|
|
instance,
|
|
|
|
|
} = .vertex,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub const VertexAttribute = struct {
|
|
|
|
|
location: u32,
|
|
|
|
|
binding: u32 = 0,
|
|
|
|
|
offset: u32,
|
|
|
|
|
format: Format,
|
|
|
|
|
};
|
|
|
|
|
|
2026-08-12 02:11:57 +01:00
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub const UniformDesc = struct {
|
|
|
|
|
name: []const u8,
|
|
|
|
|
size: u32,
|
|
|
|
|
};
|
|
|
|
|
|
2026-08-12 20:31:05 +01:00
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub const CompareOp = enum {
|
|
|
|
|
never,
|
|
|
|
|
less,
|
|
|
|
|
equal,
|
|
|
|
|
less_equal,
|
|
|
|
|
greater,
|
|
|
|
|
not_equal,
|
|
|
|
|
greater_equal,
|
|
|
|
|
always,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub const DepthFormat = enum {
|
|
|
|
|
d16_unorm,
|
|
|
|
|
d24_unorm_s8,
|
|
|
|
|
d32_sfloat,
|
|
|
|
|
};
|
|
|
|
|
|
2026-08-11 21:06:28 +01:00
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub const PipelineConfig = struct {
|
2026-08-12 02:11:57 +01:00
|
|
|
vert_shader: Handle(Shader),
|
|
|
|
|
frag_shader: Handle(Shader),
|
|
|
|
|
|
2026-08-11 21:06:28 +01:00
|
|
|
swapchain: Handle(Swapchain),
|
|
|
|
|
device: Handle(Device),
|
|
|
|
|
|
|
|
|
|
vertex_binding: ?VertexBinding = null,
|
|
|
|
|
vertex_attributes: []const VertexAttribute = &.{},
|
|
|
|
|
|
2026-08-12 02:11:57 +01:00
|
|
|
uniforms: []const UniformDesc = &.{},
|
2026-08-12 14:52:27 +01:00
|
|
|
textures: []const Handle(Texture) = &.{},
|
2026-08-12 02:11:57 +01:00
|
|
|
|
2026-08-11 21:06:28 +01:00
|
|
|
topology: enum {
|
|
|
|
|
triangle_list,
|
|
|
|
|
line_list,
|
|
|
|
|
} = .triangle_list,
|
|
|
|
|
|
|
|
|
|
cull_mode: enum {
|
|
|
|
|
front_and_back,
|
|
|
|
|
front,
|
|
|
|
|
back,
|
|
|
|
|
none,
|
|
|
|
|
} = .back,
|
|
|
|
|
|
|
|
|
|
front_face: enum {
|
|
|
|
|
ccw,
|
|
|
|
|
cw,
|
2026-08-12 20:31:05 +01:00
|
|
|
} = .ccw,
|
2026-08-12 14:52:27 +01:00
|
|
|
|
|
|
|
|
blend_mode: enum {
|
|
|
|
|
none,
|
|
|
|
|
alpha,
|
|
|
|
|
} = .none,
|
2026-08-12 20:31:05 +01:00
|
|
|
|
|
|
|
|
depth_format: ?DepthFormat = null,
|
|
|
|
|
depth_test: bool = true,
|
|
|
|
|
depth_write: bool = true,
|
|
|
|
|
depth_compare: CompareOp = .less,
|
2026-08-11 21:06:28 +01:00
|
|
|
};
|
|
|
|
|
|
2026-08-12 02:11:57 +01:00
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub const Pass = struct {
|
|
|
|
|
swapchain: Handle(Swapchain),
|
|
|
|
|
gfx: *Gfx,
|
|
|
|
|
|
2026-08-12 14:52:27 +01:00
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
2026-08-12 02:11:57 +01:00
|
|
|
pub fn end(self: *const Pass) void {
|
|
|
|
|
switch (self.gfx.ctx) {
|
2026-08-12 20:31:05 +01:00
|
|
|
.vulkan => |*v| v.endPass(self.swapchain),
|
2026-08-12 02:11:57 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub fn draw(self: *const Pass, vertex_count: u32, instance_count: u32, first_vertex: u32, first_instance: u32) void {
|
|
|
|
|
switch (self.gfx.ctx) {
|
2026-08-12 14:52:27 +01:00
|
|
|
.vulkan => |*v| v.draw(vertex_count, instance_count, first_vertex, first_instance),
|
2026-08-12 02:11:57 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub fn drawIndexed(self: *const Pass, index_count: u32, instance_count: u32, first_index: u32, vertex_offset: i32, first_instance: u32) void {
|
|
|
|
|
switch (self.gfx.ctx) {
|
2026-08-12 14:52:27 +01:00
|
|
|
.vulkan => |*v| v.drawIndexed(index_count, instance_count, first_index, vertex_offset, first_instance),
|
2026-08-12 02:11:57 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub const LoadOp = enum {
|
|
|
|
|
clear,
|
|
|
|
|
load,
|
|
|
|
|
dont_care,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub const StoreOp = enum {
|
|
|
|
|
store,
|
|
|
|
|
dont_care,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub const RenderPassConfig = struct {
|
|
|
|
|
clear_color: [4]f32 = .{ 0.0, 0.0, 0.0, 1.0 },
|
2026-08-12 20:31:05 +01:00
|
|
|
|
|
|
|
|
depth_enabled: bool = false,
|
|
|
|
|
depth_format: DepthFormat = .d32_sfloat,
|
|
|
|
|
depth_load_op: LoadOp = .clear,
|
|
|
|
|
depth_store_op: StoreOp = .dont_care,
|
|
|
|
|
|
|
|
|
|
clear_depth: f32 = 1.0,
|
|
|
|
|
clear_stencil: u8 = 0,
|
|
|
|
|
|
2026-08-12 02:11:57 +01:00
|
|
|
load_op: LoadOp = .clear,
|
|
|
|
|
store_op: StoreOp = .store,
|
2026-08-12 20:31:05 +01:00
|
|
|
|
2026-08-12 02:11:57 +01:00
|
|
|
width: ?u32 = null,
|
|
|
|
|
height: ?u32 = null,
|
|
|
|
|
};
|
|
|
|
|
|
2026-08-11 21:06:28 +01:00
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub const BufferUsage = enum {
|
|
|
|
|
vertex,
|
|
|
|
|
index,
|
2026-08-12 14:52:27 +01:00
|
|
|
image,
|
2026-08-11 21:06:28 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub const BufferSharingMode = enum {
|
|
|
|
|
exclusive,
|
|
|
|
|
};
|
|
|
|
|
|
2026-08-12 14:52:27 +01:00
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub const PixelFormat = enum {
|
|
|
|
|
rgba8_unorm,
|
|
|
|
|
rgba8_srgb,
|
|
|
|
|
bgra8_unorm,
|
|
|
|
|
bgra8_srgb,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub const Filter = enum {
|
|
|
|
|
nearest,
|
|
|
|
|
linear,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub const MipmapMode = enum {
|
|
|
|
|
nearest,
|
|
|
|
|
linear,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub const AddressMode = enum {
|
|
|
|
|
repeat,
|
|
|
|
|
mirrored_repeat,
|
|
|
|
|
clamp_to_edge,
|
|
|
|
|
clamp_to_border,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub const ImageUsage = enum {
|
|
|
|
|
texture,
|
|
|
|
|
render_target,
|
2026-08-12 20:31:05 +01:00
|
|
|
depth,
|
2026-08-12 14:52:27 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub const ImageConfig = struct {
|
|
|
|
|
/// Read a file from disk with `stb_image`
|
|
|
|
|
path: ?[]const u8 = null,
|
|
|
|
|
|
|
|
|
|
/// Raw pixel bytes (overrides `path`)
|
|
|
|
|
data: ?[]const u8 = null,
|
|
|
|
|
|
|
|
|
|
/// Required for `.data`. `null` = use source dimensions
|
|
|
|
|
size: ?[2]u32 = null,
|
|
|
|
|
|
2026-08-12 20:31:05 +01:00
|
|
|
vertical_flip: bool = false,
|
|
|
|
|
|
2026-08-12 14:52:27 +01:00
|
|
|
format: PixelFormat = .rgba8_srgb,
|
|
|
|
|
usage: ImageUsage = .texture,
|
|
|
|
|
mip_levels: u32 = 1,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub const SamplerConfig = struct {
|
|
|
|
|
min_filter: Filter = .linear,
|
|
|
|
|
mag_filter: Filter = .linear,
|
|
|
|
|
mipmap_mode: MipmapMode = .linear,
|
|
|
|
|
address_u: AddressMode = .repeat,
|
|
|
|
|
address_v: AddressMode = .repeat,
|
|
|
|
|
address_w: AddressMode = .repeat,
|
|
|
|
|
max_anisotropy: f32 = 1.0,
|
|
|
|
|
max_lod: f32 = 0,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
/// ----------------------------------------------------
|
|
|
|
|
pub const TextureConfig = struct {
|
|
|
|
|
image: ImageConfig = .{},
|
|
|
|
|
sampler: SamplerConfig = .{},
|
|
|
|
|
};
|
|
|
|
|
|
2026-08-09 14:17:49 +01:00
|
|
|
//
|
|
|
|
|
// HANDLE TYPES
|
|
|
|
|
//
|
|
|
|
|
|
2026-08-11 21:06:28 +01:00
|
|
|
pub const Adapter = struct {};
|
|
|
|
|
pub const Device = struct {};
|
|
|
|
|
|
|
|
|
|
pub const Surface = struct {};
|
|
|
|
|
pub const Swapchain = struct {};
|
|
|
|
|
|
|
|
|
|
pub const Buffer = struct {
|
|
|
|
|
handle: Handle(Buffer),
|
|
|
|
|
device: Handle(Device),
|
|
|
|
|
gfx: *Gfx,
|
|
|
|
|
|
|
|
|
|
pub fn set(self: *const Buffer, value: anytype) !void {
|
|
|
|
|
switch (self.gfx.ctx) {
|
|
|
|
|
.vulkan => |*v| {
|
|
|
|
|
const raw = v.resource.buffers.get(self.handle) orelse return error.BufferNotFound;
|
|
|
|
|
try raw.set(value, @sizeOf(@TypeOf(value)));
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn bind(self: *const Buffer) !void {
|
|
|
|
|
switch (self.gfx.ctx) {
|
2026-08-12 14:52:27 +01:00
|
|
|
.vulkan => |*v| try v.bindBuffer(self.handle),
|
2026-08-11 21:06:28 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
2026-08-09 14:17:49 +01:00
|
|
|
|
|
|
|
|
pub const Image = struct {};
|
|
|
|
|
pub const ImageView = struct {};
|
|
|
|
|
pub const Sampler = struct {};
|
2026-08-12 14:52:27 +01:00
|
|
|
pub const Texture = struct {};
|
2026-08-09 14:17:49 +01:00
|
|
|
|
|
|
|
|
pub const Shader = struct {};
|
|
|
|
|
pub const PipelineLayout = struct {};
|
2026-08-11 21:06:28 +01:00
|
|
|
|
|
|
|
|
pub const Pipeline = struct {
|
|
|
|
|
handle: Handle(Pipeline),
|
|
|
|
|
device: Handle(Device),
|
|
|
|
|
gfx: *Gfx,
|
|
|
|
|
|
|
|
|
|
pub fn bind(self: *const Pipeline) !void {
|
|
|
|
|
try switch (self.gfx.ctx) {
|
|
|
|
|
.vulkan => |*v| v.bindPipeline(self.handle),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn setUniform(self: *const Pipeline, name: []const u8, value: anytype) !void {
|
2026-08-12 02:11:57 +01:00
|
|
|
try switch (self.gfx.ctx) {
|
|
|
|
|
.vulkan => |*v| v.setUniform(self.handle, name, value),
|
|
|
|
|
};
|
2026-08-11 21:06:28 +01:00
|
|
|
}
|
|
|
|
|
};
|