Files
VulkanDriver/src/software/interpreter/vertex.zig
T
kbz_8 e3e5fa4b18
Mirror Gitea refs to GitHub / mirror (push) Successful in 20s
Test / build_and_test (push) Successful in 3m44s
Build / build (push) Successful in 5m1s
[Soft] removing SPIR-V interpreter fallback on IR interpreter fail
2026-08-09 00:06:43 +02:00

162 lines
7.5 KiB
Zig

const std = @import("std");
const vk = @import("vulkan");
const base = @import("base");
const shader_ir = @import("shader_ir");
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;
}