implementing device creation, device memory, basic buffers copy and buffers fill in Flint
Test / build_and_test (push) Successful in 4m51s
Build / build (push) Successful in 6m44s

This commit is contained in:
2026-07-10 00:19:29 +02:00
parent 3044bb37a5
commit 0224b5675f
14 changed files with 935 additions and 38 deletions
+201 -2
View File
@@ -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
</details>
## 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
<details>
<summary>
Here standeth the present reckoning of thy Vulkan 1.0 implementation
</summary>
\
⚠️ 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
</details>
+12 -2
View File
@@ -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);
}
}
+139 -16
View File
@@ -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 {
+8
View File
@@ -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);
}
+28 -12
View File
@@ -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;
}
+8 -1
View File
@@ -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);
+1 -1
View File
@@ -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| {
+81
View File
@@ -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,
};
+240
View File
@@ -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);
}
+144
View File
@@ -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,
};
}
};
+2
View File
@@ -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);
}
+49
View File
@@ -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());
}
+4 -4
View File
@@ -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));
+18
View File
@@ -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),
};
}