Compare commits

..

15 commits

35 changed files with 21416 additions and 478 deletions

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "libs/sdl"]
path = libs/sdl
url = git@github.com:ikskuh/SDL.zig.git

View file

@ -1 +1,2 @@
libs/
vk.xml

4663
assets/models/teapot.obj Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,13 @@
# Blender 4.2.0 MTL File: 'tescoPiwo.blend'
# www.blender.org
newmtl Material.001
Ns 360.000000
Ka 0.500000 0.500000 0.500000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.500000
d 1.000000
illum 3
map_Kd tescoPiwoTexture.png
map_Bump -bm 1.000000 /home/przmk/Downloads/Downloads/tescoPiwoNormal.png

1751
assets/models/tescoPiwo.obj Normal file

File diff suppressed because it is too large Load diff

BIN
assets/textures/giraffe.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 674 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

BIN
assets/textures/test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View file

@ -1,6 +1,6 @@
const std = @import("std");
const sdl = @import("sdl");
const vkgen = @import("vulkan_zig");
const vkgen = @import("vulkan");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
@ -12,12 +12,15 @@ pub fn build(b: *std.Build) void {
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
.link_libc = true,
});
// exe.addIncludePath(b.path("include/"));
// --- Dependencies ---
// Vulkan
const vkzig_dep = b.dependency("vulkan_zig", .{
const vkzig_dep = b.dependency("vulkan", .{
.registry = @as([]const u8, b.pathFromRoot("./vk.xml")),
});
const vkzig_bindings = vkzig_dep.module("vulkan-zig");
@ -25,28 +28,46 @@ pub fn build(b: *std.Build) void {
const shader_comp = vkgen.ShaderCompileStep.create(
b,
&[_][]const u8{ "glslc", "--target-env=vulkan1.3" },
.{ .real_path = "glslc" },
&[_][]const u8{"--target-env=vulkan1.3"},
"-o",
);
shader_comp.add("shader_frag", "src/shaders/shader.frag", .{});
shader_comp.add("shader_vert", "src/shaders/shader.vert", .{});
shader_comp.add("second_frag", "src/shaders/second.frag", .{});
shader_comp.add("second_vert", "src/shaders/second.vert", .{});
exe.root_module.addImport("shaders", shader_comp.getModule());
// SDL2
const sdl_sdk = sdl.init(b, null, null);
sdl_sdk.link(exe, .dynamic, .SDL2);
const sdl_sdk = sdl.init(b, .{});
sdl_sdk.link(exe, .dynamic, sdl.Library.SDL2);
exe.root_module.addImport("sdl2", sdl_sdk.getWrapperModuleVulkan(vkzig_bindings));
// zmath
const zmath = b.dependency("zmath", .{});
exe.root_module.addImport("zmath", zmath.module("root"));
// zstbi
const zstbi = b.dependency("zstbi", .{});
exe.root_module.addImport("zstbi", zstbi.module("root"));
exe.linkLibrary(zstbi.artifact("zstbi"));
// Assimp
exe.linkSystemLibrary("assimp");
// ---
b.installArtifact(exe);
const exe_check = b.addExecutable(.{
.name = "vulkan-test",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
const check = b.step("check", "Check if vulkan-test compiles");
check.dependOn(&exe.step);
check.dependOn(&exe_check.step);
const run_cmd = b.addRunArtifact(exe);

View file

@ -5,13 +5,15 @@
.dependencies = .{
.zmath = .{ .path = "libs/zmath" },
.vulkan_zig = .{
.url = "https://github.com/Snektron/vulkan-zig/archive/9f6e6177b1fdb3ed22231d9216a24480e84cfa5e.tar.gz",
.hash = "1220f2961df224f7d35dee774b26194b8b937cc252fa8e4023407776c58521d53e38",
.zstbi = .{ .path = "libs/zstbi" },
.sdl = .{ .path = "libs/sdl" },
.vulkan = .{
.url = "https://github.com/Snektron/vulkan-zig/archive/f7b21d034f527765f62935de1b62855033621989.tar.gz",
.hash = "12201e484e173e70634e664864763223427703e677f28c63ebec9332513c8ca5121c",
},
.sdl = .{
.url = "https://github.com/MasterQ32/SDL.zig/archive/1432ed3f6a020973906fbc996868131ae1d631be.tar.gz",
.hash = "1220ebeeaade31e207a56977aff537a65e6338cddc68d50217ddf30bbc58fb27d367",
.obj = .{
.url = "https://github.com/chip2n/zig-obj/archive/58f524ed6834790b29ac1e97b2f9e6b7de7b5346.tar.gz",
.hash = "1220ff46dcbeb40677c0ce8560b954885beec8b699835d9e6686beab72aa9d422c79",
},
},
@ -19,8 +21,5 @@
"build.zig",
"build.zig.zon",
"src",
// For example...
//"LICENSE",
//"README.md",
},
}

1
libs/sdl Submodule

@ -0,0 +1 @@
Subproject commit 172a84e7b5ce7d4891b8b970c68f4532f96aa7e9

View file

@ -329,7 +329,7 @@ pub inline fn boolx16(
// zig fmt: on
pub inline fn veclen(comptime T: type) comptime_int {
return @typeInfo(T).Vector.len;
return @typeInfo(T).vector.len;
}
pub inline fn splat(comptime T: type, value: f32) T {
@ -413,14 +413,14 @@ pub inline fn storeArr4(arr: *[4]f32, v: F32x4) void {
}
pub inline fn arr3Ptr(ptr: anytype) *const [3]f32 {
comptime assert(@typeInfo(@TypeOf(ptr)) == .Pointer);
comptime assert(@typeInfo(@TypeOf(ptr)) == .pointer);
const T = std.meta.Child(@TypeOf(ptr));
comptime assert(T == F32x4);
return @as(*const [3]f32, @ptrCast(ptr));
}
pub inline fn arrNPtr(ptr: anytype) [*]const f32 {
comptime assert(@typeInfo(@TypeOf(ptr)) == .Pointer);
comptime assert(@typeInfo(@TypeOf(ptr)) == .pointer);
const T = std.meta.Child(@TypeOf(ptr));
comptime assert(T == Mat or T == F32x4 or T == F32x8 or T == F32x16);
return @as([*]const f32, @ptrCast(ptr));

22
libs/zstbi/LICENSE Normal file
View file

@ -0,0 +1,22 @@
MIT License
Copyright (c) 2021 Michal Ziulek
Copyright (c) 2024 zig-gamedev contributors
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.

93
libs/zstbi/README.md Normal file
View file

@ -0,0 +1,93 @@
# zstbi v0.10.0 - stb image bindings
## Features
* Supports Zig memory allocators
* Supports decoding most popular formats
* Supports HDR images
* Supports 8-bits and 16-bits per channel
* Supports image resizing
* Supports image writing (.png, .jpg)
## Getting started
Copy `zstbi` to a subdirectory of your project and add the following to your `build.zig.zon` .dependencies:
```zig
.zstbi = .{ .path = "libs/zstbi" },
```
Then in your `build.zig` add:
```zig
pub fn build(b: *std.Build) void {
const exe = b.addExecutable(.{ ... });
const zstbi = b.dependency("zstbi", .{});
exe.root_module.addImport("zstbi", zstbi.module("root"));
exe.linkLibrary(zstbi.artifact("zstbi"));
}
```
Now in your code you may import and use `zstbi`.
Init the lib. `zstbi.init()` is cheap and you may call it whenever you need to change memory allocator. Must be called from the main thread.
```zig
const zstbi = @import("zstbi");
zstbi.init(allocator);
defer zstbi.deinit();
```
```zig
pub const Image = struct {
data: []u8,
width: u32,
height: u32,
num_components: u32,
bytes_per_component: u32,
bytes_per_row: u32,
is_hdr: bool,
...
```
```zig
pub fn loadFromFile(pathname: [:0]const u8, forced_num_components: u32) !Image
pub fn loadFromMemory(data: []const u8, forced_num_components: u32) !Image
pub fn createEmpty(width: u32, height: u32, num_components: u32, args: struct {
bytes_per_component: u32 = 0,
bytes_per_row: u32 = 0,
}) !Image
pub fn info(pathname: [:0]const u8) struct {
is_supported: bool,
width: u32,
height: u32,
num_components: u32,
}
pub fn resize(image: *const Image, new_width: u32, new_height: u32) Image
pub fn writeToFile(
image: *const Image,
filename: [:0]const u8,
image_format: ImageWriteFormat,
) ImageWriteError!void
pub fn writeToFn(
image: *const Image,
write_fn: *const fn (ctx: ?*anyopaque, data: ?*anyopaque, size: c_int) callconv(.C) void,
context: ?*anyopaque,
image_format: ImageWriteFormat,
) ImageWriteError!void
```
```zig
var image = try zstbi.Image.loadFromFile("data/image.png", forced_num_components);
defer image.deinit();
const new_resized_image = image.resize(1024, 1024);
```
Misc functions:
```zig
pub fn isHdr(filename: [:0]const u8) bool
pub fn is16bit(filename: [:0]const u8) bool
pub fn setFlipVerticallyOnLoad(should_flip: bool) void
```

52
libs/zstbi/build.zig Normal file
View file

@ -0,0 +1,52 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const target = b.standardTargetOptions(.{});
_ = b.addModule("root", .{
.root_source_file = b.path("src/zstbi.zig"),
});
const zstbi_lib = b.addStaticLibrary(.{
.name = "zstbi",
.target = target,
.optimize = optimize,
});
zstbi_lib.addIncludePath(b.path("libs/stbi"));
if (optimize == .Debug) {
// TODO: Workaround for Zig bug.
zstbi_lib.addCSourceFile(.{
.file = b.path("src/zstbi.c"),
.flags = &.{
"-std=c99",
"-fno-sanitize=undefined",
"-g",
"-O0",
},
});
} else {
zstbi_lib.addCSourceFile(.{
.file = b.path("src/zstbi.c"),
.flags = &.{
"-std=c99",
"-fno-sanitize=undefined",
},
});
}
zstbi_lib.linkLibC();
b.installArtifact(zstbi_lib);
const test_step = b.step("test", "Run zstbi tests");
const tests = b.addTest(.{
.name = "zstbi-tests",
.root_source_file = b.path("src/zstbi.zig"),
.target = target,
.optimize = optimize,
});
tests.linkLibrary(zstbi_lib);
b.installArtifact(tests);
test_step.dependOn(&b.addRunArtifact(tests).step);
}

11
libs/zstbi/build.zig.zon Normal file
View file

@ -0,0 +1,11 @@
.{
.name = "zstbi",
.version = "0.10.0",
.paths = .{
"build.zig",
"build.zig.zon",
"libs",
"src",
"README.md",
},
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

32
libs/zstbi/src/zstbi.c Normal file
View file

@ -0,0 +1,32 @@
#include <stdlib.h>
void* (*zstbiMallocPtr)(size_t size) = NULL;
void* (*zstbiReallocPtr)(void* ptr, size_t size) = NULL;
void (*zstbiFreePtr)(void* ptr) = NULL;
#define STBI_MALLOC(size) zstbiMallocPtr(size)
#define STBI_REALLOC(ptr, size) zstbiReallocPtr(ptr, size)
#define STBI_FREE(ptr) zstbiFreePtr(ptr)
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
void* (*zstbirMallocPtr)(size_t size, void* context) = NULL;
void (*zstbirFreePtr)(void* ptr, void* context) = NULL;
#define STBIR_MALLOC(size, context) zstbirMallocPtr(size, context)
#define STBIR_FREE(ptr, context) zstbirFreePtr(ptr, context)
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#include "stb_image_resize.h"
void* (*zstbiwMallocPtr)(size_t size) = NULL;
void* (*zstbiwReallocPtr)(void* ptr, size_t size) = NULL;
void (*zstbiwFreePtr)(void* ptr) = NULL;
#define STBIW_MALLOC(size) zstbiwMallocPtr(size)
#define STBIW_REALLOC(ptr, size) zstbiwReallocPtr(ptr, size)
#define STBIW_FREE(ptr) zstbiwFreePtr(ptr)
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"

619
libs/zstbi/src/zstbi.zig Normal file
View file

@ -0,0 +1,619 @@
const std = @import("std");
const testing = std.testing;
const assert = std.debug.assert;
pub fn init(allocator: std.mem.Allocator) void {
assert(mem_allocator == null);
mem_allocator = allocator;
mem_allocations = std.AutoHashMap(usize, usize).init(allocator);
// stb image
zstbiMallocPtr = zstbiMalloc;
zstbiReallocPtr = zstbiRealloc;
zstbiFreePtr = zstbiFree;
// stb image resize
zstbirMallocPtr = zstbirMalloc;
zstbirFreePtr = zstbirFree;
// stb image write
zstbiwMallocPtr = zstbiMalloc;
zstbiwReallocPtr = zstbiRealloc;
zstbiwFreePtr = zstbiFree;
}
pub fn deinit() void {
assert(mem_allocator != null);
assert(mem_allocations.?.count() == 0);
setFlipVerticallyOnLoad(false);
setFlipVerticallyOnWrite(false);
mem_allocations.?.deinit();
mem_allocations = null;
mem_allocator = null;
}
pub const JpgWriteSettings = struct {
quality: u32,
};
pub const ImageWriteFormat = union(enum) {
png,
jpg: JpgWriteSettings,
};
pub const ImageWriteError = error{
CouldNotWriteImage,
};
pub const Image = struct {
data: []u8,
width: u32,
height: u32,
num_components: u32,
bytes_per_component: u32,
bytes_per_row: u32,
is_hdr: bool,
pub fn info(pathname: [:0]const u8) struct {
is_supported: bool,
width: u32,
height: u32,
num_components: u32,
} {
assert(mem_allocator != null);
var w: c_int = 0;
var h: c_int = 0;
var c: c_int = 0;
const is_supported = stbi_info(pathname, &w, &h, &c);
return .{
.is_supported = if (is_supported == 1) true else false,
.width = @as(u32, @intCast(w)),
.height = @as(u32, @intCast(h)),
.num_components = @as(u32, @intCast(c)),
};
}
pub fn loadFromFile(pathname: [:0]const u8, forced_num_components: u32) !Image {
assert(mem_allocator != null);
var width: u32 = 0;
var height: u32 = 0;
var num_components: u32 = 0;
var bytes_per_component: u32 = 0;
var bytes_per_row: u32 = 0;
var is_hdr = false;
const data = if (isHdr(pathname)) data: {
var x: c_int = undefined;
var y: c_int = undefined;
var ch: c_int = undefined;
const ptr = stbi_loadf(
pathname,
&x,
&y,
&ch,
@as(c_int, @intCast(forced_num_components)),
);
if (ptr == null) return error.ImageInitFailed;
num_components = if (forced_num_components == 0) @as(u32, @intCast(ch)) else forced_num_components;
width = @as(u32, @intCast(x));
height = @as(u32, @intCast(y));
bytes_per_component = 2;
bytes_per_row = width * num_components * bytes_per_component;
is_hdr = true;
// Convert each component from f32 to f16.
var ptr_f16 = @as([*]f16, @ptrCast(ptr.?));
const num = width * height * num_components;
var i: u32 = 0;
while (i < num) : (i += 1) {
ptr_f16[i] = @as(f16, @floatCast(ptr.?[i]));
}
break :data @as([*]u8, @ptrCast(ptr_f16))[0 .. height * bytes_per_row];
} else data: {
var x: c_int = undefined;
var y: c_int = undefined;
var ch: c_int = undefined;
const is_16bit = is16bit(pathname);
const ptr = if (is_16bit) @as(?[*]u8, @ptrCast(stbi_load_16(
pathname,
&x,
&y,
&ch,
@as(c_int, @intCast(forced_num_components)),
))) else stbi_load(
pathname,
&x,
&y,
&ch,
@as(c_int, @intCast(forced_num_components)),
);
if (ptr == null) return error.ImageInitFailed;
num_components = if (forced_num_components == 0) @as(u32, @intCast(ch)) else forced_num_components;
width = @as(u32, @intCast(x));
height = @as(u32, @intCast(y));
bytes_per_component = if (is_16bit) 2 else 1;
bytes_per_row = width * num_components * bytes_per_component;
is_hdr = false;
break :data @as([*]u8, @ptrCast(ptr))[0 .. height * bytes_per_row];
};
return Image{
.data = data,
.width = width,
.height = height,
.num_components = num_components,
.bytes_per_component = bytes_per_component,
.bytes_per_row = bytes_per_row,
.is_hdr = is_hdr,
};
}
pub fn loadFromMemory(data: []const u8, forced_num_components: u32) !Image {
assert(mem_allocator != null);
var width: u32 = 0;
var height: u32 = 0;
var num_components: u32 = 0;
var bytes_per_component: u32 = 0;
var bytes_per_row: u32 = 0;
var is_hdr = false;
const image_data = if (isHdrFromMem(data)) data: {
var x: c_int = undefined;
var y: c_int = undefined;
var ch: c_int = undefined;
const ptr = stbi_loadf_from_memory(
data.ptr,
@as(c_int, @intCast(data.len)),
&x,
&y,
&ch,
@as(c_int, @intCast(forced_num_components)),
);
if (ptr == null) return error.ImageInitFailed;
num_components = if (forced_num_components == 0) @as(u32, @intCast(ch)) else forced_num_components;
width = @as(u32, @intCast(x));
height = @as(u32, @intCast(y));
bytes_per_component = 2;
bytes_per_row = width * num_components * bytes_per_component;
is_hdr = true;
// Convert each component from f32 to f16.
var ptr_f16 = @as([*]f16, @ptrCast(ptr.?));
const num = width * height * num_components;
var i: u32 = 0;
while (i < num) : (i += 1) {
ptr_f16[i] = @as(f16, @floatCast(ptr.?[i]));
}
break :data @as([*]u8, @ptrCast(ptr_f16))[0 .. height * bytes_per_row];
} else data: {
var x: c_int = undefined;
var y: c_int = undefined;
var ch: c_int = undefined;
const ptr = stbi_load_from_memory(
data.ptr,
@as(c_int, @intCast(data.len)),
&x,
&y,
&ch,
@as(c_int, @intCast(forced_num_components)),
);
if (ptr == null) return error.ImageInitFailed;
num_components = if (forced_num_components == 0) @as(u32, @intCast(ch)) else forced_num_components;
width = @as(u32, @intCast(x));
height = @as(u32, @intCast(y));
bytes_per_component = 1;
bytes_per_row = width * num_components * bytes_per_component;
break :data @as([*]u8, @ptrCast(ptr))[0 .. height * bytes_per_row];
};
return Image{
.data = image_data,
.width = width,
.height = height,
.num_components = num_components,
.bytes_per_component = bytes_per_component,
.bytes_per_row = bytes_per_row,
.is_hdr = is_hdr,
};
}
pub fn createEmpty(width: u32, height: u32, num_components: u32, args: struct {
bytes_per_component: u32 = 0,
bytes_per_row: u32 = 0,
}) !Image {
assert(mem_allocator != null);
const bytes_per_component = if (args.bytes_per_component == 0) 1 else args.bytes_per_component;
const bytes_per_row = if (args.bytes_per_row == 0)
width * num_components * bytes_per_component
else
args.bytes_per_row;
const size = height * bytes_per_row;
const data = @as([*]u8, @ptrCast(zstbiMalloc(size)));
@memset(data[0..size], 0);
return Image{
.data = data[0..size],
.width = width,
.height = height,
.num_components = num_components,
.bytes_per_component = bytes_per_component,
.bytes_per_row = bytes_per_row,
.is_hdr = false,
};
}
pub fn resize(image: *const Image, new_width: u32, new_height: u32) Image {
assert(mem_allocator != null);
// TODO: Add support for HDR images
const new_bytes_per_row = new_width * image.num_components * image.bytes_per_component;
const new_size = new_height * new_bytes_per_row;
const new_data = @as([*]u8, @ptrCast(zstbiMalloc(new_size)));
stbir_resize_uint8(
image.data.ptr,
@as(c_int, @intCast(image.width)),
@as(c_int, @intCast(image.height)),
0,
new_data,
@as(c_int, @intCast(new_width)),
@as(c_int, @intCast(new_height)),
0,
@as(c_int, @intCast(image.num_components)),
);
return .{
.data = new_data[0..new_size],
.width = new_width,
.height = new_height,
.num_components = image.num_components,
.bytes_per_component = image.bytes_per_component,
.bytes_per_row = new_bytes_per_row,
.is_hdr = image.is_hdr,
};
}
pub fn writeToFile(
image: Image,
filename: [:0]const u8,
image_format: ImageWriteFormat,
) ImageWriteError!void {
assert(mem_allocator != null);
const w = @as(c_int, @intCast(image.width));
const h = @as(c_int, @intCast(image.height));
const comp = @as(c_int, @intCast(image.num_components));
const result = switch (image_format) {
.png => stbi_write_png(filename.ptr, w, h, comp, image.data.ptr, 0),
.jpg => |settings| stbi_write_jpg(
filename.ptr,
w,
h,
comp,
image.data.ptr,
@as(c_int, @intCast(settings.quality)),
),
};
// if the result is 0 then it means an error occured (per stb image write docs)
if (result == 0) {
return ImageWriteError.CouldNotWriteImage;
}
}
pub fn writeToFn(
image: Image,
write_fn: *const fn (ctx: ?*anyopaque, data: ?*anyopaque, size: c_int) callconv(.C) void,
context: ?*anyopaque,
image_format: ImageWriteFormat,
) ImageWriteError!void {
assert(mem_allocator != null);
const w = @as(c_int, @intCast(image.width));
const h = @as(c_int, @intCast(image.height));
const comp = @as(c_int, @intCast(image.num_components));
const result = switch (image_format) {
.png => stbi_write_png_to_func(write_fn, context, w, h, comp, image.data.ptr, 0),
.jpg => |settings| stbi_write_jpg_to_func(
write_fn,
context,
w,
h,
comp,
image.data.ptr,
@as(c_int, @intCast(settings.quality)),
),
};
// if the result is 0 then it means an error occured (per stb image write docs)
if (result == 0) {
return ImageWriteError.CouldNotWriteImage;
}
}
pub fn deinit(image: *Image) void {
stbi_image_free(image.data.ptr);
image.* = undefined;
}
};
/// `pub fn setHdrToLdrScale(scale: f32) void`
pub const setHdrToLdrScale = stbi_hdr_to_ldr_scale;
/// `pub fn setHdrToLdrGamma(gamma: f32) void`
pub const setHdrToLdrGamma = stbi_hdr_to_ldr_gamma;
/// `pub fn setLdrToHdrScale(scale: f32) void`
pub const setLdrToHdrScale = stbi_ldr_to_hdr_scale;
/// `pub fn setLdrToHdrGamma(gamma: f32) void`
pub const setLdrToHdrGamma = stbi_ldr_to_hdr_gamma;
pub fn isHdr(filename: [:0]const u8) bool {
return stbi_is_hdr(filename) != 0;
}
pub fn isHdrFromMem(buffer: []const u8) bool {
return stbi_is_hdr_from_memory(buffer.ptr, @as(c_int, @intCast(buffer.len))) != 0;
}
pub fn is16bit(filename: [:0]const u8) bool {
return stbi_is_16_bit(filename) != 0;
}
pub fn setFlipVerticallyOnLoad(should_flip: bool) void {
stbi_set_flip_vertically_on_load(if (should_flip) 1 else 0);
}
pub fn setFlipVerticallyOnWrite(should_flip: bool) void {
stbi_flip_vertically_on_write(if (should_flip) 1 else 0);
}
var mem_allocator: ?std.mem.Allocator = null;
var mem_allocations: ?std.AutoHashMap(usize, usize) = null;
var mem_mutex: std.Thread.Mutex = .{};
const mem_alignment = 16;
extern var zstbiMallocPtr: ?*const fn (size: usize) callconv(.C) ?*anyopaque;
extern var zstbiwMallocPtr: ?*const fn (size: usize) callconv(.C) ?*anyopaque;
fn zstbiMalloc(size: usize) callconv(.C) ?*anyopaque {
mem_mutex.lock();
defer mem_mutex.unlock();
const mem = mem_allocator.?.alignedAlloc(
u8,
mem_alignment,
size,
) catch @panic("zstbi: out of memory");
mem_allocations.?.put(@intFromPtr(mem.ptr), size) catch @panic("zstbi: out of memory");
return mem.ptr;
}
extern var zstbiReallocPtr: ?*const fn (ptr: ?*anyopaque, size: usize) callconv(.C) ?*anyopaque;
extern var zstbiwReallocPtr: ?*const fn (ptr: ?*anyopaque, size: usize) callconv(.C) ?*anyopaque;
fn zstbiRealloc(ptr: ?*anyopaque, size: usize) callconv(.C) ?*anyopaque {
mem_mutex.lock();
defer mem_mutex.unlock();
const old_size = if (ptr != null) mem_allocations.?.get(@intFromPtr(ptr.?)).? else 0;
const old_mem = if (old_size > 0)
@as([*]align(mem_alignment) u8, @ptrCast(@alignCast(ptr)))[0..old_size]
else
@as([*]align(mem_alignment) u8, undefined)[0..0];
const new_mem = mem_allocator.?.realloc(old_mem, size) catch @panic("zstbi: out of memory");
if (ptr != null) {
const removed = mem_allocations.?.remove(@intFromPtr(ptr.?));
std.debug.assert(removed);
}
mem_allocations.?.put(@intFromPtr(new_mem.ptr), size) catch @panic("zstbi: out of memory");
return new_mem.ptr;
}
extern var zstbiFreePtr: ?*const fn (maybe_ptr: ?*anyopaque) callconv(.C) void;
extern var zstbiwFreePtr: ?*const fn (maybe_ptr: ?*anyopaque) callconv(.C) void;
fn zstbiFree(maybe_ptr: ?*anyopaque) callconv(.C) void {
if (maybe_ptr) |ptr| {
mem_mutex.lock();
defer mem_mutex.unlock();
const size = mem_allocations.?.fetchRemove(@intFromPtr(ptr)).?.value;
const mem = @as([*]align(mem_alignment) u8, @ptrCast(@alignCast(ptr)))[0..size];
mem_allocator.?.free(mem);
}
}
extern var zstbirMallocPtr: ?*const fn (size: usize, maybe_context: ?*anyopaque) callconv(.C) ?*anyopaque;
fn zstbirMalloc(size: usize, _: ?*anyopaque) callconv(.C) ?*anyopaque {
return zstbiMalloc(size);
}
extern var zstbirFreePtr: ?*const fn (maybe_ptr: ?*anyopaque, maybe_context: ?*anyopaque) callconv(.C) void;
fn zstbirFree(maybe_ptr: ?*anyopaque, _: ?*anyopaque) callconv(.C) void {
zstbiFree(maybe_ptr);
}
extern fn stbi_info(filename: [*:0]const u8, x: *c_int, y: *c_int, comp: *c_int) c_int;
extern fn stbi_load(
filename: [*:0]const u8,
x: *c_int,
y: *c_int,
channels_in_file: *c_int,
desired_channels: c_int,
) ?[*]u8;
extern fn stbi_load_16(
filename: [*:0]const u8,
x: *c_int,
y: *c_int,
channels_in_file: *c_int,
desired_channels: c_int,
) ?[*]u16;
extern fn stbi_loadf(
filename: [*:0]const u8,
x: *c_int,
y: *c_int,
channels_in_file: *c_int,
desired_channels: c_int,
) ?[*]f32;
pub extern fn stbi_load_from_memory(
buffer: [*]const u8,
len: c_int,
x: *c_int,
y: *c_int,
channels_in_file: *c_int,
desired_channels: c_int,
) ?[*]u8;
pub extern fn stbi_loadf_from_memory(
buffer: [*]const u8,
len: c_int,
x: *c_int,
y: *c_int,
channels_in_file: *c_int,
desired_channels: c_int,
) ?[*]f32;
extern fn stbi_image_free(image_data: ?[*]u8) void;
extern fn stbi_hdr_to_ldr_scale(scale: f32) void;
extern fn stbi_hdr_to_ldr_gamma(gamma: f32) void;
extern fn stbi_ldr_to_hdr_scale(scale: f32) void;
extern fn stbi_ldr_to_hdr_gamma(gamma: f32) void;
extern fn stbi_is_16_bit(filename: [*:0]const u8) c_int;
extern fn stbi_is_hdr(filename: [*:0]const u8) c_int;
extern fn stbi_is_hdr_from_memory(buffer: [*]const u8, len: c_int) c_int;
extern fn stbi_set_flip_vertically_on_load(flag_true_if_should_flip: c_int) void;
extern fn stbi_flip_vertically_on_write(flag: c_int) void; // flag is non-zero to flip data vertically
extern fn stbir_resize_uint8(
input_pixels: [*]const u8,
input_w: c_int,
input_h: c_int,
input_stride_in_bytes: c_int,
output_pixels: [*]u8,
output_w: c_int,
output_h: c_int,
output_stride_in_bytes: c_int,
num_channels: c_int,
) void;
extern fn stbi_write_jpg(
filename: [*:0]const u8,
w: c_int,
h: c_int,
comp: c_int,
data: [*]const u8,
quality: c_int,
) c_int;
extern fn stbi_write_png(
filename: [*:0]const u8,
w: c_int,
h: c_int,
comp: c_int,
data: [*]const u8,
stride_in_bytes: c_int,
) c_int;
extern fn stbi_write_png_to_func(
func: *const fn (?*anyopaque, ?*anyopaque, c_int) callconv(.C) void,
context: ?*anyopaque,
w: c_int,
h: c_int,
comp: c_int,
data: [*]const u8,
stride_in_bytes: c_int,
) c_int;
extern fn stbi_write_jpg_to_func(
func: *const fn (?*anyopaque, ?*anyopaque, c_int) callconv(.C) void,
context: ?*anyopaque,
x: c_int,
y: c_int,
comp: c_int,
data: [*]const u8,
quality: c_int,
) c_int;
test "zstbi basic" {
init(testing.allocator);
defer deinit();
var im1 = try Image.createEmpty(8, 6, 4, .{});
defer im1.deinit();
try testing.expect(im1.width == 8);
try testing.expect(im1.height == 6);
try testing.expect(im1.num_components == 4);
}
test "zstbi resize" {
init(testing.allocator);
defer deinit();
var im1 = try Image.createEmpty(32, 32, 4, .{});
defer im1.deinit();
var im2 = im1.resize(8, 6);
defer im2.deinit();
try testing.expect(im2.width == 8);
try testing.expect(im2.height == 6);
try testing.expect(im2.num_components == 4);
}
test "zstbi write and load file" {
init(testing.allocator);
defer deinit();
const pth = try std.fs.selfExeDirPathAlloc(testing.allocator);
defer testing.allocator.free(pth);
try std.posix.chdir(pth);
var img = try Image.createEmpty(8, 6, 4, .{});
defer img.deinit();
try img.writeToFile("test_img.png", ImageWriteFormat.png);
try img.writeToFile("test_img.jpg", .{ .jpg = .{ .quality = 80 } });
var img_png = try Image.loadFromFile("test_img.png", 0);
defer img_png.deinit();
try testing.expect(img_png.width == img.width);
try testing.expect(img_png.height == img.height);
try testing.expect(img_png.num_components == img.num_components);
var img_jpg = try Image.loadFromFile("test_img.jpg", 0);
defer img_jpg.deinit();
try testing.expect(img_jpg.width == img.width);
try testing.expect(img_jpg.height == img.height);
try testing.expect(img_jpg.num_components == 3); // RGB JPEG
try std.fs.cwd().deleteFile("test_img.png");
try std.fs.cwd().deleteFile("test_img.jpg");
}

1
profile.json Normal file

File diff suppressed because one or more lines are too long

View file

@ -6,11 +6,12 @@ const Utilities = @import("utilities.zig");
const Vertex = Utilities.Vertex;
const Device = @import("vulkan_renderer.zig").Device;
const Instance = @import("vulkan_renderer.zig").Instance;
const UboModel = @import("vulkan_renderer.zig").UboModel;
const Model = @import("vulkan_renderer.zig").Model;
const Self = @This();
ubo_model: UboModel,
ubo_model: Model,
tex_id: u32,
vertex_count: u32,
vertex_buffer: vk.Buffer,
@ -34,6 +35,7 @@ pub fn new(
transfer_command_pool: vk.CommandPool,
vertices: []const Vertex,
indices: []const u32,
tex_id: u32,
allocator: std.mem.Allocator,
) !Self {
var self: Self = undefined;
@ -50,6 +52,7 @@ pub fn new(
try self.createIndexBuffer(transfer_queue, transfer_command_pool, indices);
self.ubo_model = .{ .model = zm.identity() };
self.tex_id = tex_id;
return self;
}
@ -122,7 +125,6 @@ fn createVertexBuffer(
staging_buffer,
self.vertex_buffer,
buffer_size,
self.allocator,
);
}
@ -178,6 +180,5 @@ fn createIndexBuffer(
staging_buffer,
self.index_buffer,
buffer_size,
self.allocator,
);
}

194
src/MeshModel.zig Normal file
View file

@ -0,0 +1,194 @@
const std = @import("std");
const vk = @import("vulkan");
const zm = @import("zmath");
const ai = @import("assimp.zig").c;
const Mesh = @import("Mesh.zig");
const Device = @import("vulkan_renderer.zig").Device;
const Instance = @import("vulkan_renderer.zig").Instance;
const Vertex = @import("utilities.zig").Vertex;
const Self = @This();
allocator: std.mem.Allocator,
mesh_list: std.ArrayList(Mesh),
model: zm.Mat,
pub fn new(allocator: std.mem.Allocator, mesh_list: std.ArrayList(Mesh)) Self {
var new_mesh_model: Self = undefined;
new_mesh_model.allocator = allocator;
new_mesh_model.mesh_list = mesh_list;
new_mesh_model.model = zm.identity();
return new_mesh_model;
}
pub fn destroy(self: *Self) void {
for (0..self.mesh_list.items.len) |i| {
self.mesh_list.items[i].destroyBuffers();
}
self.mesh_list.deinit();
}
pub fn getMesh(self: Self, idx: usize) !Mesh {
if (idx >= self.mesh_list.len - 1) {
return error.MeshIndexOutOfBounds;
}
return self.mesh_list.items[idx];
}
pub fn loadMaterials(allocator: std.mem.Allocator, scene: *const ai.aiScene) !std.ArrayList(?[]const u8) {
// Create 1:1 sized list of textures
var texture_list = try std.ArrayList(?[]const u8).initCapacity(allocator, scene.mNumMaterials);
// Go through each material and copy its texture file name (if it exists)
for (0..scene.mNumMaterials) |i| {
// Get the material
const material = scene.mMaterials[i];
// Initialise the texture to empty string (will be replaced if the texture exists)
// try texture_list.append("");
// Check for diffuse texture (standard detail texture)
if (ai.aiGetMaterialTextureCount(material, ai.aiTextureType_DIFFUSE) != 0) {
// Get the path of the texture file
var path: ai.aiString = undefined;
if (ai.aiGetMaterialTexture(
material,
ai.aiTextureType_DIFFUSE,
0,
&path,
null,
null,
null,
null,
null,
null,
) == ai.AI_SUCCESS) {
// Cut of any directory information already present
var it = std.mem.splitBackwardsAny(u8, &path.data, "\\/");
if (it.next()) |filename| {
texture_list.appendAssumeCapacity(try allocator.dupe(u8, filename));
}
} else {
texture_list.appendAssumeCapacity(null);
}
} else {
texture_list.appendAssumeCapacity(null);
}
}
return texture_list;
}
pub fn loadNode(
allocator: std.mem.Allocator,
instance: Instance,
pdev: vk.PhysicalDevice,
device: Device,
transfer_queue: vk.Queue,
transfer_command_pool: vk.CommandPool,
node: *const ai.aiNode,
scene: *const ai.aiScene,
mat_to_tex: []u32,
) !std.ArrayList(Mesh) {
var mesh_list = std.ArrayList(Mesh).init(allocator);
// Go through each mesh at this node and create it, then add it to our mesh list
for (0..node.mNumMeshes) |i| {
// Load mesh here
try mesh_list.append(try loadMesh(
allocator,
instance,
pdev,
device,
transfer_queue,
transfer_command_pool,
scene.mMeshes[node.mMeshes[i]],
mat_to_tex,
));
}
// Go through each node attached to this node and load it, then append their meshes to this node's mesh list
for (0..node.mNumChildren) |i| {
var new_list = try loadNode(
allocator,
instance,
pdev,
device,
transfer_queue,
transfer_command_pool,
node.mChildren[i],
scene,
mat_to_tex,
);
defer new_list.deinit();
try mesh_list.appendSlice(new_list.items[0..]);
}
return mesh_list;
}
pub fn loadMesh(
allocator: std.mem.Allocator,
instance: Instance,
pdev: vk.PhysicalDevice,
device: Device,
transfer_queue: vk.Queue,
transfer_command_pool: vk.CommandPool,
mesh: *const ai.aiMesh,
mat_to_tex: []u32,
) !Mesh {
var vertices = try std.ArrayList(Vertex).initCapacity(allocator, mesh.mNumVertices);
var indices = std.ArrayList(u32).init(allocator);
defer vertices.deinit();
defer indices.deinit();
// Go through each vertex and copy it across to our vertices
for (0..mesh.mNumVertices) |i| {
var vertex: Vertex = undefined;
// Set position
vertex.pos = .{ mesh.mVertices[i].x, mesh.mVertices[i].y, mesh.mVertices[i].z };
// Set texture coords (if they exist)
if (mesh.mTextureCoords[0] != null) {
vertex.tex = .{ mesh.mTextureCoords[0][i].x, mesh.mTextureCoords[0][i].y };
} else {
vertex.tex = .{ 0.0, 0.0 };
}
// Set colour (just use white for now)
vertex.col = .{ 1.0, 1.0, 1.0 };
// vertices.items[i] = vertex;
vertices.appendAssumeCapacity(vertex);
}
// Iterate over indices through faces and copy across
for (0..mesh.mNumFaces) |i| {
// Get a face
const face = mesh.mFaces[i];
// Go through face's indices and add to list
for (0..face.mNumIndices) |j| {
try indices.append(face.mIndices[j]);
}
}
return try Mesh.new(
instance,
pdev,
device,
transfer_queue,
transfer_command_pool,
vertices.items,
indices.items,
mat_to_tex[mesh.mMaterialIndex],
allocator,
);
}

9
src/assimp.zig Normal file
View file

@ -0,0 +1,9 @@
pub const c = @cImport({
@cInclude("assimp/cimport.h");
@cInclude("assimp/scene.h");
@cInclude("assimp/postprocess.h");
});
// pub fn importFile(path: [:0]const u8, flags: c_uint) *const c.aiScene {
// return c.aiImportFile(path.ptr, flags);
// }

View file

@ -6,6 +6,52 @@ const zm = @import("zmath");
const VulkanRenderer = @import("vulkan_renderer.zig").VulkanRenderer;
const Vector3 = @import("utilities.zig").Vector3;
// 100 fps
const TARGET_MS_F32: f32 = 16.0;
const Delta = struct {
current_time: u64 = 0,
last_time: u64 = 0,
delta_ms: u64 = 0,
delta_f32: f32 = 0.0,
delay_amount: u32 = 0,
perf_frequency: f64,
_counter: u32 = 0,
pub fn new() Delta {
return .{
.perf_frequency = @as(f64, @floatFromInt(sdl.getPerformanceFrequency())),
};
}
pub fn start(self: *Delta) void {
self.last_time = sdl.getPerformanceCounter();
}
pub fn tick(self: *Delta) void {
self.current_time = sdl.getPerformanceCounter();
self.delta_ms = self.current_time - self.last_time;
const delta_f64 = @as(f64, @floatFromInt(self.delta_ms)) / (self.perf_frequency * 1000.0);
self.delta_f32 = @as(f32, @floatCast(delta_f64));
self.delay_amount = @as(u32, @intFromFloat(@floor(TARGET_MS_F32 - self.delta_f32)));
// Limit the output to the terminal
if (self._counter % 100 == 0) {
const fps = self.perf_frequency / @as(f64, @floatFromInt(self.delta_ms));
std.debug.print("ms/f: {d}\tFPS: {d}\n", .{ delta_f64 * 1000.0, fps });
}
self.last_time = self.current_time;
self._counter += 1;
}
};
pub fn main() !void {
const window = try initWindow();
defer sdl.quit();
@ -19,10 +65,13 @@ pub fn main() !void {
var vulkan_renderer = try VulkanRenderer.init(window, allocator);
defer vulkan_renderer.deinit();
var delta = Delta.new();
delta.start();
var angle: f32 = 0.0;
var now: u64 = sdl.getPerformanceCounter();
var last: u64 = 0;
var delta_time: f32 = 0.0;
const move_speed: f32 = 1000;
const model_handle = try vulkan_renderer.createMeshModel("tescoPiwo.obj");
mainLoop: while (true) {
while (sdl.pollEvent()) |ev| {
@ -37,29 +86,41 @@ pub fn main() !void {
}
}
last = now;
now = sdl.getPerformanceCounter();
delta_time = @as(f32, @floatFromInt(now - last)) * 1000.0 / @as(f32, @floatFromInt(now));
const keystate = sdl.getKeyboardState();
if (keystate.isPressed(sdl.Scancode.left_shift)) {
const move = zm.translation(0.0, -move_speed * delta.delta_f32, 0.0);
vulkan_renderer.updateCamera(move);
} else if (keystate.isPressed(sdl.Scancode.left_control)) {
const move = zm.translation(0.0, move_speed * delta.delta_f32, 0.0);
vulkan_renderer.updateCamera(move);
} else if (keystate.isPressed(sdl.Scancode.w)) {
const move = zm.translation(0.0, 0.0, move_speed * delta.delta_f32);
vulkan_renderer.updateCamera(move);
} else if (keystate.isPressed(sdl.Scancode.s)) {
const move = zm.translation(0.0, 0.0, -move_speed * delta.delta_f32);
vulkan_renderer.updateCamera(move);
} else if (keystate.isPressed(sdl.Scancode.a)) {
const move = zm.translation(move_speed * delta.delta_f32, 0.0, 0.0);
vulkan_renderer.updateCamera(move);
} else if (keystate.isPressed(sdl.Scancode.d)) {
const move = zm.translation(-move_speed * delta.delta_f32, 0.0, 0.0);
vulkan_renderer.updateCamera(move);
}
angle += 10.0 * delta_time;
delta.tick();
angle += 200.0 * delta.delta_f32;
if (angle > 360.0) {
angle -= 360.0;
}
var first_model = zm.identity();
var second_model = zm.identity();
first_model = zm.mul(first_model, zm.rotationZ(angle));
first_model = zm.mul(first_model, zm.translation(-2.0, 0.0, -5.0));
second_model = zm.mul(second_model, zm.rotationZ(-angle * 2));
second_model = zm.mul(second_model, zm.translation(2.0, 0.0, -5.0));
try vulkan_renderer.updateModel(0, first_model);
try vulkan_renderer.updateModel(1, second_model);
const rotate = zm.mul(zm.identity(), zm.rotationY(angle));
try vulkan_renderer.updateModel(model_handle, rotate);
try vulkan_renderer.draw();
sdl.delay(delta.delay_amount);
}
}

22
src/shaders/second.frag Normal file
View file

@ -0,0 +1,22 @@
#version 450
// Colour output from subpass 1
layout(input_attachment_index = 0, binding = 0) uniform subpassInput inputColour;
// Depth output from subpass 1
layout(input_attachment_index = 1, binding = 1) uniform subpassInput inputDepth;
layout(location = 0) out vec4 colour;
void main() {
int xHalf = 800 / 2;
if (gl_FragCoord.x > xHalf) {
float lowerBound = 0.98;
float upperBound = 1;
float depth = subpassLoad(inputDepth).r;
float depthColourScaled = 1.0f - ((depth - lowerBound) / (upperBound - lowerBound));
colour = vec4(subpassLoad(inputColour).rgb * depthColourScaled, 1.0f);
} else {
colour = subpassLoad(inputColour).rgba;
}
}

12
src/shaders/second.vert Normal file
View file

@ -0,0 +1,12 @@
#version 450
// Array for triangle that fills screen
vec2 positions[3] = vec2[](
vec2(3.0, -1.0),
vec2(-1.0, -1.0),
vec2(-1.0, 3.0)
);
void main() {
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
}

View file

@ -1,10 +1,13 @@
#version 450
layout(location = 0) in vec3 fragCol;
layout(location = 1) in vec2 fragTex;
// Final output output (must also have location)
layout(location = 0) out vec4 outColour;
layout(set = 1, binding = 0) uniform sampler2D textureSampler;
void main() {
outColour = vec4(fragCol, 1.0);
outColour = texture(textureSampler, fragTex);
}

View file

@ -2,19 +2,22 @@
layout(location = 0) in vec3 pos;
layout(location = 1) in vec3 col;
layout(location = 2) in vec2 tex;
layout(binding = 0) uniform UboViewProjection {
layout(location = 0) out vec3 fragCol;
layout(location = 1) out vec2 fragTex;
layout(set = 0, binding = 0) uniform UboViewProjection {
mat4 projection;
mat4 view;
} uboViewProjection;
layout(binding = 1) uniform UboModel {
layout(push_constant) uniform PushModel {
mat4 model;
} uboModel;
layout(location = 0) out vec3 fragCol;
} pushModel;
void main() {
gl_Position = uboViewProjection.projection * uboViewProjection.view * uboModel.model * vec4(pos, 1.0);
gl_Position = uboViewProjection.projection * uboViewProjection.view * pushModel.model * vec4(pos, 1.0);
fragCol = col;
fragTex = tex;
}

13
src/string_utils.zig Normal file
View file

@ -0,0 +1,13 @@
const std = @import("std");
pub const StringError = error{ConcatError};
/// Concatenates two strings together using `std.mem.concat()`
pub fn concat(str_1: []const u8, str_2: []const u8, allocator: std.mem.Allocator) StringError![:0]const u8 {
const path_concat = [2][]const u8{ str_1, str_2 };
return std.mem.concatWithSentinel(allocator, u8, &path_concat, 0) catch |e| {
std.log.err("[{s}] Failed to concatenate the following strings: '{s}' and '{s}'\n", .{ @errorName(e), str_1, str_2 });
return StringError.ConcatError;
};
}

View file

@ -8,12 +8,13 @@ const CommandBuffer = @import("vulkan_renderer.zig").CommandBuffer;
pub const device_extensions = [_][*:0]const u8{vk.extensions.khr_swapchain.name};
pub const Vector3 = @Vector(3, f32);
pub const Vector2 = @Vector(2, f32);
// Vertex data representation
pub const Vertex = struct {
// Vertex position (x, y, z)
pos: Vector3,
col: Vector3,
pos: Vector3, // Vertex position (x, y, z)
col: Vector3, // Vertex colour (r, g, b)
tex: Vector2, // Texture coords (u, v)
};
pub const QueueFamilyIndices = struct {
@ -36,7 +37,7 @@ pub const SwapchainImage = struct {
image_view: vk.ImageView,
};
fn findMemoryTypeIndex(pdev: vk.PhysicalDevice, instance: Instance, allowed_types: u32, properties: vk.MemoryPropertyFlags) u32 {
pub fn findMemoryTypeIndex(pdev: vk.PhysicalDevice, instance: Instance, allowed_types: u32, properties: vk.MemoryPropertyFlags) u32 {
// Get properties of physical device memory
const memory_properties = instance.getPhysicalDeviceMemoryProperties(pdev);
const mem_type_count = memory_properties.memory_type_count;
@ -96,31 +97,20 @@ pub fn createBuffer(
try device.bindBufferMemory(buffer.*, buffer_memory.*, 0);
}
pub fn copyBuffer(
device: Device,
transfer_queue: vk.Queue,
transfer_command_pool: vk.CommandPool,
src_buffer: vk.Buffer,
dst_buffer: vk.Buffer,
buffer_size: vk.DeviceSize,
allocator: std.mem.Allocator,
) !void {
fn beginCommandBuffer(device: Device, command_pool: vk.CommandPool) !CommandBuffer {
// Command buffer to hold transfer commands
const transfer_command_buffer_handle = try allocator.create(vk.CommandBuffer);
defer allocator.destroy(transfer_command_buffer_handle);
// Free temporary buffer back to pool
defer device.freeCommandBuffers(transfer_command_pool, 1, @ptrCast(transfer_command_buffer_handle));
var command_buffer_handle: vk.CommandBuffer = undefined;
// Command buffer details
const alloc_info: vk.CommandBufferAllocateInfo = .{
.command_pool = transfer_command_pool,
.command_pool = command_pool,
.level = .primary,
.command_buffer_count = 1,
};
// Allocate command buffer from pool
try device.allocateCommandBuffers(&alloc_info, @ptrCast(transfer_command_buffer_handle));
const transfer_command_buffer = CommandBuffer.init(transfer_command_buffer_handle.*, device.wrapper);
try device.allocateCommandBuffers(&alloc_info, @ptrCast(&command_buffer_handle));
const command_buffer = CommandBuffer.init(command_buffer_handle, device.wrapper);
// Information to begin the command buffer record
const begin_info: vk.CommandBufferBeginInfo = .{
@ -128,7 +118,39 @@ pub fn copyBuffer(
};
// Begin recording transfer commands
try transfer_command_buffer.beginCommandBuffer(&begin_info);
try command_buffer.beginCommandBuffer(&begin_info);
return command_buffer;
}
fn endAndSubmitCommandBuffer(device: Device, command_buffer: CommandBuffer, command_pool: vk.CommandPool, queue: vk.Queue) !void {
// End commands
try command_buffer.endCommandBuffer();
// Queue submission information
const submit_info: vk.SubmitInfo = .{
.command_buffer_count = 1,
.p_command_buffers = @ptrCast(&command_buffer.handle),
};
// Submit transfer command to transfer queue and wait until it finishes
try device.queueSubmit(queue, 1, @ptrCast(&submit_info), .null_handle);
try device.queueWaitIdle(queue);
// Free temporary buffer back to pool
device.freeCommandBuffers(command_pool, 1, @ptrCast(&command_buffer.handle));
}
pub fn copyBuffer(
device: Device,
transfer_queue: vk.Queue,
transfer_command_pool: vk.CommandPool,
src_buffer: vk.Buffer,
dst_buffer: vk.Buffer,
buffer_size: vk.DeviceSize,
) !void {
// Create buffer
const transfer_command_buffer = try beginCommandBuffer(device, transfer_command_pool);
// Region of data to copy from and to
const buffer_copy_region: vk.BufferCopy = .{
@ -140,16 +162,96 @@ pub fn copyBuffer(
// Command to copy src buffer to dst buffer
transfer_command_buffer.copyBuffer(src_buffer, dst_buffer, 1, @ptrCast(&buffer_copy_region));
// End commands
try transfer_command_buffer.endCommandBuffer();
// Command to copy src buffer to dst buffer
try endAndSubmitCommandBuffer(device, transfer_command_buffer, transfer_command_pool, transfer_queue);
}
// Queue submission information
const submit_info: vk.SubmitInfo = .{
.command_buffer_count = 1,
.p_command_buffers = @ptrCast(&transfer_command_buffer.handle),
pub fn copyImageBuffer(
device: Device,
transfer_queue: vk.Queue,
transfer_command_pool: vk.CommandPool,
src_buffer: vk.Buffer,
image: vk.Image,
width: u32,
height: u32,
) !void {
const transfer_command_buffer = try beginCommandBuffer(device, transfer_command_pool);
const image_region: vk.BufferImageCopy = .{
.buffer_offset = 0, // Offset into data
.buffer_row_length = 0, // Row length of data to calculate data spacing
.buffer_image_height = 0, // Image height to calculate data spacing
.image_subresource = .{
.aspect_mask = .{ .color_bit = true }, // Which aspect of image to copy
.mip_level = 0, // Mipmap level to copy
.base_array_layer = 0, // Starting array layer (if array)
.layer_count = 1, // Number of layers of copy starting at base_array_layer
},
.image_offset = .{ .x = 0, .y = 0, .z = 0 }, // Offset to image (as opposed to raw data buffer offset)
.image_extent = .{ .width = width, .height = height, .depth = 1 }, // Size of the region to copy as x, y, z values
};
// Submit transfer command to transfer queue and wait until it finishes
try device.queueSubmit(transfer_queue, 1, @ptrCast(&submit_info), .null_handle);
try device.queueWaitIdle(transfer_queue);
transfer_command_buffer.copyBufferToImage(src_buffer, image, .transfer_dst_optimal, 1, @ptrCast(&image_region));
try endAndSubmitCommandBuffer(device, transfer_command_buffer, transfer_command_pool, transfer_queue);
}
pub fn transitionImageLayout(
device: Device,
queue: vk.Queue,
command_pool: vk.CommandPool,
image: vk.Image,
old_layout: vk.ImageLayout,
new_layout: vk.ImageLayout,
) !void {
const command_buffer = try beginCommandBuffer(device, command_pool);
var image_memory_barrier: vk.ImageMemoryBarrier = .{
.old_layout = old_layout, // Layout to transition from
.new_layout = new_layout, // Layout to transition to
.src_queue_family_index = vk.QUEUE_FAMILY_IGNORED, // Queue family to transition from
.dst_queue_family_index = vk.QUEUE_FAMILY_IGNORED, // Queue family to transition to
.image = image, // Image being accessed and modified as part of barrier
.subresource_range = .{
.aspect_mask = .{ .color_bit = true }, // Aspect of image being altered
.base_mip_level = 0, // First mip level to start alterations on
.level_count = 1, // Number of mipmap levels to alter starting from base mipmap level
.base_array_layer = 0, // First layer to start alterations on
.layer_count = 1, // Number of layers to alter starting from base array layer
},
.src_access_mask = .{}, // Memory access stage transition must happen after
.dst_access_mask = .{}, // Memory access stage transition must happen before
};
var src_stage: vk.PipelineStageFlags = .{};
var dst_stage: vk.PipelineStageFlags = .{};
// If transitioning from new image to image ready to receive data
if (old_layout == vk.ImageLayout.undefined and new_layout == vk.ImageLayout.transfer_dst_optimal) {
image_memory_barrier.dst_access_mask.transfer_write_bit = true;
src_stage.top_of_pipe_bit = true;
dst_stage.transfer_bit = true;
} else if (old_layout == vk.ImageLayout.transfer_dst_optimal and new_layout == vk.ImageLayout.shader_read_only_optimal) {
// If transitioning from transfer destination to shader readable
image_memory_barrier.src_access_mask.transfer_write_bit = true;
image_memory_barrier.dst_access_mask.shader_read_bit = true;
src_stage.transfer_bit = true;
dst_stage.fragment_shader_bit = true;
}
command_buffer.pipelineBarrier(
src_stage, // Pipeline stages (match to src and dst access mask)
dst_stage,
.{}, // Dependency flags
0, // Memory barrier count
null, // Memory barrier data
0, // Buffer memory barrier count
null, // Buffer memory barrier data
1, // Image memory barrier count
@ptrCast(&image_memory_barrier), // Image memory barrier data
);
try endAndSubmitCommandBuffer(device, command_buffer, command_pool, queue);
}

File diff suppressed because it is too large Load diff

518
vk.xml

File diff suppressed because it is too large Load diff