Let there be images

This commit is contained in:
abux 2026-08-12 14:52:27 +01:00
parent ce1b032361
commit 655dca998b
31 changed files with 9134 additions and 213 deletions

View file

@ -10,6 +10,7 @@ Low cortisol crossplatform RHI (Rendering Hardware Interface)
- Fire and forget - Fire and forget
- Dead simple + Performance + Flexible - Dead simple + Performance + Flexible
- Handles - Handles
- Auto managed
## Architecture ## Architecture
```text ```text
@ -27,6 +28,72 @@ src/rhi/vulkan/vulkan.zig
src/rhi/vulkan/resource.zig src/rhi/vulkan/resource.zig
└─ Adapter / Device / Shader └─ Adapter / Device / Shader
``` ```
| Function | Description |
| ------------------- | ----------------------------------------------------------------- |
| `makeX(...)` | Creates an RHI resource and returns its handle |
| `deleteX(handle)` | Releases the resource and removes it from the resource pool |
| `releaseX(handle)` | Releases the resource without removing it from the resource pool |
| `getRawVkX(handle)` | Returns the backend-specific raw Vulkan GPU handle |
## Example
### Core
```zig
const adapter = try gfx.makeAdapter(.{});
const surface = try gfx.makeSurface();
const device = try gfx.makeDevice(adapter, surface);
const swapchain = try gfx.makeSwapchain(adapter, device, surface);
```
### Buffers
```zig
const vbuf = try gfx.makeBuffer(vertices, .vertex, adapter, device);
const ibuf = try gfx.makeBuffer(indices, .index, adapter, device);
```
### Shaders
```zig
const simple_vert = try gfx.makeShader("examples/assets/shaders/compiled/simple.vert.spv", device);
const simple_frag = try gfx.makeShader("examples/assets/shaders/compiled/simple.frag.spv", device);
```
### Pipelines
```zig
const pipeline = try gfx.makePipeline(.{
.vertex_attributes = &.{
.{ .location = 0, .format = .vec2, .offset = @offsetOf(Vertex, "pos") },
.{ .location = 1, .format = .vec2, .offset = @offsetOf(Vertex, "normal") },
.{ .location = 2, .format = .vec2, .offset = @offsetOf(Vertex, "uv") },
},
.vertex_binding = .{ .stride = @sizeOf(Vertex) },
.uniforms = &.{
.{ .name = "material", .offset = 0, .size = @sizeOf(UnlitMaterial) },
},
.textures = &.{texture},
.blend_mode = .alpha,
.vert_shader = simple_vert,
.frag_shader = simple_frag,
.device = device,
.swapchain = swapchain,
});
```
### Render Pass
```zig
if (gfx.beginPass(swapchain, .{
.clear_color = .{ 0.01, 0.01, 0.01, 1.0 },
})) |pass| {
defer pass.end();
try vbuf.bind();
try ibuf.bind();
try pipeline.setUniform("material", material);
try pipeline.bind();
pass.drawIndexed(indices.len, 1, 0, 0, 0);
}
```
## Examples ## Examples
- ```zig build run-simple``` - ```zig build run-simple```

View file

@ -50,6 +50,14 @@ pub fn build(b: *std.Build) !void {
mod.addImport("vulkan", vulkan.createModule()); mod.addImport("vulkan", vulkan.createModule());
mod.linkSystemLibrary("vulkan", .{}); mod.linkSystemLibrary("vulkan", .{});
const stb_image = b.addTranslateC(.{
.target = target,
.optimize = optimize,
.root_source_file = b.path("./src/vendor/stb_image/stb_image.h"),
});
mod.addImport("stb_image", stb_image.createModule());
mod.addCSourceFile(.{ .file = b.path("src/vendor/stb_image/stb_image.c") });
// //
// EXAMPLES // EXAMPLES
// //
@ -107,5 +115,8 @@ pub fn build(b: *std.Build) !void {
const example_build_step = b.addInstallArtifact(example, .{}); const example_build_step = b.addInstallArtifact(example, .{});
example_step.dependOn(&example_build_step.step); example_step.dependOn(&example_build_step.step);
const check_step = b.step("check", "Check if compiles");
check_step.dependOn(&example.step);
} }
} }

View file

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

View file

@ -1,11 +1,25 @@
#version 450 #version 450
//
// IO
//
layout(location = 0) in vec2 in_UV;
layout(location = 0) out vec4 out_color;
//
// UNIFORMS
//
layout(binding = 0) uniform UnlitMaterial { layout(binding = 0) uniform UnlitMaterial {
vec4 albedo; vec4 albedo;
} material; } material;
layout(location = 0) out vec4 outColor; layout(binding = 1) uniform sampler2D albedo_texture;
/// ----------------------------------------------------
/// ----------------------------------------------------
void main() { void main() {
outColor = material.albedo; out_color = texture(albedo_texture, in_UV);
} }

View file

