HelloWorld
This commit is contained in:
parent
5e5daa6f66
commit
9173c8c9e9
21 changed files with 2094 additions and 83 deletions
12
README.md
Normal file
12
README.md
Normal 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
|
||||
BIN
assets/shaders/compiled/simple.frag.spv
Normal file
BIN
assets/shaders/compiled/simple.frag.spv
Normal file
Binary file not shown.
BIN
assets/shaders/compiled/simple.vert.spv
Normal file
BIN
assets/shaders/compiled/simple.vert.spv
Normal file
Binary file not shown.
7
assets/shaders/simple.frag
Normal file
7
assets/shaders/simple.frag
Normal 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);
|
||||
}
|
||||
9
assets/shaders/simple.vert
Normal file
9
assets/shaders/simple.vert
Normal 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);
|
||||
}
|
||||
|
|
@ -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
|
||||
//
|
||||
|
|
|
|||
|
|
@ -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
3
compile.sh
Executable 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
|
||||
302
src/gfx.zig
302
src/gfx.zig
|
|
@ -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(...);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
149
src/rhi/root.zig
149
src/rhi/root.zig
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
131
src/rhi/vulkan/resource/buffer.zig
Normal file
131
src/rhi/vulkan/resource/buffer.zig
Normal 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);
|
||||
}
|
||||
167
src/rhi/vulkan/resource/device.zig
Normal file
167
src/rhi/vulkan/resource/device.zig
Normal 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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
53
src/rhi/vulkan/resource/surface.zig
Normal file
53
src/rhi/vulkan/resource/surface.zig
Normal 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);
|
||||
}
|
||||
169
src/rhi/vulkan/resource/swapchain.zig
Normal file
169
src/rhi/vulkan/resource/swapchain.zig
Normal 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);
|
||||
}
|
||||
|
|
@ -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
5
tools/shaders.zig
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub fn main(init: std.process.Init) !void {
|
||||
_ = init;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue