added stuff and improved stuff | added getRaw(resource) that returns tagged union of backend specific raw resources instead of getRaw(backend)(resource) | much cleaner

This commit is contained in:
abux 2026-08-13 00:37:11 +01:00
parent 3186542f0e
commit a5d69292e8
9 changed files with 408 additions and 56 deletions

View file

@ -9,8 +9,11 @@ Low cortisol crossplatform RHI (Rendering Hardware Interface)
## Features
- Fire and forget
- Dead simple + Performance + Flexible
- Handles
- Generational Handles
- Auto managed
- No hidden control flow
- No hidden memory allocations
- No preprocessor, no macros
## Architecture
```text
@ -34,7 +37,7 @@ src/rhi/vulkan/resource.zig
| `deleteX(handle)` | Releases the resource and removes it from the resource pool |
| `releaseX(handle)` | Releases the resource without removing it from the resource pool |
| `getXInfo(handle)` | Returns backend-independent information about the resource |
| `getRawVkX(handle)` | Returns the backend-specific raw Vulkan GPU handle |
| `getRawX(handle)` | Returns backend-specific raw GPU handle |
## Example
### Core
@ -57,42 +60,56 @@ const simple_frag = try gfx.makeShader("examples/assets/shaders/compiled/simple.
### Pipelines
```zig
const pipeline = try gfx.makePipeline(.{
.vertex_attributes = &.{
.{ .location = 0, .format = .vec2, .offset = @offsetOf(Vertex, "pos") },
.{ .location = 1, .format = .vec2, .offset = @offsetOf(Vertex, "normal") },
.{ .location = 2, .format = .vec2, .offset = @offsetOf(Vertex, "uv") },
},
.vertex_binding = .{ .stride = @sizeOf(Vertex) },
.vertex_attributes = &.{
.{ .location = 0, .format = .vec2, .offset = @offsetOf(Vertex, "pos") },
.{ .location = 1, .format = .vec2, .offset = @offsetOf(Vertex, "normal") },
.{ .location = 2, .format = .vec2, .offset = @offsetOf(Vertex, "uv") },
},
.vertex_binding = .{ .stride = @sizeOf(Vertex) },
.uniforms = &.{
.{ .name = "material", .offset = 0, .size = @sizeOf(UnlitMaterial) },
},
.uniforms = &.{
.{ .name = "material", .offset = 0, .size = @sizeOf(UnlitMaterial) },
},
.textures = &.{texture},
.textures = &.{texture},
.blend_mode = .alpha,
.blend_mode = .alpha,
.vert_shader = simple_vert,
.frag_shader = simple_frag,
.vert_shader = simple_vert,
.frag_shader = simple_frag,
.device = device,
.swapchain = swapchain,
.device = device,
.swapchain = swapchain,
});
```
### Render Pass
```zig
if (gfx.beginPass(swapchain, .{
.clear_color = .{ 0.01, 0.01, 0.01, 1.0 },
.clear_color = .{ 0.01, 0.01, 0.01, 1.0 },
})) |pass| {
defer pass.end();
defer pass.end();
try vbuf.bind();
try ibuf.bind();
try vbuf.bind();
try ibuf.bind();
try pipeline.setUniform("material", material);
try pipeline.setUniform("material", material);
try pipeline.bind();
pass.drawIndexed(indices.len, 1, 0, 0, 0);
try pipeline.bind();
pass.drawIndexed(indices.len, 1, 0, 0, 0);
}
```
### House
```zig
switch (try gfx.getRawAdapter(adapter)) {
.vulkan => |v| std.debug.print("---> {}\n", .{v}),
}
switch (try gfx.getRawDevice(device)) {
.vulkan => |v| std.debug.print("---> {}\n", .{v}),
}
switch (try gfx.getRawPipeline(pipeline)) {
.vulkan => |v| std.debug.print("---> {}\n", .{v}),
}
```

View file

@ -11,10 +11,15 @@ pub fn build(b: *std.Build) !void {
.target = target,
.optimize = optimize,
.link_libc = true,
.link_libcpp = true,
});
const mod_tests = b.addTest(.{
.root_module = mod,
.use_lld = true,
.use_llvm = true,
});
const run_mod_tests = b.addRunArtifact(mod_tests);
@ -42,11 +47,13 @@ pub fn build(b: *std.Build) !void {
// SYSTEM DEPENDENCIES
//
const vulkan_headers = b.dependency("vulkan_headers", .{});
const vulkan = b.addTranslateC(.{
.target = target,
.optimize = optimize,
.root_source_file = b.path("./src/vendor/vulkan/vulkan.h"),
.root_source_file = b.path("src/vendor/vulkan/vulkan.h"),
});
vulkan.addIncludePath(vulkan_headers.path("include"));
mod.addImport("vulkan", vulkan.createModule());
mod.linkSystemLibrary("vulkan", .{});
@ -102,11 +109,17 @@ pub fn build(b: *std.Build) !void {
.root_source_file = b.path(ex_src),
.target = target,
.optimize = optimize,
.link_libc = true,
.link_libcpp = true,
});
var example = b.addExecutable(.{
.name = ex_name,
.root_module = exe_mod,
.use_lld = true,
.use_llvm = true,
});
example.root_module.addImport("gfx", mod);

View file

@ -16,6 +16,10 @@
.url = "git+https://nyxformat.com/abux/NYXMath.git#cb90286ae31ac1a5d8c9c29ad0b188a5f36b4321",
.hash = "NYXMath-0.0.0-BD6ST-7WAABYZ7BWgw_wglpgaGdjCRx8ZFX85SdlOhjp",
},
.vulkan_headers = .{
.url = "https://github.com/KhronosGroup/Vulkan-Headers/archive/refs/tags/v1.4.359.tar.gz",
.hash = "N-V-__8AAMgnfgKxQBLuasOmdRqVVIc1HwfkjIUSwaryYgzo",
},
},
.paths = .{

View file

@ -157,6 +157,10 @@ pub fn main(init: std.process.Init) !void {
const device = try gfx.makeDevice(adapter, surface);
const swapchain = try gfx.makeSwapchain(adapter, device, surface);
if (gfx.getAdapterInfo(adapter)) |info| {
std.debug.print("Name: {s} | Type: {s}\n", .{ info.name, @tagName(info.gpu_type) });
}
// --- TEXTURES ---
const texture = try gfx.makeTexture(.{
.image = .{ .path = "examples/assets/textures/texture.jpg", .vertical_flip = true },

View file

@ -36,12 +36,19 @@ pub const Texture = common.Texture;
pub const Shader = common.Shader;
pub const Pipeline = common.Pipeline;
// --- RHI ---
const VulkanRHI = @import("rhi/vulkan/vulkan.zig");
// --- INFOS ---
pub const AdapterInfo = common.AdapterInfo;
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const Backend = enum { vulkan };
// --- RAWS ---
pub const RawAdapter = common.RawAdapter;
pub const RawDevice = common.RawDevice;
pub const RawBuffer = common.RawBuffer;
pub const RawShader = common.RawShader;
pub const RawPipeline = common.RawPipeline;
// --- RHI ---
pub const Backend = common.Backend;
const VulkanRHI = @import("rhi/vulkan/vulkan.zig");
/// ----------------------------------------------------
/// ----------------------------------------------------
@ -70,6 +77,10 @@ config: Config,
alloc: std.mem.Allocator,
io: std.Io,
//
// LIFETIME
//
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn init(
@ -97,6 +108,10 @@ pub fn deinit(self: *Self) void {
}
}
//
// ADAPTER
//
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn makeAdapter(self: *Self, config: AdapterConfig) !Handle(Adapter) {
@ -105,6 +120,42 @@ pub fn makeAdapter(self: *Self, config: AdapterConfig) !Handle(Adapter) {
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn deleteAdapter(self: *Self, adapter: Handle(Adapter)) void {
switch (self.ctx) {
.vulkan => |*v| v.deleteAdapter(adapter),
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn releaseAdapter(self: *Self, adapter: Handle(Adapter)) void {
switch (self.ctx) {
.vulkan => |*v| v.releaseAdapter(adapter),
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn getAdapterInfo(self: *Self, adapter: Handle(Adapter)) ?AdapterInfo {
return switch (self.ctx) {
.vulkan => |*v| v.getAdapterInfo(adapter),
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn getRawAdapter(self: *Self, adapter: Handle(Adapter)) !RawAdapter {
return switch (self.ctx) {
.vulkan => |*v| .{ .vulkan = try v.getRawAdapter(adapter) },
};
}
//
// DEVICE
//
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn makeDevice(
@ -135,13 +186,16 @@ pub fn releaseDevice(self: *Self, device: Handle(Device)) void {
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn getRAWVKDevice(self: *Self, device: Handle(Device)) ?*vk.VkDevice_T {
switch (self.ctx) {
.vulkan => |*v| return v.getRAWDevice(device),
}
return null;
pub fn getRawDevice(self: *Self, device: Handle(Device)) !RawDevice {
return switch (self.ctx) {
.vulkan => |*v| .{ .vulkan = try v.getRawDevice(device) },
};
}
//
// BUFFER
//
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn makeBuffer(
@ -168,13 +222,33 @@ pub fn makeBuffer(
}
/// ----------------------------------------------------
/// TODO:
/// ----------------------------------------------------
pub fn getRawVKBuffer(self: *Self, buffer: Handle(Buffer)) !void {
_ = self;
_ = buffer;
pub fn deleteBuffer(self: *Self, buffer: Handle(Buffer)) void {
switch (self.ctx) {
.vulkan => |*v| v.deleteBuffer(buffer),
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn releaseBuffer(self: *Self, buffer: Handle(Buffer)) void {
switch (self.ctx) {
.vulkan => |*v| v.releaseBuffer(buffer),
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn getRawBuffer(self: *Self, buffer: Handle(Buffer)) !RawBuffer {
return switch (self.ctx) {
.vulkan => |*v| .{ .vulkan = try v.getRawBuffer(buffer) },
};
}
//
// SURFACE
//
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn makeSurface(self: *Self) !Handle(Surface) {
@ -183,6 +257,10 @@ pub fn makeSurface(self: *Self) !Handle(Surface) {
};
}
//
// SWAPCHAIN
//
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn makeSwapchain(
@ -196,6 +274,10 @@ pub fn makeSwapchain(
};
}
//
// SHADER
//
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn makeShader(
@ -208,6 +290,33 @@ pub fn makeShader(
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn deleteShader(self: *Self, shader: Handle(Shader)) void {
switch (self.ctx) {
.vulkan => |*v| v.deleteShader(shader),
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn releaseShader(self: *Self, shader: Handle(Shader)) void {
switch (self.ctx) {
.vulkan => |*v| v.releaseShader(shader),
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn getRawShader(self: *Self, shader: Handle(Shader)) !RawShader {
return switch (self.ctx) {
.vulkan => |*v| .{ .vulkan = try v.getRawShader(shader) },
};
}
//
// PIPELINE
//
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn makePipeline(self: *Self, config: PipelineConfig) !Pipeline {
@ -220,6 +329,34 @@ pub fn makePipeline(self: *Self, config: PipelineConfig) !Pipeline {
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn deletePipeline(self: *Self, pipeline: Handle(Pipeline)) void {
switch (self.ctx) {
.vulkan => |*v| v.deletePipeline(pipeline),
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn releasePipeline(self: *Self, pipeline: Handle(Pipeline)) void {
switch (self.ctx) {
.vulkan => |*v| v.releasePipeline(pipeline),
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn getRawPipeline(self: *Self, pipeline: Handle(Pipeline)) !RawPipeline {
return switch (self.ctx) {
.vulkan => |*v| .{ .vulkan = try v.getRawPipeline(pipeline) },
};
}
//
// TEXTURE
//
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn makeImage(
@ -258,6 +395,10 @@ pub fn makeTexture(
};
}
//
// MISC
//
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn beginFrame(

View file

@ -2,10 +2,55 @@
//! Shared Handles and Configs across every backend
//! ----------------------------------------------------
const vk = @import("vulkan");
const gtl = @import("gtl");
const Gfx = @import("../gfx.zig");
const Handle = gtl.Handle;
// TODO: Organize these into categories... someday
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const Backend = enum { vulkan };
//
// RAW
//
pub const RawAdapter = union(Backend) {
vulkan: *vk.VkPhysicalDevice_T,
};
pub const RawDevice = union(Backend) {
vulkan: *vk.VkDevice_T,
};
pub const RawBuffer = union(Backend) {
vulkan: *vk.VkBuffer_T,
};
pub const RawShader = union(Backend) {
vulkan: *vk.VkShaderModule_T,
};
pub const RawPipeline = union(Backend) {
vulkan: *vk.VkPipeline_T,
};
//
// INFOS
//
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const AdapterInfo = struct {
name: [256]u8,
gpu_type: GPUType,
device_id: u32,
vendor_id: u32,
};
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const Window = union(enum) {

View file

@ -99,18 +99,6 @@ fn isDeviceSuitable(device: vk.VkPhysicalDevice, gpu_type: GPUType) !bool {
var features: vk.VkPhysicalDeviceFeatures = .{};
vk.vkGetPhysicalDeviceFeatures(device, &features);
if (gtl.log.print(.debug, "DEVICE", gtl.ansi.blue)) |v| {
defer v.end();
v.write("Name: {s}", .{properties.deviceName});
v.write("Type: {s}", .{
switch (properties.deviceType) {
vk.VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU => "discrete",
else => "idk",
},
});
}
const matches_class = switch (gpu_type) {
.any => true,
.discrete => properties.deviceType == vk.VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU,

View file

@ -42,6 +42,9 @@ const ImageView = common.ImageView;
const Sampler = common.Sampler;
const Texture = common.Texture;
// --- INFOS ---
const AdapterInfo = common.AdapterInfo;
// --- # ---
pub const MAX_FRAMES_IN_FLIGHT: usize = 3;
@ -84,6 +87,10 @@ sync_device: ?*vk.VkDevice_T = null,
alloc: std.mem.Allocator,
io: std.Io,
//
// LIFETIME
//
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn init(
@ -129,12 +136,24 @@ pub fn deinit(self: *Self) void {
self.instance.deinit();
}
//
// ADAPTER
//
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn makeAdapter(self: *Self, config: AdapterConfig) !Handle(Adapter) {
return try self.resource.adapters.put(try .init(self, config));
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn deleteAdapter(self: *Self, adapter: Handle(Adapter)) void {
if (self.resource.adapters.get(adapter)) |_| {
self.resource.adapters.remove(adapter) catch unreachable;
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn releaseAdapter(self: *Self, adapter: Handle(Adapter)) void {
@ -143,10 +162,36 @@ pub fn releaseAdapter(self: *Self, adapter: Handle(Adapter)) void {
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn deleteAdapter(self: *Self, adapter: Handle(Adapter)) void {
self.resource.adapters.remove(adapter) catch unreachable;
pub fn getAdapterInfo(self: *Self, adapter: Handle(Adapter)) ?AdapterInfo {
const raw_adapter = self.resource.adapters.get(adapter) orelse return null;
var props: vk.VkPhysicalDeviceProperties = .{};
vk.vkGetPhysicalDeviceProperties(raw_adapter.raw, &props);
return AdapterInfo{
.name = props.deviceName,
.gpu_type = switch (props.deviceType) {
vk.VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU => .discrete,
vk.VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU => .integrated,
vk.VK_PHYSICAL_DEVICE_TYPE_CPU => .software,
else => .any,
},
.device_id = props.deviceID,
.vendor_id = props.vendorID,
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn getRawAdapter(self: *Self, adapter: Handle(Adapter)) !*vk.VkPhysicalDevice_T {
const raw_adapter = self.resource.adapters.get(adapter) orelse return error.AdapterNotFound;
return raw_adapter.raw;
}
//
// DEVICE
//
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn makeDevice(
@ -178,17 +223,25 @@ pub fn releaseDevice(self: *Self, device: Handle(Device)) void {
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn getRAWDevice(self: *Self, device: Handle(Device)) ?*vk.VkDevice_T {
if (self.resource.devices.get(device)) |raw| return raw.raw;
return null;
pub fn getRawDevice(self: *Self, device: Handle(Device)) !*vk.VkDevice_T {
if (self.resource.devices.get(device)) |raw| return raw.raw orelse return error.NullDevice;
return error.DeviceNotFound;
}
//
// SURFACE
//
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn makeSurface(self: *Self) !Handle(Surface) {
return try self.resource.surfaces.put(try .init(&self.instance, self.config.window));
}
//
// SWAPCHAIN
//
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn makeSwapchain(
@ -221,6 +274,34 @@ pub fn makeShader(
return try self.resource.shaders.put(try .init(spv_path, raw_device, self.alloc, self.io));
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn deleteShader(self: *Self, shader: Handle(Shader)) void {
if (self.resource.shaders.get(shader)) |raw| {
raw.deinit();
self.resource.shaders.remove(shader) catch unreachable;
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn releaseShader(self: *Self, shader: Handle(Shader)) void {
if (self.resource.shaders.get(shader)) |raw| {
raw.deinit();
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn getRawShader(self: *Self, shader: Handle(Shader)) !*vk.VkShaderModule_T {
if (self.resource.shaders.get(shader)) |raw| return raw.raw orelse return error.NullShader;
return error.ShaderNotFound;
}
//
// PIPELINE
//
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn makePipeline(self: *Self, config: PipelineConfig) !Handle(Pipeline) {
@ -229,6 +310,30 @@ pub fn makePipeline(self: *Self, config: PipelineConfig) !Handle(Pipeline) {
return try self.resource.pipelines.put(try .init(raw_swapchain, raw_device, config, self, self.alloc));
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn deletePipeline(self: *Self, pipeline: Handle(Pipeline)) void {
if (self.resource.pipelines.get(pipeline)) |raw| {
raw.deinit();
self.resource.pipelines.remove(pipeline) catch unreachable;
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn releasePipeline(self: *Self, pipeline: Handle(Pipeline)) void {
if (self.resource.pipelines.get(pipeline)) |raw| {
raw.deinit();
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn getRawPipeline(self: *Self, pipeline: Handle(Pipeline)) !*vk.VkPipeline_T {
if (self.resource.pipelines.get(pipeline)) |raw| return raw.raw orelse return error.NullPipeline;
return error.PipelineNotFound;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn makeImage(
@ -266,6 +371,10 @@ pub fn makeTexture(
return try self.resource.textures.put(try .init(config, raw_adapter, raw_device));
}
//
// BUFFER
//
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn makeBuffer(
@ -281,6 +390,30 @@ pub fn makeBuffer(
return try self.resource.buffers.put(try .init(size, usage, sharing, raw_adapter, raw_device));
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn deleteBuffer(self: *Self, buffer: Handle(Buffer)) void {
if (self.resource.buffers.get(buffer)) |raw| {
raw.deinit();
self.resource.buffers.remove(buffer) catch unreachable;
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn releaseBuffer(self: *Self, buffer: Handle(Buffer)) void {
if (self.resource.buffers.get(buffer)) |raw| {
raw.deinit();
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn getRawBuffer(self: *Self, buffer: Handle(Buffer)) !*vk.VkBuffer_T {
if (self.resource.buffers.get(buffer)) |raw| return raw.raw;
return error.BufferNotFound;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn bindBuffer(self: *Self, buffer: Handle(Buffer)) !void {

View file

@ -1,2 +1,9 @@
#if defined(_WIN32)
#define VK_USE_PLATFORM_WIN32_KHR
#elif defined(__linux__)
#define VK_USE_PLATFORM_XLIB_KHR
#elif defined(__APPLE__)
#define VK_USE_PLATFORM_METAL_EXT
#endif
#include <vulkan/vulkan.h>