Add 'glfw-vulkan/' from commit 'ce8109c1aa73e1785c78acec5a9ec1bc9fa25099'
git-subtree-dir: glfw-vulkan git-subtree-mainline:df6751f276
git-subtree-split:ce8109c1aa
This commit is contained in:
51
glfw-vulkan/.gitignore
vendored
Normal file
51
glfw-vulkan/.gitignore
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
*.d
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
*.gch
|
||||
*.pch
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
*.mod
|
||||
*.smod
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*.ko
|
||||
*.elf
|
||||
*.ilk
|
||||
*.map
|
||||
*.exp
|
||||
*.so.*
|
||||
*.i*86
|
||||
*.x86_64
|
||||
*.hex
|
||||
*.dSYM/
|
||||
*.su
|
||||
*.idb
|
||||
*.pdb
|
||||
*.mod*
|
||||
*.cmd
|
||||
.tmp_versions/
|
||||
modules.order
|
||||
Module.symvers
|
||||
Mkfile.old
|
||||
dkms.conf
|
||||
|
||||
[.]zig-cache/
|
||||
zig-out/
|
||||
build/
|
||||
build-*/
|
||||
docgen_tmp/
|
||||
|
||||
.idea/
|
||||
.direnv/
|
||||
.envrc
|
||||
|
||||
imgui.ini
|
1
glfw-vulkan/.tool-versions
Normal file
1
glfw-vulkan/.tool-versions
Normal file
@@ -0,0 +1 @@
|
||||
zig 0.13.0
|
62
glfw-vulkan/build.zig
Normal file
62
glfw-vulkan/build.zig
Normal file
@@ -0,0 +1,62 @@
|
||||
const std = @import("std");
|
||||
const vkgen = @import("vulkan-zig");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{
|
||||
// .preferred_optimize_mode = .ReleaseSafe,
|
||||
});
|
||||
|
||||
const vk = b.dependency("vulkan-zig", .{
|
||||
.registry = @as([]const u8, b.pathFromRoot("reg/vk.xml")),
|
||||
});
|
||||
const vkmod = vk.module("vulkan-zig");
|
||||
|
||||
const cimgui = b.dependency("cimgui", .{});
|
||||
|
||||
// nu.linkSystemLibrary("glfw3", .{
|
||||
// .needed = true,
|
||||
// .preferred_link_mode = .static,
|
||||
// .use_pkg_config = .force,
|
||||
// });
|
||||
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "scratchzig",
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
// exe.root_module.addImport("nu", nu);
|
||||
exe.root_module.addImport("vk", vkmod);
|
||||
exe.root_module.addImport("cimgui", cimgui.module("cimgui"));
|
||||
exe.root_module.linkSystemLibrary("glfw3", .{
|
||||
.needed = true,
|
||||
.preferred_link_mode = .static,
|
||||
.use_pkg_config = .force,
|
||||
});
|
||||
exe.linkLibC();
|
||||
|
||||
const shaders = vkgen.ShaderCompileStep.create(
|
||||
b,
|
||||
&[_][]const u8{ "glslc", "--target-env=vulkan1.3" },
|
||||
"-o",
|
||||
);
|
||||
shaders.add("triangle_vert", "src/shaders/triangle.vert", .{});
|
||||
shaders.add("triangle_frag", "src/shaders/triangle.frag", .{});
|
||||
exe.root_module.addImport("shaders", shaders.getModule());
|
||||
|
||||
b.installArtifact(exe);
|
||||
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
run_cmd.step.dependOn(b.getInstallStep());
|
||||
|
||||
if (b.args) |args| {
|
||||
run_cmd.addArgs(args);
|
||||
}
|
||||
|
||||
const run_step = b.step("run", "Run the app");
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
|
||||
// const devel_step = b.step("dev", "Build development tools and test runners");
|
||||
// devel_step.dependOn(&nu_test_runner.step);
|
||||
}
|
18
glfw-vulkan/build.zig.zon
Normal file
18
glfw-vulkan/build.zig.zon
Normal file
@@ -0,0 +1,18 @@
|
||||
.{
|
||||
.name = "scratchzig",
|
||||
.version = "0.0.0",
|
||||
|
||||
.dependencies = .{
|
||||
.@"vulkan-zig" = .{
|
||||
.url = "https://github.com/Snektron/vulkan-zig/archive/f2c2e0ff80374563357cc4fe72bf7d8a2c956824.tar.gz",
|
||||
.hash = "1220cf0972c6fe05437c1a8689b955084385eb7ca1f8c14010d49ca5a89570a5d90d",
|
||||
},
|
||||
.cimgui = .{
|
||||
.path = "cimgui",
|
||||
},
|
||||
},
|
||||
|
||||
.paths = .{
|
||||
"",
|
||||
},
|
||||
}
|
92
glfw-vulkan/cimgui/build.zig
Normal file
92
glfw-vulkan/cimgui/build.zig
Normal file
@@ -0,0 +1,92 @@
|
||||
const std = @import("std");
|
||||
const vkgen = @import("vulkan-zig");
|
||||
|
||||
pub fn build(b: *std.Build) !void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const imgui_dep = b.dependency("imgui", .{});
|
||||
const cimgui_dep = b.dependency("cimgui", .{});
|
||||
|
||||
const luajit = try b.findProgram(&.{"luajit"}, &.{});
|
||||
|
||||
const gen = b.addSystemCommand(&.{luajit});
|
||||
gen.setCwd(cimgui_dep.path("generator/"));
|
||||
gen.addFileArg(cimgui_dep.path("generator/generator.lua"));
|
||||
gen.addArgs(&.{
|
||||
"zig cc",
|
||||
"comments internal noimstrv",
|
||||
"glfw",
|
||||
"vulkan",
|
||||
});
|
||||
_ = gen.captureStdOut(); // to quiet output
|
||||
|
||||
{
|
||||
const relpath = try std.fs.path.relative(
|
||||
b.allocator,
|
||||
cimgui_dep.path("generator").getPath(b),
|
||||
imgui_dep.path("").getPath(b),
|
||||
);
|
||||
defer b.allocator.free(relpath);
|
||||
gen.setEnvironmentVariable(
|
||||
"IMGUI_PATH",
|
||||
relpath,
|
||||
);
|
||||
}
|
||||
|
||||
const copy = b.addWriteFiles();
|
||||
copy.step.dependOn(&gen.step);
|
||||
_ = copy.addCopyDirectory(imgui_dep.path(""), "imgui", .{
|
||||
.include_extensions = &.{ ".h", ".cpp" },
|
||||
});
|
||||
_ = copy.addCopyFile(cimgui_dep.path("cimgui.h"), "cimgui.h");
|
||||
_ = copy.addCopyFile(cimgui_dep.path("cimgui.cpp"), "cimgui.cpp");
|
||||
_ = copy.addCopyFile(cimgui_dep.path("generator/output/cimgui_impl.h"), "cimgui_impl.h");
|
||||
|
||||
const cimgui = b.addStaticLibrary(.{
|
||||
.name = "cimgui",
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
cimgui.step.dependOn(©.step);
|
||||
cimgui.linkLibC();
|
||||
cimgui.linkLibCpp();
|
||||
cimgui.addIncludePath(copy.getDirectory());
|
||||
cimgui.addIncludePath(copy.getDirectory().path(b, "imgui"));
|
||||
cimgui.addCSourceFiles(.{
|
||||
.root = copy.getDirectory(),
|
||||
.files = &.{
|
||||
"cimgui.cpp",
|
||||
"imgui/imgui.cpp",
|
||||
"imgui/imgui_tables.cpp",
|
||||
"imgui/imgui_widgets.cpp",
|
||||
"imgui/imgui_demo.cpp",
|
||||
"imgui/imgui_draw.cpp",
|
||||
"imgui/backends/imgui_impl_glfw.cpp",
|
||||
"imgui/backends/imgui_impl_vulkan.cpp",
|
||||
},
|
||||
.flags = &.{
|
||||
"-DIMGUI_IMPL_VULKAN_NO_PROTOTYPES",
|
||||
"-DCIMGUI_USE_GLFW",
|
||||
"-DCIMGUI_USE_VULKAN",
|
||||
"-DIMGUI_IMPL_API=extern \"C\"",
|
||||
},
|
||||
});
|
||||
cimgui.installHeader(copy.getDirectory().path(b, "cimgui.h"), "cimgui.h");
|
||||
cimgui.installHeader(copy.getDirectory().path(b, "cimgui_impl.h"), "cimgui_impl.h");
|
||||
|
||||
cimgui.linkSystemLibrary2("glfw3", .{
|
||||
.needed = true,
|
||||
.preferred_link_mode = .static,
|
||||
.use_pkg_config = .force,
|
||||
});
|
||||
|
||||
b.installArtifact(cimgui);
|
||||
|
||||
const cimgui_mod = b.addModule("cimgui", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.root_source_file = b.path("src/root.zig"),
|
||||
});
|
||||
cimgui_mod.linkLibrary(cimgui);
|
||||
}
|
19
glfw-vulkan/cimgui/build.zig.zon
Normal file
19
glfw-vulkan/cimgui/build.zig.zon
Normal file
@@ -0,0 +1,19 @@
|
||||
.{
|
||||
.name = "cimgui",
|
||||
.version = "0.0.0",
|
||||
|
||||
.dependencies = .{
|
||||
.cimgui = .{
|
||||
.url = "https://github.com/cimgui/cimgui/archive/refs/tags/1.90.8dock.tar.gz",
|
||||
.hash = "12207ee69164f88f4b41ee5d44edf3835ec4dab0c0cd885799da67d56668f4a3d46b",
|
||||
},
|
||||
.imgui = .{
|
||||
.url = "https://github.com/ocornut/imgui/archive/refs/tags/v1.90.8-docking.tar.gz",
|
||||
.hash = "122065151b97161e25abb71c9df2fd9fba42aaca8c33d689a480b883d82411c8fabe",
|
||||
},
|
||||
},
|
||||
|
||||
.paths = .{
|
||||
"",
|
||||
},
|
||||
}
|
15
glfw-vulkan/cimgui/src/root.zig
Normal file
15
glfw-vulkan/cimgui/src/root.zig
Normal file
@@ -0,0 +1,15 @@
|
||||
pub usingnamespace @cImport({
|
||||
@cDefine("CIMGUI_DEFINE_ENUMS_AND_STRUCTS", {});
|
||||
@cInclude("cimgui.h");
|
||||
});
|
||||
|
||||
pub const impl = @cImport({
|
||||
@cDefine("CIMGUI_DEFINE_ENUMS_AND_STRUCTS", {});
|
||||
@cInclude("cimgui.h");
|
||||
|
||||
@cInclude("vulkan/vulkan.h");
|
||||
|
||||
@cDefine("CIMGUI_USE_VULKAN", {});
|
||||
@cDefine("CIMGUI_USE_GLFW", {});
|
||||
@cInclude("cimgui_impl.h");
|
||||
});
|
26856
glfw-vulkan/reg/vk.xml
Normal file
26856
glfw-vulkan/reg/vk.xml
Normal file
File diff suppressed because it is too large
Load Diff
174
glfw-vulkan/src/Uber.zig
Normal file
174
glfw-vulkan/src/Uber.zig
Normal file
@@ -0,0 +1,174 @@
|
||||
const std = @import("std");
|
||||
const vk = @import("vk");
|
||||
const shaders = @import("shaders");
|
||||
|
||||
const nu = @import("nu.zig");
|
||||
const Self = @This();
|
||||
|
||||
const ctx = nu.Render.ctx;
|
||||
|
||||
set_layout: vk.DescriptorSetLayout,
|
||||
layout: vk.PipelineLayout,
|
||||
pipeline: vk.Pipeline,
|
||||
|
||||
pub const Index = u16;
|
||||
|
||||
pub const Uniform = extern struct {
|
||||
mat: [16]f32,
|
||||
|
||||
const Bindings = [_]vk.DescriptorSetLayoutBinding{.{
|
||||
.binding = 0,
|
||||
.descriptor_type = .uniform_buffer,
|
||||
.descriptor_count = 1,
|
||||
.stage_flags = .{ .vertex_bit = true },
|
||||
}};
|
||||
};
|
||||
|
||||
pub const Vertex = extern struct {
|
||||
pos: [4]f32,
|
||||
color: [3]f32,
|
||||
|
||||
const Bindings = [_]vk.VertexInputBindingDescription{
|
||||
.{
|
||||
.binding = 0,
|
||||
.stride = @sizeOf(Vertex),
|
||||
.input_rate = .vertex,
|
||||
},
|
||||
};
|
||||
|
||||
const Attributes = [_]vk.VertexInputAttributeDescription{
|
||||
.{
|
||||
.binding = 0,
|
||||
.location = 0,
|
||||
.format = .r32g32b32a32_sfloat,
|
||||
.offset = @offsetOf(Vertex, "pos"),
|
||||
},
|
||||
.{
|
||||
.binding = 0,
|
||||
.location = 1,
|
||||
.format = .r32g32b32_sfloat,
|
||||
.offset = @offsetOf(Vertex, "color"),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
pub fn init(cache: vk.PipelineCache) !Self {
|
||||
const vert = try ctx.D.createShaderModule(&.{
|
||||
.code_size = shaders.triangle_vert.len,
|
||||
.p_code = @ptrCast(&shaders.triangle_vert),
|
||||
}, null);
|
||||
defer ctx.D.destroyShaderModule(vert, null);
|
||||
|
||||
const frag = try ctx.D.createShaderModule(&.{
|
||||
.code_size = shaders.triangle_frag.len,
|
||||
.p_code = @ptrCast(&shaders.triangle_frag),
|
||||
}, null);
|
||||
defer ctx.D.destroyShaderModule(frag, null);
|
||||
|
||||
const set_layout = try ctx.D.createDescriptorSetLayout(&vk.DescriptorSetLayoutCreateInfo{
|
||||
.flags = .{},
|
||||
.binding_count = @intCast(Uniform.Bindings.len),
|
||||
.p_bindings = &Uniform.Bindings,
|
||||
}, null);
|
||||
errdefer ctx.D.destroyDescriptorSetLayout(set_layout, null);
|
||||
|
||||
const layout = try ctx.D.createPipelineLayout(&vk.PipelineLayoutCreateInfo{
|
||||
.push_constant_range_count = 0,
|
||||
.set_layout_count = 1,
|
||||
.p_set_layouts = &.{set_layout},
|
||||
}, null);
|
||||
errdefer ctx.D.destroyPipelineLayout(layout, null);
|
||||
|
||||
var pipeline: vk.Pipeline = .null_handle;
|
||||
_ = try ctx.D.createGraphicsPipelines(cache, 1, &[1]vk.GraphicsPipelineCreateInfo{
|
||||
vk.GraphicsPipelineCreateInfo{
|
||||
.stage_count = 2,
|
||||
.p_stages = &.{
|
||||
vk.PipelineShaderStageCreateInfo{ .stage = .{ .vertex_bit = true }, .module = vert, .p_name = "main" },
|
||||
vk.PipelineShaderStageCreateInfo{ .stage = .{ .fragment_bit = true }, .module = frag, .p_name = "main" },
|
||||
},
|
||||
.layout = layout,
|
||||
.render_pass = .null_handle,
|
||||
.subpass = 0,
|
||||
.base_pipeline_handle = .null_handle,
|
||||
.base_pipeline_index = -1,
|
||||
.p_vertex_input_state = &vk.PipelineVertexInputStateCreateInfo{
|
||||
.vertex_binding_description_count = @intCast(Vertex.Bindings.len),
|
||||
.p_vertex_binding_descriptions = &Vertex.Bindings,
|
||||
.vertex_attribute_description_count = @intCast(Vertex.Attributes.len),
|
||||
.p_vertex_attribute_descriptions = &Vertex.Attributes,
|
||||
},
|
||||
.p_input_assembly_state = &vk.PipelineInputAssemblyStateCreateInfo{
|
||||
.topology = .triangle_list,
|
||||
.primitive_restart_enable = vk.FALSE,
|
||||
},
|
||||
.p_tessellation_state = null,
|
||||
.p_viewport_state = &vk.PipelineViewportStateCreateInfo{
|
||||
.viewport_count = 1,
|
||||
.scissor_count = 1,
|
||||
},
|
||||
.p_rasterization_state = &vk.PipelineRasterizationStateCreateInfo{
|
||||
.depth_clamp_enable = vk.FALSE,
|
||||
.rasterizer_discard_enable = vk.FALSE,
|
||||
.polygon_mode = .fill,
|
||||
.cull_mode = .{ .back_bit = true },
|
||||
.front_face = .counter_clockwise,
|
||||
.depth_bias_enable = vk.FALSE,
|
||||
.depth_bias_constant_factor = 0.0,
|
||||
.depth_bias_clamp = 0.0,
|
||||
.depth_bias_slope_factor = 0.0,
|
||||
.line_width = 1.0,
|
||||
},
|
||||
.p_multisample_state = &vk.PipelineMultisampleStateCreateInfo{
|
||||
.rasterization_samples = .{ .@"1_bit" = true },
|
||||
.sample_shading_enable = vk.FALSE,
|
||||
.min_sample_shading = 1,
|
||||
.alpha_to_coverage_enable = vk.FALSE,
|
||||
.alpha_to_one_enable = vk.FALSE,
|
||||
},
|
||||
.p_depth_stencil_state = null,
|
||||
.p_color_blend_state = &vk.PipelineColorBlendStateCreateInfo{
|
||||
.logic_op_enable = vk.FALSE,
|
||||
.logic_op = .copy,
|
||||
.blend_constants = [_]f32{ 0, 0, 0, 0 },
|
||||
.attachment_count = 1,
|
||||
.p_attachments = &.{
|
||||
vk.PipelineColorBlendAttachmentState{
|
||||
.blend_enable = vk.FALSE,
|
||||
.color_blend_op = .add,
|
||||
.src_color_blend_factor = .one,
|
||||
.dst_color_blend_factor = .zero,
|
||||
.alpha_blend_op = .add,
|
||||
.src_alpha_blend_factor = .one,
|
||||
.dst_alpha_blend_factor = .zero,
|
||||
.color_write_mask = .{ .r_bit = true, .g_bit = true, .b_bit = true, .a_bit = true },
|
||||
},
|
||||
},
|
||||
},
|
||||
.p_dynamic_state = &vk.PipelineDynamicStateCreateInfo{
|
||||
.flags = .{},
|
||||
.dynamic_state_count = 2,
|
||||
.p_dynamic_states = &.{
|
||||
.viewport,
|
||||
.scissor,
|
||||
},
|
||||
},
|
||||
.p_next = &vk.PipelineRenderingCreateInfo{
|
||||
.color_attachment_count = 1,
|
||||
.p_color_attachment_formats = &.{nu.Render.sc.cinfo.image_format},
|
||||
.depth_attachment_format = .undefined,
|
||||
.stencil_attachment_format = .undefined,
|
||||
.view_mask = 0,
|
||||
},
|
||||
},
|
||||
}, null, @ptrCast(&pipeline));
|
||||
errdefer ctx.D.destroyPipeline(pipeline, null);
|
||||
|
||||
return .{ .pipeline = pipeline, .layout = layout, .set_layout = set_layout };
|
||||
}
|
||||
|
||||
pub fn deinit(self: Self) void {
|
||||
ctx.D.destroyPipeline(self.pipeline, null);
|
||||
ctx.D.destroyPipelineLayout(self.layout, null);
|
||||
ctx.D.destroyDescriptorSetLayout(self.set_layout, null);
|
||||
}
|
25
glfw-vulkan/src/c.zig
Normal file
25
glfw-vulkan/src/c.zig
Normal file
@@ -0,0 +1,25 @@
|
||||
pub usingnamespace @cImport({
|
||||
@cDefine("GLFW_INCLUDE_NONE", {});
|
||||
@cInclude("GLFW/glfw3.h");
|
||||
});
|
||||
|
||||
const vk = @import("vk");
|
||||
const c = @This();
|
||||
|
||||
pub extern fn glfwGetInstanceProcAddress(
|
||||
instance: vk.Instance,
|
||||
procname: [*:0]const u8,
|
||||
) vk.PfnVoidFunction;
|
||||
|
||||
pub extern fn glfwGetPhysicalDevicePresentationSupport(
|
||||
instance: vk.Instance,
|
||||
pdev: vk.PhysicalDevice,
|
||||
queuefamily: u32,
|
||||
) c_int;
|
||||
|
||||
pub extern fn glfwCreateWindowSurface(
|
||||
instance: vk.Instance,
|
||||
window: *c.GLFWwindow,
|
||||
allocation_callbacks: ?*const vk.AllocationCallbacks,
|
||||
surface: *vk.SurfaceKHR,
|
||||
) vk.Result;
|
219
glfw-vulkan/src/main.zig
Normal file
219
glfw-vulkan/src/main.zig
Normal file
@@ -0,0 +1,219 @@
|
||||
const std = @import("std");
|
||||
const nu = @import("nu.zig");
|
||||
const Uber = @import("Uber.zig");
|
||||
|
||||
pub const nu_modules = .{@This()};
|
||||
pub const main = nu.main;
|
||||
|
||||
pub const nu_config: nu.Config = .{
|
||||
.window = nu.Window.Config{
|
||||
.title = "au",
|
||||
},
|
||||
.render = .{
|
||||
.app_name = "au",
|
||||
.frames_in_flight = 2,
|
||||
},
|
||||
};
|
||||
|
||||
const im = nu.ImGui;
|
||||
const ctx = nu.Render.ctx;
|
||||
const vk = @import("vk");
|
||||
|
||||
pub const depends = .{nu.ImGui};
|
||||
|
||||
const vertices: []const Uber.Vertex = &.{
|
||||
.{ .pos = .{ 0.0, 0.0, 0.0, 1.0 }, .color = .{ 1.0, 1.0, 0.0 } },
|
||||
.{ .pos = .{ 1.0, 0.0, 0.0, 1.0 }, .color = .{ 1.0, 0.0, 1.0 } },
|
||||
.{ .pos = .{ 0.0, 1.0, 0.0, 1.0 }, .color = .{ 0.0, 1.0, 1.0 } },
|
||||
.{ .pos = .{ 1.0, 1.0, 0.0, 1.0 }, .color = .{ 1.0, 1.0, 1.0 } },
|
||||
};
|
||||
|
||||
const Index = u16;
|
||||
const indices: []const Index = &.{
|
||||
1, 0, 2,
|
||||
2, 3, 1,
|
||||
};
|
||||
|
||||
const uniform: Uber.Uniform = .{
|
||||
.mat = .{
|
||||
1, 0, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1,
|
||||
},
|
||||
};
|
||||
|
||||
var mem: vk.DeviceMemory = .null_handle;
|
||||
var vbo: vk.Buffer = .null_handle;
|
||||
var ibo: vk.Buffer = .null_handle;
|
||||
var ubo: vk.Buffer = .null_handle;
|
||||
|
||||
var pool: vk.DescriptorPool = .null_handle;
|
||||
var descriptor_set: vk.DescriptorSet = .null_handle;
|
||||
|
||||
var cache: vk.PipelineCache = .null_handle;
|
||||
var uber: Uber = undefined;
|
||||
|
||||
pub fn setup(_: std.mem.Allocator) !void {
|
||||
errdefer teardown();
|
||||
const props = ctx.I.getPhysicalDeviceMemoryProperties(ctx.pdevice.*);
|
||||
const memory_type_index: u32 = for (0..props.memory_type_count) |idx| {
|
||||
const t = props.memory_types[idx];
|
||||
if (t.property_flags.host_coherent_bit and t.property_flags.host_visible_bit) {
|
||||
break @intCast(idx);
|
||||
}
|
||||
} else {
|
||||
unreachable;
|
||||
};
|
||||
|
||||
const SIZE = 0x10000;
|
||||
mem = try ctx.D.allocateMemory(&vk.MemoryAllocateInfo{
|
||||
.allocation_size = SIZE,
|
||||
.memory_type_index = memory_type_index,
|
||||
}, null);
|
||||
|
||||
const raw: [*]u8 = @ptrCast(try ctx.D.mapMemory(mem, 0, vk.WHOLE_SIZE, .{}) orelse unreachable);
|
||||
errdefer ctx.D.unmapMemory(mem);
|
||||
|
||||
// todo VMA. This doesn't work for some reason to do with alignment.
|
||||
// var fba = std.heap.FixedBufferAllocator.init(raw[0..SIZE]);
|
||||
// const aa = fba.allocator();
|
||||
|
||||
var bump: usize = 0;
|
||||
|
||||
const vbytes = std.mem.sliceAsBytes(vertices);
|
||||
vbo = try ctx.D.createBuffer(&vk.BufferCreateInfo{
|
||||
.queue_family_index_count = 1,
|
||||
.p_queue_family_indices = &.{ctx.family.*},
|
||||
.sharing_mode = .exclusive,
|
||||
.size = vbytes.len,
|
||||
.usage = .{ .vertex_buffer_bit = true },
|
||||
}, null);
|
||||
const vreq = ctx.D.getBufferMemoryRequirements(vbo);
|
||||
bump = std.mem.alignForward(usize, bump, vreq.alignment);
|
||||
@memcpy(raw + bump, vbytes);
|
||||
try ctx.D.bindBufferMemory(vbo, mem, bump);
|
||||
bump += vreq.size;
|
||||
|
||||
const ibytes = std.mem.sliceAsBytes(indices);
|
||||
ibo = try ctx.D.createBuffer(&vk.BufferCreateInfo{
|
||||
.queue_family_index_count = 1,
|
||||
.p_queue_family_indices = &.{ctx.family.*},
|
||||
.sharing_mode = .exclusive,
|
||||
.size = ibytes.len,
|
||||
.usage = .{ .index_buffer_bit = true },
|
||||
}, null);
|
||||
const ireq = ctx.D.getBufferMemoryRequirements(ibo);
|
||||
bump = std.mem.alignForward(usize, bump, ireq.alignment);
|
||||
@memcpy(raw + bump, ibytes);
|
||||
try ctx.D.bindBufferMemory(ibo, mem, bump);
|
||||
bump += ireq.size;
|
||||
|
||||
const ubytes = std.mem.asBytes(&uniform);
|
||||
ubo = try ctx.D.createBuffer(&vk.BufferCreateInfo{
|
||||
.queue_family_index_count = 1,
|
||||
.p_queue_family_indices = &.{ctx.family.*},
|
||||
.sharing_mode = .exclusive,
|
||||
.size = ubytes.len,
|
||||
.usage = .{ .uniform_buffer_bit = true },
|
||||
}, null);
|
||||
const ureq = ctx.D.getBufferMemoryRequirements(ubo);
|
||||
bump = std.mem.alignForward(usize, bump, ureq.alignment);
|
||||
@memcpy(raw + bump, ubytes);
|
||||
try ctx.D.bindBufferMemory(ubo, mem, bump);
|
||||
bump += ureq.size;
|
||||
|
||||
cache = try ctx.D.createPipelineCache(&vk.PipelineCacheCreateInfo{}, null);
|
||||
|
||||
uber = try Uber.init(cache);
|
||||
|
||||
const pool_sizes: []const vk.DescriptorPoolSize = &.{vk.DescriptorPoolSize{
|
||||
.descriptor_count = 8,
|
||||
.type = .uniform_buffer,
|
||||
}};
|
||||
|
||||
pool = try ctx.D.createDescriptorPool(&vk.DescriptorPoolCreateInfo{
|
||||
.flags = .{ .free_descriptor_set_bit = true },
|
||||
.pool_size_count = @intCast(pool_sizes.len),
|
||||
.p_pool_sizes = pool_sizes.ptr,
|
||||
.max_sets = 32,
|
||||
}, null);
|
||||
|
||||
var sets: [1]vk.DescriptorSet = .{.null_handle};
|
||||
try ctx.D.allocateDescriptorSets(&vk.DescriptorSetAllocateInfo{
|
||||
.descriptor_pool = pool,
|
||||
.descriptor_set_count = 1,
|
||||
.p_set_layouts = &.{uber.set_layout},
|
||||
}, &sets);
|
||||
descriptor_set = sets[0];
|
||||
|
||||
ctx.D.updateDescriptorSets(
|
||||
1,
|
||||
&.{
|
||||
vk.WriteDescriptorSet{
|
||||
.dst_set = descriptor_set,
|
||||
.dst_binding = 0,
|
||||
.dst_array_element = 0,
|
||||
.descriptor_count = 1,
|
||||
.descriptor_type = .uniform_buffer,
|
||||
.p_buffer_info = &.{
|
||||
vk.DescriptorBufferInfo{
|
||||
.buffer = ubo,
|
||||
.offset = 0,
|
||||
.range = vk.WHOLE_SIZE,
|
||||
},
|
||||
},
|
||||
.p_image_info = undefined,
|
||||
.p_texel_buffer_view = undefined,
|
||||
},
|
||||
},
|
||||
0,
|
||||
null,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn teardown() void {
|
||||
ctx.Q.waitIdle() catch {};
|
||||
uber.deinit();
|
||||
ctx.D.destroyPipelineCache(cache, null);
|
||||
ctx.D.destroyBuffer(ubo, null);
|
||||
ctx.D.destroyBuffer(ibo, null);
|
||||
ctx.D.destroyBuffer(vbo, null);
|
||||
ctx.D.freeMemory(mem, null);
|
||||
ctx.D.freeDescriptorSets(pool, 1, &.{descriptor_set}) catch unreachable;
|
||||
ctx.D.destroyDescriptorPool(pool, null);
|
||||
}
|
||||
|
||||
pub fn frame() !void {
|
||||
im.igShowMetricsWindow(null);
|
||||
}
|
||||
|
||||
pub fn present(cmd: ctx.CommandBufferProxy) void {
|
||||
const w, const h = nu.Window.size();
|
||||
|
||||
cmd.bindPipeline(.graphics, uber.pipeline);
|
||||
cmd.setScissor(0, 1, &.{vk.Rect2D{
|
||||
.offset = .{ .x = 0, .y = 0 },
|
||||
.extent = .{ .width = w, .height = h },
|
||||
}});
|
||||
cmd.setViewport(0, 1, &.{vk.Viewport{
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = @floatFromInt(w),
|
||||
.height = @floatFromInt(h),
|
||||
.min_depth = 0,
|
||||
.max_depth = 1,
|
||||
}});
|
||||
cmd.bindIndexBuffer(ibo, 0, .uint16);
|
||||
cmd.bindVertexBuffers(0, 1, &.{vbo}, &.{0});
|
||||
cmd.bindDescriptorSets(
|
||||
.graphics,
|
||||
uber.layout,
|
||||
0,
|
||||
1,
|
||||
&.{descriptor_set},
|
||||
0,
|
||||
null,
|
||||
);
|
||||
cmd.drawIndexed(@intCast(indices.len), 1, 0, 0, 0);
|
||||
}
|
117
glfw-vulkan/src/nu.zig
Normal file
117
glfw-vulkan/src/nu.zig
Normal file
@@ -0,0 +1,117 @@
|
||||
const std = @import("std");
|
||||
const root = @import("root");
|
||||
|
||||
pub const Window = @import("nu/Window.zig");
|
||||
pub const Render = @import("nu/Render.zig");
|
||||
pub const ImGui = @import("nu/ImGui.zig");
|
||||
pub const ctx = @import("nu/Render/ctx.zig");
|
||||
|
||||
// pub const Bus = @import("nu/Bus.zig");
|
||||
|
||||
pub const Config = struct {
|
||||
window: Window.Config = .{},
|
||||
render: Render.Config = .{},
|
||||
};
|
||||
|
||||
pub const config: Config = if (@hasDecl(root, "nu_config")) root.nu_config else .{};
|
||||
pub const engine = Engine(Window, Render, root.nu_modules);
|
||||
|
||||
// Hooks: setup, teardown, fixed, frame, present
|
||||
|
||||
pub fn main() void {
|
||||
std.log.info("use_debug_messenger: {}", .{config.render.use_debug_messenger});
|
||||
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
const alloc = gpa.allocator();
|
||||
|
||||
std.debug.print("Engine modules: ", .{});
|
||||
inline for (engine.modules) |module| std.debug.print("{s} ", .{@typeName(module)});
|
||||
std.debug.print("\n", .{});
|
||||
|
||||
engine.try_invoke("setup", .{alloc}) catch @panic("Startup failed");
|
||||
defer engine.rinvoke("teardown", .{});
|
||||
|
||||
std.debug.print("Setup complete.\n", .{});
|
||||
|
||||
while (engine.next()) {
|
||||
engine.try_invoke("fixed", .{}) catch @panic("fixed failed!");
|
||||
engine.try_invoke("frame", .{}) catch @panic("frame failed!");
|
||||
|
||||
engine.render() catch @panic("render failed!");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn Engine(comptime D: type, comptime R: type, comptime M: anytype) type {
|
||||
return struct {
|
||||
pub const modules = Graph.sort(.{ D, R } ++ M);
|
||||
|
||||
pub fn next() bool {
|
||||
return D.next();
|
||||
}
|
||||
|
||||
pub fn render() !void {
|
||||
return R.render();
|
||||
}
|
||||
|
||||
// todo remove this
|
||||
pub fn try_invoke(comptime name: []const u8, args: anytype) !void {
|
||||
inline for (modules) |mod| {
|
||||
if (@hasDecl(mod, name)) {
|
||||
try @call(.auto, @field(mod, name), args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn invoke(comptime name: []const u8, args: anytype) void {
|
||||
inline for (modules) |mod| {
|
||||
if (@hasDecl(mod, name)) {
|
||||
@call(.auto, @field(mod, name), args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// todo remove this
|
||||
pub fn try_rinvoke(comptime name: []const u8, args: anytype) !void {
|
||||
comptime var it = std.mem.reverseIterator(modules);
|
||||
inline while (it.next()) |mod| {
|
||||
if (@hasDecl(mod, name)) {
|
||||
try @call(.auto, @field(mod, name), args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rinvoke(comptime name: []const u8, args: anytype) void {
|
||||
comptime var it = std.mem.reverseIterator(modules);
|
||||
inline while (it.next()) |mod| {
|
||||
if (@hasDecl(mod, name)) {
|
||||
@call(.auto, @field(mod, name), args);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const Graph = struct {
|
||||
fn visit(
|
||||
comptime mod: type,
|
||||
comptime mark: *[]const type,
|
||||
comptime result: *[]const type,
|
||||
) void {
|
||||
if (std.mem.indexOfScalar(type, result.*, mod)) |_| return;
|
||||
if (std.mem.indexOfScalar(type, mark.*, mod)) |_| @compileError("Cycle");
|
||||
mark.* = mark.* ++ .{mod};
|
||||
if (@hasDecl(mod, "depends"))
|
||||
for (mod.depends) |dep|
|
||||
visit(dep, mark, result);
|
||||
result.* = result.* ++ .{mod};
|
||||
}
|
||||
|
||||
fn sort(comptime modules: anytype) []const type {
|
||||
var mark: []const type = &.{};
|
||||
var result: []const type = &.{};
|
||||
for (modules) |mod| visit(mod, &mark, &result);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
test {}
|
294
glfw-vulkan/src/nu/Bus.zig
Normal file
294
glfw-vulkan/src/nu/Bus.zig
Normal file
@@ -0,0 +1,294 @@
|
||||
const std = @import("std");
|
||||
// const vk = @import("vk");
|
||||
// const c = @import("../c.zig");
|
||||
// const Window = @import("../au.zig").Window;
|
||||
const Self = @This();
|
||||
|
||||
const Window = @import("Window.zig");
|
||||
const c = Window.c;
|
||||
|
||||
alloc: std.mem.Allocator,
|
||||
events: std.ArrayListUnmanaged(Event) = .{}, // todo bounded array?
|
||||
drops: std.ArrayListUnmanaged([][]const u8) = .{}, // todo bounded array?
|
||||
|
||||
pub fn init(alloc: std.mem.Allocator) Self {
|
||||
return .{
|
||||
.alloc = alloc,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.clear();
|
||||
|
||||
self.events.deinit(self.alloc);
|
||||
self.drops.deinit(self.alloc);
|
||||
}
|
||||
|
||||
pub fn connect(self: *Self, handle: *c.GLFWwindow) void {
|
||||
// todo somehow prevent double-connect?
|
||||
c.glfwSetWindowUserPointer(handle, self);
|
||||
_ = c.glfwSetWindowPosCallback(handle, onWindowPos);
|
||||
_ = c.glfwSetWindowSizeCallback(handle, onWindowSize);
|
||||
_ = c.glfwSetWindowCloseCallback(handle, onWindowClose);
|
||||
_ = c.glfwSetWindowRefreshCallback(handle, onWindowRefresh);
|
||||
_ = c.glfwSetWindowFocusCallback(handle, onWindowFocus);
|
||||
_ = c.glfwSetWindowIconifyCallback(handle, onWindowIconify);
|
||||
_ = c.glfwSetWindowMaximizeCallback(handle, onWindowMaximize);
|
||||
_ = c.glfwSetFramebufferSizeCallback(handle, onFramebufferSize);
|
||||
_ = c.glfwSetWindowContentScaleCallback(handle, onWindowContentScale);
|
||||
_ = c.glfwSetMouseButtonCallback(handle, onMouseButton);
|
||||
_ = c.glfwSetCursorPosCallback(handle, onCursorPos);
|
||||
_ = c.glfwSetCursorEnterCallback(handle, onCursorEnter);
|
||||
_ = c.glfwSetScrollCallback(handle, onScroll);
|
||||
_ = c.glfwSetKeyCallback(handle, onKey);
|
||||
_ = c.glfwSetCharModsCallback(handle, onCharMods);
|
||||
_ = c.glfwSetDropCallback(handle, onDrop);
|
||||
}
|
||||
|
||||
pub fn disconnect(_: *Self, handle: *c.GLFWwindow) void {
|
||||
// todo somehow prevent double-disconnect?
|
||||
c.glfwSetWindowUserPointer(handle, null);
|
||||
_ = c.glfwSetWindowPosCallback(handle, null);
|
||||
_ = c.glfwSetWindowSizeCallback(handle, null);
|
||||
_ = c.glfwSetWindowCloseCallback(handle, null);
|
||||
_ = c.glfwSetWindowRefreshCallback(handle, null);
|
||||
_ = c.glfwSetWindowFocusCallback(handle, null);
|
||||
_ = c.glfwSetWindowIconifyCallback(handle, null);
|
||||
_ = c.glfwSetWindowMaximizeCallback(handle, null);
|
||||
_ = c.glfwSetFramebufferSizeCallback(handle, null);
|
||||
_ = c.glfwSetWindowContentScaleCallback(handle, null);
|
||||
_ = c.glfwSetMouseButtonCallback(handle, null);
|
||||
_ = c.glfwSetCursorPosCallback(handle, null);
|
||||
_ = c.glfwSetCursorEnterCallback(handle, null);
|
||||
_ = c.glfwSetScrollCallback(handle, null);
|
||||
_ = c.glfwSetKeyCallback(handle, null);
|
||||
_ = c.glfwSetCharModsCallback(handle, null);
|
||||
_ = c.glfwSetDropCallback(handle, null);
|
||||
}
|
||||
|
||||
pub fn clear(self: *Self) void {
|
||||
for (self.drops.items) |drop| {
|
||||
for (drop) |path| {
|
||||
self.alloc.free(path);
|
||||
}
|
||||
self.alloc.free(drop);
|
||||
}
|
||||
self.drops.clearAndFree(self.alloc);
|
||||
|
||||
self.events.clearRetainingCapacity();
|
||||
}
|
||||
|
||||
fn getBus(handle: ?*c.GLFWwindow) *Self {
|
||||
return @alignCast(@ptrCast(c.glfwGetWindowUserPointer(handle)));
|
||||
}
|
||||
|
||||
pub const Event = union(enum) {
|
||||
const WindowPos = struct { x: i32, y: i32 };
|
||||
const WindowSize = struct { x: i32, y: i32 };
|
||||
const WindowClose = struct {};
|
||||
const WindowRefresh = struct {};
|
||||
const WindowFocus = struct { focused: bool };
|
||||
const WindowIconify = struct { iconified: bool };
|
||||
const WindowMaximize = struct { maximized: bool };
|
||||
const FramebufferSize = struct { width: u32, height: u32 };
|
||||
const WindowContentScale = struct { x: f32, y: f32 };
|
||||
const MouseButton = struct {
|
||||
button: c_int, // todo enum
|
||||
action: c_int, // todo enum
|
||||
mods: c_int, // todo bitmask
|
||||
};
|
||||
const CursorPos = struct { x: f64, y: f64 };
|
||||
const CursorEnter = struct { entered: bool };
|
||||
const Scroll = struct { dx: f64, dy: f64 };
|
||||
const Key = struct {
|
||||
key: c_int, // todo enum
|
||||
scan: c_int, // todo ???
|
||||
action: c_int, // todo enum
|
||||
mods: c_int, // todo bitmask
|
||||
};
|
||||
const Char = struct {
|
||||
code: u21,
|
||||
};
|
||||
const CharMods = struct {
|
||||
code: u21,
|
||||
mods: c_int, // todo bitmask
|
||||
};
|
||||
const Drop = struct {
|
||||
paths: []const []const u8,
|
||||
};
|
||||
|
||||
windowPos: WindowPos,
|
||||
windowSize: WindowSize,
|
||||
windowClose: WindowClose,
|
||||
windowRefresh: WindowRefresh,
|
||||
windowFocus: WindowFocus,
|
||||
windowIconify: WindowIconify,
|
||||
windowMaximize: WindowMaximize,
|
||||
framebufferSize: FramebufferSize,
|
||||
windowContentScale: WindowContentScale,
|
||||
mouseButton: MouseButton,
|
||||
cursorPos: CursorPos,
|
||||
cursorEnter: CursorEnter,
|
||||
scroll: Scroll,
|
||||
key: Key,
|
||||
char: Char,
|
||||
charMods: CharMods,
|
||||
drop: Drop,
|
||||
};
|
||||
|
||||
fn onWindowPos(handle: ?*c.GLFWwindow, x: c_int, y: c_int) callconv(.C) void {
|
||||
const bus = getBus(handle);
|
||||
bus.events.append(bus.alloc, .{
|
||||
.windowPos = .{
|
||||
.x = @intCast(x),
|
||||
.y = @intCast(y),
|
||||
},
|
||||
}) catch unreachable; // todo circular queue; warn
|
||||
}
|
||||
|
||||
fn onWindowSize(handle: ?*c.GLFWwindow, x: c_int, y: c_int) callconv(.C) void {
|
||||
const bus = getBus(handle);
|
||||
bus.events.append(bus.alloc, .{
|
||||
.windowSize = .{
|
||||
.x = @intCast(x),
|
||||
.y = @intCast(y),
|
||||
},
|
||||
}) catch unreachable; // todo circular queue; warn
|
||||
}
|
||||
|
||||
fn onWindowClose(handle: ?*c.GLFWwindow) callconv(.C) void {
|
||||
const bus = getBus(handle);
|
||||
bus.events.append(bus.alloc, .{
|
||||
.windowClose = .{},
|
||||
}) catch unreachable; // todo circular queue; warn
|
||||
}
|
||||
|
||||
fn onWindowRefresh(handle: ?*c.GLFWwindow) callconv(.C) void {
|
||||
const bus = getBus(handle);
|
||||
bus.events.append(bus.alloc, .{
|
||||
.windowRefresh = .{},
|
||||
}) catch unreachable; // todo circular queue; warn
|
||||
}
|
||||
|
||||
fn onWindowFocus(handle: ?*c.GLFWwindow, focused: c_int) callconv(.C) void {
|
||||
const bus = getBus(handle);
|
||||
bus.events.append(bus.alloc, .{
|
||||
.windowFocus = .{
|
||||
.focused = focused == c.GLFW_TRUE,
|
||||
},
|
||||
}) catch unreachable; // todo circular queue; warn
|
||||
}
|
||||
|
||||
fn onWindowIconify(handle: ?*c.GLFWwindow, iconified: c_int) callconv(.C) void {
|
||||
const bus = getBus(handle);
|
||||
bus.events.append(bus.alloc, .{
|
||||
.windowIconify = .{
|
||||
.iconified = iconified == c.GLFW_TRUE,
|
||||
},
|
||||
}) catch unreachable; // todo circular queue; warn
|
||||
}
|
||||
|
||||
fn onWindowMaximize(handle: ?*c.GLFWwindow, maximized: c_int) callconv(.C) void {
|
||||
const bus = getBus(handle);
|
||||
bus.events.append(bus.alloc, .{
|
||||
.windowMaximize = .{
|
||||
.maximized = maximized == c.GLFW_TRUE,
|
||||
},
|
||||
}) catch unreachable; // todo circular queue; warn
|
||||
}
|
||||
|
||||
fn onFramebufferSize(handle: ?*c.GLFWwindow, width: c_int, height: c_int) callconv(.C) void {
|
||||
const bus = getBus(handle);
|
||||
bus.events.append(bus.alloc, .{
|
||||
.framebufferSize = .{
|
||||
.width = @intCast(width),
|
||||
.height = @intCast(height),
|
||||
},
|
||||
}) catch unreachable; // todo circular queue; warn
|
||||
}
|
||||
|
||||
fn onWindowContentScale(handle: ?*c.GLFWwindow, x: f32, y: f32) callconv(.C) void {
|
||||
const bus = getBus(handle);
|
||||
bus.events.append(bus.alloc, .{
|
||||
.windowContentScale = .{
|
||||
.x = x,
|
||||
.y = y,
|
||||
},
|
||||
}) catch unreachable; // todo circular queue; warn
|
||||
}
|
||||
|
||||
fn onMouseButton(handle: ?*c.GLFWwindow, button: c_int, action: c_int, mods: c_int) callconv(.C) void {
|
||||
const bus = getBus(handle);
|
||||
bus.events.append(bus.alloc, .{
|
||||
.mouseButton = .{
|
||||
.button = button,
|
||||
.action = action,
|
||||
.mods = mods,
|
||||
},
|
||||
}) catch unreachable; // todo circular queue; warn
|
||||
}
|
||||
|
||||
fn onCursorPos(handle: ?*c.GLFWwindow, x: f64, y: f64) callconv(.C) void {
|
||||
const bus = getBus(handle);
|
||||
bus.events.append(bus.alloc, .{
|
||||
.cursorPos = .{
|
||||
.x = x,
|
||||
.y = y,
|
||||
},
|
||||
}) catch unreachable; // todo circular queue; warn
|
||||
}
|
||||
|
||||
fn onCursorEnter(handle: ?*c.GLFWwindow, entered: c_int) callconv(.C) void {
|
||||
const bus = getBus(handle);
|
||||
bus.events.append(bus.alloc, .{
|
||||
.cursorEnter = .{
|
||||
.entered = entered == c.GLFW_TRUE,
|
||||
},
|
||||
}) catch unreachable; // todo circular queue; warn
|
||||
}
|
||||
|
||||
fn onScroll(handle: ?*c.GLFWwindow, dx: f64, dy: f64) callconv(.C) void {
|
||||
const bus = getBus(handle);
|
||||
bus.events.append(bus.alloc, .{
|
||||
.scroll = .{
|
||||
.dx = dx,
|
||||
.dy = dy,
|
||||
},
|
||||
}) catch unreachable; // todo circular queue; warn
|
||||
}
|
||||
|
||||
fn onKey(handle: ?*c.GLFWwindow, key: c_int, scan: c_int, action: c_int, mods: c_int) callconv(.C) void {
|
||||
const bus = getBus(handle);
|
||||
bus.events.append(bus.alloc, .{
|
||||
.key = .{
|
||||
.key = key,
|
||||
.scan = scan,
|
||||
.action = action,
|
||||
.mods = mods,
|
||||
},
|
||||
}) catch unreachable; // todo circular queue; warn
|
||||
}
|
||||
|
||||
fn onCharMods(handle: ?*c.GLFWwindow, code: c_uint, mods: c_int) callconv(.C) void {
|
||||
const bus = getBus(handle);
|
||||
bus.events.append(bus.alloc, .{
|
||||
.charMods = .{
|
||||
.code = @intCast(code),
|
||||
.mods = mods,
|
||||
},
|
||||
}) catch unreachable; // todo circular queue; warn
|
||||
}
|
||||
|
||||
fn onDrop(handle: ?*c.GLFWwindow, count: c_int, paths: [*c][*c]const u8) callconv(.C) void {
|
||||
const bus = getBus(handle);
|
||||
|
||||
const drops = bus.alloc.alloc([]const u8, @intCast(count)) catch unreachable; // todo warn
|
||||
for (drops, paths) |*dst, src| {
|
||||
dst.* = bus.alloc.dupe(u8, std.mem.sliceTo(src, 0)) catch unreachable; // todo warn
|
||||
}
|
||||
bus.drops.append(bus.alloc, drops) catch unreachable; // todo warn
|
||||
|
||||
bus.events.append(bus.alloc, .{
|
||||
.drop = .{ .paths = drops },
|
||||
}) catch unreachable; // todo circular queue; warn
|
||||
}
|
110
glfw-vulkan/src/nu/ImGui.zig
Normal file
110
glfw-vulkan/src/nu/ImGui.zig
Normal file
@@ -0,0 +1,110 @@
|
||||
//! all imgui code through this path
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
const vk = @import("vk");
|
||||
const nu = @import("../nu.zig");
|
||||
|
||||
const Render = @import("Render.zig");
|
||||
const Window = @import("Window.zig");
|
||||
|
||||
const im = @import("cimgui");
|
||||
pub usingnamespace im;
|
||||
|
||||
pub const Config = struct {};
|
||||
const config = nu.config.imgui;
|
||||
|
||||
pub const depends = .{ Render, Window };
|
||||
|
||||
pub fn loader_wrapper(procname: [*c]const u8, _: ?*anyopaque) callconv(.C) vk.PfnVoidFunction {
|
||||
return Render.ctx.glfwGetInstanceProcAddress(Render.ctx.instance.*, procname);
|
||||
}
|
||||
|
||||
var ctx: *im.ImGuiContext = undefined;
|
||||
var descriptor_pool: vk.DescriptorPool = undefined;
|
||||
|
||||
pub fn setup(_: std.mem.Allocator) !void {
|
||||
ctx = im.igCreateContext(null) orelse {
|
||||
return error.igCreateContextFailed;
|
||||
};
|
||||
errdefer im.igDestroyContext(ctx);
|
||||
|
||||
if (!im.impl.ImGui_ImplVulkan_LoadFunctions(loader_wrapper, null)) {
|
||||
return error.igVulkanLoadFunctionsFailed;
|
||||
}
|
||||
|
||||
if (!im.impl.ImGui_ImplGlfw_InitForVulkan(@ptrCast(Window.handle), true)) {
|
||||
return error.igGlfwInitFailed;
|
||||
}
|
||||
errdefer im.impl.ImGui_ImplGlfw_Shutdown();
|
||||
|
||||
descriptor_pool = try Render.ctx.D.createDescriptorPool(
|
||||
&vk.DescriptorPoolCreateInfo{
|
||||
.flags = .{ .free_descriptor_set_bit = true },
|
||||
.pool_size_count = 1,
|
||||
.p_pool_sizes = &.{vk.DescriptorPoolSize{
|
||||
.descriptor_count = 32,
|
||||
.type = .combined_image_sampler,
|
||||
}},
|
||||
.max_sets = 32,
|
||||
},
|
||||
null,
|
||||
);
|
||||
errdefer Render.ctx.D.destroyDescriptorPool(descriptor_pool, null);
|
||||
|
||||
if (im.impl.ImGui_ImplVulkan_Init(@constCast(&im.impl.ImGui_ImplVulkan_InitInfo{
|
||||
.Instance = @ptrFromInt(@intFromEnum(Render.ctx.instance.*)),
|
||||
.PhysicalDevice = @ptrFromInt(@intFromEnum(Render.ctx.pdevice.*)),
|
||||
.Device = @ptrFromInt(@intFromEnum(Render.ctx.device.*)),
|
||||
.QueueFamily = Render.ctx.family.*, // todo
|
||||
.Queue = @ptrFromInt(@intFromEnum(Render.ctx.Q.handle)), // todo
|
||||
.DescriptorPool = @ptrFromInt(@intFromEnum(descriptor_pool)),
|
||||
.RenderPass = null,
|
||||
.MinImageCount = 2,
|
||||
.ImageCount = @intCast(Render.sc.frames_in_flight),
|
||||
.PipelineRenderingCreateInfo = @bitCast(vk.PipelineRenderingCreateInfo{
|
||||
.view_mask = 0,
|
||||
.depth_attachment_format = .undefined,
|
||||
.stencil_attachment_format = .undefined,
|
||||
.color_attachment_count = 1,
|
||||
.p_color_attachment_formats = &.{Render.sc.cinfo.image_format}, // todo
|
||||
}),
|
||||
.MSAASamples = 0,
|
||||
.PipelineCache = null,
|
||||
.Subpass = 0,
|
||||
.UseDynamicRendering = true,
|
||||
.Allocator = null,
|
||||
})) != true) {
|
||||
return error.igVulkanInitFailed;
|
||||
}
|
||||
errdefer im.impl.ImGui_ImplVulkan_Shutdown();
|
||||
|
||||
if (!im.impl.ImGui_ImplVulkan_CreateFontsTexture()) {
|
||||
return error.igVulkanFontTextureFailed;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn teardown() void {
|
||||
Render.ctx.D.deviceWaitIdle() catch |err| std.debug.panic("Device wait failed: {!}", .{err});
|
||||
im.impl.ImGui_ImplVulkan_Shutdown();
|
||||
Render.ctx.D.destroyDescriptorPool(descriptor_pool, null);
|
||||
im.impl.ImGui_ImplGlfw_Shutdown();
|
||||
im.igDestroyContext(ctx);
|
||||
}
|
||||
|
||||
pub fn frame() !void {
|
||||
im.impl.ImGui_ImplGlfw_NewFrame();
|
||||
im.impl.ImGui_ImplVulkan_NewFrame();
|
||||
im.igNewFrame();
|
||||
}
|
||||
|
||||
pub fn rpresent(cmd: Render.ctx.CommandBufferProxy) void { // todo
|
||||
im.igEndFrame();
|
||||
im.igRender();
|
||||
|
||||
im.impl.ImGui_ImplVulkan_RenderDrawData(
|
||||
@ptrCast(im.igGetDrawData()),
|
||||
@ptrFromInt(@intFromEnum(cmd.handle)),
|
||||
null,
|
||||
);
|
||||
}
|
189
glfw-vulkan/src/nu/Render.zig
Normal file
189
glfw-vulkan/src/nu/Render.zig
Normal file
@@ -0,0 +1,189 @@
|
||||
//! Isolate vulkan code (except for ImGui) through this module.
|
||||
//!
|
||||
//! Requires that Window module already be initialized.
|
||||
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const vk = @import("vk");
|
||||
|
||||
const nu = @import("../nu.zig");
|
||||
|
||||
pub const ctx = @import("Render/ctx.zig");
|
||||
|
||||
const SwapChain = @import("Render/SwapChain.zig");
|
||||
|
||||
pub const Config = struct {
|
||||
app_name: [*:0]const u8 = "nu-au-app",
|
||||
app_version: struct {
|
||||
variant: u3 = 0,
|
||||
major: u7 = 0,
|
||||
minor: u10 = 0,
|
||||
patch: u12 = 0,
|
||||
} = .{},
|
||||
engine_name: [*:0]const u8 = "nu-au",
|
||||
engine_version: struct {
|
||||
variant: u3 = 0,
|
||||
major: u7 = 0,
|
||||
minor: u10 = 0,
|
||||
patch: u12 = 0,
|
||||
} = .{},
|
||||
use_debug_messenger: bool = switch (builtin.mode) {
|
||||
.Debug, .ReleaseSafe => true,
|
||||
.ReleaseSmall, .ReleaseFast => false,
|
||||
},
|
||||
frames_in_flight: u8 = 2,
|
||||
};
|
||||
const config = nu.config.render;
|
||||
|
||||
pub const depends = .{nu.Window};
|
||||
|
||||
const Flight = struct {
|
||||
pool: vk.CommandPool = .null_handle,
|
||||
cmd: vk.CommandBuffer = .null_handle,
|
||||
|
||||
pub fn init() !Flight {
|
||||
const pool = try ctx.D.createCommandPool(
|
||||
&.{ .queue_family_index = ctx.family.* },
|
||||
null,
|
||||
);
|
||||
errdefer ctx.D.destroyCommandPool(pool, null);
|
||||
|
||||
var cmds: [1]vk.CommandBuffer = undefined;
|
||||
try ctx.D.allocateCommandBuffers(
|
||||
&vk.CommandBufferAllocateInfo{
|
||||
.command_buffer_count = 1,
|
||||
.command_pool = pool,
|
||||
.level = .primary,
|
||||
},
|
||||
&cmds,
|
||||
);
|
||||
errdefer ctx.D.freeCommandBuffers(pool, 1, &cmds);
|
||||
|
||||
return .{
|
||||
.pool = pool,
|
||||
.cmd = cmds[0],
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: Flight) void {
|
||||
const cmds: [1]vk.CommandBuffer = .{self.cmd};
|
||||
ctx.D.freeCommandBuffers(self.pool, 1, &cmds);
|
||||
ctx.D.destroyCommandPool(self.pool, null);
|
||||
}
|
||||
};
|
||||
|
||||
pub const sc: *const SwapChain = &_sc;
|
||||
|
||||
var _sc: SwapChain = undefined;
|
||||
var _flights: [nu.config.render.frames_in_flight]Flight = undefined;
|
||||
|
||||
pub fn setup(alloc: std.mem.Allocator) !void {
|
||||
try ctx.init(alloc);
|
||||
errdefer ctx.deinit();
|
||||
|
||||
errdefer for (_flights) |flight| flight.deinit();
|
||||
for (&_flights) |*flight| flight.* = try Flight.init();
|
||||
|
||||
_sc = try SwapChain.init(alloc, _flights.len);
|
||||
errdefer _sc.deinit();
|
||||
|
||||
nu.Window.add_resize_callback(&on_resize);
|
||||
}
|
||||
|
||||
fn on_resize(_: u32, _: u32) void {
|
||||
_sc.rebuild() catch @panic("rebuild on resize failed");
|
||||
}
|
||||
|
||||
pub fn teardown() void {
|
||||
_sc.deinit();
|
||||
for (_flights) |flight| flight.deinit();
|
||||
ctx.deinit();
|
||||
}
|
||||
|
||||
pub fn render() !void {
|
||||
const target = try _sc.acquire() orelse return;
|
||||
const flight = &_flights[target.flight_index];
|
||||
|
||||
const render_area: vk.Rect2D = .{
|
||||
.offset = .{ .x = 0, .y = 0 },
|
||||
.extent = _sc.cinfo.image_extent,
|
||||
};
|
||||
|
||||
try ctx.D.resetCommandPool(flight.pool, .{});
|
||||
var cmd = ctx.CommandBufferProxy.init(flight.cmd, ctx.dw);
|
||||
|
||||
try cmd.beginCommandBuffer(&.{ .flags = .{ .one_time_submit_bit = true } });
|
||||
{
|
||||
cmd.pipelineBarrier(
|
||||
.{ .top_of_pipe_bit = true },
|
||||
.{ .color_attachment_output_bit = true },
|
||||
.{},
|
||||
0,
|
||||
null,
|
||||
0,
|
||||
null,
|
||||
1,
|
||||
&.{target.top_of_pipe_barrier()},
|
||||
);
|
||||
|
||||
cmd.beginRendering(&vk.RenderingInfo{
|
||||
.render_area = render_area,
|
||||
.layer_count = 1,
|
||||
.view_mask = 0,
|
||||
.color_attachment_count = 1,
|
||||
.p_color_attachments = &.{
|
||||
vk.RenderingAttachmentInfo{
|
||||
.image_view = target.view,
|
||||
.image_layout = .color_attachment_optimal,
|
||||
.resolve_mode = .{},
|
||||
.resolve_image_view = .null_handle,
|
||||
.resolve_image_layout = .undefined,
|
||||
.load_op = .clear,
|
||||
.store_op = .store,
|
||||
.clear_value = .{ .color = .{ .float_32 = .{ 0.1, 0.1, 0.1, 1 } } },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
nu.engine.invoke("present", .{cmd});
|
||||
nu.engine.rinvoke("rpresent", .{cmd});
|
||||
|
||||
cmd.endRendering();
|
||||
|
||||
cmd.pipelineBarrier(
|
||||
.{ .color_attachment_output_bit = true },
|
||||
.{ .bottom_of_pipe_bit = true },
|
||||
.{},
|
||||
0,
|
||||
null,
|
||||
0,
|
||||
null,
|
||||
1,
|
||||
&.{target.bottom_of_pipe_barrier()},
|
||||
);
|
||||
}
|
||||
try cmd.endCommandBuffer();
|
||||
|
||||
try ctx.Q.submit(
|
||||
1,
|
||||
&.{
|
||||
vk.SubmitInfo{
|
||||
.wait_semaphore_count = 1,
|
||||
// don't start writing to color attachment until the swapchain image has been acquired.
|
||||
.p_wait_semaphores = &.{
|
||||
target.acquired,
|
||||
},
|
||||
.p_wait_dst_stage_mask = &.{
|
||||
vk.PipelineStageFlags{ .color_attachment_output_bit = true },
|
||||
},
|
||||
.command_buffer_count = 1,
|
||||
.p_command_buffers = &.{flight.cmd},
|
||||
.signal_semaphore_count = 1,
|
||||
.p_signal_semaphores = &.{target.complete},
|
||||
},
|
||||
},
|
||||
target.available, // target will become available again once these finish
|
||||
);
|
||||
|
||||
try _sc.present(target);
|
||||
}
|
66
glfw-vulkan/src/nu/Render/Debug.zig
Normal file
66
glfw-vulkan/src/nu/Render/Debug.zig
Normal file
@@ -0,0 +1,66 @@
|
||||
const std = @import("std");
|
||||
const vk = @import("vk");
|
||||
|
||||
pub const ci: vk.DebugUtilsMessengerCreateInfoEXT = .{
|
||||
.message_severity = .{
|
||||
.error_bit_ext = true,
|
||||
.info_bit_ext = true,
|
||||
.verbose_bit_ext = true,
|
||||
.warning_bit_ext = true,
|
||||
},
|
||||
.message_type = .{
|
||||
.device_address_binding_bit_ext = true,
|
||||
.general_bit_ext = true,
|
||||
.performance_bit_ext = true,
|
||||
.validation_bit_ext = true,
|
||||
},
|
||||
.pfn_user_callback = &debug_callback,
|
||||
.p_user_data = null,
|
||||
};
|
||||
|
||||
pub fn debug_callback(
|
||||
msg_severity: vk.DebugUtilsMessageSeverityFlagsEXT,
|
||||
msg_type: vk.DebugUtilsMessageTypeFlagsEXT,
|
||||
p_data: ?*const vk.DebugUtilsMessengerCallbackDataEXT,
|
||||
_: ?*anyopaque,
|
||||
) callconv(vk.vulkan_call_conv) vk.Bool32 {
|
||||
// ripped from std.log.defaultLog
|
||||
|
||||
const data = p_data orelse return vk.FALSE;
|
||||
const message = data.p_message orelse return vk.FALSE;
|
||||
|
||||
const severity_prefix = if (msg_severity.verbose_bit_ext)
|
||||
"verbose:"
|
||||
else if (msg_severity.info_bit_ext)
|
||||
"info:"
|
||||
else if (msg_severity.warning_bit_ext)
|
||||
"warning:"
|
||||
else if (msg_severity.error_bit_ext)
|
||||
"error:"
|
||||
else
|
||||
"?:";
|
||||
|
||||
const type_prefix = if (msg_type.general_bit_ext)
|
||||
""
|
||||
else if (msg_type.validation_bit_ext)
|
||||
"validation:"
|
||||
else if (msg_type.performance_bit_ext)
|
||||
"performance:"
|
||||
else if (msg_type.device_address_binding_bit_ext)
|
||||
"device_address_binding:"
|
||||
else
|
||||
"?:";
|
||||
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
var bw = std.io.bufferedWriter(stderr);
|
||||
const writer = bw.writer();
|
||||
|
||||
std.debug.lockStdErr();
|
||||
defer std.debug.unlockStdErr();
|
||||
nosuspend {
|
||||
writer.print("vk-{s}{s} {s}\n", .{ severity_prefix, type_prefix, message }) catch return vk.FALSE;
|
||||
bw.flush() catch return vk.FALSE;
|
||||
}
|
||||
|
||||
return vk.FALSE;
|
||||
}
|
288
glfw-vulkan/src/nu/Render/SwapChain.zig
Normal file
288
glfw-vulkan/src/nu/Render/SwapChain.zig
Normal file
@@ -0,0 +1,288 @@
|
||||
const std = @import("std");
|
||||
const vk = @import("vk");
|
||||
const ctx = @import("ctx.zig");
|
||||
|
||||
fn _choose_format(alloc: std.mem.Allocator) !vk.SurfaceFormatKHR {
|
||||
var count: u32 = undefined;
|
||||
std.debug.assert(.success == try ctx.I.getPhysicalDeviceSurfaceFormatsKHR(
|
||||
ctx.pdevice.*,
|
||||
ctx.surface.*,
|
||||
&count,
|
||||
null,
|
||||
));
|
||||
const formats = try alloc.alloc(vk.SurfaceFormatKHR, count);
|
||||
defer alloc.free(formats);
|
||||
std.debug.assert(.success == try ctx.I.getPhysicalDeviceSurfaceFormatsKHR(
|
||||
ctx.pdevice.*,
|
||||
ctx.surface.*,
|
||||
&count,
|
||||
formats.ptr,
|
||||
));
|
||||
|
||||
for (formats) |format| {
|
||||
if (format.color_space == .srgb_nonlinear_khr) return format;
|
||||
} else {
|
||||
return formats[0];
|
||||
}
|
||||
}
|
||||
|
||||
fn _choose_mode(alloc: std.mem.Allocator) !vk.PresentModeKHR {
|
||||
_ = ctx;
|
||||
_ = alloc;
|
||||
|
||||
return .fifo_khr;
|
||||
}
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub const Target = struct {
|
||||
image_index: u32,
|
||||
flight_index: u32,
|
||||
image: vk.Image,
|
||||
view: vk.ImageView,
|
||||
acquired: vk.Semaphore, // this semaphore will be signaled when the target is acquired
|
||||
complete: vk.Semaphore, // this semaphore should be signaled when the render is complete
|
||||
available: vk.Fence, // this fence should be signaled when the target flight is available
|
||||
|
||||
pub fn top_of_pipe_barrier(target: Target) vk.ImageMemoryBarrier {
|
||||
return vk.ImageMemoryBarrier{
|
||||
.src_access_mask = .{},
|
||||
.dst_access_mask = .{ .color_attachment_write_bit = true },
|
||||
.old_layout = .undefined,
|
||||
.new_layout = .color_attachment_optimal,
|
||||
.src_queue_family_index = 0,
|
||||
.dst_queue_family_index = 0,
|
||||
.image = target.image,
|
||||
.subresource_range = .{
|
||||
.aspect_mask = .{ .color_bit = true },
|
||||
.base_mip_level = 0,
|
||||
.level_count = 1,
|
||||
.base_array_layer = 0,
|
||||
.layer_count = 1,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn bottom_of_pipe_barrier(target: Target) vk.ImageMemoryBarrier {
|
||||
return vk.ImageMemoryBarrier{
|
||||
.src_access_mask = .{ .color_attachment_write_bit = true },
|
||||
.dst_access_mask = .{},
|
||||
.old_layout = .color_attachment_optimal,
|
||||
.new_layout = .present_src_khr,
|
||||
.src_queue_family_index = 0,
|
||||
.dst_queue_family_index = 0,
|
||||
.image = target.image,
|
||||
.subresource_range = .{
|
||||
.aspect_mask = .{ .color_bit = true },
|
||||
.base_mip_level = 0,
|
||||
.level_count = 1,
|
||||
.base_array_layer = 0,
|
||||
.layer_count = 1,
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const Sync = struct {
|
||||
acquired: vk.Semaphore = .null_handle,
|
||||
complete: vk.Semaphore = .null_handle,
|
||||
available: vk.Fence = .null_handle,
|
||||
};
|
||||
|
||||
const View = struct {
|
||||
image: vk.Image,
|
||||
view: vk.ImageView,
|
||||
};
|
||||
|
||||
alloc: std.mem.Allocator,
|
||||
|
||||
frames_in_flight: u32,
|
||||
flight_index: usize = 0,
|
||||
flight_syncs: std.MultiArrayList(Sync) = .{},
|
||||
|
||||
cinfo: vk.SwapchainCreateInfoKHR = undefined,
|
||||
handle: vk.SwapchainKHR = .null_handle,
|
||||
|
||||
chain: std.MultiArrayList(View) = .{},
|
||||
|
||||
pub fn init(alloc: std.mem.Allocator, frames_in_flight: usize) !Self {
|
||||
var self: Self = .{
|
||||
.alloc = alloc,
|
||||
.frames_in_flight = @intCast(frames_in_flight),
|
||||
};
|
||||
errdefer self.deinit();
|
||||
|
||||
try self.flight_syncs.resize(alloc, frames_in_flight);
|
||||
for (self.flight_syncs.items(.acquired)) |*sem|
|
||||
sem.* = try ctx.D.createSemaphore(&.{}, null);
|
||||
for (self.flight_syncs.items(.complete)) |*sem|
|
||||
sem.* = try ctx.D.createSemaphore(&.{}, null);
|
||||
for (self.flight_syncs.items(.available)) |*fnc|
|
||||
fnc.* = try ctx.D.createFence(&.{ .flags = .{ .signaled_bit = true } }, null);
|
||||
|
||||
const caps = try ctx.getPhysicalDeviceSurfaceCapabilities();
|
||||
const format = try _choose_format(alloc);
|
||||
const mode = try _choose_mode(alloc);
|
||||
|
||||
self.cinfo = .{
|
||||
.surface = ctx.surface.*,
|
||||
.min_image_count = std.math.clamp(
|
||||
@min(3, caps.min_image_count + 1),
|
||||
caps.min_image_count,
|
||||
if (caps.max_image_count > 0) caps.max_image_count else 127,
|
||||
),
|
||||
.image_format = format.format,
|
||||
.image_color_space = format.color_space,
|
||||
.image_extent = undefined, // set in rebuild
|
||||
.image_array_layers = 1,
|
||||
.image_usage = .{ .color_attachment_bit = true },
|
||||
.image_sharing_mode = .exclusive,
|
||||
.pre_transform = .{ .identity_bit_khr = true },
|
||||
.composite_alpha = .{ .opaque_bit_khr = true },
|
||||
.present_mode = mode,
|
||||
.clipped = vk.TRUE,
|
||||
.old_swapchain = .null_handle,
|
||||
};
|
||||
|
||||
// try self.rebuild();
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn rebuild(self: *Self) !void {
|
||||
const caps = try ctx.getPhysicalDeviceSurfaceCapabilities();
|
||||
self.cinfo.image_extent = caps.current_extent;
|
||||
|
||||
self.handle = try ctx.D.createSwapchainKHR(&self.cinfo, null);
|
||||
ctx.D.destroySwapchainKHR(self.cinfo.old_swapchain, null);
|
||||
self.cinfo.old_swapchain = self.handle;
|
||||
|
||||
for (self.chain.items(.view)) |view| ctx.D.destroyImageView(view, null);
|
||||
@memset(self.chain.items(.view), .null_handle);
|
||||
@memset(self.chain.items(.image), .null_handle);
|
||||
|
||||
var count: u32 = undefined;
|
||||
std.debug.assert(
|
||||
.success == try ctx.D.getSwapchainImagesKHR(
|
||||
self.handle,
|
||||
&count,
|
||||
null,
|
||||
),
|
||||
);
|
||||
try self.chain.resize(self.alloc, count);
|
||||
std.debug.assert(
|
||||
.success == try ctx.D.getSwapchainImagesKHR(
|
||||
self.handle,
|
||||
&count,
|
||||
self.chain.items(.image).ptr,
|
||||
),
|
||||
);
|
||||
|
||||
for (self.chain.items(.image), self.chain.items(.view)) |image, *view| {
|
||||
view.* = try ctx.D.createImageView(&vk.ImageViewCreateInfo{
|
||||
.image = image,
|
||||
.view_type = .@"2d",
|
||||
.format = self.cinfo.image_format,
|
||||
.components = .{
|
||||
.r = .identity,
|
||||
.g = .identity,
|
||||
.b = .identity,
|
||||
.a = .identity,
|
||||
},
|
||||
.subresource_range = .{
|
||||
.aspect_mask = .{ .color_bit = true },
|
||||
.base_mip_level = 0,
|
||||
.level_count = 1,
|
||||
.base_array_layer = 0,
|
||||
.layer_count = 1,
|
||||
},
|
||||
}, null);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
// The easiest way to ensure fences and semaphores are not in use for deletion. If it fails, the device must
|
||||
// have failed somehow and sync is not necessary so just continue with cleanup.
|
||||
ctx.D.deviceWaitIdle() catch {};
|
||||
|
||||
// images are owned by swapchain and not explicitly destroyed
|
||||
for (self.chain.items(.view)) |view| ctx.D.destroyImageView(view, null);
|
||||
self.chain.deinit(self.alloc);
|
||||
|
||||
ctx.D.destroySwapchainKHR(self.handle, null);
|
||||
|
||||
for (self.flight_syncs.items(.acquired)) |sem| ctx.D.destroySemaphore(sem, null);
|
||||
for (self.flight_syncs.items(.complete)) |sem| ctx.D.destroySemaphore(sem, null);
|
||||
for (self.flight_syncs.items(.available)) |fnc| ctx.D.destroyFence(fnc, null);
|
||||
self.flight_syncs.deinit(self.alloc);
|
||||
}
|
||||
|
||||
pub fn acquire(self: *Self) !?Target {
|
||||
var target: Target = .{
|
||||
.flight_index = @intCast(self.flight_index),
|
||||
.acquired = self.flight_syncs.items(.acquired)[self.flight_index],
|
||||
.complete = self.flight_syncs.items(.complete)[self.flight_index],
|
||||
.available = self.flight_syncs.items(.available)[self.flight_index],
|
||||
.image = undefined,
|
||||
.view = undefined,
|
||||
.image_index = undefined,
|
||||
};
|
||||
|
||||
const timeout = std.math.maxInt(u64);
|
||||
|
||||
std.debug.assert(.success == try ctx.D.waitForFences(
|
||||
1,
|
||||
&.{target.available},
|
||||
vk.TRUE,
|
||||
std.math.maxInt(u64),
|
||||
));
|
||||
|
||||
// two attempts
|
||||
|
||||
target.image_index = for (0..2) |_| {
|
||||
if (self.handle == .null_handle) try self.rebuild();
|
||||
|
||||
if (ctx.D.acquireNextImageKHR(
|
||||
self.handle,
|
||||
timeout,
|
||||
target.acquired,
|
||||
.null_handle,
|
||||
)) |res| switch (res.result) {
|
||||
.success, .suboptimal_khr => break res.image_index,
|
||||
else => unreachable,
|
||||
} else |err| switch (err) {
|
||||
error.OutOfDateKHR => {
|
||||
self.handle = .null_handle;
|
||||
},
|
||||
else => return err,
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
};
|
||||
|
||||
target.image = self.chain.items(.image)[target.image_index];
|
||||
target.view = self.chain.items(.view)[target.image_index];
|
||||
|
||||
try ctx.D.resetFences(1, &.{target.available});
|
||||
self.flight_index = @mod(self.flight_index + 1, self.flight_syncs.len);
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
pub fn present(self: *Self, target: Target) !void {
|
||||
if (ctx.Q.presentKHR(&vk.PresentInfoKHR{
|
||||
.wait_semaphore_count = 1,
|
||||
.p_wait_semaphores = &.{target.complete},
|
||||
.swapchain_count = 1,
|
||||
.p_swapchains = &.{self.handle},
|
||||
.p_image_indices = &.{target.image_index},
|
||||
.p_results = null,
|
||||
})) |res| switch (res) {
|
||||
.success => {},
|
||||
.suboptimal_khr => self.handle = .null_handle,
|
||||
else => unreachable,
|
||||
} else |err| switch (err) {
|
||||
error.OutOfDateKHR => self.handle = .null_handle,
|
||||
else => return err,
|
||||
}
|
||||
}
|
293
glfw-vulkan/src/nu/Render/au/Bus.zig
Normal file
293
glfw-vulkan/src/nu/Render/au/Bus.zig
Normal file
@@ -0,0 +1,293 @@
|
||||
const std = @import("std");
|
||||
const vk = @import("vk");
|
||||
const c = @import("../c.zig");
|
||||
const Window = @import("../au.zig").Window;
|
||||
const Self = @This();
|
||||
|
||||
alloc: std.mem.Allocator,
|
||||
events: std.ArrayListUnmanaged(Event) = .{}, // todo bounded array?
|
||||
drops: std.ArrayListUnmanaged([][]const u8) = .{}, // todo bounded array?
|
||||
|
||||
pub fn init(alloc: std.mem.Allocator) Self {
|
||||
return .{
|
||||
.alloc = alloc,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.clear();
|
||||
|
||||
self.events.deinit(self.alloc);
|
||||
self.drops.deinit(self.alloc);
|
||||
}
|
||||
|
||||
pub fn connect(self: *Self, window: *Window) !void {
|
||||
// todo somehow prevent double-connect?
|
||||
c.glfwSetWindowUserPointer(window.handle, self);
|
||||
_ = c.glfwSetWindowPosCallback(window.handle, onWindowPos);
|
||||
_ = c.glfwSetWindowSizeCallback(window.handle, onWindowSize);
|
||||
_ = c.glfwSetWindowCloseCallback(window.handle, onWindowClose);
|
||||
_ = c.glfwSetWindowRefreshCallback(window.handle, onWindowRefresh);
|
||||
_ = c.glfwSetWindowFocusCallback(window.handle, onWindowFocus);
|
||||
_ = c.glfwSetWindowIconifyCallback(window.handle, onWindowIconify);
|
||||
_ = c.glfwSetWindowMaximizeCallback(window.handle, onWindowMaximize);
|
||||
_ = c.glfwSetFramebufferSizeCallback(window.handle, onFramebufferSize);
|
||||
_ = c.glfwSetWindowContentScaleCallback(window.handle, onWindowContentScale);
|
||||
_ = c.glfwSetMouseButtonCallback(window.handle, onMouseButton);
|
||||
_ = c.glfwSetCursorPosCallback(window.handle, onCursorPos);
|
||||
_ = c.glfwSetCursorEnterCallback(window.handle, onCursorEnter);
|
||||
_ = c.glfwSetScrollCallback(window.handle, onScroll);
|
||||
_ = c.glfwSetKeyCallback(window.handle, onKey);
|
||||
_ = c.glfwSetCharModsCallback(window.handle, onCharMods);
|
||||
_ = c.glfwSetDropCallback(window.handle, onDrop);
|
||||
}
|
||||
|
||||
pub fn disconnect(_: *Self, window: *Window) !void {
|
||||
// todo somehow prevent double-disconnect?
|
||||
c.glfwSetWindowUserPointer(window.handle, null);
|
||||
_ = c.glfwSetWindowPosCallback(window.handle, null);
|
||||
_ = c.glfwSetWindowSizeCallback(window.handle, null);
|
||||
_ = c.glfwSetWindowCloseCallback(window.handle, null);
|
||||
_ = c.glfwSetWindowRefreshCallback(window.handle, null);
|
||||
_ = c.glfwSetWindowFocusCallback(window.handle, null);
|
||||
_ = c.glfwSetWindowIconifyCallback(window.handle, null);
|
||||
_ = c.glfwSetWindowMaximizeCallback(window.handle, null);
|
||||
_ = c.glfwSetFramebufferSizeCallback(window.handle, null);
|
||||
_ = c.glfwSetWindowContentScaleCallback(window.handle, null);
|
||||
_ = c.glfwSetMouseButtonCallback(window.handle, null);
|
||||
_ = c.glfwSetCursorPosCallback(window.handle, null);
|
||||
_ = c.glfwSetCursorEnterCallback(window.handle, null);
|
||||
_ = c.glfwSetScrollCallback(window.handle, null);
|
||||
_ = c.glfwSetKeyCallback(window.handle, null);
|
||||
_ = c.glfwSetCharModsCallback(window.handle, null);
|
||||
_ = c.glfwSetDropCallback(window.handle, null);
|
||||
}
|
||||
|
||||
pub fn clear(self: *Self) void {
|
||||
for (self.drops.items) |drop| {
|
||||
for (drop) |path| {
|
||||
self.alloc.free(path);
|
||||
}
|
||||
self.alloc.free(drop);
|
||||
}
|
||||
self.drops.clearAndFree(self.alloc);
|
||||
|
||||
self.events.clearRetainingCapacity();
|
||||
}
|
||||
|
||||
fn getBus(handle: ?*c.GLFWwindow) *Self {
|
||||
return @alignCast(@ptrCast(c.glfwGetWindowUserPointer(handle)));
|
||||
}
|
||||
|
||||
pub const Event = union(enum) {
|
||||
const WindowPos = struct { x: i32, y: i32 };
|
||||
const WindowSize = struct { x: i32, y: i32 };
|
||||
const WindowClose = struct {};
|
||||
const WindowRefresh = struct {};
|
||||
const WindowFocus = struct { focused: bool };
|
||||
const WindowIconify = struct { iconified: bool };
|
||||
const WindowMaximize = struct { maximized: bool };
|
||||
const FramebufferSize = struct { extent: vk.Extent2D };
|
||||
const WindowContentScale = struct { x: f32, y: f32 };
|
||||
const MouseButton = struct {
|
||||
button: c_int, // todo enum
|
||||
action: c_int, // todo enum
|
||||
mods: c_int, // todo bitmask
|
||||
};
|
||||
const CursorPos = struct { x: f64, y: f64 };
|
||||
const CursorEnter = struct { entered: bool };
|
||||
const Scroll = struct { dx: f64, dy: f64 };
|
||||
const Key = struct {
|
||||
key: c_int, // todo enum
|
||||
scan: c_int, // todo ???
|
||||
action: c_int, // todo enum
|
||||
mods: c_int, // todo bitmask
|
||||
};
|
||||
const Char = struct {
|
||||
code: u21,
|
||||
};
|
||||
const CharMods = struct {
|
||||
code: u21,
|
||||
mods: c_int, // todo bitmask
|
||||
};
|
||||
const Drop = struct {
|
||||
paths: []const []const u8,
|
||||
};
|
||||
|
||||
windowPos: WindowPos,
|
||||
windowSize: WindowSize,
|
||||
windowClose: WindowClose,
|
||||
windowRefresh: WindowRefresh,
|
||||
windowFocus: WindowFocus,
|
||||
windowIconify: WindowIconify,
|
||||
windowMaximize: WindowMaximize,
|
||||
framebufferSize: FramebufferSize,
|
||||
windowContentScale: WindowContentScale,
|
||||
mouseButton: MouseButton,
|
||||
cursorPos: CursorPos,
|
||||
cursorEnter: CursorEnter,
|
||||
scroll: Scroll,
|
||||
key: Key,
|
||||
char: Char,
|
||||
charMods: CharMods,
|
||||
drop: Drop,
|
||||
};
|
||||
|
||||
fn onWindowPos(handle: ?*c.GLFWwindow, x: c_int, y: c_int) callconv(.C) void {
|
||||
const bus = getBus(handle);
|
||||
bus.events.append(bus.alloc, .{
|
||||
.windowPos = .{
|
||||
.x = @intCast(x),
|
||||
.y = @intCast(y),
|
||||
},
|
||||
}) catch unreachable; // todo circular queue; warn
|
||||
}
|
||||
|
||||
fn onWindowSize(handle: ?*c.GLFWwindow, x: c_int, y: c_int) callconv(.C) void {
|
||||
const bus = getBus(handle);
|
||||
bus.events.append(bus.alloc, .{
|
||||
.windowSize = .{
|
||||
.x = @intCast(x),
|
||||
.y = @intCast(y),
|
||||
},
|
||||
}) catch unreachable; // todo circular queue; warn
|
||||
}
|
||||
|
||||
fn onWindowClose(handle: ?*c.GLFWwindow) callconv(.C) void {
|
||||
const bus = getBus(handle);
|
||||
bus.events.append(bus.alloc, .{
|
||||
.windowClose = .{},
|
||||
}) catch unreachable; // todo circular queue; warn
|
||||
}
|
||||
|
||||
fn onWindowRefresh(handle: ?*c.GLFWwindow) callconv(.C) void {
|
||||
const bus = getBus(handle);
|
||||
bus.events.append(bus.alloc, .{
|
||||
.windowRefresh = .{},
|
||||
}) catch unreachable; // todo circular queue; warn
|
||||
}
|
||||
|
||||
fn onWindowFocus(handle: ?*c.GLFWwindow, focused: c_int) callconv(.C) void {
|
||||
const bus = getBus(handle);
|
||||
bus.events.append(bus.alloc, .{
|
||||
.windowFocus = .{
|
||||
.focused = focused == c.GLFW_TRUE,
|
||||
},
|
||||
}) catch unreachable; // todo circular queue; warn
|
||||
}
|
||||
|
||||
fn onWindowIconify(handle: ?*c.GLFWwindow, iconified: c_int) callconv(.C) void {
|
||||
const bus = getBus(handle);
|
||||
bus.events.append(bus.alloc, .{
|
||||
.windowIconify = .{
|
||||
.iconified = iconified == c.GLFW_TRUE,
|
||||
},
|
||||
}) catch unreachable; // todo circular queue; warn
|
||||
}
|
||||
|
||||
fn onWindowMaximize(handle: ?*c.GLFWwindow, maximized: c_int) callconv(.C) void {
|
||||
const bus = getBus(handle);
|
||||
bus.events.append(bus.alloc, .{
|
||||
.windowMaximize = .{
|
||||
.maximized = maximized == c.GLFW_TRUE,
|
||||
},
|
||||
}) catch unreachable; // todo circular queue; warn
|
||||
}
|
||||
|
||||
fn onFramebufferSize(handle: ?*c.GLFWwindow, width: c_int, height: c_int) callconv(.C) void {
|
||||
const bus = getBus(handle);
|
||||
bus.events.append(bus.alloc, .{
|
||||
.framebufferSize = .{
|
||||
.extent = .{
|
||||
.width = @intCast(width),
|
||||
.height = @intCast(height),
|
||||
},
|
||||
},
|
||||
}) catch unreachable; // todo circular queue; warn
|
||||
}
|
||||
|
||||
fn onWindowContentScale(handle: ?*c.GLFWwindow, x: f32, y: f32) callconv(.C) void {
|
||||
const bus = getBus(handle);
|
||||
bus.events.append(bus.alloc, .{
|
||||
.windowContentScale = .{
|
||||
.x = x,
|
||||
.y = y,
|
||||
},
|
||||
}) catch unreachable; // todo circular queue; warn
|
||||
}
|
||||
|
||||
fn onMouseButton(handle: ?*c.GLFWwindow, button: c_int, action: c_int, mods: c_int) callconv(.C) void {
|
||||
const bus = getBus(handle);
|
||||
bus.events.append(bus.alloc, .{
|
||||
.mouseButton = .{
|
||||
.button = button,
|
||||
.action = action,
|
||||
.mods = mods,
|
||||
},
|
||||
}) catch unreachable; // todo circular queue; warn
|
||||
}
|
||||
|
||||
fn onCursorPos(handle: ?*c.GLFWwindow, x: f64, y: f64) callconv(.C) void {
|
||||
const bus = getBus(handle);
|
||||
bus.events.append(bus.alloc, .{
|
||||
.cursorPos = .{
|
||||
.x = x,
|
||||
.y = y,
|
||||
},
|
||||
}) catch unreachable; // todo circular queue; warn
|
||||
}
|
||||
|
||||
fn onCursorEnter(handle: ?*c.GLFWwindow, entered: c_int) callconv(.C) void {
|
||||
const bus = getBus(handle);
|
||||
bus.events.append(bus.alloc, .{
|
||||
.cursorEnter = .{
|
||||
.entered = entered == c.GLFW_TRUE,
|
||||
},
|
||||
}) catch unreachable; // todo circular queue; warn
|
||||
}
|
||||
|
||||
fn onScroll(handle: ?*c.GLFWwindow, dx: f64, dy: f64) callconv(.C) void {
|
||||
const bus = getBus(handle);
|
||||
bus.events.append(bus.alloc, .{
|
||||
.scroll = .{
|
||||
.dx = dx,
|
||||
.dy = dy,
|
||||
},
|
||||
}) catch unreachable; // todo circular queue; warn
|
||||
}
|
||||
|
||||
fn onKey(handle: ?*c.GLFWwindow, key: c_int, scan: c_int, action: c_int, mods: c_int) callconv(.C) void {
|
||||
const bus = getBus(handle);
|
||||
bus.events.append(bus.alloc, .{
|
||||
.key = .{
|
||||
.key = key,
|
||||
.scan = scan,
|
||||
.action = action,
|
||||
.mods = mods,
|
||||
},
|
||||
}) catch unreachable; // todo circular queue; warn
|
||||
}
|
||||
|
||||
fn onCharMods(handle: ?*c.GLFWwindow, code: c_uint, mods: c_int) callconv(.C) void {
|
||||
const bus = getBus(handle);
|
||||
bus.events.append(bus.alloc, .{
|
||||
.charMods = .{
|
||||
.code = @intCast(code),
|
||||
.mods = mods,
|
||||
},
|
||||
}) catch unreachable; // todo circular queue; warn
|
||||
}
|
||||
|
||||
fn onDrop(handle: ?*c.GLFWwindow, count: c_int, paths: [*c][*c]const u8) callconv(.C) void {
|
||||
const bus = getBus(handle);
|
||||
|
||||
const drops = bus.alloc.alloc([]const u8, @intCast(count)) catch unreachable; // todo warn
|
||||
for (drops, paths) |*dst, src| {
|
||||
dst.* = bus.alloc.dupe(u8, std.mem.sliceTo(src, 0)) catch unreachable; // todo warn
|
||||
}
|
||||
bus.drops.append(bus.alloc, drops) catch unreachable; // todo warn
|
||||
|
||||
bus.events.append(bus.alloc, .{
|
||||
.drop = .{ .paths = drops },
|
||||
}) catch unreachable; // todo circular queue; warn
|
||||
}
|
45
glfw-vulkan/src/nu/Render/au/VkAllocator.zig
Normal file
45
glfw-vulkan/src/nu/Render/au/VkAllocator.zig
Normal file
@@ -0,0 +1,45 @@
|
||||
// todo look into Vulkan Memory Allocator
|
||||
|
||||
const std = @import("std");
|
||||
const vk = @import("vk");
|
||||
const au = @import("../au.zig");
|
||||
|
||||
const Self = @This();
|
||||
|
||||
props: vk.PhysicalDeviceMemoryProperties,
|
||||
|
||||
pub fn init() Self {
|
||||
return .{
|
||||
.props = au.I.getPhysicalDeviceMemoryProperties(au.device_config.pdev),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn heaps(self: Self) []const vk.MemoryHeap {
|
||||
return self.props.memory_heaps[0..self.props.memory_heap_count];
|
||||
}
|
||||
|
||||
pub fn types(self: Self) []const vk.MemoryType {
|
||||
return self.props.memory_types[0..self.props.memory_type_count];
|
||||
}
|
||||
|
||||
pub fn alloc(self: Self, reqs: vk.MemoryRequirements, flags: vk.MemoryPropertyFlags) !vk.DeviceMemory {
|
||||
const memory_type_bits: std.bit_set.IntegerBitSet(vk.MAX_MEMORY_TYPES) = .{
|
||||
.mask = reqs.memory_type_bits,
|
||||
};
|
||||
|
||||
for (self.types(), 0..) |typ, idx| {
|
||||
if (!memory_type_bits.isSet(idx)) continue;
|
||||
if (!typ.property_flags.contains(flags)) continue;
|
||||
|
||||
return try au.D.allocateMemory(&.{
|
||||
.allocation_size = reqs.size,
|
||||
.memory_type_index = @intCast(idx),
|
||||
}, null);
|
||||
}
|
||||
|
||||
return error.NoSuitableMemoryType;
|
||||
}
|
||||
|
||||
pub fn free(_: Self, memory: vk.DeviceMemory) void {
|
||||
au.D.freeMemory(memory, null);
|
||||
}
|
313
glfw-vulkan/src/nu/Render/ctx.zig
Normal file
313
glfw-vulkan/src/nu/Render/ctx.zig
Normal file
@@ -0,0 +1,313 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const vk = @import("vk");
|
||||
const nu = @import("../../nu.zig");
|
||||
|
||||
const Debug = @import("Debug.zig");
|
||||
|
||||
const config = nu.config.render;
|
||||
|
||||
pub const versions: []const vk.ApiInfo = &.{
|
||||
vk.features.version_1_0,
|
||||
vk.features.version_1_1,
|
||||
vk.features.version_1_2,
|
||||
vk.features.version_1_3,
|
||||
};
|
||||
|
||||
pub const instance_exts: []const vk.ApiInfo = if (config.use_debug_messenger) &.{
|
||||
vk.extensions.ext_debug_utils,
|
||||
vk.extensions.khr_surface,
|
||||
} else &.{
|
||||
vk.extensions.khr_surface,
|
||||
};
|
||||
|
||||
pub const device_exts: []const vk.ApiInfo = &.{
|
||||
vk.extensions.khr_swapchain,
|
||||
vk.extensions.khr_dynamic_rendering,
|
||||
vk.extensions.khr_timeline_semaphore,
|
||||
};
|
||||
|
||||
pub const apis = versions ++ instance_exts ++ device_exts;
|
||||
|
||||
pub const layers: []const [*:0]const u8 =
|
||||
if (config.use_debug_messenger) &.{
|
||||
"VK_LAYER_KHRONOS_validation",
|
||||
} else &.{};
|
||||
|
||||
pub const BaseWrapper = vk.BaseWrapper(apis);
|
||||
pub const InstanceWrapper = vk.InstanceWrapper(apis);
|
||||
pub const DeviceWrapper = vk.DeviceWrapper(apis);
|
||||
pub const InstanceProxy = vk.InstanceProxy(apis);
|
||||
pub const DeviceProxy = vk.DeviceProxy(apis);
|
||||
pub const QueueProxy = vk.QueueProxy(apis);
|
||||
pub const CommandBufferProxy = vk.CommandBufferProxy(apis);
|
||||
|
||||
var _iw: InstanceWrapper = undefined;
|
||||
pub const iw: *const InstanceWrapper = &_iw;
|
||||
var _dw: DeviceWrapper = undefined;
|
||||
pub const dw: *const DeviceWrapper = &_dw;
|
||||
|
||||
var _B: BaseWrapper = undefined;
|
||||
pub const B: *const BaseWrapper = &_B;
|
||||
var _I: InstanceProxy = undefined;
|
||||
pub const I: *const InstanceProxy = &_I;
|
||||
var _D: DeviceProxy = undefined;
|
||||
pub const D: *const DeviceProxy = &_D;
|
||||
var _Q: QueueProxy = undefined;
|
||||
pub const Q: *const QueueProxy = &_Q;
|
||||
|
||||
var _instance: vk.Instance = undefined;
|
||||
pub const instance: *const vk.Instance = &_instance;
|
||||
var _messenger: if (config.use_debug_messenger) vk.DebugUtilsMessengerEXT else void = undefined;
|
||||
pub const messenger: *const if (config.use_debug_messenger) vk.DebugUtilsMessengerEXT else void = &_messenger;
|
||||
var _surface: vk.SurfaceKHR = undefined;
|
||||
pub const surface: *const vk.SurfaceKHR = &_surface;
|
||||
var _pdevice: vk.PhysicalDevice = undefined;
|
||||
pub const pdevice: *const vk.PhysicalDevice = &_pdevice;
|
||||
var _device: vk.Device = undefined;
|
||||
pub const device: *const vk.Device = &_device;
|
||||
var _family: u32 = undefined;
|
||||
pub const family: *const u32 = &_family;
|
||||
var _queue: vk.Queue = undefined;
|
||||
pub const queue: *const vk.Queue = &_queue;
|
||||
|
||||
pub fn init(alloc: std.mem.Allocator) !void {
|
||||
_B = if (config.use_debug_messenger)
|
||||
try BaseWrapper.load(glfwGetInstanceProcAddress)
|
||||
else
|
||||
BaseWrapper.loadNoFail(glfwGetInstanceProcAddress);
|
||||
|
||||
_instance = try _create_instance(alloc);
|
||||
_iw = if (config.use_debug_messenger)
|
||||
try InstanceWrapper.load(_instance, glfwGetInstanceProcAddress)
|
||||
else
|
||||
InstanceWrapper.loadNoFail(_instance, glfwGetInstanceProcAddress);
|
||||
errdefer _destroy_instance();
|
||||
_I = InstanceProxy.init(_instance, iw);
|
||||
|
||||
if (config.use_debug_messenger) _messenger = try _create_messenger();
|
||||
errdefer if (config.use_debug_messenger) _destroy_messenger();
|
||||
|
||||
_surface = try _create_surface();
|
||||
errdefer _destroy_surface();
|
||||
|
||||
_pdevice = try _select_pdevice(alloc);
|
||||
_family = try _select_queue_family_index(alloc); // only one queue supported
|
||||
_device = try _create_device(alloc);
|
||||
_dw = if (config.use_debug_messenger)
|
||||
try DeviceWrapper.load(_device, iw.dispatch.vkGetDeviceProcAddr)
|
||||
else
|
||||
DeviceWrapper.loadNoFail(_device, iw.dispatch.vkGetDeviceProcAddr);
|
||||
errdefer _destroy_device();
|
||||
_D = DeviceProxy.init(_device, dw);
|
||||
_queue = D.getDeviceQueue(_family, 0); // only one queue supported
|
||||
_Q = QueueProxy.init(_queue, dw);
|
||||
}
|
||||
|
||||
pub fn deinit() void {
|
||||
_destroy_device();
|
||||
_destroy_surface();
|
||||
if (config.use_debug_messenger) _destroy_messenger();
|
||||
_destroy_instance();
|
||||
}
|
||||
|
||||
fn _create_instance(alloc: std.mem.Allocator) !vk.Instance {
|
||||
var extnames = std.ArrayList([*:0]const u8).init(alloc);
|
||||
defer extnames.deinit();
|
||||
|
||||
for (instance_exts) |ext|
|
||||
try extnames.append(ext.name);
|
||||
|
||||
var glfw_exts_count: u32 = 0;
|
||||
const glfw_exts: [*]const [*:0]const u8 =
|
||||
glfwGetRequiredInstanceExtensions(&glfw_exts_count);
|
||||
try extnames.appendSlice(glfw_exts[0..glfw_exts_count]);
|
||||
|
||||
var ci: vk.InstanceCreateInfo = .{
|
||||
.p_application_info = &vk.ApplicationInfo{
|
||||
.p_application_name = config.app_name,
|
||||
.application_version = vk.makeApiVersion(
|
||||
config.app_version.variant,
|
||||
config.app_version.major,
|
||||
config.app_version.minor,
|
||||
config.app_version.patch,
|
||||
),
|
||||
.p_engine_name = config.engine_name,
|
||||
.engine_version = vk.makeApiVersion(
|
||||
config.engine_version.variant,
|
||||
config.engine_version.major,
|
||||
config.engine_version.minor,
|
||||
config.engine_version.patch,
|
||||
),
|
||||
.api_version = vk.features.version_1_3.version,
|
||||
},
|
||||
.enabled_extension_count = @intCast(extnames.items.len),
|
||||
.pp_enabled_extension_names = extnames.items.ptr,
|
||||
.enabled_layer_count = @intCast(layers.len),
|
||||
.pp_enabled_layer_names = layers.ptr,
|
||||
};
|
||||
|
||||
if (config.use_debug_messenger) ci.p_next = &Debug.ci;
|
||||
|
||||
return try B.createInstance(&ci, null);
|
||||
}
|
||||
|
||||
fn _destroy_instance() void {
|
||||
I.destroyInstance(null);
|
||||
}
|
||||
|
||||
fn _create_messenger() !vk.DebugUtilsMessengerEXT {
|
||||
return try I.createDebugUtilsMessengerEXT(&Debug.ci, null);
|
||||
}
|
||||
|
||||
fn _destroy_messenger() void {
|
||||
I.destroyDebugUtilsMessengerEXT(_messenger, null);
|
||||
}
|
||||
|
||||
fn _create_surface() !vk.SurfaceKHR {
|
||||
var res: vk.SurfaceKHR = undefined;
|
||||
if (glfwCreateWindowSurface(
|
||||
_instance,
|
||||
nu.Window.handle,
|
||||
null,
|
||||
&res,
|
||||
) != .success) {
|
||||
return error.CreateWindowSurfaceFailed;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
fn _destroy_surface() void {
|
||||
I.destroySurfaceKHR(_surface, null);
|
||||
}
|
||||
|
||||
fn _select_pdevice(alloc: std.mem.Allocator) !vk.PhysicalDevice {
|
||||
var count: u32 = undefined;
|
||||
_ = try I.enumeratePhysicalDevices(
|
||||
&count,
|
||||
null,
|
||||
);
|
||||
const pdevs = try alloc.alloc(vk.PhysicalDevice, count);
|
||||
defer alloc.free(pdevs);
|
||||
_ = try I.enumeratePhysicalDevices(
|
||||
&count,
|
||||
pdevs.ptr,
|
||||
);
|
||||
|
||||
const scores = try alloc.alloc(i32, count);
|
||||
@memset(scores, 0);
|
||||
defer alloc.free(scores);
|
||||
|
||||
for (pdevs, scores) |pdev, *score| {
|
||||
const props = I.getPhysicalDeviceProperties(pdev);
|
||||
score.* += switch (props.device_type) {
|
||||
.discrete_gpu => 1000,
|
||||
.integrated_gpu => 500,
|
||||
else => 0,
|
||||
};
|
||||
}
|
||||
|
||||
const idx = std.mem.indexOfMax(i32, scores);
|
||||
|
||||
return pdevs[idx];
|
||||
}
|
||||
|
||||
fn _select_queue_family_index(alloc: std.mem.Allocator) !u32 {
|
||||
var count: u32 = undefined;
|
||||
I.getPhysicalDeviceQueueFamilyProperties(
|
||||
_pdevice,
|
||||
&count,
|
||||
null,
|
||||
);
|
||||
const families = try alloc.alloc(vk.QueueFamilyProperties, count);
|
||||
defer alloc.free(families);
|
||||
I.getPhysicalDeviceQueueFamilyProperties(
|
||||
_pdevice,
|
||||
&count,
|
||||
families.ptr,
|
||||
);
|
||||
|
||||
for (families, 0..) |prop, idx| {
|
||||
if (!prop.queue_flags.graphics_bit) continue;
|
||||
|
||||
if (!prop.queue_flags.transfer_bit) continue;
|
||||
|
||||
if (try I.getPhysicalDeviceSurfaceSupportKHR(
|
||||
_pdevice,
|
||||
@intCast(idx),
|
||||
_surface,
|
||||
) != vk.TRUE) continue;
|
||||
|
||||
return @intCast(idx);
|
||||
}
|
||||
|
||||
return error.NoSuitableQueueFamily;
|
||||
}
|
||||
|
||||
fn _create_device(alloc: std.mem.Allocator) !vk.Device {
|
||||
const qci: []const vk.DeviceQueueCreateInfo = &.{
|
||||
vk.DeviceQueueCreateInfo{
|
||||
.queue_count = 1,
|
||||
.queue_family_index = @intCast(_family),
|
||||
.p_queue_priorities = &[_]f32{1.0},
|
||||
},
|
||||
};
|
||||
|
||||
var extnames = std.ArrayList([*:0]const u8).init(alloc);
|
||||
defer extnames.deinit();
|
||||
|
||||
for (device_exts) |ext|
|
||||
try extnames.append(ext.name);
|
||||
|
||||
const ci: vk.DeviceCreateInfo = .{
|
||||
.queue_create_info_count = @intCast(qci.len),
|
||||
.p_queue_create_infos = qci.ptr,
|
||||
.enabled_extension_count = @intCast(extnames.items.len),
|
||||
.pp_enabled_extension_names = extnames.items.ptr,
|
||||
.p_next = &vk.PhysicalDeviceDynamicRenderingFeaturesKHR{
|
||||
.dynamic_rendering = vk.TRUE,
|
||||
},
|
||||
};
|
||||
|
||||
return try I.createDevice(_pdevice, &ci, null);
|
||||
}
|
||||
|
||||
fn _destroy_device() void {
|
||||
D.deviceWaitIdle() catch |err| switch (err) {
|
||||
error.OutOfHostMemory,
|
||||
error.OutOfDeviceMemory,
|
||||
error.DeviceLost,
|
||||
=> {
|
||||
// In these cases we would destroy the device anyway, so just fall through. Note any child objects must
|
||||
// already be destroyed. This assumes normal cleanup has been done before _destroy_device was called.
|
||||
},
|
||||
else => unreachable,
|
||||
};
|
||||
D.destroyDevice(null);
|
||||
}
|
||||
|
||||
pub fn getPhysicalDeviceSurfaceCapabilities() !vk.SurfaceCapabilitiesKHR {
|
||||
return try I.getPhysicalDeviceSurfaceCapabilitiesKHR(_pdevice, _surface);
|
||||
}
|
||||
|
||||
pub extern fn glfwGetInstanceProcAddress(
|
||||
instance: vk.Instance,
|
||||
procname: [*:0]const u8,
|
||||
) vk.PfnVoidFunction;
|
||||
|
||||
pub extern fn glfwGetPhysicalDevicePresentationSupport(
|
||||
instance: vk.Instance,
|
||||
pdev: vk.PhysicalDevice,
|
||||
queuefamily: u32,
|
||||
) c_int;
|
||||
|
||||
pub extern fn glfwCreateWindowSurface(
|
||||
instance: vk.Instance,
|
||||
window: *nu.Window.c.GLFWwindow,
|
||||
allocation_callbacks: ?*const vk.AllocationCallbacks,
|
||||
surface: *vk.SurfaceKHR,
|
||||
) vk.Result;
|
||||
|
||||
pub extern fn glfwGetRequiredInstanceExtensions(
|
||||
count: *u32,
|
||||
) [*][*:0]const u8;
|
102
glfw-vulkan/src/nu/Window.zig
Normal file
102
glfw-vulkan/src/nu/Window.zig
Normal file
@@ -0,0 +1,102 @@
|
||||
//! GLFW Adaptor
|
||||
|
||||
// todo restructure to handle multiple windows
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
const nu = @import("../nu.zig");
|
||||
|
||||
pub const c = @cImport({
|
||||
@cDefine("GLFW_INCLUDE_NONE", {});
|
||||
@cInclude("GLFW/glfw3.h");
|
||||
});
|
||||
|
||||
pub const Bus = @import("Bus.zig");
|
||||
|
||||
pub const Config = struct {
|
||||
title: [*:0]const u8 = "Hello World",
|
||||
width: u32 = 1280,
|
||||
height: u32 = 720,
|
||||
x11_class_name: [*:0]const u8 = "floating_window",
|
||||
x11_instance_name: [*:0]const u8 = "floating_window",
|
||||
unfocused_wait: f32 = 1.0 / 20.0,
|
||||
};
|
||||
const config: Config = nu.config.window;
|
||||
|
||||
pub fn driver() nu.Driver {
|
||||
return nu.Driver{
|
||||
.module = .{
|
||||
.name = "Window",
|
||||
.dependencies = &.{}, // todo bus
|
||||
.setup = setup,
|
||||
.teardown = teardown,
|
||||
},
|
||||
.next = next,
|
||||
};
|
||||
}
|
||||
|
||||
pub var handle: *c.GLFWwindow = undefined;
|
||||
|
||||
pub fn setup(_: std.mem.Allocator) !void {
|
||||
if (c.glfwInit() != c.GLFW_TRUE) std.debug.panic("GLFW Init Failed", .{});
|
||||
|
||||
if (c.glfwVulkanSupported() != c.GLFW_TRUE)
|
||||
std.debug.panic("GLFW Vulkan not supported", .{});
|
||||
|
||||
c.glfwWindowHint(c.GLFW_CLIENT_API, c.GLFW_NO_API);
|
||||
c.glfwWindowHintString(c.GLFW_X11_CLASS_NAME, config.x11_class_name);
|
||||
c.glfwWindowHintString(c.GLFW_X11_INSTANCE_NAME, config.x11_instance_name);
|
||||
handle = c.glfwCreateWindow(
|
||||
@intCast(config.width),
|
||||
@intCast(config.height),
|
||||
config.title,
|
||||
null,
|
||||
null,
|
||||
) orelse std.debug.panic("GLFW Create Window Failed", .{});
|
||||
|
||||
_ = c.glfwSetFramebufferSizeCallback(handle, &resize_callback);
|
||||
|
||||
// bus.connect(handle);
|
||||
// errdefer bus.disconnect(handle);
|
||||
}
|
||||
|
||||
pub fn teardown() void {
|
||||
c.glfwDestroyWindow(handle);
|
||||
c.glfwTerminate();
|
||||
}
|
||||
|
||||
pub fn next() bool {
|
||||
if (c.glfwWindowShouldClose(handle) == c.GLFW_TRUE)
|
||||
return false;
|
||||
|
||||
if (c.glfwGetWindowAttrib(handle, c.GLFW_FOCUSED) == c.GLFW_TRUE) {
|
||||
c.glfwPollEvents();
|
||||
} else {
|
||||
c.glfwWaitEventsTimeout(config.unfocused_wait);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
var _resize_callbacks: std.BoundedArray(*const fn (u32, u32) void, 16) = .{};
|
||||
|
||||
fn resize_callback(_: ?*c.GLFWwindow, w: c_int, h: c_int) callconv(.C) void {
|
||||
for (_resize_callbacks.slice()) |cb| {
|
||||
cb(@intCast(w), @intCast(h));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_resize_callback(cb: *const fn (u32, u32) void) void {
|
||||
_resize_callbacks.appendAssumeCapacity(cb);
|
||||
}
|
||||
|
||||
pub fn set_title(title: [:0]const u8) void {
|
||||
c.glfwSetWindowTitle(handle, title);
|
||||
}
|
||||
|
||||
pub fn size() std.meta.Tuple(&[_]type{ u32, u32 }) {
|
||||
var w: c_int = undefined;
|
||||
var h: c_int = undefined;
|
||||
c.glfwGetFramebufferSize(handle, &w, &h);
|
||||
return .{ @intCast(w), @intCast(h) };
|
||||
}
|
9
glfw-vulkan/src/shaders/triangle.frag
Normal file
9
glfw-vulkan/src/shaders/triangle.frag
Normal file
@@ -0,0 +1,9 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec3 v_color;
|
||||
|
||||
layout(location = 0) out vec4 f_color;
|
||||
|
||||
void main() {
|
||||
f_color = vec4(v_color, 1.0);
|
||||
}
|
15
glfw-vulkan/src/shaders/triangle.vert
Normal file
15
glfw-vulkan/src/shaders/triangle.vert
Normal file
@@ -0,0 +1,15 @@
|
||||
#version 450
|
||||
|
||||
layout (set = 0, binding = 0) uniform CameraBuffer {
|
||||
mat4 mat;
|
||||
} cam;
|
||||
|
||||
layout (location = 0) in vec4 a_pos;
|
||||
layout (location = 1) in vec3 a_color;
|
||||
|
||||
layout (location = 0) out vec3 v_color;
|
||||
|
||||
void main() {
|
||||
gl_Position = a_pos * cam.mat;
|
||||
v_color = a_color;
|
||||
}
|
Reference in New Issue
Block a user