199 lines
7.2 KiB
Zig
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);
|
|
}
|
|
}
|
|
}
|