Files
VulkanDriver/src/soft/device/fragment.zig
T
kbz_8 ffc212de7d
Build / build (push) Successful in 38s
Test / build_and_test (push) Successful in 30s
bumping spirv interpreter
2026-06-11 12:33:56 +02:00

199 lines
7.2 KiB
Zig

const std = @import("std");
const vk = @import("vulkan");
const base = @import("base");
const zm = base.zm;
const spv = @import("spv");
const VertexInterpolationLocation = @import("rasterizer/common.zig").VertexInterpolationLocation;
const Renderer = @import("Renderer.zig");
const SoftImage = @import("../SoftImage.zig");
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 shaderInvocation(
allocator: std.mem.Allocator,
draw_call: *Renderer.DrawCall,
batch_id: usize,
position: zm.F32x4,
front_face: bool,
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();
const pipeline = draw_call.renderer.state.pipeline orelse return undefined;
const shader = pipeline.stages.getPtr(.fragment) orelse return undefined;
const runtime = &shader.runtimes[batch_id];
const mutex = &runtime.mutex;
const rt = &runtime.rt;
mutex.lock(io) catch return SpvRuntimeError.Unknown;
defer mutex.unlock(io);
rt.resetInvocation(allocator);
try rt.populatePushConstants(draw_call.renderer.state.push_constant_blob[0..]);
rt.writeBuiltIn(std.mem.asBytes(&position), .FragCoord) catch |err| switch (err) {
SpvRuntimeError.NotFound => {},
else => return err,
};
rt.writeBuiltIn(std.mem.asBytes(&front_face), .FrontFacing) catch |err| switch (err) {
SpvRuntimeError.NotFound => {},
else => return err,
};
const entry = try rt.getEntryPointByName(shader.entry);
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
for (0..4) |component| {
const result_word = rt.getResultByLocationComponent(@intCast(location), @intCast(component), .input) catch |err| switch (err) {
SpvRuntimeError.NotFound => continue,
else => return err,
};
var input = fragment_inputs[location][component];
if (input.blob.len == 0) {
const memory_size = try rt.getResultMemorySize(result_word);
const zeroes = allocator.alloc(u8, memory_size + INTERFACE_BLOB_PADDING) catch return SpvRuntimeError.OutOfMemory;
@memset(zeroes, 0);
fragment_inputs[location][component] = .{
.blob = zeroes,
.size = memory_size,
.free_responsability = true,
};
input = fragment_inputs[location][component];
}
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);
}
}
}
}
}
rt.callEntryPoint(allocator, entry) catch |err| switch (err) {
// Some errors can be safely ignored
SpvRuntimeError.OutOfBounds => {},
SpvRuntimeError.Killed => {
try rt.flushDescriptorSets(allocator);
freeOwnedInputs(allocator, fragment_inputs);
return undefined; // FIXME
},
else => return err,
};
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;
for (1..4) |component| {
_ = rt.getResultByLocationComponent(@intCast(location), @intCast(component), .output) catch |err| switch (err) {
SpvRuntimeError.NotFound => continue,
else => return err,
};
has_split_components = true;
break;
}
if (has_split_components) {
for (0..4) |component| {
const result_word = rt.getResultByLocationComponent(@intCast(location), @intCast(component), .output) catch |err| switch (err) {
SpvRuntimeError.NotFound => continue,
else => return err,
};
try readFragmentOutput(allocator, rt, &outputs, location, component, result_word);
}
continue;
}
const result_word = rt.getResultByLocation(@intCast(location), .output) catch |err| switch (err) {
SpvRuntimeError.NotFound => continue,
else => return err,
};
try readFragmentOutput(allocator, rt, &outputs, location, 0, result_word);
}
try rt.flushDescriptorSets(allocator);
freeOwnedInputs(allocator, fragment_inputs);
if (derivatives) |owned_derivatives| {
freeOwnedInputs(allocator, owned_derivatives.dx);
freeOwnedInputs(allocator, owned_derivatives.dy);
}
return outputs;
}
fn readFragmentOutput(
allocator: std.mem.Allocator,
rt: anytype,
outputs: *[spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(zm.F32x4)]u8,
location: usize,
component: usize,
result_word: spv.SpvWord,
) SpvRuntimeError!void {
const memory_size = try rt.getResultMemorySize(result_word);
const offset = component * @sizeOf(f32);
const location_size = @sizeOf(zm.F32x4);
const direct_capacity = location_size - offset;
if (memory_size <= direct_capacity) {
try rt.readOutput(outputs[location][offset .. offset + memory_size], result_word);
return;
}
const output = allocator.alloc(u8, memory_size + INTERFACE_BLOB_PADDING) catch return SpvRuntimeError.OutOfMemory;
defer allocator.free(output);
@memset(output, 0);
try rt.readOutput(output, result_word);
var source_offset: usize = 0;
var target_location = location;
var target_offset = offset;
while (source_offset < memory_size and target_location < outputs.len) {
const copy_size = @min(memory_size - source_offset, location_size - target_offset);
@memcpy(
outputs[target_location][target_offset .. target_offset + copy_size],
output[source_offset .. source_offset + copy_size],
);
source_offset += copy_size;
target_location += 1;
target_offset = offset;
}
if (source_offset != memory_size)
return SpvRuntimeError.OutOfBounds;
}
fn freeOwnedInputs(allocator: std.mem.Allocator, inputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolationLocation) void {
for (inputs) |location| {
for (location) |input| {
if (input.free_responsability)
allocator.free(input.blob);
}
}
}