From 82a13f47a415116906319765ffc9831a86a721c9 Mon Sep 17 00:00:00 2001 From: Kbz-8 Date: Tue, 5 May 2026 02:21:35 +0200 Subject: [PATCH] working on wsi --- README.md | 12 +-- src/soft/SoftImage.zig | 44 ++++++--- src/soft/SoftInstance.zig | 6 +- src/soft/SoftPhysicalDevice.zig | 12 ++- src/soft/device/blitter.zig | 16 +-- src/vulkan/Device.zig | 2 +- src/vulkan/Dispatchable.zig | 6 +- src/vulkan/Image.zig | 19 +++- src/vulkan/Instance.zig | 7 +- src/vulkan/NonDispatchable.zig | 10 +- src/vulkan/PhysicalDevice.zig | 55 +++++++++-- src/vulkan/Queue.zig | 44 ++++++++- src/vulkan/lib_vulkan.zig | 143 ++++++++++++++++++++++++--- src/vulkan/wsi/SurfaceKHR.zig | 16 +-- src/vulkan/wsi/SwapchainKHR.zig | 57 +++++++++-- src/vulkan/wsi/WaylandSurfaceKHR.zig | 90 ++++++++++++++--- src/vulkan/wsi/clients/wayland.zig | 34 ++++--- 17 files changed, 458 insertions(+), 115 deletions(-) diff --git a/README.md b/README.md index 0fc9ed7..03c6caa 100644 --- a/README.md +++ b/README.md @@ -168,12 +168,12 @@ vkGetPhysicalDeviceMemoryProperties | ✅ Implemented vkGetPhysicalDeviceProperties | ✅ Implemented vkGetPhysicalDeviceQueueFamilyProperties | ✅ Implemented vkGetPhysicalDeviceSparseImageFormatProperties | ⚙️ WIP -vkGetPhysicalDeviceSurfaceCapabilitiesKHR | ⚙️ WIP -vkGetPhysicalDeviceSurfaceFormatsKHR | ⚙️ WIP -vkGetPhysicalDeviceSurfacePresentModesKHR | ⚙️ WIP -vkGetPhysicalDeviceSurfaceSupportKHR | ⚙️ WIP +vkGetPhysicalDeviceSurfaceCapabilitiesKHR | ✅ Implemented +vkGetPhysicalDeviceSurfaceFormatsKHR | ✅ Implemented +vkGetPhysicalDeviceSurfacePresentModesKHR | ✅ Implemented +vkGetPhysicalDeviceSurfaceSupportKHR | ✅ Implemented vkGetPhysicalDeviceWaylandPresentationSupportKHR | ⚙️ WIP -vkGetPhysicalDeviceWin32PresentationSupportKHR | ⚙️ WIP +vkGetPhysicalDeviceWin32PresentationSupportKHR | ⚙️ WIP vkGetPhysicalDeviceXcbPresentationSupportKHR | ⚙️ WIP vkGetPhysicalDeviceXlibPresentationSupportKHR | ⚙️ WIP vkGetPipelineCacheData | ⚙️ WIP @@ -184,7 +184,7 @@ vkInvalidateMappedMemoryRanges | ✅ Implemented vkMapMemory | ✅ Implemented vkMergePipelineCaches | ⚙️ WIP vkQueueBindSparse | ⚙️ WIP -vkQueuePresentKHR | ⚙️ WIP +vkQueuePresentKHR | ✅ Implemented vkQueueSubmit | ✅ Implemented vkQueueWaitIdle | ✅ Implemented vkResetCommandBuffer | ✅ Implemented diff --git a/src/soft/SoftImage.zig b/src/soft/SoftImage.zig index dddea53..c5e3cb5 100644 --- a/src/soft/SoftImage.zig +++ b/src/soft/SoftImage.zig @@ -29,6 +29,9 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v .getMemoryRequirements = getMemoryRequirements, .getSubresourceLayout = getSubresourceLayout, .getTotalSizeForAspect = getTotalSizeForAspect, + .getSliceMemSizeForMipLevel = getSliceMemSizeForMipLevel, + .getRowPitchMemSizeForMipLevel = getRowPitchMemSizeForMipLevel, + .copyToMemory = copyToMemory, }; self.* = .{ @@ -106,10 +109,10 @@ pub fn copyToImageSingleAspect(self: *const Self, dst: *Self, region: vk.ImageCo const one_is_3D = (self.interface.image_type == .@"3d") != (dst.interface.image_type == .@"3d"); const both_are_3D = (self.interface.image_type == .@"3d") and (dst.interface.image_type == .@"3d"); - const src_row_pitch_bytes = self.getRowPitchMemSizeForMipLevel(region.src_subresource.aspect_mask, region.src_subresource.mip_level); - const src_depth_pitch_bytes = self.getSliceMemSizeForMipLevel(region.src_subresource.aspect_mask, region.src_subresource.mip_level); - const dst_row_pitch_bytes = dst.getRowPitchMemSizeForMipLevel(region.dst_subresource.aspect_mask, region.dst_subresource.mip_level); - const dst_depth_pitch_bytes = dst.getSliceMemSizeForMipLevel(region.dst_subresource.aspect_mask, region.dst_subresource.mip_level); + const src_row_pitch_bytes = self.interface.getRowPitchMemSizeForMipLevel(region.src_subresource.aspect_mask, region.src_subresource.mip_level); + const src_depth_pitch_bytes = self.interface.getSliceMemSizeForMipLevel(region.src_subresource.aspect_mask, region.src_subresource.mip_level); + const dst_row_pitch_bytes = dst.interface.getRowPitchMemSizeForMipLevel(region.dst_subresource.aspect_mask, region.dst_subresource.mip_level); + const dst_depth_pitch_bytes = dst.interface.getSliceMemSizeForMipLevel(region.dst_subresource.aspect_mask, region.dst_subresource.mip_level); const src_array_pitch = self.getLayerSize(region.src_subresource.aspect_mask); const dst_array_pitch = dst.getLayerSize(region.dst_subresource.aspect_mask); @@ -238,6 +241,11 @@ pub fn copyFromBuffer(self: *const Self, src: *const SoftBuffer, region: vk.Buff ); } +pub fn copyToMemory(interface: *const Interface, memory: []u8, subresource: vk.ImageSubresourceLayers) VkError!void { + const self: *const Self = @alignCast(@fieldParentPtr("interface", interface)); + try self.copy(null, memory, subresource, .{ .x = 0, .y = 0, .z = 0 }, interface.extent); +} + /// Based on SwiftShader vk::Image::copy pub fn copy( self: *const Self, @@ -284,10 +292,10 @@ pub fn copy( var src_memory = if (is_source) base_src_memory orelse return VkError.InvalidDeviceMemoryDrv else image_map; var dst_memory = if (is_source) image_map else base_dst_memory orelse return VkError.InvalidDeviceMemoryDrv; - const src_slice_pitch_bytes = if (is_source) memory_slice_pitch_bytes else self.getSliceMemSizeForMipLevel(image_subresource.aspect_mask, image_subresource.mip_level); - const dst_slice_pitch_bytes = if (is_source) self.getSliceMemSizeForMipLevel(image_subresource.aspect_mask, image_subresource.mip_level) else memory_slice_pitch_bytes; - const src_row_pitch_bytes = if (is_source) memory_row_pitch_bytes else self.getRowPitchMemSizeForMipLevel(image_subresource.aspect_mask, image_subresource.mip_level); - const dst_row_pitch_bytes = if (is_source) self.getRowPitchMemSizeForMipLevel(image_subresource.aspect_mask, image_subresource.mip_level) else memory_row_pitch_bytes; + const src_slice_pitch_bytes = if (is_source) memory_slice_pitch_bytes else self.interface.getSliceMemSizeForMipLevel(image_subresource.aspect_mask, image_subresource.mip_level); + const dst_slice_pitch_bytes = if (is_source) self.interface.getSliceMemSizeForMipLevel(image_subresource.aspect_mask, image_subresource.mip_level) else memory_slice_pitch_bytes; + const src_row_pitch_bytes = if (is_source) memory_row_pitch_bytes else self.interface.getRowPitchMemSizeForMipLevel(image_subresource.aspect_mask, image_subresource.mip_level); + const dst_row_pitch_bytes = if (is_source) self.interface.getRowPitchMemSizeForMipLevel(image_subresource.aspect_mask, image_subresource.mip_level) else memory_row_pitch_bytes; const src_layer_size = if (is_source) memory_slice_pitch_bytes else self.getLayerSize(image_subresource.aspect_mask); const dst_layer_size = if (is_source) self.getLayerSize(image_subresource.aspect_mask) else memory_slice_pitch_bytes; @@ -352,8 +360,8 @@ pub fn writeInt4(self: *Self, offset: vk.Offset3D, subresource: vk.ImageSubresou } pub fn getTexelMemoryOffsetInSubresource(self: *const Self, offset: vk.Offset3D, subresource: vk.ImageSubresource) usize { - return @as(usize, @intCast(offset.z)) * self.getSliceMemSizeForMipLevel(subresource.aspect_mask, subresource.mip_level) + - @as(usize, @intCast(offset.y)) * self.getRowPitchMemSizeForMipLevel(subresource.aspect_mask, subresource.mip_level) + + return @as(usize, @intCast(offset.z)) * self.interface.getSliceMemSizeForMipLevel(subresource.aspect_mask, subresource.mip_level) + + @as(usize, @intCast(offset.y)) * self.interface.getRowPitchMemSizeForMipLevel(subresource.aspect_mask, subresource.mip_level) + @as(usize, @intCast(offset.x)) * base.format.texelSize(base.format.fromAspect(self.interface.format, subresource.aspect_mask)); } @@ -369,7 +377,7 @@ fn getSubresourceOffset(self: *const Self, aspect_mask: vk.ImageAspectFlags, mip const is_3D = (self.interface.image_type == .@"3d") and self.interface.flags.@"2d_array_compatible_bit"; const layer_offset = if (is_3D) - self.getSliceMemSizeForMipLevel(aspect_mask, mip_level) + self.interface.getSliceMemSizeForMipLevel(aspect_mask, mip_level) else self.getLayerSize(aspect_mask); return offset + layer * layer_offset; @@ -427,8 +435,8 @@ fn getSubresourceLayout(interface: *const Interface, subresource: vk.ImageSubres return .{ .offset = try self.getSubresourceOffset(subresource.aspect_mask, subresource.mip_level, subresource.array_layer), .size = self.getMultiSampledLevelSize(subresource.aspect_mask, subresource.mip_level), - .row_pitch = self.getRowPitchMemSizeForMipLevel(subresource.aspect_mask, subresource.mip_level), - .array_pitch = self.getSliceMemSizeForMipLevel(subresource.aspect_mask, subresource.mip_level), + .row_pitch = self.interface.getRowPitchMemSizeForMipLevel(subresource.aspect_mask, subresource.mip_level), + .array_pitch = self.interface.getSliceMemSizeForMipLevel(subresource.aspect_mask, subresource.mip_level), .depth_pitch = self.getLayerSize(subresource.aspect_mask), }; } @@ -446,7 +454,7 @@ pub inline fn getMultiSampledLevelSize(self: *const Self, aspect_mask: vk.ImageA } pub inline fn getMipLevelSize(self: *const Self, aspect_mask: vk.ImageAspectFlags, mip_level: u32) usize { - return self.getSliceMemSizeForMipLevel(aspect_mask, mip_level) * self.getMipLevelExtent(mip_level).depth; + return self.interface.getSliceMemSizeForMipLevel(aspect_mask, mip_level) * self.getMipLevelExtent(mip_level).depth; } pub fn getMipLevelExtent(self: *const Self, mip_level: u32) vk.Extent3D { @@ -463,13 +471,17 @@ pub fn getMipLevelExtent(self: *const Self, mip_level: u32) vk.Extent3D { return extent; } -pub fn getSliceMemSizeForMipLevel(self: *const Self, aspect_mask: vk.ImageAspectFlags, mip_level: u32) usize { +pub fn getSliceMemSizeForMipLevel(interface: *const Interface, aspect_mask: vk.ImageAspectFlags, mip_level: u32) usize { + const self: *const Self = @alignCast(@fieldParentPtr("interface", interface)); + const mip_extent = self.getMipLevelExtent(mip_level); const format = self.interface.formatFromAspect(aspect_mask); return base.format.sliceMemSize(format, mip_extent.width, mip_extent.height); } -pub fn getRowPitchMemSizeForMipLevel(self: *const Self, aspect_mask: vk.ImageAspectFlags, mip_level: u32) usize { +pub fn getRowPitchMemSizeForMipLevel(interface: *const Interface, aspect_mask: vk.ImageAspectFlags, mip_level: u32) usize { + const self: *const Self = @alignCast(@fieldParentPtr("interface", interface)); + const mip_extent = self.getMipLevelExtent(mip_level); const format = self.interface.formatFromAspect(aspect_mask); return base.format.pitchMemSize(format, mip_extent.width); diff --git a/src/soft/SoftInstance.zig b/src/soft/SoftInstance.zig index a2fde30..936d881 100644 --- a/src/soft/SoftInstance.zig +++ b/src/soft/SoftInstance.zig @@ -17,7 +17,7 @@ allocator: std.mem.Allocator, fn castExtension(comptime ext: vk.ApiInfo) vk.ExtensionProperties { var props: vk.ExtensionProperties = .{ - .extension_name = undefined, + .extension_name = @splat(0), .spec_version = @bitCast(ext.version), }; @memcpy(props.extension_name[0..ext.name.len], ext.name); @@ -25,7 +25,11 @@ fn castExtension(comptime ext: vk.ApiInfo) vk.ExtensionProperties { } pub const EXTENSIONS = [_]vk.ExtensionProperties{ + //castExtension(vk.extensions.lunarg_direct_driver_loading), castExtension(vk.extensions.khr_get_physical_device_properties_2), + castExtension(vk.extensions.khr_surface), + castExtension(vk.extensions.khr_wayland_surface), + castExtension(vk.extensions.khr_swapchain), }; pub fn create(allocator: std.mem.Allocator, infos: *const vk.InstanceCreateInfo) VkError!*Interface { diff --git a/src/soft/SoftPhysicalDevice.zig b/src/soft/SoftPhysicalDevice.zig index 6d296ba..933b4fb 100644 --- a/src/soft/SoftPhysicalDevice.zig +++ b/src/soft/SoftPhysicalDevice.zig @@ -8,6 +8,7 @@ const SoftDevice = @import("SoftDevice.zig"); const VkError = base.VkError; const VulkanAllocator = base.VulkanAllocator; +const SurfaceKHR = base.SurfaceKHR; const Self = @This(); pub const Interface = base.PhysicalDevice; @@ -30,8 +31,13 @@ pub fn create(allocator: std.mem.Allocator, instance: *base.Instance) VkError!*S .getFormatProperties = getFormatProperties, .getImageFormatProperties = getImageFormatProperties, .getSparseImageFormatProperties = getSparseImageFormatProperties, - .getSparseImageFormatProperties2 = getSparseImageFormatProperties2, .release = destroy, + + // VK_KHR_get_physical_device_properties_2 + .getSparseImageFormatProperties2 = getSparseImageFormatProperties2, + + // VK_KHR_surface + .getSurfaceSupportKHR = getSurfaceSupportKHR, }; interface.props.api_version = @bitCast(lib.VULKAN_VERSION); @@ -813,3 +819,7 @@ fn checkFormatUsage(usage: vk.ImageUsageFlags, features: vk.FormatFeatureFlags) return false; return true; } + +pub fn getSurfaceSupportKHR(_: *Interface, _: u32, _: *SurfaceKHR) VkError!bool { + return true; +} diff --git a/src/soft/device/blitter.zig b/src/soft/device/blitter.zig index 8370b4c..4e96865 100644 --- a/src/soft/device/blitter.zig +++ b/src/soft/device/blitter.zig @@ -98,8 +98,8 @@ pub fn clear(pixel: vk.ClearValue, format: vk.Format, dst: *SoftImage, view_form .array_layer = range.base_array_layer, }; - const dst_slice_pitch_bytes = dst.getSliceMemSizeForMipLevel(subresource.aspect_mask, subresource.mip_level); - const dst_row_pitch_bytes = dst.getRowPitchMemSizeForMipLevel(subresource.aspect_mask, subresource.mip_level); + const dst_slice_pitch_bytes = dst.interface.getSliceMemSizeForMipLevel(subresource.aspect_mask, subresource.mip_level); + const dst_row_pitch_bytes = dst.interface.getRowPitchMemSizeForMipLevel(subresource.aspect_mask, subresource.mip_level); const last_mip_level = dst.interface.getLastMipLevel(range); const last_layer = dst.interface.getLastLayerIndex(range); @@ -207,8 +207,8 @@ fn fastClear(clear_value: vk.ClearValue, clear_format: vk.Format, dst: *SoftImag const dst_memory = if (dst.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv; while (subresource.mip_level <= last_mip_level) : (subresource.mip_level += 1) { - const dst_slice_pitch_bytes = dst.getSliceMemSizeForMipLevel(subresource.aspect_mask, subresource.mip_level); - const dst_row_pitch_bytes = dst.getRowPitchMemSizeForMipLevel(subresource.aspect_mask, subresource.mip_level); + const dst_slice_pitch_bytes = dst.interface.getSliceMemSizeForMipLevel(subresource.aspect_mask, subresource.mip_level); + const dst_row_pitch_bytes = dst.interface.getRowPitchMemSizeForMipLevel(subresource.aspect_mask, subresource.mip_level); const extent = dst.getMipLevelExtent(subresource.mip_level); if (render_area == null) { @@ -375,10 +375,10 @@ pub fn blitRegion(src: *const SoftImage, dst: *SoftImage, region: vk.ImageBlit, const y0 = @as(f32, @floatFromInt(src_offset_0.y)) + (0.5 - @as(f32, @floatFromInt(dst_offset_0.y))) * height_ratio; const z0 = @as(f32, @floatFromInt(src_offset_0.z)) + (0.5 - @as(f32, @floatFromInt(dst_offset_0.z))) * depth_ratio; - const src_slice_pitch_bytes = src.getSliceMemSizeForMipLevel(region.src_subresource.aspect_mask, region.src_subresource.mip_level); - const src_row_pitch_bytes = src.getRowPitchMemSizeForMipLevel(region.src_subresource.aspect_mask, region.src_subresource.mip_level); - const dst_slice_pitch_bytes = dst.getSliceMemSizeForMipLevel(region.dst_subresource.aspect_mask, region.dst_subresource.mip_level); - const dst_row_pitch_bytes = dst.getRowPitchMemSizeForMipLevel(region.dst_subresource.aspect_mask, region.dst_subresource.mip_level); + const src_slice_pitch_bytes = src.interface.getSliceMemSizeForMipLevel(region.src_subresource.aspect_mask, region.src_subresource.mip_level); + const src_row_pitch_bytes = src.interface.getRowPitchMemSizeForMipLevel(region.src_subresource.aspect_mask, region.src_subresource.mip_level); + const dst_slice_pitch_bytes = dst.interface.getSliceMemSizeForMipLevel(region.dst_subresource.aspect_mask, region.dst_subresource.mip_level); + const dst_row_pitch_bytes = dst.interface.getRowPitchMemSizeForMipLevel(region.dst_subresource.aspect_mask, region.dst_subresource.mip_level); const src_format = base.format.fromAspect(src.interface.format, region.src_subresource.aspect_mask); const dst_format = base.format.fromAspect(dst.interface.format, region.dst_subresource.aspect_mask); diff --git a/src/vulkan/Device.zig b/src/vulkan/Device.zig index a191dc4..7dc86cc 100644 --- a/src/vulkan/Device.zig +++ b/src/vulkan/Device.zig @@ -80,7 +80,7 @@ pub fn init(allocator: std.mem.Allocator, instance: *Instance, physical_device: .instance = instance, .physical_device = physical_device, .queues = .empty, - .host_allocator = VulkanAllocator.from(allocator).clone(), + .host_allocator = VulkanAllocator.from(allocator).cloneWithScope(.object), .dispatch_table = undefined, .vtable = undefined, }; diff --git a/src/vulkan/Dispatchable.zig b/src/vulkan/Dispatchable.zig index d110f6c..e3d70a5 100644 --- a/src/vulkan/Dispatchable.zig +++ b/src/vulkan/Dispatchable.zig @@ -23,13 +23,13 @@ pub fn Dispatchable(comptime T: type) type { return self; } - pub inline fn intrusiveDestroy(self: *Self, allocator: std.mem.Allocator) void { + pub fn intrusiveDestroy(self: *Self, allocator: std.mem.Allocator) void { self.object.destroy(allocator); allocator.destroy(self); std.log.debug("Destroyed dispatchable handle of type '{s}' at 0x{X}", .{ @typeName(T), @intFromPtr(self) }); } - pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void { + pub fn destroy(self: *Self, allocator: std.mem.Allocator) void { allocator.destroy(self); std.log.debug("Destroyed dispatchable handle of type '{s}' at 0x{X}", .{ @typeName(T), @intFromPtr(self) }); } @@ -42,7 +42,7 @@ pub fn Dispatchable(comptime T: type) type { return @enumFromInt(@intFromPtr(self)); } - pub inline fn fromHandle(vk_handle: anytype) VkError!*Self { + pub fn fromHandle(vk_handle: anytype) VkError!*Self { const handle = @intFromEnum(vk_handle); if (handle == 0) { return VkError.InvalidHandleDrv; diff --git a/src/vulkan/Image.zig b/src/vulkan/Image.zig index dd3cf29..753cd9e 100644 --- a/src/vulkan/Image.zig +++ b/src/vulkan/Image.zig @@ -32,6 +32,9 @@ pub const VTable = struct { getMemoryRequirements: *const fn (*Self, *vk.MemoryRequirements) VkError!void, getSubresourceLayout: *const fn (*const Self, vk.ImageSubresource) VkError!vk.SubresourceLayout, getTotalSizeForAspect: *const fn (*const Self, vk.ImageAspectFlags) VkError!usize, + getSliceMemSizeForMipLevel: *const fn (*const Self, vk.ImageAspectFlags, u32) usize, + getRowPitchMemSizeForMipLevel: *const fn (*const Self, vk.ImageAspectFlags, u32) usize, + copyToMemory: *const fn (*const Self, []u8, vk.ImageSubresourceLayers) VkError!void, }; pub fn init(device: *Device, allocator: std.mem.Allocator, info: *const vk.ImageCreateInfo) VkError!Self { @@ -58,7 +61,7 @@ pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void { self.vtable.destroy(self, allocator); } -pub inline fn bindMemory(self: *Self, memory: *DeviceMemory, offset: vk.DeviceSize) VkError!void { +pub fn bindMemory(self: *Self, memory: *DeviceMemory, offset: vk.DeviceSize) VkError!void { const image_size = try self.getTotalSize(); if (offset >= image_size or !self.allowed_memory_types.isSet(memory.memory_type_index)) { return VkError.ValidationFailed; @@ -67,12 +70,24 @@ pub inline fn bindMemory(self: *Self, memory: *DeviceMemory, offset: vk.DeviceSi self.memory_offset = offset; } -pub inline fn getMemoryRequirements(self: *Self, requirements: *vk.MemoryRequirements) VkError!void { +pub fn getMemoryRequirements(self: *Self, requirements: *vk.MemoryRequirements) VkError!void { requirements.size = try self.getTotalSize(); requirements.memory_type_bits = self.allowed_memory_types.mask; try self.vtable.getMemoryRequirements(self, requirements); } +pub fn getSliceMemSizeForMipLevel(self: *const Self, aspect_mask: vk.ImageAspectFlags, mip_level: u32) usize { + return self.vtable.getSliceMemSizeForMipLevel(self, aspect_mask, mip_level); +} + +pub fn getRowPitchMemSizeForMipLevel(self: *const Self, aspect_mask: vk.ImageAspectFlags, mip_level: u32) usize { + return self.vtable.getRowPitchMemSizeForMipLevel(self, aspect_mask, mip_level); +} + +pub inline fn copyToMemory(self: *const Self, memory: []u8, subresource: vk.ImageSubresourceLayers) VkError!void { + try self.vtable.copyToMemory(self, memory, subresource); +} + pub inline fn getTexelSize(self: *const Self) usize { return lib.format.texelSize(self.format); } diff --git a/src/vulkan/Instance.zig b/src/vulkan/Instance.zig index c32bb9f..c900cdd 100644 --- a/src/vulkan/Instance.zig +++ b/src/vulkan/Instance.zig @@ -66,11 +66,12 @@ pub fn enumerateExtensionProperties(layer_name: ?[]const u8, count: *u32, p_prop if (layer_name) |_| { return VkError.LayerNotPresent; } - if (@hasDecl(root, "EXTENSIONS")) { + + if (comptime @hasDecl(root.Instance, "EXTENSIONS")) { count.* = root.Instance.EXTENSIONS.len; if (p_properties) |properties| { - for (root.Instance.EXTENSIONS, 0..) |ext, i| { - properties[i] = ext; + for (root.Instance.EXTENSIONS, properties[0..]) |ext, *prop| { + prop.* = ext; } } } else { diff --git a/src/vulkan/NonDispatchable.zig b/src/vulkan/NonDispatchable.zig index a1d16bd..63a2851 100644 --- a/src/vulkan/NonDispatchable.zig +++ b/src/vulkan/NonDispatchable.zig @@ -20,13 +20,13 @@ pub fn NonDispatchable(comptime T: type) type { return self; } - pub inline fn intrusiveDestroy(self: *Self, allocator: std.mem.Allocator) void { + pub fn intrusiveDestroy(self: *Self, allocator: std.mem.Allocator) void { self.object.destroy(allocator); allocator.destroy(self); std.log.debug("Destroyed non dispatchable handle of type '{s}' at 0x{X}", .{ @typeName(T), @intFromPtr(self) }); } - pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void { + pub fn destroy(self: *Self, allocator: std.mem.Allocator) void { allocator.destroy(self); std.log.debug("Destroyed non dispatchable handle of type '{s}' at 0x{X}", .{ @typeName(T), @intFromPtr(self) }); } @@ -39,7 +39,11 @@ pub fn NonDispatchable(comptime T: type) type { return @enumFromInt(@intFromPtr(self)); } - pub inline fn fromHandle(vk_handle: anytype) VkError!*Self { + pub fn fromObject(object: *T) *Self { + return @alignCast(@constCast(@fieldParentPtr("object", &object))); + } + + pub fn fromHandle(vk_handle: anytype) VkError!*Self { const handle = @intFromEnum(vk_handle); if (handle == 0) { return VkError.InvalidHandleDrv; diff --git a/src/vulkan/PhysicalDevice.zig b/src/vulkan/PhysicalDevice.zig index 7c3a90b..9b4544e 100644 --- a/src/vulkan/PhysicalDevice.zig +++ b/src/vulkan/PhysicalDevice.zig @@ -5,6 +5,7 @@ const root = @import("lib.zig"); const Instance = @import("Instance.zig"); const VkError = @import("error_set.zig").VkError; const Device = @import("Device.zig"); +const SurfaceKHR = @import("wsi/SurfaceKHR.zig"); const Self = @This(); pub const ObjectType: vk.ObjectType = .physical_device; @@ -21,8 +22,13 @@ pub const DispatchTable = struct { getFormatProperties: *const fn (*Self, vk.Format) VkError!vk.FormatProperties, getImageFormatProperties: *const fn (*Self, vk.Format, vk.ImageType, vk.ImageTiling, vk.ImageUsageFlags, vk.ImageCreateFlags) VkError!vk.ImageFormatProperties, getSparseImageFormatProperties: *const fn (*Self, vk.Format, vk.ImageType, vk.SampleCountFlags, vk.ImageTiling, vk.ImageUsageFlags, ?[*]vk.SparseImageFormatProperties) VkError!u32, - getSparseImageFormatProperties2: ?*const fn (*Self, vk.Format, vk.ImageType, vk.SampleCountFlags, vk.ImageTiling, vk.ImageUsageFlags, ?[*]vk.SparseImageFormatProperties2) VkError!u32, release: *const fn (*Self, std.mem.Allocator) VkError!void, + + // VK_KHR_get_physical_device_properties_2 + getSparseImageFormatProperties2: ?*const fn (*Self, vk.Format, vk.ImageType, vk.SampleCountFlags, vk.ImageTiling, vk.ImageUsageFlags, ?[*]vk.SparseImageFormatProperties2) VkError!u32, + + // VK_KHR_surface + getSurfaceSupportKHR: ?*const fn (*Self, u32, *SurfaceKHR) VkError!bool, }; pub fn init(allocator: std.mem.Allocator, instance: *Instance) VkError!Self { @@ -52,15 +58,15 @@ pub fn init(allocator: std.mem.Allocator, instance: *Instance) VkError!Self { }; } -pub fn createDevice(self: *Self, allocator: std.mem.Allocator, infos: *const vk.DeviceCreateInfo) VkError!*Device { +pub inline fn createDevice(self: *Self, allocator: std.mem.Allocator, infos: *const vk.DeviceCreateInfo) VkError!*Device { return try self.dispatch_table.createDevice(self, allocator, infos); } -pub fn getFormatProperties(self: *Self, format: vk.Format) VkError!vk.FormatProperties { +pub inline fn getFormatProperties(self: *Self, format: vk.Format) VkError!vk.FormatProperties { return try self.dispatch_table.getFormatProperties(self, format); } -pub fn getImageFormatProperties( +pub inline fn getImageFormatProperties( self: *Self, format: vk.Format, image_type: vk.ImageType, @@ -71,7 +77,7 @@ pub fn getImageFormatProperties( return self.dispatch_table.getImageFormatProperties(self, format, image_type, tiling, usage, flags); } -pub fn getSparseImageFormatProperties( +pub inline fn getSparseImageFormatProperties( self: *Self, format: vk.Format, image_type: vk.ImageType, @@ -83,6 +89,12 @@ pub fn getSparseImageFormatProperties( return self.dispatch_table.getSparseImageFormatProperties(self, format, image_type, samples, tiling, usage, properties); } +pub fn releasePhysicalDevice(self: *Self, allocator: std.mem.Allocator) VkError!void { + self.queue_family_props.deinit(allocator); + self.queue_family_props = .empty; + try self.dispatch_table.release(self, allocator); +} + pub fn getSparseImageFormatProperties2( self: *Self, format: vk.Format, @@ -98,8 +110,33 @@ pub fn getSparseImageFormatProperties2( 0; } -pub fn releasePhysicalDevice(self: *Self, allocator: std.mem.Allocator) VkError!void { - self.queue_family_props.deinit(allocator); - self.queue_family_props = .empty; - try self.dispatch_table.release(self, allocator); +pub fn getSurfaceCapabilitiesKHR(_: *Self, surface: *SurfaceKHR, capabilities: *vk.SurfaceCapabilitiesKHR) VkError!void { + capabilities.* = try surface.getCapabilities(); +} + +pub fn getSurfaceFormatsKHR(_: *Self, _: *SurfaceKHR, count: *u32, p_formats: ?[*]vk.SurfaceFormatKHR) VkError!void { + const surface_formats = SurfaceKHR.getFormats(); + count.* = surface_formats.len; + if (p_formats) |formats| { + for (formats[0..], surface_formats[0..]) |*format, surface_format| { + format.* = surface_format; + } + } +} + +pub fn getSurfacePresentModesKHR(_: *Self, _: *SurfaceKHR, count: *u32, p_modes: ?[*]vk.PresentModeKHR) VkError!void { + const surface_modes = SurfaceKHR.getPresentModes(); + count.* = surface_modes.len; + if (p_modes) |modes| { + for (modes[0..], surface_modes[0..]) |*mode, surface_mode| { + mode.* = surface_mode; + } + } +} + +pub fn getSurfaceSupportKHR(self: *Self, queue_family_index: u32, surface: *SurfaceKHR) VkError!bool { + return if (self.dispatch_table.getSurfaceSupportKHR) |pfn| + pfn(self, queue_family_index, surface) + else + false; } diff --git a/src/vulkan/Queue.zig b/src/vulkan/Queue.zig index 176a3a6..0da0517 100644 --- a/src/vulkan/Queue.zig +++ b/src/vulkan/Queue.zig @@ -1,12 +1,18 @@ const std = @import("std"); const vk = @import("vulkan"); -const Dispatchable = @import("Dispatchable.zig").Dispatchable; -const VkError = @import("error_set.zig").VkError; -const VulkanAllocator = @import("VulkanAllocator.zig"); +const errors = @import("error_set.zig"); +const Dispatchable = @import("Dispatchable.zig").Dispatchable; +const NonDispatchable = @import("NonDispatchable.zig").NonDispatchable; +const VkError = errors.VkError; +const VulkanAllocator = @import("VulkanAllocator.zig"); +const toVkResult = errors.toVkResult; + +const BinarySemaphore = @import("BinarySemaphore.zig"); const CommandBuffer = @import("CommandBuffer.zig"); const Device = @import("Device.zig"); +const SwapchainKHR = @import("wsi/SwapchainKHR.zig"); const Fence = @import("Fence.zig"); @@ -76,7 +82,7 @@ pub inline fn bindSparse(self: *Self, info: []const vk.BindSparseInfo, fence: ?* try self.dispatch_table.bindSparse(self, info, fence); } -pub inline fn submit(self: *Self, infos: []const vk.SubmitInfo, p_fence: ?*Fence) VkError!void { +pub fn submit(self: *Self, infos: []const vk.SubmitInfo, p_fence: ?*Fence) VkError!void { if (infos.len == 0) { if (p_fence) |fence| { try fence.signal(); @@ -92,6 +98,36 @@ pub inline fn submit(self: *Self, infos: []const vk.SubmitInfo, p_fence: ?*Fence try self.dispatch_table.submit(self, submit_infos.items, p_fence); } +pub fn presentKHR(_: *Self, info: *const vk.PresentInfoKHR) VkError!void { + if (info.p_wait_semaphores) |p_wait_semaphores| { + for (p_wait_semaphores[0..], 0..info.wait_semaphore_count) |p_semaphore, _| { + const semaphore = try NonDispatchable(BinarySemaphore).fromHandleObject(p_semaphore); + // TODO: handle semaphores + _ = semaphore; + } + } + + var cmd_err: ?VkError = null; + for (info.p_swapchains[0..], info.p_image_indices[0..], 0..info.swapchain_count) |p_swapchain, image_index, i| { + const swapchain = try NonDispatchable(SwapchainKHR).fromHandleObject(p_swapchain); + swapchain.present(image_index) catch |err| { + if (info.p_results) |results| { + results[i] = toVkResult(err); + } + if (cmd_err) |cmd_err_type| + switch (cmd_err_type) { + VkError.SuboptimalKhr => cmd_err = err, + else => {}, + } + else + cmd_err = err; + }; + } + + if (cmd_err) |err| + return err; +} + pub inline fn waitIdle(self: *Self) VkError!void { try self.dispatch_table.waitIdle(self); } diff --git a/src/vulkan/lib_vulkan.zig b/src/vulkan/lib_vulkan.zig index 230f711..ee3e4ef 100644 --- a/src/vulkan/lib_vulkan.zig +++ b/src/vulkan/lib_vulkan.zig @@ -84,7 +84,9 @@ const global_pfn_map = std.StaticStringMap(vk.PfnVoidFunction).initComptime(.{ }); const instance_pfn_map = std.StaticStringMap(vk.PfnVoidFunction).initComptime(.{ + functionMapEntryPoint("vkCreateWaylandSurfaceKHR"), functionMapEntryPoint("vkDestroyInstance"), + functionMapEntryPoint("vkDestroySurfaceKHR"), functionMapEntryPoint("vkEnumeratePhysicalDevices"), functionMapEntryPoint("vkGetDeviceProcAddr"), }); @@ -106,11 +108,17 @@ const physical_device_pfn_map = std.StaticStringMap(vk.PfnVoidFunction).initComp functionMapEntryPoint("vkGetPhysicalDeviceQueueFamilyProperties2KHR"), functionMapEntryPoint("vkGetPhysicalDeviceSparseImageFormatProperties"), functionMapEntryPoint("vkGetPhysicalDeviceSparseImageFormatProperties2KHR"), + functionMapEntryPoint("vkGetPhysicalDeviceSurfaceCapabilitiesKHR"), + functionMapEntryPoint("vkGetPhysicalDeviceSurfaceFormatsKHR"), + functionMapEntryPoint("vkGetPhysicalDeviceSurfacePresentModesKHR"), + functionMapEntryPoint("vkGetPhysicalDeviceSurfaceSupportKHR"), + functionMapEntryPoint("vkGetPhysicalDeviceWaylandPresentationSupportKHR"), }); const device_pfn_map = block: { @setEvalBranchQuota(65535); break :block std.StaticStringMap(vk.PfnVoidFunction).initComptime(.{ + functionMapEntryPoint("vkAcquireNextImageKHR"), functionMapEntryPoint("vkAllocateCommandBuffers"), functionMapEntryPoint("vkAllocateDescriptorSets"), functionMapEntryPoint("vkAllocateDescriptorSets"), @@ -181,6 +189,7 @@ const device_pfn_map = block: { functionMapEntryPoint("vkCreateSampler"), functionMapEntryPoint("vkCreateSemaphore"), functionMapEntryPoint("vkCreateShaderModule"), + functionMapEntryPoint("vkCreateSwapchainKHR"), functionMapEntryPoint("vkDestroyBuffer"), functionMapEntryPoint("vkDestroyBufferView"), functionMapEntryPoint("vkDestroyCommandPool"), @@ -200,6 +209,7 @@ const device_pfn_map = block: { functionMapEntryPoint("vkDestroySampler"), functionMapEntryPoint("vkDestroySemaphore"), functionMapEntryPoint("vkDestroyShaderModule"), + functionMapEntryPoint("vkDestroySwapchainKHR"), functionMapEntryPoint("vkDeviceWaitIdle"), functionMapEntryPoint("vkEndCommandBuffer"), functionMapEntryPoint("vkFlushMappedMemoryRanges"), @@ -218,10 +228,12 @@ const device_pfn_map = block: { functionMapEntryPoint("vkGetPipelineCacheData"), functionMapEntryPoint("vkGetQueryPoolResults"), functionMapEntryPoint("vkGetRenderAreaGranularity"), + functionMapEntryPoint("vkGetSwapchainImagesKHR"), functionMapEntryPoint("vkInvalidateMappedMemoryRanges"), functionMapEntryPoint("vkMapMemory"), functionMapEntryPoint("vkMergePipelineCaches"), functionMapEntryPoint("vkQueueBindSparse"), + functionMapEntryPoint("vkQueuePresentKHR"), functionMapEntryPoint("vkQueueSubmit"), functionMapEntryPoint("vkQueueWaitIdle"), functionMapEntryPoint("vkResetCommandBuffer"), @@ -2194,6 +2206,19 @@ pub export fn strollResetCommandBuffer(p_cmd: vk.CommandBuffer, flags: vk.Comman // WSI functions =================================================================================================================================== +pub export fn strollAcquireNextImageKHR(p_device: vk.Device, p_swapchain: vk.SwapchainKHR, timeout: u64, p_semaphore: vk.Semaphore, p_fence: vk.Fence, image_index: *u32) callconv(vk.vulkan_call_conv) vk.Result { + entryPointBeginLogTrace(.vkAcquireNextImageKHR); + defer entryPointEndLogTrace(); + + Dispatchable(Device).checkHandleValidity(p_device) catch |err| return toVkResult(err); + + const swapchain = NonDispatchable(SwapchainKHR).fromHandleObject(p_swapchain) catch |err| return toVkResult(err); + const semaphore = NonDispatchable(BinarySemaphore).fromHandleObject(p_semaphore) catch |err| return toVkResult(err); + const fence = NonDispatchable(Fence).fromHandleObject(p_fence) catch |err| return toVkResult(err); + swapchain.getNextImage(timeout, semaphore, fence, image_index) catch |err| return toVkResult(err); + return .success; +} + pub export fn strollCreateSwapchainKHR(p_device: vk.Device, info: *const vk.SwapchainCreateInfoKHR, callbacks: ?*const vk.AllocationCallbacks, p_swapchain: *vk.SwapchainKHR) callconv(vk.vulkan_call_conv) vk.Result { entryPointBeginLogTrace(.vkCreateSwapchainKHR); defer entryPointEndLogTrace(); @@ -2201,13 +2226,47 @@ pub export fn strollCreateSwapchainKHR(p_device: vk.Device, info: *const vk.Swap if (info.s_type != .swapchain_create_info_khr) { return .error_validation_failed; } + + if (info.old_swapchain != .null_handle) { + const old_swapchain = NonDispatchable(SwapchainKHR).fromHandleObject(info.old_swapchain) catch |err| return toVkResult(err); + old_swapchain.detachSurface() catch |err| return toVkResult(err); + } + const allocator = VulkanAllocator.init(callbacks, .object).allocator(); const device = Dispatchable(Device).fromHandleObject(p_device) catch |err| return toVkResult(err); + const surface = NonDispatchable(SurfaceKHR).fromHandleObject(info.surface) catch |err| return toVkResult(err); const swapchain = SwapchainKHR.create(device, allocator, info) catch |err| return toVkResult(err); + swapchain.surface = surface; + surface.swapchain = swapchain; p_swapchain.* = (NonDispatchable(SwapchainKHR).wrap(allocator, swapchain) catch |err| return toVkResult(err)).toVkHandle(vk.SwapchainKHR); return .success; } +pub export fn strollCreateWaylandSurfaceKHR(p_instance: vk.Instance, info: *const vk.WaylandSurfaceCreateInfoKHR, callbacks: ?*const vk.AllocationCallbacks, p_surface: *vk.SurfaceKHR) callconv(vk.vulkan_call_conv) vk.Result { + entryPointBeginLogTrace(.vkCreateWaylandSurfaceKHR); + defer entryPointEndLogTrace(); + + if (info.s_type != .wayland_surface_create_info_khr) { + return .error_validation_failed; + } + const allocator = VulkanAllocator.init(callbacks, .object).allocator(); + const instance = Dispatchable(Instance).fromHandleObject(p_instance) catch |err| return toVkResult(err); + const surface = WaylandSurfaceKHR.create(instance, allocator, info) catch |err| return toVkResult(err); + p_surface.* = (NonDispatchable(SurfaceKHR).wrap(allocator, surface) catch |err| return toVkResult(err)).toVkHandle(vk.SurfaceKHR); + return .success; +} + +pub export fn strollDestroySurfaceKHR(p_instance: vk.Instance, p_surface: vk.SurfaceKHR, callbacks: ?*const vk.AllocationCallbacks) callconv(vk.vulkan_call_conv) void { + entryPointBeginLogTrace(.vkDestroySurfaceKHR); + defer entryPointEndLogTrace(); + + NonDispatchable(Instance).checkHandleValidity(p_instance) catch |err| return errorLogger(err); + + const allocator = VulkanAllocator.init(callbacks, .object).allocator(); + const non_dispatchable = NonDispatchable(SurfaceKHR).fromHandle(p_surface) catch |err| return errorLogger(err); + non_dispatchable.intrusiveDestroy(allocator); +} + pub export fn strollDestroySwapchainKHR(p_device: vk.Device, p_swapchain: vk.SwapchainKHR, callbacks: ?*const vk.AllocationCallbacks) callconv(vk.vulkan_call_conv) void { entryPointBeginLogTrace(.vkDestroySwapchainKHR); defer entryPointEndLogTrace(); @@ -2219,27 +2278,79 @@ pub export fn strollDestroySwapchainKHR(p_device: vk.Device, p_swapchain: vk.Swa non_dispatchable.intrusiveDestroy(allocator); } -pub export fn strollCreateWaylandSurfaceKHR(p_device: vk.Device, info: *const vk.WaylandSurfaceCreateInfoKHR, callbacks: ?*const vk.AllocationCallbacks, p_surface: *vk.SurfaceKHR) callconv(vk.vulkan_call_conv) vk.Result { - entryPointBeginLogTrace(.vkCreateWaylandSurfaceKHR); +pub export fn strollGetPhysicalDeviceSurfaceCapabilitiesKHR(p_physical_device: vk.PhysicalDevice, p_surface: vk.SurfaceKHR, capabilities: *vk.SurfaceCapabilitiesKHR) callconv(vk.vulkan_call_conv) vk.Result { + entryPointBeginLogTrace(.vkGetPhysicalDeviceSurfaceCapabilitiesKHR); defer entryPointEndLogTrace(); - if (info.s_type != .wayland_surface_create_info_khr) { - return .error_validation_failed; - } - const allocator = VulkanAllocator.init(callbacks, .object).allocator(); - const device = Dispatchable(Device).fromHandleObject(p_device) catch |err| return toVkResult(err); - const surface = WaylandSurfaceKHR.create(device, allocator, info) catch |err| return toVkResult(err); - p_surface.* = (NonDispatchable(SurfaceKHR).wrap(allocator, surface) catch |err| return toVkResult(err)).toVkHandle(vk.SurfaceKHR); + const physical_device = Dispatchable(PhysicalDevice).fromHandleObject(p_physical_device) catch |err| return toVkResult(err); + const surface = NonDispatchable(SurfaceKHR).fromHandleObject(p_surface) catch |err| return toVkResult(err); + physical_device.getSurfaceCapabilitiesKHR(surface, capabilities) catch |err| return toVkResult(err); return .success; } -pub export fn strollDestroySurfaceKHR(p_device: vk.Device, p_surface: vk.SurfaceKHR, callbacks: ?*const vk.AllocationCallbacks) callconv(vk.vulkan_call_conv) void { - entryPointBeginLogTrace(.vkDestroySurfaceKHR); +pub export fn strollGetPhysicalDeviceSurfaceFormatsKHR(p_physical_device: vk.PhysicalDevice, p_surface: vk.SurfaceKHR, count: *u32, p_formats: ?[*]vk.SurfaceFormatKHR) callconv(vk.vulkan_call_conv) vk.Result { + entryPointBeginLogTrace(.vkGetPhysicalDeviceSurfaceFormatsKHR); defer entryPointEndLogTrace(); - Dispatchable(Device).checkHandleValidity(p_device) catch |err| return errorLogger(err); - - const allocator = VulkanAllocator.init(callbacks, .object).allocator(); - const non_dispatchable = NonDispatchable(SurfaceKHR).fromHandle(p_surface) catch |err| return errorLogger(err); - non_dispatchable.intrusiveDestroy(allocator); + const physical_device = Dispatchable(PhysicalDevice).fromHandleObject(p_physical_device) catch |err| return toVkResult(err); + const surface = NonDispatchable(SurfaceKHR).fromHandleObject(p_surface) catch |err| return toVkResult(err); + physical_device.getSurfaceFormatsKHR(surface, count, p_formats) catch |err| return toVkResult(err); + return .success; +} + +pub export fn strollGetPhysicalDeviceSurfacePresentModesKHR(p_physical_device: vk.PhysicalDevice, p_surface: vk.SurfaceKHR, count: *u32, p_modes: ?[*]vk.PresentModeKHR) callconv(vk.vulkan_call_conv) vk.Result { + entryPointBeginLogTrace(.vkGetPhysicalDeviceSurfacePresentModesKHR); + defer entryPointEndLogTrace(); + + const physical_device = Dispatchable(PhysicalDevice).fromHandleObject(p_physical_device) catch |err| return toVkResult(err); + const surface = NonDispatchable(SurfaceKHR).fromHandleObject(p_surface) catch |err| return toVkResult(err); + physical_device.getSurfacePresentModesKHR(surface, count, p_modes) catch |err| return toVkResult(err); + return .success; +} + +pub export fn strollGetPhysicalDeviceSurfaceSupportKHR(p_physical_device: vk.PhysicalDevice, queue_family_index: u32, p_surface: vk.SurfaceKHR, p_supported: *vk.Bool32) callconv(vk.vulkan_call_conv) vk.Result { + entryPointBeginLogTrace(.vkGetPhysicalDeviceSurfaceSupportKHR); + defer entryPointEndLogTrace(); + + const physical_device = Dispatchable(PhysicalDevice).fromHandleObject(p_physical_device) catch |err| return toVkResult(err); + const surface = NonDispatchable(SurfaceKHR).fromHandleObject(p_surface) catch |err| return toVkResult(err); + p_supported.* = if (physical_device.getSurfaceSupportKHR(queue_family_index, surface) catch |err| return toVkResult(err)) .true else .false; + return .success; +} + +pub export fn strollGetPhysicalDeviceWaylandPresentationSupportKHR(p_physical_device: vk.PhysicalDevice, _: u32, _: *anyopaque) callconv(vk.vulkan_call_conv) vk.Bool32 { + entryPointBeginLogTrace(.vkGetPhysicalDeviceWaylandPresentationSupportKHR); + defer entryPointEndLogTrace(); + + Dispatchable(PhysicalDevice).checkHandleValidity(p_physical_device) catch |err| errorLogger(err); + return .true; +} + +pub export fn strollGetSwapchainImagesKHR(p_device: vk.Device, p_swapchain: vk.SwapchainKHR, count: *u32, p_images: ?[*]vk.Image) callconv(vk.vulkan_call_conv) vk.Result { + entryPointBeginLogTrace(.vkGetSwapchainImagesKHR); + defer entryPointEndLogTrace(); + + Dispatchable(Device).checkHandleValidity(p_device) catch |err| return toVkResult(err); + + const swapchain = NonDispatchable(SwapchainKHR).fromHandleObject(p_swapchain) catch |err| return toVkResult(err); + count.* = @intCast(swapchain.images.len); + if (p_images) |images| { + for (images[0..], swapchain.images[0..]) |*image, swapchain_image| { + image.* = NonDispatchable(Image).fromObject(swapchain_image.image).toVkHandle(vk.Image); + } + } + + return .success; +} + +pub export fn strollQueuePresentKHR(p_queue: vk.Queue, info: *const vk.PresentInfoKHR) callconv(vk.vulkan_call_conv) vk.Result { + entryPointBeginLogTrace(.vkQueuePresentKHR); + defer entryPointEndLogTrace(); + + if (info.s_type != .present_info_khr) { + return .error_validation_failed; + } + const queue = Dispatchable(Queue).fromHandleObject(p_queue) catch |err| return toVkResult(err); + queue.presentKHR(info) catch |err| return toVkResult(err); + return .success; } diff --git a/src/vulkan/wsi/SurfaceKHR.zig b/src/vulkan/wsi/SurfaceKHR.zig index b716c30..f29fb9a 100644 --- a/src/vulkan/wsi/SurfaceKHR.zig +++ b/src/vulkan/wsi/SurfaceKHR.zig @@ -4,7 +4,7 @@ const lib = @import("../lib.zig"); const VkError = @import("../error_set.zig").VkError; -const Device = @import("../Device.zig"); +const Instance = lib.Instance; const PresentImage = @import("PresentImage.zig"); const SwapchainKHR = @import("SwapchainKHR.zig"); @@ -20,7 +20,7 @@ const present_modes = [_]vk.PresentModeKHR{ .immediate_khr, }; -owner: *Device, +owner: *Instance, swapchain: ?*SwapchainKHR, vtable: *const VTable, @@ -33,10 +33,10 @@ pub const VTable = struct { presentImage: *const fn (*Self, std.mem.Allocator, *PresentImage) VkError!void, }; -pub fn init(device: *Device, allocator: std.mem.Allocator) VkError!Self { +pub fn init(instance: *Instance, allocator: std.mem.Allocator) VkError!Self { _ = allocator; return .{ - .owner = device, + .owner = instance, .swapchain = null, .vtable = undefined, }; @@ -83,10 +83,10 @@ pub inline fn presentImage(self: *Self, allocator: std.mem.Allocator, image: *Pr try self.vtable.presentImage(self, allocator, image); } -pub inline fn getFormats() []vk.SurfaceFormatKHR { - return formats; +pub inline fn getFormats() []const vk.SurfaceFormatKHR { + return &formats; } -pub inline fn getPresentModes() []vk.PresentModeKHR { - return present_modes; +pub inline fn getPresentModes() []const vk.PresentModeKHR { + return &present_modes; } diff --git a/src/vulkan/wsi/SwapchainKHR.zig b/src/vulkan/wsi/SwapchainKHR.zig index 1b4dae0..6b354e9 100644 --- a/src/vulkan/wsi/SwapchainKHR.zig +++ b/src/vulkan/wsi/SwapchainKHR.zig @@ -4,16 +4,18 @@ const lib = @import("../lib.zig"); const VkError = @import("../error_set.zig").VkError; +const BinarySemaphore = lib.BinarySemaphore; const Device = @import("../Device.zig"); -const SurfaceKHR = @import("SurfaceKHR.zig"); +const Fence = lib.Fence; +const Image = lib.Image; const PresentImage = @import("PresentImage.zig"); -const Image = @import("../Image.zig"); +const SurfaceKHR = lib.SurfaceKHR; const Self = @This(); pub const ObjectType: vk.ObjectType = .swapchain_khr; owner: *Device, -surface: *SurfaceKHR, +surface: ?*SurfaceKHR, images: []PresentImage, pub fn create(device: *Device, allocator: std.mem.Allocator, info: *const vk.SwapchainCreateInfoKHR) VkError!*Self { @@ -48,19 +50,58 @@ pub fn create(device: *Device, allocator: std.mem.Allocator, info: *const vk.Swa self.* = .{ .owner = device, - .surface = undefined, + .surface = null, .images = images, }; return self; } -pub fn getImage(self: *const Self, index: usize) VkError!*Image { - return if (index < self.images.len) self.images[index].image else VkError.Incomplete; +pub fn getNextImage(self: *const Self, timeout: u64, semaphore: *BinarySemaphore, fence: *Fence, index: *u32) VkError!void { + // TODO: handle timeout correctly + + for (self.images, 0..) |*image, i| { + if (image.state == .Available) { + image.state = .Drawing; + index.* = @intCast(i); + // TODO: signal semaphore + _ = semaphore; + try fence.signal(); + return; + } + } + + return if (timeout > 0) VkError.Timeout else VkError.NotReady; +} + +pub fn present(self: *Self, index: usize) VkError!void { + const allocator = self.owner.host_allocator.allocator(); + + const image = &self.images[index]; + if (self.surface) |surface| { + image.state = .Presenting; + try surface.presentImage(allocator, image); + } +} + +pub fn detachSurface(self: *Self) VkError!void { + const allocator = self.owner.host_allocator.allocator(); + + if (self.surface) |surface| { + surface.swapchain = null; + for (self.images) |*image| { + if (image.state == .Available) + try surface.detachImage(allocator, image); + } + } + self.surface = null; } pub fn destroy(self: *Self, allocator: std.mem.Allocator) void { - for (self.images) |*image| { - image.deinit(allocator); + if (self.surface) |surface| { + for (self.images) |*image| { + surface.detachImage(allocator, image) catch {}; + image.deinit(allocator); + } } allocator.destroy(self); } diff --git a/src/vulkan/wsi/WaylandSurfaceKHR.zig b/src/vulkan/wsi/WaylandSurfaceKHR.zig index cdb9b94..fa77822 100644 --- a/src/vulkan/wsi/WaylandSurfaceKHR.zig +++ b/src/vulkan/wsi/WaylandSurfaceKHR.zig @@ -6,14 +6,17 @@ const wayland = @import("clients/wayland.zig"); const PresentImage = @import("PresentImage.zig"); const VkError = @import("../error_set.zig").VkError; -const Device = @import("../Device.zig"); +const Instance = lib.Instance; const Self = @This(); pub const Interface = @import("SurfaceKHR.zig"); const WaylandImage = struct { buffer: *wayland.wl_buffer, - data: []u8, + data: []align(std.heap.page_size_min) u8, + width: u32, + height: u32, + stride: u32, }; fn wlRegistryHandleGlobal(data: *anyopaque, registry: *wayland.wl_registry, name: c_uint, interface: [*:0]const u8, _: c_uint) callconv(.c) void { @@ -38,13 +41,13 @@ surface: *wayland.wl_surface, shm: *wayland.wl_shm, image_map: std.AutoHashMapUnmanaged(*PresentImage, *WaylandImage), -pub fn create(device: *Device, allocator: std.mem.Allocator, info: *const vk.WaylandSurfaceCreateInfoKHR) VkError!*Interface { +pub fn create(instance: *Instance, allocator: std.mem.Allocator, info: *const vk.WaylandSurfaceCreateInfoKHR) VkError!*Interface { const self = allocator.create(Self) catch return VkError.OutOfHostMemory; errdefer allocator.destroy(self); try wayland.load(); - var interface = try Interface.init(device, allocator); + var interface = try Interface.init(instance, allocator); interface.vtable = &.{ .destroy = destroy, @@ -84,21 +87,84 @@ pub fn getCapabilities(interface: *const Interface, capabilities: *vk.SurfaceCap pub fn attachImage(interface: *Interface, allocator: std.mem.Allocator, image: *PresentImage) VkError!void { const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); - _ = self; - _ = image; - _ = allocator; + + if (self.image_map.contains(image)) + return; + + const width: u32 = image.image.extent.width; + const height: u32 = image.image.extent.height; + + const stride: u32 = @intCast(image.image.getRowPitchMemSizeForMipLevel(.{ .color_bit = true }, 0)); + const size: usize = @as(usize, stride) * @as(usize, height); + + const wl_image = allocator.create(WaylandImage) catch return VkError.OutOfHostMemory; + errdefer allocator.destroy(wl_image); + + const fd = try createShmFile(size); + defer _ = std.c.close(fd); + + const data = std.posix.mmap(null, size, .{ .READ = true, .WRITE = true }, .{ .TYPE = .SHARED }, fd, 0) catch return VkError.OutOfHostMemory; + errdefer std.posix.munmap(data); + + const pool = wayland.wl_shm_create_pool(self.shm, fd, @intCast(size)) orelse return VkError.Unknown; + defer wayland.wl_shm_pool_destroy(pool); + + const buffer = wayland.wl_shm_pool_create_buffer(pool, 0, @intCast(width), @intCast(height), @intCast(stride), wayland.WL_SHM_FORMAT_ARGB8888) orelse return VkError.Unknown; + errdefer wayland.wl_buffer_destroy(buffer); + + wl_image.* = .{ + .buffer = buffer, + .data = data, + .width = width, + .height = height, + .stride = stride, + }; + + self.image_map.put(allocator, image, wl_image) catch return VkError.OutOfHostMemory; } pub fn detachImage(interface: *Interface, allocator: std.mem.Allocator, image: *PresentImage) VkError!void { const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); - _ = self; - _ = image; - _ = allocator; + const entry = self.image_map.fetchRemove(image) orelse return; + const wl_image = entry.value; + + wayland.wl_buffer_destroy(wl_image.buffer); + std.posix.munmap(wl_image.data); + allocator.destroy(wl_image); } pub fn presentImage(interface: *Interface, allocator: std.mem.Allocator, image: *PresentImage) VkError!void { const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); - _ = self; - _ = image; _ = allocator; + + const wl_image = self.image_map.get(image) orelse return VkError.Unknown; + + try image.image.copyToMemory(wl_image.data, .{ + .aspect_mask = .{ .color_bit = true }, + .mip_level = 0, + .base_array_layer = 0, + .layer_count = 1, + }); + + wayland.wl_surface_attach(self.surface, wl_image.buffer, 0, 0); + wayland.wl_surface_damage(self.surface, 0, 0, @intCast(wl_image.width), @intCast(wl_image.height)); + wayland.wl_surface_commit(self.surface); + + // Better: bind wl_display_flush in wayland.zig and call it here. + // With the currently available bindings, roundtrip forces the commit out, + // but it is heavier than necessary. + _ = wayland.wl_display_roundtrip(self.display); + + image.state = .Available; +} + +fn createShmFile(size: usize) VkError!std.posix.fd_t { + const name = "stroll_vk_wayland_surface"; + + const fd = std.posix.memfd_create(name, std.posix.FD_CLOEXEC) catch return VkError.Unknown; + errdefer std.c.close(fd); + + _ = std.c.ftruncate(fd, @intCast(size)); + + return fd; } diff --git a/src/vulkan/wsi/clients/wayland.zig b/src/vulkan/wsi/clients/wayland.zig index d70c618..99cdeed 100644 --- a/src/vulkan/wsi/clients/wayland.zig +++ b/src/vulkan/wsi/clients/wayland.zig @@ -9,6 +9,8 @@ pub const wl_registry_listener = extern struct { global_remove: *const fn (*anyopaque, *wl_registry, c_uint) callconv(.c) void, }; +pub const WL_SHM_FORMAT_ARGB8888 = 0; + pub const wl_buffer = opaque {}; pub const wl_callback = opaque {}; pub const wl_display = vk.wl_display; @@ -42,23 +44,27 @@ pub fn load() VkError!void { if (ref_count.load(.monotonic) != 0) return; - module = std.DynLib.open("libwayland-client.so.0") catch return VkError.Unknown; + module = std.DynLib.open("libwayland-client.so.0") catch { + _ = ref_count.fetchSub(1, .monotonic); + return VkError.Unknown; + }; errdefer module.close(); + errdefer std.debug.print("test {s}\n", .{std.c.dlerror().?}); // zig fmt: off - wl_display_dispatch = module.lookup(@TypeOf(wl_display_dispatch), "wl_display_dispatch" ) orelse return VkError.Unknown; - wl_display_get_registry = module.lookup(@TypeOf(wl_display_get_registry), "wl_display_get_registry" ) orelse return VkError.Unknown; - wl_display_roundtrip = module.lookup(@TypeOf(wl_display_roundtrip), "wl_display_roundtrip" ) orelse return VkError.Unknown; - wl_display_sync = module.lookup(@TypeOf(wl_display_sync), "wl_display_sync" ) orelse return VkError.Unknown; - wl_registry_add_listener = module.lookup(@TypeOf(wl_registry_add_listener), "wl_registry_add_listener" ) orelse return VkError.Unknown; - wl_registry_bind = module.lookup(@TypeOf(wl_registry_bind), "wl_registry_bind" ) orelse return VkError.Unknown; - wl_buffer_destroy = module.lookup(@TypeOf(wl_buffer_destroy), "wl_buffer_destroy" ) orelse return VkError.Unknown; - wl_shm_create_pool = module.lookup(@TypeOf(wl_shm_create_pool), "wl_shm_create_pool" ) orelse return VkError.Unknown; - wl_shm_pool_create_buffer = module.lookup(@TypeOf(wl_shm_pool_create_buffer), "wl_shm_pool_create_buffer" ) orelse return VkError.Unknown; - wl_shm_pool_destroy = module.lookup(@TypeOf(wl_shm_pool_destroy), "wl_shm_pool_destroy" ) orelse return VkError.Unknown; - wl_surface_attach = module.lookup(@TypeOf(wl_surface_attach), "wl_surface_attach" ) orelse return VkError.Unknown; - wl_surface_damage = module.lookup(@TypeOf(wl_surface_damage), "wl_surface_damage" ) orelse return VkError.Unknown; - wl_surface_commit = module.lookup(@TypeOf(wl_surface_commit), "wl_surface_commit" ) orelse return VkError.Unknown; + wl_display_dispatch = module.lookup(@TypeOf(wl_display_dispatch), "wl_display_dispatch" ) orelse return VkError.Unknown; + wl_display_get_registry = module.lookup(@TypeOf(wl_display_get_registry), "wl_display_get_registry" ) orelse return VkError.Unknown; + wl_display_roundtrip = module.lookup(@TypeOf(wl_display_roundtrip), "wl_display_roundtrip" ) orelse return VkError.Unknown; + wl_display_sync = module.lookup(@TypeOf(wl_display_sync), "wl_display_sync" ) orelse return VkError.Unknown; + wl_registry_add_listener = module.lookup(@TypeOf(wl_registry_add_listener), "wl_registry_add_listener" ) orelse return VkError.Unknown; + wl_registry_bind = module.lookup(@TypeOf(wl_registry_bind), "wl_registry_bind" ) orelse return VkError.Unknown; + wl_buffer_destroy = module.lookup(@TypeOf(wl_buffer_destroy), "wl_buffer_destroy" ) orelse return VkError.Unknown; + wl_shm_create_pool = module.lookup(@TypeOf(wl_shm_create_pool), "wl_shm_create_pool" ) orelse return VkError.Unknown; + wl_shm_pool_create_buffer = module.lookup(@TypeOf(wl_shm_pool_create_buffer), "wl_shm_pool_create_buffer") orelse return VkError.Unknown; + wl_shm_pool_destroy = module.lookup(@TypeOf(wl_shm_pool_destroy), "wl_shm_pool_destroy" ) orelse return VkError.Unknown; + wl_surface_attach = module.lookup(@TypeOf(wl_surface_attach), "wl_surface_attach" ) orelse return VkError.Unknown; + wl_surface_damage = module.lookup(@TypeOf(wl_surface_damage), "wl_surface_damage" ) orelse return VkError.Unknown; + wl_surface_commit = module.lookup(@TypeOf(wl_surface_commit), "wl_surface_commit" ) orelse return VkError.Unknown; // zig fmt: on wl_shm_interface = module.lookup(*wl_interface, "wl_shm_interface") orelse return VkError.Unknown;