@ -1,13 +1,18 @@
#version 450 #version 450
layout(binding = 0) uniform UnlitMaterial { //
vec4 albedo; // IO
} material; //
layout(location = 0) in vec2 inPosition; layout(location = 0) in vec2 in_position;
layout(location = 1) in vec2 inNormal; layout(location = 1) in vec2 in_normal;
layout(location = 2) in vec2 inUV; layout(location = 2) in vec2 in_UV;
layout(location = 0) out vec2 out_UV;
/// ----------------------------------------------------
/// ----------------------------------------------------
void main() { void main() {
gl_Position = vec4(inPosition, 0.0, 1.0); gl_Position = vec4(in_position, 0.0, 1.0);
out_UV = in_UV;
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 532 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

View file

@ -83,6 +83,12 @@ pub fn main(init: std.process.Init) !void {
const device = try gfx.makeDevice(adapter, surface); const device = try gfx.makeDevice(adapter, surface);
const swapchain = try gfx.makeSwapchain(adapter, device, surface); const swapchain = try gfx.makeSwapchain(adapter, device, surface);
// --- TEXTURES ---
const texture = try gfx.makeTexture(.{
.image = .{ .path = "examples/assets/textures/texture.jpg" },
.sampler = .{ .min_filter = .linear, .address_u = .repeat },
}, adapter, device);
// --- BUFFERS --- // --- BUFFERS ---
const vbuf = try gfx.makeBuffer(vertices, .vertex, adapter, device); const vbuf = try gfx.makeBuffer(vertices, .vertex, adapter, device);
const ibuf = try gfx.makeBuffer(indices, .index, adapter, device); const ibuf = try gfx.makeBuffer(indices, .index, adapter, device);
@ -104,6 +110,10 @@ pub fn main(init: std.process.Init) !void {
.{ .name = "material", .offset = 0, .size = @sizeOf(UnlitMaterial) }, .{ .name = "material", .offset = 0, .size = @sizeOf(UnlitMaterial) },
}, },
.textures = &.{texture},
.blend_mode = .alpha,
.vert_shader = simple_vert, .vert_shader = simple_vert,
.frag_shader = simple_frag, .frag_shader = simple_frag,

View file

@ -6,7 +6,7 @@
const std = @import("std"); const std = @import("std");
const gtl = @import("gtl"); const gtl = @import("gtl");
const vk = @import("vulkan"); const vk = @import("vulkan");
const common = @import("rhi/root.zig"); const common = @import("rhi/common.zig");
const Handle = gtl.Handle; const Handle = gtl.Handle;
const Self = @This(); const Self = @This();
@ -16,14 +16,20 @@ const RenderPassConfig = common.RenderPassConfig;
const PipelineConfig = common.PipelineConfig; const PipelineConfig = common.PipelineConfig;
const BufferUsage = common.BufferUsage; const BufferUsage = common.BufferUsage;
const Pass = common.Pass; const Pass = common.Pass;
const ImageConfig = common.ImageConfig;
const SamplerConfig = common.SamplerConfig;
const TextureConfig = common.TextureConfig;
// --- HANDLES --- // --- HANDLES ---
const Window = @import("rhi/root.zig").Window; const Window = common.Window;
const Adapter = common.Adapter; const Adapter = common.Adapter;
const Device = common.Device; const Device = common.Device;
const Buffer = common.Buffer; const Buffer = common.Buffer;
const Surface = common.Surface; const Surface = common.Surface;
const Swapchain = common.Swapchain; const Swapchain = common.Swapchain;
const Image = common.Image;
const Sampler = common.Sampler;
const Texture = common.Texture;
const Shader = common.Shader; const Shader = common.Shader;
const Pipeline = common.Pipeline; const Pipeline = common.Pipeline;
@ -111,10 +117,27 @@ pub fn makeDevice(
/// ---------------------------------------------------- /// ----------------------------------------------------
/// ---------------------------------------------------- /// ----------------------------------------------------
pub fn destroyDevice(self: *Self, device: Handle(Device)) !void { pub fn deleteDevice(self: *Self, device: Handle(Device)) void {
try switch (self.ctx) { switch (self.ctx) {
.vulkan => |*v| v.destroyDevice(device), .vulkan => |*v| v.deleteDevice(device),
}; }
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn releaseDevice(self: *Self, device: Handle(Device)) void {
switch (self.ctx) {
.vulkan => |*v| v.releaseDevice(device),
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn getRAWVKDevice(self: *Self, device: Handle(Device)) ?*vk.VkDevice_T {
switch (self.ctx) {
.vulkan => |*v| return v.getRAWDevice(device),
}
return null;
} }
/// ---------------------------------------------------- /// ----------------------------------------------------
@ -195,6 +218,44 @@ pub fn makePipeline(self: *Self, config: PipelineConfig) !Pipeline {
}; };
} }
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn makeImage(
self: *Self,
config: ImageConfig,
adapter: Handle(Adapter),
device: Handle(Device),
) !Handle(Image) {
return try switch (self.ctx) {
.vulkan => |*v| v.makeImage(config, adapter, device),
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn makeSampler(
self: *Self,
config: SamplerConfig,
device: Handle(Device),
) !Handle(Sampler) {
return try switch (self.ctx) {
.vulkan => |*v| v.makeSampler(config, device),
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn makeTexture(
self: *Self,
config: TextureConfig,
adapter: Handle(Adapter),
device: Handle(Device),
) !Handle(Texture) {
return try switch (self.ctx) {
.vulkan => |*v| v.makeTexture(config, adapter, device),
};
}
/// ---------------------------------------------------- /// ----------------------------------------------------
/// ---------------------------------------------------- /// ----------------------------------------------------
pub fn beginFrame( pub fn beginFrame(

View file

@ -1,7 +1,7 @@
//! ---------------------------------------------------- //! ----------------------------------------------------
//! Shared Handles and Configs across every backend
//! ---------------------------------------------------- //! ----------------------------------------------------
const vk = @import("vulkan");
const gtl = @import("gtl"); const gtl = @import("gtl");
const Gfx = @import("../gfx.zig"); const Gfx = @import("../gfx.zig");
const Handle = gtl.Handle; const Handle = gtl.Handle;
@ -32,7 +32,7 @@ pub const Window = union(enum) {
/// ---------------------------------------------------- /// ----------------------------------------------------
/// ---------------------------------------------------- /// ----------------------------------------------------
pub const GpuType = enum { pub const GPUType = enum {
any, any,
discrete, discrete,
integrated, integrated,
@ -44,7 +44,7 @@ pub const GpuType = enum {
pub const AdapterConfig = struct { pub const AdapterConfig = struct {
/// Preferred class of GPU /// Preferred class of GPU
/// `.any` disables filtering by class /// `.any` disables filtering by class
gpu_type: GpuType = .discrete, gpu_type: GPUType = .discrete,
/// Select the adapter at a specific enumeration index /// Select the adapter at a specific enumeration index
/// `null` picks the first suitable one /// `null` picks the first suitable one
@ -110,6 +110,7 @@ pub const PipelineConfig = struct {
vertex_attributes: []const VertexAttribute = &.{}, vertex_attributes: []const VertexAttribute = &.{},
uniforms: []const UniformDesc = &.{}, uniforms: []const UniformDesc = &.{},
textures: []const Handle(Texture) = &.{},
topology: enum { topology: enum {
triangle_list, triangle_list,
@ -127,6 +128,11 @@ pub const PipelineConfig = struct {
ccw, ccw,
cw, cw,
} = .cw, } = .cw,
blend_mode: enum {
none,
alpha,
} = .none,
}; };
/// ---------------------------------------------------- /// ----------------------------------------------------
@ -135,6 +141,8 @@ pub const Pass = struct {
swapchain: Handle(Swapchain), swapchain: Handle(Swapchain),
gfx: *Gfx, gfx: *Gfx,
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn end(self: *const Pass) void { pub fn end(self: *const Pass) void {
switch (self.gfx.ctx) { switch (self.gfx.ctx) {
.vulkan => |*v| v.endRendering(self.swapchain), .vulkan => |*v| v.endRendering(self.swapchain),
@ -145,7 +153,7 @@ pub const Pass = struct {
/// ---------------------------------------------------- /// ----------------------------------------------------
pub fn draw(self: *const Pass, vertex_count: u32, instance_count: u32, first_vertex: u32, first_instance: u32) void { pub fn draw(self: *const Pass, vertex_count: u32, instance_count: u32, first_vertex: u32, first_instance: u32) void {
switch (self.gfx.ctx) { switch (self.gfx.ctx) {
.vulkan => |*v| v.draw(vertex_count, instance_count, first_vertex, first_instance) catch {}, .vulkan => |*v| v.draw(vertex_count, instance_count, first_vertex, first_instance),
} }
} }
@ -153,12 +161,7 @@ pub const Pass = struct {
/// ---------------------------------------------------- /// ----------------------------------------------------
pub fn drawIndexed(self: *const Pass, index_count: u32, instance_count: u32, first_index: u32, vertex_offset: i32, first_instance: u32) void { pub fn drawIndexed(self: *const Pass, index_count: u32, instance_count: u32, first_index: u32, vertex_offset: i32, first_instance: u32) void {
switch (self.gfx.ctx) { switch (self.gfx.ctx) {
.vulkan => |*v| { .vulkan => |*v| v.drawIndexed(index_count, instance_count, first_index, vertex_offset, first_instance),
const frame = v.currentFrame();
if (frame.cmd_buf) |cmd| {
vk.vkCmdDrawIndexed(cmd, index_count, instance_count, first_index, vertex_offset, first_instance);
}
},
} }
} }
}; };
@ -193,6 +196,7 @@ pub const RenderPassConfig = struct {
pub const BufferUsage = enum { pub const BufferUsage = enum {
vertex, vertex,
index, index,
image,
}; };
/// ---------------------------------------------------- /// ----------------------------------------------------
@ -201,6 +205,84 @@ pub const BufferSharingMode = enum {
exclusive, exclusive,
}; };
/// ----------------------------------------------------
/// ----------------------------------------------------
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const PixelFormat = enum {
rgba8_unorm,
rgba8_srgb,
bgra8_unorm,
bgra8_srgb,
};
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const Filter = enum {
nearest,
linear,
};
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const MipmapMode = enum {
nearest,
linear,
};
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const AddressMode = enum {
repeat,
mirrored_repeat,
clamp_to_edge,
clamp_to_border,
};
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const ImageUsage = enum {
texture,
render_target,
};
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const ImageConfig = struct {
/// Read a file from disk with `stb_image`
path: ?[]const u8 = null,
/// Raw pixel bytes (overrides `path`)
data: ?[]const u8 = null,
/// Required for `.data`. `null` = use source dimensions
size: ?[2]u32 = null,
format: PixelFormat = .rgba8_srgb,
usage: ImageUsage = .texture,
mip_levels: u32 = 1,
};
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const SamplerConfig = struct {
min_filter: Filter = .linear,
mag_filter: Filter = .linear,
mipmap_mode: MipmapMode = .linear,
address_u: AddressMode = .repeat,
address_v: AddressMode = .repeat,
address_w: AddressMode = .repeat,
max_anisotropy: f32 = 1.0,
max_lod: f32 = 0,
};
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const TextureConfig = struct {
image: ImageConfig = .{},
sampler: SamplerConfig = .{},
};
// //
// HANDLE TYPES // HANDLE TYPES
// //
@ -227,19 +309,7 @@ pub const Buffer = struct {
pub fn bind(self: *const Buffer) !void { pub fn bind(self: *const Buffer) !void {
switch (self.gfx.ctx) { switch (self.gfx.ctx) {
.vulkan => |*v| { .vulkan => |*v| try v.bindBuffer(self.handle),
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),
}
}
},
} }
} }
}; };
@ -247,6 +317,7 @@ pub const Buffer = struct {
pub const Image = struct {}; pub const Image = struct {};
pub const ImageView = struct {}; pub const ImageView = struct {};
pub const Sampler = struct {}; pub const Sampler = struct {};
pub const Texture = struct {};
pub const Shader = struct {}; pub const Shader = struct {};
pub const PipelineLayout = struct {}; pub const PipelineLayout = struct {};

View file

@ -6,7 +6,7 @@ const std = @import("std");
const gtl = @import("gtl"); const gtl = @import("gtl");
const vk = @import("vulkan"); const vk = @import("vulkan");
const Resource = @import("resource.zig"); const Resource = @import("resource.zig");
const Window = @import("../root.zig").Window; const Window = @import("../common.zig").Window;
const MainConfig = @import("../../gfx.zig").Config; const MainConfig = @import("../../gfx.zig").Config;
const Self = @This(); const Self = @This();

View file

@ -4,7 +4,7 @@
const vk = @import("vulkan"); const vk = @import("vulkan");
const std = @import("std"); const std = @import("std");
const gtl = @import("gtl"); const gtl = @import("gtl");
const common = @import("../root.zig"); const common = @import("../common.zig");
const Instance = @import("instance.zig"); const Instance = @import("instance.zig");
const Self = @This(); const Self = @This();
@ -30,17 +30,31 @@ const Pipeline = common.Pipeline;
const PipelineLayout = common.PipelineLayout; const PipelineLayout = common.PipelineLayout;
const VKPipeline = @import("resource/pipeline.zig"); const VKPipeline = @import("resource/pipeline.zig");
const Image = common.Image;
const VKImage = @import("resource/image.zig");
const Sampler = common.Sampler;
const VKSampler = @import("resource/sampler.zig");
const Texture = common.Texture;
const VKTexture = @import("resource/texture.zig");
// //
// FIELDS // FIELDS
// //
adapters: gtl.ResourceMap(Adapter, VKAdapter), adapters: gtl.ResourceMap(Adapter, VKAdapter),
devices: gtl.ResourceMap(Device, VKDevice), devices: gtl.ResourceMap(Device, VKDevice),
buffers: gtl.ResourceMap(Buffer, VKBuffer),
surfaces: gtl.ResourceMap(Surface, VKSurface), surfaces: gtl.ResourceMap(Surface, VKSurface),
swapchains: gtl.ResourceMap(Swapchain, VKSwapchain), swapchains: gtl.ResourceMap(Swapchain, VKSwapchain),
buffers: gtl.ResourceMap(Buffer, VKBuffer),
images: gtl.ResourceMap(Image, VKImage),
samplers: gtl.ResourceMap(Sampler, VKSampler),
textures: gtl.ResourceMap(Texture, VKTexture),
shaders: gtl.ResourceMap(Shader, VKShader), shaders: gtl.ResourceMap(Shader, VKShader),
pipelines: gtl.ResourceMap(Pipeline, VKPipeline), pipelines: gtl.ResourceMap(Pipeline, VKPipeline),
@ -50,11 +64,16 @@ pub fn init(alloc: std.mem.Allocator) Self {
return .{ return .{
.adapters = .init(alloc), .adapters = .init(alloc),
.devices = .init(alloc), .devices = .init(alloc),
.buffers = .init(alloc),
.surfaces = .init(alloc), .surfaces = .init(alloc),
.swapchains = .init(alloc), .swapchains = .init(alloc),
.buffers = .init(alloc),
.images = .init(alloc),
.samplers = .init(alloc),
.textures = .init(alloc),
.shaders = .init(alloc), .shaders = .init(alloc),
.pipelines = .init(alloc), .pipelines = .init(alloc),
}; };
@ -98,6 +117,30 @@ pub fn deinit(
self.pipelines.deinit(); self.pipelines.deinit();
} }
{ // FREE IMAGES
var it = self.images.valueIterator();
while (it.next()) |v| {
v.deinit();
}
self.images.deinit();
}
{ // FREE SAMPLERS
var it = self.samplers.valueIterator();
while (it.next()) |v| {
v.deinit();
}
self.samplers.deinit();
}
{ // FREE TEXTURES
var it = self.textures.valueIterator();
while (it.next()) |v| {
v.deinit();
}
self.textures.deinit();
}
{ // FREE BUFFERS { // FREE BUFFERS
var it = self.buffers.valueIterator(); var it = self.buffers.valueIterator();
while (it.next()) |v| { while (it.next()) |v| {

View file

@ -5,9 +5,10 @@
const std = @import("std"); const std = @import("std");
const gtl = @import("gtl"); const gtl = @import("gtl");
const vk = @import("vulkan"); const vk = @import("vulkan");
const common = @import("../../common.zig");
const VKBackend = @import("../vulkan.zig"); const VKBackend = @import("../vulkan.zig");
const AdapterConfig = @import("../../root.zig").AdapterConfig; const AdapterConfig = common.AdapterConfig;
const GpuType = @import("../../root.zig").GpuType; const GPUType = common.GPUType;
const Self = @This(); const Self = @This();
// //
@ -91,7 +92,7 @@ fn findGraphicsQueueFamily(
/// ---------------------------------------------------- /// ----------------------------------------------------
/// ---------------------------------------------------- /// ----------------------------------------------------
fn isDeviceSuitable(device: vk.VkPhysicalDevice, gpu_type: GpuType) !bool { fn isDeviceSuitable(device: vk.VkPhysicalDevice, gpu_type: GPUType) !bool {
var properties: vk.VkPhysicalDeviceProperties = .{}; var properties: vk.VkPhysicalDeviceProperties = .{};
vk.vkGetPhysicalDeviceProperties(device, &properties); vk.vkGetPhysicalDeviceProperties(device, &properties);

View file

@ -6,7 +6,7 @@ const std = @import("std");
const vk = @import("vulkan"); const vk = @import("vulkan");
const Self = @This(); const Self = @This();
const common = @import("../../root.zig"); const common = @import("../../common.zig");
const Adapter = @import("adapter.zig"); const Adapter = @import("adapter.zig");
const Device = @import("device.zig"); const Device = @import("device.zig");
const BufferUsage = common.BufferUsage; const BufferUsage = common.BufferUsage;
@ -38,6 +38,7 @@ pub fn init(
.usage = switch (usage) { .usage = switch (usage) {
.vertex => vk.VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, .vertex => vk.VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
.index => vk.VK_BUFFER_USAGE_INDEX_BUFFER_BIT, .index => vk.VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
.image => vk.VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
}, },
.sharingMode = switch (sharing) { .sharingMode = switch (sharing) {
.exclusive => vk.VK_SHARING_MODE_EXCLUSIVE, .exclusive => vk.VK_SHARING_MODE_EXCLUSIVE,
@ -123,7 +124,12 @@ pub fn set(
} }
const dst: [*]u8 = @ptrCast(mapped.?); const dst: [*]u8 = @ptrCast(mapped.?);
const src: [*]const u8 = @ptrCast(&value);
const src: [*]const u8 = switch (@typeInfo(@TypeOf(value))) {
.optional => @ptrCast(value.?),
.pointer => @ptrCast(value),
else => @ptrCast(&value),
};
@memcpy(dst[0..size], src[0..size]); @memcpy(dst[0..size], src[0..size]);

View file

@ -19,11 +19,11 @@ pub const QueueFamilies = struct {
// FIELDS // FIELDS
// //
raw: *vk.VkDevice_T, raw: vk.VkDevice,
physical_device: vk.VkPhysicalDevice, physical_device: vk.VkPhysicalDevice,
graphics_queue: *vk.VkQueue_T, graphics_queue: *vk.VkQueue_T,
present_queue: *vk.VkQueue_T, present_queue: *vk.VkQueue_T,
graphics_command_pool: *vk.VkCommandPool_T, graphics_command_pool: vk.VkCommandPool,
/// ---------------------------------------------------- /// ----------------------------------------------------
/// ---------------------------------------------------- /// ----------------------------------------------------
@ -109,19 +109,26 @@ pub fn init(
vk.vkGetDeviceQueue(device, queue_families.present.?, 0, &present_queue); vk.vkGetDeviceQueue(device, queue_families.present.?, 0, &present_queue);
return .{ return .{
.raw = device.?, .raw = device,
.physical_device = adapter.raw, .physical_device = adapter.raw,
.graphics_queue = graphics_queue.?, .graphics_queue = graphics_queue.?,
.present_queue = present_queue.?, .present_queue = present_queue.?,
.graphics_command_pool = graphics_command_pool.?, .graphics_command_pool = graphics_command_pool,
}; };
} }
/// ---------------------------------------------------- /// ----------------------------------------------------
/// ---------------------------------------------------- /// ----------------------------------------------------
pub fn deinit(self: *Self) void { pub fn deinit(self: *Self) void {
vk.vkDestroyCommandPool(self.raw, self.graphics_command_pool, null); if (self.graphics_command_pool) |raw| {
vk.vkDestroyDevice(self.raw, null); vk.vkDestroyCommandPool(self.raw, raw, null);
self.graphics_command_pool = null;
}
if (self.raw) |raw| {
vk.vkDestroyDevice(raw, null);
self.raw = null;
}
} }
/// ---------------------------------------------------- /// ----------------------------------------------------

View file

@ -0,0 +1,367 @@
//! ----------------------------------------------------
//! `🗲` Vulkan Image `🗲`
//! ----------------------------------------------------
const std = @import("std");
const vk = @import("vulkan");
const stb = @import("stb_image");
const common = @import("../../common.zig");
const Self = @This();
const Adapter = @import("adapter.zig");
const Device = @import("device.zig");
const ImageConfig = common.ImageConfig;
const PixelFormat = common.PixelFormat;
//
// FIELDS
//
raw: vk.VkImage = null,
raw_view: vk.VkImageView = null,
memory: vk.VkDeviceMemory = null,
width: u32 = 0,
height: u32 = 0,
format: vk.VkFormat = vk.VK_FORMAT_UNDEFINED,
mip_levels: u32 = 1,
device: *const Device,
adapter: *const Adapter,
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn init(
config: ImageConfig,
adapter: *const Adapter,
device: *const Device,
) !Self {
var self: Self = .{
.device = device,
.adapter = adapter,
};
errdefer self.deinit();
// --- DECODE PIXELS ---
var loaded: ?[*]u8 = null;
var pixels: []const u8 = undefined;
var tex_size: [2]i32 = .{ 0, 0 };
if (config.path) |path| {
var tex_channel: i32 = 0;
const data = stb.stbi_load(path.ptr, &tex_size[0], &tex_size[1], &tex_channel, stb.STBI_rgb_alpha) orelse {
return error.FileNotFound;
};
loaded = data;
pixels = data[0..@as(usize, @intCast(tex_size[0] * tex_size[1] * 4))];
} else if (config.data) |data| {
pixels = data;
} else {
return error.MissingImageSource;
}
defer if (loaded) |data| stb.stbi_image_free(data);
// --- SIZE ---
const size: [2]u32 = blk: {
if (config.path) |_| break :blk .{ @intCast(tex_size[0]), @intCast(tex_size[1]) };
if (config.size) |size| break :blk size;
return error.SizeRequired;
};
self.width = size[0];
self.height = size[1];
self.mip_levels = config.mip_levels;
// --- FORMAT ---
self.format = switch (config.format) {
.rgba8_unorm => vk.VK_FORMAT_R8G8B8A8_UNORM,
.rgba8_srgb => vk.VK_FORMAT_R8G8B8A8_SRGB,
.bgra8_unorm => vk.VK_FORMAT_B8G8R8A8_UNORM,
.bgra8_srgb => vk.VK_FORMAT_B8G8R8A8_SRGB,
};
// --- IMAGE ---
var usage: vk.VkImageUsageFlags = vk.VK_IMAGE_USAGE_TRANSFER_DST_BIT | vk.VK_IMAGE_USAGE_SAMPLED_BIT;
switch (config.usage) {
.texture => {},
.render_target => usage |= vk.VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
}
const image_info: vk.VkImageCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.imageType = vk.VK_IMAGE_TYPE_2D,
.extent = .{
.width = self.width,
.height = self.height,
.depth = 1,
},
.mipLevels = self.mip_levels,
.arrayLayers = 1,
.format = self.format,
.tiling = vk.VK_IMAGE_TILING_OPTIMAL,
.initialLayout = vk.VK_IMAGE_LAYOUT_UNDEFINED,
.usage = usage,
.samples = vk.VK_SAMPLE_COUNT_1_BIT,
.sharingMode = vk.VK_SHARING_MODE_EXCLUSIVE,
};
if (vk.vkCreateImage(device.raw, &image_info, null, &self.raw) != vk.VK_SUCCESS) {
return error.FailedToCreateImage;
}
// --- MEMORY ---
var mem_req: vk.VkMemoryRequirements = .{};
vk.vkGetImageMemoryRequirements(device.raw, self.raw, &mem_req);
const mem_type = findMemoryType(adapter.raw, mem_req.memoryTypeBits, vk.VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) orelse {
return error.FailedToFindSuitableMemoryType;
};
const alloc_info: vk.VkMemoryAllocateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = mem_req.size,
.memoryTypeIndex = mem_type,
};
if (vk.vkAllocateMemory(device.raw, &alloc_info, null, &self.memory) != vk.VK_SUCCESS) {
return error.FailedToAllocateImageMemory;
}
_ = vk.vkBindImageMemory(device.raw, self.raw, self.memory, 0);
// --- UPLOAD ---
try uploadPixels(&self, pixels);
// --- DEFAULT IMAGE VIEW ---
var view_create_info: vk.VkImageViewCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = self.raw,
.viewType = vk.VK_IMAGE_VIEW_TYPE_2D,
.format = self.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 = self.mip_levels,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
if (vk.vkCreateImageView(device.raw, &view_create_info, null, &self.raw_view) != vk.VK_SUCCESS) {
return error.FailedToCreateImageView;
}
return self;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn deinit(self: *Self) void {
if (self.raw_view) |view| {
vk.vkDestroyImageView(self.device.raw, view, null);
}
if (self.memory) |memory| {
vk.vkFreeMemory(self.device.raw, memory, null);
}
if (self.raw) |raw| {
vk.vkDestroyImage(self.device.raw, raw, null);
}
}
/// ----------------------------------------------------
/// Copy pixels into the image through a one-shot staging upload
/// ----------------------------------------------------
fn uploadPixels(self: *Self, pixels: []const u8) !void {
const device = self.device;
// --- STAGING BUFFER ---
const buffer_info: vk.VkBufferCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = pixels.len,
.usage = vk.VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
.sharingMode = vk.VK_SHARING_MODE_EXCLUSIVE,
};
var staging: vk.VkBuffer = null;
if (vk.vkCreateBuffer(device.raw, &buffer_info, null, &staging) != vk.VK_SUCCESS) {
return error.FailedToCreateStagingBuffer;
}
defer vk.vkDestroyBuffer(device.raw, staging, null);
var mem_req: vk.VkMemoryRequirements = .{};
vk.vkGetBufferMemoryRequirements(device.raw, staging, &mem_req);
const mem_type = findMemoryType(self.adapter.raw, mem_req.memoryTypeBits, vk.VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | vk.VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) orelse {
return error.FailedToFindSuitableMemoryType;
};
var staging_memory: vk.VkDeviceMemory = null;
const alloc_info: vk.VkMemoryAllocateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = mem_req.size,
.memoryTypeIndex = mem_type,
};
if (vk.vkAllocateMemory(device.raw, &alloc_info, null, &staging_memory) != vk.VK_SUCCESS) {
return error.FailedToAllocateStagingMemory;
}
defer vk.vkFreeMemory(device.raw, staging_memory, null);
_ = vk.vkBindBufferMemory(device.raw, staging, staging_memory, 0);
// --- COPY PIXELS INTO STAGING ---
var mapped: ?*anyopaque = null;
if (vk.vkMapMemory(device.raw, staging_memory, 0, pixels.len, 0, &mapped) != vk.VK_SUCCESS) {
return error.FailedToMapMemory;
}
const dst: [*]u8 = @ptrCast(mapped.?);
@memcpy(dst[0..pixels.len], pixels);
vk.vkUnmapMemory(device.raw, staging_memory);
// --- ONE-SHOT COMMAND BUFFER ---
var cmd: vk.VkCommandBuffer = null;
const cmd_alloc_info: vk.VkCommandBufferAllocateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = device.graphics_command_pool,
.level = vk.VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = 1,
};
if (vk.vkAllocateCommandBuffers(device.raw, &cmd_alloc_info, &cmd) != vk.VK_SUCCESS) {
return error.FailedToAllocateCommandBuffers;
}
defer vk.vkFreeCommandBuffers(device.raw, device.graphics_command_pool, 1, &cmd);
const begin_info: vk.VkCommandBufferBeginInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = vk.VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
};
if (vk.vkBeginCommandBuffer(cmd, &begin_info) != vk.VK_SUCCESS) {
return error.FailedToBeginCommandBuffer;
}
// --- UNDEFINED -> TRANSFER_DST_OPTIMAL ---
const pre_barrier: vk.VkImageMemoryBarrier = .{
.sType = vk.VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = 0,
.dstAccessMask = vk.VK_ACCESS_TRANSFER_WRITE_BIT,
.oldLayout = vk.VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = vk.VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.srcQueueFamilyIndex = vk.VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = vk.VK_QUEUE_FAMILY_IGNORED,
.image = self.raw,
.subresourceRange = .{
.aspectMask = vk.VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = self.mip_levels,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
vk.vkCmdPipelineBarrier(
cmd,
vk.VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
vk.VK_PIPELINE_STAGE_TRANSFER_BIT,
0,
0,
null,
0,
null,
1,
&pre_barrier,
);
// --- COPY BUFFER -> IMAGE (level 0) ---
const region: vk.VkBufferImageCopy = .{
.bufferOffset = 0,
.bufferRowLength = 0,
.bufferImageHeight = 0,
.imageSubresource = .{
.aspectMask = vk.VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.imageOffset = .{ .x = 0, .y = 0, .z = 0 },
.imageExtent = .{
.width = self.width,
.height = self.height,
.depth = 1,
},
};
vk.vkCmdCopyBufferToImage(cmd, staging, self.raw, vk.VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
// --- TRANSFER_DST_OPTIMAL -> SHADER_READ_ONLY_OPTIMAL ---
const post_barrier: vk.VkImageMemoryBarrier = .{
.sType = vk.VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = vk.VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = vk.VK_ACCESS_SHADER_READ_BIT,
.oldLayout = vk.VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.newLayout = vk.VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.srcQueueFamilyIndex = vk.VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = vk.VK_QUEUE_FAMILY_IGNORED,
.image = self.raw,
.subresourceRange = .{
.aspectMask = vk.VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = self.mip_levels,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
vk.vkCmdPipelineBarrier(
cmd,
vk.VK_PIPELINE_STAGE_TRANSFER_BIT,
vk.VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
0,
0,
null,
0,
null,
1,
&post_barrier,
);
if (vk.vkEndCommandBuffer(cmd) != vk.VK_SUCCESS) {
return error.FailedToEndCommandBuffer;
}
// --- SUBMIT + WAIT ---
const submit_info: vk.VkSubmitInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_SUBMIT_INFO,
.commandBufferCount = 1,
.pCommandBuffers = &cmd,
};
if (vk.vkQueueSubmit(device.graphics_queue, 1, &submit_info, null) != vk.VK_SUCCESS) {
return error.FailedToSubmitCommandBuffer;
}
if (vk.vkQueueWaitIdle(device.graphics_queue) != vk.VK_SUCCESS) {
return error.FailedToWaitForQueue;
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
fn findMemoryType(
physical_device: vk.VkPhysicalDevice,
type_bits: u32,
properties: vk.VkMemoryPropertyFlags,
) ?u32 {
var mem_props: vk.VkPhysicalDeviceMemoryProperties = .{};
vk.vkGetPhysicalDeviceMemoryProperties(physical_device, &mem_props);
for (0..mem_props.memoryTypeCount) |i| {
if ((type_bits & (@as(u32, 1) << @intCast(i))) != 0 and
(mem_props.memoryTypes[i].propertyFlags & properties) == properties)
{
return @intCast(i);
}
}
return null;
}

View file

@ -5,13 +5,19 @@
const std = @import("std"); const std = @import("std");
const vk = @import("vulkan"); const vk = @import("vulkan");
const gtl = @import("gtl"); const gtl = @import("gtl");
const common = @import("../../common.zig");
const VulkanRHI = @import("../vulkan.zig"); const VulkanRHI = @import("../vulkan.zig");
const Self = @This(); const Self = @This();
const Device = @import("device.zig"); const Device = @import("device.zig");
const Swapchain = @import("swapchain.zig"); const Swapchain = @import("swapchain.zig");
const Config = @import("../../root.zig").PipelineConfig; const VKTexture = @import("texture.zig");
const UniformDesc = @import("../../root.zig").UniformDesc; const Config = common.PipelineConfig;
const UniformDesc = common.UniformDesc;
const MAX_FRAMES_IN_FLIGHT = VulkanRHI.MAX_FRAMES_IN_FLIGHT;
const MAX_BINDINGS = 16;
const MAX_TEXTURES = 16;
const MAX_WRITES = MAX_BINDINGS;
// //
// FIELDS // FIELDS
@ -23,12 +29,14 @@ device: *const Device,
descriptor_set_layout: vk.VkDescriptorSetLayout = null, descriptor_set_layout: vk.VkDescriptorSetLayout = null,
descriptor_pool: vk.VkDescriptorPool = null, descriptor_pool: vk.VkDescriptorPool = null,
descriptor_set: vk.VkDescriptorSet = null, descriptor_sets: [MAX_FRAMES_IN_FLIGHT]vk.VkDescriptorSet = .{null} ** MAX_FRAMES_IN_FLIGHT,
uniform_buffer: vk.VkBuffer = null, uniform_buffers: [MAX_FRAMES_IN_FLIGHT]vk.VkBuffer = .{null} ** MAX_FRAMES_IN_FLIGHT,
uniform_memory: vk.VkDeviceMemory = null, uniform_memories: [MAX_FRAMES_IN_FLIGHT]vk.VkDeviceMemory = .{null} ** MAX_FRAMES_IN_FLIGHT,
uniforms: []UniformDesc, uniforms: []UniformDesc,
vulkan: *VulkanRHI,
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
/// ---------------------------------------------------- /// ----------------------------------------------------
@ -43,6 +51,7 @@ pub fn init(
var self: Self = .{ var self: Self = .{
.device = device, .device = device,
.uniforms = &.{}, .uniforms = &.{},
.vulkan = vulkan,
.alloc = alloc, .alloc = alloc,
}; };
errdefer self.deinit(); errdefer self.deinit();
@ -67,20 +76,38 @@ pub fn init(
var set_layout: vk.VkDescriptorSetLayout = null; var set_layout: vk.VkDescriptorSetLayout = null;
if (uniform_size > 0) { const texture_count = config.textures.len;
const bindings = [_]vk.VkDescriptorSetLayoutBinding{ const has_descriptors = uniform_size > 0 or texture_count > 0;
.{
if (has_descriptors) {
var bindings: [MAX_BINDINGS]vk.VkDescriptorSetLayoutBinding = undefined;
var binding_count: u32 = 0;
if (uniform_size > 0) {
bindings[binding_count] = .{
.binding = 0, .binding = 0,
.descriptorType = vk.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .descriptorType = vk.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = 1, .descriptorCount = 1,
.stageFlags = vk.VK_SHADER_STAGE_VERTEX_BIT | vk.VK_SHADER_STAGE_FRAGMENT_BIT, .stageFlags = vk.VK_SHADER_STAGE_VERTEX_BIT | vk.VK_SHADER_STAGE_FRAGMENT_BIT,
.pImmutableSamplers = null, .pImmutableSamplers = null,
}, };
}; binding_count += 1;
}
for (0..texture_count) |i| {
bindings[binding_count] = .{
.binding = @intCast(i + 1),
.descriptorType = vk.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = 1,
.stageFlags = vk.VK_SHADER_STAGE_FRAGMENT_BIT,
.pImmutableSamplers = null,
};
binding_count += 1;
}
var set_layout_info: vk.VkDescriptorSetLayoutCreateInfo = .{ var set_layout_info: vk.VkDescriptorSetLayoutCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, .sType = vk.VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = bindings.len, .bindingCount = binding_count,
.pBindings = &bindings, .pBindings = &bindings,
}; };
@ -253,6 +280,17 @@ pub fn init(
.alphaBlendOp = vk.VK_BLEND_OP_ADD, .alphaBlendOp = vk.VK_BLEND_OP_ADD,
}; };
switch (config.blend_mode) {
.none => {},
.alpha => {
color_blend_attachment.blendEnable = vk.VK_TRUE;
color_blend_attachment.srcColorBlendFactor = vk.VK_BLEND_FACTOR_SRC_ALPHA;
color_blend_attachment.dstColorBlendFactor = vk.VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
color_blend_attachment.srcAlphaBlendFactor = vk.VK_BLEND_FACTOR_ONE;
color_blend_attachment.dstAlphaBlendFactor = vk.VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
},
}
var color_blending: vk.VkPipelineColorBlendStateCreateInfo = .{ var color_blending: vk.VkPipelineColorBlendStateCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, .sType = vk.VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.logicOpEnable = vk.VK_FALSE, .logicOpEnable = vk.VK_FALSE,
@ -298,65 +336,38 @@ pub fn init(
// UNIFORM BUFFER + DESCRIPTORS // UNIFORM BUFFER + DESCRIPTORS
// //
if (uniform_size > 0) { if (has_descriptors) {
var buffer_info: vk.VkBufferCreateInfo = .{ // --- RESOLVE TEXTURES ---
.sType = vk.VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, var textures: [MAX_TEXTURES]*VKTexture = undefined;
.size = uniform_size, for (config.textures, 0..) |handle, i| {
.usage = vk.VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, textures[i] = vulkan.resource.textures.get(handle) orelse return error.TextureNotFound;
.sharingMode = vk.VK_SHARING_MODE_EXCLUSIVE,
};
var uniform_buffer: vk.VkBuffer = null;
if (vk.vkCreateBuffer(device.raw, &buffer_info, null, &uniform_buffer) != vk.VK_SUCCESS) {
return error.FailedToCreateUniformBuffer;
} }
self.uniform_buffer = uniform_buffer.?;
var mem_req: vk.VkMemoryRequirements = .{};
var mem_props: vk.VkPhysicalDeviceMemoryProperties = .{};
vk.vkGetBufferMemoryRequirements(device.raw, uniform_buffer, &mem_req);
vk.vkGetPhysicalDeviceMemoryProperties(device.physical_device, &mem_props);
var mem_type: u32 = 0;
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,
};
var uniform_memory: vk.VkDeviceMemory = null;
if (vk.vkAllocateMemory(device.raw, &alloc_info, null, &uniform_memory) != vk.VK_SUCCESS) {
return error.FailedToAllocateUniformMemory;
}
self.uniform_memory = uniform_memory;
_ = vk.vkBindBufferMemory(device.raw, uniform_buffer, uniform_memory, 0);
// --- DESCRIPTOR POOL --- // --- DESCRIPTOR POOL ---
const pool_sizes = [_]vk.VkDescriptorPoolSize{ var pool_sizes: [2]vk.VkDescriptorPoolSize = undefined;
.{ var pool_size_count: u32 = 0;
if (uniform_size > 0) {
pool_sizes[pool_size_count] = .{
.type = vk.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .type = vk.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = 1, .descriptorCount = @intCast(MAX_FRAMES_IN_FLIGHT),
}, };
}; pool_size_count += 1;
}
if (texture_count > 0) {
pool_sizes[pool_size_count] = .{
.type = vk.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = @intCast(MAX_FRAMES_IN_FLIGHT * texture_count),
};
pool_size_count += 1;
}
var pool_info: vk.VkDescriptorPoolCreateInfo = .{ var pool_info: vk.VkDescriptorPoolCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, .sType = vk.VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.poolSizeCount = pool_sizes.len, .poolSizeCount = pool_size_count,
.pPoolSizes = &pool_sizes, .pPoolSizes = &pool_sizes,
.maxSets = 1, .maxSets = @intCast(MAX_FRAMES_IN_FLIGHT),
}; };
var descriptor_pool: vk.VkDescriptorPool = null; var descriptor_pool: vk.VkDescriptorPool = null;
@ -365,36 +376,113 @@ pub fn init(
} }
self.descriptor_pool = descriptor_pool.?; self.descriptor_pool = descriptor_pool.?;
// --- DESCRIPTOR SET --- // --- PER-FRAME UNIFORM BUFFER + DESCRIPTOR SET ---
var set_alloc_info: vk.VkDescriptorSetAllocateInfo = .{ for (0..MAX_FRAMES_IN_FLIGHT) |frame| {
.sType = vk.VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, if (uniform_size > 0) {
.descriptorPool = descriptor_pool, var buffer_info: vk.VkBufferCreateInfo = .{
.descriptorSetCount = 1, .sType = vk.VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pSetLayouts = @ptrCast(&self.descriptor_set_layout), .size = uniform_size,
}; .usage = vk.VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
.sharingMode = vk.VK_SHARING_MODE_EXCLUSIVE,
};
if (vk.vkAllocateDescriptorSets(device.raw, &set_alloc_info, &self.descriptor_set) != vk.VK_SUCCESS) { var uniform_buffer: vk.VkBuffer = null;
return error.FailedToAllocateDescriptorSets; if (vk.vkCreateBuffer(device.raw, &buffer_info, null, &uniform_buffer) != vk.VK_SUCCESS) {
return error.FailedToCreateUniformBuffer;
}
self.uniform_buffers[frame] = uniform_buffer.?;
var mem_req: vk.VkMemoryRequirements = .{};
var mem_props: vk.VkPhysicalDeviceMemoryProperties = .{};
vk.vkGetBufferMemoryRequirements(device.raw, uniform_buffer, &mem_req);
vk.vkGetPhysicalDeviceMemoryProperties(device.physical_device, &mem_props);
var mem_type: u32 = 0;
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,
};
var uniform_memory: vk.VkDeviceMemory = null;
if (vk.vkAllocateMemory(device.raw, &alloc_info, null, &uniform_memory) != vk.VK_SUCCESS) {
return error.FailedToAllocateUniformMemory;
}
self.uniform_memories[frame] = uniform_memory;
_ = vk.vkBindBufferMemory(device.raw, uniform_buffer, uniform_memory, 0);
}
// --- DESCRIPTOR SET ---
var set_alloc_info: vk.VkDescriptorSetAllocateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.descriptorPool = descriptor_pool,
.descriptorSetCount = 1,
.pSetLayouts = @ptrCast(&self.descriptor_set_layout),
};
if (vk.vkAllocateDescriptorSets(device.raw, &set_alloc_info, &self.descriptor_sets[frame]) != vk.VK_SUCCESS) {
return error.FailedToAllocateDescriptorSets;
}
// --- WRITE DESCRIPTOR SET ---
var writes: [MAX_WRITES]vk.VkWriteDescriptorSet = undefined;
var write_count: u32 = 0;
var buffer_descriptor: vk.VkDescriptorBufferInfo = undefined;
var image_descriptors: [MAX_TEXTURES]vk.VkDescriptorImageInfo = undefined;
if (uniform_size > 0) {
buffer_descriptor = .{
.buffer = self.uniform_buffers[frame],
.offset = 0,
.range = uniform_size,
};
writes[write_count] = .{
.sType = vk.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = self.descriptor_sets[frame],
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = vk.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.pBufferInfo = &buffer_descriptor,
};
write_count += 1;
}
for (textures[0..texture_count], 0..) |texture, i| {
image_descriptors[i] = .{
.imageView = texture.image.raw_view,
.sampler = texture.sampler.raw,
.imageLayout = vk.VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
};
writes[write_count] = .{
.sType = vk.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = self.descriptor_sets[frame],
.dstBinding = @intCast(i + 1),
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = vk.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.pImageInfo = &image_descriptors[i],
};
write_count += 1;
}
vk.vkUpdateDescriptorSets(device.raw, write_count, &writes, 0, null);
} }
// --- WRITE DESCRIPTOR SET ---
const buffer_descriptor: vk.VkDescriptorBufferInfo = .{
.buffer = uniform_buffer,
.offset = 0,
.range = uniform_size,
};
const write_descriptor: vk.VkWriteDescriptorSet = .{
.sType = vk.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = self.descriptor_set,
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = vk.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.pBufferInfo = &buffer_descriptor,
};
vk.vkUpdateDescriptorSets(device.raw, 1, &write_descriptor, 0, null);
} }
return self; return self;
@ -409,11 +497,15 @@ pub fn deinit(self: *Self) void {
if (self.descriptor_pool) |pool| { if (self.descriptor_pool) |pool| {
vk.vkDestroyDescriptorPool(self.device.raw, pool, null); vk.vkDestroyDescriptorPool(self.device.raw, pool, null);
} }
if (self.uniform_memory) |memory| { for (&self.uniform_memories) |memory| {
vk.vkFreeMemory(self.device.raw, memory, null); if (memory) |m| {
vk.vkFreeMemory(self.device.raw, m, null);
}
} }
if (self.uniform_buffer) |buffer| { for (&self.uniform_buffers) |buffer| {
vk.vkDestroyBuffer(self.device.raw, buffer, null); if (buffer) |b| {
vk.vkDestroyBuffer(self.device.raw, b, null);
}
} }
if (self.layout) |layout| { if (self.layout) |layout| {
vk.vkDestroyPipelineLayout(self.device.raw, layout, null); vk.vkDestroyPipelineLayout(self.device.raw, layout, null);
@ -428,6 +520,7 @@ pub fn deinit(self: *Self) void {
/// Set a uniform value by name /// Set a uniform value by name
/// ---------------------------------------------------- /// ----------------------------------------------------
pub fn setUniform(self: *Self, name: []const u8, value: anytype) !void { pub fn setUniform(self: *Self, name: []const u8, value: anytype) !void {
const uniform_memory = self.uniform_memories[self.vulkan.current_frame];
for (self.uniforms) |uniform| { for (self.uniforms) |uniform| {
if (std.mem.eql(u8, uniform.name, name)) { if (std.mem.eql(u8, uniform.name, name)) {
if (@sizeOf(@TypeOf(value)) > uniform.size) { if (@sizeOf(@TypeOf(value)) > uniform.size) {
@ -437,7 +530,7 @@ pub fn setUniform(self: *Self, name: []const u8, value: anytype) !void {
var mapped: ?*anyopaque = null; var mapped: ?*anyopaque = null;
if (vk.vkMapMemory( if (vk.vkMapMemory(
self.device.raw, self.device.raw,
self.uniform_memory, uniform_memory,
uniform.offset, uniform.offset,
uniform.size, uniform.size,
0, 0,
@ -450,7 +543,7 @@ pub fn setUniform(self: *Self, name: []const u8, value: anytype) !void {
const src: [*]const u8 = @ptrCast(&value); const src: [*]const u8 = @ptrCast(&value);
@memcpy(dst[0..@sizeOf(@TypeOf(value))], src[0..@sizeOf(@TypeOf(value))]); @memcpy(dst[0..@sizeOf(@TypeOf(value))], src[0..@sizeOf(@TypeOf(value))]);
vk.vkUnmapMemory(self.device.raw, self.uniform_memory); vk.vkUnmapMemory(self.device.raw, uniform_memory);
return; return;
} }
} }

View file

@ -0,0 +1,85 @@
//! ----------------------------------------------------
//! `🗲` Vulkan Sampler `🗲`
//! ----------------------------------------------------
const std = @import("std");
const vk = @import("vulkan");
const common = @import("../../common.zig");
const Self = @This();
const Device = @import("device.zig");
const SamplerConfig = common.SamplerConfig;
//
// FIELDS
//
raw: *vk.VkSampler_T,
device: *const Device,
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn init(
config: SamplerConfig,
device: *const Device,
) !Self {
var create_info: vk.VkSamplerCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.magFilter = switch (config.mag_filter) {
.nearest => vk.VK_FILTER_NEAREST,
.linear => vk.VK_FILTER_LINEAR,
},
.minFilter = switch (config.min_filter) {
.nearest => vk.VK_FILTER_NEAREST,
.linear => vk.VK_FILTER_LINEAR,
},
.mipmapMode = switch (config.mipmap_mode) {
.nearest => vk.VK_SAMPLER_MIPMAP_MODE_NEAREST,
.linear => vk.VK_SAMPLER_MIPMAP_MODE_LINEAR,
},
.addressModeU = switch (config.address_u) {
.repeat => vk.VK_SAMPLER_ADDRESS_MODE_REPEAT,
.mirrored_repeat => vk.VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT,
.clamp_to_edge => vk.VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
.clamp_to_border => vk.VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
},
.addressModeV = switch (config.address_v) {
.repeat => vk.VK_SAMPLER_ADDRESS_MODE_REPEAT,
.mirrored_repeat => vk.VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT,
.clamp_to_edge => vk.VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
.clamp_to_border => vk.VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
},
.addressModeW = switch (config.address_w) {
.repeat => vk.VK_SAMPLER_ADDRESS_MODE_REPEAT,
.mirrored_repeat => vk.VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT,
.clamp_to_edge => vk.VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
.clamp_to_border => vk.VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
},
.mipLodBias = 0.0,
.anisotropyEnable = if (config.max_anisotropy > 1.0) vk.VK_TRUE else vk.VK_FALSE,
.maxAnisotropy = config.max_anisotropy,
.compareEnable = vk.VK_FALSE,
.compareOp = vk.VK_COMPARE_OP_ALWAYS,
.minLod = 0.0,
.maxLod = if (config.max_lod > 0) config.max_lod else vk.VK_LOD_CLAMP_NONE,
.borderColor = vk.VK_BORDER_COLOR_INT_OPAQUE_BLACK,
.unnormalizedCoordinates = vk.VK_FALSE,
};
var sampler: vk.VkSampler = null;
if (vk.vkCreateSampler(device.raw, &create_info, null, &sampler) != vk.VK_SUCCESS) {
return error.FailedToCreateSampler;
}
return .{
.raw = sampler.?,
.device = device,
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn deinit(self: *Self) void {
vk.vkDestroySampler(self.device.raw, self.raw, null);
}

View file

@ -5,7 +5,7 @@
const std = @import("std"); const std = @import("std");
const vk = @import("vulkan"); const vk = @import("vulkan");
const Instance = @import("../instance.zig"); const Instance = @import("../instance.zig");
const Window = @import("../../root.zig").Window; const Window = @import("../../common.zig").Window;
const Self = @This(); const Self = @This();
// //

View file

@ -0,0 +1,44 @@
//! ----------------------------------------------------
//! `🗲` Vulkan Texture `🗲`
//! ----------------------------------------------------
const vk = @import("vulkan");
const common = @import("../../common.zig");
const Self = @This();
const Adapter = @import("adapter.zig");
const Device = @import("device.zig");
const VKImage = @import("image.zig");
const VKSampler = @import("sampler.zig");
const TextureConfig = common.TextureConfig;
//
// FIELDS
//
image: VKImage,
sampler: VKSampler,
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn init(
config: TextureConfig,
adapter: *const Adapter,
device: *const Device,
) !Self {
var image = try VKImage.init(config.image, adapter, device);
errdefer image.deinit();
return .{
.image = image,
.sampler = try VKSampler.init(config.sampler, device),
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn deinit(self: *Self) void {
self.sampler.deinit();
self.image.deinit();
}

View file

@ -1,53 +0,0 @@
//! ----------------------------------------------------
//! `🗲` 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

@ -5,7 +5,7 @@
const std = @import("std"); const std = @import("std");
const vk = @import("vulkan"); const vk = @import("vulkan");
const gtl = @import("gtl"); const gtl = @import("gtl");
const common = @import("../root.zig"); const common = @import("../common.zig");
const Instance = @import("instance.zig"); const Instance = @import("instance.zig");
const Resource = @import("resource.zig"); const Resource = @import("resource.zig");
@ -22,6 +22,9 @@ const BufferSharingMode = common.BufferSharingMode;
const AdapterConfig = common.AdapterConfig; const AdapterConfig = common.AdapterConfig;
const RenderPassConfig = common.RenderPassConfig; const RenderPassConfig = common.RenderPassConfig;
const PipelineConfig = common.PipelineConfig; const PipelineConfig = common.PipelineConfig;
const ImageConfig = common.ImageConfig;
const SamplerConfig = common.SamplerConfig;
const TextureConfig = common.TextureConfig;
// --- HANDLES --- // --- HANDLES ---
const Adapter = common.Adapter; const Adapter = common.Adapter;
@ -33,8 +36,13 @@ const Buffer = common.Buffer;
const Shader = common.Shader; const Shader = common.Shader;
const Pipeline = common.Pipeline; const Pipeline = common.Pipeline;
const Image = common.Image;
const ImageView = common.ImageView;
const Sampler = common.Sampler;
const Texture = common.Texture;
// --- # --- // --- # ---
const MAX_FRAMES_IN_FLIGHT: usize = 3; pub const MAX_FRAMES_IN_FLIGHT: usize = 3;
/// ---------------------------------------------------- /// ----------------------------------------------------
/// ---------------------------------------------------- /// ----------------------------------------------------
@ -116,6 +124,18 @@ pub fn makeAdapter(self: *Self, config: AdapterConfig) !Handle(Adapter) {
return try self.resource.adapters.put(try .init(self, config)); return try self.resource.adapters.put(try .init(self, config));
} }
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn releaseAdapter(self: *Self, adapter: Handle(Adapter)) void {
if (self.resource.adapters.get(adapter)) |_| {}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn deleteAdapter(self: *Self, adapter: Handle(Adapter)) void {
self.resource.adapters.remove(adapter) catch unreachable;
}
/// ---------------------------------------------------- /// ----------------------------------------------------
/// ---------------------------------------------------- /// ----------------------------------------------------
pub fn makeDevice( pub fn makeDevice(
@ -130,13 +150,28 @@ pub fn makeDevice(
/// ---------------------------------------------------- /// ----------------------------------------------------
/// ---------------------------------------------------- /// ----------------------------------------------------
pub fn destroyDevice(self: *Self, device: Handle(Device)) !void { pub fn deleteDevice(self: *Self, device: Handle(Device)) void {
if (self.resource.devices.get(device)) |raw| { if (self.resource.devices.get(device)) |raw| {
raw.deinit(); raw.deinit();
try self.resource.devices.remove(device); self.resource.devices.remove(device) catch unreachable;
} }
} }
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn releaseDevice(self: *Self, device: Handle(Device)) void {
if (self.resource.devices.get(device)) |raw| {
raw.deinit();
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn getRAWDevice(self: *Self, device: Handle(Device)) ?*vk.VkDevice_T {
if (self.resource.devices.get(device)) |raw| return raw.raw;
return null;
}
/// ---------------------------------------------------- /// ----------------------------------------------------
/// ---------------------------------------------------- /// ----------------------------------------------------
pub fn makeSurface(self: *Self) !Handle(Surface) { pub fn makeSurface(self: *Self) !Handle(Surface) {
@ -183,6 +218,43 @@ pub fn makePipeline(self: *Self, config: PipelineConfig) !Handle(Pipeline) {
return try self.resource.pipelines.put(try .init(raw_swapchain, raw_device, config, self, self.alloc)); return try self.resource.pipelines.put(try .init(raw_swapchain, raw_device, config, self, self.alloc));
} }
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn makeImage(
self: *Self,
config: ImageConfig,
adapter: Handle(Adapter),
device: Handle(Device),
) !Handle(Image) {
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.images.put(try .init(config, raw_adapter, raw_device));
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn makeSampler(
self: *Self,
config: SamplerConfig,
device: Handle(Device),
) !Handle(Sampler) {
const raw_device = self.resource.devices.get(device) orelse return error.DeviceNotFound;
return try self.resource.samplers.put(try .init(config, raw_device));
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn makeTexture(
self: *Self,
config: TextureConfig,
adapter: Handle(Adapter),
device: Handle(Device),
) !Handle(Texture) {
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.textures.put(try .init(config, raw_adapter, raw_device));
}
/// ---------------------------------------------------- /// ----------------------------------------------------
/// ---------------------------------------------------- /// ----------------------------------------------------
pub fn makeBuffer( pub fn makeBuffer(
@ -198,6 +270,24 @@ pub fn makeBuffer(
return try self.resource.buffers.put(try .init(size, usage, sharing, raw_adapter, raw_device)); return try self.resource.buffers.put(try .init(size, usage, sharing, raw_adapter, raw_device));
} }
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn bindBuffer(self: *Self, buffer: Handle(Buffer)) !void {
const raw = self.resource.buffers.get(buffer) orelse return error.BufferNotFound;
const frame = self.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),
.image => {}, // TODO:
}
}
}
// //
// FRAME + RENDERING // FRAME + RENDERING
// //
@ -212,7 +302,7 @@ pub fn beginFrame(
const raw_device = self.resource.devices.get(device) orelse return error.DeviceNotFound; 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 raw_swapchain = self.resource.swapchains.get(swapchain) orelse return error.SwapchainNotFound;
try self.ensureFrameSync(raw_device.raw, raw_swapchain.raw); try self.ensureFrameSync(raw_device.raw.?, raw_swapchain.raw);
const frame = self.currentFrame(); const frame = self.currentFrame();
@ -504,7 +594,7 @@ pub fn bindPipeline(self: *Self, pipeline: Handle(Pipeline)) !void {
const raw_pipeline = self.resource.pipelines.get(pipeline) orelse return error.PipelineNotFound; const raw_pipeline = self.resource.pipelines.get(pipeline) orelse return error.PipelineNotFound;
const frame = self.currentFrame(); const frame = self.currentFrame();
vk.vkCmdBindPipeline(frame.cmd_buf, vk.VK_PIPELINE_BIND_POINT_GRAPHICS, raw_pipeline.raw.?); vk.vkCmdBindPipeline(frame.cmd_buf, vk.VK_PIPELINE_BIND_POINT_GRAPHICS, raw_pipeline.raw.?);
if (raw_pipeline.descriptor_set) |set| { if (raw_pipeline.descriptor_sets[self.current_frame]) |set| {
vk.vkCmdBindDescriptorSets( vk.vkCmdBindDescriptorSets(
frame.cmd_buf, frame.cmd_buf,
vk.VK_PIPELINE_BIND_POINT_GRAPHICS, vk.VK_PIPELINE_BIND_POINT_GRAPHICS,
@ -527,10 +617,19 @@ pub fn setUniform(self: *Self, pipeline: Handle(Pipeline), name: []const u8, val
/// ---------------------------------------------------- /// ----------------------------------------------------
/// ---------------------------------------------------- /// ----------------------------------------------------
pub fn draw(self: *Self, vertex_count: u32, instance_count: u32, first_vertex: u32, first_instance: u32) !void { 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); vk.vkCmdDraw(self.currentFrame().cmd_buf, 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 {
const frame = self.currentFrame();
if (frame.cmd_buf) |cmd| {
vk.vkCmdDrawIndexed(cmd, index_count, instance_count, first_index, vertex_offset, first_instance);
}
}
/// ---------------------------------------------------- /// ----------------------------------------------------
/// ---------------------------------------------------- /// ----------------------------------------------------
pub fn currentFrame(self: *Self) *Frame { pub fn currentFrame(self: *Self) *Frame {

View file

@ -1 +0,0 @@
#include <SDL3/SDL.h>

2
src/vendor/stb_image/stb_image.c vendored Normal file
View file

@ -0,0 +1,2 @@
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

7988
src/vendor/stb_image/stb_image.h vendored Normal file

File diff suppressed because it is too large Load diff

Binary file not shown.

1
zls.json Normal file
View file

@ -0,0 +1 @@
{ "enable_build_on_save": true, "build_on_save_step": "check" }