174 lines
7.6 KiB
Zig
174 lines
7.6 KiB
Zig
const std = @import("std");
|
|
const vk = @import("vulkan");
|
|
const base = @import("base");
|
|
const shader_ir = @import("shader_ir");
|
|
|
|
const bc = @import("bytecode.zig");
|
|
const Shader = @import("Shader.zig");
|
|
const SoftPipeline = @import("../SoftPipeline.zig");
|
|
const Renderer = @import("../device/Renderer.zig");
|
|
const blitter = @import("../device/blitter.zig");
|
|
|
|
const VkError = base.VkError;
|
|
const ir = shader_ir.ir;
|
|
const interface_blob_padding = @sizeOf(base.zm.F32x4);
|
|
|
|
pub fn run(
|
|
allocator: std.mem.Allocator,
|
|
pipeline: *SoftPipeline,
|
|
shader: *Shader,
|
|
batch_id: usize,
|
|
batch_size: usize,
|
|
vertex_count: usize,
|
|
first_vertex: usize,
|
|
first_instance: usize,
|
|
indices: ?[]const u32,
|
|
primitive_restart: ?[]const bool,
|
|
instance_index: usize,
|
|
draw_call: *Renderer.DrawCall,
|
|
) VkError!void {
|
|
const slot = &shader.runtimes[batch_id];
|
|
const io = draw_call.renderer.device.interface.io();
|
|
slot.mutex.lock(io) catch return VkError.DeviceLost;
|
|
defer slot.mutex.unlock(io);
|
|
|
|
var invocation_index = batch_id;
|
|
while (invocation_index < vertex_count) : (invocation_index += batch_size) {
|
|
const output = &draw_call.vertices[(instance_index * vertex_count) + invocation_index];
|
|
if (primitive_restart) |restart| {
|
|
if (restart[invocation_index]) {
|
|
output.primitive_restart = true;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
const vertex_index: u32 = if (indices) |draw_indices| draw_indices[invocation_index] else @intCast(first_vertex + invocation_index);
|
|
try populateInputs(
|
|
&slot.runtime,
|
|
&shader.program,
|
|
pipeline,
|
|
draw_call,
|
|
vertex_index,
|
|
@intCast(first_instance + instance_index),
|
|
);
|
|
const outcome = slot.runtime.run(&shader.program, .{}) catch return VkError.Unknown;
|
|
if (outcome == .discarded)
|
|
continue;
|
|
try collectOutputs(allocator, &slot.runtime, &shader.program, output);
|
|
}
|
|
}
|
|
|
|
fn populateInputs(
|
|
runtime: anytype,
|
|
program: *const @import("Program.zig"),
|
|
pipeline: *SoftPipeline,
|
|
draw_call: *Renderer.DrawCall,
|
|
vertex_index: u32,
|
|
instance_index: u32,
|
|
) VkError!void {
|
|
for (program.interfaces, 0..) |optional_binding, index| {
|
|
const binding = optional_binding orelse continue;
|
|
if (binding.direction != .input)
|
|
continue;
|
|
const variable = ir.id.InterfaceVariableId.fromIndex(index);
|
|
var values: [4]u32 = @splat(0);
|
|
switch (binding.semantic) {
|
|
.builtin => |builtin| values[0] = switch (builtin) {
|
|
.vertex_index => vertex_index,
|
|
.instance_index => instance_index,
|
|
else => return VkError.InvalidPipelineDrv,
|
|
},
|
|
.location => |location| {
|
|
const attribute = findAttribute(
|
|
pipeline.interface.mode.graphics.input_assembly.attribute_description orelse &.{},
|
|
location.location,
|
|
) orelse {
|
|
runtime.writeInput(program, variable, values[0..binding.span.components]) catch return VkError.Unknown;
|
|
continue;
|
|
};
|
|
const binding_description = findBinding(
|
|
pipeline.interface.mode.graphics.input_assembly.binding_description orelse return VkError.ValidationFailed,
|
|
attribute.binding,
|
|
) orelse return VkError.ValidationFailed;
|
|
const vertex_buffer = draw_call.renderer.state.data.graphics.vertex_buffers[attribute.binding];
|
|
const buffer = vertex_buffer.buffer;
|
|
const memory = buffer.interface.memory orelse return VkError.InvalidDeviceMemoryDrv;
|
|
const input_index = switch (binding_description.input_rate) {
|
|
.vertex => @as(usize, vertex_index),
|
|
.instance => @as(usize, instance_index),
|
|
else => return VkError.ValidationFailed,
|
|
};
|
|
const offset = buffer.interface.offset + vertex_buffer.offset + binding_description.stride * input_index + attribute.offset;
|
|
const input_size = base.format.texelSize(attribute.format);
|
|
var robust_bytes: [64]u8 = @splat(0);
|
|
if (input_size > robust_bytes.len)
|
|
return VkError.Unknown;
|
|
if (offset < memory.size) {
|
|
const available = @min(input_size, @as(usize, @intCast(memory.size - offset)));
|
|
const mapped = memory.map(offset, available) catch &.{};
|
|
@memcpy(robust_bytes[0..mapped.len], mapped);
|
|
}
|
|
values = if (base.format.isUnnormalizedInteger(attribute.format))
|
|
blitter.readInt4(robust_bytes[0..input_size], attribute.format)
|
|
else
|
|
@bitCast(blitter.readFloat4(robust_bytes[0..input_size], attribute.format));
|
|
const first_component: usize = location.component;
|
|
const end_component = first_component + binding.span.components;
|
|
runtime.writeInput(program, variable, values[first_component..end_component]) catch return VkError.Unknown;
|
|
continue;
|
|
},
|
|
}
|
|
runtime.writeInput(program, variable, values[0..binding.span.components]) catch return VkError.Unknown;
|
|
}
|
|
}
|
|
|
|
fn collectOutputs(allocator: std.mem.Allocator, runtime: anytype, program: *const @import("Program.zig"), output: *Renderer.Vertex) VkError!void {
|
|
for (program.interfaces, 0..) |optional_binding, index| {
|
|
const binding = optional_binding orelse continue;
|
|
if (binding.direction != .output)
|
|
continue;
|
|
const variable = ir.id.InterfaceVariableId.fromIndex(index);
|
|
var values: [4]u32 = @splat(0);
|
|
runtime.readOutput(program, variable, values[0..binding.span.components]) catch return VkError.Unknown;
|
|
switch (binding.semantic) {
|
|
.builtin => |builtin| switch (builtin) {
|
|
.position => @memcpy(std.mem.asBytes(&output.position), std.mem.asBytes(&values)),
|
|
else => return VkError.InvalidPipelineDrv,
|
|
},
|
|
.location => |location| {
|
|
if (location.location >= output.outputs.len or location.component >= output.outputs[0].len)
|
|
return VkError.ValidationFailed;
|
|
const size = @as(usize, binding.span.components) * @sizeOf(u32);
|
|
const blob = allocator.alloc(u8, size + interface_blob_padding) catch return VkError.OutOfDeviceMemory;
|
|
@memset(blob, 0);
|
|
@memcpy(blob[0..size], std.mem.asBytes(&values)[0..size]);
|
|
output.outputs[location.location][location.component] = .{
|
|
.interpolation_type = switch (binding.span.kind) {
|
|
.signed_integer, .unsigned_integer, .boolean => .flat,
|
|
.floating => .smooth,
|
|
},
|
|
.centroid = false,
|
|
.blob = blob,
|
|
.size = size,
|
|
};
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
fn findAttribute(attributes: []const vk.VertexInputAttributeDescription, location: u32) ?vk.VertexInputAttributeDescription {
|
|
for (attributes) |attribute|
|
|
if (attribute.location == location) return attribute;
|
|
return null;
|
|
}
|
|
|
|
fn findBinding(bindings: []const vk.VertexInputBindingDescription, binding: u32) ?vk.VertexInputBindingDescription {
|
|
for (bindings) |description|
|
|
if (description.binding == binding) return description;
|
|
return null;
|
|
}
|
|
|
|
comptime {
|
|
_ = bc;
|
|
}
|