implementing proper pipeline cache
This commit is contained in:
@@ -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
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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}),
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user