Files
VulkanDriver/src/software/SoftPipelineCache.zig
T
kbz_8 d8a5452c6f
Test / build_and_test (push) Successful in 27s
Build / build (push) Successful in 38s
renaming folders
2026-06-16 14:28:21 +02:00

239 lines
7.5 KiB
Zig

const std = @import("std");
const vk = @import("vulkan");
const base = @import("base");
const spv = @import("spv");
const VkError = base.VkError;
const SoftDevice = @import("SoftDevice.zig");
const SoftShaderModule = @import("SoftShaderModule.zig");
const Self = @This();
pub const Interface = base.PipelineCache;
const SpecConstant = struct {
id: u32,
data: []u8,
};
const CacheRecord = extern struct {
magic: [8]u8,
version: u32,
code_hash: u64,
entry_hash: u64,
spec_hash: u64,
};
const CachedShader = struct {
module: *SoftShaderModule,
entry: []u8,
specs: []SpecConstant,
runtime: spv.Runtime,
fn deinit(self: *@This(), object_allocator: std.mem.Allocator, data_allocator: std.mem.Allocator) void {
self.module.unref(object_allocator);
data_allocator.free(self.entry);
for (self.specs) |spec| {
data_allocator.free(spec.data);
}
if (self.specs.len != 0) {
data_allocator.free(self.specs);
}
self.runtime.deinit(data_allocator);
}
};
interface: Interface,
mutex: std.Io.Mutex,
shaders: std.ArrayList(CachedShader),
pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const vk.PipelineCacheCreateInfo) VkError!*Self {
const self = allocator.create(Self) catch return VkError.OutOfHostMemory;
errdefer allocator.destroy(self);
var interface = try Interface.init(device, allocator, info);
interface.vtable = &.{
.destroy = destroy,
};
self.* = .{
.interface = interface,
.mutex = .init,
.shaders = .empty,
};
return self;
}
pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const soft_device: *SoftDevice = @alignCast(@fieldParentPtr("interface", interface.owner));
const data_allocator = soft_device.device_allocator.allocator();
for (self.shaders.items) |*shader| {
shader.deinit(allocator, data_allocator);
}
self.shaders.deinit(data_allocator);
allocator.destroy(self);
}
pub fn cloneRuntime(
self: *Self,
allocator: std.mem.Allocator,
module: *SoftShaderModule,
entry: []const u8,
specialization: ?*const vk.SpecializationInfo,
image_api: spv.Runtime.ImageAPI,
) VkError!?spv.Runtime {
const io = self.interface.owner.io();
self.mutex.lock(io) catch return VkError.DeviceLost;
defer self.mutex.unlock(io);
for (self.shaders.items) |*shader| {
if (shader.module == module and std.mem.eql(u8, shader.entry, entry) and specsEqual(shader.specs, specialization)) {
return spv.Runtime.initFrom(allocator, &shader.runtime, image_api) catch return VkError.OutOfDeviceMemory;
}
}
return null;
}
pub fn storeRuntimeTemplate(
self: *Self,
object_allocator: std.mem.Allocator,
data_allocator: std.mem.Allocator,
module: *SoftShaderModule,
entry: []const u8,
specialization: ?*const vk.SpecializationInfo,
image_api: spv.Runtime.ImageAPI,
) VkError!void {
const io = self.interface.owner.io();
self.mutex.lock(io) catch return VkError.DeviceLost;
defer self.mutex.unlock(io);
for (self.shaders.items) |*shader| {
if (shader.module == module and
std.mem.eql(u8, shader.entry, entry) and
specsEqual(shader.specs, specialization))
{
return;
}
}
var cached: CachedShader = .{
.module = module,
.entry = data_allocator.dupe(u8, entry) catch return VkError.OutOfDeviceMemory,
.specs = try cloneSpecs(data_allocator, specialization),
.runtime = spv.Runtime.init(data_allocator, &module.module, image_api) catch return VkError.OutOfDeviceMemory,
};
var module_ref = false;
errdefer {
if (module_ref) cached.module.unref(object_allocator);
data_allocator.free(cached.entry);
for (cached.specs) |spec| {
data_allocator.free(spec.data);
}
if (cached.specs.len != 0) {
data_allocator.free(cached.specs);
}
cached.runtime.deinit(data_allocator);
}
module.ref();
module_ref = true;
try applySpecialization(&cached.runtime, data_allocator, specialization);
try appendCacheRecord(&self.interface, module, entry, cached.specs);
self.shaders.append(data_allocator, cached) catch return VkError.OutOfDeviceMemory;
}
fn applySpecialization(runtime: *spv.Runtime, allocator: std.mem.Allocator, specialization: ?*const vk.SpecializationInfo) VkError!void {
const info = specialization orelse return;
const entries = info.p_map_entries orelse return;
const data = specializationData(info);
for (entries[0..info.map_entry_count]) |entry| {
runtime.addSpecializationInfo(
allocator,
.{
.id = @intCast(entry.constant_id),
.offset = @intCast(entry.offset),
.size = @intCast(entry.size),
},
data,
) catch return VkError.OutOfDeviceMemory;
}
}
fn cloneSpecs(allocator: std.mem.Allocator, specialization: ?*const vk.SpecializationInfo) VkError![]SpecConstant {
const info = specialization orelse return &.{};
const entries = info.p_map_entries orelse return &.{};
if (info.map_entry_count == 0) return &.{};
const data = specializationData(info);
const specs = allocator.alloc(SpecConstant, info.map_entry_count) catch return VkError.OutOfDeviceMemory;
var initialized: usize = 0;
errdefer {
for (specs[0..initialized]) |spec| {
allocator.free(spec.data);
}
if (specs.len != 0) {
allocator.free(specs);
}
}
for (specs, entries[0..info.map_entry_count]) |*spec, entry| {
spec.* = .{
.id = entry.constant_id,
.data = allocator.dupe(u8, data[entry.offset .. entry.offset + entry.size]) catch return VkError.OutOfDeviceMemory,
};
initialized += 1;
}
return specs;
}
fn specsEqual(specs: []const SpecConstant, specialization: ?*const vk.SpecializationInfo) bool {
const info = specialization orelse return specs.len == 0;
const entries = info.p_map_entries orelse return specs.len == 0;
if (specs.len != info.map_entry_count) return false;
const data = specializationData(info);
for (specs) |spec| {
var found = false;
for (entries[0..info.map_entry_count]) |entry| {
if (spec.id != entry.constant_id) continue;
if (!std.mem.eql(u8, spec.data, data[entry.offset .. entry.offset + entry.size])) {
return false;
}
found = true;
break;
}
if (!found) return false;
}
return true;
}
fn specializationData(info: *const vk.SpecializationInfo) []const u8 {
return @as([*]const u8, @ptrCast(@alignCast(info.p_data)))[0..info.data_size];
}
fn appendCacheRecord(interface: *Interface, module: *SoftShaderModule, entry: []const u8, specs: []const SpecConstant) VkError!void {
var spec_hasher = std.hash.Wyhash.init(0);
for (specs) |spec| {
spec_hasher.update(std.mem.asBytes(&spec.id));
spec_hasher.update(spec.data);
}
const record: CacheRecord = .{
.magic = "APESOFTC".*,
.version = 1,
.code_hash = std.hash.Wyhash.hash(0, std.mem.sliceAsBytes(module.module.code)),
.entry_hash = std.hash.Wyhash.hash(0, entry),
.spec_hash = spec_hasher.final(),
};
try interface.appendPayload(std.mem.asBytes(&record));
}