Low cortisol depth management

This commit is contained in:
abux 2026-08-12 20:31:05 +01:00
parent 655dca998b
commit 7e1d95054e
20 changed files with 2131 additions and 58 deletions

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
.zig-cache/
zig-pkg/
zig-out/

View file

@ -33,6 +33,7 @@ src/rhi/vulkan/resource.zig
| `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 |
| `getXInfo(handle)` | Returns backend-independent information about the resource |
| `getRawVkX(handle)` | Returns the backend-specific raw Vulkan GPU handle |
## Example

View file

@ -58,6 +58,14 @@ pub fn build(b: *std.Build) !void {
mod.addImport("stb_image", stb_image.createModule());
mod.addCSourceFile(.{ .file = b.path("src/vendor/stb_image/stb_image.c") });
const fast_obj = b.addTranslateC(.{
.target = target,
.optimize = optimize,
.root_source_file = b.path("./src/vendor/fast_obj/fast_obj.h"),
});
mod.addImport("fast_obj", fast_obj.createModule());
mod.addCSourceFiles(.{ .files = &.{"src/vendor/fast_obj/fast_obj.c"} });
//
// EXAMPLES
//
@ -67,6 +75,7 @@ pub fn build(b: *std.Build) !void {
src: []const u8,
}{
.{ .name = "simple", .src = "examples/simple/simple.zig" },
.{ .name = "3D", .src = "examples/simple_3D/simple_3D.zig" },
}) |excfg| {
const ex_name = excfg.name;
const ex_src = excfg.src;
@ -110,13 +119,19 @@ pub fn build(b: *std.Build) !void {
example.root_module.addImport("sdl", sdl.createModule());
example.root_module.linkSystemLibrary("SDL3", .{});
const example_math = b.dependency("NYXMath", .{
.target = target,
.optimize = optimize,
});
example.root_module.addImport("math", example_math.module("NYXMath"));
const example_run = b.addRunArtifact(example);
example_run_step.dependOn(&example_run.step);
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);
// const check_step = b.step("check", "Check if compiles");
// check_step.dependOn(&example.step);
}
}

View file

@ -1,3 +1,7 @@
clear
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
glslc examples/assets/shaders/3D.vert -o examples/assets/shaders/compiled/3D.vert.spv
glslc examples/assets/shaders/3D.frag -o examples/assets/shaders/compiled/3D.frag.spv

View file

@ -0,0 +1,26 @@
#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(binding = 2) uniform sampler2D albedo_texture;
/// ----------------------------------------------------
/// ----------------------------------------------------
void main() {
// out_color = vec4(1.0);
out_color = texture(albedo_texture, in_UV);
}

View file

@ -0,0 +1,45 @@
#version 450
//
// IN
//
layout(location = 0) in vec3 in_position;
layout(location = 1) in vec3 in_normal;
layout(location = 2) in vec2 in_UV;
//
// OUT
//
layout(location = 0) out vec2 out_UV;
layout(location = 1) out vec3 out_normal;
layout(location = 2) out vec3 out_world_position;
//
// UNIFORMS
//
layout(set = 0, binding = 0) uniform Camera {
mat4 projection;
mat4 view;
} camera;
layout(set = 0, binding = 1) uniform Model {
mat4 model;
} object;
/// ----------------------------------------------------
/// ----------------------------------------------------
void main() {
// --- GET WORLD POSITION ---
vec4 world_position = object.model * vec4(in_position, 1.0);
// --- SHOW POSITION AT ---
gl_Position = camera.projection * camera.view * world_position;
// --- SET ---
out_UV = in_UV;
out_normal = mat3(transpose(inverse(object.model))) * in_normal;
out_world_position = world_position.xyz;
}

Binary file not shown.

Binary file not shown.

View file

@ -77,15 +77,17 @@ pub fn main(init: std.process.Init) !void {
}, init.gpa, init.io);
defer gfx.deinit();
errdefer gfx.deinit();
// --- # ---
const adapter = try gfx.makeAdapter(.{});
const adapter = try gfx.makeAdapter(.{ .gpu_type = .any });
const surface = try gfx.makeSurface();
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" },
.image = .{ .path = "examples/assets/textures/bully.jpg" },
.sampler = .{ .min_filter = .linear, .address_u = .repeat },
}, adapter, device);
@ -107,10 +109,12 @@ pub fn main(init: std.process.Init) !void {
.vertex_binding = .{ .stride = @sizeOf(Vertex) },
.uniforms = &.{
.{ .name = "material", .offset = 0, .size = @sizeOf(UnlitMaterial) },
.{ .name = "material", .size = @sizeOf(UnlitMaterial) },
},
.textures = &.{texture},
.textures = &.{
texture,
},
.blend_mode = .alpha,

View file

@ -0,0 +1,253 @@
//! ----------------------------------------------------
//! ----------------------------------------------------
const std = @import("std");
const sdl = @import("sdl");
const math = @import("math");
const Gfx = @import("gfx");
/// ----------------------------------------------------
/// ----------------------------------------------------
const UnlitMaterial = struct {
albedo: [4]f32 = .{ 1, 1, 1, 1 },
};
/// ----------------------------------------------------
/// ----------------------------------------------------
const Vertex = struct {
pos: [3]f32,
normal: [3]f32,
uv: [2]f32,
};
/// ----------------------------------------------------
/// ----------------------------------------------------
const vertices = [_]Vertex{
.{ .pos = .{ -0.5, -0.5, 0.5 }, .normal = .{ 0, 0, 1 }, .uv = .{ 0, 0 } },
.{ .pos = .{ 0.5, -0.5, 0.5 }, .normal = .{ 0, 0, 1 }, .uv = .{ 1, 0 } },
.{ .pos = .{ 0.5, 0.5, 0.5 }, .normal = .{ 0, 0, 1 }, .uv = .{ 1, 1 } },
.{ .pos = .{ -0.5, 0.5, 0.5 }, .normal = .{ 0, 0, 1 }, .uv = .{ 0, 1 } },
.{ .pos = .{ 0.5, -0.5, -0.5 }, .normal = .{ 0, 0, -1 }, .uv = .{ 0, 0 } },
.{ .pos = .{ -0.5, -0.5, -0.5 }, .normal = .{ 0, 0, -1 }, .uv = .{ 1, 0 } },
.{ .pos = .{ -0.5, 0.5, -0.5 }, .normal = .{ 0, 0, -1 }, .uv = .{ 1, 1 } },
.{ .pos = .{ 0.5, 0.5, -0.5 }, .normal = .{ 0, 0, -1 }, .uv = .{ 0, 1 } },
.{ .pos = .{ -0.5, -0.5, -0.5 }, .normal = .{ -1, 0, 0 }, .uv = .{ 0, 0 } },
.{ .pos = .{ -0.5, -0.5, 0.5 }, .normal = .{ -1, 0, 0 }, .uv = .{ 1, 0 } },
.{ .pos = .{ -0.5, 0.5, 0.5 }, .normal = .{ -1, 0, 0 }, .uv = .{ 1, 1 } },
.{ .pos = .{ -0.5, 0.5, -0.5 }, .normal = .{ -1, 0, 0 }, .uv = .{ 0, 1 } },
.{ .pos = .{ 0.5, -0.5, 0.5 }, .normal = .{ 1, 0, 0 }, .uv = .{ 0, 0 } },
.{ .pos = .{ 0.5, -0.5, -0.5 }, .normal = .{ 1, 0, 0 }, .uv = .{ 1, 0 } },
.{ .pos = .{ 0.5, 0.5, -0.5 }, .normal = .{ 1, 0, 0 }, .uv = .{ 1, 1 } },
.{ .pos = .{ 0.5, 0.5, 0.5 }, .normal = .{ 1, 0, 0 }, .uv = .{ 0, 1 } },
.{ .pos = .{ -0.5, 0.5, 0.5 }, .normal = .{ 0, 1, 0 }, .uv = .{ 0, 0 } },
.{ .pos = .{ 0.5, 0.5, 0.5 }, .normal = .{ 0, 1, 0 }, .uv = .{ 1, 0 } },
.{ .pos = .{ 0.5, 0.5, -0.5 }, .normal = .{ 0, 1, 0 }, .uv = .{ 1, 1 } },
.{ .pos = .{ -0.5, 0.5, -0.5 }, .normal = .{ 0, 1, 0 }, .uv = .{ 0, 1 } },
.{ .pos = .{ -0.5, -0.5, -0.5 }, .normal = .{ 0, -1, 0 }, .uv = .{ 0, 0 } },
.{ .pos = .{ 0.5, -0.5, -0.5 }, .normal = .{ 0, -1, 0 }, .uv = .{ 1, 0 } },
.{ .pos = .{ 0.5, -0.5, 0.5 }, .normal = .{ 0, -1, 0 }, .uv = .{ 1, 1 } },
.{ .pos = .{ -0.5, -0.5, 0.5 }, .normal = .{ 0, -1, 0 }, .uv = .{ 0, 1 } },
};
/// ----------------------------------------------------
/// ----------------------------------------------------
const indices = [_]u32{
0, 1, 2,
0, 2, 3,
4, 5, 6,
4, 6, 7,
8, 9, 10,
8, 10, 11,
12, 13, 14,
12, 14, 15,
16, 17, 18,
16, 18, 19,
20, 21, 22,
20, 22, 23,
};
/// ----------------------------------------------------
/// ----------------------------------------------------
const Camera = struct {
projection: math.Mat4x4,
view: math.Mat4x4,
};
/// ----------------------------------------------------
/// ----------------------------------------------------
const Model = struct {
matrix: math.Mat4x4,
};
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn main(init: std.process.Init) !void {
//
// DATA
//
var material: UnlitMaterial = .{ .albedo = .{ 1.0, 0.2, 0.2, 1.0 } };
const camera: Camera = .{
.projection = .perspective(std.math.degreesToRadians(90), 800.0 / 600.0, 0.01, 1000),
.view = .lookAt(.new(.{ 1, 1, -2 }), .new(.{ 0, 0, 1 }), .new(.{ 0, 1, 0 })),
};
const model: Model = .{
.matrix = math.Mat4x4.translate(
0,
0,
0,
)
.mul(math.Quat.fromEuler(.init(0, 0, 0)).toMat4())
.mul(math.Mat4x4.scale(
1,
1,
1,
)),
};
//
// SDL
//
// --- # ---
if (!sdl.SDL_Init(sdl.SDL_INIT_VIDEO)) return error.FailedToInitSDL;
defer sdl.SDL_Quit();
// --- # ---
const window = sdl.SDL_CreateWindow("NYXGFX", 800, 600, 0) orelse return error.FailedTomakeWindow;
defer sdl.SDL_DestroyWindow(window);
// --- # ---
const props = sdl.SDL_GetWindowProperties(window);
const display = sdl.SDL_GetPointerProperty(props, sdl.SDL_PROP_WINDOW_X11_DISPLAY_POINTER, null);
const xwindow = sdl.SDL_GetNumberProperty(props, sdl.SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0);
//
// GFX
//
// --- # ---
var gfx: Gfx = try .init(.{
.window = .{
.xlib = .{
.window = @intCast(xwindow),
.display = display.?,
},
},
.backend = .vulkan,
}, init.gpa, init.io);
defer gfx.deinit();
errdefer gfx.deinit();
// --- # ---
const adapter = try gfx.makeAdapter(.{ .gpu_type = .any });
const surface = try gfx.makeSurface();
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", .vertical_flip = true },
.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);
// --- SHADERS ---
const simple_vert = try gfx.makeShader("examples/assets/shaders/compiled/3D.vert.spv", device);
const simple_frag = try gfx.makeShader("examples/assets/shaders/compiled/3D.frag.spv", device);
// --- PIPELINE ---
const pipeline = try gfx.makePipeline(.{
.vertex_attributes = &.{
.{ .location = 0, .format = .vec3, .offset = @offsetOf(Vertex, "pos") },
.{ .location = 1, .format = .vec3, .offset = @offsetOf(Vertex, "normal") },
.{ .location = 2, .format = .vec2, .offset = @offsetOf(Vertex, "uv") },
},
.vertex_binding = .{ .stride = @sizeOf(Vertex) },
.uniforms = &.{
.{ .name = "camera", .size = @sizeOf(Camera) },
.{ .name = "model", .size = @sizeOf(Model) },
},
.textures = &.{
texture,
},
.depth_format = .d32_sfloat,
.vert_shader = simple_vert,
.frag_shader = simple_frag,
.device = device,
.swapchain = swapchain,
});
// --- MISC ---
var event: sdl.SDL_Event = undefined;
var running: bool = true;
var i: f32 = 0;
while (running) {
// --- INPUTS ---
while (sdl.SDL_PollEvent(&event)) {
switch (event.type) {
sdl.SDL_EVENT_QUIT => running = false,
else => {},
}
}
// --- ANIMATION ---
i += 0.001;
i = @mod(i, 1);
material.albedo[1] = i;
try pipeline.setUniform("camera", Camera{
.projection = camera.projection.transpose(),
.view = camera.view.transpose(),
});
try pipeline.setUniform("model", Model{
.matrix = model.matrix.transpose(),
});
// --- FRAME ---
try gfx.beginFrame(swapchain, device);
defer gfx.endFrame(swapchain, device) catch unreachable;
// --- NEW PASS ---
if (gfx.beginPass(swapchain, .{
.clear_color = .{ 0.01, 0.01, 0.01, 1.0 },
.depth_enabled = true,
})) |pass| {
defer pass.end();
try vbuf.bind();
try ibuf.bind();
try pipeline.bind();
pass.drawIndexed(indices.len, 1, 0, 0, 0);
}
// --- NEW PASS ---
if (gfx.beginPass(swapchain, .{
.load_op = .load,
})) |pass| {
defer pass.end();
}
}
}

View file

@ -7,32 +7,34 @@ const std = @import("std");
const gtl = @import("gtl");
const vk = @import("vulkan");
const common = @import("rhi/common.zig");
const Handle = gtl.Handle;
const Self = @This();
// --- # ---
pub const Handle = gtl.Handle;
// --- CONFIGS ---
const AdapterConfig = common.AdapterConfig;
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;
pub const AdapterConfig = common.AdapterConfig;
pub const RenderPassConfig = common.RenderPassConfig;
pub const PipelineConfig = common.PipelineConfig;
pub const BufferUsage = common.BufferUsage;
pub const Pass = common.Pass;
pub const ImageConfig = common.ImageConfig;
pub const SamplerConfig = common.SamplerConfig;
pub const TextureConfig = common.TextureConfig;
// --- HANDLES ---
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;
pub const Window = common.Window;
pub const Adapter = common.Adapter;
pub const Device = common.Device;
pub const Buffer = common.Buffer;
pub const Surface = common.Surface;
pub const Swapchain = common.Swapchain;
pub const Image = common.Image;
pub const Sampler = common.Sampler;
pub const Texture = common.Texture;
const Shader = common.Shader;
const Pipeline = common.Pipeline;
pub const Shader = common.Shader;
pub const Pipeline = common.Pipeline;
// --- RHI ---
const VulkanRHI = @import("rhi/vulkan/vulkan.zig");
@ -288,7 +290,7 @@ pub fn beginPass(
config: RenderPassConfig,
) ?Pass {
switch (self.ctx) {
.vulkan => |*v| v.beginRendering(swapchain, config) catch return null,
.vulkan => |*v| v.beginPass(swapchain, config) catch return null,
}
return .{

View file

@ -93,10 +93,30 @@ pub const VertexAttribute = struct {
/// ----------------------------------------------------
pub const UniformDesc = struct {
name: []const u8,
offset: u32,
size: u32,
};
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const CompareOp = enum {
never,
less,
equal,
less_equal,
greater,
not_equal,
greater_equal,
always,
};
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const DepthFormat = enum {
d16_unorm,
d24_unorm_s8,
d32_sfloat,
};
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const PipelineConfig = struct {
@ -127,12 +147,17 @@ pub const PipelineConfig = struct {
front_face: enum {
ccw,
cw,
} = .cw,
} = .ccw,
blend_mode: enum {
none,
alpha,
} = .none,
depth_format: ?DepthFormat = null,
depth_test: bool = true,
depth_write: bool = true,
depth_compare: CompareOp = .less,
};
/// ----------------------------------------------------
@ -145,7 +170,7 @@ pub const Pass = struct {
/// ----------------------------------------------------
pub fn end(self: *const Pass) void {
switch (self.gfx.ctx) {
.vulkan => |*v| v.endRendering(self.swapchain),
.vulkan => |*v| v.endPass(self.swapchain),
}
}
@ -185,8 +210,18 @@ pub const StoreOp = enum {
/// ----------------------------------------------------
pub const RenderPassConfig = struct {
clear_color: [4]f32 = .{ 0.0, 0.0, 0.0, 1.0 },
depth_enabled: bool = false,
depth_format: DepthFormat = .d32_sfloat,
depth_load_op: LoadOp = .clear,
depth_store_op: StoreOp = .dont_care,
clear_depth: f32 = 1.0,
clear_stencil: u8 = 0,
load_op: LoadOp = .clear,
store_op: StoreOp = .store,
width: ?u32 = null,
height: ?u32 = null,
};
@ -244,6 +279,7 @@ pub const AddressMode = enum {
pub const ImageUsage = enum {
texture,
render_target,
depth,
};
/// ----------------------------------------------------
@ -258,6 +294,8 @@ pub const ImageConfig = struct {
/// Required for `.data`. `null` = use source dimensions
size: ?[2]u32 = null,
vertical_flip: bool = false,
format: PixelFormat = .rgba8_srgb,
usage: ImageUsage = .texture,
mip_levels: u32 = 1,

View file

@ -50,6 +50,7 @@ pub fn init(
if (config.path) |path| {
var tex_channel: i32 = 0;
stb.stbi_set_flip_vertically_on_load(@intFromBool(config.vertical_flip));
const data = stb.stbi_load(path.ptr, &tex_size[0], &tex_size[1], &tex_channel, stb.STBI_rgb_alpha) orelse {
return error.FileNotFound;
};
@ -86,6 +87,7 @@ pub fn init(
switch (config.usage) {
.texture => {},
.render_target => usage |= vk.VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
.depth => usage |= vk.VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
}
const image_info: vk.VkImageCreateInfo = .{

View file

@ -34,6 +34,7 @@ descriptor_sets: [MAX_FRAMES_IN_FLIGHT]vk.VkDescriptorSet = .{null} ** MAX_FRAME
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,
uniform_offsets: []u32,
vulkan: *VulkanRHI,
@ -51,20 +52,37 @@ pub fn init(
var self: Self = .{
.device = device,
.uniforms = &.{},
.uniform_offsets = &.{},
.vulkan = vulkan,
.alloc = alloc,
};
errdefer self.deinit();
// --- COMPUTE UNIFORM LAYOUT ---
// Each uniform block gets its own descriptor binding. Blocks are packed
// into a single per-frame buffer, each at a `minUniformBufferOffsetAlignment`
// aligned offset so the same layout maps to `glBindBufferRange` (GL) and
// CBV offsets (D3D12) unchanged.
var uniform_size: u32 = 0;
for (config.uniforms) |u| {
uniform_size = @max(uniform_size, u.offset + u.size);
}
uniform_size = std.mem.alignForward(u32, uniform_size, 16);
// --- COPY UNIFORMS ---
if (config.uniforms.len > 0) {
var props: vk.VkPhysicalDeviceProperties = .{};
vk.vkGetPhysicalDeviceProperties(device.physical_device, &props);
const alignment: u32 = @intCast(@max(
@as(u64, 16),
props.limits.minUniformBufferOffsetAlignment,
));
const uniform_offsets = try alloc.alloc(u32, config.uniforms.len);
self.uniform_offsets = uniform_offsets;
for (config.uniforms, 0..) |u, i| {
uniform_offsets[i] = std.mem.alignForward(u32, uniform_size, alignment);
uniform_size = uniform_offsets[i] + u.size;
}
uniform_size = std.mem.alignForward(u32, uniform_size, alignment);
// --- COPY UNIFORMS ---
const uniforms = try alloc.alloc(UniformDesc, config.uniforms.len);
@memcpy(uniforms, config.uniforms);
self.uniforms = uniforms;
@ -83,9 +101,9 @@ pub fn init(
var bindings: [MAX_BINDINGS]vk.VkDescriptorSetLayoutBinding = undefined;
var binding_count: u32 = 0;
if (uniform_size > 0) {
for (config.uniforms, 0..) |_, i| {
bindings[binding_count] = .{
.binding = 0,
.binding = @intCast(i),
.descriptorType = vk.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = 1,
.stageFlags = vk.VK_SHADER_STAGE_VERTEX_BIT | vk.VK_SHADER_STAGE_FRAGMENT_BIT,
@ -94,9 +112,9 @@ pub fn init(
binding_count += 1;
}
for (0..texture_count) |i| {
for (config.textures, 0..) |_, i| {
bindings[binding_count] = .{
.binding = @intCast(i + 1),
.binding = @intCast(config.uniforms.len + i),
.descriptorType = vk.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = 1,
.stageFlags = vk.VK_SHADER_STAGE_FRAGMENT_BIT,
@ -298,10 +316,29 @@ pub fn init(
.pAttachments = &color_blend_attachment,
};
var depth_stencil: vk.VkPipelineDepthStencilStateCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.depthTestEnable = if (config.depth_test) vk.VK_TRUE else vk.VK_FALSE,
.depthWriteEnable = if (config.depth_write) vk.VK_TRUE else vk.VK_FALSE,
.depthCompareOp = switch (config.depth_compare) {
.never => vk.VK_COMPARE_OP_NEVER,
.less => vk.VK_COMPARE_OP_LESS,
.equal => vk.VK_COMPARE_OP_EQUAL,
.less_equal => vk.VK_COMPARE_OP_LESS_OR_EQUAL,
.greater => vk.VK_COMPARE_OP_GREATER,
.not_equal => vk.VK_COMPARE_OP_NOT_EQUAL,
.greater_equal => vk.VK_COMPARE_OP_GREATER_OR_EQUAL,
.always => vk.VK_COMPARE_OP_ALWAYS,
},
.depthBoundsTestEnable = vk.VK_FALSE,
.stencilTestEnable = vk.VK_FALSE,
};
var rendering_info: vk.VkPipelineRenderingCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO,
.colorAttachmentCount = 1,
.pColorAttachmentFormats = &swapchain.format,
.depthAttachmentFormat = if (config.depth_format) |f| VulkanRHI.depthFormatToVk(f) else vk.VK_FORMAT_UNDEFINED,
};
var pipeline_info: vk.VkGraphicsPipelineCreateInfo = .{
@ -316,6 +353,7 @@ pub fn init(
.pRasterizationState = &rasterizer,
.pMultisampleState = &multisampling,
.pColorBlendState = &color_blending,
.pDepthStencilState = if (config.depth_format != null) &depth_stencil else null,
.pDynamicState = &dynamic_state,
.layout = pipeline_layout,
@ -347,10 +385,10 @@ pub fn init(
var pool_sizes: [2]vk.VkDescriptorPoolSize = undefined;
var pool_size_count: u32 = 0;
if (uniform_size > 0) {
if (config.uniforms.len > 0) {
pool_sizes[pool_size_count] = .{
.type = vk.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = @intCast(MAX_FRAMES_IN_FLIGHT),
.descriptorCount = @intCast(MAX_FRAMES_IN_FLIGHT * config.uniforms.len),
};
pool_size_count += 1;
}
@ -440,24 +478,24 @@ pub fn init(
// --- WRITE DESCRIPTOR SET ---
var writes: [MAX_WRITES]vk.VkWriteDescriptorSet = undefined;
var write_count: u32 = 0;
var buffer_descriptor: vk.VkDescriptorBufferInfo = undefined;
var buffer_descriptors: [MAX_BINDINGS]vk.VkDescriptorBufferInfo = undefined;
var image_descriptors: [MAX_TEXTURES]vk.VkDescriptorImageInfo = undefined;
if (uniform_size > 0) {
buffer_descriptor = .{
for (config.uniforms, 0..) |u, i| {
buffer_descriptors[i] = .{
.buffer = self.uniform_buffers[frame],
.offset = 0,
.range = uniform_size,
.offset = self.uniform_offsets[i],
.range = u.size,
};
writes[write_count] = .{
.sType = vk.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = self.descriptor_sets[frame],
.dstBinding = 0,
.dstBinding = @intCast(i),
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = vk.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.pBufferInfo = &buffer_descriptor,
.pBufferInfo = &buffer_descriptors[i],
};
write_count += 1;
}
@ -472,7 +510,7 @@ pub fn init(
writes[write_count] = .{
.sType = vk.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = self.descriptor_sets[frame],
.dstBinding = @intCast(i + 1),
.dstBinding = @intCast(config.uniforms.len + i),
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = vk.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
@ -514,6 +552,7 @@ pub fn deinit(self: *Self) void {
vk.vkDestroyPipeline(self.device.raw, raw, null);
}
self.alloc.free(self.uniforms);
self.alloc.free(self.uniform_offsets);
}
/// ----------------------------------------------------
@ -521,7 +560,7 @@ pub fn deinit(self: *Self) 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, 0..) |uniform, i| {
if (std.mem.eql(u8, uniform.name, name)) {
if (@sizeOf(@TypeOf(value)) > uniform.size) {
return error.UniformSizeMismatch;
@ -531,7 +570,7 @@ pub fn setUniform(self: *Self, name: []const u8, value: anytype) !void {
if (vk.vkMapMemory(
self.device.raw,
uniform_memory,
uniform.offset,
self.uniform_offsets[i],
uniform.size,
0,
&mapped,

View file

@ -12,7 +12,7 @@ const Self = @This();
// FIELDS
//
raw: *vk.VkShaderModule_T,
raw: vk.VkShaderModule,
device: *const Device,
/// ----------------------------------------------------
@ -40,7 +40,10 @@ pub fn init(
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn deinit(self: *Self) void {
vk.vkDestroyShaderModule(self.device.raw, self.raw, null);
if (self.raw) |raw| {
vk.vkDestroyShaderModule(self.device.raw, raw, null);
self.raw = null;
}
}
/// ----------------------------------------------------

View file

@ -27,6 +27,11 @@ raw: *vk.VkSwapchainKHR_T,
images: []vk.VkImage,
image_views: []vk.VkImageView,
depth_images: []vk.VkImage = &.{},
depth_memories: []vk.VkDeviceMemory = &.{},
depth_views: []vk.VkImageView = &.{},
depth_format: vk.VkFormat = vk.VK_FORMAT_UNDEFINED,
format: vk.VkFormat,
extent: vk.VkExtent2D,
@ -158,6 +163,8 @@ pub fn init(
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn deinit(self: *Self) void {
self.destroyDepth();
for (self.image_views) |view| {
vk.vkDestroyImageView(self.device.raw, view, null);
}
@ -167,3 +174,164 @@ pub fn deinit(self: *Self) void {
self.alloc.free(self.images);
self.alloc.free(self.image_views);
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn ensureDepth(self: *Self, format: vk.VkFormat) !void {
if (self.depth_format == format and self.depth_views.len > 0) return;
self.destroyDepth();
const count = self.images.len;
const images = try self.alloc.alloc(vk.VkImage, count);
errdefer self.alloc.free(images);
const memories = try self.alloc.alloc(vk.VkDeviceMemory, count);
errdefer self.alloc.free(memories);
const views = try self.alloc.alloc(vk.VkImageView, count);
var created: usize = 0;
errdefer {
for (0..created) |i| {
if (views[i]) |view| vk.vkDestroyImageView(self.device.raw, view, null);
if (memories[i]) |memory| vk.vkFreeMemory(self.device.raw, memory, null);
if (images[i]) |image| vk.vkDestroyImage(self.device.raw, image, null);
}
self.alloc.free(views);
}
const aspect: vk.VkImageAspectFlags = if (isDepthStencilFormat(format))
vk.VK_IMAGE_ASPECT_DEPTH_BIT | vk.VK_IMAGE_ASPECT_STENCIL_BIT
else
vk.VK_IMAGE_ASPECT_DEPTH_BIT;
const image_info: vk.VkImageCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.imageType = vk.VK_IMAGE_TYPE_2D,
.extent = .{
.width = self.extent.width,
.height = self.extent.height,
.depth = 1,
},
.mipLevels = 1,
.arrayLayers = 1,
.format = format,
.tiling = vk.VK_IMAGE_TILING_OPTIMAL,
.initialLayout = vk.VK_IMAGE_LAYOUT_UNDEFINED,
.usage = vk.VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
.samples = vk.VK_SAMPLE_COUNT_1_BIT,
.sharingMode = vk.VK_SHARING_MODE_EXCLUSIVE,
};
for (0..count) |i| {
created = i;
if (vk.vkCreateImage(self.device.raw, &image_info, null, &images[i]) != vk.VK_SUCCESS) {
return error.FailedToCreateDepthImage;
}
var mem_req: vk.VkMemoryRequirements = .{};
vk.vkGetImageMemoryRequirements(self.device.raw, images[i], &mem_req);
const mem_type = findMemoryType(
self.device.physical_device,
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(self.device.raw, &alloc_info, null, &memories[i]) != vk.VK_SUCCESS) {
return error.FailedToAllocateDepthMemory;
}
_ = vk.vkBindImageMemory(self.device.raw, images[i], memories[i], 0);
const view_info: vk.VkImageViewCreateInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = images[i],
.viewType = vk.VK_IMAGE_VIEW_TYPE_2D,
.format = format,
.components = .{
.r = vk.VK_COMPONENT_SWIZZLE_IDENTITY,
.g = vk.VK_COMPONENT_SWIZZLE_IDENTITY,
.b = vk.VK_COMPONENT_SWIZZLE_IDENTITY,
.a = vk.VK_COMPONENT_SWIZZLE_IDENTITY,
},
.subresourceRange = .{
.aspectMask = aspect,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
if (vk.vkCreateImageView(self.device.raw, &view_info, null, &views[i]) != vk.VK_SUCCESS) {
return error.FailedToCreateDepthImageView;
}
created = i + 1;
}
self.depth_images = images;
self.depth_memories = memories;
self.depth_views = views;
self.depth_format = format;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
fn destroyDepth(self: *Self) void {
if (self.depth_views.len > 0) {
for (self.depth_views) |view| {
if (view) |v| vk.vkDestroyImageView(self.device.raw, v, null);
}
self.alloc.free(self.depth_views);
self.depth_views = &.{};
}
if (self.depth_memories.len > 0) {
for (self.depth_memories) |memory| {
if (memory) |m| vk.vkFreeMemory(self.device.raw, m, null);
}
self.alloc.free(self.depth_memories);
self.depth_memories = &.{};
}
if (self.depth_images.len > 0) {
for (self.depth_images) |image| {
if (image) |im| vk.vkDestroyImage(self.device.raw, im, null);
}
self.alloc.free(self.depth_images);
self.depth_images = &.{};
}
self.depth_format = vk.VK_FORMAT_UNDEFINED;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
fn isDepthStencilFormat(format: vk.VkFormat) bool {
return format == vk.VK_FORMAT_D16_UNORM_S8_UINT or
format == vk.VK_FORMAT_D24_UNORM_S8_UINT or
format == vk.VK_FORMAT_D32_SFLOAT_S8_UINT;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
fn findMemoryType(
physical_device: vk.VkPhysicalDevice,
type_bits: u32,
properties: vk.VkMemoryPropertyFlags,
) ?u32 {
var mem_props: vk.VkPhysicalDeviceMemoryProperties = .{};
vk.vkGetPhysicalDeviceMemoryProperties(physical_device, &mem_props);
for (0..mem_props.memoryTypeCount) |i| {
if ((type_bits & (@as(u32, 1) << @intCast(i))) != 0 and
(mem_props.memoryTypes[i].propertyFlags & properties) == properties)
{
return @intCast(i);
}
}
return null;
}

View file

@ -1,5 +1,6 @@
//! ----------------------------------------------------
//! `🗲` Vulkan RHI `🗲`
//! `🗲` Vulkan RHI `🗲`
//! 😭 Send Help 😭
//! ----------------------------------------------------
const std = @import("std");
@ -44,6 +45,16 @@ const Texture = common.Texture;
// --- # ---
pub const MAX_FRAMES_IN_FLIGHT: usize = 3;
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn depthFormatToVk(format: common.DepthFormat) vk.VkFormat {
return switch (format) {
.d16_unorm => vk.VK_FORMAT_D16_UNORM,
.d24_unorm_s8 => vk.VK_FORMAT_D24_UNORM_S8_UINT,
.d32_sfloat => vk.VK_FORMAT_D32_SFLOAT,
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
const Frame = struct {
@ -419,7 +430,7 @@ pub fn endFrame(
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn beginRendering(
pub fn beginPass(
self: *Self,
swapchain: Handle(Swapchain),
config: RenderPassConfig,
@ -474,6 +485,79 @@ pub fn beginRendering(
&barrier,
);
var depth_attachment: vk.VkRenderingAttachmentInfo = undefined;
if (config.depth_enabled) {
const depth_format = depthFormatToVk(config.depth_format);
try raw_swapchain.ensureDepth(depth_format);
const depth_aspect: vk.VkImageAspectFlags = if (config.depth_format == .d24_unorm_s8)
vk.VK_IMAGE_ASPECT_DEPTH_BIT | vk.VK_IMAGE_ASPECT_STENCIL_BIT
else
vk.VK_IMAGE_ASPECT_DEPTH_BIT;
const depth_barrier: vk.VkImageMemoryBarrier = .{
.sType = vk.VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = 0,
.dstAccessMask = vk.VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
.oldLayout = vk.VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = vk.VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
.srcQueueFamilyIndex = vk.VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = vk.VK_QUEUE_FAMILY_IGNORED,
.image = raw_swapchain.depth_images[frame.image_index],
.subresourceRange = .{
.aspectMask = depth_aspect,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
vk.vkCmdPipelineBarrier(
frame.cmd_buf,
vk.VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
vk.VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | vk.VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
0,
0,
null,
0,
null,
1,
&depth_barrier,
);
depth_attachment = .{
.sType = vk.VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.imageView = raw_swapchain.depth_views[frame.image_index],
.imageLayout = vk.VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
.loadOp = switch (config.depth_load_op) {
.clear => vk.VK_ATTACHMENT_LOAD_OP_CLEAR,
.load => vk.VK_ATTACHMENT_LOAD_OP_LOAD,
.dont_care => vk.VK_ATTACHMENT_LOAD_OP_DONT_CARE,
},
.storeOp = switch (config.depth_store_op) {
.store => vk.VK_ATTACHMENT_STORE_OP_STORE,
.dont_care => vk.VK_ATTACHMENT_STORE_OP_DONT_CARE,
},
.clearValue = .{
.depthStencil = .{
.depth = config.clear_depth,
.stencil = @intCast(config.clear_stencil),
},
},
};
}
const color_attachment: vk.VkRenderingAttachmentInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.imageView = raw_swapchain.image_views[frame.image_index],
@ -494,7 +578,7 @@ pub fn beginRendering(
},
};
const rendering_info: vk.VkRenderingInfo = .{
var rendering_info: vk.VkRenderingInfo = .{
.sType = vk.VK_STRUCTURE_TYPE_RENDERING_INFO,
.renderArea = .{
.offset = .{
@ -507,6 +591,9 @@ pub fn beginRendering(
.colorAttachmentCount = 1,
.pColorAttachments = &color_attachment,
};
if (config.depth_enabled) {
rendering_info.pDepthAttachment = &depth_attachment;
}
vk.vkCmdBeginRendering(
frame.cmd_buf,
@ -536,7 +623,7 @@ pub fn beginRendering(
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn endRendering(
pub fn endPass(
self: *Self,
swapchain: Handle(Swapchain),
) void {

28
src/vendor/fast_obj/fast_obj.c vendored Normal file
View file

@ -0,0 +1,28 @@
/*
*
* MIT License
*
* Copyright (c) 2018 Richard Knight
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
#define FAST_OBJ_IMPLEMENTATION
#include "fast_obj.h"

1357
src/vendor/fast_obj/fast_obj.h vendored Normal file

File diff suppressed because it is too large Load diff

Binary file not shown.