HelloWorld

This commit is contained in:
abux 2026-08-11 21:06:28 +01:00
parent 5e5daa6f66
commit 9173c8c9e9
21 changed files with 2094 additions and 83 deletions

12
README.md Normal file
View file

@ -0,0 +1,12 @@
# NYXGFX
Low cortisol crossplatform RHI (Rendering Hardware Interface)
## Backends
[x] Vulkan
[ ] OpenGL
[ ] DX12
## Features
- Fire and forget
- Dead simple + Performance + Flexible
- Handles

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,7 @@
#version 450
layout(location = 0) out vec4 outColor;
void main() {
outColor = vec4(0.2, 0.2, 0.8, 1.0);
}

View file

@ -0,0 +1,9 @@
#version 450
layout(location = 0) in vec2 inPosition;
layout(location = 1) in vec2 inNormal;
layout(location = 2) in vec2 inUV;
void main() {
gl_Position = vec4(inPosition, 0.0, 1.0);
}

View file

@ -32,6 +32,12 @@ pub fn build(b: *std.Build) void {
});
mod.addImport("gtl", gtl.module("gtl"));
const math = b.dependency("NYXMath", .{
.target = target,
.optimize = optimize,
});
mod.addImport("math", math.module("NYXMath"));
//
// SYSTEM DEPENDENCIES
//

View file

@ -9,8 +9,12 @@
.dependencies = .{
.gtl = .{
.url = "git+https://nyxformat.com/abux/GTL.git#cf41b732416562be189eb93d5086e83811f3bb0c",
.hash = "gtl-0.0.0-e3QoJCkUAQDRre0v1BbYuDST1AG67AEIW3LCt4i3LMR9",
.url = "git+https://nyxformat.com/abux/GTL.git#b0679ccd967eb70ea8617a5db9eee2741f2aba9e",
.hash = "gtl-0.0.0-e3QoJB4UAQD3DiNq1CmbJirXkXSTFBeHMoSwzuAKIYAv",
},
.NYXMath = .{
.url = "git+https://nyxformat.com/abux/NYXMath.git#cb90286ae31ac1a5d8c9c29ad0b188a5f36b4321",
.hash = "NYXMath-0.0.0-BD6ST-7WAABYZ7BWgw_wglpgaGdjCRx8ZFX85SdlOhjp",
},
},

3
compile.sh Executable file
View file

@ -0,0 +1,3 @@
clear
glslc assets/shaders/simple.vert -o assets/shaders/compiled/simple.vert.spv
glslc assets/shaders/simple.frag -o assets/shaders/compiled/simple.frag.spv

View file

@ -2,16 +2,32 @@
//! Low cortisol graphics
//! ----------------------------------------------------
// --- # ---
const std = @import("std");
const gtl = @import("gtl");
const vk = @import("vulkan");
const Window = @import("rhi/root.zig").Window;
const VulkanRHI = @import("rhi/vulkan/vulkan.zig");
const common = @import("rhi/root.zig");
const Handle = gtl.Handle;
const Self = @This();
// --- CONFIGS ---
const PipelineConfig = common.PipelineConfig;
const BufferUsage = common.BufferUsage;
// --- HANDLES ---
const Window = @import("rhi/root.zig").Window;
const Adapter = common.Adapter;
const Device = common.Device;
const Buffer = common.Buffer;
const Surface = common.Surface;
const Swapchain = common.Swapchain;
const Shader = common.Shader;
const Pipeline = common.Pipeline;
// --- RHI ---
const VulkanRHI = @import("rhi/vulkan/vulkan.zig");
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const Backend = enum { vulkan };
@ -39,22 +55,26 @@ pub const Config = struct {
ctx: BackendCTX,
config: Config,
alloc: std.mem.Allocator,
io: std.Io,
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn init(
config: Config,
alloc: std.mem.Allocator,
io: std.Io,
) !Self {
const backend_ctx: BackendCTX = switch (config.backend) {
.vulkan => .{ .vulkan = try .init(config, alloc) },
.vulkan => .{ .vulkan = try .init(config, alloc, io) },
};
return .{
.config = config,
.ctx = backend_ctx,
.alloc = alloc,
.io = io,
};
}
@ -66,13 +86,213 @@ pub fn deinit(self: *Self) void {
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
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);
}
},
}
}
//
// TESTS
//
const sdl = @import("sdl");
const math = @import("math");
const Vertex = struct {
pos: [2]f32,
normal: [2]f32,
uv: [2]f32,
};
// const vertices = [_]Vertex{
// .{ .pos = .{ -0.5, -0.5 }, .normal = .{ 0, 0 }, .uv = .{ 0, 0 } },
// .{ .pos = .{ 0.5, -0.5 }, .normal = .{ 0, 0 }, .uv = .{ 1, 0 } },
// .{ .pos = .{ 0.5, 0.5 }, .normal = .{ 0, 0 }, .uv = .{ 1, 1 } },
//
// .{ .pos = .{ -0.5, -0.5 }, .normal = .{ 0, 0 }, .uv = .{ 0, 0 } },
// .{ .pos = .{ 0.5, 0.5 }, .normal = .{ 0, 0 }, .uv = .{ 1, 1 } },
// .{ .pos = .{ -0.5, 0.5 }, .normal = .{ 0, 0 }, .uv = .{ 0, 1 } },
// };
const vertices = [_]Vertex{
.{ .pos = .{ -0.5, -0.5 }, .normal = .{ 0, 0 }, .uv = .{ 0, 0 } },
.{ .pos = .{ 0.5, -0.5 }, .normal = .{ 0, 0 }, .uv = .{ 1, 0 } },
.{ .pos = .{ 0.5, 0.5 }, .normal = .{ 0, 0 }, .uv = .{ 1, 1 } },
.{ .pos = .{ -0.5, 0.5 }, .normal = .{ 0, 0 }, .uv = .{ 0, 1 } },
};
const indices = [_]u32{
0, 1, 2,
0, 2, 3,
};
test "Main" {
//
// SDL
//
// --- # ---
if (!sdl.SDL_Init(sdl.SDL_INIT_VIDEO)) return error.FailedToInitSDL;
defer sdl.SDL_Quit();
@ -86,6 +306,10 @@ test "Main" {
const display = sdl.SDL_GetPointerProperty(props, sdl.SDL_PROP_WINDOW_X11_DISPLAY_POINTER, null);
const xwindow = sdl.SDL_GetNumberProperty(props, sdl.SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0);
//
// GFX
//
// --- # ---
var gfx: Self = try .init(Config{
.window = .{
@ -94,12 +318,39 @@ test "Main" {
.display = display.?,
},
},
}, std.testing.allocator);
}, std.testing.allocator, std.testing.io);
defer gfx.deinit();
// --- # ---
var count: u32 = 0;
_ = vk.vkEnumerateInstanceExtensionProperties(null, &count, null);
const adapter = try gfx.createAdapter();
const surface = try gfx.createSurface();
const device = try gfx.createDevice(adapter, surface);
const swapchain = try gfx.createSwapchain(adapter, device, surface);
const vbuf = try gfx.createBuffer(vertices, .vertex, adapter, device);
const ibuf = try gfx.createBuffer(indices, .index, adapter, device);
const simple_shader = try gfx.createShader(
"assets/shaders/compiled/simple.vert.spv",
"assets/shaders/compiled/simple.frag.spv",
device,
);
const pipeline = try gfx.createPipeline(.{
.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) },
.shader = simple_shader,
.device = device,
.swapchain = swapchain,
});
try pipeline.setUniform("uTime", 0.1);
try pipeline.setUniform("uTime", 0.1);
// --- # ---
if (gtl.log.print(.debug, "WINDOW", gtl.ansi.blue)) |v| {
@ -108,6 +359,37 @@ test "Main" {
v.write("Title: {s}", .{"NYXGFX"});
v.write("Width: {}", .{800});
v.write("Height: {}", .{600});
v.write("Count: {}", .{count});
}
var event: sdl.SDL_Event = undefined;
var running: bool = true;
while (running) {
while (sdl.SDL_PollEvent(&event)) {
switch (event.type) {
sdl.SDL_EVENT_QUIT => running = false,
else => {},
}
}
try gfx.beginFrame(swapchain, device);
defer gfx.endFrame(swapchain, device) catch unreachable;
{
try gfx.beginRendering(swapchain);
defer gfx.endRendering(swapchain) catch unreachable;
try pipeline.bind();
try vbuf.bind();
try ibuf.bind();
try gfx.drawIndexed(indices.len, 1, 0, 0, 0);
}
// if (gfx.beginRendering(...)) |pass| {
// defer pass.end();
//
// try pass.bindPipeline(...);
// try pass.draw(...);
// }
}
}

