[Soft] removing SPIR-V interpreter fallback on IR interpreter fail
Mirror Gitea refs to GitHub / mirror (push) Successful in 20s
Test / build_and_test (push) Successful in 3m44s
Build / build (push) Successful in 5m1s

This commit is contained in:
2026-08-09 00:06:43 +02:00
parent f40e5b742d
commit e3e5fa4b18
16 changed files with 200 additions and 182 deletions
+1 -1
View File
@@ -103,7 +103,7 @@ fn compileStage(allocator: std.mem.Allocator, info: *const vk.PipelineShaderStag
.graphics => if (expected_stage == .compute) return VkError.ValidationFailed, .graphics => if (expected_stage == .compute) return VkError.ValidationFailed,
} }
const shader_module = base.NonDispatchable(base.ShaderModule).fromHandleObject(info.module) catch |err| return err; const shader_module = try base.NonDispatchable(base.ShaderModule).fromHandleObject(info.module);
var module = shader_module.instantiateIr(allocator, .{ var module = shader_module.instantiateIr(allocator, .{
.entry_point = std.mem.span(info.p_name), .entry_point = std.mem.span(info.p_name),
.stage = expected_stage, .stage = expected_stage,
+3 -1
View File
@@ -252,7 +252,9 @@ fn launchHostDaemon(instance: *base.Instance, allocator: std.mem.Allocator) VkEr
const local_path = std.fmt.allocPrint(allocator, "/tmp/ape_phi_device_{d}_{d}.host", .{ process_id, thread_id }) catch return VkError.OutOfHostMemory; const local_path = std.fmt.allocPrint(allocator, "/tmp/ape_phi_device_{d}_{d}.host", .{ process_id, thread_id }) catch return VkError.OutOfHostMemory;
defer allocator.free(local_path); defer allocator.free(local_path);
errdefer std.Io.Dir.deleteFileAbsolute(io, local_path) catch {}; errdefer std.Io.Dir.deleteFileAbsolute(io, local_path) catch |err| {
std.log.scoped(.PhiDevice).warn("Failed to remove Phi host daemon after launch error: {s}", .{@errorName(err)});
};
std.Io.Dir.writeFile(.cwd(), io, .{ std.Io.Dir.writeFile(.cwd(), io, .{
.sub_path = local_path, .sub_path = local_path,
+1 -1
View File
@@ -132,7 +132,7 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v
.command_allocator = undefined, .command_allocator = undefined,
.commands = .empty, .commands = .empty,
}; };
self.command_allocator = .init(interface.host_allocator.allocator()); self.command_allocator = .init(self.interface.host_allocator.allocator());
return self; return self;
} }
+1
View File
@@ -49,6 +49,7 @@ pub fn create(allocator: std.mem.Allocator, infos: *const vk.InstanceCreateInfo)
.requestPhysicalDevices = requestPhysicalDevices, .requestPhysicalDevices = requestPhysicalDevices,
.releasePhysicalDevices = releasePhysicalDevices, .releasePhysicalDevices = releasePhysicalDevices,
.io = io, .io = io,
.enumerate_drm_devices = false,
}; };
return &self.interface; return &self.interface;
} }
+25 -16
View File
@@ -48,13 +48,14 @@ const Runtime = struct {
rt: spv.Runtime, rt: spv.Runtime,
}; };
const Shader = struct { const SpvShader = struct {
module: *SoftShaderModule, module: *SoftShaderModule,
runtimes: []Runtime, runtimes: []Runtime,
entry: []const u8, entry: []const u8,
interpreter: ?InterpreterShader,
}; };
const Shader = if (base.config.soft_ir_interpreter) InterpreterShader else SpvShader;
const Stages = enum { const Stages = enum {
vertex, vertex,
tessellation_control, tessellation_control,
@@ -107,7 +108,10 @@ pub fn createCompute(device: *base.Device, allocator: std.mem.Allocator, cache:
}; };
self.stages.put(.compute, try createShader(allocator, device_allocator, runtimes_allocator, soft_cache, soft_module, &info.stage, runtimes_count)); self.stages.put(.compute, try createShader(allocator, device_allocator, runtimes_allocator, soft_cache, soft_module, &info.stage, runtimes_count));
std.log.scoped(.ComputePipeline).debug("Created {d} runtimes for compute stage", .{runtimes_count}); std.log.scoped(.ComputePipeline).debug("Created {d} {s} runtimes for compute stage", .{
runtimes_count,
if (comptime base.config.soft_ir_interpreter) "IR" else "SPIR-V",
});
return self; return self;
} }
@@ -152,7 +156,10 @@ pub fn createGraphics(device: *base.Device, allocator: std.mem.Allocator, cache:
const soft_module: *SoftShaderModule = @alignCast(@fieldParentPtr("interface", module)); const soft_module: *SoftShaderModule = @alignCast(@fieldParentPtr("interface", module));
const shader = try createShader(allocator, device_allocator, runtimes_allocator, soft_cache, soft_module, &stage, runtimes_count); const shader = try createShader(allocator, device_allocator, runtimes_allocator, soft_cache, soft_module, &stage, runtimes_count);
std.log.scoped(.GraphicsPipeline).debug("Created {d} runtimes for:", .{runtimes_count}); std.log.scoped(.GraphicsPipeline).debug("Created {d} {s} runtimes for:", .{
runtimes_count,
if (comptime base.config.soft_ir_interpreter) "IR" else "SPIR-V",
});
if (stage.stage.contains(.{ .vertex_bit = true })) { if (stage.stage.contains(.{ .vertex_bit = true })) {
std.log.scoped(.GraphicsPipeline).debug("> Vertex stage", .{}); std.log.scoped(.GraphicsPipeline).debug("> Vertex stage", .{});
@@ -183,15 +190,18 @@ pub fn createGraphics(device: *base.Device, allocator: std.mem.Allocator, cache:
pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void { pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const device_allocator = interface.owner.device_allocator.allocator();
var it = self.stages.iterator(); var it = self.stages.iterator();
while (it.next()) |entry| { if (comptime base.config.soft_ir_interpreter) {
if (entry.value.interpreter) |*interpreter| while (it.next()) |entry|
interpreter.deinit(); entry.value.deinit();
entry.value.module.unref(allocator); } else {
for (entry.value.runtimes) |*runtime| { const device_allocator = interface.owner.device_allocator.allocator();
runtime.rt.function_stack.clearAndFree(device_allocator); // Hacky to avoid leaks while (it.next()) |entry| {
entry.value.module.unref(allocator);
for (entry.value.runtimes) |*runtime| {
runtime.rt.function_stack.clearAndFree(device_allocator); // Hacky to avoid leaks
}
} }
} }
self.runtimes_allocator.deinit(); self.runtimes_allocator.deinit();
@@ -207,6 +217,9 @@ fn createShader(
stage: *const vk.PipelineShaderStageCreateInfo, stage: *const vk.PipelineShaderStageCreateInfo,
runtimes_count: usize, runtimes_count: usize,
) VkError!Shader { ) VkError!Shader {
if (comptime base.config.soft_ir_interpreter)
return InterpreterShader.compile(runtimes_allocator, module, stage, runtimes_count);
const entry = std.mem.span(stage.p_name); const entry = std.mem.span(stage.p_name);
const execution_model = executionModelForStage(stage.stage) orelse return VkError.Unknown; const execution_model = executionModelForStage(stage.stage) orelse return VkError.Unknown;
const runtimes = runtimes_allocator.alloc(Runtime, runtimes_count) catch return VkError.OutOfDeviceMemory; const runtimes = runtimes_allocator.alloc(Runtime, runtimes_count) catch return VkError.OutOfDeviceMemory;
@@ -259,15 +272,11 @@ fn createShader(
} }
} }
var shader: Shader = .{ return .{
.module = module, .module = module,
.runtimes = runtimes, .runtimes = runtimes,
.entry = runtimes_allocator.dupe(u8, entry) catch return VkError.OutOfDeviceMemory, .entry = runtimes_allocator.dupe(u8, entry) catch return VkError.OutOfDeviceMemory,
.interpreter = null,
}; };
if (comptime base.config.soft_ir_interpreter)
shader.interpreter = try InterpreterShader.compile(runtimes_allocator, module, stage, runtimes_count);
return shader;
} }
fn initRuntime(allocator: std.mem.Allocator, module: *SoftShaderModule, stage: *const vk.PipelineShaderStageCreateInfo, image_api: spv.Runtime.ImageAPI) VkError!spv.Runtime { fn initRuntime(allocator: std.mem.Allocator, module: *SoftShaderModule, stage: *const vk.PipelineShaderStageCreateInfo, image_api: spv.Runtime.ImageAPI) VkError!spv.Runtime {
+58 -50
View File
@@ -71,48 +71,49 @@ pub fn dispatchBase(self: *Self, base_group_x: u32, base_group_y: u32, base_grou
const pipeline = self.state.pipeline orelse return VkError.InvalidPipelineDrv; const pipeline = self.state.pipeline orelse return VkError.InvalidPipelineDrv;
const shader = pipeline.stages.getPtr(.compute) orelse return VkError.InvalidPipelineDrv; const shader = pipeline.stages.getPtr(.compute) orelse return VkError.InvalidPipelineDrv;
if (comptime base.config.soft_ir_interpreter) {
if (shader.interpreter) |*interpreter_shader|
return ir_compute.dispatch(interpreter_shader, base_group_x, base_group_y, base_group_z, group_count_x, group_count_y, group_count_z);
}
const spv_module = &shader.module.module;
self.batch_size = if (spv_module.reflection_infos.has_atomics) 1 else shader.runtimes.len;
const allocator = self.device.interface.device_allocator.allocator();
const local_size = try getLocalSize(&shader.runtimes[0].rt, allocator, spv_module);
const local_size_xy = std.math.mul(usize, local_size[0], local_size[1]) catch return VkError.ValidationFailed;
const invocations_per_workgroup = std.math.mul(usize, local_size_xy, local_size[2]) catch return VkError.ValidationFailed;
self.invocation_index.store(0, .monotonic);
const io = self.device.interface.io(); const io = self.device.interface.io();
const timer = std.Io.Timestamp.now(io, .real); const timer = std.Io.Timestamp.now(io, .real);
defer if (comptime base.config.logs != .none) { defer if (comptime base.config.logs != .none) {
const duration = timer.untilNow(io, .real); const duration = timer.untilNow(io, .real);
const ms: f32 = @floatFromInt(duration.toMicroseconds()); const ms: f32 = @floatFromInt(duration.toMicroseconds());
std.log.scoped(.ComputeDispatcher).debug("Compute dispatch took {}ms", .{ms / 1000}); std.log.scoped(.ComputeDispatcher).debug("Compute dispatch took {}ms using {s} interpreter", .{ ms / 1000, if (comptime base.config.soft_ir_interpreter) "IR" else "SPIR-V" });
}; };
var wg: std.Io.Group = .init; if (comptime base.config.soft_ir_interpreter) {
for (0..@min(self.batch_size, group_count)) |batch_id| { return ir_compute.dispatch(shader, base_group_x, base_group_y, base_group_z, group_count_x, group_count_y, group_count_z);
const run_data: RunData = .{ } else {
.self = self, const spv_module = &shader.module.module;
.batch_id = batch_id, self.batch_size = if (spv_module.reflection_infos.has_atomics) 1 else shader.runtimes.len;
.group_count = group_count,
.base_group_x = @as(usize, @intCast(base_group_x)),
.base_group_y = @as(usize, @intCast(base_group_y)),
.base_group_z = @as(usize, @intCast(base_group_z)),
.group_count_x = @as(usize, @intCast(group_count_x)),
.group_count_y = @as(usize, @intCast(group_count_y)),
.group_count_z = @as(usize, @intCast(group_count_z)),
.invocations_per_workgroup = invocations_per_workgroup,
.local_size = local_size,
.pipeline = pipeline,
};
wg.async(self.device.interface.io(), runWrapper, .{run_data}); const allocator = self.device.interface.device_allocator.allocator();
const local_size = try getLocalSize(&shader.runtimes[0].rt, allocator, spv_module);
const local_size_xy = std.math.mul(usize, local_size[0], local_size[1]) catch return VkError.ValidationFailed;
const invocations_per_workgroup = std.math.mul(usize, local_size_xy, local_size[2]) catch return VkError.ValidationFailed;
self.invocation_index.store(0, .monotonic);
var wg: std.Io.Group = .init;
for (0..@min(self.batch_size, group_count)) |batch_id| {
const run_data: RunData = .{
.self = self,
.batch_id = batch_id,
.group_count = group_count,
.base_group_x = @as(usize, @intCast(base_group_x)),
.base_group_y = @as(usize, @intCast(base_group_y)),
.base_group_z = @as(usize, @intCast(base_group_z)),
.group_count_x = @as(usize, @intCast(group_count_x)),
.group_count_y = @as(usize, @intCast(group_count_y)),
.group_count_z = @as(usize, @intCast(group_count_z)),
.invocations_per_workgroup = invocations_per_workgroup,
.local_size = local_size,
.pipeline = pipeline,
};
wg.async(self.device.interface.io(), runWrapper, .{run_data});
}
wg.await(self.device.interface.io()) catch return VkError.DeviceLost;
} }
wg.await(self.device.interface.io()) catch return VkError.DeviceLost;
} }
fn runWrapper(data: RunData) void { fn runWrapper(data: RunData) void {
@@ -138,17 +139,9 @@ inline fn run(data: RunData) !void {
var barrier_runtimes: []spv.Runtime = &.{}; var barrier_runtimes: []spv.Runtime = &.{};
var barrier_statuses: []spv.Runtime.EntryPointStatus = &.{}; var barrier_statuses: []spv.Runtime.EntryPointStatus = &.{};
if (uses_control_barrier) { var initialized_barrier_runtimes: usize = 0;
barrier_runtimes = try allocator.alloc(spv.Runtime, data.invocations_per_workgroup);
barrier_statuses = try allocator.alloc(spv.Runtime.EntryPointStatus, data.invocations_per_workgroup);
for (barrier_runtimes) |*barrier_rt| {
barrier_rt.* = try spv.Runtime.init(allocator, rt.mod, rt.image_api);
try barrier_rt.copySpecializationConstantsFrom(allocator, rt);
}
}
defer { defer {
for (barrier_runtimes) |*barrier_rt| { for (barrier_runtimes[0..initialized_barrier_runtimes]) |*barrier_rt| {
barrier_rt.resetInvocation(allocator); barrier_rt.resetInvocation(allocator);
barrier_rt.deinit(allocator); barrier_rt.deinit(allocator);
} }
@@ -156,6 +149,19 @@ inline fn run(data: RunData) !void {
allocator.free(barrier_statuses); allocator.free(barrier_statuses);
} }
if (uses_control_barrier) {
barrier_runtimes = try allocator.alloc(spv.Runtime, data.invocations_per_workgroup);
barrier_statuses = try allocator.alloc(spv.Runtime.EntryPointStatus, data.invocations_per_workgroup);
for (barrier_runtimes) |*barrier_rt| {
barrier_rt.* = try spv.Runtime.init(allocator, rt.mod, rt.image_api);
initialized_barrier_runtimes += 1;
try barrier_rt.copySpecializationConstantsFrom(allocator, rt);
try prepareRuntime(data.self, barrier_rt);
}
} else {
try prepareRuntime(data.self, rt);
}
var group_index: usize = data.batch_id; var group_index: usize = data.batch_id;
while (group_index < data.group_count) : (group_index += data.self.batch_size) { while (group_index < data.group_count) : (group_index += data.self.batch_size) {
var modulo: usize = group_index; var modulo: usize = group_index;
@@ -188,10 +194,6 @@ inline fn run(data: RunData) !void {
defer rt.destroyWorkgroupMemory(allocator, workgroup_memory); defer rt.destroyWorkgroupMemory(allocator, workgroup_memory);
rt.resetInvocation(allocator); rt.resetInvocation(allocator);
if (rt.specialization_constants.count() != 0)
try rt.applySpecializationInvocationLayout(allocator);
try ExecutionDevice.writeDescriptorSets(data.self.state, rt);
try rt.populatePushConstants(data.self.state.push_constant_blob[0..]);
try rt.bindWorkgroupMemory(workgroup_memory); try rt.bindWorkgroupMemory(workgroup_memory);
try setupWorkgroupBuiltins(data.self, rt, data.local_size, group_count_vec, group_id_vec); try setupWorkgroupBuiltins(data.self, rt, data.local_size, group_count_vec, group_id_vec);
@@ -228,6 +230,16 @@ inline fn run(data: RunData) !void {
} }
} }
fn prepareRuntime(self: *Self, rt: *spv.Runtime) !void {
const allocator = self.device.interface.device_allocator.allocator();
rt.resetInvocation(allocator);
if (rt.specialization_constants.count() != 0)
try rt.applySpecializationInvocationLayout(allocator);
try ExecutionDevice.writeDescriptorSets(self.state, rt);
try rt.populatePushConstants(self.state.push_constant_blob[0..]);
}
fn runBarrierWorkgroup( fn runBarrierWorkgroup(
data: RunData, data: RunData,
runtimes: []spv.Runtime, runtimes: []spv.Runtime,
@@ -242,10 +254,6 @@ fn runBarrierWorkgroup(
defer runtimes[0].destroyWorkgroupMemory(allocator, workgroup_memory); defer runtimes[0].destroyWorkgroupMemory(allocator, workgroup_memory);
for (runtimes, 0..) |*rt, i| { for (runtimes, 0..) |*rt, i| {
rt.resetInvocation(allocator); rt.resetInvocation(allocator);
if (rt.specialization_constants.count() != 0)
try rt.applySpecializationInvocationLayout(allocator);
try ExecutionDevice.writeDescriptorSets(data.self.state, rt);
try rt.populatePushConstants(data.self.state.push_constant_blob[0..]);
try rt.bindWorkgroupMemory(workgroup_memory); try rt.bindWorkgroupMemory(workgroup_memory);
try setupWorkgroupBuiltins(data.self, rt, data.local_size, group_count, group_id); try setupWorkgroupBuiltins(data.self, rt, data.local_size, group_count, group_id);
try setupSubgroupBuiltins(data.self, rt, data.local_size, group_id, i); try setupSubgroupBuiltins(data.self, rt, data.local_size, group_id, i);
+8 -6
View File
@@ -246,14 +246,16 @@ fn drawCall(self: *Self, bounded_allocator: *BoundedAllocator, vertex_count: usi
}; };
const pipeline = self.state.pipeline orelse return VkError.InvalidPipelineDrv; const pipeline = self.state.pipeline orelse return VkError.InvalidPipelineDrv;
const vertex_shader = pipeline.stages.getPtrAssertContains(.vertex); if (comptime !base.config.soft_ir_interpreter) {
for (vertex_shader.runtimes[0..]) |*runtime| { const vertex_shader = pipeline.stages.getPtrAssertContains(.vertex);
ExecutionDevice.writeDescriptorSets(self.state, &runtime.rt) catch return VkError.Unknown; for (vertex_shader.runtimes) |*runtime| {
}
if (pipeline.stages.getPtr(.fragment)) |fragment_shader| {
for (fragment_shader.runtimes[0..]) |*runtime| {
ExecutionDevice.writeDescriptorSets(self.state, &runtime.rt) catch return VkError.Unknown; ExecutionDevice.writeDescriptorSets(self.state, &runtime.rt) catch return VkError.Unknown;
} }
if (pipeline.stages.getPtr(.fragment)) |fragment_shader| {
for (fragment_shader.runtimes) |*runtime| {
ExecutionDevice.writeDescriptorSets(self.state, &runtime.rt) catch return VkError.Unknown;
}
}
} }
self.vertexShaderStage(allocator, &draw_call, vertex_count, instance_count, first_vertex, first_instance, indices, primitive_restart) catch |err| { self.vertexShaderStage(allocator, &draw_call, vertex_count, instance_count, first_vertex, first_instance, indices, primitive_restart) catch |err| {
+3
View File
@@ -36,6 +36,9 @@ pub fn shaderInvocation(
inputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolationLocation, inputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolationLocation,
derivative_inputs: ?DerivativeInputs, derivative_inputs: ?DerivativeInputs,
) SpvRuntimeError!InvocationResult { ) SpvRuntimeError!InvocationResult {
if (comptime base.config.soft_ir_interpreter)
return SpvRuntimeError.InvalidSpirV;
var fragment_inputs = inputs; var fragment_inputs = inputs;
errdefer freeOwnedInputs(allocator, fragment_inputs); errdefer freeOwnedInputs(allocator, fragment_inputs);
+1 -1
View File
@@ -447,7 +447,7 @@ fn rasterizeTransformedPoint(
const point_min_x = vertex.position[0] - (point_size / 2.0); const point_min_x = vertex.position[0] - (point_size / 2.0);
const point_min_y = vertex.position[1] - (point_size / 2.0); const point_min_y = vertex.position[1] - (point_size / 2.0);
const pipeline = draw_call.renderer.state.pipeline orelse return; const pipeline = draw_call.renderer.state.pipeline orelse return;
const has_fragment_shader = pipeline.stages.getPtr(.fragment) != null; const has_fragment_shader = if (comptime base.config.soft_ir_interpreter) false else pipeline.stages.getPtr(.fragment) != null;
var py = min_y; var py = min_y;
while (py <= max_y) : (py += 1) { while (py <= max_y) : (py += 1) {
+8 -3
View File
@@ -134,7 +134,12 @@ fn drawLineBresenham(
const pipeline = draw_call.renderer.state.pipeline orelse return; const pipeline = draw_call.renderer.state.pipeline orelse return;
const fragment_stage = pipeline.stages.getPtr(.fragment); const fragment_stage = pipeline.stages.getPtr(.fragment);
const runtimes_count = if (fragment_stage) |stage| stage.runtimes.len else 1; const runtimes_count = if (comptime base.config.soft_ir_interpreter)
1
else if (fragment_stage) |stage|
stage.runtimes.len
else
1;
if (runtimes_count == 0) if (runtimes_count == 0)
return; return;
@@ -170,7 +175,7 @@ fn drawLineBresenham(
.color_attachment_access = color_attachment_access, .color_attachment_access = color_attachment_access,
.depth_attachment_access = depth_attachment_access, .depth_attachment_access = depth_attachment_access,
.stencil_attachment_access = stencil_attachment_access, .stencil_attachment_access = stencil_attachment_access,
.has_fragment_shader = fragment_stage != null, .has_fragment_shader = if (comptime base.config.soft_ir_interpreter) false else fragment_stage != null,
}; };
draw_call.rasterizer_wait_group.async(io, runWrapper, .{run_data}); draw_call.rasterizer_wait_group.async(io, runWrapper, .{run_data});
@@ -191,7 +196,7 @@ fn drawLineDiamond(
) VkError!void { ) VkError!void {
const pipeline = draw_call.renderer.state.pipeline orelse return; const pipeline = draw_call.renderer.state.pipeline orelse return;
const fragment_stage = pipeline.stages.getPtr(.fragment); const fragment_stage = pipeline.stages.getPtr(.fragment);
const has_fragment_shader = fragment_stage != null; const has_fragment_shader = if (comptime base.config.soft_ir_interpreter) false else fragment_stage != null;
const batch_id: usize = 0; const batch_id: usize = 0;
const min_x: i32 = @intFromFloat(@floor(@min(v0.position[0], v1.position[0]) - 1.0)); const min_x: i32 = @intFromFloat(@floor(@min(v0.position[0], v1.position[0]) - 1.0));
@@ -89,24 +89,37 @@ pub fn drawTriangle(
} }
const fragment_stage = pipeline.stages.getPtr(.fragment); const fragment_stage = pipeline.stages.getPtr(.fragment);
const fragment_uses_derivatives = if (fragment_stage) |stage| const fragment_uses_derivatives = if (comptime base.config.soft_ir_interpreter)
false
else if (fragment_stage) |stage|
stage.module.module.reflection_infos.needs_derivatives stage.module.module.reflection_infos.needs_derivatives
else else
false; false;
const early_fragment_tests = if (fragment_stage) |stage| const early_fragment_tests = if (comptime base.config.soft_ir_interpreter)
false
else if (fragment_stage) |stage|
stage.module.module.reflection_infos.early_fragment_tests stage.module.module.reflection_infos.early_fragment_tests
else else
false; false;
const fragment_uses_sample_id = if (fragment_stage) |stage| const fragment_uses_sample_id = if (comptime base.config.soft_ir_interpreter)
false
else if (fragment_stage) |stage|
stage.module.module.builtins.get(.SampleId) != null stage.module.module.builtins.get(.SampleId) != null
else else
false; false;
const fragment_uses_centroid = if (fragment_stage) |stage| const fragment_uses_centroid = if (comptime base.config.soft_ir_interpreter)
false
else if (fragment_stage) |stage|
fragmentStageUsesInputDecoration(stage, .Centroid) fragmentStageUsesInputDecoration(stage, .Centroid)
else else
false; false;
const runtimes_count = if (fragment_stage) |stage| stage.runtimes.len else 1; const runtimes_count = if (comptime base.config.soft_ir_interpreter)
1
else if (fragment_stage) |stage|
stage.runtimes.len
else
1;
if (runtimes_count == 0) if (runtimes_count == 0)
return; return;
const sample_count = pipeline_data.multisample.rasterization_samples.toInt(); const sample_count = pipeline_data.multisample.rasterization_samples.toInt();
@@ -173,7 +186,7 @@ pub fn drawTriangle(
.depth_attachment_access = depth_attachment_access, .depth_attachment_access = depth_attachment_access,
.stencil_attachment_access = stencil_attachment_access, .stencil_attachment_access = stencil_attachment_access,
.front_face = front_face, .front_face = front_face,
.has_fragment_shader = fragment_stage != null, .has_fragment_shader = if (comptime base.config.soft_ir_interpreter) false else fragment_stage != null,
.early_fragment_tests = early_fragment_tests, .early_fragment_tests = early_fragment_tests,
.fragment_uses_derivatives = fragment_uses_derivatives, .fragment_uses_derivatives = fragment_uses_derivatives,
.fragment_uses_sample_id = fragment_uses_sample_id, .fragment_uses_sample_id = fragment_uses_sample_id,
+14 -20
View File
@@ -43,26 +43,20 @@ pub fn runWrapper(data: RunData) void {
inline fn run(data: RunData) !void { inline fn run(data: RunData) !void {
const shader = data.pipeline.stages.getPtrAssertContains(.vertex); const shader = data.pipeline.stages.getPtrAssertContains(.vertex);
if (comptime base.config.soft_ir_interpreter) { if (comptime base.config.soft_ir_interpreter) {
// Interpolation decorations are not represented in the common IR yet, return ir_vertex.run(
// so fragment-linked graphics pipelines retain the SPIR-V path. data.allocator,
if (data.pipeline.stages.getPtr(.fragment) == null) { data.pipeline,
if (shader.interpreter) |*interpreter_shader| { shader,
return ir_vertex.run( data.batch_id,
data.allocator, data.batch_size,
data.pipeline, data.vertex_count,
interpreter_shader, data.first_vertex,
data.batch_id, data.first_instance,
data.batch_size, data.indices,
data.vertex_count, data.primitive_restart,
data.first_vertex, data.instance_index,
data.first_instance, data.draw_call,
data.indices, );
data.primitive_restart,
data.instance_index,
data.draw_call,
);
}
}
} }
const runtime = &shader.runtimes[data.batch_id]; const runtime = &shader.runtimes[data.batch_id];
const mutex = &runtime.mutex; const mutex = &runtime.mutex;
+33 -41
View File
@@ -47,24 +47,25 @@ interfaces: []const ?InterfaceBinding,
pub fn compile(backing_allocator: std.mem.Allocator, module: *const module_ir.Module) !Self { pub fn compile(backing_allocator: std.mem.Allocator, module: *const module_ir.Module) !Self {
try ir.validator.validate(module); try ir.validator.validate(module);
var result: Self = undefined; var arena = std.heap.ArenaAllocator.init(backing_allocator);
result.arena = std.heap.ArenaAllocator.init(backing_allocator); errdefer arena.deinit();
errdefer result.arena.deinit();
var lowerer = try Lowerer.init(result.arena.allocator(), module); var lowerer = try Lowerer.init(arena.allocator(), module);
try lowerer.lower(); try lowerer.lower();
result.stage = module.stage; return .{
result.entry_pc = lowerer.entry_pc; .arena = arena,
result.register_count = lowerer.register_count; .stage = module.stage,
result.scratch_count = lowerer.scratch_count; .entry_pc = lowerer.entry_pc,
result.code = lowerer.code.items; .register_count = lowerer.register_count,
result.edges = lowerer.edges.items; .scratch_count = lowerer.scratch_count,
result.copies = lowerer.copies.items; .code = lowerer.code.items,
result.branches = lowerer.branches.items; .edges = lowerer.edges.items,
result.initializers = lowerer.initializers.items; .copies = lowerer.copies.items,
result.interfaces = lowerer.interfaces; .branches = lowerer.branches.items,
return result; .initializers = lowerer.initializers.items,
.interfaces = lowerer.interfaces,
};
} }
pub fn deinit(self: *Self) void { pub fn deinit(self: *Self) void {
@@ -158,41 +159,32 @@ const Lowerer = struct {
fn allocate(self: *Lowerer, type_id: ids.TypeId) !bc.Span { fn allocate(self: *Lowerer, type_id: ids.TypeId) !bc.Span {
const ty = self.module.types.get(type_id) orelse return CompileError.UnsupportedType; const ty = self.module.types.get(type_id) orelse return CompileError.UnsupportedType;
var kind: bc.ValueKind = undefined;
var components: u8 = 1; var components: u8 = 1;
const kind: bc.ValueKind = switch (ty.*) {
switch (ty.*) { .boolean => .boolean,
.boolean => kind = .boolean, .integer => |integer| if (integer.bits == 32)
.integer => |integer| { if (integer.signedness == .signed) .signed_integer else .unsigned_integer
if (integer.bits != 32) else
return CompileError.UnsupportedType; return CompileError.UnsupportedType,
.floating => |floating| if (floating.bits == 32)
kind = if (integer.signedness == .signed) .signed_integer else .unsigned_integer; .floating
}, else
.floating => |floating| { return CompileError.UnsupportedType,
if (floating.bits != 32) .vector => |vector| blk: {
return CompileError.UnsupportedType;
kind = .floating;
},
.vector => |vector| {
const element = self.module.types.get(vector.element_type) orelse return CompileError.UnsupportedType; const element = self.module.types.get(vector.element_type) orelse return CompileError.UnsupportedType;
components = vector.length; components = vector.length;
break :blk switch (element.*) {
kind = switch (element.*) {
.boolean => .boolean, .boolean => .boolean,
.integer => |integer| blk: { .integer => |integer| if (integer.bits == 32)
if (integer.bits != 32) if (integer.signedness == .signed) .signed_integer else .unsigned_integer
return CompileError.UnsupportedType; else
return CompileError.UnsupportedType,
break :blk if (integer.signedness == .signed) .signed_integer else .unsigned_integer;
},
.floating => |floating| if (floating.bits == 32) .floating else return CompileError.UnsupportedType, .floating => |floating| if (floating.bits == 32) .floating else return CompileError.UnsupportedType,
else => return CompileError.UnsupportedType, else => return CompileError.UnsupportedType,
}; };
}, },
else => return CompileError.UnsupportedType, else => return CompileError.UnsupportedType,
} };
const end = std.math.add(usize, self.register_count, components) catch return CompileError.TooManyRegisters; const end = std.math.add(usize, self.register_count, components) catch return CompileError.TooManyRegisters;
if (end > @as(usize, std.math.maxInt(bc.Register)) + 1) if (end > @as(usize, std.math.maxInt(bc.Register)) + 1)
+16 -19
View File
@@ -21,17 +21,15 @@ program: Program,
runtimes: []RuntimeSlot, runtimes: []RuntimeSlot,
workgroup_size: ?[3]u32, workgroup_size: ?[3]u32,
/// Compiles a stage when the current interpreter can execute its complete pub fn compile(allocator: std.mem.Allocator, module: *SoftShaderModule, stage: *const vk.PipelineShaderStageCreateInfo, runtime_count: usize) VkError!Self {
/// interface. `null` deliberately selects the existing SPIR-V runtime. const expected_stage = commonStage(stage.stage) orelse {
pub fn compile( std.log.scoped(.IrInterpreter).err("unsupported shader stage", .{});
allocator: std.mem.Allocator, return VkError.ValidationFailed;
module: *SoftShaderModule, };
stage: *const vk.PipelineShaderStageCreateInfo, if (expected_stage == .fragment) {
runtime_count: usize, std.log.scoped(.IrInterpreter).err("fragment shaders are not supported", .{});
) VkError!?Self { return VkError.ValidationFailed;
const expected_stage = commonStage(stage.stage) orelse return null; }
if (expected_stage == .fragment)
return null;
const specializations = try specializationValues(allocator, stage.p_specialization_info); const specializations = try specializationValues(allocator, stage.p_specialization_info);
defer if (specializations.len != 0) allocator.free(specializations); defer if (specializations.len != 0) allocator.free(specializations);
@@ -43,25 +41,24 @@ pub fn compile(
}) catch |err| { }) catch |err| {
if (err == error.OutOfMemory) if (err == error.OutOfMemory)
return VkError.OutOfDeviceMemory; return VkError.OutOfDeviceMemory;
std.log.scoped(.SoftIrInterpreter).debug("IR translation fallback: {s}", .{@errorName(err)}); std.log.scoped(.IrInterpreter).err("IR translation failed: {s}", .{@errorName(err)});
return null; return VkError.ValidationFailed;
}; };
defer module_ir.deinit(); defer module_ir.deinit();
var program = Program.compile(allocator, &module_ir) catch |err| { var program = Program.compile(allocator, &module_ir) catch |err| {
if (err == error.OutOfMemory) if (err == error.OutOfMemory)
return VkError.OutOfDeviceMemory; return VkError.OutOfDeviceMemory;
std.log.scoped(.SoftIrInterpreter).debug("bytecode lowering fallback: {s}", .{@errorName(err)}); std.log.scoped(.IrInterpreter).err("bytecode lowering failed: {s}", .{@errorName(err)});
return null; return VkError.ValidationFailed;
}; };
errdefer program.deinit(); errdefer program.deinit();
if (!hasCompatibleInterface(&program, expected_stage) or if (!hasCompatibleInterface(&program, expected_stage) or
(expected_stage == .compute and module_ir.execution_modes.workgroup_size == null)) (expected_stage == .compute and module_ir.execution_modes.workgroup_size == null))
{ {
std.log.scoped(.SoftIrInterpreter).debug("stage interface or execution modes require the SPIR-V runtime", .{}); std.log.scoped(.IrInterpreter).err("unsupported stage interface or execution modes", .{});
program.deinit(); return VkError.ValidationFailed;
return null;
} }
const runtimes = allocator.alloc(RuntimeSlot, runtime_count) catch return VkError.OutOfDeviceMemory; const runtimes = allocator.alloc(RuntimeSlot, runtime_count) catch return VkError.OutOfDeviceMemory;
@@ -76,7 +73,7 @@ pub fn compile(
initialized += 1; initialized += 1;
} }
std.log.scoped(.SoftIrInterpreter).debug("compiled {s} stage to {d} bytecode instructions", .{ std.log.scoped(.IrInterpreter).debug("compiled {s} stage to {d} bytecode instructions", .{
@tagName(expected_stage), @tagName(expected_stage),
program.code.len, program.code.len,
}); });
+1 -13
View File
@@ -3,7 +3,6 @@ const vk = @import("vulkan");
const base = @import("base"); const base = @import("base");
const shader_ir = @import("shader_ir"); const shader_ir = @import("shader_ir");
const bc = @import("bytecode.zig");
const Shader = @import("Shader.zig"); const Shader = @import("Shader.zig");
const SoftPipeline = @import("../SoftPipeline.zig"); const SoftPipeline = @import("../SoftPipeline.zig");
const Renderer = @import("../device/Renderer.zig"); const Renderer = @import("../device/Renderer.zig");
@@ -58,14 +57,7 @@ pub fn run(
} }
} }
fn populateInputs( fn populateInputs(runtime: anytype, program: *const @import("Program.zig"), pipeline: *SoftPipeline, draw_call: *Renderer.DrawCall, vertex_index: u32, instance_index: u32) VkError!void {
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| { for (program.interfaces, 0..) |optional_binding, index| {
const binding = optional_binding orelse continue; const binding = optional_binding orelse continue;
if (binding.direction != .input) if (binding.direction != .input)
@@ -167,7 +159,3 @@ fn findBinding(bindings: []const vk.VertexInputBindingDescription, binding: u32)
if (description.binding == binding) return description; if (description.binding == binding) return description;
return null; return null;
} }
comptime {
_ = bc;
}
+8 -4
View File
@@ -34,6 +34,7 @@ pub const VTable = struct {
releasePhysicalDevices: *const fn (*Self, std.mem.Allocator) VkError!void, releasePhysicalDevices: *const fn (*Self, std.mem.Allocator) VkError!void,
requestPhysicalDevices: *const fn (*Self, std.mem.Allocator, []lib.drm.Card) VkError!void, requestPhysicalDevices: *const fn (*Self, std.mem.Allocator, []lib.drm.Card) VkError!void,
io: *const fn (*Self) std.Io, io: *const fn (*Self) std.Io,
enumerate_drm_devices: bool = true,
}; };
pub const DispatchTable = struct { pub const DispatchTable = struct {
@@ -154,10 +155,13 @@ pub fn releasePhysicalDevices(self: *Self, allocator: std.mem.Allocator) VkError
} }
pub fn requestPhysicalDevices(self: *Self, allocator: std.mem.Allocator) VkError!void { pub fn requestPhysicalDevices(self: *Self, allocator: std.mem.Allocator) VkError!void {
const devices = try drm.enumerateDrmPhysicalDevices(allocator, self); if (self.vtable.enumerate_drm_devices) {
defer allocator.free(devices); const devices = try drm.enumerateDrmPhysicalDevices(allocator, self);
defer allocator.free(devices);
try self.vtable.requestPhysicalDevices(self, allocator, devices); try self.vtable.requestPhysicalDevices(self, allocator, devices);
} else {
try self.vtable.requestPhysicalDevices(self, allocator, &.{});
}
if (self.physical_devices.items.len == 0) { if (self.physical_devices.items.len == 0) {
std.log.scoped(.vkCreateInstance).err("No VkPhysicalDevice found", .{}); std.log.scoped(.vkCreateInstance).err("No VkPhysicalDevice found", .{});