[Soft] adding new experimental shader interpreter based on new IR
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user