diff --git a/README.md b/README.md index b92a891..d94ac84 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,11 @@ Low cortisol crossplatform RHI (Rendering Hardware Interface) ## Features - Fire and forget - Dead simple + Performance + Flexible -- Handles +- Generational Handles - Auto managed +- No hidden control flow +- No hidden memory allocations +- No preprocessor, no macros ## Architecture ```text @@ -34,7 +37,7 @@ src/rhi/vulkan/resource.zig | `deleteX(handle)` | Releases the resource and removes it from the resource pool | | `releaseX(handle)` | Releases the resource without removing it from the resource pool | | `getXInfo(handle)` | Returns backend-independent information about the resource | -| `getRawVkX(handle)` | Returns the backend-specific raw Vulkan GPU handle | +| `getRawX(handle)` | Returns backend-specific raw GPU handle | ## Example ### Core @@ -57,42 +60,56 @@ const simple_frag = try gfx.makeShader("examples/assets/shaders/compiled/simple. ### Pipelines ```zig const pipeline = try gfx.makePipeline(.{ - .vertex_attributes = &.{ - .{ .location = 0, .format = .vec2, .offset = @offsetOf(Vertex, "pos") }, - .{ .location = 1, .format = .vec2, .offset = @offsetOf(Vertex, "normal") }, - .{ .location = 2, .format = .vec2, .offset = @offsetOf(Vertex, "uv") }, - }, - .vertex_binding = .{ .stride = @sizeOf(Vertex) }, + .vertex_attributes = &.{ + .{ .location = 0, .format = .vec2, .offset = @offsetOf(Vertex, "pos") }, + .{ .location = 1, .format = .vec2, .offset = @offsetOf(Vertex, "normal") }, + .{ .location = 2, .format = .vec2, .offset = @offsetOf(Vertex, "uv") }, + }, + .vertex_binding = .{ .stride = @sizeOf(Vertex) }, - .uniforms = &.{ - .{ .name = "material", .offset = 0, .size = @sizeOf(UnlitMaterial) }, - }, + .uniforms = &.{ + .{ .name = "material", .offset = 0, .size = @sizeOf(UnlitMaterial) }, + }, - .textures = &.{texture}, + .textures = &.{texture}, - .blend_mode = .alpha, + .blend_mode = .alpha, - .vert_shader = simple_vert, - .frag_shader = simple_frag, + .vert_shader = simple_vert, + .frag_shader = simple_frag, - .device = device, - .swapchain = swapchain, + .device = device, + .swapchain = swapchain, }); ``` ### Render Pass ```zig if (gfx.beginPass(swapchain, .{ - .clear_color = .{ 0.01, 0.01, 0.01, 1.0 }, + .clear_color = .{ 0.01, 0.01, 0.01, 1.0 }, })) |pass| { - defer pass.end(); + defer pass.end(); - try vbuf.bind(); - try ibuf.bind(); + try vbuf.bind(); + try ibuf.bind(); - try pipeline.setUniform("material", material); + try pipeline.setUniform("material", material); - try pipeline.bind(); - pass.drawIndexed(indices.len, 1, 0, 0, 0); + try pipeline.bind(); + pass.drawIndexed(indices.len, 1, 0, 0, 0); +} +``` +### House +```zig +switch (try gfx.getRawAdapter(adapter)) { + .vulkan => |v| std.debug.print("---> {}\n", .{v}), +} + +switch (try gfx.getRawDevice(device)) { + .vulkan => |v| std.debug.print("---> {}\n", .{v}), +} + +switch (try gfx.getRawPipeline(pipeline)) { + .vulkan => |v| std.debug.print("---> {}\n", .{v}), } ``` diff --git a/build.zig b/build.zig index 26443bb..126a19a 100644 --- a/build.zig +++ b/build.zig @@ -11,10 +11,15 @@ pub fn build(b: *std.Build) !void { .target = target, .optimize = optimize, + + .link_libc = true, + .link_libcpp = true, }); const mod_tests = b.addTest(.{ .root_module = mod, + .use_lld = true, + .use_llvm = true, }); const run_mod_tests = b.addRunArtifact(mod_tests); @@ -42,11 +47,13 @@ pub fn build(b: *std.Build) !void { // SYSTEM DEPENDENCIES // + const vulkan_headers = b.dependency("vulkan_headers", .{}); const vulkan = b.addTranslateC(.{ .target = target, .optimize = optimize, - .root_source_file = b.path("./src/vendor/vulkan/vulkan.h"), + .root_source_file = b.path("src/vendor/vulkan/vulkan.h"), }); + vulkan.addIncludePath(vulkan_headers.path("include")); mod.addImport("vulkan", vulkan.createModule()); mod.linkSystemLibrary("vulkan", .{}); @@ -102,11 +109,17 @@ pub fn build(b: *std.Build) !void { .root_source_file = b.path(ex_src), .target = target, .optimize = optimize, + + .link_libc = true, + .link_libcpp = true, }); var example = b.addExecutable(.{ .name = ex_name, .root_module = exe_mod, + + .use_lld = true, + .use_llvm = true, }); example.root_module.addImport("gfx", mod); diff --git a/build.zig.zon b/build.zig.zon index 7624bc1..96dba49 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -16,6 +16,10 @@ .url = "git+https://nyxformat.com/abux/NYXMath.git#cb90286ae31ac1a5d8c9c29ad0b188a5f36b4321", .hash = "NYXMath-0.0.0-BD6ST-7WAABYZ7BWgw_wglpgaGdjCRx8ZFX85SdlOhjp", }, + .vulkan_headers = .{ + .url = "https://github.com/KhronosGroup/Vulkan-Headers/archive/refs/tags/v1.4.359.tar.gz", + .hash = "N-V-__8AAMgnfgKxQBLuasOmdRqVVIc1HwfkjIUSwaryYgzo", + }, }, .paths = .{ diff --git a/examples/simple_3D/simple_3D.zig b/examples/simple_3D/simple_3D.zig index ab80bf8..ff964e4 100644 --- a/examples/simple_3D/simple_3D.zig +++ b/examples/simple_3D/simple_3D.zig @@ -157,6 +157,10 @@ pub fn main(init: std.process.Init) !void { const device = try gfx.makeDevice(adapter, surface); const swapchain = try gfx.makeSwapchain(adapter, device, surface); + if (gfx.getAdapterInfo(adapter)) |info| { + std.debug.print("Name: {s} | Type: {s}\n", .{ info.name, @tagName(info.gpu_type) }); + } + // --- TEXTURES --- const texture = try gfx.makeTexture(.{ .image = .{ .path = "examples/assets/textures/texture.jpg", .vertical_flip = true }, diff --git a/src/gfx.zig b/src/gfx.zig index 29b860b..46887e8 100644 --- a/src/gfx.zig +++ b/src/gfx.zig @@ -36,12 +36,19 @@ pub const Texture = common.Texture; pub const Shader = common.Shader; pub const Pipeline = common.Pipeline; -// --- RHI --- -const VulkanRHI = @import("rhi/vulkan/vulkan.zig"); +// --- INFOS --- +pub const AdapterInfo = common.AdapterInfo; -/// ---------------------------------------------------- -/// ---------------------------------------------------- -pub const Backend = enum { vulkan }; +// --- RAWS --- +pub const RawAdapter = common.RawAdapter; +pub const RawDevice = common.RawDevice; +pub const RawBuffer = common.RawBuffer; +pub const RawShader = common.RawShader; +pub const RawPipeline = common.RawPipeline; + +// --- RHI --- +pub const Backend = common.Backend; +const VulkanRHI = @import("rhi/vulkan/vulkan.zig"); /// ---------------------------------------------------- /// ---------------------------------------------------- @@ -70,6 +77,10 @@ config: Config, alloc: std.mem.Allocator, io: std.Io, +// +// LIFETIME +// + /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn init( @@ -97,6 +108,10 @@ pub fn deinit(self: *Self) void { } } +// +// ADAPTER +// + /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn makeAdapter(self: *Self, config: AdapterConfig) !Handle(Adapter) { @@ -105,6 +120,42 @@ pub fn makeAdapter(self: *Self, config: AdapterConfig) !Handle(Adapter) { }; } +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn deleteAdapter(self: *Self, adapter: Handle(Adapter)) void { + switch (self.ctx) { + .vulkan => |*v| v.deleteAdapter(adapter), + } +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn releaseAdapter(self: *Self, adapter: Handle(Adapter)) void { + switch (self.ctx) { + .vulkan => |*v| v.releaseAdapter(adapter), + } +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn getAdapterInfo(self: *Self, adapter: Handle(Adapter)) ?AdapterInfo { + return switch (self.ctx) { + .vulkan => |*v| v.getAdapterInfo(adapter), + }; +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn getRawAdapter(self: *Self, adapter: Handle(Adapter)) !RawAdapter { + return switch (self.ctx) { + .vulkan => |*v| .{ .vulkan = try v.getRawAdapter(adapter) }, + }; +} + +// +// DEVICE +// + /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn makeDevice( @@ -135,13 +186,16 @@ pub fn releaseDevice(self: *Self, device: Handle(Device)) void { /// ---------------------------------------------------- /// ---------------------------------------------------- -pub fn getRAWVKDevice(self: *Self, device: Handle(Device)) ?*vk.VkDevice_T { - switch (self.ctx) { - .vulkan => |*v| return v.getRAWDevice(device), - } - return null; +pub fn getRawDevice(self: *Self, device: Handle(Device)) !RawDevice { + return switch (self.ctx) { + .vulkan => |*v| .{ .vulkan = try v.getRawDevice(device) }, + }; } +// +// BUFFER +// + /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn makeBuffer( @@ -168,13 +222,33 @@ pub fn makeBuffer( } /// ---------------------------------------------------- -/// TODO: /// ---------------------------------------------------- -pub fn getRawVKBuffer(self: *Self, buffer: Handle(Buffer)) !void { - _ = self; - _ = buffer; +pub fn deleteBuffer(self: *Self, buffer: Handle(Buffer)) void { + switch (self.ctx) { + .vulkan => |*v| v.deleteBuffer(buffer), + } } +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn releaseBuffer(self: *Self, buffer: Handle(Buffer)) void { + switch (self.ctx) { + .vulkan => |*v| v.releaseBuffer(buffer), + } +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn getRawBuffer(self: *Self, buffer: Handle(Buffer)) !RawBuffer { + return switch (self.ctx) { + .vulkan => |*v| .{ .vulkan = try v.getRawBuffer(buffer) }, + }; +} + +// +// SURFACE +// + /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn makeSurface(self: *Self) !Handle(Surface) { @@ -183,6 +257,10 @@ pub fn makeSurface(self: *Self) !Handle(Surface) { }; } +// +// SWAPCHAIN +// + /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn makeSwapchain( @@ -196,6 +274,10 @@ pub fn makeSwapchain( }; } +// +// SHADER +// + /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn makeShader( @@ -208,6 +290,33 @@ pub fn makeShader( }; } +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn deleteShader(self: *Self, shader: Handle(Shader)) void { + switch (self.ctx) { + .vulkan => |*v| v.deleteShader(shader), + } +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn releaseShader(self: *Self, shader: Handle(Shader)) void { + switch (self.ctx) { + .vulkan => |*v| v.releaseShader(shader), + } +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn getRawShader(self: *Self, shader: Handle(Shader)) !RawShader { + return switch (self.ctx) { + .vulkan => |*v| .{ .vulkan = try v.getRawShader(shader) }, + }; +} +// +// PIPELINE +// + /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn makePipeline(self: *Self, config: PipelineConfig) !Pipeline { @@ -220,6 +329,34 @@ pub fn makePipeline(self: *Self, config: PipelineConfig) !Pipeline { }; } +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn deletePipeline(self: *Self, pipeline: Handle(Pipeline)) void { + switch (self.ctx) { + .vulkan => |*v| v.deletePipeline(pipeline), + } +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn releasePipeline(self: *Self, pipeline: Handle(Pipeline)) void { + switch (self.ctx) { + .vulkan => |*v| v.releasePipeline(pipeline), + } +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn getRawPipeline(self: *Self, pipeline: Handle(Pipeline)) !RawPipeline { + return switch (self.ctx) { + .vulkan => |*v| .{ .vulkan = try v.getRawPipeline(pipeline) }, + }; +} + +// +// TEXTURE +// + /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn makeImage( @@ -258,6 +395,10 @@ pub fn makeTexture( }; } +// +// MISC +// + /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn beginFrame( diff --git a/src/rhi/common.zig b/src/rhi/common.zig index c9135f5..f31e885 100644 --- a/src/rhi/common.zig +++ b/src/rhi/common.zig @@ -2,10 +2,55 @@ //! Shared Handles and Configs across every backend //! ---------------------------------------------------- +const vk = @import("vulkan"); const gtl = @import("gtl"); const Gfx = @import("../gfx.zig"); const Handle = gtl.Handle; +// TODO: Organize these into categories... someday + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub const Backend = enum { vulkan }; + +// +// RAW +// + +pub const RawAdapter = union(Backend) { + vulkan: *vk.VkPhysicalDevice_T, +}; + +pub const RawDevice = union(Backend) { + vulkan: *vk.VkDevice_T, +}; + +pub const RawBuffer = union(Backend) { + vulkan: *vk.VkBuffer_T, +}; + +pub const RawShader = union(Backend) { + vulkan: *vk.VkShaderModule_T, +}; + +pub const RawPipeline = union(Backend) { + vulkan: *vk.VkPipeline_T, +}; + +// +// INFOS +// + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub const AdapterInfo = struct { + name: [256]u8, + gpu_type: GPUType, + + device_id: u32, + vendor_id: u32, +}; + /// ---------------------------------------------------- /// ---------------------------------------------------- pub const Window = union(enum) { diff --git a/src/rhi/vulkan/resource/adapter.zig b/src/rhi/vulkan/resource/adapter.zig index 7db28b6..fbf8930 100644 --- a/src/rhi/vulkan/resource/adapter.zig +++ b/src/rhi/vulkan/resource/adapter.zig @@ -99,18 +99,6 @@ fn isDeviceSuitable(device: vk.VkPhysicalDevice, gpu_type: GPUType) !bool { var features: vk.VkPhysicalDeviceFeatures = .{}; vk.vkGetPhysicalDeviceFeatures(device, &features); - if (gtl.log.print(.debug, "DEVICE", gtl.ansi.blue)) |v| { - defer v.end(); - - v.write("Name: {s}", .{properties.deviceName}); - v.write("Type: {s}", .{ - switch (properties.deviceType) { - vk.VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU => "discrete", - else => "idk", - }, - }); - } - const matches_class = switch (gpu_type) { .any => true, .discrete => properties.deviceType == vk.VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU, diff --git a/src/rhi/vulkan/vulkan.zig b/src/rhi/vulkan/vulkan.zig index 4461990..550b762 100644 --- a/src/rhi/vulkan/vulkan.zig +++ b/src/rhi/vulkan/vulkan.zig @@ -42,6 +42,9 @@ const ImageView = common.ImageView; const Sampler = common.Sampler; const Texture = common.Texture; +// --- INFOS --- +const AdapterInfo = common.AdapterInfo; + // --- # --- pub const MAX_FRAMES_IN_FLIGHT: usize = 3; @@ -84,6 +87,10 @@ sync_device: ?*vk.VkDevice_T = null, alloc: std.mem.Allocator, io: std.Io, +// +// LIFETIME +// + /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn init( @@ -129,12 +136,24 @@ pub fn deinit(self: *Self) void { self.instance.deinit(); } +// +// ADAPTER +// + /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn makeAdapter(self: *Self, config: AdapterConfig) !Handle(Adapter) { return try self.resource.adapters.put(try .init(self, config)); } +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn deleteAdapter(self: *Self, adapter: Handle(Adapter)) void { + if (self.resource.adapters.get(adapter)) |_| { + self.resource.adapters.remove(adapter) catch unreachable; + } +} + /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn releaseAdapter(self: *Self, adapter: Handle(Adapter)) void { @@ -143,10 +162,36 @@ pub fn releaseAdapter(self: *Self, adapter: Handle(Adapter)) void { /// ---------------------------------------------------- /// ---------------------------------------------------- -pub fn deleteAdapter(self: *Self, adapter: Handle(Adapter)) void { - self.resource.adapters.remove(adapter) catch unreachable; +pub fn getAdapterInfo(self: *Self, adapter: Handle(Adapter)) ?AdapterInfo { + const raw_adapter = self.resource.adapters.get(adapter) orelse return null; + + var props: vk.VkPhysicalDeviceProperties = .{}; + vk.vkGetPhysicalDeviceProperties(raw_adapter.raw, &props); + + return AdapterInfo{ + .name = props.deviceName, + .gpu_type = switch (props.deviceType) { + vk.VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU => .discrete, + vk.VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU => .integrated, + vk.VK_PHYSICAL_DEVICE_TYPE_CPU => .software, + else => .any, + }, + .device_id = props.deviceID, + .vendor_id = props.vendorID, + }; } +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn getRawAdapter(self: *Self, adapter: Handle(Adapter)) !*vk.VkPhysicalDevice_T { + const raw_adapter = self.resource.adapters.get(adapter) orelse return error.AdapterNotFound; + return raw_adapter.raw; +} + +// +// DEVICE +// + /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn makeDevice( @@ -178,17 +223,25 @@ pub fn releaseDevice(self: *Self, device: Handle(Device)) void { /// ---------------------------------------------------- /// ---------------------------------------------------- -pub fn getRAWDevice(self: *Self, device: Handle(Device)) ?*vk.VkDevice_T { - if (self.resource.devices.get(device)) |raw| return raw.raw; - return null; +pub fn getRawDevice(self: *Self, device: Handle(Device)) !*vk.VkDevice_T { + if (self.resource.devices.get(device)) |raw| return raw.raw orelse return error.NullDevice; + return error.DeviceNotFound; } +// +// SURFACE +// + /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn makeSurface(self: *Self) !Handle(Surface) { return try self.resource.surfaces.put(try .init(&self.instance, self.config.window)); } +// +// SWAPCHAIN +// + /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn makeSwapchain( @@ -221,6 +274,34 @@ pub fn makeShader( return try self.resource.shaders.put(try .init(spv_path, raw_device, self.alloc, self.io)); } +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn deleteShader(self: *Self, shader: Handle(Shader)) void { + if (self.resource.shaders.get(shader)) |raw| { + raw.deinit(); + self.resource.shaders.remove(shader) catch unreachable; + } +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn releaseShader(self: *Self, shader: Handle(Shader)) void { + if (self.resource.shaders.get(shader)) |raw| { + raw.deinit(); + } +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn getRawShader(self: *Self, shader: Handle(Shader)) !*vk.VkShaderModule_T { + if (self.resource.shaders.get(shader)) |raw| return raw.raw orelse return error.NullShader; + return error.ShaderNotFound; +} + +// +// PIPELINE +// + /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn makePipeline(self: *Self, config: PipelineConfig) !Handle(Pipeline) { @@ -229,6 +310,30 @@ pub fn makePipeline(self: *Self, config: PipelineConfig) !Handle(Pipeline) { return try self.resource.pipelines.put(try .init(raw_swapchain, raw_device, config, self, self.alloc)); } +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn deletePipeline(self: *Self, pipeline: Handle(Pipeline)) void { + if (self.resource.pipelines.get(pipeline)) |raw| { + raw.deinit(); + self.resource.pipelines.remove(pipeline) catch unreachable; + } +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn releasePipeline(self: *Self, pipeline: Handle(Pipeline)) void { + if (self.resource.pipelines.get(pipeline)) |raw| { + raw.deinit(); + } +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn getRawPipeline(self: *Self, pipeline: Handle(Pipeline)) !*vk.VkPipeline_T { + if (self.resource.pipelines.get(pipeline)) |raw| return raw.raw orelse return error.NullPipeline; + return error.PipelineNotFound; +} + /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn makeImage( @@ -266,6 +371,10 @@ pub fn makeTexture( return try self.resource.textures.put(try .init(config, raw_adapter, raw_device)); } +// +// BUFFER +// + /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn makeBuffer( @@ -281,6 +390,30 @@ pub fn makeBuffer( return try self.resource.buffers.put(try .init(size, usage, sharing, raw_adapter, raw_device)); } +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn deleteBuffer(self: *Self, buffer: Handle(Buffer)) void { + if (self.resource.buffers.get(buffer)) |raw| { + raw.deinit(); + self.resource.buffers.remove(buffer) catch unreachable; + } +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn releaseBuffer(self: *Self, buffer: Handle(Buffer)) void { + if (self.resource.buffers.get(buffer)) |raw| { + raw.deinit(); + } +} + +/// ---------------------------------------------------- +/// ---------------------------------------------------- +pub fn getRawBuffer(self: *Self, buffer: Handle(Buffer)) !*vk.VkBuffer_T { + if (self.resource.buffers.get(buffer)) |raw| return raw.raw; + return error.BufferNotFound; +} + /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn bindBuffer(self: *Self, buffer: Handle(Buffer)) !void { diff --git a/src/vendor/vulkan/vulkan.h b/src/vendor/vulkan/vulkan.h index e73eaa9..10aa359 100644 --- a/src/vendor/vulkan/vulkan.h +++ b/src/vendor/vulkan/vulkan.h @@ -1,2 +1,9 @@ +#if defined(_WIN32) +#define VK_USE_PLATFORM_WIN32_KHR +#elif defined(__linux__) #define VK_USE_PLATFORM_XLIB_KHR +#elif defined(__APPLE__) +#define VK_USE_PLATFORM_METAL_EXT +#endif + #include