diff --git a/README.md b/README.md index 1bf5871..b698320 100644 --- a/README.md +++ b/README.md @@ -247,6 +247,205 @@ The same rites apply as for the Soft build. ⚠️ Implemented, yet perchance not fully tested nor proven conformant, but rather working in a manner most general to thee and thine.\ Assume thou that functions lacking in this array are, for now, not intended to be wrought. +Name | Status +-------------------------------------------------|-------- +vkAcquireNextImage2KHR | ⚙️ WIP +vkAcquireNextImageKHR | ⚙️ WIP +vkAllocateCommandBuffers | ✅ Implemented +vkAllocateDescriptorSets | ⚙️ WIP +vkAllocateMemory | ✅ Implemented +vkBeginCommandBuffer | ✅ Implemented +vkBindBufferMemory | ⚙️ WIP +vkBindImageMemory | ⚙️ WIP +vkCmdBeginQuery | ⚙️ WIP +vkCmdBeginRenderPass | ⚙️ WIP +vkCmdBindDescriptorSets | ⚙️ WIP +vkCmdBindIndexBuffer | ⚙️ WIP +vkCmdBindPipeline | ⚙️ WIP +vkCmdBindVertexBuffers | ⚙️ WIP +vkCmdBlitImage | ⚙️ WIP +vkCmdClearAttachments | ⚙️ WIP +vkCmdClearColorImage | ⚙️ WIP +vkCmdClearDepthStencilImage | ⚙️ WIP +vkCmdCopyBuffer | ✅ Implemented +vkCmdCopyBufferToImage | ⚙️ WIP +vkCmdCopyImage | ⚙️ WIP +vkCmdCopyImageToBuffer | ⚙️ WIP +vkCmdCopyQueryPoolResults | ⚙️ WIP +vkCmdDispatch | ⚙️ WIP +vkCmdDispatchBaseKHR | ⚙️ WIP +vkCmdDispatchIndirect | ⚙️ WIP +vkCmdDraw | ⚙️ WIP +vkCmdDrawIndexed | ⚙️ WIP +vkCmdDrawIndexedIndirect | ⚙️ WIP +vkCmdDrawIndirect | ⚙️ WIP +vkCmdEndQuery | ⚙️ WIP +vkCmdEndRenderPass | ⚙️ WIP +vkCmdExecuteCommands | ⚙️ WIP +vkCmdFillBuffer | ✅ Implemented +vkCmdNextSubpass | ⚙️ WIP +vkCmdPipelineBarrier | ⚙️ WIP +vkCmdPushConstants | ⚙️ WIP +vkCmdResetEvent | ⚙️ WIP +vkCmdResetQueryPool | ⚙️ WIP +vkCmdResolveImage | ⚙️ WIP +vkCmdSetBlendConstants | ⚙️ WIP +vkCmdSetDepthBias | ⚙️ WIP +vkCmdSetDepthBounds | ⚙️ WIP +vkCmdSetDeviceMaskKHR | ⚙️ WIP +vkCmdSetEvent | ⚙️ WIP +vkCmdSetLineWidth | ⚙️ WIP +vkCmdSetScissor | ⚙️ WIP +vkCmdSetStencilCompareMask | ⚙️ WIP +vkCmdSetStencilReference | ⚙️ WIP +vkCmdSetStencilWriteMask | ⚙️ WIP +vkCmdSetViewport | ⚙️ WIP +vkCmdUpdateBuffer | ⚙️ WIP +vkCmdWaitEvents | ⚙️ WIP +vkCmdWriteTimestamp | ⚙️ WIP +vkCreateBuffer | ⚙️ WIP +vkCreateBufferView | ⚙️ WIP +vkCreateCommandPool | ✅ Implemented +vkCreateComputePipelines | ⚙️ WIP +vkCreateDescriptorPool | ⚙️ WIP +vkCreateDescriptorSetLayout | ⚙️ WIP +vkCreateDevice | ✅ Implemented +vkCreateEvent | ⚙️ WIP +vkCreateFence | ⚙️ WIP +vkCreateFramebuffer | ⚙️ WIP +vkCreateGraphicsPipelines | ⚙️ WIP +vkCreateImage | ⚙️ WIP +vkCreateImageView | ⚙️ WIP +vkCreateInstance | ✅ Implemented +vkCreatePipelineCache | ⚙️ WIP +vkCreatePipelineLayout | ⚙️ WIP +vkCreateQueryPool | ⚙️ WIP +vkCreateRenderPass | ⚙️ WIP +vkCreateSampler | ⚙️ WIP +vkCreateSemaphore | ⚙️ WIP +vkCreateShaderModule | ⚙️ WIP +vkCreateSwapchainKHR | ⚙️ WIP +vkCreateWaylandSurfaceKHR | ⚙️ WIP +vkCreateWin32SurfaceKHR | ⚙️ WIP +vkCreateXcbSurfaceKHR | ⚙️ WIP +vkCreateXlibSurfaceKHR | ⚙️ WIP +vkDestroyBuffer | ⚙️ WIP +vkDestroyBufferView | ⚙️ WIP +vkDestroyCommandPool | ✅ Implemented +vkDestroyDescriptorPool | ⚙️ WIP +vkDestroyDescriptorSetLayout | ⚙️ WIP +vkDestroyDevice | ✅ Implemented +vkDestroyEvent | ⚙️ WIP +vkDestroyFence | ⚙️ WIP +vkDestroyFramebuffer | ⚙️ WIP +vkDestroyImage | ⚙️ WIP +vkDestroyImageView | ⚙️ WIP +vkDestroyInstance | ✅ Implemented +vkDestroyPipeline | ⚙️ WIP +vkDestroyPipelineCache | ⚙️ WIP +vkDestroyPipelineLayout | ⚙️ WIP +vkDestroyQueryPool | ⚙️ WIP +vkDestroyRenderPass | ⚙️ WIP +vkDestroySampler | ⚙️ WIP +vkDestroySemaphore | ⚙️ WIP +vkDestroyShaderModule | ⚙️ WIP +vkDestroySurfaceKHR | ⚙️ WIP +vkDestroySwapchainKHR | ⚙️ WIP +vkDeviceWaitIdle | ⚙️ WIP +vkEndCommandBuffer | ✅ Implemented +vkEnumerateDeviceExtensionProperties | ⚙️ WIP +vkEnumerateDeviceLayerProperties | ⚙️ WIP +vkEnumerateInstanceExtensionProperties | ⚙️ WIP +vkEnumerateInstanceLayerProperties | ⚙️ WIP +vkEnumeratePhysicalDeviceGroupsKHR | ⚙️ WIP +vkEnumeratePhysicalDevices | ✅ Implemented +vkFlushMappedMemoryRanges | ⚙️ WIP +vkFreeCommandBuffers | ⚙️ WIP +vkFreeDescriptorSets | ⚙️ WIP +vkFreeMemory | ✅ Implemented +vkGetBufferDeviceAddress | ⚙️ WIP +vkGetBufferDeviceAddressEXT | ⚙️ WIP +vkGetBufferDeviceAddressKHR | ⚙️ WIP +vkGetBufferMemoryRequirements | ⚙️ WIP +vkGetDeviceGroupPeerMemoryFeaturesKHR | ⚙️ WIP +vkGetDeviceGroupPresentCapabilitiesKHR | ⚙️ WIP +vkGetDeviceGroupSurfacePresentModesKHR | ⚙️ WIP +vkGetDeviceMemoryCommitment | ⚙️ WIP +vkGetDeviceProcAddr | ⚙️ WIP +vkGetDeviceQueue | ⚙️ WIP +vkGetEventStatus | ⚙️ WIP +vkGetFenceStatus | ⚙️ WIP +vkGetImageMemoryRequirements | ⚙️ WIP +vkGetImageSparseMemoryRequirements | ⚙️ WIP +vkGetImageSubresourceLayout | ⚙️ WIP +vkGetInstanceProcAddr | ⚙️ WIP +vkGetPhysicalDeviceFeatures | ✅ Implemented +vkGetPhysicalDeviceFormatProperties | ⚙️ WIP +vkGetPhysicalDeviceImageFormatProperties | ⚙️ WIP +vkGetPhysicalDeviceMemoryProperties | ✅ Implemented +vkGetPhysicalDeviceProperties | ✅ Implemented +vkGetPhysicalDeviceQueueFamilyProperties | ⚙️ WIP +vkGetPhysicalDeviceSparseImageFormatProperties | ⚙️ WIP +vkGetPhysicalDeviceSurfaceCapabilitiesKHR | ⚙️ WIP +vkGetPhysicalDeviceSurfaceFormatsKHR | ⚙️ WIP +vkGetPhysicalDeviceSurfacePresentModesKHR | ⚙️ WIP +vkGetPhysicalDeviceSurfaceSupportKHR | ⚙️ WIP +vkGetPhysicalDeviceWaylandPresentationSupportKHR | ⚙️ WIP +vkGetPhysicalDeviceWin32PresentationSupportKHR | ⚙️ WIP +vkGetPhysicalDeviceXcbPresentationSupportKHR | ⚙️ WIP +vkGetPhysicalDeviceXlibPresentationSupportKHR | ⚙️ WIP +vkGetPipelineCacheData | ⚙️ WIP +vkGetQueryPoolResults | ⚙️ WIP +vkGetRenderAreaGranularity | ⚙️ WIP +vkGetSwapchainImagesKHR | ⚙️ WIP +vkInvalidateMappedMemoryRanges | ⚙️ WIP +vkMapMemory | ⚙️ WIP +vkMergePipelineCaches | ⚙️ WIP +vkQueueBindSparse | ⚙️ WIP +vkQueuePresentKHR | ⚙️ WIP +vkQueueSubmit | ✅ Implemented +vkQueueWaitIdle | ⚙️ WIP +vkResetCommandBuffer | ⚙️ WIP +vkResetCommandPool | ✅ Implemented +vkResetDescriptorPool | ⚙️ WIP +vkResetEvent | ⚙️ WIP +vkResetFences | ⚙️ WIP +vkResetQueryPool | ⚙️ WIP +vkSetEvent | ⚙️ WIP +vkUnmapMemory | ⚙️ WIP +vkUpdateDescriptorSets | ⚙️ WIP +vkWaitForFences | ⚙️ WIP + + +## Flint [Intel(R)] + +Flint be an implementation of the Vulkan specification, fashioned for the elder Intel iGPUs of generations 9 through 11. +Whether Haswell and those that followed-even the Xe kindred-shall one day partake of its workings remaineth unwritten. + +### Build + +If thou art truly determined: +``` +zig build flint --release=[fast|safe|small] +``` + +And should thou seek additional build options with: +``` +zig build --help +``` + +The same rites apply as for the Soft and Phi build. + +#### Vulkan 1.0 specification +
+ + Here standeth the present reckoning of thy Vulkan 1.0 implementation + + +\ +⚠️ Implemented, yet perchance not fully tested nor proven conformant, but rather working in a manner most general to thee and thine.\ +Assume thou that functions lacking in this array are, for now, not intended to be wrought. + Name | Status -------------------------------------------------|-------- vkAcquireNextImage2KHR | ⚙️ WIP @@ -399,7 +598,7 @@ vkGetQueryPoolResults | ⚙️ WIP vkGetRenderAreaGranularity | ⚙️ WIP vkGetSwapchainImagesKHR | ⚙️ WIP vkInvalidateMappedMemoryRanges | ⚙️ WIP -vkMapMemory | ⚙️ WIP +vkMapMemory | ✅ Implemented vkMergePipelineCaches | ⚙️ WIP vkQueueBindSparse | ⚙️ WIP vkQueuePresentKHR | ⚙️ WIP @@ -412,7 +611,7 @@ vkResetEvent | ⚙️ WIP vkResetFences | ⚙️ WIP vkResetQueryPool | ⚙️ WIP vkSetEvent | ⚙️ WIP -vkUnmapMemory | ⚙️ WIP +vkUnmapMemory | ✅ Implemented vkUpdateDescriptorSets | ⚙️ WIP vkWaitForFences | ⚙️ WIP
diff --git a/src/intel/FlintBuffer.zig b/src/intel/FlintBuffer.zig index 07e1df8..ccdce50 100644 --- a/src/intel/FlintBuffer.zig +++ b/src/intel/FlintBuffer.zig @@ -17,6 +17,8 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v errdefer allocator.destroy(self); var interface = try Interface.init(device, allocator, info); + interface.allowed_memory_types = std.bit_set.IntegerBitSet(32).initEmpty(); + interface.allowed_memory_types.set(0); interface.vtable = &.{ .destroy = destroy, @@ -35,6 +37,14 @@ pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void { } pub fn getMemoryRequirements(interface: *Interface, requirements: *vk.MemoryRequirements) void { - _ = interface; - _ = requirements; + requirements.alignment = 16; + if (interface.usage.uniform_texel_buffer_bit or interface.usage.storage_texel_buffer_bit) { + requirements.alignment = @max(requirements.alignment, interface.owner.physical_device.props.limits.min_texel_buffer_offset_alignment); + } + if (interface.usage.storage_buffer_bit) { + requirements.alignment = @max(requirements.alignment, interface.owner.physical_device.props.limits.min_storage_buffer_offset_alignment); + } + if (interface.usage.uniform_buffer_bit) { + requirements.alignment = @max(requirements.alignment, interface.owner.physical_device.props.limits.min_uniform_buffer_offset_alignment); + } } diff --git a/src/intel/FlintCommandBuffer.zig b/src/intel/FlintCommandBuffer.zig index 91c5b4f..1e9ae87 100644 --- a/src/intel/FlintCommandBuffer.zig +++ b/src/intel/FlintCommandBuffer.zig @@ -3,11 +3,39 @@ const vk = @import("vulkan"); const base = @import("base"); const VkError = base.VkError; +const FlintDevice = @import("FlintDevice.zig"); +const FlintDeviceMemory = @import("FlintDeviceMemory.zig"); +const kmd = @import("kmd.zig"); const Self = @This(); pub const Interface = base.CommandBuffer; +const BufferRange = struct { + memory: *FlintDeviceMemory, + offset: vk.DeviceSize, + size: vk.DeviceSize, + + fn init(buffer: *base.Buffer, offset: vk.DeviceSize, size: vk.DeviceSize) VkError!BufferRange { + const base_memory = buffer.memory orelse return VkError.InvalidDeviceMemoryDrv; + const memory: *FlintDeviceMemory = @alignCast(@fieldParentPtr("interface", base_memory)); + + const bound, const bound_overflow = @addWithOverflow(offset, size); + if (bound_overflow != 0 or bound > buffer.size) return VkError.ValidationFailed; + + const memory_offset, const memory_offset_overflow = @addWithOverflow(buffer.offset, offset); + if (memory_offset_overflow != 0) return VkError.ValidationFailed; + + return .{ + .memory = memory, + .offset = memory_offset, + .size = size, + }; + } +}; + interface: Interface, +batch: std.ArrayList(u32), +relocations: std.ArrayList(kmd.Relocation), pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const vk.CommandBufferAllocateInfo) VkError!*Self { const self = allocator.create(Self) catch return VkError.OutOfHostMemory; @@ -67,18 +95,64 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v .writeTimestamp = writeTimestamp, }; - self.* = .{ .interface = interface }; + self.* = .{ + .interface = interface, + .batch = .empty, + .relocations = .empty, + }; return self; } pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void { const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); + const command_allocator = self.interface.host_allocator.allocator(); + self.batch.deinit(command_allocator); + self.relocations.deinit(command_allocator); allocator.destroy(self); } -pub fn execute(self: *Self) VkError!void { +pub fn submitGpuBatch(self: *Self) VkError!void { try self.interface.submit(); - self.interface.finish() catch {}; + defer self.interface.finish() catch {}; + + if (self.batch.items.len == 0) return; + + const device: *FlintDevice = @alignCast(@fieldParentPtr("interface", self.interface.owner)); + const allocator = self.interface.host_allocator.allocator(); + try device.kmd.submitBatch(self.interface.owner.io(), allocator, self.batch.items, self.relocations.items); +} + +fn emit(self: *Self, dword: u32) VkError!void { + self.batch.append(self.interface.host_allocator.allocator(), dword) catch return VkError.OutOfHostMemory; +} + +fn emitRelocatedAddress(self: *Self, range: BufferRange, read: bool, write: bool) VkError!void { + const address_offset = self.batch.items.len * @sizeOf(u32); + try self.emit(@intCast(range.offset)); + try self.emit(0); + self.relocations.append(self.interface.host_allocator.allocator(), .{ + .target_handle = try range.memory.allocation.handle(), + .offset = @intCast(address_offset), + .delta = @intCast(range.offset), + .read = read, + .write = write, + }) catch return VkError.OutOfHostMemory; +} + +fn copyRangeFromRegion(buffer: *base.Buffer, offset: vk.DeviceSize, size: vk.DeviceSize) VkError!BufferRange { + return BufferRange.init(buffer, offset, size); +} + +fn fillRange(buffer: *base.Buffer, offset: vk.DeviceSize, size: vk.DeviceSize) VkError!BufferRange { + const resolved_size = if (size == vk.WHOLE_SIZE) blk: { + if (offset > buffer.size) return VkError.ValidationFailed; + break :blk std.mem.alignBackward(vk.DeviceSize, buffer.size - offset, @sizeOf(u32)); + } else blk: { + if (size % @sizeOf(u32) != 0) return VkError.ValidationFailed; + break :blk size; + }; + + return BufferRange.init(buffer, offset, resolved_size); } pub fn begin(interface: *Interface, info: *const vk.CommandBufferBeginInfo) VkError!void { @@ -91,8 +165,15 @@ pub fn end(interface: *Interface) VkError!void { } pub fn reset(interface: *Interface, flags: vk.CommandBufferResetFlags) VkError!void { - _ = interface; - _ = flags; + const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); + if (flags.release_resources_bit) { + const command_allocator = self.interface.host_allocator.allocator(); + self.batch.clearAndFree(command_allocator); + self.relocations.clearAndFree(command_allocator); + } else { + self.batch.clearRetainingCapacity(); + self.relocations.clearRetainingCapacity(); + } } pub fn beginQuery(interface: *Interface, pool: *base.QueryPool, query: u32, flags: vk.QueryControlFlags) VkError!void { @@ -180,10 +261,31 @@ pub fn clearDepthStencilImage(interface: *Interface, image: *base.Image, layout: } pub fn copyBuffer(interface: *Interface, src: *base.Buffer, dst: *base.Buffer, regions: []const vk.BufferCopy) VkError!void { - _ = interface; - _ = src; - _ = dst; - _ = regions; + const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); + + for (regions) |region| { + const src_range = try copyRangeFromRegion(src, region.src_offset, region.size); + const dst_range = try copyRangeFromRegion(dst, region.dst_offset, region.size); + + var copied: vk.DeviceSize = 0; + while (copied < src_range.size) { + const chunk = @min(src_range.size - copied, kmd.max_blt_span); + const src_chunk: BufferRange = .{ .memory = src_range.memory, .offset = src_range.offset + copied, .size = chunk }; + const dst_chunk: BufferRange = .{ .memory = dst_range.memory, .offset = dst_range.offset + copied, .size = chunk }; + const width: u32 = @intCast(chunk); + + try self.emit(kmd.xy_src_copy_blt | kmd.xy_blt_write_alpha | kmd.xy_blt_write_rgb); + try self.emit(kmd.blt_depth_8 | kmd.rop_source_copy | width); + try self.emit(0); + try self.emit((1 << 16) | width); + try self.emitRelocatedAddress(dst_chunk, false, true); + try self.emit(0); + try self.emit(width); + try self.emitRelocatedAddress(src_chunk, true, false); + + copied += chunk; + } + } } pub fn copyBufferToImage(interface: *Interface, src: *base.Buffer, dst: *base.Image, dst_layout: vk.ImageLayout, regions: []const vk.BufferImageCopy) VkError!void { @@ -288,16 +390,37 @@ pub fn endRenderPass(interface: *Interface) VkError!void { } pub fn executeCommands(interface: *Interface, commands: *Interface) VkError!void { - _ = interface; - _ = commands; + const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); + const secondary: *Self = @alignCast(@fieldParentPtr("interface", commands)); + const allocator = self.interface.host_allocator.allocator(); + const relocation_offset = self.batch.items.len * @sizeOf(u32); + + self.batch.appendSlice(allocator, secondary.batch.items) catch return VkError.OutOfHostMemory; + for (secondary.relocations.items) |relocation| { + self.relocations.append(allocator, .{ + .target_handle = relocation.target_handle, + .offset = relocation.offset + relocation_offset, + .delta = relocation.delta, + .read = relocation.read, + .write = relocation.write, + }) catch return VkError.OutOfHostMemory; + } } pub fn fillBuffer(interface: *Interface, buffer: *base.Buffer, offset: vk.DeviceSize, size: vk.DeviceSize, data: u32) VkError!void { - _ = interface; - _ = buffer; - _ = offset; - _ = size; - _ = data; + const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); + const dst_range = try fillRange(buffer, offset, size); + + var filled: vk.DeviceSize = 0; + while (filled < dst_range.size) { + const dst_chunk: BufferRange = .{ .memory = dst_range.memory, .offset = dst_range.offset + filled, .size = @sizeOf(u32) }; + + try self.emit(kmd.mi_store_data_imm_dword); + try self.emitRelocatedAddress(dst_chunk, false, true); + try self.emit(data); + + filled += @sizeOf(u32); + } } pub fn updateBuffer(interface: *Interface, buffer: *base.Buffer, offset: vk.DeviceSize, data: []const u8) VkError!void { diff --git a/src/intel/FlintDevice.zig b/src/intel/FlintDevice.zig index 7988704..4d09a10 100644 --- a/src/intel/FlintDevice.zig +++ b/src/intel/FlintDevice.zig @@ -3,6 +3,8 @@ const vk = @import("vulkan"); const base = @import("base"); const FlintQueue = @import("FlintQueue.zig"); +const FlintPhysicalDevice = @import("FlintPhysicalDevice.zig"); +const Kmd = @import("kmd.zig"); pub const FlintBinarySemaphore = @import("FlintBinarySemaphore.zig"); pub const FlintBuffer = @import("FlintBuffer.zig"); @@ -32,12 +34,16 @@ const Self = @This(); pub const Interface = base.Device; interface: Interface, +kmd: Kmd.Device, pub fn create(instance: *base.Instance, physical_device: *base.PhysicalDevice, allocator: std.mem.Allocator, info: *const vk.DeviceCreateInfo) VkError!*Self { const self = allocator.create(Self) catch return VkError.OutOfHostMemory; errdefer allocator.destroy(self); var interface = try Interface.init(allocator, instance, physical_device, info); + const flint_physical_device: *const FlintPhysicalDevice = @alignCast(@fieldParentPtr("interface", physical_device)); + var kmd_device = try Kmd.Device.open(instance.io(), flint_physical_device); + errdefer kmd_device.close(instance.io()); interface.vtable = &.{ .createQueue = FlintQueue.create, @@ -73,6 +79,7 @@ pub fn create(instance: *base.Instance, physical_device: *base.PhysicalDevice, a self.* = .{ .interface = interface, + .kmd = kmd_device, }; try self.interface.createQueues(allocator, info); @@ -81,6 +88,7 @@ pub fn create(instance: *base.Instance, physical_device: *base.PhysicalDevice, a pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) VkError!void { const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); + self.kmd.close(interface.io()); allocator.destroy(self); } diff --git a/src/intel/FlintDeviceMemory.zig b/src/intel/FlintDeviceMemory.zig index edbeba9..6caebad 100644 --- a/src/intel/FlintDeviceMemory.zig +++ b/src/intel/FlintDeviceMemory.zig @@ -1,9 +1,9 @@ const std = @import("std"); const vk = @import("vulkan"); const base = @import("base"); -const lib = @import("lib.zig"); const FlintDevice = @import("FlintDevice.zig"); +const Kmd = @import("kmd.zig"); const VkError = base.VkError; @@ -11,12 +11,19 @@ const Self = @This(); pub const Interface = base.DeviceMemory; interface: Interface, +allocation: Kmd.Memory, pub fn create(device: *FlintDevice, allocator: std.mem.Allocator, size: vk.DeviceSize, memory_type_index: u32) VkError!*Self { const self = allocator.create(Self) catch return VkError.OutOfHostMemory; errdefer allocator.destroy(self); + if (memory_type_index >= device.interface.physical_device.mem_props.memory_type_count) { + return VkError.ValidationFailed; + } + var interface = try Interface.init(&device.interface, size, memory_type_index); + var allocation = try device.kmd.allocateMemory(device.interface.io(), size); + errdefer allocation.deinit(&device.kmd, device.interface.io()); interface.vtable = &.{ .destroy = destroy, @@ -28,33 +35,42 @@ pub fn create(device: *FlintDevice, allocator: std.mem.Allocator, size: vk.Devic self.* = .{ .interface = interface, + .allocation = allocation, }; return self; } pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void { const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); + const device: *FlintDevice = @alignCast(@fieldParentPtr("interface", interface.owner)); + self.allocation.deinit(&device.kmd, interface.owner.io()); allocator.destroy(self); } pub fn flushRange(interface: *Interface, offset: vk.DeviceSize, size: vk.DeviceSize) VkError!void { - _ = interface; - _ = offset; - _ = size; + const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); + const device: *FlintDevice = @alignCast(@fieldParentPtr("interface", interface.owner)); + try self.allocation.flushRange(&device.kmd, interface.owner.io(), offset, size); } pub fn invalidateRange(interface: *Interface, offset: vk.DeviceSize, size: vk.DeviceSize) VkError!void { - _ = interface; - _ = offset; - _ = size; + const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); + const device: *FlintDevice = @alignCast(@fieldParentPtr("interface", interface.owner)); + try self.allocation.invalidateRange(&device.kmd, interface.owner.io(), offset, size); } pub fn map(interface: *Interface, offset: vk.DeviceSize, size: vk.DeviceSize) VkError![]u8 { const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); - _ = self; - _ = offset; - _ = size; - return VkError.Unknown; + if (interface.is_mapped) return VkError.MemoryMapFailed; + + const device: *FlintDevice = @alignCast(@fieldParentPtr("interface", interface.owner)); + const data = try self.allocation.map(&device.kmd, interface.owner.io(), offset, size); + interface.is_mapped = true; + return data; } -pub fn unmap(_: *Interface) void {} +pub fn unmap(interface: *Interface) void { + const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); + self.allocation.unmap(); + interface.is_mapped = false; +} diff --git a/src/intel/FlintPhysicalDevice.zig b/src/intel/FlintPhysicalDevice.zig index 0e03651..57f9a83 100644 --- a/src/intel/FlintPhysicalDevice.zig +++ b/src/intel/FlintPhysicalDevice.zig @@ -25,12 +25,12 @@ fn castExtension(comptime ext: vk.ApiInfo) vk.ExtensionProperties { } pub const EXTENSIONS = [_]vk.ExtensionProperties{ - castExtension(vk.extensions.khr_device_group), castExtension(vk.extensions.khr_swapchain), }; interface: Interface, kmd_type: lib.KmdType, +node_path: [base.drm.max_node_name:0]u8, pub fn create(allocator: std.mem.Allocator, instance: *base.Instance, drm_device: *const base.drm.Device, kmd_type: lib.KmdType) VkError!*Self { const self = allocator.create(Self) catch return VkError.OutOfHostMemory; @@ -225,11 +225,18 @@ pub fn create(allocator: std.mem.Allocator, instance: *base.Instance, drm_device self.* = .{ .interface = interface, .kmd_type = kmd_type, + .node_path = @splat(0), }; + const node_path = drm_device.nodePath(); + @memcpy(self.node_path[0..node_path.len], node_path); return self; } +pub fn getNodePath(self: *const Self) [:0]const u8 { + return std.mem.sliceTo(&self.node_path, 0); +} + pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) VkError!void { const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); allocator.destroy(self); diff --git a/src/intel/FlintQueue.zig b/src/intel/FlintQueue.zig index a7667b6..798b516 100644 --- a/src/intel/FlintQueue.zig +++ b/src/intel/FlintQueue.zig @@ -47,7 +47,7 @@ pub fn submit(interface: *Interface, infos: []Interface.SubmitInfo, fence: ?*bas for (info.command_buffers.items) |command_buffer| { const intel_command_buffer: *FlintCommandBuffer = @alignCast(@fieldParentPtr("interface", command_buffer)); - _ = intel_command_buffer; + try intel_command_buffer.submitGpuBatch(); } for (info.signal_semaphores.items) |semaphore| { diff --git a/src/intel/i915/drm.zig b/src/intel/i915/drm.zig new file mode 100644 index 0000000..9026fe6 --- /dev/null +++ b/src/intel/i915/drm.zig @@ -0,0 +1,81 @@ +const vk = @import("vulkan"); + +pub const command_base = 0x40; +pub const i915_gem_create = 0x1b; +pub const i915_gem_mmap_gtt = 0x24; +pub const i915_gem_set_domain = 0x1f; +pub const i915_gem_execbuffer2 = 0x29; +pub const i915_gem_wait = 0x2c; +pub const gem_close = 0x09; + +pub const i915_mmap_offset_wb = 2; +pub const i915_gem_domain_cpu = 0x00000001; +pub const i915_gem_domain_gtt = 0x00000040; +pub const i915_exec_blt = 3 << 0; +pub const exec_object_write = 1 << 2; +pub const mi_flush_dw: u32 = (0x26 << 23) | 3; + +pub const GemCreate = extern struct { + size: u64, + handle: u32, + pad: u32, +}; + +pub const GemMmapOffset = extern struct { + handle: u32, + pad: u32, + offset: u64, + flags: u64, + extensions: u64, +}; + +pub const GemClose = extern struct { + handle: u32, + pad: u32, +}; + +pub const GemSetDomain = extern struct { + handle: u32, + read_domains: u32, + write_domain: u32, +}; + +pub const RelocationEntry = extern struct { + target_handle: u32, + delta: u32, + offset: u64, + presumed_offset: u64, + read_domains: u32, + write_domain: u32, +}; + +pub const ExecObject2 = extern struct { + handle: u32, + relocation_count: u32, + relocs_ptr: u64, + alignment: u64, + offset: u64, + flags: u64, + rsvd1: u64, + rsvd2: u64, +}; + +pub const ExecBuffer2 = extern struct { + buffers_ptr: u64, + buffer_count: u32, + batch_start_offset: u32, + batch_len: u32, + DR1: u32, + DR4: u32, + num_cliprects: u32, + cliprects_ptr: u64, + flags: u64, + rsvd1: u64, + rsvd2: u64, +}; + +pub const GemWait = extern struct { + bo_handle: u32, + flags: u32, + timeout_ns: i64, +}; diff --git a/src/intel/i915/kmd.zig b/src/intel/i915/kmd.zig index e69de29..4a20ab6 100644 --- a/src/intel/i915/kmd.zig +++ b/src/intel/i915/kmd.zig @@ -0,0 +1,240 @@ +const std = @import("std"); +const vk = @import("vulkan"); +const base = @import("base"); + +const drm = @import("drm.zig"); +const common_kmd = @import("../kmd.zig"); + +const VkError = base.VkError; + +const IOCTL = std.os.linux.IOCTL; + +const Mapping = struct { + bytes: []align(std.heap.page_size_min) u8, + + inline fn slice(self: Mapping, offset: vk.DeviceSize, size: vk.DeviceSize) VkError![]u8 { + const start: usize = @intCast(offset); + const len: usize = @intCast(size); + return self.bytes[start .. start + len]; + } +}; + +pub const Device = struct { + card: base.drm.Card, + + pub fn open(io: std.Io, node_path: []const u8) VkError!Device { + return .{ + .card = base.drm.Card.open(io, node_path) catch return VkError.InitializationFailed, + }; + } + + pub fn close(self: *Device, io: std.Io) void { + self.card.close(io); + } + + pub fn allocateMemory(self: *Device, io: std.Io, size: vk.DeviceSize) VkError!Memory { + var create = drm.GemCreate{ + .size = size, + .handle = 0, + .pad = 0, + }; + base.utils.ioctl(self.card.handle, io, drmIoctlIowr(drm.command_base + drm.i915_gem_create, drm.GemCreate), &create) catch return VkError.OutOfDeviceMemory; + + var memory = Memory{ + .handle = create.handle, + .size = create.size, + .mapping = null, + }; + errdefer memory.deinit(self, io); + + try memory.setDomain(self, io, drm.i915_gem_domain_cpu, 0); + return memory; + } + + pub fn submitBatch(self: *Device, io: std.Io, allocator: std.mem.Allocator, commands: []const u32, relocations: []const common_kmd.Relocation) VkError!void { + const trailer_words = 6; + const batch_size = (commands.len + trailer_words) * @sizeOf(u32); + var batch = try self.allocateMemory(io, batch_size); + defer batch.deinit(self, io); + + { + const batch_map = try batch.map(self, io, 0, batch_size); + const batch_words = std.mem.bytesAsSlice(u32, batch_map); + @memcpy(batch_words[0..commands.len], commands); + batch_words[commands.len + 0] = drm.mi_flush_dw; + batch_words[commands.len + 1] = 0; + batch_words[commands.len + 2] = 0; + batch_words[commands.len + 3] = 0; + batch_words[commands.len + 4] = 0; + batch_words[commands.len + 5] = 0x05000000; + batch.unmap(); + } + try batch.flushRange(self, io, 0, batch_size); + + var objects = std.ArrayList(drm.ExecObject2).empty; + defer objects.deinit(allocator); + + var object_handles = std.ArrayList(u32).empty; + defer object_handles.deinit(allocator); + + for (relocations) |relocation| { + if (std.mem.indexOfScalar(u32, object_handles.items, relocation.target_handle) == null) { + object_handles.append(allocator, relocation.target_handle) catch return VkError.OutOfHostMemory; + objects.append(allocator, .{ + .handle = relocation.target_handle, + .relocation_count = 0, + .relocs_ptr = 0, + .alignment = 0, + .offset = 0, + .flags = if (relocation.write) drm.exec_object_write else 0, + .rsvd1 = 0, + .rsvd2 = 0, + }) catch return VkError.OutOfHostMemory; + } else if (relocation.write) { + const index = std.mem.indexOfScalar(u32, object_handles.items, relocation.target_handle).?; + objects.items[index].flags |= drm.exec_object_write; + } + } + + var i915_relocations = std.ArrayList(drm.RelocationEntry).empty; + defer i915_relocations.deinit(allocator); + + for (relocations) |relocation| { + i915_relocations.append(allocator, .{ + .target_handle = relocation.target_handle, + .delta = relocation.delta, + .offset = relocation.offset, + .presumed_offset = 0, + .read_domains = 0, + .write_domain = 0, + }) catch return VkError.OutOfHostMemory; + } + + objects.append(allocator, .{ + .handle = batch.handle, + .relocation_count = @intCast(i915_relocations.items.len), + .relocs_ptr = @intFromPtr(i915_relocations.items.ptr), + .alignment = 0, + .offset = 0, + .flags = 0, + .rsvd1 = 0, + .rsvd2 = 0, + }) catch return VkError.OutOfHostMemory; + + var execbuffer = drm.ExecBuffer2{ + .buffers_ptr = @intFromPtr(objects.items.ptr), + .buffer_count = @intCast(objects.items.len), + .batch_start_offset = 0, + .batch_len = @intCast(batch_size), + .DR1 = 0, + .DR4 = 0, + .num_cliprects = 0, + .cliprects_ptr = 0, + .flags = drm.i915_exec_blt, + .rsvd1 = 0, + .rsvd2 = 0, + }; + base.utils.ioctl(self.card.handle, io, drmIoctlIowr(drm.command_base + drm.i915_gem_execbuffer2, drm.ExecBuffer2), &execbuffer) catch return VkError.DeviceLost; + + for (object_handles.items) |handle| { + const object = objects.items[std.mem.indexOfScalar(u32, object_handles.items, handle).?]; + if (object.flags & drm.exec_object_write == 0) continue; + + var wait = drm.GemWait{ + .bo_handle = handle, + .flags = 0, + .timeout_ns = -1, + }; + base.utils.ioctl(self.card.handle, io, drmIoctlIowr(drm.command_base + drm.i915_gem_wait, drm.GemWait), &wait) catch return VkError.DeviceLost; + } + } +}; + +pub const Memory = struct { + handle: u32, + size: vk.DeviceSize, + mapping: ?Mapping, + + pub fn deinit(self: *Memory, device: *Device, io: std.Io) void { + self.unmap(); + + var close = drm.GemClose{ + .handle = self.handle, + .pad = 0, + }; + base.utils.ioctl(device.card.handle, io, drmIoctlIow(drm.gem_close, drm.GemClose), &close) catch {}; + + self.* = undefined; + } + + pub fn map(self: *Memory, device: *Device, io: std.Io, offset: vk.DeviceSize, size: vk.DeviceSize) VkError![]u8 { + if (offset > self.size) return VkError.MemoryMapFailed; + const available = self.size - offset; + const map_size = if (size == vk.WHOLE_SIZE) available else size; + if (map_size > available) return VkError.MemoryMapFailed; + if (map_size > std.math.maxInt(usize)) return VkError.MemoryMapFailed; + + if (self.mapping) |mapping| { + return mapping.slice(offset, map_size); + } + + var mmap_offset = drm.GemMmapOffset{ + .handle = self.handle, + .pad = 0, + .offset = 0, + .flags = drm.i915_mmap_offset_wb, + .extensions = 0, + }; + base.utils.ioctl(device.card.handle, io, drmIoctlIowr(drm.command_base + drm.i915_gem_mmap_gtt, drm.GemMmapOffset), &mmap_offset) catch return VkError.MemoryMapFailed; + + if (self.size > std.math.maxInt(usize)) return VkError.MemoryMapFailed; + const full_size: usize = @intCast(self.size); + const bytes = std.posix.mmap( + null, + full_size, + .{ .READ = true, .WRITE = true }, + .{ .TYPE = .SHARED }, + device.card.handle.handle, + @intCast(mmap_offset.offset), + ) catch return VkError.MemoryMapFailed; + + self.mapping = .{ .bytes = bytes }; + return self.mapping.?.slice(offset, map_size); + } + + pub fn unmap(self: *Memory) void { + if (self.mapping) |mapping| { + std.posix.munmap(mapping.bytes); + self.mapping = null; + } + } + + pub fn flushRange(self: *Memory, device: *Device, io: std.Io, offset: vk.DeviceSize, size: vk.DeviceSize) VkError!void { + _ = offset; + _ = size; + try self.setDomain(device, io, drm.i915_gem_domain_cpu, 0); + } + + pub fn invalidateRange(self: *Memory, device: *Device, io: std.Io, offset: vk.DeviceSize, size: vk.DeviceSize) VkError!void { + _ = offset; + _ = size; + try self.setDomain(device, io, drm.i915_gem_domain_cpu, 0); + } + + fn setDomain(self: *Memory, device: *Device, io: std.Io, read_domains: u32, write_domain: u32) VkError!void { + var domain = drm.GemSetDomain{ + .handle = self.handle, + .read_domains = read_domains, + .write_domain = write_domain, + }; + base.utils.ioctl(device.card.handle, io, drmIoctlIow(drm.command_base + drm.i915_gem_set_domain, drm.GemSetDomain), &domain) catch return VkError.DeviceLost; + } +}; + +inline fn drmIoctlIow(nr: u8, comptime T: type) u32 { + return IOCTL.IOW('d', nr, T); +} + +inline fn drmIoctlIowr(nr: u8, comptime T: type) u32 { + return IOCTL.IOWR('d', nr, T); +} diff --git a/src/intel/kmd.zig b/src/intel/kmd.zig new file mode 100644 index 0000000..22d27aa --- /dev/null +++ b/src/intel/kmd.zig @@ -0,0 +1,144 @@ +const std = @import("std"); +const vk = @import("vulkan"); +const base = @import("base"); +const lib = @import("lib.zig"); + +const FlintPhysicalDevice = @import("FlintPhysicalDevice.zig"); +const i915_kmd = @import("i915/kmd.zig"); +const xe = @import("xe/kmd.zig"); + +const VkError = base.VkError; + +pub const xy_src_copy_blt: u32 = (2 << 29) | (0x53 << 22) | 8; +pub const xy_blt_write_alpha: u32 = 1 << 21; +pub const xy_blt_write_rgb: u32 = 1 << 20; +pub const mi_store_data_imm_dword: u32 = (0x20 << 23) | 2; +pub const blt_depth_8: u32 = 0 << 24; +pub const rop_source_copy: u32 = 0xcc << 16; +pub const max_blt_span: vk.DeviceSize = 32 * 1024 - 1; + +pub const Relocation = struct { + target_handle: u32, + offset: u64, + delta: u32, + read: bool = false, + write: bool = false, +}; + +pub const Device = union(lib.KmdType) { + Invalid: void, + I915: i915_kmd.Device, + Xe: xe.Device, + + pub fn open(io: std.Io, physical_device: *const FlintPhysicalDevice) VkError!Device { + return switch (physical_device.kmd_type) { + .I915 => .{ .I915 = try i915_kmd.Device.open(io, physical_device.getNodePath()) }, + .Xe => .{ .Xe = try xe.Device.open(io, physical_device.getNodePath()) }, + .Invalid => VkError.InitializationFailed, + }; + } + + pub fn close(self: *Device, io: std.Io) void { + switch (self.*) { + .I915 => |*device| device.close(io), + .Xe => |*device| device.close(io), + .Invalid => {}, + } + self.* = .{ .Invalid = {} }; + } + + pub fn allocateMemory(self: *Device, io: std.Io, size: vk.DeviceSize) VkError!Memory { + return switch (self.*) { + .I915 => |*device| .{ .I915 = try device.allocateMemory(io, size) }, + .Xe => |*device| .{ .Xe = try device.allocateMemory(io, size) }, + .Invalid => VkError.OutOfDeviceMemory, + }; + } + + pub fn submitBatch(self: *Device, io: std.Io, allocator: std.mem.Allocator, commands: []const u32, relocations: []const Relocation) VkError!void { + return switch (self.*) { + .I915 => |*device| device.submitBatch(io, allocator, commands, relocations), + .Xe => |*device| device.submitBatch(io, allocator, commands, relocations), + .Invalid => VkError.DeviceLost, + }; + } +}; + +pub const Memory = union(lib.KmdType) { + Invalid: void, + I915: i915_kmd.Memory, + Xe: xe.Memory, + + pub fn deinit(self: *Memory, device: *Device, io: std.Io) void { + switch (self.*) { + .I915 => |*memory| switch (device.*) { + .I915 => |*adapter| memory.deinit(adapter, io), + else => {}, + }, + .Xe => |*memory| switch (device.*) { + .Xe => |*adapter| memory.deinit(adapter, io), + else => {}, + }, + .Invalid => {}, + } + self.* = .{ .Invalid = {} }; + } + + pub fn map(self: *Memory, device: *Device, io: std.Io, offset: vk.DeviceSize, size: vk.DeviceSize) VkError![]u8 { + return switch (self.*) { + .I915 => |*memory| switch (device.*) { + .I915 => |*adapter| memory.map(adapter, io, offset, size), + else => VkError.MemoryMapFailed, + }, + .Xe => |*memory| switch (device.*) { + .Xe => |*adapter| memory.map(adapter, io, offset, size), + else => VkError.MemoryMapFailed, + }, + .Invalid => VkError.MemoryMapFailed, + }; + } + + pub fn unmap(self: *Memory) void { + switch (self.*) { + .I915 => |*memory| memory.unmap(), + .Xe => |*memory| memory.unmap(), + .Invalid => {}, + } + } + + pub fn flushRange(self: *Memory, device: *Device, io: std.Io, offset: vk.DeviceSize, size: vk.DeviceSize) VkError!void { + return switch (self.*) { + .I915 => |*memory| switch (device.*) { + .I915 => |*adapter| memory.flushRange(adapter, io, offset, size), + else => VkError.InvalidDeviceMemoryDrv, + }, + .Xe => |*memory| switch (device.*) { + .Xe => |*adapter| memory.flushRange(adapter, io, offset, size), + else => VkError.InvalidDeviceMemoryDrv, + }, + .Invalid => VkError.InvalidDeviceMemoryDrv, + }; + } + + pub fn invalidateRange(self: *Memory, device: *Device, io: std.Io, offset: vk.DeviceSize, size: vk.DeviceSize) VkError!void { + return switch (self.*) { + .I915 => |*memory| switch (device.*) { + .I915 => |*adapter| memory.invalidateRange(adapter, io, offset, size), + else => VkError.InvalidDeviceMemoryDrv, + }, + .Xe => |*memory| switch (device.*) { + .Xe => |*adapter| memory.invalidateRange(adapter, io, offset, size), + else => VkError.InvalidDeviceMemoryDrv, + }, + .Invalid => VkError.InvalidDeviceMemoryDrv, + }; + } + + pub fn handle(self: *const Memory) VkError!u32 { + return switch (self.*) { + .I915 => |*memory| memory.handle, + .Xe => VkError.FeatureNotPresent, + .Invalid => VkError.InvalidDeviceMemoryDrv, + }; + } +}; diff --git a/src/intel/lib.zig b/src/intel/lib.zig index 068d2ae..043e9f3 100644 --- a/src/intel/lib.zig +++ b/src/intel/lib.zig @@ -9,6 +9,7 @@ pub const FlintInstance = @import("FlintInstance.zig"); pub const FlintDevice = @import("FlintDevice.zig"); pub const FlintPhysicalDevice = @import("FlintPhysicalDevice.zig"); pub const FlintQueue = @import("FlintQueue.zig"); +pub const kmd = @import("kmd.zig"); pub const FlintBinarySemaphore = @import("FlintBinarySemaphore.zig"); pub const FlintBuffer = @import("FlintBuffer.zig"); @@ -85,5 +86,6 @@ test { std.testing.refAllDecls(FlintRenderPass); std.testing.refAllDecls(FlintSampler); std.testing.refAllDecls(FlintShaderModule); + std.testing.refAllDecls(kmd); std.testing.refAllDecls(base); } diff --git a/src/intel/xe/kmd.zig b/src/intel/xe/kmd.zig index e69de29..751e259 100644 --- a/src/intel/xe/kmd.zig +++ b/src/intel/xe/kmd.zig @@ -0,0 +1,49 @@ +const std = @import("std"); +const vk = @import("vulkan"); +const base = @import("base"); + +const VkError = base.VkError; + +pub const Device = struct { + card: base.drm.Card, + + pub fn open(io: std.Io, node_path: []const u8) VkError!Device { + return .{ + .card = base.drm.Card.open(io, node_path) catch return VkError.InitializationFailed, + }; + } + + pub fn close(self: *Device, io: std.Io) void { + self.card.close(io); + } + + pub fn allocateMemory(_: *Device, _: std.Io, _: vk.DeviceSize) VkError!Memory { + return VkError.OutOfDeviceMemory; + } + + pub fn submitBatch(_: *Device, _: std.Io, _: std.mem.Allocator, _: []const u32, _: []const @import("../kmd.zig").Relocation) VkError!void { + return VkError.FeatureNotPresent; + } +}; + +pub const Memory = struct { + pub fn deinit(_: *Memory, _: *Device, _: std.Io) void {} + + pub fn map(_: *Memory, _: *Device, _: std.Io, _: vk.DeviceSize, _: vk.DeviceSize) VkError![]u8 { + return VkError.MemoryMapFailed; + } + + pub fn unmap(_: *Memory) void {} + + pub fn flushRange(_: *Memory, _: *Device, _: std.Io, _: vk.DeviceSize, _: vk.DeviceSize) VkError!void { + return VkError.FeatureNotPresent; + } + + pub fn invalidateRange(_: *Memory, _: *Device, _: std.Io, _: vk.DeviceSize, _: vk.DeviceSize) VkError!void { + return VkError.FeatureNotPresent; + } +}; + +test { + std.testing.refAllDecls(@This()); +} diff --git a/src/vulkan/Buffer.zig b/src/vulkan/Buffer.zig index d2e4d1d..70acd4a 100644 --- a/src/vulkan/Buffer.zig +++ b/src/vulkan/Buffer.zig @@ -39,21 +39,21 @@ 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 { - if (offset > memory.size or !self.allowed_memory_types.isSet(memory.memory_type_index)) { +pub fn bindMemory(self: *Self, memory: *DeviceMemory, offset: vk.DeviceSize) VkError!void { + if (offset > memory.size or self.size > memory.size - offset or !self.allowed_memory_types.isSet(memory.memory_type_index)) { return VkError.ValidationFailed; } self.memory = memory; self.offset = offset; } -pub inline fn getMemoryRequirements(self: *Self, requirements: *vk.MemoryRequirements) void { +pub fn getMemoryRequirements(self: *Self, requirements: *vk.MemoryRequirements) void { requirements.size = self.size; requirements.memory_type_bits = self.allowed_memory_types.mask; self.vtable.getMemoryRequirements(self, requirements); } -pub inline fn getDeviceAddress(self: *Self) VkError!vk.DeviceAddress { +pub fn getDeviceAddress(self: *Self) VkError!vk.DeviceAddress { const memory = self.memory orelse return 0; const map = try memory.map(self.offset, self.size); return @intCast(@intFromPtr(map.ptr)); diff --git a/src/vulkan/utils.zig b/src/vulkan/utils.zig index 2d925a8..f9a6a98 100644 --- a/src/vulkan/utils.zig +++ b/src/vulkan/utils.zig @@ -28,3 +28,21 @@ pub fn writePacked(comptime T: type, bytes: []u8, value: T) void { const raw: [@sizeOf(T)]u8 = @bitCast(value); @memcpy(bytes[0..@sizeOf(T)], raw[0..]); } + +pub fn ioctl(file: std.Io.File, io: std.Io, request: u32, arg: ?*anyopaque) (std.Io.Cancelable || std.posix.UnexpectedError)!void { + const result = try io.operate(.{ .device_io_control = .{ + .file = file, + .code = request, + .arg = arg, + } }); + + const rc = if (@import("builtin").link_libc) + @as(c_int, @intCast(result.device_io_control)) + else + @as(usize, @bitCast(@as(isize, @intCast(result.device_io_control)))); + + return switch (std.posix.errno(rc)) { + .SUCCESS => {}, + else => |e| std.posix.unexpectedErrno(e), + }; +}