241 lines
8.7 KiB
Zig
241 lines
8.7 KiB
Zig
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);
|
|
}
|