View file

@ -1,3 +1,11 @@
//! ----------------------------------------------------
//! ----------------------------------------------------
const vk = @import("vulkan");
const gtl = @import("gtl");
const Gfx = @import("../gfx.zig");
const Handle = gtl.Handle;
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const Window = union(enum) {
@ -22,16 +30,153 @@ pub const Window = union(enum) {
},
};
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const AdapterConfig = struct {};
/// ----------------------------------------------------
/// ----------------------------------------------------
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,
};
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const PipelineConfig = struct {
shader: Handle(Shader),
swapchain: Handle(Swapchain),
device: Handle(Device),
vertex_binding: ?VertexBinding = null,
vertex_attributes: []const VertexAttribute = &.{},
topology: enum {
triangle_list,
line_list,
} = .triangle_list,
cull_mode: enum {
front_and_back,
front,
back,
none,
} = .back,
front_face: enum {
ccw,
cw,
} = .cw,
};
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const BufferUsage = enum {
vertex,
index,
};
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const BufferSharingMode = enum {
exclusive,
};
//
// HANDLE TYPES
//
pub const Buffer = struct {};
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) {
.vulkan => |*v| {
const raw = v.resource.buffers.get(self.handle) orelse return error.BufferNotFound;
const frame = v.currentFrame();
if (frame.cmd_buf) |cmd| {
const buffers = [_]vk.VkBuffer{raw.raw};
const offsets = [_]vk.VkDeviceSize{0};
switch (raw.usage) {
.vertex => vk.vkCmdBindVertexBuffers(cmd, 0, 1, &buffers, &offsets),
.index => vk.vkCmdBindIndexBuffer(cmd, raw.raw, 0, vk.VK_INDEX_TYPE_UINT32),
}
}
},
}
}
};
pub const Image = struct {};
pub const ImageView = struct {};
pub const Sampler = struct {};
pub const Shader = struct {};
pub const Pipeline = struct {};
pub const PipelineLayout = struct {};
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 {
_ = self;
_ = name;
_ = value;
}
};

View file

