adding backend agnostic IR
Test / build_and_test (push) Successful in 5m16s
Build / build (push) Successful in 6m50s

This commit is contained in:
2026-07-20 22:07:41 +02:00
parent 2375abf688
commit 9b1b223186
70 changed files with 7358 additions and 250 deletions
+27 -14
View File
@@ -5,6 +5,8 @@ const kmd = @import("kmd.zig");
const VkError = base.VkError;
const FlintDevice = @import("FlintDevice.zig");
const FlintDescriptorSet = @import("FlintDescriptorSet.zig");
const FlintPipeline = @import("FlintPipeline.zig");
const MemoryRange = @import("MemoryRange.zig");
@@ -17,6 +19,8 @@ pub const Interface = base.CommandBuffer;
interface: Interface,
batch: std.ArrayList(u32),
relocations: std.ArrayList(kmd.Relocation),
bound_compute_pipeline: ?*FlintPipeline,
bound_compute_descriptor_sets: [base.vulkan_max_descriptor_sets]?*FlintDescriptorSet,
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;
@@ -80,6 +84,8 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v
.interface = interface,
.batch = .empty,
.relocations = .empty,
.bound_compute_pipeline = null,
.bound_compute_descriptor_sets = @splat(null),
};
return self;
}
@@ -96,8 +102,7 @@ pub fn submitGpuBatch(self: *Self, syncs: []const kmd.SyncDependency) VkError!vo
try self.interface.submit();
defer self.interface.finish() catch @panic("Caught an error while handling an error");
if (self.batch.items.len == 0) return;
// Empty command buffers still need a no-op submission to carry queue synchronization.
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, syncs);
@@ -122,6 +127,8 @@ pub fn reset(interface: *Interface, flags: vk.CommandBufferResetFlags) VkError!v
self.batch.clearRetainingCapacity();
self.relocations.clearRetainingCapacity();
}
self.bound_compute_pipeline = null;
self.bound_compute_descriptor_sets = @splat(null);
}
pub fn emit(self: *Self, dword: u32) VkError!void {
@@ -165,18 +172,27 @@ pub fn beginRenderPass(interface: *Interface, render_pass: *base.RenderPass, fra
_ = clear_values;
}
pub fn bindDescriptorSets(interface: *Interface, bind_point: vk.PipelineBindPoint, first_set: u32, sets: [base.VULKAN_MAX_DESCRIPTOR_SETS]?*base.DescriptorSet, dynamic_offsets: []const u32) VkError!void {
_ = interface;
_ = bind_point;
_ = first_set;
_ = sets;
pub fn bindDescriptorSets(interface: *Interface, bind_point: vk.PipelineBindPoint, first_set: u32, sets: [base.vulkan_max_descriptor_sets]?*base.DescriptorSet, dynamic_offsets: []const u32) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
if (bind_point != .compute) return;
if (first_set >= base.vulkan_max_descriptor_sets) return VkError.ValidationFailed;
for (sets, 0..) |set, index| {
const base_set = set orelse break;
const destination = first_set + index;
if (destination >= base.vulkan_max_descriptor_sets) return VkError.ValidationFailed;
self.bound_compute_descriptor_sets[destination] = @alignCast(@fieldParentPtr("interface", base_set));
}
_ = dynamic_offsets;
}
pub fn bindPipeline(interface: *Interface, bind_point: vk.PipelineBindPoint, pipeline: *base.Pipeline) VkError!void {
_ = interface;
_ = bind_point;
_ = pipeline;
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
if (bind_point != .compute)
return;
const flint_pipeline: *FlintPipeline = @alignCast(@fieldParentPtr("interface", pipeline));
self.bound_compute_pipeline = flint_pipeline;
}
pub fn bindIndexBuffer(interface: *Interface, buffer: *base.Buffer, offset: usize, index_type: vk.IndexType) VkError!void {
@@ -269,10 +285,7 @@ pub fn copyQueryPoolResults(interface: *Interface, pool: *base.QueryPool, first:
}
pub fn dispatch(interface: *Interface, group_count_x: u32, group_count_y: u32, group_count_z: u32) VkError!void {
_ = interface;
_ = group_count_x;
_ = group_count_y;
_ = group_count_z;
try dispatchBase(interface, 0, 0, 0, group_count_x, group_count_y, group_count_z);
}
pub fn dispatchBase(interface: *Interface, base_group_x: u32, base_group_y: u32, base_group_z: u32, group_count_x: u32, group_count_y: u32, group_count_z: u32) VkError!void {
+23 -6
View File
@@ -3,11 +3,15 @@ const vk = @import("vulkan");
const base = @import("base");
const VkError = base.VkError;
const VulkanAllocator = base.VulkanAllocator;
const FlintDescriptorSet = @import("FlintDescriptorSet.zig");
const Self = @This();
pub const Interface = base.DescriptorPool;
interface: Interface,
sets: std.ArrayList(*FlintDescriptorSet),
pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const vk.DescriptorPoolCreateInfo) VkError!*Self {
const self = allocator.create(Self) catch return VkError.OutOfHostMemory;
@@ -24,29 +28,42 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v
self.* = .{
.interface = interface,
.sets = std.ArrayList(*FlintDescriptorSet).initCapacity(allocator, info.max_sets) catch return VkError.OutOfHostMemory,
};
return self;
}
pub fn allocateDescriptorSet(interface: *Interface, layout: *base.DescriptorSetLayout) VkError!*base.DescriptorSet {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
_ = self;
_ = layout;
return VkError.Unknown;
if (self.sets.items.len == self.sets.capacity) return VkError.OutOfPoolMemory;
const allocator = VulkanAllocator.init(null, .object).allocator();
const set = try FlintDescriptorSet.create(interface.owner, allocator, layout);
self.sets.appendAssumeCapacity(set);
return &set.interface;
}
pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const set_allocator = VulkanAllocator.init(null, .object).allocator();
for (self.sets.items) |set| set.interface.destroy(set_allocator);
self.sets.deinit(allocator);
allocator.destroy(self);
}
pub fn freeDescriptorSet(interface: *Interface, set: *base.DescriptorSet) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
_ = self;
_ = set;
const flint_set: *FlintDescriptorSet = @alignCast(@fieldParentPtr("interface", set));
const index = std.mem.indexOfScalar(*FlintDescriptorSet, self.sets.items, flint_set) orelse return VkError.ValidationFailed;
_ = self.sets.orderedRemove(index);
const allocator = VulkanAllocator.init(null, .object).allocator();
set.destroy(allocator);
}
pub fn reset(interface: *Interface, _: vk.DescriptorPoolResetFlags) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
_ = self;
const allocator = VulkanAllocator.init(null, .object).allocator();
for (self.sets.items) |set| set.interface.destroy(allocator);
self.sets.clearRetainingCapacity();
}
+132 -5
View File
@@ -3,11 +3,25 @@ const vk = @import("vulkan");
const base = @import("base");
const VkError = base.VkError;
const NonDispatchable = base.NonDispatchable;
const Self = @This();
pub const Interface = base.DescriptorSet;
pub const DescriptorBuffer = struct {
buffer: ?*base.Buffer,
offset: vk.DeviceSize,
size: vk.DeviceSize,
};
const Descriptor = union(enum) {
buffer: []DescriptorBuffer,
unsupported,
};
interface: Interface,
heap: []u8,
descriptors: []Descriptor,
pub fn create(device: *base.Device, allocator: std.mem.Allocator, layout: *base.DescriptorSetLayout) VkError!*Self {
const self = allocator.create(Self) catch return VkError.OutOfHostMemory;
@@ -21,26 +35,139 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, layout: *base.
.write = write,
};
var heap_size = layout.bindings.len * @sizeOf(Descriptor);
for (layout.bindings) |binding| {
heap_size += switch (binding.descriptor_type) {
.uniform_buffer,
.uniform_buffer_dynamic,
.storage_buffer,
.storage_buffer_dynamic,
=> binding.array_size * @sizeOf(DescriptorBuffer),
else => 0,
};
}
const heap = allocator.alloc(u8, heap_size) catch return VkError.OutOfHostMemory;
errdefer allocator.free(heap);
var fixed = std.heap.FixedBufferAllocator.init(heap);
const descriptors = fixed.allocator().alloc(Descriptor, layout.bindings.len) catch return VkError.OutOfHostMemory;
for (descriptors, layout.bindings) |*descriptor, binding| {
descriptor.* = switch (binding.descriptor_type) {
.uniform_buffer, .uniform_buffer_dynamic, .storage_buffer, .storage_buffer_dynamic => blk: {
const buffers = fixed.allocator().alloc(DescriptorBuffer, binding.array_size) catch return VkError.OutOfHostMemory;
for (buffers) |*buffer| {
buffer.* = .{
.buffer = null,
.offset = 0,
.size = 0,
};
}
break :blk .{ .buffer = buffers };
},
else => .unsupported,
};
}
self.* = .{
.interface = interface,
.heap = heap,
.descriptors = descriptors,
};
return self;
}
pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
allocator.free(self.heap);
allocator.destroy(self);
}
pub fn copy(interface: *Interface, src_interface: *const Interface, data: vk.CopyDescriptorSet) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
_ = self;
_ = src_interface;
_ = data;
const src: *const Self = @alignCast(@fieldParentPtr("interface", src_interface));
if (data.dst_binding >= self.descriptors.len or data.src_binding >= src.descriptors.len)
return VkError.ValidationFailed;
const dst = switch (self.descriptors[data.dst_binding]) {
.buffer => |buffers| buffers,
.unsupported => return VkError.FeatureNotPresent,
};
const source = switch (src.descriptors[data.src_binding]) {
.buffer => |buffers| buffers,
.unsupported => return VkError.FeatureNotPresent,
};
const dst_start: usize = @intCast(data.dst_array_element);
const src_start: usize = @intCast(data.src_array_element);
const count: usize = @intCast(data.descriptor_count);
if (dst_start > dst.len or count > dst.len - dst_start)
return VkError.ValidationFailed;
if (src_start > source.len or count > source.len - src_start)
return VkError.ValidationFailed;
@memcpy(dst[dst_start .. dst_start + count], source[src_start .. src_start + count]);
}
pub fn write(interface: *Interface, write_data: vk.WriteDescriptorSet) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
_ = self;
_ = write_data;
switch (write_data.descriptor_type) {
.uniform_buffer,
.uniform_buffer_dynamic,
.storage_buffer,
.storage_buffer_dynamic,
=> {
if (write_data.dst_binding >= self.descriptors.len)
return VkError.ValidationFailed;
const descriptors = switch (self.descriptors[write_data.dst_binding]) {
.buffer => |buffers| buffers,
.unsupported => return VkError.FeatureNotPresent,
};
const start: usize = @intCast(write_data.dst_array_element);
const count: usize = @intCast(write_data.descriptor_count);
if (start > descriptors.len or count > descriptors.len - start)
return VkError.ValidationFailed;
for (write_data.p_buffer_info, 0..write_data.descriptor_count) |buffer_info, index| {
const descriptor = &descriptors[start + index];
descriptor.* = .{ .buffer = null, .offset = buffer_info.offset, .size = buffer_info.range };
if (buffer_info.buffer == .null_handle)
continue;
const buffer = try NonDispatchable(base.Buffer).fromHandleObject(buffer_info.buffer);
if (descriptor.offset > buffer.size)
return VkError.ValidationFailed;
if (descriptor.size == vk.WHOLE_SIZE)
descriptor.size = buffer.size - descriptor.offset;
if (descriptor.size > buffer.size - descriptor.offset)
return VkError.ValidationFailed;
descriptor.buffer = buffer;
}
},
else => return VkError.FeatureNotPresent,
}
}
pub fn getBuffer(self: *const Self, binding: u32, array_element: u32) VkError!DescriptorBuffer {
if (binding >= self.descriptors.len) return VkError.ValidationFailed;
const buffers = switch (self.descriptors[binding]) {
.buffer => |items| items,
.unsupported => return VkError.FeatureNotPresent,
};
if (array_element >= buffers.len) return VkError.ValidationFailed;
return buffers[array_element];
}
+2 -2
View File
@@ -46,8 +46,8 @@ pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void {
}
pub fn getMemoryRequirements(_: *Interface, requirements: *vk.MemoryRequirements) VkError!void {
requirements.alignment = lib.IMAGE_MEMORY_ALIGNMENT;
requirements.size = std.mem.alignForward(vk.DeviceSize, requirements.size, lib.IMAGE_MEMORY_ALIGNMENT);
requirements.alignment = lib.image_memory_alignment;
requirements.size = std.mem.alignForward(vk.DeviceSize, requirements.size, lib.image_memory_alignment);
}
pub fn copyToMemory(interface: *const Interface, dst: []u8, subresource: vk.ImageSubresourceLayers) VkError!void {
+2 -2
View File
@@ -26,7 +26,7 @@ fn castExtension(comptime ext: vk.ApiInfo) vk.ExtensionProperties {
return props;
}
pub const EXTENSIONS = [_]vk.ExtensionProperties{
pub const extensions = [_]vk.ExtensionProperties{
castExtension(vk.extensions.khr_device_group_creation),
castExtension(vk.extensions.khr_get_physical_device_properties_2),
castExtension(vk.extensions.khr_surface),
@@ -73,7 +73,7 @@ fn requestPhysicalDevices(interface: *Interface, allocator: std.mem.Allocator, d
if (drm_device.node_type != .render or
std.meta.activeTag(drm_device.device_info) != .pci or
drm_device.device_info.pci.vendor_id != lib.INTEL_PCI_VENDOR_ID)
drm_device.device_info.pci.vendor_id != lib.intel_pci_vendor_id)
continue;
const version = device.getVersion(io_var, allocator) catch continue;
+6 -6
View File
@@ -23,7 +23,7 @@ fn castExtension(comptime ext: vk.ApiInfo) vk.ExtensionProperties {
return props;
}
pub const EXTENSIONS = [_]vk.ExtensionProperties{
pub const extensions = [_]vk.ExtensionProperties{
castExtension(vk.extensions.khr_swapchain),
};
@@ -53,9 +53,9 @@ pub fn create(allocator: std.mem.Allocator, instance: *base.Instance, drm_device
.getSurfaceSupportKHR = getSurfaceSupportKHR,
};
interface.props.api_version = @bitCast(lib.VULKAN_VERSION);
interface.props.vendor_id = lib.INTEL_PCI_VENDOR_ID;
interface.props.driver_version = @bitCast(base.DRIVER_VERSION);
interface.props.api_version = @bitCast(lib.vulkan_version);
interface.props.vendor_id = lib.intel_pci_vendor_id;
interface.props.driver_version = @bitCast(base.driver_version);
interface.props.device_id = drm_device.device_info.pci.device_id;
interface.props.device_type = .integrated_gpu;
@@ -256,10 +256,10 @@ pub fn enumerateExtensionProperties(_: *const Interface, layer_name: ?[]const u8
return VkError.LayerNotPresent;
}
const available = EXTENSIONS.len;
const available = extensions.len;
if (p_properties) |properties| {
const write_count = @min(count.*, available);
for (EXTENSIONS[0..write_count], properties[0..write_count]) |ext, *prop| {
for (extensions[0..write_count], properties[0..write_count]) |ext, *prop| {
prop.* = ext;
}
count.* = @intCast(write_count);
+12 -2
View File
@@ -8,6 +8,7 @@ const Self = @This();
pub const Interface = base.Pipeline;
interface: Interface,
host_allocator: base.VulkanAllocator,
pub fn createCompute(device: *base.Device, allocator: std.mem.Allocator, cache: ?*base.PipelineCache, info: *const vk.ComputePipelineCreateInfo) VkError!*Self {
const self = allocator.create(Self) catch return VkError.OutOfHostMemory;
@@ -15,8 +16,12 @@ pub fn createCompute(device: *base.Device, allocator: std.mem.Allocator, cache:
var interface = try Interface.initCompute(device, allocator, cache, info);
interface.vtable = &.{ .destroy = destroy };
self.* = .{
.interface = interface,
.host_allocator = base.VulkanAllocator.from(allocator).clone(),
};
errdefer self.interface.layout.unref(allocator);
self.* = .{ .interface = interface };
return self;
}
@@ -27,7 +32,12 @@ pub fn createGraphics(device: *base.Device, allocator: std.mem.Allocator, cache:
var interface = try Interface.initGraphics(device, allocator, cache, info);
interface.vtable = &.{ .destroy = destroy };
self.* = .{ .interface = interface };
self.* = .{
.interface = interface,
.host_allocator = base.VulkanAllocator.from(allocator).clone(),
};
errdefer self.interface.layout.unref(allocator);
return self;
}
+6
View File
@@ -8,6 +8,7 @@ const Self = @This();
pub const Interface = base.ShaderModule;
interface: Interface,
code: []u32,
ref_count: std.atomic.Value(usize),
pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const vk.ShaderModuleCreateInfo) VkError!*Self {
@@ -16,9 +17,13 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v
var interface = try Interface.init(device, allocator, info);
interface.vtable = &.{ .destroy = destroy };
if (info.code_size % @sizeOf(u32) != 0) return VkError.ValidationFailed;
const code = allocator.dupe(u32, info.p_code[0 .. info.code_size / @sizeOf(u32)]) catch return VkError.OutOfHostMemory;
errdefer allocator.free(code);
self.* = .{
.interface = interface,
.code = code,
.ref_count = std.atomic.Value(usize).init(1),
};
return self;
@@ -30,6 +35,7 @@ pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void {
}
pub fn drop(self: *Self, allocator: std.mem.Allocator) void {
allocator.free(self.code);
allocator.destroy(self);
}
+1 -1
View File
@@ -32,7 +32,7 @@ pub fn blitImageRegion(cmd: *FlintCommandBuffer, src: *base.Image, dst: *base.Im
if (base.format.isCompressed(src_format) or base.format.isCompressed(dst_format))
return VkError.FormatNotSupported;
// XY_SRC_COPY_BLT does not perform component conversion. Different Vulkan
// xy_src_copy_blt does not perform component conversion. Different Vulkan
// names are safe only when they describe the same bytes and values.
if (!bitwiseCompatibleFormats(src_format, dst_format) or src_texel_size != dst_texel_size)
return VkError.FormatNotSupported;
-1
View File
@@ -1,4 +1,3 @@
pub const command_base = 0x40;
pub const gem_create = 0x1b;
pub const gem_mmap_gtt = 0x24;
+3 -3
View File
@@ -9,7 +9,7 @@ const i915_kmd = @import("i915/kmd.zig");
const xe = @import("xe/kmd.zig");
const VkError = base.VkError;
const IOCTL = std.os.linux.IOCTL;
const ioctl = std.os.linux.IOCTL;
pub const xy_src_copy_blt: u32 = (2 << 29) | (0x53 << 22) | 8;
pub const xy_blt_write_alpha: u32 = 1 << 21;
@@ -160,9 +160,9 @@ pub const Memory = union(KmdType) {
};
pub inline fn drmIoctlIow(nr: u8, comptime T: type) u32 {
return IOCTL.IOW('d', nr, T);
return ioctl.IOW('d', nr, T);
}
pub inline fn drmIoctlIowr(nr: u8, comptime T: type) u32 {
return IOCTL.IOWR('d', nr, T);
return ioctl.IOWR('d', nr, T);
}
+5 -5
View File
@@ -35,13 +35,13 @@ pub const FlintShaderModule = @import("FlintShaderModule.zig");
pub const Instance = FlintInstance;
pub const DRIVER_NAME = "Flint";
pub const driver_name = "Flint";
pub const PHYSICAL_DEVICE_DEFAULT_NAME = "Unkown Intel device";
pub const physical_device_default_name = "Unkown Intel device";
pub const INTEL_PCI_VENDOR_ID = 0x8086;
pub const intel_pci_vendor_id = 0x8086;
pub const VULKAN_VERSION = vk.makeApiVersion(
pub const vulkan_version = vk.makeApiVersion(
0,
config.flint_vulkan_version.major,
config.flint_vulkan_version.minor,
@@ -49,7 +49,7 @@ pub const VULKAN_VERSION = vk.makeApiVersion(
);
/// GEM buffer objects are page based
pub const IMAGE_MEMORY_ALIGNMENT = std.heap.page_size_max;
pub const image_memory_alignment = std.heap.page_size_max;
pub const KmdType = enum {
invalid,