Let there be images
This commit is contained in:
parent
ce1b032361
commit
655dca998b
31 changed files with 9134 additions and 213 deletions
67
README.md
67
README.md
|
|
@ -10,6 +10,7 @@ Low cortisol crossplatform RHI (Rendering Hardware Interface)
|
|||
- Fire and forget
|
||||
- Dead simple + Performance + Flexible
|
||||
- Handles
|
||||
- Auto managed
|
||||
|
||||
## Architecture
|
||||
```text
|
||||
|
|
@ -27,6 +28,72 @@ src/rhi/vulkan/vulkan.zig
|
|||
src/rhi/vulkan/resource.zig
|
||||
└─ 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
|
||||
- ```zig build run-simple```
|
||||
|
|
|
|||
11
build.zig
11
build.zig
|
|
@ -50,6 +50,14 @@ pub fn build(b: *std.Build) !void {
|
|||
mod.addImport("vulkan", vulkan.createModule());
|
||||
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
|
||||
//
|
||||
|
|
@ -107,5 +115,8 @@ pub fn build(b: *std.Build) !void {
|
|||
|
||||
const example_build_step = b.addInstallArtifact(example, .{});
|
||||
example_step.dependOn(&example_build_step.step);
|
||||
|
||||
const check_step = b.step("check", "Check if compiles");
|
||||
check_step.dependOn(&example.step);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +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
|
||||
glslc examples/assets/shaders/simple.vert -o examples/assets/shaders/compiled/simple.vert.spv
|
||||
glslc examples/assets/shaders/simple.frag -o examples/assets/shaders/compiled/simple.frag.spv
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -1,11 +1,25 @@
|
|||
#version 450
|
||||
|
||||
//
|
||||
// IO
|
||||
//
|
||||
|
||||
layout(location = 0) in vec2 in_UV;
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
//
|
||||
// UNIFORMS
|
||||
//
|
||||
|
||||
layout(binding = 0) uniform UnlitMaterial {
|
||||
vec4 albedo;
|
||||
} material;
|
||||
|
||||
layout(location = 0) out vec4 outColor;
|
||||
layout(binding = 1) uniform sampler2D albedo_texture;
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
void main() {
|
||||
outColor = material.albedo;
|
||||
out_color = texture(albedo_texture, in_UV);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,18 @@
|
|||
#version 450
|
||||
|
||||
layout(binding = 0) uniform UnlitMaterial {
|
||||
vec4 albedo;
|
||||
} material;
|
||||
//
|
||||
// IO
|
||||
//
|
||||
|
||||
layout(location = 0) in vec2 inPosition;
|
||||
layout(location = 1) in vec2 inNormal;
|
||||
layout(location = 2) in vec2 inUV;
|
||||
layout(location = 0) in vec2 in_position;
|
||||
layout(location = 1) in vec2 in_normal;
|
||||
layout(location = 2) in vec2 in_UV;
|
||||
|
||||
layout(location = 0) out vec2 out_UV;
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
void main() {
|
||||
gl_Position = vec4(inPosition, 0.0, 1.0);
|
||||
gl_Position = vec4(in_position, 0.0, 1.0);
|
||||
out_UV = in_UV;
|
||||
}
|
||||
|
|
|
|||
BIN
examples/assets/textures/NYXWorld.png
Normal file
BIN
examples/assets/textures/NYXWorld.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 316 KiB |
BIN
examples/assets/textures/bully.jpg
Normal file
BIN
examples/assets/textures/bully.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 532 KiB |
BIN
examples/assets/textures/scammer.jpg
Normal file
BIN
examples/assets/textures/scammer.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
BIN
examples/assets/textures/texture.jpg
Normal file
BIN
examples/assets/textures/texture.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 75 KiB |
|
|
@ -83,6 +83,12 @@ pub fn main(init: std.process.Init) !void {
|
|||
const device = try gfx.makeDevice(adapter, 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 ---
|
||||
const vbuf = try gfx.makeBuffer(vertices, .vertex, 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) },
|
||||
},
|
||||
|
||||
.textures = &.{texture},
|
||||
|
||||
.blend_mode = .alpha,
|
||||
|
||||
.vert_shader = simple_vert,
|
||||
.frag_shader = simple_frag,
|
||||
|
||||
|
|
|
|||
73
src/gfx.zig
73
src/gfx.zig
|
|
@ -6,7 +6,7 @@
|
|||
const std = @import("std");
|
||||
const gtl = @import("gtl");
|
||||
const vk = @import("vulkan");
|
||||
const common = @import("rhi/root.zig");
|
||||
const common = @import("rhi/common.zig");
|
||||
const Handle = gtl.Handle;
|
||||
const Self = @This();
|
||||
|
||||
|
|
@ -16,14 +16,20 @@ const RenderPassConfig = common.RenderPassConfig;
|
|||
const PipelineConfig = common.PipelineConfig;
|
||||
const BufferUsage = common.BufferUsage;
|
||||
const Pass = common.Pass;
|
||||
const ImageConfig = common.ImageConfig;
|
||||
const SamplerConfig = common.SamplerConfig;
|
||||
const TextureConfig = common.TextureConfig;
|
||||
|
||||
// --- HANDLES ---
|
||||
const Window = @import("rhi/root.zig").Window;
|
||||
const Window = common.Window;
|
||||
const Adapter = common.Adapter;
|
||||
const Device = common.Device;
|
||||
const Buffer = common.Buffer;
|
||||
const Surface = common.Surface;
|
||||
const Swapchain = common.Swapchain;
|
||||
const Image = common.Image;
|
||||
const Sampler = common.Sampler;
|
||||
const Texture = common.Texture;
|
||||
|
||||
const Shader = common.Shader;
|
||||
const Pipeline = common.Pipeline;
|
||||
|
|
@ -111,10 +117,27 @@ pub fn makeDevice(
|
|||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub fn destroyDevice(self: *Self, device: Handle(Device)) !void {
|
||||
try switch (self.ctx) {
|
||||
.vulkan => |*v| v.destroyDevice(device),
|
||||
};
|
||||
pub fn deleteDevice(self: *Self, device: Handle(Device)) void {
|
||||
switch (self.ctx) {
|
||||
.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(
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//! ----------------------------------------------------
|
||||
//! Shared Handles and Configs across every backend
|
||||
//! ----------------------------------------------------
|
||||
|
||||
const vk = @import("vulkan");
|
||||
const gtl = @import("gtl");
|
||||
const Gfx = @import("../gfx.zig");
|
||||
const Handle = gtl.Handle;
|
||||
|
|
@ -32,7 +32,7 @@ pub const Window = union(enum) {
|
|||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub const GpuType = enum {
|
||||
pub const GPUType = enum {
|
||||
any,
|
||||
discrete,
|
||||
integrated,
|
||||
|
|
@ -44,7 +44,7 @@ pub const GpuType = enum {
|
|||
pub const AdapterConfig = struct {
|
||||
/// Preferred class of GPU
|
||||
/// `.any` disables filtering by class
|
||||
gpu_type: GpuType = .discrete,
|
||||
gpu_type: GPUType = .discrete,
|
||||
|
||||
/// Select the adapter at a specific enumeration index
|
||||
/// `null` picks the first suitable one
|
||||
|
|
@ -110,6 +110,7 @@ pub const PipelineConfig = struct {
|
|||
vertex_attributes: []const VertexAttribute = &.{},
|
||||
|
||||
uniforms: []const UniformDesc = &.{},
|
||||
textures: []const Handle(Texture) = &.{},
|
||||
|
||||
topology: enum {
|
||||
triangle_list,
|
||||
|
|
@ -127,6 +128,11 @@ pub const PipelineConfig = struct {
|
|||
ccw,
|
||||
cw,
|
||||
} = .cw,
|
||||
|
||||
blend_mode: enum {
|
||||
none,
|
||||
alpha,
|
||||
} = .none,
|
||||
};
|
||||
|
||||
/// ----------------------------------------------------
|
||||
|
|
@ -135,6 +141,8 @@ pub const Pass = struct {
|
|||
swapchain: Handle(Swapchain),
|
||||
gfx: *Gfx,
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub fn end(self: *const Pass) void {
|
||||
switch (self.gfx.ctx) {
|
||||
.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 {
|
||||
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 {
|
||||
switch (self.gfx.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);
|
||||
}
|
||||
},
|
||||
.vulkan => |*v| v.drawIndexed(index_count, instance_count, first_index, vertex_offset, first_instance),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -193,6 +196,7 @@ pub const RenderPassConfig = struct {
|
|||
pub const BufferUsage = enum {
|
||||
vertex,
|
||||
index,
|
||||
image,
|
||||
};
|
||||
|
||||
/// ----------------------------------------------------
|
||||
|
|
@ -201,6 +205,84 @@ pub const BufferSharingMode = enum {
|
|||
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
|
||||
//
|
||||
|
|
@ -227,19 +309,7 @@ pub const Buffer = struct {
|
|||
|
||||
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),
|
||||
}
|
||||
}
|
||||
},
|
||||
.vulkan => |*v| try v.bindBuffer(self.handle),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -247,6 +317,7 @@ pub const Buffer = struct {
|
|||
pub const Image = struct {};
|
||||
pub const ImageView = struct {};
|
||||
pub const Sampler = struct {};
|
||||
pub const Texture = struct {};
|
||||
|
||||
pub const Shader = struct {};
|
||||
pub const PipelineLayout = struct {};
|
||||
|
|
@ -6,7 +6,7 @@ const std = @import("std");
|
|||
const gtl = @import("gtl");
|
||||
const vk = @import("vulkan");
|
||||
const Resource = @import("resource.zig");
|
||||
const Window = @import("../root.zig").Window;
|
||||
const Window = @import("../common.zig").Window;
|
||||
const MainConfig = @import("../../gfx.zig").Config;
|
||||
const Self = @This();
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
const vk = @import("vulkan");
|
||||
const std = @import("std");
|
||||
const gtl = @import("gtl");
|
||||
const common = @import("../root.zig");
|
||||
const common = @import("../common.zig");
|
||||
const Instance = @import("instance.zig");
|
||||
const Self = @This();
|
||||
|
||||
|
|
@ -30,17 +30,31 @@ const Pipeline = common.Pipeline;
|
|||
const PipelineLayout = common.PipelineLayout;
|
||||
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
|
||||
//
|
||||
|
||||
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),
|
||||
|
||||
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),
|
||||
pipelines: gtl.ResourceMap(Pipeline, VKPipeline),
|
||||
|
||||
|
|
@ -50,11 +64,16 @@ pub fn init(alloc: std.mem.Allocator) Self {
|
|||
return .{
|
||||
.adapters = .init(alloc),
|
||||
.devices = .init(alloc),
|
||||
.buffers = .init(alloc),
|
||||
|
||||
.surfaces = .init(alloc),
|
||||
.swapchains = .init(alloc),
|
||||
|
||||
.buffers = .init(alloc),
|
||||
.images = .init(alloc),
|
||||
|
||||
.samplers = .init(alloc),
|
||||
.textures = .init(alloc),
|
||||
|
||||
.shaders = .init(alloc),
|
||||
.pipelines = .init(alloc),
|
||||
};
|
||||
|
|
@ -98,6 +117,30 @@ pub fn 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
|
||||
var it = self.buffers.valueIterator();
|
||||
while (it.next()) |v| {
|
||||
|
|
|
|||
|
|
@ -5,9 +5,10 @@
|
|||
const std = @import("std");
|
||||
const gtl = @import("gtl");
|
||||
const vk = @import("vulkan");
|
||||
const common = @import("../../common.zig");
|
||||
const VKBackend = @import("../vulkan.zig");
|
||||
const AdapterConfig = @import("../../root.zig").AdapterConfig;
|
||||
const GpuType = @import("../../root.zig").GpuType;
|
||||
const AdapterConfig = common.AdapterConfig;
|
||||
const GPUType = common.GPUType;
|
||||
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 = .{};
|
||||
vk.vkGetPhysicalDeviceProperties(device, &properties);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ const std = @import("std");
|
|||
const vk = @import("vulkan");
|
||||
const Self = @This();
|
||||
|
||||
const common = @import("../../root.zig");
|
||||
const common = @import("../../common.zig");
|
||||
const Adapter = @import("adapter.zig");
|
||||
const Device = @import("device.zig");
|
||||
const BufferUsage = common.BufferUsage;
|
||||
|
|
@ -38,6 +38,7 @@ pub fn init(
|
|||
.usage = switch (usage) {
|
||||
.vertex => vk.VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
|
||||
.index => vk.VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
|
||||
.image => vk.VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||||
},
|
||||
.sharingMode = switch (sharing) {
|
||||
.exclusive => vk.VK_SHARING_MODE_EXCLUSIVE,
|
||||
|
|
@ -123,7 +124,12 @@ pub fn set(
|
|||
}
|
||||
|
||||
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]);
|
||||
|
||||
|
|
|
|||
|
|
@ -19,11 +19,11 @@ pub const QueueFamilies = struct {
|
|||
// FIELDS
|
||||
//
|
||||
|
||||
raw: *vk.VkDevice_T,
|
||||
raw: vk.VkDevice,
|
||||
physical_device: vk.VkPhysicalDevice,
|
||||
graphics_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);
|
||||
|
||||
return .{
|
||||
.raw = device.?,
|
||||
.raw = device,
|
||||
.physical_device = adapter.raw,
|
||||
.graphics_queue = graphics_queue.?,
|
||||
.present_queue = present_queue.?,
|
||||
.graphics_command_pool = graphics_command_pool.?,
|
||||
.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);
|
||||
if (self.graphics_command_pool) |raw| {
|
||||
vk.vkDestroyCommandPool(self.raw, raw, null);
|
||||
self.graphics_command_pool = null;
|
||||
}
|
||||
|
||||
if (self.raw) |raw| {
|
||||
vk.vkDestroyDevice(raw, null);
|
||||
self.raw = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
|
|
|
|||
367
src/rhi/vulkan/resource/image.zig
Normal file
367
src/rhi/vulkan/resource/image.zig
Normal 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, ®ion);
|
||||
|
||||
// --- 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;
|
||||
}
|
||||
|
|
@ -5,13 +5,19 @@
|
|||
const std = @import("std");
|
||||
const vk = @import("vulkan");
|
||||
const gtl = @import("gtl");
|
||||
const common = @import("../../common.zig");
|
||||
const VulkanRHI = @import("../vulkan.zig");
|
||||
const Self = @This();
|
||||
|
||||
const Device = @import("device.zig");
|
||||
const Swapchain = @import("swapchain.zig");
|
||||
const Config = @import("../../root.zig").PipelineConfig;
|
||||
const UniformDesc = @import("../../root.zig").UniformDesc;
|
||||
const VKTexture = @import("texture.zig");
|
||||
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
|
||||
|
|
@ -23,12 +29,14 @@ device: *const Device,
|
|||
|
||||
descriptor_set_layout: vk.VkDescriptorSetLayout = 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_memory: vk.VkDeviceMemory = null,
|
||||
uniform_buffers: [MAX_FRAMES_IN_FLIGHT]vk.VkBuffer = .{null} ** MAX_FRAMES_IN_FLIGHT,
|
||||
uniform_memories: [MAX_FRAMES_IN_FLIGHT]vk.VkDeviceMemory = .{null} ** MAX_FRAMES_IN_FLIGHT,
|
||||
uniforms: []UniformDesc,
|
||||
|
||||
vulkan: *VulkanRHI,
|
||||
|
||||
alloc: std.mem.Allocator,
|
||||
|
||||
/// ----------------------------------------------------
|
||||
|
|
@ -43,6 +51,7 @@ pub fn init(
|
|||
var self: Self = .{
|
||||
.device = device,
|
||||
.uniforms = &.{},
|
||||
.vulkan = vulkan,
|
||||
.alloc = alloc,
|
||||
};
|
||||
errdefer self.deinit();
|
||||
|
|
@ -67,20 +76,38 @@ pub fn init(
|
|||
|
||||
var set_layout: vk.VkDescriptorSetLayout = null;
|
||||
|
||||
if (uniform_size > 0) {
|
||||
const bindings = [_]vk.VkDescriptorSetLayoutBinding{
|
||||
.{
|
||||
const texture_count = config.textures.len;
|
||||
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,
|
||||
.descriptorType = vk.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = vk.VK_SHADER_STAGE_VERTEX_BIT | vk.VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
.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 = .{
|
||||
.sType = vk.VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||
.bindingCount = bindings.len,
|
||||
.bindingCount = binding_count,
|
||||
.pBindings = &bindings,
|
||||
};
|
||||
|
||||
|
|
@ -253,6 +280,17 @@ pub fn init(
|
|||
.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 = .{
|
||||
.sType = vk.VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
||||
.logicOpEnable = vk.VK_FALSE,
|
||||
|
|
@ -298,65 +336,38 @@ pub fn init(
|
|||
// UNIFORM BUFFER + DESCRIPTORS
|
||||
//
|
||||
|
||||
if (uniform_size > 0) {
|
||||
var buffer_info: vk.VkBufferCreateInfo = .{
|
||||
.sType = vk.VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
.size = uniform_size,
|
||||
.usage = vk.VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
|
||||
.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;
|
||||
if (has_descriptors) {
|
||||
// --- RESOLVE TEXTURES ---
|
||||
var textures: [MAX_TEXTURES]*VKTexture = undefined;
|
||||
for (config.textures, 0..) |handle, i| {
|
||||
textures[i] = vulkan.resource.textures.get(handle) orelse return error.TextureNotFound;
|
||||
}
|
||||
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 ---
|
||||
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,
|
||||
.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 = .{
|
||||
.sType = vk.VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
|
||||
.poolSizeCount = pool_sizes.len,
|
||||
.poolSizeCount = pool_size_count,
|
||||
.pPoolSizes = &pool_sizes,
|
||||
.maxSets = 1,
|
||||
.maxSets = @intCast(MAX_FRAMES_IN_FLIGHT),
|
||||
};
|
||||
|
||||
var descriptor_pool: vk.VkDescriptorPool = null;
|
||||
|
|
@ -365,36 +376,113 @@ pub fn init(
|
|||
}
|
||||
self.descriptor_pool = descriptor_pool.?;
|
||||
|
||||
// --- 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),
|
||||
};
|
||||
// --- PER-FRAME UNIFORM BUFFER + DESCRIPTOR SET ---
|
||||
for (0..MAX_FRAMES_IN_FLIGHT) |frame| {
|
||||
if (uniform_size > 0) {
|
||||
var buffer_info: vk.VkBufferCreateInfo = .{
|
||||
.sType = vk.VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
.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) {
|
||||
return error.FailedToAllocateDescriptorSets;
|
||||
var uniform_buffer: vk.VkBuffer = null;
|
||||
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;
|
||||
|
|
@ -409,11 +497,15 @@ pub fn deinit(self: *Self) void {
|
|||
if (self.descriptor_pool) |pool| {
|
||||
vk.vkDestroyDescriptorPool(self.device.raw, pool, null);
|
||||
}
|
||||
if (self.uniform_memory) |memory| {
|
||||
vk.vkFreeMemory(self.device.raw, memory, null);
|
||||
for (&self.uniform_memories) |memory| {
|
||||
if (memory) |m| {
|
||||
vk.vkFreeMemory(self.device.raw, m, null);
|
||||
}
|
||||
}
|
||||
if (self.uniform_buffer) |buffer| {
|
||||
vk.vkDestroyBuffer(self.device.raw, buffer, null);
|
||||
for (&self.uniform_buffers) |buffer| {
|
||||
if (buffer) |b| {
|
||||
vk.vkDestroyBuffer(self.device.raw, b, null);
|
||||
}
|
||||
}
|
||||
if (self.layout) |layout| {
|
||||
vk.vkDestroyPipelineLayout(self.device.raw, layout, null);
|
||||
|
|
@ -428,6 +520,7 @@ pub fn deinit(self: *Self) void {
|
|||
/// Set a uniform value by name
|
||||
/// ----------------------------------------------------
|
||||
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| {
|
||||
if (std.mem.eql(u8, uniform.name, name)) {
|
||||
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;
|
||||
if (vk.vkMapMemory(
|
||||
self.device.raw,
|
||||
self.uniform_memory,
|
||||
uniform_memory,
|
||||
uniform.offset,
|
||||
uniform.size,
|
||||
0,
|
||||
|
|
@ -450,7 +543,7 @@ pub fn setUniform(self: *Self, name: []const u8, value: anytype) !void {
|
|||
const src: [*]const u8 = @ptrCast(&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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
85
src/rhi/vulkan/resource/sampler.zig
Normal file
85
src/rhi/vulkan/resource/sampler.zig
Normal 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);
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
const std = @import("std");
|
||||
const vk = @import("vulkan");
|
||||
const Instance = @import("../instance.zig");
|
||||
const Window = @import("../../root.zig").Window;
|
||||
const Window = @import("../../common.zig").Window;
|
||||
const Self = @This();
|
||||
|
||||
//
|
||||
|
|
|
|||
44
src/rhi/vulkan/resource/texture.zig
Normal file
44
src/rhi/vulkan/resource/texture.zig
Normal 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();
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
const std = @import("std");
|
||||
const vk = @import("vulkan");
|
||||
const gtl = @import("gtl");
|
||||
const common = @import("../root.zig");
|
||||
const common = @import("../common.zig");
|
||||
|
||||
const Instance = @import("instance.zig");
|
||||
const Resource = @import("resource.zig");
|
||||
|
|
@ -22,6 +22,9 @@ const BufferSharingMode = common.BufferSharingMode;
|
|||
const AdapterConfig = common.AdapterConfig;
|
||||
const RenderPassConfig = common.RenderPassConfig;
|
||||
const PipelineConfig = common.PipelineConfig;
|
||||
const ImageConfig = common.ImageConfig;
|
||||
const SamplerConfig = common.SamplerConfig;
|
||||
const TextureConfig = common.TextureConfig;
|
||||
|
||||
// --- HANDLES ---
|
||||
const Adapter = common.Adapter;
|
||||
|
|
@ -33,8 +36,13 @@ const Buffer = common.Buffer;
|
|||
const Shader = common.Shader;
|
||||
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));
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
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(
|
||||
|
|
@ -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| {
|
||||
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) {
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
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(
|
||||
|
|
@ -198,6 +270,24 @@ pub fn makeBuffer(
|
|||
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
|
||||
//
|
||||
|
|
@ -212,7 +302,7 @@ pub fn beginFrame(
|
|||
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);
|
||||
try self.ensureFrameSync(raw_device.raw.?, raw_swapchain.raw);
|
||||
|
||||
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 frame = self.currentFrame();
|
||||
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(
|
||||
frame.cmd_buf,
|
||||
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);
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
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 {
|
||||
|
|
|
|||
1
src/vendor/sdl3/sdl.h
vendored
1
src/vendor/sdl3/sdl.h
vendored
|
|
@ -1 +0,0 @@
|
|||
#include <SDL3/SDL.h>
|
||||
2
src/vendor/stb_image/stb_image.c
vendored
Normal file
2
src/vendor/stb_image/stb_image.c
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
7988
src/vendor/stb_image/stb_image.h
vendored
Normal file
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
1
zls.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{ "enable_build_on_save": true, "build_on_save_step": "check" }
|
||||
Loading…
Add table
Reference in a new issue