@ -3,6 +3,7 @@
//! ----------------------------------------------------
const std = @import("std");
const gtl = @import("gtl");
const vk = @import("vulkan");
const Resource = @import("resource.zig");
const Window = @import("../root.zig").Window;
@ -14,6 +15,8 @@ const Self = @This();
//
raw: *vk.VkInstance_T,
messenger: ?vk.VkDebugUtilsMessengerEXT = null,
pfn_destroy_debug_utils_messenger: vk.PFN_vkDestroyDebugUtilsMessengerEXT = null,
/// ----------------------------------------------------
/// ----------------------------------------------------
@ -21,49 +24,79 @@ pub fn init(
config: MainConfig,
alloc: std.mem.Allocator,
) !Self {
const validation_layers: []const []const u8 = &.{
const validation_layers: []const [*:0]const u8 = &.{
"VK_LAYER_KHRONOS_validation",
};
const extensions = getWindowExtensions(
config.window,
config.enable_validation,
);
var app_info: vk.VkApplicationInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pApplicationName = config.app_name.ptr,
.pEngineName = config.engine_name.ptr,
.apiVersion = vk.VK_API_VERSION_1_4,
.apiVersion = vk.VK_API_VERSION_1_3,
};
var messenger_info: vk.VkDebugUtilsMessengerCreateInfoEXT = undefined;
var create_info: vk.VkInstanceCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pApplicationInfo = &app_info,
.enabledExtensionCount = @intCast(extensions.len),
.ppEnabledExtensionNames = extensions.ptr,
.flags = vk.VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR,
};
if (config.enable_validation and !try checkValidationLayerSupport(validation_layers, alloc)) {
if (config.enable_validation) {
if (!try checkValidationLayerSupport(validation_layers, alloc)) {
return error.ValidationLayerNotAvailable;
} else {
}
create_info.enabledLayerCount = @intCast(validation_layers.len);
create_info.ppEnabledLayerNames = @ptrCast(validation_layers.ptr);
create_info.ppEnabledLayerNames = validation_layers.ptr;
messenger_info = makeMessengerInfo();
create_info.pNext = &messenger_info;
}
var instance: vk.VkInstance = null;
if (vk.vkCreateInstance(&create_info, null, &instance) != vk.VK_SUCCESS) {
return error.FailedToCreateVulkanInstance;
}
errdefer vk.vkDestroyInstance(instance.?, null);
var messenger: vk.VkDebugUtilsMessengerEXT = null;
var pfn_destroy: vk.PFN_vkDestroyDebugUtilsMessengerEXT = null;
if (config.enable_validation) {
const pfn_create: vk.PFN_vkCreateDebugUtilsMessengerEXT = @ptrCast(
vk.vkGetInstanceProcAddr(instance.?, "vkCreateDebugUtilsMessengerEXT"),
);
pfn_destroy = @ptrCast(
vk.vkGetInstanceProcAddr(instance.?, "vkDestroyDebugUtilsMessengerEXT"),
);
if (pfn_create.?(instance.?, &messenger_info, null, &messenger) != vk.VK_SUCCESS) {
return error.FailedToCreateDebugUtilsMessenger;
}
}
return .{
.raw = instance.?,
.messenger = messenger,
.pfn_destroy_debug_utils_messenger = pfn_destroy,
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn deinit(self: *Self) void {
if (self.messenger) |messenger| {
if (self.pfn_destroy_debug_utils_messenger) |pfn_destroy| {
pfn_destroy(self.raw, messenger, null);
}
}
vk.vkDestroyInstance(self.raw, null);
}
@ -72,27 +105,84 @@ pub fn deinit(self: *Self) void {
/// ----------------------------------------------------
fn getWindowExtensions(
window: Window,
enable_validation: bool,
) []const [*:0]const u8 {
return switch (window) {
.xlib => &.{
"VK_KHR_surface",
"VK_KHR_xlib_surface",
},
.xlib => if (enable_validation)
&.{ "VK_KHR_surface", "VK_KHR_xlib_surface", "VK_EXT_debug_utils" }
else
&.{ "VK_KHR_surface", "VK_KHR_xlib_surface" },
.wayland => &.{
"VK_KHR_surface",
"VK_KHR_wayland_surface",
},
.wayland => if (enable_validation)
&.{ "VK_KHR_surface", "VK_KHR_wayland_surface", "VK_EXT_debug_utils" }
else
&.{ "VK_KHR_surface", "VK_KHR_wayland_surface" },
else => @panic("Not implemented yet..."),
};
}
/// ----------------------------------------------------
/// Helper
/// ----------------------------------------------------
fn makeMessengerInfo() vk.VkDebugUtilsMessengerCreateInfoEXT {
return .{
.sType = vk.VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
.messageSeverity = @intCast(
vk.VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
vk.VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
),
.messageType = @intCast(
vk.VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
vk.VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
vk.VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
),
.pfnUserCallback = debugCallback,
};
}
/// ----------------------------------------------------
/// Helper
/// ----------------------------------------------------
fn debugCallback(
message_severity: vk.VkDebugUtilsMessageSeverityFlagBitsEXT,
message_types: vk.VkDebugUtilsMessageTypeFlagsEXT,
callback_data: ?*const vk.VkDebugUtilsMessengerCallbackDataEXT,
user_data: ?*anyopaque,
) callconv(.c) vk.VkBool32 {
_ = user_data;
const severity: []const u8 = switch (message_severity) {
vk.VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT => gtl.ansi.yellow ++ "[WARN]" ++ gtl.ansi.reset,
vk.VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT => gtl.ansi.red ++ "[ERROR]" ++ gtl.ansi.reset,
vk.VK_DEBUG_UTILS_MESSAGE_SEVERITY_FLAG_BITS_MAX_ENUM_EXT => gtl.ansi.blue ++ "[FLAG_BITS_MAX_ENUM]" ++ gtl.ansi.reset,
vk.VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT => gtl.ansi.blue ++ "[INFO_BIT]" ++ gtl.ansi.reset,
vk.VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT => gtl.ansi.red ++ "[VERBOSE_BIT]" ++ gtl.ansi.reset,
else => "IDK",
};
const message_type = switch (message_types) {
vk.VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT => gtl.ansi.yellow ++ "[VALIDATION]" ++ gtl.ansi.reset,
vk.VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT => gtl.ansi.purple ++ "[GENERAL]" ++ gtl.ansi.reset,
vk.VK_DEBUG_UTILS_MESSAGE_TYPE_DEVICE_ADDRESS_BINDING_BIT_EXT => gtl.ansi.purple ++ "[ADDRESS_BINDING_BIT]" ++ gtl.ansi.reset,
vk.VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT => gtl.ansi.red ++ "[PERFORMANCE_BIT]" ++ gtl.ansi.reset,
else => "IDK",
};
if (callback_data) |data| {
if (data.pMessage) |message| {
std.debug.print("{s}{s} {s}\n", .{ severity, message_type, message });
}
}
return vk.VK_FALSE;
}
/// ----------------------------------------------------
/// Helper
/// ----------------------------------------------------
fn checkValidationLayerSupport(
layers: []const []const u8,
layers: []const [*:0]const u8,
alloc: std.mem.Allocator,
) !bool {
// --- GET LAYER COUNT ---
@ -116,7 +206,7 @@ fn checkValidationLayerSupport(
for (available_layers) |*prop| {
const available_name: []const u8 = std.mem.sliceTo(&prop.layerName, 0);
if (std.mem.eql(u8, name, available_name)) {
if (std.mem.eql(u8, std.mem.span(name), available_name)) {
found = true;
break;
}

View file

@ -1,11 +1,28 @@
//! ----------------------------------------------------
//! ----------------------------------------------------
const vk = @import("vulkan");
const std = @import("std");
const gtl = @import("gtl");
const common = @import("../root.zig");
const Instance = @import("instance.zig");
const Self = @This();
const Adapter = common.Adapter;
const VKAdapter = @import("resource/adapter.zig");
const Device = common.Device;
const VKDevice = @import("resource/device.zig");
const Buffer = common.Buffer;
const VKBuffer = @import("resource/buffer.zig");
const Surface = common.Surface;
const VKSurface = @import("resource/surface.zig");
const Swapchain = common.Swapchain;
const VKSwapchain = @import("resource/swapchain.zig");
const Shader = common.Shader;
const VKShader = @import("resource/shader.zig");
@ -17,6 +34,13 @@ const VKPipeline = @import("resource/pipeline.zig");
// FIELDS
//
adapters: gtl.ResourceMap(Adapter, VKAdapter),
devices: gtl.ResourceMap(Device, VKDevice),
buffers: gtl.ResourceMap(Buffer, VKBuffer),
surfaces: gtl.ResourceMap(Surface, VKSurface),
swapchains: gtl.ResourceMap(Swapchain, VKSwapchain),
shaders: gtl.ResourceMap(Shader, VKShader),
pipelines: gtl.ResourceMap(Pipeline, VKPipeline),
@ -24,6 +48,13 @@ pipelines: gtl.ResourceMap(Pipeline, VKPipeline),
/// ----------------------------------------------------
pub fn init(alloc: std.mem.Allocator) Self {
return .{
.adapters = .init(alloc),
.devices = .init(alloc),
.buffers = .init(alloc),
.surfaces = .init(alloc),
.swapchains = .init(alloc),
.shaders = .init(alloc),
.pipelines = .init(alloc),
};
@ -31,12 +62,59 @@ pub fn init(alloc: std.mem.Allocator) Self {
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn deinit(self: *Self) void {
pub fn deinit(
self: *Self,
instance: *Instance,
) void {
{ // FREE SWAPCHAINS
var it = self.swapchains.valueIterator();
while (it.next()) |v| {
v.deinit();
}
self.swapchains.deinit();
}
{ // FREE SURFACES
var it = self.surfaces.valueIterator();
while (it.next()) |v| {
v.deinit(instance);
}
self.surfaces.deinit();
}
{ // FREE SHADERS
var it = self.shaders.valueIterator();
while (it.next()) |v| {
v.deinit();
}
self.shaders.deinit();
}
{ // FREE PIPELINES
var it = self.pipelines.valueIterator();
while (it.next()) |v| {
v.deinit();
}
self.pipelines.deinit();
}
{ // FREE BUFFERS
var it = self.buffers.valueIterator();
while (it.next()) |v| {
v.deinit();
}
self.buffers.deinit();
}
{ // FREE ADAPTERS
self.adapters.deinit();
}
{ // FREE DEVICES
var it = self.devices.valueIterator();
while (it.next()) |v| {
v.deinit();
}
self.devices.deinit();
}
}

View file

@ -1,43 +1,43 @@
//! ----------------------------------------------------
//! `🗲` Vulkan Device `🗲`
//! `🗲` Vulkan Adapter `🗲`
//! ----------------------------------------------------
const gtl = @import("gtl");
const std = @import("std");
const gtl = @import("gtl");
const vk = @import("vulkan");
const Instance = @import("instance.zig");
const VKBackend = @import("../vulkan.zig");
const Self = @This();
//
// FIELDS
//
raw: *vk.VkDevice_T,
raw: *vk.VkPhysicalDevice_T,
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn init(
instance: *Instance,
alloc: std.mem.Allocator,
) !Self {
pub fn init(backend: *const VKBackend) !Self {
var physical: vk.VkPhysicalDevice = null;
var count: u32 = 0;
if (vk.vkEnumeratePhysicalDevices(instance.raw, &count, null) != vk.VK_SUCCESS) {
// --- GET PHYSICAL DEVICES COUNT ---
if (vk.vkEnumeratePhysicalDevices(backend.instance.raw, &count, null) != vk.VK_SUCCESS) {
return error.FailedToEnumeratePhysicalDevices;
}
if (count == 0) {
return error.FailedToFindGPUWithVKSupport;
}
const devices = try alloc.alloc(vk.VkPhysicalDevice, count);
defer alloc.free(devices);
// --- # ---
const devices = try backend.alloc.alloc(vk.VkPhysicalDevice, count);
defer backend.alloc.free(devices);
if (vk.vkEnumeratePhysicalDevices(instance.raw, &count, devices.ptr) != vk.VK_SUCCESS) {
// --- GET PHYSICAL DEVICES ---
if (vk.vkEnumeratePhysicalDevices(backend.instance.raw, &count, devices.ptr) != vk.VK_SUCCESS) {
return error.FailedToEnumeratePhysicalDevices;
}
// --- CHECK WHICH DEVICE IS SUITABLE ---
for (devices) |device| {
if (try isDeviceSuitable(device)) {
physical = device;
@ -45,32 +45,40 @@ pub fn init(
}
}
var features: vk.VkPhysicalDeviceFeatures = .{};
var queue_create_info: vk.VkDeviceQueueCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
};
var create_info: vk.VkDeviceCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pQueueCreateInfos = &queue_create_info,
.queueCreateInfoCount = 1,
.pEnabledFeatures = &features,
};
var device: vk.VkDevice = null;
if (vk.vkCreateDevice(physical, &create_info, null, &device) != vk.VK_SUCCESS) {
return error.FailedToCreateVulkanDevice;
// --- FIND GFX QUEUE FAMILY ---
var queue_family_index: u32 = 0;
if (!try findGraphicsQueueFamily(physical, backend.alloc, &queue_family_index)) {
return error.FailedToFindGraphicsQueueFamily;
}
return .{
.raw = device.?,
.raw = physical.?,
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn deinit(self: *Self) void {
vk.vkDestroyDevice(self.raw, null);
fn findGraphicsQueueFamily(
device: vk.VkPhysicalDevice,
alloc: std.mem.Allocator,
out_index: *u32,
) !bool {
var count: u32 = 0;
vk.vkGetPhysicalDeviceQueueFamilyProperties(device, &count, null);
const families = try alloc.alloc(vk.VkQueueFamilyProperties, count);
defer alloc.free(families);
vk.vkGetPhysicalDeviceQueueFamilyProperties(device, &count, families.ptr);
for (families, 0..) |family, i| {
if (family.queueFlags & @as(vk.VkQueueFlags, @intCast(vk.VK_QUEUE_GRAPHICS_BIT)) != 0) {
out_index.* = @intCast(i);
return true;
}
}
return false;
}
/// ----------------------------------------------------

View file

@ -0,0 +1,131 @@
//! ----------------------------------------------------
//! `🗲` Vulkan Buffer `🗲`
//! ----------------------------------------------------
const std = @import("std");
const vk = @import("vulkan");
const Self = @This();
const common = @import("../../root.zig");
const Adapter = @import("adapter.zig");
const Device = @import("device.zig");
const BufferUsage = common.BufferUsage;
const BufferSharingMode = common.BufferSharingMode;
//
// FIELDS
//
raw: *vk.VkBuffer_T,
data: ?*vk.VkDeviceMemory_T,
usage: BufferUsage,
// --- TEMP ---
adapter: *const Adapter,
device: *const Device,
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn init(
size: u32,
usage: BufferUsage,
sharing: BufferSharingMode,
adapter: *const Adapter,
device: *const Device,
) !Self {
var info: vk.VkBufferCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.usage = switch (usage) {
.vertex => vk.VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
.index => vk.VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
},
.sharingMode = switch (sharing) {
.exclusive => vk.VK_SHARING_MODE_EXCLUSIVE,
},
.size = size,
};
var raw: vk.VkBuffer = null;
if (vk.vkCreateBuffer(device.raw, &info, null, &raw) != vk.VK_SUCCESS) {
return error.FailedToCreateBuffer;
}
return .{
.raw = raw.?,
.data = null,
.usage = usage,
.adapter = adapter,
.device = device,
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn deinit(self: *Self) void {
if (self.data) |data| {
vk.vkFreeMemory(self.device.raw, data, null);
}
vk.vkDestroyBuffer(self.device.raw, self.raw, null);
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn set(
self: *Self,
value: anytype,
size: u32,
) !void {
if (self.data == null) {
var mem_req: vk.VkMemoryRequirements = .{};
var mem_props: vk.VkPhysicalDeviceMemoryProperties = .{};
var mem_type: u32 = 0;
vk.vkGetBufferMemoryRequirements(self.device.raw, self.raw, &mem_req);
vk.vkGetPhysicalDeviceMemoryProperties(self.adapter.raw, &mem_props);
for (0..mem_props.memoryTypeCount) |i| {
if ((mem_req.memoryTypeBits & (@as(u32, 1) << @intCast(i))) != 0 and
(mem_props.memoryTypes[i].propertyFlags &
(vk.VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
vk.VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) != 0)
{
mem_type = @intCast(i);
break;
}
}
var alloc_info: vk.VkMemoryAllocateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = mem_req.size,
.memoryTypeIndex = mem_type,
};
if (vk.vkAllocateMemory(self.device.raw, &alloc_info, null, &self.data) != vk.VK_SUCCESS) {
return error.FailedToAllocateMemory;
}
_ = vk.vkBindBufferMemory(self.device.raw, self.raw, self.data, 0);
}
var mapped: ?*anyopaque = null;
const result = vk.vkMapMemory(
self.device.raw,
self.data,
0,
size,
0,
&mapped,
);
if (result != vk.VK_SUCCESS) {
return error.FailedToMapMemory;
}
const dst: [*]u8 = @ptrCast(mapped.?);
const src: [*]const u8 = @ptrCast(&value);
@memcpy(dst[0..size], src[0..size]);
vk.vkUnmapMemory(self.device.raw, self.data);
}

View file

@ -0,0 +1,167 @@
//! ----------------------------------------------------
//! `🗲` Vulkan Device `🗲`
//! ----------------------------------------------------
const std = @import("std");
const vk = @import("vulkan");
const Adapter = @import("adapter.zig");
const Surface = @import("surface.zig");
const Self = @This();
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const QueueFamilies = struct {
graphics: ?u32 = null,
present: ?u32 = null,
};
//
// FIELDS
//
raw: *vk.VkDevice_T,
graphics_queue: *vk.VkQueue_T,
present_queue: *vk.VkQueue_T,
graphics_command_pool: *vk.VkCommandPool_T,
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn init(
adapter: *Adapter,
surface: *Surface,
alloc: std.mem.Allocator,
) !Self {
// --- # ---
const extensions: []const [*:0]const u8 = &.{
"VK_KHR_swapchain",
};
// --- # ---
var features13: vk.VkPhysicalDeviceVulkan13Features = .{
.sType = vk.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES,
.dynamicRendering = vk.VK_TRUE,
};
// --- FIND QUEUE FAMILIES ---
const queue_families = try findQueueFamilies(adapter.raw, surface.raw, alloc);
if (queue_families.graphics == null or queue_families.present == null) {
return error.FailedToFindSuitableQueueFamily;
}
// --- CREATE QUEUE INFOS ---
const queue_priority: f32 = 1.0;
var queue_create_infos: [2]vk.VkDeviceQueueCreateInfo = undefined;
var queue_create_info_count: u32 = 0;
queue_create_infos[0] = .{
.sType = vk.VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.queueFamilyIndex = queue_families.graphics.?,
.queueCount = 1,
.pQueuePriorities = &queue_priority,
};
queue_create_info_count += 1;
if (queue_families.present.? != queue_families.graphics.?) {
queue_create_infos[1] = .{
.sType = vk.VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.queueFamilyIndex = queue_families.present.?,
.queueCount = 1,
.pQueuePriorities = &queue_priority,
};
queue_create_info_count += 1;
}
// --- DEVICE CREATE INFO ---
var features: vk.VkPhysicalDeviceFeatures = .{};
var create_info: vk.VkDeviceCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pQueueCreateInfos = &queue_create_infos,
.queueCreateInfoCount = queue_create_info_count,
.pEnabledFeatures = &features,
.enabledExtensionCount = extensions.len,
.ppEnabledExtensionNames = extensions.ptr,
.pNext = &features13,
};
// --- # ---
var device: vk.VkDevice = null;
if (vk.vkCreateDevice(adapter.raw, &create_info, null, &device) != vk.VK_SUCCESS) {
return error.FailedToCreateVulkanDevice;
}
// --- # ---
var pool_info: vk.VkCommandPoolCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.flags = vk.VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
.queueFamilyIndex = queue_families.graphics.?,
};
var graphics_command_pool: vk.VkCommandPool = null;
if (vk.vkCreateCommandPool(device, &pool_info, null, &graphics_command_pool) != vk.VK_SUCCESS) {
return error.FailedToCreateCommandPool;
}
// --- GET QUEUES ---
var graphics_queue: vk.VkQueue = null;
vk.vkGetDeviceQueue(device, queue_families.graphics.?, 0, &graphics_queue);
var present_queue: vk.VkQueue = null;
vk.vkGetDeviceQueue(device, queue_families.present.?, 0, &present_queue);
return .{
.raw = device.?,
.graphics_queue = graphics_queue.?,
.present_queue = present_queue.?,
.graphics_command_pool = graphics_command_pool.?,
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn deinit(self: *Self) void {
vk.vkDestroyCommandPool(self.raw, self.graphics_command_pool, null);
vk.vkDestroyDevice(self.raw, null);
}
/// ----------------------------------------------------
/// ----------------------------------------------------
fn findQueueFamilies(
adapter: vk.VkPhysicalDevice,
surface: *vk.VkSurfaceKHR_T,
alloc: std.mem.Allocator,
) !QueueFamilies {
var indices: QueueFamilies = .{};
// --- GET FAMILY COUNT ---
var queue_family_count: u32 = 0;
vk.vkGetPhysicalDeviceQueueFamilyProperties(adapter, &queue_family_count, null);
// --- CREATE BUFFER ---
const queue_families = try alloc.alloc(vk.VkQueueFamilyProperties, queue_family_count);
defer alloc.free(queue_families);
vk.vkGetPhysicalDeviceQueueFamilyProperties(adapter, &queue_family_count, queue_families.ptr);
// --- FIND SUPPORTED FAMILIES ---
for (queue_families, 0..) |family, i| {
var supported: vk.VkBool32 = vk.VK_FALSE;
if (family.queueFlags & @as(vk.VkQueueFlags, @intCast(vk.VK_QUEUE_GRAPHICS_BIT)) != 0) {
indices.graphics = @intCast(i);
}
_ = vk.vkGetPhysicalDeviceSurfaceSupportKHR(
adapter,
@intCast(i),
surface,
&supported,
);
if (supported == vk.VK_TRUE) {
indices.present = @intCast(i);
}
if (indices.graphics != null and indices.present != null) {
break;
}
}
return indices;
}

View file

@ -0,0 +1,247 @@
//! ----------------------------------------------------
//! `🗲` Vulkan Pipeline `🗲`
//! ----------------------------------------------------
const std = @import("std");
const vk = @import("vulkan");
const gtl = @import("gtl");
const Self = @This();
const Device = @import("device.zig");
const Shader = @import("shader.zig");
const Swapchain = @import("swapchain.zig");
const Config = @import("../../root.zig").PipelineConfig;
//
// FIELDS
//
raw: *vk.VkPipeline_T,
layout: *vk.VkPipelineLayout_T,
device: *const Device,
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn init(
shader: *const Shader,
swapchain: *const Swapchain,
device: *const Device,
config: Config,
alloc: std.mem.Allocator,
) !Self {
//
// LAYOUT
//
var pipeline_layout_info: vk.VkPipelineLayoutCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
};
var pipeline_layout: vk.VkPipelineLayout = null;
if (vk.vkCreatePipelineLayout(device.raw, &pipeline_layout_info, null, &pipeline_layout) != vk.VK_SUCCESS) {
return error.FailedToCreatePipelineLayout;
}
errdefer vk.vkDestroyPipelineLayout(device.raw, pipeline_layout, null);
//
// PIPELINE
//
const vert_shader_stage_info: vk.VkPipelineShaderStageCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = vk.VK_SHADER_STAGE_VERTEX_BIT,
.pName = "main",
.module = shader.vert,
};
const frag_shader_stage_info: vk.VkPipelineShaderStageCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = vk.VK_SHADER_STAGE_FRAGMENT_BIT,
.pName = "main",
.module = shader.frag,
};
var shader_stages = [_]vk.VkPipelineShaderStageCreateInfo{
vert_shader_stage_info,
frag_shader_stage_info,
};
var dynamic_states = [_]vk.VkDynamicState{
vk.VK_DYNAMIC_STATE_VIEWPORT,
vk.VK_DYNAMIC_STATE_SCISSOR,
};
var dynamic_state: vk.VkPipelineDynamicStateCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.pDynamicStates = &dynamic_states,
.dynamicStateCount = dynamic_states.len,
};
var vertex_binding: vk.VkVertexInputBindingDescription = undefined;
var vertex_binding_present = false;
if (config.vertex_binding) |binding| {
vertex_binding = .{
.binding = binding.binding,
.stride = binding.stride,
.inputRate = switch (binding.input_rate) {
.vertex => vk.VK_VERTEX_INPUT_RATE_VERTEX,
.instance => vk.VK_VERTEX_INPUT_RATE_INSTANCE,
},
};
vertex_binding_present = true;
}
const vertex_attributes = try alloc.alloc(vk.VkVertexInputAttributeDescription, config.vertex_attributes.len);
defer alloc.free(vertex_attributes);
for (config.vertex_attributes, 0..) |attr, i| {
vertex_attributes[i] = .{
.location = attr.location,
.binding = attr.binding,
.format = switch (attr.format) {
.int => vk.VK_FORMAT_R32_SINT,
.float => vk.VK_FORMAT_R32_SFLOAT,
.vec2 => vk.VK_FORMAT_R32G32_SFLOAT,
.vec3 => vk.VK_FORMAT_R32G32B32_SFLOAT,
.vec4 => vk.VK_FORMAT_R32G32B32A32_SFLOAT,
},
.offset = attr.offset,
};
}
var vert_input_info: vk.VkPipelineVertexInputStateCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.vertexBindingDescriptionCount = if (vertex_binding_present) 1 else 0,
.pVertexBindingDescriptions = if (vertex_binding_present) &vertex_binding else null,
.vertexAttributeDescriptionCount = @intCast(vertex_attributes.len),
.pVertexAttributeDescriptions = vertex_attributes.ptr,
};
var input_assembly: vk.VkPipelineInputAssemblyStateCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = switch (config.topology) {
.triangle_list => vk.VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
.line_list => vk.VK_PRIMITIVE_TOPOLOGY_LINE_LIST,
},
.primitiveRestartEnable = vk.VK_FALSE,
};
var viewport: vk.VkViewport = .{
.x = 0.0,
.y = 0.0,
.width = @floatFromInt(swapchain.extent.width),
.height = @floatFromInt(swapchain.extent.height),
.minDepth = 0,
.maxDepth = 1,
};
var scissor: vk.VkRect2D = .{
.offset = .{ .x = 0, .y = 0 },
.extent = swapchain.extent,
};
var viewport_state: vk.VkPipelineViewportStateCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pViewports = &viewport,
.viewportCount = 1,
.pScissors = &scissor,
.scissorCount = 1,
};
var rasterizer: vk.VkPipelineRasterizationStateCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.depthClampEnable = vk.VK_FALSE,
.rasterizerDiscardEnable = vk.VK_FALSE,
.polygonMode = vk.VK_POLYGON_MODE_FILL,
.cullMode = switch (config.cull_mode) {
.front_and_back => vk.VK_CULL_MODE_FRONT_AND_BACK,
.front => vk.VK_CULL_MODE_FRONT_BIT,
.back => vk.VK_CULL_MODE_BACK_BIT,
.none => vk.VK_CULL_MODE_NONE,
},
.frontFace = switch (config.front_face) {
.cw => vk.VK_FRONT_FACE_CLOCKWISE,
.ccw => vk.VK_FRONT_FACE_COUNTER_CLOCKWISE,
},
.depthBiasEnable = vk.VK_FALSE,
.lineWidth = 1.0,
};
var multisampling: vk.VkPipelineMultisampleStateCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.rasterizationSamples = vk.VK_SAMPLE_COUNT_1_BIT,
.minSampleShading = 1.0,
};
var color_blend_attachment: vk.VkPipelineColorBlendAttachmentState = .{
.colorWriteMask = vk.VK_COLOR_COMPONENT_R_BIT | vk.VK_COLOR_COMPONENT_G_BIT | vk.VK_COLOR_COMPONENT_B_BIT | vk.VK_COLOR_COMPONENT_A_BIT,
.srcColorBlendFactor = vk.VK_BLEND_FACTOR_ONE,
.dstColorBlendFactor = vk.VK_BLEND_FACTOR_ZERO,
.srcAlphaBlendFactor = vk.VK_BLEND_FACTOR_ONE,
.dstAlphaBlendFactor = vk.VK_BLEND_FACTOR_ZERO,
.colorBlendOp = vk.VK_BLEND_OP_ADD,
.alphaBlendOp = vk.VK_BLEND_OP_ADD,
};
var color_blending: vk.VkPipelineColorBlendStateCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.logicOpEnable = vk.VK_FALSE,
.attachmentCount = 1,
.pAttachments = &color_blend_attachment,
};
var rendering_info: vk.VkPipelineRenderingCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO,
.colorAttachmentCount = 1,
.pColorAttachmentFormats = &swapchain.format,
};
var pipeline_info: vk.VkGraphicsPipelineCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.stageCount = shader_stages.len,
.pStages = &shader_stages,
.pVertexInputState = &vert_input_info,
.pInputAssemblyState = &input_assembly,
.pViewportState = &viewport_state,
.pRasterizationState = &rasterizer,
.pMultisampleState = &multisampling,
.pColorBlendState = &color_blending,
.pDynamicState = &dynamic_state,
.layout = pipeline_layout,
.renderPass = null,
.subpass = 0,
.pNext = &rendering_info,
};
var pipeline: vk.VkPipeline = null;
if (vk.vkCreateGraphicsPipelines(device.raw, null, 1, &pipeline_info, null, &pipeline) != vk.VK_SUCCESS) {
return error.FailedToCreateGraphicsPipelines;
}
return .{
.raw = pipeline.?,
.layout = pipeline_layout.?,
.device = device,
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn deinit(self: *Self) void {
vk.vkDestroyPipelineLayout(self.device.raw, self.layout, null);
vk.vkDestroyPipeline(self.device.raw, self.raw, null);
}

View file

@ -0,0 +1,84 @@
//! ----------------------------------------------------
//! `🗲` Vulkan Shader `🗲`
//! ----------------------------------------------------
const std = @import("std");
const vk = @import("vulkan");
const gtl = @import("gtl");
const Device = @import("device.zig");
const Self = @This();
//
// FIELDS
//
vert: *vk.VkShaderModule_T,
frag: *vk.VkShaderModule_T,
device: *const Device,
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn init(
vert_spv_path: []const u8,
frag_spv_path: []const u8,
device: *const Device,
alloc: std.mem.Allocator,
io: std.Io,
) !Self {
const vert_code = try readFile(vert_spv_path, alloc, io);
defer alloc.free(vert_code);
const frag_code = try readFile(frag_spv_path, alloc, io);
defer alloc.free(frag_code);
const vert_module = try createModule(vert_code, device);
const frag_module = try createModule(frag_code, device);
return .{
.vert = vert_module,
.frag = frag_module,
.device = device,
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn deinit(self: *Self) void {
vk.vkDestroyShaderModule(self.device.raw, self.vert, null);
vk.vkDestroyShaderModule(self.device.raw, self.frag, null);
}
/// ----------------------------------------------------
/// Helper
/// ----------------------------------------------------
fn createModule(
code: []u8,
device: *const Device,
) !*vk.VkShaderModule_T {
var create_info: vk.VkShaderModuleCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.pCode = @ptrCast(@alignCast(code.ptr)),
.codeSize = code.len,
};
var module: vk.VkShaderModule = null;
if (vk.vkCreateShaderModule(device.raw, &create_info, null, &module) != vk.VK_SUCCESS) {
return error.FailedToCreateShaderModule;
}
return module.?;
}
/// ----------------------------------------------------
/// Helper
/// ----------------------------------------------------
fn readFile(
path: []const u8,
alloc: std.mem.Allocator,
io: std.Io,
) ![]u8 {
return std.Io.Dir.cwd().readFileAlloc(io, path, alloc, .unlimited) catch |err| {
gtl.log.err("{s}: {s}\n", .{ @errorName(err), path }, null);
return err;
};
}

View file

@ -0,0 +1,53 @@
//! ----------------------------------------------------
//! `🗲` Vulkan Surface `🗲`
//! ----------------------------------------------------
const std = @import("std");
const vk = @import("vulkan");
const Instance = @import("../instance.zig");
const Window = @import("../../root.zig").Window;
const Self = @This();
//
// FIELDS
//
raw: *vk.VkSurfaceKHR_T,
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn init(
instance: *Instance,
window: Window,
) !Self {
const surface: *vk.VkSurfaceKHR_T = blk: {
var raw: vk.VkSurfaceKHR = null;
switch (window) {
.xlib => |v| {
var info: vk.VkXlibSurfaceCreateInfoKHR = .{
.sType = vk.VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR,
.window = v.window,
.dpy = @ptrCast(v.display),
};
if (vk.vkCreateXlibSurfaceKHR(instance.raw, &info, null, &raw) != vk.VK_SUCCESS) {
return error.FailedToCreateXlibSurfaceKHR;
}
break :blk raw.?;
},
else => return error.NotImplementedYet,
}
return error.FailedToCreateSurface;
};
return .{
.raw = surface,
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn deinit(self: *Self, instance: *Instance) void {
vk.vkDestroySurfaceKHR(instance.raw, self.raw, null);
}

View file

@ -0,0 +1,169 @@
//! ----------------------------------------------------
//! `🗲` Vulkan Swapchain `🗲`
//! ----------------------------------------------------
const std = @import("std");
const gtl = @import("gtl");
const vk = @import("vulkan");
const Self = @This();
const Adapter = @import("adapter.zig");
const Device = @import("device.zig");
const Surface = @import("surface.zig");
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const Config = struct {
width: u32,
height: u32,
};
//
// FIELDS
//
raw: *vk.VkSwapchainKHR_T,
images: []vk.VkImage,
image_views: []vk.VkImageView,
format: vk.VkFormat,
extent: vk.VkExtent2D,
device: *const Device,
alloc: std.mem.Allocator,
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn init(
adapter: *const Adapter,
device: *const Device,
surface: *const Surface,
config: Config,
alloc: std.mem.Allocator,
) !Self {
// --- # ---
var capabilities: vk.VkSurfaceCapabilitiesKHR = .{};
_ = vk.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(adapter.raw, surface.raw, &capabilities);
// --- # ---
var format_count: u32 = 0;
_ = vk.vkGetPhysicalDeviceSurfaceFormatsKHR(adapter.raw, surface.raw, &format_count, null);
const formats = try alloc.alloc(vk.VkSurfaceFormatKHR, format_count);
defer alloc.free(formats);
_ = vk.vkGetPhysicalDeviceSurfaceFormatsKHR(adapter.raw, surface.raw, &format_count, formats.ptr);
// --- # ---
var present_mode_count: u32 = 0;
_ = vk.vkGetPhysicalDeviceSurfacePresentModesKHR(adapter.raw, surface.raw, &present_mode_count, null);
const present_modes = try alloc.alloc(vk.VkPresentModeKHR, present_mode_count);
defer alloc.free(present_modes);
_ = vk.vkGetPhysicalDeviceSurfacePresentModesKHR(adapter.raw, surface.raw, &present_mode_count, present_modes.ptr);
// --- # ---
const extent: vk.VkExtent2D = blk: {
if (capabilities.currentExtent.width != std.math.maxInt(u32)) break :blk capabilities.currentExtent;
break :blk .{ .width = config.width, .height = config.height };
};
// --- # ---
const format: vk.VkSurfaceFormatKHR = blk: {
for (formats) |v| {
if (v.format == vk.VK_FORMAT_B8G8R8A8_SRGB and v.colorSpace == vk.VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
break :blk v;
}
}
break :blk formats[0];
};
// --- # ---
var image_count: u32 = capabilities.minImageCount + 1;
if (capabilities.maxImageCount > 0 and image_count > capabilities.maxImageCount) {
image_count = capabilities.maxImageCount;
}
var create_info: vk.VkSwapchainCreateInfoKHR = .{
.sType = vk.VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
.surface = surface.raw,
.minImageCount = image_count,
.imageFormat = format.format,
.imageColorSpace = format.colorSpace,
.imageExtent = extent,
.imageArrayLayers = 1,
.imageUsage = vk.VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
.preTransform = capabilities.currentTransform,
.compositeAlpha = vk.VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
.presentMode = present_modes[0],
.clipped = 1,
.imageSharingMode = vk.VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = null,
};
var swapchain: vk.VkSwapchainKHR = null;
if (vk.vkCreateSwapchainKHR(device.raw, &create_info, null, &swapchain) != vk.VK_SUCCESS) {
return error.FailedToCreateSwapchainKHR;
}
// --- GET IMAGES ---
_ = vk.vkGetSwapchainImagesKHR(device.raw, swapchain, &image_count, null);
const images = try alloc.alloc(vk.VkImage, image_count);
_ = vk.vkGetSwapchainImagesKHR(device.raw, swapchain, &image_count, images.ptr);
// --- GET IMAGE VIEWS ---
const image_views = try alloc.alloc(vk.VkImageView, images.len);
for (images, 0..) |image, i| {
var image_view_create_info: vk.VkImageViewCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = image,
.viewType = vk.VK_IMAGE_VIEW_TYPE_2D,
.format = format.format,
.components = .{
.r = vk.VK_COMPONENT_SWIZZLE_IDENTITY,
.g = vk.VK_COMPONENT_SWIZZLE_IDENTITY,
.b = vk.VK_COMPONENT_SWIZZLE_IDENTITY,
.a = vk.VK_COMPONENT_SWIZZLE_IDENTITY,
},
.subresourceRange = .{
.aspectMask = vk.VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
if (vk.vkCreateImageView(device.raw, &image_view_create_info, null, &image_views[i]) != vk.VK_SUCCESS) {
return error.FailedToCreateImageView;
}
}
return .{
.raw = swapchain.?,
.images = images,
.image_views = image_views,
.extent = extent,
.format = format.format,
.device = device,
.alloc = alloc,
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn deinit(self: *Self) void {
for (self.image_views) |view| {
vk.vkDestroyImageView(self.device.raw, view, null);
}
vk.vkDestroySwapchainKHR(self.device.raw, self.raw, null);
self.alloc.free(self.images);
self.alloc.free(self.image_views);
}

View file

@ -4,59 +4,570 @@
const std = @import("std");
const vk = @import("vulkan");
const gtl = @import("gtl");
const common = @import("../root.zig");
const Instance = @import("instance.zig");
const Surface = @import("surface.zig");
const Device = @import("device.zig");
const Resource = @import("resource.zig");
const MainConfig = @import("../../gfx.zig").Config;
const Handle = gtl.Handle;
const Self = @This();
// --- ENUMS ---
const BufferUsage = common.BufferUsage;
const BufferSharingMode = common.BufferSharingMode;
// --- CONFIGS ---
const PipelineConfig = common.PipelineConfig;
// --- HANDLES ---
const Adapter = common.Adapter;
const Device = common.Device;
const Window = common.Window;
const Surface = common.Surface;
const Swapchain = common.Swapchain;
const Buffer = common.Buffer;
const Shader = common.Shader;
const Pipeline = common.Pipeline;
// --- # ---
const MAX_FRAMES_IN_FLIGHT: usize = 3;
/// ----------------------------------------------------
/// ----------------------------------------------------
const Frame = struct {
cmd_buf: vk.VkCommandBuffer = null,
image_index: u32 = 0,
image_available: vk.VkSemaphore = null,
fence: vk.VkFence = null,
};
//
// FIELDS
//
instance: Instance,
surface: Surface,
device: Device,
resource: Resource,
config: MainConfig,
frames: [MAX_FRAMES_IN_FLIGHT]Frame = .{ .{}, .{}, .{} },
current_frame: usize = 0,
render_finished: []vk.VkSemaphore = &.{},
sync_device: ?*vk.VkDevice_T = null,
alloc: std.mem.Allocator,
io: std.Io,
pub fn currentFrame(self: *Self) *Frame {
return &self.frames[self.current_frame];
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn init(
config: MainConfig,
alloc: std.mem.Allocator,
io: std.Io,
) !Self {
// --- INSTANCE ---
var instance: Instance = try .init(config, alloc);
errdefer instance.deinit();
// --- SURFACE ---
var surface: Surface = try .init(&instance, config.window);
errdefer surface.deinit(&instance);
// --- DEVICE ---
var device: Device = try .init(&instance, alloc);
errdefer device.deinit();
return .{
.instance = instance,
.surface = surface,
.device = device,
.resource = .init(alloc),
.config = config,
.alloc = alloc,
.io = io,
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn deinit(self: *Self) void {
self.surface.deinit(&self.instance);
self.resource.deinit();
self.device.deinit();
if (self.sync_device) |device| {
_ = vk.vkDeviceWaitIdle(device);
for (self.render_finished) |semaphore| {
vk.vkDestroySemaphore(device, semaphore, null);
}
self.alloc.free(self.render_finished);
for (&self.frames) |*frame| {
if (frame.fence) |fence| {
vk.vkDestroyFence(device, fence, null);
}
if (frame.image_available) |semaphore| {
vk.vkDestroySemaphore(device, semaphore, null);
}
}
}
self.resource.deinit(&self.instance);
self.instance.deinit();
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn createAdapter(self: *Self) !Handle(Adapter) {
return try self.resource.adapters.put(try .init(self));
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn createDevice(
self: *Self,
adapter: Handle(Adapter),
surface: Handle(Surface),
) !Handle(Device) {
const raw_adapter = self.resource.adapters.get(adapter) orelse return error.AdapterNotFound;
const raw_surface = self.resource.surfaces.get(surface) orelse return error.SurfaceNotFound;
return try self.resource.devices.put(try .init(raw_adapter, raw_surface, self.alloc));
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn destroyDevice(self: *Self, device: Handle(Device)) !void {
if (self.resource.devices.get(device)) |raw| {
raw.deinit();
try self.resource.devices.remove(device);
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn createSurface(self: *Self) !Handle(Surface) {
return try self.resource.surfaces.put(try .init(&self.instance, self.config.window));
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn createSwapchain(
self: *Self,
adapter: Handle(Adapter),
device: Handle(Device),
surface: Handle(Surface),
) !Handle(Swapchain) {
const raw_adapter = self.resource.adapters.get(adapter) orelse return error.AdapterNotFound;
const raw_device = self.resource.devices.get(device) orelse return error.DeviceNotFound;
const raw_surface = self.resource.surfaces.get(surface) orelse return error.SurfaceNotFound;
return try self.resource.swapchains.put(try .init(
raw_adapter,
raw_device,
raw_surface,
.{ .width = 800, .height = 600 },
self.alloc,
));
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn createShader(
self: *Self,
vert_spv_path: []const u8,
frag_spv_path: []const u8,
device: Handle(Device),
) !Handle(Shader) {
const raw_device = self.resource.devices.get(device) orelse return error.DeviceNotFound;
return try self.resource.shaders.put(try .init(vert_spv_path, frag_spv_path, raw_device, self.alloc, self.io));
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn createPipeline(self: *Self, config: PipelineConfig) !Handle(Pipeline) {
const raw_device = self.resource.devices.get(config.device) orelse return error.DeviceNotFound;
const raw_swapchain = self.resource.swapchains.get(config.swapchain) orelse return error.SwapchainNotFound;
const raw_shader = self.resource.shaders.get(config.shader) orelse return error.ShaderNotFound;
return try self.resource.pipelines.put(try .init(raw_shader, raw_swapchain, raw_device, config, self.alloc));
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn createBuffer(
self: *Self,
size: u32,
usage: BufferUsage,
sharing: BufferSharingMode,
adapter: Handle(Adapter),
device: Handle(Device),
) !Handle(Buffer) {
const raw_adapter = self.resource.adapters.get(adapter) orelse return error.AdapterNotFound;
const raw_device = self.resource.devices.get(device) orelse return error.DeviceNotFound;
return try self.resource.buffers.put(try .init(size, usage, sharing, raw_adapter, raw_device));
}
// /// ----------------------------------------------------
// /// ----------------------------------------------------
// pub const AttributeDesc = struct {
// binding: u32,
// location: u32,
// uniform_type: UniformType,
// offset: u32,
// };
// /// ----------------------------------------------------
// /// ----------------------------------------------------
// pub fn createUniform(
// self: *Self,
// attributes: []const AttributeDesc,
// ) !void {
// _ = self;
// _ = attributes;
// }
//
// FRAME + RENDERING
//
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn beginFrame(
self: *Self,
swapchain: Handle(Swapchain),
device: Handle(Device),
) !void {
const raw_device = self.resource.devices.get(device) orelse return error.DeviceNotFound;
const raw_swapchain = self.resource.swapchains.get(swapchain) orelse return error.SwapchainNotFound;
try self.ensureFrameSync(raw_device.raw, raw_swapchain.raw);
const frame = self.currentFrame();
if (frame.fence) |fence| {
_ = vk.vkWaitForFences(raw_device.raw, 1, &fence, vk.VK_TRUE, std.math.maxInt(u64));
_ = vk.vkResetFences(raw_device.raw, 1, &fence);
}
if (frame.cmd_buf == null) {
const alloc_info: vk.VkCommandBufferAllocateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = raw_device.graphics_command_pool,
.level = vk.VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = 1,
};
if (vk.vkAllocateCommandBuffers(
raw_device.raw,
&alloc_info,
&frame.cmd_buf,
) != vk.VK_SUCCESS) {
return error.FailedToAllocateCommandBuffers;
}
}
const begin_info: vk.VkCommandBufferBeginInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = vk.VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
.pInheritanceInfo = null,
};
if (vk.vkBeginCommandBuffer(
frame.cmd_buf,
&begin_info,
) != vk.VK_SUCCESS) {
return error.FailedToBeginCommandBuffer;
}
const result = vk.vkAcquireNextImageKHR(
raw_device.raw,
raw_swapchain.raw,
std.math.maxInt(u64),
frame.image_available,
null,
&frame.image_index,
);
switch (result) {
vk.VK_SUCCESS, vk.VK_SUBOPTIMAL_KHR => {},
vk.VK_ERROR_OUT_OF_DATE_KHR => return error.SwapchainOutOfDate,
else => return error.FailedToAcquireSwapchainImage,
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn endFrame(
self: *Self,
swapchain: Handle(Swapchain),
device: Handle(Device),
) !void {
const raw_device = self.resource.devices.get(device) orelse return error.DeviceNotFound;
const raw_swapchain = self.resource.swapchains.get(swapchain) orelse return error.SwapchainNotFound;
const frame = self.currentFrame();
if (vk.vkEndCommandBuffer(frame.cmd_buf) != vk.VK_SUCCESS) {
return error.FailedToEndCommandBuffer;
}
const render_finished = self.render_finished[frame.image_index];
const wait_stage = [_]u32{
vk.VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
};
const submit_info: vk.VkSubmitInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_SUBMIT_INFO,
.waitSemaphoreCount = 1,
.pWaitSemaphores = &frame.image_available,
.pWaitDstStageMask = &wait_stage,
.commandBufferCount = 1,
.pCommandBuffers = &frame.cmd_buf,
.signalSemaphoreCount = 1,
.pSignalSemaphores = &render_finished,
};
if (vk.vkQueueSubmit(raw_device.graphics_queue, 1, &submit_info, frame.fence) != vk.VK_SUCCESS) {
return error.FailedToSubmitCommandBuffer;
}
const present_info: vk.VkPresentInfoKHR = .{
.sType = vk.VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.waitSemaphoreCount = 1,
.pWaitSemaphores = &render_finished,
.swapchainCount = 1,
.pSwapchains = &raw_swapchain.raw,
.pImageIndices = &frame.image_index,
};
_ = vk.vkQueuePresentKHR(
raw_device.present_queue,
&present_info,
);
self.current_frame = (self.current_frame + 1) % MAX_FRAMES_IN_FLIGHT;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn beginRendering(
self: *Self,
swapchain: Handle(Swapchain),
) !void {
const raw_swapchain = self.resource.swapchains.get(swapchain) orelse return error.SwapchainNotFound;
const frame = self.currentFrame();
const barrier: vk.VkImageMemoryBarrier = .{
.sType = vk.VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = 0,
.dstAccessMask = vk.VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.oldLayout = vk.VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = vk.VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
.srcQueueFamilyIndex = vk.VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = vk.VK_QUEUE_FAMILY_IGNORED,
.image = raw_swapchain.images[frame.image_index],
.subresourceRange = .{
.aspectMask = vk.VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
vk.vkCmdPipelineBarrier(
frame.cmd_buf,
vk.VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
vk.VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
0,
0,
null,
0,
null,
1,
&barrier,
);
const color_attachment: vk.VkRenderingAttachmentInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.imageView = raw_swapchain.image_views[frame.image_index],
.imageLayout = vk.VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
.loadOp = vk.VK_ATTACHMENT_LOAD_OP_CLEAR,
.storeOp = vk.VK_ATTACHMENT_STORE_OP_STORE,
.clearValue = .{
.color = .{
.float32 = .{ 0.01, 0.01, 0.01, 1.0 },
},
},
};
const rendering_info: vk.VkRenderingInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_RENDERING_INFO,
.renderArea = .{
.offset = .{
.x = 0,
.y = 0,
},
.extent = raw_swapchain.extent,
},
.layerCount = 1,
.colorAttachmentCount = 1,
.pColorAttachments = &color_attachment,
};
vk.vkCmdBeginRendering(
frame.cmd_buf,
&rendering_info,
);
const viewport: vk.VkViewport = .{
.x = 0,
.y = 0,
.width = @floatFromInt(raw_swapchain.extent.width),
.height = @floatFromInt(raw_swapchain.extent.height),
.minDepth = 0.0,
.maxDepth = 1.0,
};
const scissor: vk.VkRect2D = .{
.offset = .{
.x = 0,
.y = 0,
},
.extent = raw_swapchain.extent,
};
vk.vkCmdSetViewport(frame.cmd_buf, 0, 1, &viewport);
vk.vkCmdSetScissor(frame.cmd_buf, 0, 1, &scissor);
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn endRendering(
self: *Self,
swapchain: Handle(Swapchain),
) !void {
const raw_swapchain = self.resource.swapchains.get(swapchain) orelse return error.SwapchainNotFound;
const frame = self.currentFrame();
vk.vkCmdEndRendering(frame.cmd_buf);
const barrier: vk.VkImageMemoryBarrier = .{
.sType = vk.VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = vk.VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.dstAccessMask = 0,
.oldLayout = vk.VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
.newLayout = vk.VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
.srcQueueFamilyIndex = vk.VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = vk.VK_QUEUE_FAMILY_IGNORED,
.image = raw_swapchain.images[frame.image_index],
.subresourceRange = .{
.aspectMask = vk.VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
vk.vkCmdPipelineBarrier(
frame.cmd_buf,
vk.VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
vk.VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
0,
0,
null,
0,
null,
1,
&barrier,
);
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn bindPipeline(self: *Self, pipeline: Handle(Pipeline)) !void {
const raw_pipeline = self.resource.pipelines.get(pipeline) orelse return error.PipelineNotFound;
vk.vkCmdBindPipeline(self.currentFrame().cmd_buf, vk.VK_PIPELINE_BIND_POINT_GRAPHICS, raw_pipeline.raw);
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn draw(self: *Self, vertex_count: u32, instance_count: u32, first_vertex: u32, first_instance: u32) !void {
vk.vkCmdDraw(self.currentFrame().cmd_buf, vertex_count, instance_count, first_vertex, first_instance);
}
/// ----------------------------------------------------
/// Lazily create the frame sync objects (semaphores + fence)
/// ----------------------------------------------------
fn ensureFrameSync(
self: *Self,
device: *vk.VkDevice_T,
raw_swapchain: *vk.VkSwapchainKHR_T,
) !void {
if (self.sync_device != null) return;
self.sync_device = device;
const semaphore_info: vk.VkSemaphoreCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
};
const fence_info: vk.VkFenceCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
.flags = vk.VK_FENCE_CREATE_SIGNALED_BIT,
};
var image_count: u32 = 0;
_ = vk.vkGetSwapchainImagesKHR(device, raw_swapchain, &image_count, null);
const render_finished = try self.alloc.alloc(vk.VkSemaphore, image_count);
errdefer self.alloc.free(render_finished);
for (render_finished, 0..) |*semaphore, i| {
_ = i;
if (vk.vkCreateSemaphore(device, &semaphore_info, null, semaphore) != vk.VK_SUCCESS) {
return error.FailedToCreateSemaphore;
}
}
self.render_finished = render_finished;
for (&self.frames) |*frame| {
var image_available: vk.VkSemaphore = null;
if (vk.vkCreateSemaphore(device, &semaphore_info, null, &image_available) != vk.VK_SUCCESS) {
return error.FailedToCreateSemaphore;
}
var fence: vk.VkFence = null;
if (vk.vkCreateFence(device, &fence_info, null, &fence) != vk.VK_SUCCESS) {
vk.vkDestroySemaphore(device, image_available, null);
return error.FailedToCreateFence;
}
frame.image_available = image_available.?;
frame.fence = fence.?;
}
}

5
tools/shaders.zig Normal file
View file

@ -0,0 +1,5 @@
const std = @import("std");
pub fn main(init: std.process.Init) !void {
_ = init;
}