adding fragment derivatives, base pipeline cache, missing KHR function to avoid CTS crash + lots of minor bugfixes
This commit is contained in:
@@ -104,8 +104,8 @@ pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void {
|
||||
allocator.destroy(self);
|
||||
}
|
||||
|
||||
pub fn execute(self: *Self, device: *ExecutionDevice) void {
|
||||
self.interface.submit() catch return;
|
||||
pub fn execute(self: *Self, device: *ExecutionDevice) VkError!void {
|
||||
try self.interface.submit();
|
||||
defer self.interface.finish() catch {};
|
||||
|
||||
for (self.commands.items) |command| {
|
||||
@@ -116,7 +116,7 @@ pub fn execute(self: *Self, device: *ExecutionDevice) void {
|
||||
std.debug.dumpErrorReturnTrace(trace);
|
||||
}
|
||||
}
|
||||
return; // Should we return or continue ? Maybe device lost ?
|
||||
return VkError.DeviceLost;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -988,7 +988,7 @@ pub fn executeCommands(interface: *Interface, commands: *Interface) VkError!void
|
||||
|
||||
pub fn execute(context: *anyopaque, device: *ExecutionDevice) VkError!void {
|
||||
const impl: *Impl = @ptrCast(@alignCast(context));
|
||||
impl.cmd.execute(device);
|
||||
try impl.cmd.execute(device);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -46,6 +46,10 @@ pub fn allocateDescriptorSet(interface: *Interface, layout: *base.DescriptorSetL
|
||||
|
||||
pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void {
|
||||
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
|
||||
const set_allocator = VulkanAllocator.init(null, .object).allocator();
|
||||
for (self.list.items) |set| {
|
||||
set.interface.destroy(set_allocator);
|
||||
}
|
||||
self.list.deinit(allocator);
|
||||
allocator.destroy(self);
|
||||
}
|
||||
@@ -59,7 +63,7 @@ pub fn freeDescriptorSet(interface: *Interface, set: *base.DescriptorSet) VkErro
|
||||
}
|
||||
|
||||
const allocator = VulkanAllocator.init(null, .object).allocator();
|
||||
allocator.destroy(soft_set);
|
||||
set.destroy(allocator);
|
||||
}
|
||||
|
||||
pub fn reset(interface: *Interface, _: vk.DescriptorPoolResetFlags) VkError!void {
|
||||
@@ -67,7 +71,7 @@ pub fn reset(interface: *Interface, _: vk.DescriptorPoolResetFlags) VkError!void
|
||||
const allocator = VulkanAllocator.init(null, .object).allocator();
|
||||
|
||||
for (self.list.items) |set| {
|
||||
allocator.destroy(set);
|
||||
set.interface.destroy(allocator);
|
||||
}
|
||||
self.list.clearRetainingCapacity();
|
||||
}
|
||||
|
||||
@@ -46,7 +46,14 @@ device_allocator: if (config.debug_allocator) std.heap.DebugAllocator(.{}) else
|
||||
|
||||
pub fn create(instance: *base.Instance, physical_device: *base.PhysicalDevice, allocator: std.mem.Allocator, info: *const vk.DeviceCreateInfo) VkError!*Self {
|
||||
const self = allocator.create(Self) catch return VkError.OutOfHostMemory;
|
||||
errdefer allocator.destroy(self);
|
||||
var initialized = false;
|
||||
errdefer {
|
||||
if (initialized) {
|
||||
self.interface.destroy(allocator) catch {};
|
||||
} else {
|
||||
allocator.destroy(self);
|
||||
}
|
||||
}
|
||||
|
||||
var interface = try Interface.init(allocator, instance, physical_device, info);
|
||||
|
||||
@@ -83,6 +90,7 @@ pub fn create(instance: *base.Instance, physical_device: *base.PhysicalDevice, a
|
||||
.interface = interface,
|
||||
.device_allocator = if (config.debug_allocator) .init else .{},
|
||||
};
|
||||
initialized = true;
|
||||
|
||||
try self.interface.createQueues(allocator, info);
|
||||
return self;
|
||||
|
||||
@@ -25,6 +25,7 @@ fn castExtension(comptime ext: vk.ApiInfo) vk.ExtensionProperties {
|
||||
}
|
||||
|
||||
pub const EXTENSIONS = [_]vk.ExtensionProperties{
|
||||
castExtension(vk.extensions.khr_device_group_creation),
|
||||
castExtension(vk.extensions.khr_get_physical_device_properties_2),
|
||||
castExtension(vk.extensions.khr_surface),
|
||||
castExtension(vk.extensions.khr_wayland_surface),
|
||||
@@ -60,13 +61,16 @@ fn requestPhysicalDevices(interface: *Interface, allocator: std.mem.Allocator) V
|
||||
// Software driver has only one physical device (the CPU)
|
||||
const physical_device = try SoftPhysicalDevice.create(allocator, interface);
|
||||
errdefer physical_device.interface.releasePhysicalDevice(allocator) catch {};
|
||||
interface.physical_devices.append(allocator, try Dispatchable(base.PhysicalDevice).wrap(allocator, &physical_device.interface)) catch return VkError.OutOfHostMemory;
|
||||
const dispatchable = try Dispatchable(base.PhysicalDevice).wrap(allocator, &physical_device.interface);
|
||||
errdefer dispatchable.destroy(allocator);
|
||||
interface.physical_devices.append(allocator, dispatchable) catch return VkError.OutOfHostMemory;
|
||||
}
|
||||
|
||||
fn releasePhysicalDevices(interface: *Interface, allocator: std.mem.Allocator) VkError!void {
|
||||
const physical_device = interface.physical_devices.getLast();
|
||||
try physical_device.object.releasePhysicalDevice(allocator);
|
||||
physical_device.destroy(allocator);
|
||||
for (interface.physical_devices.items) |physical_device| {
|
||||
try physical_device.object.releasePhysicalDevice(allocator);
|
||||
physical_device.destroy(allocator);
|
||||
}
|
||||
|
||||
interface.physical_devices.deinit(allocator);
|
||||
interface.physical_devices = .empty;
|
||||
|
||||
@@ -22,7 +22,7 @@ fn castExtension(comptime ext: vk.ApiInfo) vk.ExtensionProperties {
|
||||
return props;
|
||||
}
|
||||
|
||||
const EXTENSIONS = [_]vk.ExtensionProperties{
|
||||
pub const EXTENSIONS = [_]vk.ExtensionProperties{
|
||||
castExtension(vk.extensions.khr_swapchain),
|
||||
};
|
||||
|
||||
@@ -59,6 +59,7 @@ pub fn create(allocator: std.mem.Allocator, instance: *base.Instance) VkError!*S
|
||||
interface.props.driver_version = @bitCast(lib.DRIVER_VERSION);
|
||||
interface.props.device_id = lib.DEVICE_ID;
|
||||
interface.props.device_type = .cpu;
|
||||
interface.props.pipeline_cache_uuid = lib.PIPELINE_CACHE_UUID;
|
||||
interface.props.limits = .{
|
||||
.max_image_dimension_1d = 4096,
|
||||
.max_image_dimension_2d = 4096,
|
||||
@@ -203,7 +204,6 @@ pub fn create(allocator: std.mem.Allocator, instance: *base.Instance) VkError!*S
|
||||
|
||||
if (device_name[0] == 0) {
|
||||
const name = blk: {
|
||||
|
||||
// If arch is x86 we try to get precise CPU name through CPUID
|
||||
// and fallback to vendor name if not available
|
||||
if (comptime builtin.cpu.arch.isX86()) {
|
||||
@@ -251,6 +251,7 @@ pub fn create(allocator: std.mem.Allocator, instance: *base.Instance) VkError!*S
|
||||
self.* = .{
|
||||
.interface = interface,
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -274,11 +275,16 @@ pub fn enumerateExtensionProperties(_: *const Interface, layer_name: ?[]const u8
|
||||
return VkError.LayerNotPresent;
|
||||
}
|
||||
|
||||
count.* = EXTENSIONS.len;
|
||||
const available = EXTENSIONS.len;
|
||||
if (p_properties) |properties| {
|
||||
for (EXTENSIONS, properties[0..]) |ext, *prop| {
|
||||
const write_count = @min(count.*, available);
|
||||
for (EXTENSIONS[0..write_count], properties[0..write_count]) |ext, *prop| {
|
||||
prop.* = ext;
|
||||
}
|
||||
count.* = @intCast(write_count);
|
||||
if (write_count < available) return VkError.Incomplete;
|
||||
} else {
|
||||
count.* = @intCast(available);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,8 @@ stages: std.EnumMap(Stages, Shader),
|
||||
|
||||
pub fn createCompute(device: *base.Device, allocator: std.mem.Allocator, cache: ?*base.PipelineCache, info: *const vk.ComputePipelineCreateInfo) VkError!*Self {
|
||||
const self = allocator.create(Self) catch return VkError.OutOfHostMemory;
|
||||
errdefer allocator.destroy(self);
|
||||
var initialized = false;
|
||||
errdefer if (initialized) self.interface.destroy(allocator) else allocator.destroy(self);
|
||||
|
||||
var interface = try Interface.initCompute(device, allocator, cache, info);
|
||||
|
||||
@@ -71,7 +72,7 @@ pub fn createCompute(device: *base.Device, allocator: std.mem.Allocator, cache:
|
||||
.runtimes_allocator = .init(device_allocator),
|
||||
.stages = std.EnumMap(Stages, Shader).init(.{}),
|
||||
};
|
||||
errdefer self.runtimes_allocator.deinit();
|
||||
initialized = true;
|
||||
const runtimes_allocator = self.runtimes_allocator.allocator();
|
||||
|
||||
const instance: *SoftInstance = @alignCast(@fieldParentPtr("interface", device.instance));
|
||||
@@ -138,7 +139,8 @@ pub fn createCompute(device: *base.Device, allocator: std.mem.Allocator, cache:
|
||||
|
||||
pub fn createGraphics(device: *base.Device, allocator: std.mem.Allocator, cache: ?*base.PipelineCache, info: *const vk.GraphicsPipelineCreateInfo) VkError!*Self {
|
||||
const self = allocator.create(Self) catch return VkError.OutOfHostMemory;
|
||||
errdefer allocator.destroy(self);
|
||||
var initialized = false;
|
||||
errdefer if (initialized) self.interface.destroy(allocator) else allocator.destroy(self);
|
||||
|
||||
var interface = try Interface.initGraphics(device, allocator, cache, info);
|
||||
|
||||
@@ -154,7 +156,7 @@ pub fn createGraphics(device: *base.Device, allocator: std.mem.Allocator, cache:
|
||||
.runtimes_allocator = .init(device_allocator),
|
||||
.stages = std.EnumMap(Stages, Shader).init(.{}),
|
||||
};
|
||||
errdefer self.runtimes_allocator.deinit();
|
||||
initialized = true;
|
||||
const runtimes_allocator = self.runtimes_allocator.allocator();
|
||||
|
||||
const instance: *SoftInstance = @alignCast(@fieldParentPtr("interface", device.instance));
|
||||
|
||||
+10
-13
@@ -68,11 +68,11 @@ pub fn bindSparse(interface: *Interface, info: []const vk.BindSparseInfo, fence:
|
||||
pub fn submit(interface: *Interface, infos: []Interface.SubmitInfo, p_fence: ?*base.Fence) VkError!void {
|
||||
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
|
||||
const soft_device: *SoftDevice = @alignCast(@fieldParentPtr("interface", interface.owner));
|
||||
const allocator = soft_device.device_allocator.allocator();
|
||||
const io = interface.owner.io();
|
||||
const allocator = soft_device.device_allocator.allocator();
|
||||
|
||||
const task_data = allocator.create(TaskData) catch return VkError.OutOfDeviceMemory;
|
||||
errdefer allocator.destroy(task_data);
|
||||
const data = allocator.create(TaskData) catch return VkError.OutOfDeviceMemory;
|
||||
errdefer allocator.destroy(data);
|
||||
|
||||
var cloned_infos = try cloneSubmitInfos(allocator, infos);
|
||||
errdefer deinitSubmitInfos(allocator, &cloned_infos);
|
||||
@@ -87,7 +87,7 @@ pub fn submit(interface: *Interface, infos: []Interface.SubmitInfo, p_fence: ?*b
|
||||
break :blk seq;
|
||||
};
|
||||
|
||||
task_data.* = .{
|
||||
data.* = .{
|
||||
.queue = self,
|
||||
.soft_device = soft_device,
|
||||
.sequence = sequence,
|
||||
@@ -95,7 +95,7 @@ pub fn submit(interface: *Interface, infos: []Interface.SubmitInfo, p_fence: ?*b
|
||||
.fence = p_fence,
|
||||
};
|
||||
|
||||
self.group.async(io, Self.taskRunner, .{task_data});
|
||||
self.group.async(io, taskRunner, .{data});
|
||||
}
|
||||
|
||||
pub fn waitIdle(interface: *Interface) VkError!void {
|
||||
@@ -115,7 +115,7 @@ fn executeSubmitInfo(soft_device: *SoftDevice, info: Interface.SubmitInfo) VkErr
|
||||
|
||||
for (info.command_buffers.items) |command_buffer| {
|
||||
const soft_command_buffer: *SoftCommandBuffer = @alignCast(@fieldParentPtr("interface", command_buffer));
|
||||
soft_command_buffer.execute(&execution_device);
|
||||
try soft_command_buffer.execute(&execution_device);
|
||||
}
|
||||
|
||||
for (info.signal_semaphores.items) |semaphore| {
|
||||
@@ -126,6 +126,7 @@ fn executeSubmitInfo(soft_device: *SoftDevice, info: Interface.SubmitInfo) VkErr
|
||||
fn taskRunner(data: *TaskData) void {
|
||||
const io = data.queue.interface.owner.io();
|
||||
const allocator = data.soft_device.device_allocator.allocator();
|
||||
|
||||
defer {
|
||||
deinitSubmitInfos(allocator, &data.infos);
|
||||
allocator.destroy(data);
|
||||
@@ -140,18 +141,13 @@ fn taskRunner(data: *TaskData) void {
|
||||
}
|
||||
}
|
||||
|
||||
var submit_err: ?VkError = null;
|
||||
for (data.infos.items) |info| {
|
||||
executeSubmitInfo(data.soft_device, info) catch |err| {
|
||||
submit_err = err;
|
||||
std.log.scoped(.SoftQueue).err("Command buffer execution failed with '{s}'", .{@errorName(err)});
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
if (submit_err) |err| {
|
||||
std.log.scoped(.SoftQueue).err("Queue submit failed with '{s}'", .{@errorName(err)});
|
||||
}
|
||||
|
||||
if (data.fence) |fence| {
|
||||
fence.signal() catch |err| {
|
||||
std.log.scoped(.SoftQueue).err("Queue submit fence signal failed with '{s}'", .{@errorName(err)});
|
||||
@@ -159,9 +155,10 @@ fn taskRunner(data: *TaskData) void {
|
||||
}
|
||||
|
||||
data.queue.mutex.lock(io) catch return;
|
||||
defer data.queue.mutex.unlock(io);
|
||||
|
||||
data.queue.executing_sequence += 1;
|
||||
data.queue.condition.broadcast(io);
|
||||
data.queue.mutex.unlock(io);
|
||||
}
|
||||
|
||||
fn cloneSubmitInfos(allocator: std.mem.Allocator, infos: []Interface.SubmitInfo) VkError!std.ArrayList(Interface.SubmitInfo) {
|
||||
|
||||
@@ -14,7 +14,6 @@ pub const Interface = base.ShaderModule;
|
||||
|
||||
interface: Interface,
|
||||
module: spv.Module,
|
||||
module_allocator: std.heap.ArenaAllocator,
|
||||
|
||||
/// Pipelines need SPIR-V module reference so shader module may not
|
||||
/// be destroy on call to `vkDestroyShaderModule`
|
||||
@@ -29,10 +28,6 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v
|
||||
const soft_device: *SoftDevice = @alignCast(@fieldParentPtr("interface", device));
|
||||
const device_allocator = soft_device.device_allocator.allocator();
|
||||
|
||||
var module_allocator_arena: std.heap.ArenaAllocator = .init(device_allocator);
|
||||
errdefer module_allocator_arena.deinit();
|
||||
const module_allocator = module_allocator_arena.allocator();
|
||||
|
||||
interface.vtable = &.{
|
||||
.destroy = destroy,
|
||||
};
|
||||
@@ -41,7 +36,7 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v
|
||||
|
||||
self.* = .{
|
||||
.interface = interface,
|
||||
.module = spv.Module.init(module_allocator, code, .{
|
||||
.module = spv.Module.init(device_allocator, code, .{
|
||||
.use_simd_vectors_specializations = base.config.shaders_simd,
|
||||
}) catch |err| switch (err) {
|
||||
spv.Module.ModuleError.OutOfMemory => return VkError.OutOfHostMemory,
|
||||
@@ -55,9 +50,9 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v
|
||||
return VkError.ValidationFailed;
|
||||
},
|
||||
},
|
||||
.module_allocator = module_allocator_arena,
|
||||
.ref_count = std.atomic.Value(usize).init(1),
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -67,7 +62,11 @@ pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void {
|
||||
}
|
||||
|
||||
pub fn drop(self: *Self, allocator: std.mem.Allocator) void {
|
||||
self.module_allocator.deinit();
|
||||
const soft_device: *SoftDevice = @alignCast(@fieldParentPtr("interface", self.interface.owner));
|
||||
const device_allocator = soft_device.device_allocator.allocator();
|
||||
|
||||
self.module.deinit(device_allocator);
|
||||
|
||||
allocator.destroy(self);
|
||||
}
|
||||
|
||||
|
||||
@@ -683,6 +683,12 @@ pub fn readFloat4(map: []const u8, src_format: vk.Format) F32x4 {
|
||||
c[1] = std.mem.bytesToValue(f32, map[4..]);
|
||||
},
|
||||
|
||||
.r32g32b32_sfloat => {
|
||||
c[0] = std.mem.bytesToValue(f32, map[0..]);
|
||||
c[1] = std.mem.bytesToValue(f32, map[4..]);
|
||||
c[2] = std.mem.bytesToValue(f32, map[8..]);
|
||||
},
|
||||
|
||||
.d32_sfloat,
|
||||
.r32_sfloat,
|
||||
=> c[0] = std.mem.bytesToValue(f32, map),
|
||||
@@ -1190,6 +1196,14 @@ pub fn readInt4(map: []const u8, src_format: vk.Format) U32x4 {
|
||||
c[1] = std.mem.bytesToValue(u32, map[4..]);
|
||||
},
|
||||
|
||||
.r32g32b32_sint,
|
||||
.r32g32b32_uint,
|
||||
=> {
|
||||
c[0] = std.mem.bytesToValue(u32, map[0..]);
|
||||
c[1] = std.mem.bytesToValue(u32, map[4..]);
|
||||
c[2] = std.mem.bytesToValue(u32, map[8..]);
|
||||
},
|
||||
|
||||
.r8g8b8a8_uint,
|
||||
=> {
|
||||
c[0] = map[0];
|
||||
|
||||
@@ -13,15 +13,51 @@ const VkError = base.VkError;
|
||||
const SpvRuntimeError = spv.Runtime.RuntimeError;
|
||||
const INTERFACE_BLOB_PADDING = @sizeOf(zm.F32x4);
|
||||
|
||||
pub const DerivativeInputs = struct {
|
||||
dx: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolationLocation,
|
||||
dy: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolationLocation,
|
||||
};
|
||||
|
||||
pub fn shaderUsesDerivatives(code: []const spv.SpvWord) bool {
|
||||
var i: usize = 5;
|
||||
while (i < code.len) {
|
||||
const opcode_data = code[i];
|
||||
const word_count = (opcode_data & (~spv.spv.SpvOpCodeMask)) >> spv.spv.SpvWordCountShift;
|
||||
if (word_count == 0)
|
||||
return false;
|
||||
|
||||
const opcode = opcode_data & spv.spv.SpvOpCodeMask;
|
||||
switch (opcode) {
|
||||
@intFromEnum(spv.spv.SpvOp.DPdx),
|
||||
@intFromEnum(spv.spv.SpvOp.DPdy),
|
||||
@intFromEnum(spv.spv.SpvOp.DPdxFine),
|
||||
@intFromEnum(spv.spv.SpvOp.DPdyFine),
|
||||
@intFromEnum(spv.spv.SpvOp.DPdxCoarse),
|
||||
@intFromEnum(spv.spv.SpvOp.DPdyCoarse),
|
||||
=> return true,
|
||||
else => {},
|
||||
}
|
||||
|
||||
i += word_count;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn shaderInvocation(
|
||||
allocator: std.mem.Allocator,
|
||||
draw_call: *Renderer.DrawCall,
|
||||
batch_id: usize,
|
||||
position: zm.F32x4,
|
||||
inputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolationLocation,
|
||||
derivative_inputs: ?DerivativeInputs,
|
||||
) SpvRuntimeError![spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(zm.F32x4)]u8 {
|
||||
var fragment_inputs = inputs;
|
||||
errdefer freeOwnedInputs(allocator, fragment_inputs);
|
||||
const derivatives = derivative_inputs;
|
||||
errdefer if (derivatives) |owned_derivatives| {
|
||||
freeOwnedInputs(allocator, owned_derivatives.dx);
|
||||
freeOwnedInputs(allocator, owned_derivatives.dy);
|
||||
};
|
||||
|
||||
const io = draw_call.renderer.device.interface.io();
|
||||
|
||||
@@ -66,6 +102,13 @@ pub fn shaderInvocation(
|
||||
|
||||
if (input.blob.len != 0) {
|
||||
try rt.writeInput(input.blob, result_word);
|
||||
if (derivatives) |derivative| {
|
||||
const dx = derivative.dx[location][component];
|
||||
const dy = derivative.dy[location][component];
|
||||
if (dx.blob.len != 0 and dy.blob.len != 0) {
|
||||
try rt.setDerivativeFromMemory(allocator, result_word, dx.blob, dy.blob);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,8 +124,7 @@ pub fn shaderInvocation(
|
||||
else => return err,
|
||||
};
|
||||
|
||||
var outputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(zm.F32x4)]u8 = undefined;
|
||||
@memset(std.mem.asBytes(&outputs), 0);
|
||||
var outputs = std.mem.zeroes([spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(zm.F32x4)]u8);
|
||||
|
||||
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
|
||||
var has_split_components = false;
|
||||
@@ -117,6 +159,10 @@ pub fn shaderInvocation(
|
||||
|
||||
try rt.flushDescriptorSets(allocator);
|
||||
freeOwnedInputs(allocator, fragment_inputs);
|
||||
if (derivatives) |owned_derivatives| {
|
||||
freeOwnedInputs(allocator, owned_derivatives.dx);
|
||||
freeOwnedInputs(allocator, owned_derivatives.dy);
|
||||
}
|
||||
|
||||
return outputs;
|
||||
}
|
||||
|
||||
@@ -304,8 +304,7 @@ fn clipTransformAndRasterizePoint(
|
||||
if (!common.scissorContainsPixel(draw_call.scissor, px, py))
|
||||
continue;
|
||||
|
||||
var outputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(zm.F32x4)]u8 = undefined;
|
||||
@memset(std.mem.asBytes(&outputs), 0);
|
||||
var outputs = std.mem.zeroes([spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(zm.F32x4)]u8);
|
||||
if (has_fragment_shader) {
|
||||
outputs = fragment.shaderInvocation(
|
||||
allocator,
|
||||
@@ -313,6 +312,7 @@ fn clipTransformAndRasterizePoint(
|
||||
0,
|
||||
zm.f32x4(@floatFromInt(px), @floatFromInt(py), transformed.position[2], 1.0),
|
||||
try common.interpolateVertexOutputs(allocator, &transformed, &transformed, &transformed, 1.0, 0.0, 0.0),
|
||||
null,
|
||||
) catch |err| {
|
||||
std.log.scoped(.@"Fragment stage").err("catched a '{s}'", .{@errorName(err)});
|
||||
if (comptime base.config.logs == .verbose) {
|
||||
|
||||
@@ -152,8 +152,7 @@ inline fn run(data: RunData) !void {
|
||||
const t = @as(f32, @floatFromInt(step)) / @as(f32, @floatFromInt(@max(data.d_x, 1)));
|
||||
const z = ((1.0 - t) * data.start_vertex.position[2]) + (t * data.end_vertex.position[2]);
|
||||
|
||||
var outputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(F32x4)]u8 = undefined;
|
||||
@memset(std.mem.asBytes(&outputs), 0);
|
||||
var outputs = std.mem.zeroes([spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(F32x4)]u8);
|
||||
if (data.has_fragment_shader) {
|
||||
outputs = fragment.shaderInvocation(
|
||||
data.allocator,
|
||||
@@ -161,6 +160,7 @@ inline fn run(data: RunData) !void {
|
||||
data.batch_id,
|
||||
zm.f32x4(@floatFromInt(pixel_x), @floatFromInt(pixel_y), z, 1.0),
|
||||
try common.interpolateLineOutputs(data.allocator, data.start_vertex, data.end_vertex, t),
|
||||
null,
|
||||
) catch |err| {
|
||||
std.log.scoped(.@"Fragment stage").err("catched a '{s}'", .{@errorName(err)});
|
||||
if (comptime base.config.logs == .verbose) {
|
||||
|
||||
@@ -226,6 +226,58 @@ pub fn interpolateLineOutputs(
|
||||
return interpolateVertexOutputs(allocator, v0, v1, v0, 1.0 - t, t, 0.0);
|
||||
}
|
||||
|
||||
pub fn interpolateVertexOutputDerivatives(
|
||||
allocator: std.mem.Allocator,
|
||||
v0: *const Renderer.Vertex,
|
||||
v1: *const Renderer.Vertex,
|
||||
v2: *const Renderer.Vertex,
|
||||
db0: f32,
|
||||
db1: f32,
|
||||
db2: f32,
|
||||
) VkError![spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolationLocation {
|
||||
var inputs = [_]VertexInterpolationLocation{[_]VertexInterpolation{.{
|
||||
.blob = &.{},
|
||||
.size = 0,
|
||||
.free_responsability = false,
|
||||
}} ** 4} ** spv.SPIRV_MAX_OUTPUT_LOCATIONS;
|
||||
|
||||
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
|
||||
for (0..4) |component| {
|
||||
const out0 = v0.outputs[location][component] orelse continue;
|
||||
const out1 = v1.outputs[location][component] orelse continue;
|
||||
const out2 = v2.outputs[location][component] orelse continue;
|
||||
|
||||
const len = @min(out0.size, out1.size, out2.size);
|
||||
if (len == 0)
|
||||
continue;
|
||||
|
||||
const input = allocator.alloc(u8, len + @sizeOf(F32x4)) catch return VkError.OutOfDeviceMemory;
|
||||
@memset(input, 0);
|
||||
|
||||
if (out0.interpolation_type != .flat) {
|
||||
var byte_index: usize = 0;
|
||||
while (byte_index + @sizeOf(F32x4) <= len) : (byte_index += @sizeOf(F32x4)) {
|
||||
const value0 = std.mem.bytesToValue(F32x4, out0.blob[byte_index..]);
|
||||
const value1 = std.mem.bytesToValue(F32x4, out1.blob[byte_index..]);
|
||||
const value2 = std.mem.bytesToValue(F32x4, out2.blob[byte_index..]);
|
||||
base.utils.writePacked(F32x4, input[byte_index..], interpolateF32x4(value0, value1, value2, db0, db1, db2));
|
||||
}
|
||||
|
||||
while (byte_index + @sizeOf(f32) <= len) : (byte_index += @sizeOf(f32)) {
|
||||
const value0 = std.mem.bytesToValue(f32, out0.blob[byte_index..]);
|
||||
const value1 = std.mem.bytesToValue(f32, out1.blob[byte_index..]);
|
||||
const value2 = std.mem.bytesToValue(f32, out2.blob[byte_index..]);
|
||||
base.utils.writePacked(f32, input[byte_index..], (value0 * db0) + (value1 * db1) + (value2 * db2));
|
||||
}
|
||||
}
|
||||
|
||||
inputs[location][component] = .{ .blob = input, .size = len, .free_responsability = true };
|
||||
}
|
||||
}
|
||||
|
||||
return inputs;
|
||||
}
|
||||
|
||||
inline fn interpolateF32x4(value0: F32x4, value1: F32x4, value2: F32x4, b0: f32, b1: f32, b2: f32) F32x4 {
|
||||
return (value0 * zm.f32x4s(b0)) + (value1 * zm.f32x4s(b1)) + (value2 * zm.f32x4s(b2));
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ const RunData = struct {
|
||||
stencil_attachment_access: ?*common.RenderTargetAccess,
|
||||
front_face: bool,
|
||||
has_fragment_shader: bool,
|
||||
fragment_uses_derivatives: bool,
|
||||
};
|
||||
|
||||
pub fn drawTriangle(
|
||||
@@ -56,6 +57,10 @@ pub fn drawTriangle(
|
||||
|
||||
const pipeline = draw_call.renderer.state.pipeline orelse return;
|
||||
const fragment_stage = pipeline.stages.getPtr(.fragment);
|
||||
const fragment_uses_derivatives = if (fragment_stage) |stage|
|
||||
fragment.shaderUsesDerivatives(stage.module.module.code)
|
||||
else
|
||||
false;
|
||||
const runtimes_count = if (fragment_stage) |stage| stage.runtimes.len else 1;
|
||||
if (runtimes_count == 0)
|
||||
return;
|
||||
@@ -106,6 +111,7 @@ pub fn drawTriangle(
|
||||
.stencil_attachment_access = stencil_attachment_access,
|
||||
.front_face = front_face,
|
||||
.has_fragment_shader = fragment_stage != null,
|
||||
.fragment_uses_derivatives = fragment_uses_derivatives,
|
||||
};
|
||||
|
||||
draw_call.rasterizer_wait_group.async(io, runWrapper, .{run_data});
|
||||
@@ -171,15 +177,49 @@ inline fn run(data: RunData) !void {
|
||||
const b2 = w2 / data.area;
|
||||
const z = (b0 * data.v0.position[2]) + (b1 * data.v1.position[2]) + (b2 * data.v2.position[2]);
|
||||
|
||||
var outputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(F32x4)]u8 = undefined;
|
||||
@memset(std.mem.asBytes(&outputs), 0);
|
||||
var outputs = std.mem.zeroes([spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(F32x4)]u8);
|
||||
if (data.has_fragment_shader) {
|
||||
const inputs = try common.interpolateVertexOutputs(data.allocator, &data.v0, &data.v1, &data.v2, b0, b1, b2);
|
||||
const derivative_inputs: ?fragment.DerivativeInputs = if (data.fragment_uses_derivatives) blk: {
|
||||
var derivatives: fragment.DerivativeInputs = undefined;
|
||||
|
||||
const p_dx = zm.f32x4(@as(f32, @floatFromInt(x)) + 1.5, @as(f32, @floatFromInt(y)) + 0.5, 0.0, 1.0);
|
||||
const dx_w0 = edgeFunction(data.v1.position, data.v2.position, p_dx);
|
||||
const dx_w1 = edgeFunction(data.v2.position, data.v0.position, p_dx);
|
||||
const dx_w2 = edgeFunction(data.v0.position, data.v1.position, p_dx);
|
||||
derivatives.dx = try common.interpolateVertexOutputDerivatives(
|
||||
data.allocator,
|
||||
&data.v0,
|
||||
&data.v1,
|
||||
&data.v2,
|
||||
(dx_w0 / data.area) - b0,
|
||||
(dx_w1 / data.area) - b1,
|
||||
(dx_w2 / data.area) - b2,
|
||||
);
|
||||
|
||||
const p_dy = zm.f32x4(@as(f32, @floatFromInt(x)) + 0.5, @as(f32, @floatFromInt(y)) + 1.5, 0.0, 1.0);
|
||||
const dy_w0 = edgeFunction(data.v1.position, data.v2.position, p_dy);
|
||||
const dy_w1 = edgeFunction(data.v2.position, data.v0.position, p_dy);
|
||||
const dy_w2 = edgeFunction(data.v0.position, data.v1.position, p_dy);
|
||||
derivatives.dy = try common.interpolateVertexOutputDerivatives(
|
||||
data.allocator,
|
||||
&data.v0,
|
||||
&data.v1,
|
||||
&data.v2,
|
||||
(dy_w0 / data.area) - b0,
|
||||
(dy_w1 / data.area) - b1,
|
||||
(dy_w2 / data.area) - b2,
|
||||
);
|
||||
break :blk derivatives;
|
||||
} else null;
|
||||
|
||||
outputs = fragment.shaderInvocation(
|
||||
data.allocator,
|
||||
data.draw_call,
|
||||
data.batch_id,
|
||||
zm.f32x4(@floatFromInt(x), @floatFromInt(y), z, 1.0),
|
||||
try common.interpolateVertexOutputs(data.allocator, &data.v0, &data.v1, &data.v2, b0, b1, b2),
|
||||
inputs,
|
||||
derivative_inputs,
|
||||
) catch |err| {
|
||||
std.log.scoped(.@"Fragment stage").err("catched a '{s}'", .{@errorName(err)});
|
||||
if (comptime base.config.logs == .verbose) {
|
||||
|
||||
@@ -9,6 +9,7 @@ const SpvRuntimeError = spv.Runtime.RuntimeError;
|
||||
|
||||
const Renderer = @import("Renderer.zig");
|
||||
const SoftPipeline = @import("../SoftPipeline.zig");
|
||||
const blitter = @import("blitter.zig");
|
||||
|
||||
const VkError = base.VkError;
|
||||
const INTERFACE_BLOB_PADDING = @sizeOf(F32x4);
|
||||
@@ -129,6 +130,9 @@ fn setupBuiltins(rt: *spv.Runtime, vertex_index_u32: u32, instance_index: usize)
|
||||
}
|
||||
|
||||
fn writeVertexInput(rt: *spv.Runtime, allocator: std.mem.Allocator, raw_input: []const u8, format: vk.Format, location: u32) !void {
|
||||
var expanded_input: [@sizeOf(F32x4)]u8 = @splat(0);
|
||||
const expanded_slice = expandedVertexInput(raw_input, format, &expanded_input);
|
||||
|
||||
var has_split_components = false;
|
||||
for (1..4) |component| {
|
||||
_ = rt.getResultByLocationComponent(location, @intCast(component), .input) catch |err| switch (err) {
|
||||
@@ -148,8 +152,8 @@ fn writeVertexInput(rt: *spv.Runtime, allocator: std.mem.Allocator, raw_input: [
|
||||
const input_memory_size = try rt.getResultMemorySize(result_word);
|
||||
const raw_offset = component * @sizeOf(f32);
|
||||
|
||||
if (raw_offset + input_memory_size <= raw_input.len) {
|
||||
try rt.writeInput(raw_input[raw_offset .. raw_offset + input_memory_size], result_word);
|
||||
if (raw_offset + input_memory_size <= expanded_slice.len) {
|
||||
try rt.writeInput(expanded_slice[raw_offset .. raw_offset + input_memory_size], result_word);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -179,8 +183,8 @@ fn writeVertexInput(rt: *spv.Runtime, allocator: std.mem.Allocator, raw_input: [
|
||||
|
||||
const input_memory_size = try rt.getInputLocationMemorySize(location);
|
||||
|
||||
if (raw_input.len >= input_memory_size) {
|
||||
try rt.writeInputLocation(raw_input[0..input_memory_size], location);
|
||||
if (expanded_slice.len >= input_memory_size) {
|
||||
try rt.writeInputLocation(expanded_slice[0..input_memory_size], location);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -188,12 +192,24 @@ fn writeVertexInput(rt: *spv.Runtime, allocator: std.mem.Allocator, raw_input: [
|
||||
defer allocator.free(input);
|
||||
|
||||
@memset(input, 0);
|
||||
@memcpy(input[0..raw_input.len], raw_input);
|
||||
@memcpy(input[0..expanded_slice.len], expanded_slice);
|
||||
|
||||
fillMissingVertexComponents(input, raw_input.len, format);
|
||||
fillMissingVertexComponents(input, expanded_slice.len, format);
|
||||
try rt.writeInputLocation(input, location);
|
||||
}
|
||||
|
||||
fn expandedVertexInput(raw_input: []const u8, format: vk.Format, expanded: *[@sizeOf(F32x4)]u8) []const u8 {
|
||||
if (base.format.isUnnormalizedInteger(format)) {
|
||||
const value = blitter.readInt4(raw_input, format);
|
||||
@memcpy(expanded, std.mem.asBytes(&value));
|
||||
return expanded;
|
||||
}
|
||||
|
||||
const value = blitter.readFloat4(raw_input, format);
|
||||
@memcpy(expanded, std.mem.asBytes(&value));
|
||||
return expanded;
|
||||
}
|
||||
|
||||
fn fillMissingVertexComponents(input: []u8, raw_input_size: usize, format: vk.Format) void {
|
||||
if (input.len < @sizeOf(F32x4) or raw_input_size > 3 * @sizeOf(f32))
|
||||
return;
|
||||
|
||||
@@ -41,6 +41,7 @@ pub const DRIVER_NAME = "Soft";
|
||||
pub const VULKAN_VERSION = vk.makeApiVersion(0, 1, 0, 0);
|
||||
pub const DRIVER_VERSION = vk.makeApiVersion(0, 0, 0, 1);
|
||||
pub const DEVICE_ID = 0x600DCAFE;
|
||||
pub const PIPELINE_CACHE_UUID: [vk.UUID_SIZE]u8 = "ApeSoftCacheUUID".*;
|
||||
|
||||
/// Generic system memory.
|
||||
pub const MEMORY_TYPE_GENERIC_BIT = 0;
|
||||
|
||||
Reference in New Issue
Block a user