implementing proper pipeline cache
Test / build_and_test (push) Successful in 2m3s
Build / build (push) Successful in 2m21s

This commit is contained in:
2026-06-13 23:08:12 +02:00
parent a8ba6fd14f
commit 57b5533195
13 changed files with 823 additions and 218 deletions
+3 -3
View File
@@ -113,7 +113,7 @@ vkCreateGraphicsPipelines | ✅ Implemented
vkCreateImage | ✅ Implemented
vkCreateImageView | ✅ Implemented
vkCreateInstance | ✅ Implemented
vkCreatePipelineCache | ⚙️ WIP
vkCreatePipelineCache | ✅ Implemented
vkCreatePipelineLayout | ✅ Implemented
vkCreateQueryPool | ✅ Implemented
vkCreateRenderPass | ✅ Implemented
@@ -184,13 +184,13 @@ vkGetPhysicalDeviceWaylandPresentationSupportKHR | ✅ Implemented
vkGetPhysicalDeviceWin32PresentationSupportKHR | ⚙️ WIP
vkGetPhysicalDeviceXcbPresentationSupportKHR | ⚙️ WIP
vkGetPhysicalDeviceXlibPresentationSupportKHR | ⚙️ WIP
vkGetPipelineCacheData | ⚙️ WIP
vkGetPipelineCacheData | ✅ Implemented
vkGetQueryPoolResults | ✅ Implemented
vkGetRenderAreaGranularity | ✅ Implemented
vkGetSwapchainImagesKHR | ✅ Implemented
vkInvalidateMappedMemoryRanges | ✅ Implemented
vkMapMemory | ✅ Implemented
vkMergePipelineCaches | ⚙️ WIP
vkMergePipelineCaches | ✅ Implemented
vkQueueBindSparse | ❎ Unsupported
vkQueuePresentKHR | ✅ Implemented
vkQueueSubmit | ✅ Implemented
+2 -2
View File
@@ -26,8 +26,8 @@
.hash = "N-V-__8AAF9uOh0I4P_99za7N822J3JwsDaqONrFVrcEQo59",
},
.SPIRV_Interpreter = .{
.url = "git+https://git.kbz8.me/kbz_8/SPIRV-Interpreter#c7320325fdf6b1e5e6371ff620ed08b926587eed",
.hash = "SPIRV_Interpreter-0.0.1-ajmpn5F_BwBQHPDX6S9rSgLls4FYjpdxGD30B17paW7L",
.url = "git+https://git.kbz8.me/kbz_8/SPIRV-Interpreter#5c1eb25b4835f4161dd49cd000b19ae1cca6e80a",
.hash = "SPIRV_Interpreter-0.0.1-ajmpn8mDBwChlKPOGs3EkJl6RiTJYDcvpLbsIJaHBAOe",
.lazy = true,
},
//.SPIRV_Interpreter = .{
+120 -91
View File
@@ -22,6 +22,7 @@ const SoftImageView = @import("SoftImageView.zig");
const SoftInstance = @import("SoftInstance.zig");
const SoftSampler = @import("SoftSampler.zig");
const SoftShaderModule = @import("SoftShaderModule.zig");
const SoftPipelineCache = @import("SoftPipelineCache.zig");
const Self = @This();
pub const Interface = base.Pipeline;
@@ -66,6 +67,10 @@ pub fn createCompute(device: *base.Device, allocator: std.mem.Allocator, cache:
const soft_module: *SoftShaderModule = @alignCast(@fieldParentPtr("interface", module));
const device_allocator = soft_device.device_allocator.allocator();
const soft_cache: ?*SoftPipelineCache = if (cache) |pipeline_cache|
@alignCast(@fieldParentPtr("interface", pipeline_cache))
else
null;
self.* = .{
.interface = interface,
@@ -85,56 +90,8 @@ pub fn createCompute(device: *base.Device, allocator: std.mem.Allocator, cache:
},
};
self.stages.put(.compute, blk: {
var shader: Shader = undefined;
soft_module.ref();
shader.module = soft_module;
const runtimes = runtimes_allocator.alloc(Runtime, runtimes_count) catch return VkError.OutOfDeviceMemory;
for (runtimes) |*runtime| {
runtime.mutex = .init;
runtime.rt = spv.Runtime.init(
runtimes_allocator,
&soft_module.module,
.{
.readImageFloat4 = readImageFloat4,
.readImageInt4 = readImageInt4,
.writeImageFloat4 = writeImageFloat4,
.writeImageInt4 = writeImageInt4,
.sampleImageFloat4 = sampleImageFloat4,
.sampleImageInt4 = sampleImageInt4,
.sampleImageDref = sampleImageDref,
.queryImageSize = queryImageSize,
},
) catch |err| {
std.log.scoped(.SpvRuntimeInit).err("SPIR-V Runtime failed to initialize, {s}", .{@errorName(err)});
return VkError.Unknown;
};
if (info.stage.p_specialization_info) |specialization| {
if (specialization.p_map_entries) |map| {
const data: []const u8 = @as([*]const u8, @ptrCast(@alignCast(specialization.p_data)))[0..specialization.data_size];
for (map[0..], 0..specialization.map_entry_count) |entry, _| {
runtime.rt.addSpecializationInfo(
runtimes_allocator,
.{
.id = @intCast(entry.constant_id),
.offset = @intCast(entry.offset),
.size = @intCast(entry.size),
},
data,
) catch return VkError.OutOfDeviceMemory;
}
}
}
}
shader.runtimes = runtimes;
shader.entry = runtimes_allocator.dupe(u8, std.mem.span(info.stage.p_name)) catch return VkError.OutOfDeviceMemory;
self.stages.put(.compute, try createShader(allocator, device_allocator, runtimes_allocator, soft_cache, soft_module, &info.stage, runtimes_count));
std.log.scoped(.ComputePipeline).debug("Created {d} runtimes for compute stage", .{runtimes_count});
break :blk shader;
});
return self;
}
@@ -151,6 +108,10 @@ pub fn createGraphics(device: *base.Device, allocator: std.mem.Allocator, cache:
const soft_device: *SoftDevice = @alignCast(@fieldParentPtr("interface", device));
const device_allocator = soft_device.device_allocator.allocator();
const soft_cache: ?*SoftPipelineCache = if (cache) |pipeline_cache|
@alignCast(@fieldParentPtr("interface", pipeline_cache))
else
null;
self.* = .{
.interface = interface,
@@ -172,50 +133,9 @@ pub fn createGraphics(device: *base.Device, allocator: std.mem.Allocator, cache:
if (info.p_stages) |stages| {
for (stages[0..], 0..info.stage_count) |stage, _| {
var shader: Shader = undefined;
const module = try NonDispatchable(ShaderModule).fromHandleObject(stage.module);
const soft_module: *SoftShaderModule = @alignCast(@fieldParentPtr("interface", module));
soft_module.ref();
shader.module = soft_module;
const runtimes = runtimes_allocator.alloc(Runtime, runtimes_count) catch return VkError.OutOfHostMemory;
for (runtimes) |*runtime| {
runtime.mutex = .init;
runtime.rt = spv.Runtime.init(
runtimes_allocator,
&soft_module.module,
.{
.readImageFloat4 = readImageFloat4,
.readImageInt4 = readImageInt4,
.writeImageFloat4 = writeImageFloat4,
.writeImageInt4 = writeImageInt4,
.sampleImageFloat4 = sampleImageFloat4,
.sampleImageInt4 = sampleImageInt4,
.sampleImageDref = sampleImageDref,
.queryImageSize = queryImageSize,
},
) catch |err| {
std.log.scoped(.SpvRuntimeInit).err("SPIR-V Runtime failed to initialize, {s}", .{@errorName(err)});
return VkError.Unknown;
};
if (stage.p_specialization_info) |specialization| {
if (specialization.p_map_entries) |map| {
const data: []const u8 = @as([*]const u8, @ptrCast(@alignCast(specialization.p_data)))[0..specialization.data_size];
for (map[0..], 0..specialization.map_entry_count) |entry, _| {
runtime.rt.addSpecializationInfo(runtimes_allocator, .{
.id = @intCast(entry.constant_id),
.offset = @intCast(entry.offset),
.size = @intCast(entry.size),
}, data) catch return VkError.OutOfHostMemory;
}
}
}
}
shader.runtimes = runtimes;
shader.entry = runtimes_allocator.dupe(u8, std.mem.span(stage.p_name)) catch return VkError.OutOfHostMemory;
const shader = try createShader(allocator, device_allocator, runtimes_allocator, soft_cache, soft_module, &stage, runtimes_count);
std.log.scoped(.GraphicsPipeline).debug("Created {d} runtimes for:", .{runtimes_count});
@@ -262,6 +182,115 @@ pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void {
allocator.destroy(self);
}
fn createShader(
object_allocator: std.mem.Allocator,
cache_allocator: std.mem.Allocator,
runtimes_allocator: std.mem.Allocator,
cache: ?*SoftPipelineCache,
module: *SoftShaderModule,
stage: *const vk.PipelineShaderStageCreateInfo,
runtimes_count: usize,
) VkError!Shader {
const entry = std.mem.span(stage.p_name);
const runtimes = runtimes_allocator.alloc(Runtime, runtimes_count) catch return VkError.OutOfDeviceMemory;
var initialized: usize = 0;
var module_ref = false;
errdefer {
for (runtimes[0..initialized]) |*runtime| {
runtime.rt.deinit(runtimes_allocator);
}
if (module_ref) {
module.unref(object_allocator);
}
}
module.ref();
module_ref = true;
const image_api = imageApi();
var cache_hit = false;
if (cache) |pipeline_cache| {
if (try pipeline_cache.cloneRuntime(runtimes_allocator, module, entry, stage.p_specialization_info, image_api)) |runtime| {
runtimes[0] = .{
.mutex = .init,
.rt = runtime,
};
initialized = 1;
cache_hit = true;
}
}
if (cache_hit) {
for (runtimes[initialized..]) |*runtime| {
runtime.* = .{
.mutex = .init,
.rt = (try cache.?.cloneRuntime(runtimes_allocator, module, entry, stage.p_specialization_info, image_api)).?,
};
initialized += 1;
}
} else {
for (runtimes) |*runtime| {
runtime.* = .{
.mutex = .init,
.rt = try initRuntime(runtimes_allocator, module, stage, image_api),
};
initialized += 1;
}
if (cache) |pipeline_cache| {
try pipeline_cache.storeRuntimeTemplate(object_allocator, cache_allocator, module, entry, stage.p_specialization_info, image_api);
}
}
return .{
.module = module,
.runtimes = runtimes,
.entry = runtimes_allocator.dupe(u8, entry) catch return VkError.OutOfDeviceMemory,
};
}
fn initRuntime(
allocator: std.mem.Allocator,
module: *SoftShaderModule,
stage: *const vk.PipelineShaderStageCreateInfo,
image_api: spv.Runtime.ImageAPI,
) VkError!spv.Runtime {
var runtime = spv.Runtime.init(allocator, &module.module, image_api) catch |err| {
std.log.scoped(.SpvRuntimeInit).err("SPIR-V Runtime failed to initialize, {s}", .{@errorName(err)});
return VkError.Unknown;
};
errdefer runtime.deinit(allocator);
try applySpecialization(&runtime, allocator, stage.p_specialization_info);
return runtime;
}
fn applySpecialization(runtime: *spv.Runtime, allocator: std.mem.Allocator, specialization: ?*const vk.SpecializationInfo) VkError!void {
const info = specialization orelse return;
const map = info.p_map_entries orelse return;
const data: []const u8 = @as([*]const u8, @ptrCast(@alignCast(info.p_data)))[0..info.data_size];
for (map[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 imageApi() spv.Runtime.ImageAPI {
return .{
.readImageFloat4 = readImageFloat4,
.readImageInt4 = readImageInt4,
.writeImageFloat4 = writeImageFloat4,
.writeImageInt4 = writeImageInt4,
.sampleImageFloat4 = sampleImageFloat4,
.sampleImageInt4 = sampleImageInt4,
.sampleImageDref = sampleImageDref,
.queryImageSize = queryImageSize,
};
}
fn readImageFloat4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32) SpvRuntimeError!spv.Runtime.Vec4(f32) {
var pixel = zm.f32x4s(0.0);
if (dim == .Buffer) {
+207 -1
View File
@@ -1,14 +1,50 @@
const std = @import("std");
const vk = @import("vulkan");
const base = @import("base");
const spv = @import("spv");
const VkError = base.VkError;
const Device = base.Device;
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;
@@ -22,11 +58,181 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v
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));
}
+4
View File
@@ -65,6 +65,8 @@ pub const Vertex = struct {
pub const DrawCall = struct {
renderer: *Self,
vertices: []Vertex,
vertex_count: usize,
instance_count: usize,
viewport: vk.Viewport,
scissor: vk.Rect2D,
@@ -87,6 +89,8 @@ pub const DrawCall = struct {
const self: @This() = .{
.vertices = allocator.alloc(Vertex, vertex_count * instance_count) catch return VkError.OutOfDeviceMemory,
.vertex_count = vertex_count,
.instance_count = instance_count,
.renderer = renderer,
.viewport = undefined,
.scissor = undefined,
+146
View File
@@ -560,19 +560,37 @@ inline fn normalizedI16(value: u16) f32 {
return @max(@as(f32, @floatFromInt(signed)) / @as(f32, @floatFromInt(std.math.maxInt(i16))), -1.0);
}
inline fn signedBits(value: u32, comptime bits: u5) i32 {
const shift: u5 = @intCast(32 - @as(u32, bits));
return @as(i32, @bitCast(value << shift)) >> shift;
}
inline fn normalizedSignedBits(value: u32, comptime bits: u5) f32 {
const max = (1 << (bits - 1)) - 1;
return @max(@as(f32, @floatFromInt(signedBits(value, bits))) / @as(f32, @floatFromInt(max)), -1.0);
}
pub fn readFloat4(map: []const u8, src_format: vk.Format) F32x4 {
var c: F32x4 = .{ 0.0, 0.0, 0.0, 1.0 };
switch (src_format) {
.r8_uscaled => c[0] = @floatFromInt(map[0]),
.r8_uint,
.r8_unorm,
.r8_srgb,
=> c[0] = @as(f32, @floatFromInt(map[0])) / std.math.maxInt(u8),
.r8_sscaled => c[0] = @floatFromInt(@as(i8, @bitCast(map[0]))),
.r8_sint,
.r8_snorm,
=> c[0] = normalizedI8(map[0]),
.r16_uscaled => c[0] = @floatFromInt(std.mem.bytesToValue(u16, map)),
.r16_sscaled => c[0] = @floatFromInt(@as(i16, @bitCast(std.mem.bytesToValue(u16, map)))),
.r16_snorm => c[0] = normalizedI16(std.mem.bytesToValue(u16, map)),
.r16_unorm,
.d16_unorm,
@@ -592,6 +610,11 @@ pub fn readFloat4(map: []const u8, src_format: vk.Format) F32x4 {
c[3] = @as(f32, @floatFromInt(map[3])) / std.math.maxInt(u8);
},
.r8g8_uscaled => {
c[0] = @floatFromInt(map[0]);
c[1] = @floatFromInt(map[1]);
},
.r8g8_uint,
.r8g8_unorm,
.r8g8_srgb,
@@ -600,6 +623,11 @@ pub fn readFloat4(map: []const u8, src_format: vk.Format) F32x4 {
c[1] = @as(f32, @floatFromInt(map[1])) / std.math.maxInt(u8);
},
.r8g8_sscaled => {
c[0] = @floatFromInt(@as(i8, @bitCast(map[0])));
c[1] = @floatFromInt(@as(i8, @bitCast(map[1])));
},
.r8g8_sint,
.r8g8_snorm,
=> {
@@ -607,6 +635,20 @@ pub fn readFloat4(map: []const u8, src_format: vk.Format) F32x4 {
c[1] = normalizedI8(map[1]);
},
.r8g8b8a8_uscaled => {
c[0] = @floatFromInt(map[0]);
c[1] = @floatFromInt(map[1]);
c[2] = @floatFromInt(map[2]);
c[3] = @floatFromInt(map[3]);
},
.r8g8b8a8_sscaled => {
c[0] = @floatFromInt(@as(i8, @bitCast(map[0])));
c[1] = @floatFromInt(@as(i8, @bitCast(map[1])));
c[2] = @floatFromInt(@as(i8, @bitCast(map[2])));
c[3] = @floatFromInt(@as(i8, @bitCast(map[3])));
},
.r8g8b8a8_snorm => {
c[0] = normalizedI8(map[0]);
c[1] = normalizedI8(map[1]);
@@ -652,6 +694,11 @@ pub fn readFloat4(map: []const u8, src_format: vk.Format) F32x4 {
.r16_sfloat => c[0] = std.mem.bytesToValue(f16, map),
.r16g16_uscaled => {
c[0] = @floatFromInt(std.mem.bytesToValue(u16, map[0..]));
c[1] = @floatFromInt(std.mem.bytesToValue(u16, map[2..]));
},
.r16g16_sint,
.r16g16_uint,
=> {
@@ -659,6 +706,11 @@ pub fn readFloat4(map: []const u8, src_format: vk.Format) F32x4 {
c[1] = @floatFromInt(std.mem.bytesToValue(u16, map[2..]));
},
.r16g16_sscaled => {
c[0] = @floatFromInt(@as(i16, @bitCast(std.mem.bytesToValue(u16, map[0..]))));
c[1] = @floatFromInt(@as(i16, @bitCast(std.mem.bytesToValue(u16, map[2..]))));
},
.r16g16_snorm => {
c[0] = normalizedI16(std.mem.bytesToValue(u16, map[0..]));
c[1] = normalizedI16(std.mem.bytesToValue(u16, map[2..]));
@@ -702,6 +754,20 @@ pub fn readFloat4(map: []const u8, src_format: vk.Format) F32x4 {
c[3] = @as(f32, @floatFromInt(std.mem.bytesToValue(u16, map[6..]))) / std.math.maxInt(u16);
},
.r16g16b16a16_uscaled => {
c[0] = @floatFromInt(std.mem.bytesToValue(u16, map[0..]));
c[1] = @floatFromInt(std.mem.bytesToValue(u16, map[2..]));
c[2] = @floatFromInt(std.mem.bytesToValue(u16, map[4..]));
c[3] = @floatFromInt(std.mem.bytesToValue(u16, map[6..]));
},
.r16g16b16a16_sscaled => {
c[0] = @floatFromInt(@as(i16, @bitCast(std.mem.bytesToValue(u16, map[0..]))));
c[1] = @floatFromInt(@as(i16, @bitCast(std.mem.bytesToValue(u16, map[2..]))));
c[2] = @floatFromInt(@as(i16, @bitCast(std.mem.bytesToValue(u16, map[4..]))));
c[3] = @floatFromInt(@as(i16, @bitCast(std.mem.bytesToValue(u16, map[6..]))));
},
.r16g16b16a16_sint,
.r16g16b16a16_snorm,
=> {
@@ -749,6 +815,22 @@ pub fn readFloat4(map: []const u8, src_format: vk.Format) F32x4 {
c[3] = normalizedI8(pack[3]);
},
.a8b8g8r8_uscaled_pack32 => {
const pack = std.mem.bytesToValue(@Vector(4, u8), map);
c[0] = @floatFromInt(pack[0]);
c[1] = @floatFromInt(pack[1]);
c[2] = @floatFromInt(pack[2]);
c[3] = @floatFromInt(pack[3]);
},
.a8b8g8r8_sscaled_pack32 => {
const pack = std.mem.bytesToValue(@Vector(4, u8), map);
c[0] = @floatFromInt(@as(i8, @bitCast(pack[0])));
c[1] = @floatFromInt(@as(i8, @bitCast(pack[1])));
c[2] = @floatFromInt(@as(i8, @bitCast(pack[2])));
c[3] = @floatFromInt(@as(i8, @bitCast(pack[3])));
},
.a2b10g10r10_uint_pack32,
.a2b10g10r10_unorm_pack32,
=> {
@@ -759,6 +841,30 @@ pub fn readFloat4(map: []const u8, src_format: vk.Format) F32x4 {
c[3] = @as(f32, @floatFromInt((pack & 0xC0000000) >> 30)) / std.math.maxInt(u2);
},
.a2b10g10r10_uscaled_pack32 => {
const pack = std.mem.bytesToValue(u32, map);
c[0] = @floatFromInt(pack & 0x000003FF);
c[1] = @floatFromInt((pack & 0x000FFC00) >> 10);
c[2] = @floatFromInt((pack & 0x3FF00000) >> 20);
c[3] = @floatFromInt((pack & 0xC0000000) >> 30);
},
.a2b10g10r10_sscaled_pack32 => {
const pack = std.mem.bytesToValue(u32, map);
c[0] = @floatFromInt(signedBits(pack & 0x000003FF, 10));
c[1] = @floatFromInt(signedBits((pack & 0x000FFC00) >> 10, 10));
c[2] = @floatFromInt(signedBits((pack & 0x3FF00000) >> 20, 10));
c[3] = @floatFromInt(signedBits((pack & 0xC0000000) >> 30, 2));
},
.a2b10g10r10_snorm_pack32 => {
const pack = std.mem.bytesToValue(u32, map);
c[0] = normalizedSignedBits(pack & 0x000003FF, 10);
c[1] = normalizedSignedBits((pack & 0x000FFC00) >> 10, 10);
c[2] = normalizedSignedBits((pack & 0x3FF00000) >> 20, 10);
c[3] = normalizedSignedBits((pack & 0xC0000000) >> 30, 2);
},
.a2r10g10b10_uint_pack32,
.a2r10g10b10_unorm_pack32,
=> {
@@ -769,6 +875,30 @@ pub fn readFloat4(map: []const u8, src_format: vk.Format) F32x4 {
c[3] = @as(f32, @floatFromInt((pack & 0xC0000000) >> 30)) / std.math.maxInt(u2);
},
.a2r10g10b10_uscaled_pack32 => {
const pack = std.mem.bytesToValue(u32, map);
c[2] = @floatFromInt(pack & 0x000003FF);
c[1] = @floatFromInt((pack & 0x000FFC00) >> 10);
c[0] = @floatFromInt((pack & 0x3FF00000) >> 20);
c[3] = @floatFromInt((pack & 0xC0000000) >> 30);
},
.a2r10g10b10_sscaled_pack32 => {
const pack = std.mem.bytesToValue(u32, map);
c[2] = @floatFromInt(signedBits(pack & 0x000003FF, 10));
c[1] = @floatFromInt(signedBits((pack & 0x000FFC00) >> 10, 10));
c[0] = @floatFromInt(signedBits((pack & 0x3FF00000) >> 20, 10));
c[3] = @floatFromInt(signedBits((pack & 0xC0000000) >> 30, 2));
},
.a2r10g10b10_snorm_pack32 => {
const pack = std.mem.bytesToValue(u32, map);
c[2] = normalizedSignedBits(pack & 0x000003FF, 10);
c[1] = normalizedSignedBits((pack & 0x000FFC00) >> 10, 10);
c[0] = normalizedSignedBits((pack & 0x3FF00000) >> 20, 10);
c[3] = normalizedSignedBits((pack & 0xC0000000) >> 30, 2);
},
.r5g6b5_unorm_pack16 => {
const pack = std.mem.bytesToValue(u16, map);
c[0] = @as(f32, @floatFromInt((pack & 0xF800) >> 11)) / std.math.maxInt(u5);
@@ -1267,6 +1397,14 @@ pub fn readInt4(map: []const u8, src_format: vk.Format) U32x4 {
c[3] = (pack & 0xC0000000) >> 30;
},
.a2b10g10r10_sint_pack32 => {
const pack = std.mem.bytesToValue(u32, map);
c[0] = @bitCast(signedBits(pack & 0x000003FF, 10));
c[1] = @bitCast(signedBits((pack & 0x000FFC00) >> 10, 10));
c[2] = @bitCast(signedBits((pack & 0x3FF00000) >> 20, 10));
c[3] = @bitCast(signedBits((pack & 0xC0000000) >> 30, 2));
},
.a2r10g10b10_unorm_pack32,
.a2r10g10b10_uint_pack32,
=> {
@@ -1277,6 +1415,14 @@ pub fn readInt4(map: []const u8, src_format: vk.Format) U32x4 {
c[3] = (pack & 0xC0000000) >> 30;
},
.a2r10g10b10_sint_pack32 => {
const pack = std.mem.bytesToValue(u32, map);
c[2] = @bitCast(signedBits(pack & 0x000003FF, 10));
c[1] = @bitCast(signedBits((pack & 0x000FFC00) >> 10, 10));
c[0] = @bitCast(signedBits((pack & 0x3FF00000) >> 20, 10));
c[3] = @bitCast(signedBits((pack & 0xC0000000) >> 30, 2));
},
else => base.unsupported("Blitter: read int from source format {any}", .{src_format}),
}
+54 -21
View File
@@ -121,7 +121,9 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
};
switch (topology) {
.point_list => for (draw_call.vertices) |*vertex| {
.point_list => for (0..draw_call.instance_count) |instance_index| {
const range = instanceVertexRange(draw_call, instance_index);
for (draw_call.vertices[range.start..range.end]) |*vertex| {
if (vertex.primitive_restart)
continue;
@@ -133,9 +135,13 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
if (depth_attachment_access) |*access| access else null,
if (stencil_attachment_access) |*access| access else null,
);
}
},
.triangle_list => for (0..@divTrunc(draw_call.vertices.len, 3)) |triangle_index| {
const first_vertex = triangle_index * 3;
.triangle_list => for (0..draw_call.instance_count) |instance_index| {
const range = instanceVertexRange(draw_call, instance_index);
const vertex_count = range.end - range.start;
for (0..@divTrunc(vertex_count, 3)) |triangle_index| {
const first_vertex = range.start + triangle_index * 3;
const v0 = &draw_call.vertices[first_vertex + 0];
const v1 = &draw_call.vertices[first_vertex + 1];
const v2 = &draw_call.vertices[first_vertex + 2];
@@ -151,11 +157,14 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
if (depth_attachment_access) |*access| access else null,
if (stencil_attachment_access) |*access| access else null,
);
}
},
.triangle_fan => {
var segment_start = firstNonRestart(draw_call, 0);
while (segment_start < draw_call.vertices.len) {
const segment_end = nextRestart(draw_call, segment_start);
for (0..draw_call.instance_count) |instance_index| {
const range = instanceVertexRange(draw_call, instance_index);
var segment_start = firstNonRestart(draw_call, range.start, range.end);
while (segment_start < range.end) {
const segment_end = nextRestart(draw_call, segment_start, range.end);
if (segment_end - segment_start >= 3) {
const v0 = &draw_call.vertices[segment_start];
for ((segment_start + 1)..(segment_end - 1)) |vertex_index| {
@@ -175,13 +184,16 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
);
}
}
segment_start = firstNonRestart(draw_call, segment_end + 1);
segment_start = firstNonRestart(draw_call, segment_end + 1, range.end);
}
}
},
.triangle_strip => {
var segment_start = firstNonRestart(draw_call, 0);
while (segment_start < draw_call.vertices.len) {
const segment_end = nextRestart(draw_call, segment_start);
for (0..draw_call.instance_count) |instance_index| {
const range = instanceVertexRange(draw_call, instance_index);
var segment_start = firstNonRestart(draw_call, range.start, range.end);
while (segment_start < range.end) {
const segment_end = nextRestart(draw_call, segment_start, range.end);
if (segment_end - segment_start >= 3) {
for (segment_start..(segment_end - 2)) |vertex_index| {
const local_index = vertex_index - segment_start;
@@ -216,11 +228,15 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
}
}
}
segment_start = firstNonRestart(draw_call, segment_end + 1);
segment_start = firstNonRestart(draw_call, segment_end + 1, range.end);
}
}
},
.line_list => for (0..@divTrunc(draw_call.vertices.len, 2)) |line_index| {
const first_vertex = line_index * 2;
.line_list => for (0..draw_call.instance_count) |instance_index| {
const range = instanceVertexRange(draw_call, instance_index);
const vertex_count = range.end - range.start;
for (0..@divTrunc(vertex_count, 2)) |line_index| {
const first_vertex = range.start + line_index * 2;
const v0 = &draw_call.vertices[first_vertex + 0];
const v1 = &draw_call.vertices[first_vertex + 1];
@@ -233,11 +249,14 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
if (depth_attachment_access) |*access| access else null,
if (stencil_attachment_access) |*access| access else null,
);
}
},
.line_strip => {
var segment_start = firstNonRestart(draw_call, 0);
while (segment_start < draw_call.vertices.len) {
const segment_end = nextRestart(draw_call, segment_start);
for (0..draw_call.instance_count) |instance_index| {
const range = instanceVertexRange(draw_call, instance_index);
var segment_start = firstNonRestart(draw_call, range.start, range.end);
while (segment_start < range.end) {
const segment_end = nextRestart(draw_call, segment_start, range.end);
if (segment_end - segment_start >= 2) {
for (segment_start..(segment_end - 1)) |vertex_index| {
const v0 = &draw_call.vertices[vertex_index + 0];
@@ -254,7 +273,8 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
);
}
}
segment_start = firstNonRestart(draw_call, segment_end + 1);
segment_start = firstNonRestart(draw_call, segment_end + 1, range.end);
}
}
},
else => base.unsupported("primitive topology {any}", .{topology}),
@@ -263,15 +283,28 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
draw_call.rasterizer_wait_group.await(io) catch return VkError.DeviceLost;
}
fn firstNonRestart(draw_call: *const DrawCall, start: usize) usize {
const VertexRange = struct {
start: usize,
end: usize,
};
fn instanceVertexRange(draw_call: *const DrawCall, instance_index: usize) VertexRange {
const start = instance_index * draw_call.vertex_count;
return .{
.start = start,
.end = @min(start + draw_call.vertex_count, draw_call.vertices.len),
};
}
fn firstNonRestart(draw_call: *const DrawCall, start: usize, end: usize) usize {
var index = start;
while (index < draw_call.vertices.len and draw_call.vertices[index].primitive_restart) : (index += 1) {}
while (index < end and draw_call.vertices[index].primitive_restart) : (index += 1) {}
return index;
}
fn nextRestart(draw_call: *const DrawCall, start: usize) usize {
fn nextRestart(draw_call: *const DrawCall, start: usize, end: usize) usize {
var index = start;
while (index < draw_call.vertices.len and !draw_call.vertices[index].primitive_restart) : (index += 1) {}
while (index < end and !draw_call.vertices[index].primitive_restart) : (index += 1) {}
return index;
}
+77 -4
View File
@@ -75,13 +75,21 @@ inline fn run(data: RunData) !void {
if (data.pipeline.interface.mode.graphics.input_assembly.attribute_description) |attributes| {
for (attributes) |attribute| {
const binding_info = (data.pipeline.interface.mode.graphics.input_assembly.binding_description orelse return)[attribute.binding];
const binding_info = findBindingDescription(
data.pipeline.interface.mode.graphics.input_assembly.binding_description orelse return,
attribute.binding,
) orelse return VkError.ValidationFailed;
const vertex_buffer = data.draw_call.renderer.state.data.graphics.vertex_buffers[attribute.binding];
const buffer = vertex_buffer.buffer;
const buffer_memory_size = base.format.texelSize(attribute.format);
const buffer_memory = if (buffer.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv;
const offset = buffer.interface.offset + vertex_buffer.offset + (binding_info.stride * vertex_index) + attribute.offset;
const input_index = switch (binding_info.input_rate) {
.vertex => vertex_index,
.instance => data.instance_index,
else => return VkError.ValidationFailed,
};
const offset = buffer.interface.offset + vertex_buffer.offset + (binding_info.stride * input_index) + attribute.offset;
const buffer_memory_map: []u8 = try buffer_memory.map(offset, buffer_memory_size);
@@ -91,7 +99,6 @@ inline fn run(data: RunData) !void {
rt.callEntryPoint(data.allocator, entry) catch |err| switch (err) {
// Some errors can be safely ignored
SpvRuntimeError.OutOfBounds => {},
SpvRuntimeError.Killed => {
try rt.flushDescriptorSets(data.allocator);
return;
@@ -99,7 +106,7 @@ inline fn run(data: RunData) !void {
else => return err,
};
try rt.readBuiltIn(std.mem.asBytes(&output.position), .Position);
try readPosition(rt, std.mem.asBytes(&output.position));
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
for (0..4) |component| {
@@ -129,6 +136,72 @@ inline fn run(data: RunData) !void {
}
}
fn findBindingDescription(binding_descriptions: []const vk.VertexInputBindingDescription, binding: u32) ?vk.VertexInputBindingDescription {
for (binding_descriptions) |description| {
if (description.binding == binding)
return description;
}
return null;
}
fn readPosition(rt: *spv.Runtime, output: []u8) !void {
if (rt.readBuiltIn(output, .Position)) {
return;
} else |err| switch (err) {
SpvRuntimeError.InvalidSpirV => {},
else => return err,
}
for (rt.results) |*result| {
const variant = result.variant orelse continue;
switch (variant) {
.AccessChain => |*access_chain| {
if (access_chain.indexes.len == 0)
continue;
const base_variant = rt.results[access_chain.base].variant orelse continue;
switch (base_variant) {
.Variable => |variable| {
if (variable.storage_class != .Output)
continue;
},
else => continue,
}
if (!isConstantZero(rt, access_chain.indexes[0]))
continue;
switch (access_chain.value) {
.Pointer => |ptr| switch (ptr.ptr) {
.common => |value| _ = try value.read(output),
else => continue,
},
else => _ = try access_chain.value.read(output),
}
return;
},
else => {},
}
}
return SpvRuntimeError.InvalidSpirV;
}
fn isConstantZero(rt: *spv.Runtime, result_word: spv.SpvWord) bool {
if (result_word >= rt.results.len)
return false;
const variant = rt.results[result_word].variant orelse return false;
switch (variant) {
.Constant => |constant| {
var value: u32 = undefined;
_ = constant.value.read(std.mem.asBytes(&value)) catch return false;
return value == 0;
},
else => return false,
}
}
fn setupBuiltins(rt: *spv.Runtime, vertex_index_u32: u32, instance_index: usize) !void {
const instance_index_u32: u32 = @intCast(instance_index);
+57 -13
View File
@@ -8,6 +8,7 @@ const VkError = @import("error_set.zig").VkError;
const Device = @import("Device.zig");
const PipelineCache = @import("PipelineCache.zig");
const PipelineLayout = @import("PipelineLayout.zig");
const RenderPass = @import("RenderPass.zig");
const Self = @This();
pub const ObjectType: vk.ObjectType = .pipeline;
@@ -108,6 +109,13 @@ pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipe
var color_attachments: ?[]vk.PipelineColorBlendAttachmentState = null;
errdefer if (color_attachments) |value| allocator.free(value);
const dynamic_state = try parseDynamicState(info.p_dynamic_state);
const rasterizer_discard_enable = if (info.p_rasterization_state) |state|
state.rasterizer_discard_enable == .true
else
false;
const has_color_attachments, const has_depth_stencil_attachment = try renderPassAttachmentState(info);
return .{
.owner = device,
.vtable = undefined,
@@ -144,8 +152,12 @@ pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipe
},
.viewport_state = .{
.viewports = blk: {
if (rasterizer_discard_enable or dynamic_state.viewport) {
break :blk null;
}
if (info.p_viewport_state) |viewport_state| {
if (viewport_state.p_viewports) |p_viewports| {
if (viewport_state.viewport_count != 0) {
const p_viewports = viewport_state.p_viewports orelse return VkError.ValidationFailed;
const copy = allocator.dupe(vk.Viewport, p_viewports[0..viewport_state.viewport_count]) catch return VkError.OutOfHostMemory;
viewports = copy;
break :blk viewports;
@@ -154,8 +166,12 @@ pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipe
break :blk null;
},
.scissor = blk: {
if (rasterizer_discard_enable or dynamic_state.scissor) {
break :blk null;
}
if (info.p_viewport_state) |viewport_state| {
if (viewport_state.p_scissors) |p_scissors| {
if (viewport_state.scissor_count != 0) {
const p_scissors = viewport_state.p_scissors orelse return VkError.ValidationFailed;
const copy = allocator.dupe(vk.Rect2D, p_scissors[0..viewport_state.scissor_count]) catch return VkError.OutOfHostMemory;
scissors = copy;
break :blk scissors;
@@ -171,9 +187,17 @@ pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipe
.line_width = if (info.p_rasterization_state) |state| state.line_width else return VkError.ValidationFailed,
},
.color_blend = blk: {
if (rasterizer_discard_enable or !has_color_attachments) {
break :blk .{
.attachments = null,
.constants = .{ 0.0, 0.0, 0.0, 0.0 },
};
}
if (info.p_color_blend_state) |state| {
break :blk .{
.attachments = if (state.p_attachments) |attachments| blk_attachments: {
.attachments = if (state.attachment_count != 0) blk_attachments: {
const attachments = state.p_attachments orelse return VkError.ValidationFailed;
color_attachments = allocator.dupe(vk.PipelineColorBlendAttachmentState, attachments[0..state.attachment_count]) catch return VkError.OutOfHostMemory;
break :blk_attachments color_attachments;
} else null,
@@ -186,12 +210,26 @@ pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipe
.constants = .{ 0.0, 0.0, 0.0, 0.0 },
};
},
.depth_stencil = if (info.p_depth_stencil_state) |state| state.* else null,
.dynamic_state = blk: {
var state: DynamicState = .{};
.depth_stencil = if (rasterizer_discard_enable or !has_depth_stencil_attachment)
null
else if (info.p_depth_stencil_state) |state|
state.*
else
null,
.dynamic_state = dynamic_state,
},
},
};
}
if (info.p_dynamic_state) |dynamic_state| {
if (dynamic_state.p_dynamic_states) |states| {
fn parseDynamicState(info: ?*const vk.PipelineDynamicStateCreateInfo) VkError!DynamicState {
var state: DynamicState = .{};
const dynamic_state = info orelse return state;
if (dynamic_state.dynamic_state_count == 0) {
return state;
}
const states = dynamic_state.p_dynamic_states orelse return VkError.ValidationFailed;
for (states[0..dynamic_state.dynamic_state_count]) |info_state| {
switch (info_state) {
.viewport => state.viewport = true,
@@ -206,13 +244,19 @@ pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipe
else => return VkError.Unknown,
}
}
}
return state;
}
break :blk state;
},
},
},
fn renderPassAttachmentState(info: *const vk.GraphicsPipelineCreateInfo) VkError!struct { bool, bool } {
if (info.render_pass == .null_handle) {
return .{ true, true };
}
const render_pass = try NonDispatchable(RenderPass).fromHandleObject(info.render_pass);
return .{
try render_pass.subpassHasColorAttachments(info.subpass),
try render_pass.subpassHasDepthStencilAttachment(info.subpass),
};
}
+74 -24
View File
@@ -12,7 +12,7 @@ const Self = @This();
pub const ObjectType: vk.ObjectType = .pipeline_cache;
owner: *Device,
data_available: bool,
data: []u8,
vtable: *const VTable,
@@ -24,55 +24,105 @@ pub const Header = vk.PipelineCacheHeaderVersionOne;
pub fn init(device: *Device, allocator: std.mem.Allocator, info: *const vk.PipelineCacheCreateInfo) VkError!Self {
_ = allocator;
const has_initial_data = info.initial_data_size != 0 or info.p_initial_data != null;
if (info.initial_data_size != 0 and info.p_initial_data == null) {
return VkError.ValidationFailed;
}
const initial_data = if (info.p_initial_data) |ptr|
@as([*]const u8, @ptrCast(ptr))[0..info.initial_data_size]
else
&.{};
const data_allocator = device.host_allocator.allocator();
const data = if (isCompatibleInitialData(device, initial_data))
data_allocator.dupe(u8, initial_data) catch return VkError.OutOfHostMemory
else
createEmptyData(device, data_allocator) catch return VkError.OutOfHostMemory;
return .{
.owner = device,
.data_available = !has_initial_data or info.initial_data_size >= dataSize(),
.data = data,
.vtable = undefined,
};
}
pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void {
pub fn destroy(self: *Self, allocator: std.mem.Allocator) void {
self.owner.host_allocator.allocator().free(self.data);
self.vtable.destroy(self, allocator);
}
pub fn merge(self: *Self) VkError!void {
_ = self;
pub fn merge(self: *Self, source: *const Self) VkError!void {
if (source.data.len <= @sizeOf(Header)) {
return;
}
if (!isCompatibleInitialData(self.owner, source.data)) {
return;
}
const old_len = self.data.len;
const source_payload = source.data[@sizeOf(Header)..];
self.data = self.owner.host_allocator.allocator().realloc(self.data, old_len + source_payload.len) catch return VkError.OutOfHostMemory;
@memcpy(self.data[old_len..], source_payload);
}
pub fn appendPayload(self: *Self, payload: []const u8) VkError!void {
if (payload.len == 0) {
return;
}
const old_len = self.data.len;
self.data = self.owner.host_allocator.allocator().realloc(self.data, old_len + payload.len) catch return VkError.OutOfHostMemory;
@memcpy(self.data[old_len..], payload);
}
pub fn getData(self: *Self, data: ?[]u8) vk.Result {
if (!self.data_available) {
return .success;
}
const cache_header = self.header();
const bytes = std.mem.asBytes(&cache_header);
if (data) |dst| {
if (dst.len < bytes.len) {
const written = @min(dst.len, self.data.len);
@memcpy(dst[0..written], self.data[0..written]);
if (written < self.data.len) {
return .incomplete;
}
@memcpy(dst[0..bytes.len], bytes);
}
return .success;
}
pub inline fn dataSize() usize {
return @sizeOf(Header);
}
pub inline fn availableDataSize(self: *const Self) usize {
return if (self.data_available) dataSize() else 0;
return self.data.len;
}
fn header(self: *const Self) Header {
fn makeHeader(device: *const Device) Header {
return .{
.header_size = @sizeOf(Header),
.header_version = .one,
.vendor_id = @intCast(root.VULKAN_VENDOR_ID),
.device_id = self.owner.physical_device.props.device_id,
.pipeline_cache_uuid = self.owner.physical_device.props.pipeline_cache_uuid,
.device_id = device.physical_device.props.device_id,
.pipeline_cache_uuid = device.physical_device.props.pipeline_cache_uuid,
};
}
fn createEmptyData(device: *const Device, allocator: std.mem.Allocator) VkError![]u8 {
const cache_header = makeHeader(device);
return allocator.dupe(u8, std.mem.asBytes(&cache_header)) catch VkError.OutOfHostMemory;
}
fn isCompatibleInitialData(device: *const Device, initial_data: []const u8) bool {
if (initial_data.len == 0) {
return false;
}
if (initial_data.len < @sizeOf(Header)) {
return false;
}
const cache_header = readHeader(initial_data);
const expected = makeHeader(device);
return cache_header.header_size == @sizeOf(Header) and
cache_header.header_version == .one and
cache_header.vendor_id == expected.vendor_id and
cache_header.device_id == expected.device_id and
std.mem.eql(u8, &cache_header.pipeline_cache_uuid, &expected.pipeline_cache_uuid);
}
inline fn readHeader(bytes: []const u8) Header {
return std.mem.bytesToValue(Header, bytes);
}
+22
View File
@@ -98,3 +98,25 @@ pub fn destroy(self: *Self, allocator: std.mem.Allocator) void {
pub inline fn getRenderAreaGranularity(self: *Self) vk.Extent2D {
return self.vtable.getRenderAreaGranularity(self);
}
pub fn subpassHasColorAttachments(self: *const Self, subpass_index: u32) VkError!bool {
if (subpass_index >= self.subpasses.len) {
return VkError.ValidationFailed;
}
const color_attachments = self.subpasses[subpass_index].color_attachments orelse return false;
for (color_attachments) |attachment| {
if (attachment.attachment != vk.ATTACHMENT_UNUSED) {
return true;
}
}
return false;
}
pub fn subpassHasDepthStencilAttachment(self: *const Self, subpass_index: u32) VkError!bool {
if (subpass_index >= self.subpasses.len) {
return VkError.ValidationFailed;
}
return self.subpasses[subpass_index].depth_stencil_attachments != null;
}
+1 -1
View File
@@ -1,4 +1,4 @@
//! Here lies the documentation of the common internal API that backends need to implement
//! Here lies the documentation of the common internal API that Ape backends need to implement
const std = @import("std");
const builtin = @import("builtin");
+5 -7
View File
@@ -1480,12 +1480,10 @@ pub export fn apeGetImageSparseMemoryRequirements(p_device: vk.Device, p_image:
defer entryPointEndLogTrace();
Dispatchable(Device).checkHandleValidity(p_device) catch |err| return errorLogger(err);
NonDispatchable(Image).checkHandleValidity(p_image) catch |err| return errorLogger(err);
const image = NonDispatchable(Image).fromHandleObject(p_image) catch |err| return errorLogger(err);
lib.unsupported("sparse images are not supported", .{});
notImplementedWarning();
_ = image;
_ = requirements;
}
@@ -1511,7 +1509,7 @@ pub export fn apeGetPipelineCacheData(p_device: vk.Device, p_cache: vk.PipelineC
const bytes = @as([*]u8, @ptrCast(ptr))[0..size.*];
break :blk cache.getData(bytes);
} else cache.getData(null);
size.* = if (result == .incomplete) 0 else available;
size.* = if (result == .incomplete) @min(size.*, available) else available;
return result;
}
@@ -1578,10 +1576,10 @@ pub export fn apeMergePipelineCaches(p_device: vk.Device, p_dst: vk.PipelineCach
const dst = NonDispatchable(PipelineCache).fromHandleObject(p_dst) catch |err| return toVkResult(err);
for (0..count) |i| {
_ = NonDispatchable(PipelineCache).fromHandleObject(p_srcs[i]) catch |err| return toVkResult(err);
const src = NonDispatchable(PipelineCache).fromHandleObject(p_srcs[i]) catch |err| return toVkResult(err);
dst.merge(src) catch |err| return toVkResult(err);
}
dst.merge() catch |err| return toVkResult(err);
return .success;
}