implementing proper pipeline cache
This commit is contained in:
@@ -113,7 +113,7 @@ vkCreateGraphicsPipelines | ✅ Implemented
|
|||||||
vkCreateImage | ✅ Implemented
|
vkCreateImage | ✅ Implemented
|
||||||
vkCreateImageView | ✅ Implemented
|
vkCreateImageView | ✅ Implemented
|
||||||
vkCreateInstance | ✅ Implemented
|
vkCreateInstance | ✅ Implemented
|
||||||
vkCreatePipelineCache | ⚙️ WIP
|
vkCreatePipelineCache | ✅ Implemented
|
||||||
vkCreatePipelineLayout | ✅ Implemented
|
vkCreatePipelineLayout | ✅ Implemented
|
||||||
vkCreateQueryPool | ✅ Implemented
|
vkCreateQueryPool | ✅ Implemented
|
||||||
vkCreateRenderPass | ✅ Implemented
|
vkCreateRenderPass | ✅ Implemented
|
||||||
@@ -184,13 +184,13 @@ vkGetPhysicalDeviceWaylandPresentationSupportKHR | ✅ Implemented
|
|||||||
vkGetPhysicalDeviceWin32PresentationSupportKHR | ⚙️ WIP
|
vkGetPhysicalDeviceWin32PresentationSupportKHR | ⚙️ WIP
|
||||||
vkGetPhysicalDeviceXcbPresentationSupportKHR | ⚙️ WIP
|
vkGetPhysicalDeviceXcbPresentationSupportKHR | ⚙️ WIP
|
||||||
vkGetPhysicalDeviceXlibPresentationSupportKHR | ⚙️ WIP
|
vkGetPhysicalDeviceXlibPresentationSupportKHR | ⚙️ WIP
|
||||||
vkGetPipelineCacheData | ⚙️ WIP
|
vkGetPipelineCacheData | ✅ Implemented
|
||||||
vkGetQueryPoolResults | ✅ Implemented
|
vkGetQueryPoolResults | ✅ Implemented
|
||||||
vkGetRenderAreaGranularity | ✅ Implemented
|
vkGetRenderAreaGranularity | ✅ Implemented
|
||||||
vkGetSwapchainImagesKHR | ✅ Implemented
|
vkGetSwapchainImagesKHR | ✅ Implemented
|
||||||
vkInvalidateMappedMemoryRanges | ✅ Implemented
|
vkInvalidateMappedMemoryRanges | ✅ Implemented
|
||||||
vkMapMemory | ✅ Implemented
|
vkMapMemory | ✅ Implemented
|
||||||
vkMergePipelineCaches | ⚙️ WIP
|
vkMergePipelineCaches | ✅ Implemented
|
||||||
vkQueueBindSparse | ❎ Unsupported
|
vkQueueBindSparse | ❎ Unsupported
|
||||||
vkQueuePresentKHR | ✅ Implemented
|
vkQueuePresentKHR | ✅ Implemented
|
||||||
vkQueueSubmit | ✅ Implemented
|
vkQueueSubmit | ✅ Implemented
|
||||||
|
|||||||
+2
-2
@@ -26,8 +26,8 @@
|
|||||||
.hash = "N-V-__8AAF9uOh0I4P_99za7N822J3JwsDaqONrFVrcEQo59",
|
.hash = "N-V-__8AAF9uOh0I4P_99za7N822J3JwsDaqONrFVrcEQo59",
|
||||||
},
|
},
|
||||||
.SPIRV_Interpreter = .{
|
.SPIRV_Interpreter = .{
|
||||||
.url = "git+https://git.kbz8.me/kbz_8/SPIRV-Interpreter#c7320325fdf6b1e5e6371ff620ed08b926587eed",
|
.url = "git+https://git.kbz8.me/kbz_8/SPIRV-Interpreter#5c1eb25b4835f4161dd49cd000b19ae1cca6e80a",
|
||||||
.hash = "SPIRV_Interpreter-0.0.1-ajmpn5F_BwBQHPDX6S9rSgLls4FYjpdxGD30B17paW7L",
|
.hash = "SPIRV_Interpreter-0.0.1-ajmpn8mDBwChlKPOGs3EkJl6RiTJYDcvpLbsIJaHBAOe",
|
||||||
.lazy = true,
|
.lazy = true,
|
||||||
},
|
},
|
||||||
//.SPIRV_Interpreter = .{
|
//.SPIRV_Interpreter = .{
|
||||||
|
|||||||
+121
-92
@@ -22,6 +22,7 @@ const SoftImageView = @import("SoftImageView.zig");
|
|||||||
const SoftInstance = @import("SoftInstance.zig");
|
const SoftInstance = @import("SoftInstance.zig");
|
||||||
const SoftSampler = @import("SoftSampler.zig");
|
const SoftSampler = @import("SoftSampler.zig");
|
||||||
const SoftShaderModule = @import("SoftShaderModule.zig");
|
const SoftShaderModule = @import("SoftShaderModule.zig");
|
||||||
|
const SoftPipelineCache = @import("SoftPipelineCache.zig");
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
pub const Interface = base.Pipeline;
|
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 soft_module: *SoftShaderModule = @alignCast(@fieldParentPtr("interface", module));
|
||||||
|
|
||||||
const device_allocator = soft_device.device_allocator.allocator();
|
const device_allocator = soft_device.device_allocator.allocator();
|
||||||
|
const soft_cache: ?*SoftPipelineCache = if (cache) |pipeline_cache|
|
||||||
|
@alignCast(@fieldParentPtr("interface", pipeline_cache))
|
||||||
|
else
|
||||||
|
null;
|
||||||
|
|
||||||
self.* = .{
|
self.* = .{
|
||||||
.interface = interface,
|
.interface = interface,
|
||||||
@@ -85,56 +90,8 @@ pub fn createCompute(device: *base.Device, allocator: std.mem.Allocator, cache:
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
self.stages.put(.compute, blk: {
|
self.stages.put(.compute, try createShader(allocator, device_allocator, runtimes_allocator, soft_cache, soft_module, &info.stage, runtimes_count));
|
||||||
var shader: Shader = undefined;
|
std.log.scoped(.ComputePipeline).debug("Created {d} runtimes for compute stage", .{runtimes_count});
|
||||||
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;
|
|
||||||
|
|
||||||
std.log.scoped(.ComputePipeline).debug("Created {d} runtimes for compute stage", .{runtimes_count});
|
|
||||||
break :blk shader;
|
|
||||||
});
|
|
||||||
return self;
|
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 soft_device: *SoftDevice = @alignCast(@fieldParentPtr("interface", device));
|
||||||
const device_allocator = soft_device.device_allocator.allocator();
|
const device_allocator = soft_device.device_allocator.allocator();
|
||||||
|
const soft_cache: ?*SoftPipelineCache = if (cache) |pipeline_cache|
|
||||||
|
@alignCast(@fieldParentPtr("interface", pipeline_cache))
|
||||||
|
else
|
||||||
|
null;
|
||||||
|
|
||||||
self.* = .{
|
self.* = .{
|
||||||
.interface = interface,
|
.interface = interface,
|
||||||
@@ -172,50 +133,9 @@ pub fn createGraphics(device: *base.Device, allocator: std.mem.Allocator, cache:
|
|||||||
|
|
||||||
if (info.p_stages) |stages| {
|
if (info.p_stages) |stages| {
|
||||||
for (stages[0..], 0..info.stage_count) |stage, _| {
|
for (stages[0..], 0..info.stage_count) |stage, _| {
|
||||||
var shader: Shader = undefined;
|
|
||||||
|
|
||||||
const module = try NonDispatchable(ShaderModule).fromHandleObject(stage.module);
|
const module = try NonDispatchable(ShaderModule).fromHandleObject(stage.module);
|
||||||
const soft_module: *SoftShaderModule = @alignCast(@fieldParentPtr("interface", module));
|
const soft_module: *SoftShaderModule = @alignCast(@fieldParentPtr("interface", module));
|
||||||
soft_module.ref();
|
const shader = try createShader(allocator, device_allocator, runtimes_allocator, soft_cache, soft_module, &stage, runtimes_count);
|
||||||
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;
|
|
||||||
|
|
||||||
std.log.scoped(.GraphicsPipeline).debug("Created {d} runtimes for:", .{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);
|
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) {
|
fn readImageFloat4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32) SpvRuntimeError!spv.Runtime.Vec4(f32) {
|
||||||
var pixel = zm.f32x4s(0.0);
|
var pixel = zm.f32x4s(0.0);
|
||||||
if (dim == .Buffer) {
|
if (dim == .Buffer) {
|
||||||
|
|||||||
@@ -1,14 +1,50 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const vk = @import("vulkan");
|
const vk = @import("vulkan");
|
||||||
const base = @import("base");
|
const base = @import("base");
|
||||||
|
const spv = @import("spv");
|
||||||
|
|
||||||
const VkError = base.VkError;
|
const VkError = base.VkError;
|
||||||
const Device = base.Device;
|
const SoftDevice = @import("SoftDevice.zig");
|
||||||
|
const SoftShaderModule = @import("SoftShaderModule.zig");
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
pub const Interface = base.PipelineCache;
|
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,
|
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 {
|
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;
|
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.* = .{
|
self.* = .{
|
||||||
.interface = interface,
|
.interface = interface,
|
||||||
|
.mutex = .init,
|
||||||
|
.shaders = .empty,
|
||||||
};
|
};
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void {
|
pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void {
|
||||||
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
|
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);
|
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 {
|
pub const DrawCall = struct {
|
||||||
renderer: *Self,
|
renderer: *Self,
|
||||||
vertices: []Vertex,
|
vertices: []Vertex,
|
||||||
|
vertex_count: usize,
|
||||||
|
instance_count: usize,
|
||||||
|
|
||||||
viewport: vk.Viewport,
|
viewport: vk.Viewport,
|
||||||
scissor: vk.Rect2D,
|
scissor: vk.Rect2D,
|
||||||
@@ -87,6 +89,8 @@ pub const DrawCall = struct {
|
|||||||
|
|
||||||
const self: @This() = .{
|
const self: @This() = .{
|
||||||
.vertices = allocator.alloc(Vertex, vertex_count * instance_count) catch return VkError.OutOfDeviceMemory,
|
.vertices = allocator.alloc(Vertex, vertex_count * instance_count) catch return VkError.OutOfDeviceMemory,
|
||||||
|
.vertex_count = vertex_count,
|
||||||
|
.instance_count = instance_count,
|
||||||
.renderer = renderer,
|
.renderer = renderer,
|
||||||
.viewport = undefined,
|
.viewport = undefined,
|
||||||
.scissor = 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);
|
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 {
|
pub fn readFloat4(map: []const u8, src_format: vk.Format) F32x4 {
|
||||||
var c: F32x4 = .{ 0.0, 0.0, 0.0, 1.0 };
|
var c: F32x4 = .{ 0.0, 0.0, 0.0, 1.0 };
|
||||||
|
|
||||||
switch (src_format) {
|
switch (src_format) {
|
||||||
|
.r8_uscaled => c[0] = @floatFromInt(map[0]),
|
||||||
|
|
||||||
.r8_uint,
|
.r8_uint,
|
||||||
.r8_unorm,
|
.r8_unorm,
|
||||||
.r8_srgb,
|
.r8_srgb,
|
||||||
=> c[0] = @as(f32, @floatFromInt(map[0])) / std.math.maxInt(u8),
|
=> c[0] = @as(f32, @floatFromInt(map[0])) / std.math.maxInt(u8),
|
||||||
|
|
||||||
|
.r8_sscaled => c[0] = @floatFromInt(@as(i8, @bitCast(map[0]))),
|
||||||
|
|
||||||
.r8_sint,
|
.r8_sint,
|
||||||
.r8_snorm,
|
.r8_snorm,
|
||||||
=> c[0] = normalizedI8(map[0]),
|
=> 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_snorm => c[0] = normalizedI16(std.mem.bytesToValue(u16, map)),
|
||||||
.r16_unorm,
|
.r16_unorm,
|
||||||
.d16_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);
|
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_uint,
|
||||||
.r8g8_unorm,
|
.r8g8_unorm,
|
||||||
.r8g8_srgb,
|
.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);
|
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_sint,
|
||||||
.r8g8_snorm,
|
.r8g8_snorm,
|
||||||
=> {
|
=> {
|
||||||
@@ -607,6 +635,20 @@ pub fn readFloat4(map: []const u8, src_format: vk.Format) F32x4 {
|
|||||||
c[1] = normalizedI8(map[1]);
|
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 => {
|
.r8g8b8a8_snorm => {
|
||||||
c[0] = normalizedI8(map[0]);
|
c[0] = normalizedI8(map[0]);
|
||||||
c[1] = normalizedI8(map[1]);
|
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),
|
.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_sint,
|
||||||
.r16g16_uint,
|
.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..]));
|
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 => {
|
.r16g16_snorm => {
|
||||||
c[0] = normalizedI16(std.mem.bytesToValue(u16, map[0..]));
|
c[0] = normalizedI16(std.mem.bytesToValue(u16, map[0..]));
|
||||||
c[1] = normalizedI16(std.mem.bytesToValue(u16, map[2..]));
|
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);
|
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_sint,
|
||||||
.r16g16b16a16_snorm,
|
.r16g16b16a16_snorm,
|
||||||
=> {
|
=> {
|
||||||
@@ -749,6 +815,22 @@ pub fn readFloat4(map: []const u8, src_format: vk.Format) F32x4 {
|
|||||||
c[3] = normalizedI8(pack[3]);
|
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_uint_pack32,
|
||||||
.a2b10g10r10_unorm_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);
|
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_uint_pack32,
|
||||||
.a2r10g10b10_unorm_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);
|
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 => {
|
.r5g6b5_unorm_pack16 => {
|
||||||
const pack = std.mem.bytesToValue(u16, map);
|
const pack = std.mem.bytesToValue(u16, map);
|
||||||
c[0] = @as(f32, @floatFromInt((pack & 0xF800) >> 11)) / std.math.maxInt(u5);
|
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;
|
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_unorm_pack32,
|
||||||
.a2r10g10b10_uint_pack32,
|
.a2r10g10b10_uint_pack32,
|
||||||
=> {
|
=> {
|
||||||
@@ -1277,6 +1415,14 @@ pub fn readInt4(map: []const u8, src_format: vk.Format) U32x4 {
|
|||||||
c[3] = (pack & 0xC0000000) >> 30;
|
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}),
|
else => base.unsupported("Blitter: read int from source format {any}", .{src_format}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -121,41 +121,50 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
|
|||||||
};
|
};
|
||||||
|
|
||||||
switch (topology) {
|
switch (topology) {
|
||||||
.point_list => for (draw_call.vertices) |*vertex| {
|
.point_list => for (0..draw_call.instance_count) |instance_index| {
|
||||||
if (vertex.primitive_restart)
|
const range = instanceVertexRange(draw_call, instance_index);
|
||||||
continue;
|
for (draw_call.vertices[range.start..range.end]) |*vertex| {
|
||||||
|
if (vertex.primitive_restart)
|
||||||
|
continue;
|
||||||
|
|
||||||
try clipTransformAndRasterizePoint(
|
try clipTransformAndRasterizePoint(
|
||||||
allocator,
|
allocator,
|
||||||
draw_call,
|
draw_call,
|
||||||
vertex,
|
vertex,
|
||||||
color_attachment_access,
|
color_attachment_access,
|
||||||
if (depth_attachment_access) |*access| access else null,
|
if (depth_attachment_access) |*access| access else null,
|
||||||
if (stencil_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| {
|
.triangle_list => for (0..draw_call.instance_count) |instance_index| {
|
||||||
const first_vertex = triangle_index * 3;
|
const range = instanceVertexRange(draw_call, instance_index);
|
||||||
const v0 = &draw_call.vertices[first_vertex + 0];
|
const vertex_count = range.end - range.start;
|
||||||
const v1 = &draw_call.vertices[first_vertex + 1];
|
for (0..@divTrunc(vertex_count, 3)) |triangle_index| {
|
||||||
const v2 = &draw_call.vertices[first_vertex + 2];
|
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];
|
||||||
|
|
||||||
try clipTransformAndRasterizeTriangle(
|
try clipTransformAndRasterizeTriangle(
|
||||||
renderer,
|
renderer,
|
||||||
allocator,
|
allocator,
|
||||||
draw_call,
|
draw_call,
|
||||||
v0,
|
v0,
|
||||||
v1,
|
v1,
|
||||||
v2,
|
v2,
|
||||||
color_attachment_access,
|
color_attachment_access,
|
||||||
if (depth_attachment_access) |*access| access else null,
|
if (depth_attachment_access) |*access| access else null,
|
||||||
if (stencil_attachment_access) |*access| access else null,
|
if (stencil_attachment_access) |*access| access else null,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
.triangle_fan => {
|
.triangle_fan => {
|
||||||
var segment_start = firstNonRestart(draw_call, 0);
|
for (0..draw_call.instance_count) |instance_index| {
|
||||||
while (segment_start < draw_call.vertices.len) {
|
const range = instanceVertexRange(draw_call, instance_index);
|
||||||
const segment_end = nextRestart(draw_call, segment_start);
|
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) {
|
if (segment_end - segment_start >= 3) {
|
||||||
const v0 = &draw_call.vertices[segment_start];
|
const v0 = &draw_call.vertices[segment_start];
|
||||||
for ((segment_start + 1)..(segment_end - 1)) |vertex_index| {
|
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 => {
|
.triangle_strip => {
|
||||||
var segment_start = firstNonRestart(draw_call, 0);
|
for (0..draw_call.instance_count) |instance_index| {
|
||||||
while (segment_start < draw_call.vertices.len) {
|
const range = instanceVertexRange(draw_call, instance_index);
|
||||||
const segment_end = nextRestart(draw_call, segment_start);
|
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) {
|
if (segment_end - segment_start >= 3) {
|
||||||
for (segment_start..(segment_end - 2)) |vertex_index| {
|
for (segment_start..(segment_end - 2)) |vertex_index| {
|
||||||
const local_index = vertex_index - segment_start;
|
const local_index = vertex_index - segment_start;
|
||||||
@@ -216,28 +228,35 @@ 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| {
|
.line_list => for (0..draw_call.instance_count) |instance_index| {
|
||||||
const first_vertex = line_index * 2;
|
const range = instanceVertexRange(draw_call, instance_index);
|
||||||
const v0 = &draw_call.vertices[first_vertex + 0];
|
const vertex_count = range.end - range.start;
|
||||||
const v1 = &draw_call.vertices[first_vertex + 1];
|
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];
|
||||||
|
|
||||||
try clipTransformAndRasterizeLine(
|
try clipTransformAndRasterizeLine(
|
||||||
allocator,
|
allocator,
|
||||||
draw_call,
|
draw_call,
|
||||||
v0,
|
v0,
|
||||||
v1,
|
v1,
|
||||||
color_attachment_access,
|
color_attachment_access,
|
||||||
if (depth_attachment_access) |*access| access else null,
|
if (depth_attachment_access) |*access| access else null,
|
||||||
if (stencil_attachment_access) |*access| access else null,
|
if (stencil_attachment_access) |*access| access else null,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
.line_strip => {
|
.line_strip => {
|
||||||
var segment_start = firstNonRestart(draw_call, 0);
|
for (0..draw_call.instance_count) |instance_index| {
|
||||||
while (segment_start < draw_call.vertices.len) {
|
const range = instanceVertexRange(draw_call, instance_index);
|
||||||
const segment_end = nextRestart(draw_call, segment_start);
|
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) {
|
if (segment_end - segment_start >= 2) {
|
||||||
for (segment_start..(segment_end - 1)) |vertex_index| {
|
for (segment_start..(segment_end - 1)) |vertex_index| {
|
||||||
const v0 = &draw_call.vertices[vertex_index + 0];
|
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}),
|
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;
|
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;
|
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;
|
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;
|
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;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,13 +75,21 @@ inline fn run(data: RunData) !void {
|
|||||||
|
|
||||||
if (data.pipeline.interface.mode.graphics.input_assembly.attribute_description) |attributes| {
|
if (data.pipeline.interface.mode.graphics.input_assembly.attribute_description) |attributes| {
|
||||||
for (attributes) |attribute| {
|
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 vertex_buffer = data.draw_call.renderer.state.data.graphics.vertex_buffers[attribute.binding];
|
||||||
const buffer = vertex_buffer.buffer;
|
const buffer = vertex_buffer.buffer;
|
||||||
const buffer_memory_size = base.format.texelSize(attribute.format);
|
const buffer_memory_size = base.format.texelSize(attribute.format);
|
||||||
const buffer_memory = if (buffer.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv;
|
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);
|
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) {
|
rt.callEntryPoint(data.allocator, entry) catch |err| switch (err) {
|
||||||
// Some errors can be safely ignored
|
// Some errors can be safely ignored
|
||||||
SpvRuntimeError.OutOfBounds => {},
|
|
||||||
SpvRuntimeError.Killed => {
|
SpvRuntimeError.Killed => {
|
||||||
try rt.flushDescriptorSets(data.allocator);
|
try rt.flushDescriptorSets(data.allocator);
|
||||||
return;
|
return;
|
||||||
@@ -99,7 +106,7 @@ inline fn run(data: RunData) !void {
|
|||||||
else => return err,
|
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..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
|
||||||
for (0..4) |component| {
|
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 {
|
fn setupBuiltins(rt: *spv.Runtime, vertex_index_u32: u32, instance_index: usize) !void {
|
||||||
const instance_index_u32: u32 = @intCast(instance_index);
|
const instance_index_u32: u32 = @intCast(instance_index);
|
||||||
|
|
||||||
|
|||||||
+72
-28
@@ -8,6 +8,7 @@ const VkError = @import("error_set.zig").VkError;
|
|||||||
const Device = @import("Device.zig");
|
const Device = @import("Device.zig");
|
||||||
const PipelineCache = @import("PipelineCache.zig");
|
const PipelineCache = @import("PipelineCache.zig");
|
||||||
const PipelineLayout = @import("PipelineLayout.zig");
|
const PipelineLayout = @import("PipelineLayout.zig");
|
||||||
|
const RenderPass = @import("RenderPass.zig");
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
pub const ObjectType: vk.ObjectType = .pipeline;
|
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;
|
var color_attachments: ?[]vk.PipelineColorBlendAttachmentState = null;
|
||||||
errdefer if (color_attachments) |value| allocator.free(value);
|
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 .{
|
return .{
|
||||||
.owner = device,
|
.owner = device,
|
||||||
.vtable = undefined,
|
.vtable = undefined,
|
||||||
@@ -144,8 +152,12 @@ pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipe
|
|||||||
},
|
},
|
||||||
.viewport_state = .{
|
.viewport_state = .{
|
||||||
.viewports = blk: {
|
.viewports = blk: {
|
||||||
|
if (rasterizer_discard_enable or dynamic_state.viewport) {
|
||||||
|
break :blk null;
|
||||||
|
}
|
||||||
if (info.p_viewport_state) |viewport_state| {
|
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;
|
const copy = allocator.dupe(vk.Viewport, p_viewports[0..viewport_state.viewport_count]) catch return VkError.OutOfHostMemory;
|
||||||
viewports = copy;
|
viewports = copy;
|
||||||
break :blk viewports;
|
break :blk viewports;
|
||||||
@@ -154,8 +166,12 @@ pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipe
|
|||||||
break :blk null;
|
break :blk null;
|
||||||
},
|
},
|
||||||
.scissor = blk: {
|
.scissor = blk: {
|
||||||
|
if (rasterizer_discard_enable or dynamic_state.scissor) {
|
||||||
|
break :blk null;
|
||||||
|
}
|
||||||
if (info.p_viewport_state) |viewport_state| {
|
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;
|
const copy = allocator.dupe(vk.Rect2D, p_scissors[0..viewport_state.scissor_count]) catch return VkError.OutOfHostMemory;
|
||||||
scissors = copy;
|
scissors = copy;
|
||||||
break :blk scissors;
|
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,
|
.line_width = if (info.p_rasterization_state) |state| state.line_width else return VkError.ValidationFailed,
|
||||||
},
|
},
|
||||||
.color_blend = blk: {
|
.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| {
|
if (info.p_color_blend_state) |state| {
|
||||||
break :blk .{
|
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;
|
color_attachments = allocator.dupe(vk.PipelineColorBlendAttachmentState, attachments[0..state.attachment_count]) catch return VkError.OutOfHostMemory;
|
||||||
break :blk_attachments color_attachments;
|
break :blk_attachments color_attachments;
|
||||||
} else null,
|
} else null,
|
||||||
@@ -186,36 +210,56 @@ pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipe
|
|||||||
.constants = .{ 0.0, 0.0, 0.0, 0.0 },
|
.constants = .{ 0.0, 0.0, 0.0, 0.0 },
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
.depth_stencil = if (info.p_depth_stencil_state) |state| state.* else null,
|
.depth_stencil = if (rasterizer_discard_enable or !has_depth_stencil_attachment)
|
||||||
.dynamic_state = blk: {
|
null
|
||||||
var state: DynamicState = .{};
|
else if (info.p_depth_stencil_state) |state|
|
||||||
|
state.*
|
||||||
if (info.p_dynamic_state) |dynamic_state| {
|
else
|
||||||
if (dynamic_state.p_dynamic_states) |states| {
|
null,
|
||||||
for (states[0..dynamic_state.dynamic_state_count]) |info_state| {
|
.dynamic_state = dynamic_state,
|
||||||
switch (info_state) {
|
|
||||||
.viewport => state.viewport = true,
|
|
||||||
.scissor => state.scissor = true,
|
|
||||||
.line_width => state.line_width = true,
|
|
||||||
.depth_bias => state.depth_bias = true,
|
|
||||||
.blend_constants => state.blend_constants = true,
|
|
||||||
.depth_bounds => state.depth_bounds = true,
|
|
||||||
.stencil_compare_mask => state.stencil_compare_mask = true,
|
|
||||||
.stencil_write_mask => state.stencil_write_mask = true,
|
|
||||||
.stencil_reference => state.stencil_reference = true,
|
|
||||||
else => return VkError.Unknown,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break :blk state;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
.scissor => state.scissor = true,
|
||||||
|
.line_width => state.line_width = true,
|
||||||
|
.depth_bias => state.depth_bias = true,
|
||||||
|
.blend_constants => state.blend_constants = true,
|
||||||
|
.depth_bounds => state.depth_bounds = true,
|
||||||
|
.stencil_compare_mask => state.stencil_compare_mask = true,
|
||||||
|
.stencil_write_mask => state.stencil_write_mask = true,
|
||||||
|
.stencil_reference => state.stencil_reference = true,
|
||||||
|
else => return VkError.Unknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 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),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void {
|
pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void {
|
||||||
switch (self.mode) {
|
switch (self.mode) {
|
||||||
.compute => {},
|
.compute => {},
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const Self = @This();
|
|||||||
pub const ObjectType: vk.ObjectType = .pipeline_cache;
|
pub const ObjectType: vk.ObjectType = .pipeline_cache;
|
||||||
|
|
||||||
owner: *Device,
|
owner: *Device,
|
||||||
data_available: bool,
|
data: []u8,
|
||||||
|
|
||||||
vtable: *const VTable,
|
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 {
|
pub fn init(device: *Device, allocator: std.mem.Allocator, info: *const vk.PipelineCacheCreateInfo) VkError!Self {
|
||||||
_ = allocator;
|
_ = 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 .{
|
return .{
|
||||||
.owner = device,
|
.owner = device,
|
||||||
.data_available = !has_initial_data or info.initial_data_size >= dataSize(),
|
.data = data,
|
||||||
.vtable = undefined,
|
.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);
|
self.vtable.destroy(self, allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn merge(self: *Self) VkError!void {
|
pub fn merge(self: *Self, source: *const Self) VkError!void {
|
||||||
_ = self;
|
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 {
|
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 (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;
|
return .incomplete;
|
||||||
}
|
}
|
||||||
|
|
||||||
@memcpy(dst[0..bytes.len], bytes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return .success;
|
return .success;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn dataSize() usize {
|
|
||||||
return @sizeOf(Header);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn availableDataSize(self: *const Self) usize {
|
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 .{
|
return .{
|
||||||
.header_size = @sizeOf(Header),
|
.header_size = @sizeOf(Header),
|
||||||
.header_version = .one,
|
.header_version = .one,
|
||||||
.vendor_id = @intCast(root.VULKAN_VENDOR_ID),
|
.vendor_id = @intCast(root.VULKAN_VENDOR_ID),
|
||||||
.device_id = self.owner.physical_device.props.device_id,
|
.device_id = device.physical_device.props.device_id,
|
||||||
.pipeline_cache_uuid = self.owner.physical_device.props.pipeline_cache_uuid,
|
.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 {
|
pub inline fn getRenderAreaGranularity(self: *Self) vk.Extent2D {
|
||||||
return self.vtable.getRenderAreaGranularity(self);
|
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 std = @import("std");
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
|
|||||||
@@ -1480,12 +1480,10 @@ pub export fn apeGetImageSparseMemoryRequirements(p_device: vk.Device, p_image:
|
|||||||
defer entryPointEndLogTrace();
|
defer entryPointEndLogTrace();
|
||||||
|
|
||||||
Dispatchable(Device).checkHandleValidity(p_device) catch |err| return errorLogger(err);
|
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;
|
_ = 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.*];
|
const bytes = @as([*]u8, @ptrCast(ptr))[0..size.*];
|
||||||
break :blk cache.getData(bytes);
|
break :blk cache.getData(bytes);
|
||||||
} else cache.getData(null);
|
} else cache.getData(null);
|
||||||
size.* = if (result == .incomplete) 0 else available;
|
size.* = if (result == .incomplete) @min(size.*, available) else available;
|
||||||
return result;
|
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);
|
const dst = NonDispatchable(PipelineCache).fromHandleObject(p_dst) catch |err| return toVkResult(err);
|
||||||
|
|
||||||
for (0..count) |i| {
|
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;
|
return .success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user