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
|
- 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```
|
||||||
|
|
|
||||||
11
build.zig
11
build.zig
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
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 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,
|
||||||
|
|
||||||
|
|
|
||||||
73
src/gfx.zig
73
src/gfx.zig
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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 {};
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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| {
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
|
|
|
||||||
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 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
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 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();
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
||||||
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 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 {
|
||||||
|
|
|
||||||
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