fixing compute shared atomic memory, fixing vertex/fragment payloads, implementing early fragment depth tests
Test / build_and_test (push) Successful in 2m9s
Build / build (push) Successful in 2m35s

This commit is contained in:
2026-06-29 14:31:31 +02:00
parent 7c6e0dc2a8
commit 14ce2c6f9b
13 changed files with 256 additions and 50 deletions
+2 -2
View File
@@ -32,8 +32,8 @@
// Soft dependencies // Soft dependencies
.SPIRV_Interpreter = .{ .SPIRV_Interpreter = .{
.url = "git+https://github.com/Kbz-8/SPIRV-Interpreter#b9b6087fef5a61a3d9e3187ff7c66dffc54603da", .url = "git+https://github.com/Kbz-8/SPIRV-Interpreter#fcf303a0c62a66ff32fc92fe9c92ca9294f72caf",
.hash = "SPIRV_Interpreter-0.0.1-ajmpn9JxCADthSmyktTCV_jIA8iJU_aiM_kboXqfPDDD", .hash = "SPIRV_Interpreter-0.0.1-ajmpnx3dCAAfuabTUWPWdTVqPGBPeSUCCTI-ncKowi5D",
.lazy = true, .lazy = true,
}, },
//.SPIRV_Interpreter = .{ //.SPIRV_Interpreter = .{
+23 -3
View File
@@ -194,6 +194,7 @@ fn createShader(
runtimes_count: usize, runtimes_count: usize,
) VkError!Shader { ) VkError!Shader {
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 runtimes = runtimes_allocator.alloc(Runtime, runtimes_count) catch return VkError.OutOfDeviceMemory; const runtimes = runtimes_allocator.alloc(Runtime, runtimes_count) catch return VkError.OutOfDeviceMemory;
var initialized: usize = 0; var initialized: usize = 0;
var module_ref = false; var module_ref = false;
@@ -212,7 +213,7 @@ fn createShader(
const image_api = imageApi(); const image_api = imageApi();
var cache_hit = false; var cache_hit = false;
if (cache) |pipeline_cache| { if (cache) |pipeline_cache| {
if (try pipeline_cache.cloneRuntime(runtimes_allocator, module, entry, stage.p_specialization_info, image_api)) |runtime| { if (try pipeline_cache.cloneRuntime(runtimes_allocator, module, entry, execution_model, stage.p_specialization_info, image_api)) |runtime| {
runtimes[0] = .{ runtimes[0] = .{
.mutex = .init, .mutex = .init,
.rt = runtime, .rt = runtime,
@@ -226,7 +227,7 @@ fn createShader(
for (runtimes[initialized..]) |*runtime| { for (runtimes[initialized..]) |*runtime| {
runtime.* = .{ runtime.* = .{
.mutex = .init, .mutex = .init,
.rt = (try cache.?.cloneRuntime(runtimes_allocator, module, entry, stage.p_specialization_info, image_api)).?, .rt = (try cache.?.cloneRuntime(runtimes_allocator, module, entry, execution_model, stage.p_specialization_info, image_api)).?,
}; };
initialized += 1; initialized += 1;
} }
@@ -240,7 +241,7 @@ fn createShader(
} }
if (cache) |pipeline_cache| { if (cache) |pipeline_cache| {
try pipeline_cache.storeRuntimeTemplate(object_allocator, cache_allocator, module, entry, stage.p_specialization_info, image_api); try pipeline_cache.storeRuntimeTemplate(object_allocator, cache_allocator, module, entry, execution_model, stage.p_specialization_info, image_api);
} }
} }
@@ -258,10 +259,29 @@ fn initRuntime(allocator: std.mem.Allocator, module: *SoftShaderModule, stage: *
}; };
errdefer runtime.deinit(allocator); errdefer runtime.deinit(allocator);
const entry = runtime.getEntryPointByNameAndExecutionModel(std.mem.span(stage.p_name), executionModelForStage(stage.stage) orelse return VkError.Unknown) catch |err| {
std.log.scoped(.SpvRuntimeInit).err("SPIR-V Runtime failed to select entry point, {s}", .{@errorName(err)});
return VkError.Unknown;
};
runtime.selectEntryPoint(entry) catch |err| {
std.log.scoped(.SpvRuntimeInit).err("SPIR-V Runtime failed to activate entry point, {s}", .{@errorName(err)});
return VkError.Unknown;
};
try applySpecialization(&runtime, allocator, stage.p_specialization_info); try applySpecialization(&runtime, allocator, stage.p_specialization_info);
return runtime; return runtime;
} }
fn executionModelForStage(stage: vk.ShaderStageFlags) ?spv.spv.SpvExecutionModel {
if (stage.vertex_bit) return .Vertex;
if (stage.tessellation_control_bit) return .TessellationControl;
if (stage.tessellation_evaluation_bit) return .TessellationEvaluation;
if (stage.geometry_bit) return .Geometry;
if (stage.fragment_bit) return .Fragment;
if (stage.compute_bit) return .GLCompute;
return null;
}
fn applySpecialization(runtime: *spv.Runtime, allocator: std.mem.Allocator, specialization: ?*const vk.SpecializationInfo) VkError!void { fn applySpecialization(runtime: *spv.Runtime, allocator: std.mem.Allocator, specialization: ?*const vk.SpecializationInfo) VkError!void {
const info = specialization orelse return; const info = specialization orelse return;
const map = info.p_map_entries orelse return; const map = info.p_map_entries orelse return;
+15 -2
View File
@@ -26,6 +26,7 @@ const CacheRecord = extern struct {
const CachedShader = struct { const CachedShader = struct {
module: *SoftShaderModule, module: *SoftShaderModule,
entry: []u8, entry: []u8,
execution_model: spv.spv.SpvExecutionModel,
specs: []SpecConstant, specs: []SpecConstant,
runtime: spv.Runtime, runtime: spv.Runtime,
@@ -81,6 +82,7 @@ pub fn cloneRuntime(
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
module: *SoftShaderModule, module: *SoftShaderModule,
entry: []const u8, entry: []const u8,
execution_model: spv.spv.SpvExecutionModel,
specialization: ?*const vk.SpecializationInfo, specialization: ?*const vk.SpecializationInfo,
image_api: spv.Runtime.ImageAPI, image_api: spv.Runtime.ImageAPI,
) VkError!?spv.Runtime { ) VkError!?spv.Runtime {
@@ -89,8 +91,14 @@ pub fn cloneRuntime(
defer self.mutex.unlock(io); defer self.mutex.unlock(io);
for (self.shaders.items) |*shader| { for (self.shaders.items) |*shader| {
if (shader.module == module and std.mem.eql(u8, shader.entry, entry) and specsEqual(shader.specs, specialization)) { if (shader.module == module and shader.execution_model == execution_model and std.mem.eql(u8, shader.entry, entry) and specsEqual(shader.specs, specialization)) {
return spv.Runtime.initFrom(allocator, &shader.runtime, image_api) catch return VkError.OutOfDeviceMemory; var runtime = spv.Runtime.initFrom(allocator, &shader.runtime, image_api) catch return VkError.OutOfDeviceMemory;
errdefer runtime.deinit(allocator);
const entry_point = runtime.getEntryPointByNameAndExecutionModel(entry, execution_model) catch return VkError.Unknown;
runtime.selectEntryPoint(entry_point) catch return VkError.Unknown;
return runtime;
} }
} }
@@ -103,6 +111,7 @@ pub fn storeRuntimeTemplate(
data_allocator: std.mem.Allocator, data_allocator: std.mem.Allocator,
module: *SoftShaderModule, module: *SoftShaderModule,
entry: []const u8, entry: []const u8,
execution_model: spv.spv.SpvExecutionModel,
specialization: ?*const vk.SpecializationInfo, specialization: ?*const vk.SpecializationInfo,
image_api: spv.Runtime.ImageAPI, image_api: spv.Runtime.ImageAPI,
) VkError!void { ) VkError!void {
@@ -112,6 +121,7 @@ pub fn storeRuntimeTemplate(
for (self.shaders.items) |*shader| { for (self.shaders.items) |*shader| {
if (shader.module == module and if (shader.module == module and
shader.execution_model == execution_model and
std.mem.eql(u8, shader.entry, entry) and std.mem.eql(u8, shader.entry, entry) and
specsEqual(shader.specs, specialization)) specsEqual(shader.specs, specialization))
{ {
@@ -122,6 +132,7 @@ pub fn storeRuntimeTemplate(
var cached: CachedShader = .{ var cached: CachedShader = .{
.module = module, .module = module,
.entry = data_allocator.dupe(u8, entry) catch return VkError.OutOfDeviceMemory, .entry = data_allocator.dupe(u8, entry) catch return VkError.OutOfDeviceMemory,
.execution_model = execution_model,
.specs = try cloneSpecs(data_allocator, specialization), .specs = try cloneSpecs(data_allocator, specialization),
.runtime = spv.Runtime.init(data_allocator, &module.module, image_api) catch return VkError.OutOfDeviceMemory, .runtime = spv.Runtime.init(data_allocator, &module.module, image_api) catch return VkError.OutOfDeviceMemory,
}; };
@@ -141,6 +152,8 @@ pub fn storeRuntimeTemplate(
module.ref(); module.ref();
module_ref = true; module_ref = true;
const entry_point = cached.runtime.getEntryPointByNameAndExecutionModel(entry, execution_model) catch return VkError.Unknown;
cached.runtime.selectEntryPoint(entry_point) catch return VkError.Unknown;
try applySpecialization(&cached.runtime, data_allocator, specialization); try applySpecialization(&cached.runtime, data_allocator, specialization);
try appendCacheRecord(&self.interface, module, entry, cached.specs); try appendCacheRecord(&self.interface, module, entry, cached.specs);
+6 -3
View File
@@ -594,6 +594,9 @@ pub fn sampleImageInt4(image: *SoftImage, image_view: *SoftImageView, sampler: *
const scale_v: f32 = if (sampler.interface.unnormalized_coordinates == .true) 1.0 else @floatFromInt(extent.height); const scale_v: f32 = if (sampler.interface.unnormalized_coordinates == .true) 1.0 else @floatFromInt(extent.height);
const scale_w: f32 = if (sampler.interface.unnormalized_coordinates == .true) 1.0 else @floatFromInt(extent.depth); const scale_w: f32 = if (sampler.interface.unnormalized_coordinates == .true) 1.0 else @floatFromInt(extent.depth);
const ix = @as(i32, @intFromFloat(@floor(coord.u * scale_u))) + offset.x;
const iy = @as(i32, @intFromFloat(@floor(coord.v * scale_v))) + offset.y;
const iz = @as(i32, @intFromFloat(@floor(coord.w * scale_w))) + offset.z;
const color = try readSampledInt4( const color = try readSampledInt4(
image, image,
image_view, image_view,
@@ -601,9 +604,9 @@ pub fn sampleImageInt4(image: *SoftImage, image_view: *SoftImageView, sampler: *
dim, dim,
coord, coord,
mip_level, mip_level,
@as(i32, @intFromFloat(@floor(coord.u * scale_u))) + offset.x, ix,
@as(i32, @intFromFloat(@floor(coord.v * scale_v))) + offset.y, iy,
@as(i32, @intFromFloat(@floor(coord.w * scale_w))) + offset.z, iz,
); );
return swizzleInt4(color, image_view.interface.components); return swizzleInt4(color, image_view.interface.components);
} }
+27 -10
View File
@@ -73,7 +73,7 @@ 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;
const spv_module = &shader.module.module; const spv_module = &shader.module.module;
self.batch_size = shader.runtimes.len; self.batch_size = if (spv_module.reflection_infos.has_atomics) 1 else shader.runtimes.len;
const allocator = self.device.device_allocator.allocator(); const allocator = self.device.device_allocator.allocator();
const local_size = try getLocalSize(&shader.runtimes[0].rt, allocator, spv_module); const local_size = try getLocalSize(&shader.runtimes[0].rt, allocator, spv_module);
@@ -131,7 +131,7 @@ inline fn run(data: RunData) !void {
const rt = &shader.runtimes[data.batch_id].rt; const rt = &shader.runtimes[data.batch_id].rt;
const entry = try rt.getEntryPointByName(shader.entry); const entry = try rt.getEntryPointByName(shader.entry);
const uses_control_barrier = rt.mod.reflection_infos.has_control_barriers; const uses_control_barrier = rt.mod.reflection_infos.has_control_barriers or rt.mod.reflection_infos.has_atomics;
var barrier_runtimes: []spv.Runtime = &.{}; var barrier_runtimes: []spv.Runtime = &.{};
var barrier_statuses: []spv.Runtime.EntryPointStatus = &.{}; var barrier_statuses: []spv.Runtime.EntryPointStatus = &.{};
@@ -146,17 +146,13 @@ inline fn run(data: RunData) !void {
defer { defer {
for (barrier_runtimes) |*barrier_rt| { for (barrier_runtimes) |*barrier_rt| {
barrier_rt.resetInvocation(allocator);
barrier_rt.deinit(allocator); barrier_rt.deinit(allocator);
} }
allocator.free(barrier_runtimes); allocator.free(barrier_runtimes);
allocator.free(barrier_statuses); allocator.free(barrier_statuses);
} }
if (!uses_control_barrier)
try ExecutionDevice.writeDescriptorSets(data.self.state, rt);
try rt.populatePushConstants(data.self.state.push_constant_blob[0..]);
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;
@@ -185,8 +181,16 @@ inline fn run(data: RunData) !void {
continue; continue;
} }
const workgroup_memory = try rt.createWorkgroupMemory(allocator);
defer rt.destroyWorkgroupMemory(allocator, workgroup_memory);
for (0..data.invocations_per_workgroup) |i| { for (0..data.invocations_per_workgroup) |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 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);
const invocation_index = data.self.invocation_index.fetchAdd(1, .monotonic); const invocation_index = data.self.invocation_index.fetchAdd(1, .monotonic);
@@ -208,13 +212,13 @@ inline fn run(data: RunData) !void {
SpvRuntimeError.Killed => continue, SpvRuntimeError.Killed => continue,
else => return err, else => return err,
}; };
try flushWorkgroupMemory(rt, workgroup_memory);
try rt.flushDescriptorSets(allocator);
if (data.self.final_dump != null and data.self.final_dump.? == invocation_index) { if (data.self.final_dump != null and data.self.final_dump.? == invocation_index) {
@branchHint(.cold); @branchHint(.cold);
try dumpResultsTable(allocator, io, rt, false); try dumpResultsTable(allocator, io, rt, false);
} }
try rt.flushDescriptorSets(allocator);
} }
} }
} }
@@ -228,14 +232,19 @@ fn runBarrierWorkgroup(
group_id: @Vector(3, u32), group_id: @Vector(3, u32),
) !void { ) !void {
const allocator = data.self.device.device_allocator.allocator(); const allocator = data.self.device.device_allocator.allocator();
const workgroup_memory = try runtimes[0].createWorkgroupMemory(allocator);
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 ExecutionDevice.writeDescriptorSets(data.self.state, rt);
try rt.populatePushConstants(data.self.state.push_constant_blob[0..]); try rt.populatePushConstants(data.self.state.push_constant_blob[0..]);
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);
statuses[i] = try rt.beginEntryPoint(allocator, entry); statuses[i] = try rt.beginEntryPoint(allocator, entry);
try flushWorkgroupMemory(rt, workgroup_memory);
try rt.flushDescriptorSets(allocator); try rt.flushDescriptorSets(allocator);
} }
@@ -253,12 +262,20 @@ fn runBarrierWorkgroup(
for (runtimes, 0..) |*rt, i| { for (runtimes, 0..) |*rt, i| {
if (statuses[i] == .completed) if (statuses[i] == .completed)
continue; continue;
try rt.bindWorkgroupMemory(workgroup_memory);
statuses[i] = try rt.continueEntryPoint(allocator); statuses[i] = try rt.continueEntryPoint(allocator);
try flushWorkgroupMemory(rt, workgroup_memory);
try rt.flushDescriptorSets(allocator); try rt.flushDescriptorSets(allocator);
} }
} }
} }
fn flushWorkgroupMemory(rt: *spv.Runtime, workgroup_memory: []const spv.Runtime.WorkgroupMemory) spv.Runtime.RuntimeError!void {
for (workgroup_memory) |memory| {
_ = try (try rt.results[memory.result].getValue()).read(memory.bytes);
}
}
fn dumpResultsTable(allocator: std.mem.Allocator, io: std.Io, rt: *spv.Runtime, comptime is_early: bool) !void { fn dumpResultsTable(allocator: std.mem.Allocator, io: std.Io, rt: *spv.Runtime, comptime is_early: bool) !void {
@branchHint(.cold); @branchHint(.cold);
const file = try std.Io.Dir.cwd().createFile( const file = try std.Io.Dir.cwd().createFile(
+7 -1
View File
@@ -101,7 +101,9 @@ fn writeDescriptorValue(value: anytype, payload: DescriptorPayload, descriptor_i
}; };
switch (payload) { switch (payload) {
.raw => |data| _ = try dst.write(data), .raw => |data| {
_ = try dst.write(data);
},
.sampled_image => |data| switch (dst.*) { .sampled_image => |data| switch (dst.*) {
.Image => _ = try dst.write(std.mem.asBytes(&data.image)), .Image => _ = try dst.write(std.mem.asBytes(&data.image)),
.Sampler => _ = try dst.write(std.mem.asBytes(&data.sampler)), .Sampler => _ = try dst.write(std.mem.asBytes(&data.sampler)),
@@ -144,6 +146,10 @@ pub fn writeDescriptorSets(state: *PipelineState, rt: *spv.Runtime) !void {
switch (binding) { switch (binding) {
.buffer => |buffer_data_array| for (buffer_data_array, 0..) |buffer_data, descriptor_index| { .buffer => |buffer_data_array| for (buffer_data_array, 0..) |buffer_data, descriptor_index| {
if (buffer_data.object) |buffer| { if (buffer_data.object) |buffer| {
const memory = buffer.interface.memory orelse continue;
if (@intFromPtr(memory.vtable) == 0)
continue;
const binding_layout = set.?.interface.layout.bindings[binding_index]; const binding_layout = set.?.interface.layout.bindings[binding_index];
const dynamic_offset: vk.DeviceSize = switch (binding_layout.descriptor_type) { const dynamic_offset: vk.DeviceSize = switch (binding_layout.descriptor_type) {
.uniform_buffer_dynamic, .uniform_buffer_dynamic,
+3 -1
View File
@@ -39,6 +39,8 @@ pub const IndexBuffer = struct {
index_type: vk.IndexType, index_type: vk.IndexType,
}; };
pub const InterpolationType = enum { smooth, flat, noperspective };
pub const DynamicState = struct { pub const DynamicState = struct {
viewports: ?[]const vk.Viewport, viewports: ?[]const vk.Viewport,
scissor: ?[]const vk.Rect2D, scissor: ?[]const vk.Rect2D,
@@ -57,7 +59,7 @@ pub const Vertex = struct {
position: F32x4, position: F32x4,
point_size: f32, point_size: f32,
outputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS][4]?struct { outputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS][4]?struct {
interpolation_type: enum { smooth, flat, noperspective }, interpolation_type: InterpolationType,
blob: []u8, blob: []u8,
size: usize, size: usize,
}, },
+15 -11
View File
@@ -57,6 +57,9 @@ pub fn shaderInvocation(
defer mutex.unlock(io); defer mutex.unlock(io);
rt.resetInvocation(allocator); rt.resetInvocation(allocator);
if (rt.specialization_constants.count() != 0)
try rt.applySpecializationInvocationLayout(allocator);
try @import("Device.zig").writeDescriptorSets(draw_call.renderer.state, rt);
try rt.populatePushConstants(draw_call.renderer.state.push_constant_blob[0..]); try rt.populatePushConstants(draw_call.renderer.state.push_constant_blob[0..]);
rt.writeBuiltIn(allocator, std.mem.asBytes(&position), .FragCoord) catch |err| switch (err) { rt.writeBuiltIn(allocator, std.mem.asBytes(&position), .FragCoord) catch |err| switch (err) {
SpvRuntimeError.NotFound => {}, SpvRuntimeError.NotFound => {},
@@ -96,8 +99,8 @@ pub fn shaderInvocation(
var input = fragment_inputs[location][component]; var input = fragment_inputs[location][component];
const result_word = rt.getResultByLocationComponent(@intCast(location), @intCast(component), .input) catch |err| switch (err) { const result_word = rt.getResultByLocationComponent(@intCast(location), @intCast(component), .input) catch |err| switch (err) {
SpvRuntimeError.NotFound => { SpvRuntimeError.NotFound => {
if (input.blob.len != 0) { if (input.size != 0) {
rt.writeInputLocation(input.blob, @intCast(location)) catch |write_err| switch (write_err) { rt.writeInputLocation(input.blob[0..input.size], @intCast(location)) catch |write_err| switch (write_err) {
SpvRuntimeError.NotFound => {}, SpvRuntimeError.NotFound => {},
else => return write_err, else => return write_err,
}; };
@@ -110,11 +113,11 @@ pub fn shaderInvocation(
const has_result_value = rt.results[result_word].variant != null; const has_result_value = rt.results[result_word].variant != null;
const memory_size = if (has_result_value) const memory_size = if (has_result_value)
try rt.getResultMemorySize(result_word) try rt.getResultMemorySize(result_word)
else if (input.blob.len == 0) else if (input.size == 0)
try rt.getInputLocationMemorySize(@intCast(location)) try rt.getInputLocationMemorySize(@intCast(location))
else else
input.blob.len; input.size;
if (input.blob.len == 0) { if (input.size == 0) {
const zeroes = allocator.alloc(u8, memory_size + INTERFACE_BLOB_PADDING) catch return SpvRuntimeError.OutOfMemory; const zeroes = allocator.alloc(u8, memory_size + INTERFACE_BLOB_PADDING) catch return SpvRuntimeError.OutOfMemory;
@memset(zeroes, 0); @memset(zeroes, 0);
fragment_inputs[location][component] = .{ fragment_inputs[location][component] = .{
@@ -125,16 +128,17 @@ pub fn shaderInvocation(
input = fragment_inputs[location][component]; input = fragment_inputs[location][component];
} }
if (input.blob.len != 0) { if (input.size != 0) {
if (!has_result_value or input.blob.len < memory_size) const input_bytes = input.blob[0..input.size];
try rt.writeInputLocation(input.blob, @intCast(location)) if (!has_result_value or input.size < memory_size)
try rt.writeInputLocation(input_bytes, @intCast(location))
else else
try rt.writeInput(allocator, input.blob, result_word); try rt.writeInput(allocator, input_bytes, result_word);
if (derivatives) |derivative| { if (derivatives) |derivative| {
const dx = derivative.dx[location][component]; const dx = derivative.dx[location][component];
const dy = derivative.dy[location][component]; const dy = derivative.dy[location][component];
if (dx.blob.len != 0 and dy.blob.len != 0) { if (dx.size != 0 and dy.size != 0) {
try rt.setDerivativeFromMemory(allocator, result_word, dx.blob, dy.blob); try rt.setDerivativeFromMemory(allocator, result_word, dx.blob[0..dx.size], dy.blob[0..dy.size]);
} }
} }
} }
+1
View File
@@ -431,6 +431,7 @@ fn rasterizeTransformedPoint(
fragment_result.depth orelse vertex.position[2], fragment_result.depth orelse vertex.position[2],
null, null,
fragment_result.sample_mask, fragment_result.sample_mask,
false,
); );
} }
} }
@@ -283,6 +283,7 @@ inline fn run(data: RunData) !void {
data.draw_call.renderer.state.pipeline.?.interface.mode.graphics.multisample.rasterization_samples.toInt(), data.draw_call.renderer.state.pipeline.?.interface.mode.graphics.multisample.rasterization_samples.toInt(),
), ),
fragment_result.sample_mask, fragment_result.sample_mask,
false,
); );
} }
} }
+28 -1
View File
@@ -172,6 +172,31 @@ pub fn depthTestAndUpdate(depth: *RenderTargetAccess, x: usize, y: usize, z: f32
return depthTestAndUpdateAtOffset(depth, offset, z, state); return depthTestAndUpdateAtOffset(depth, offset, z, state);
} }
pub fn depthTestSampleAndUpdate(
io: std.Io,
depth: *RenderTargetAccess,
x: usize,
y: usize,
sample_index: usize,
z: f32,
state: ?vk.PipelineDepthStencilStateCreateInfo,
) VkError!bool {
const depth_offset = targetSampleOffset(depth.*, x, y, sample_index) orelse return false;
depth.mutex.lock(io) catch return VkError.DeviceLost;
defer depth.mutex.unlock(io);
if (state) |depth_state| {
return depthTestAndUpdateAtOffset(depth, depth_offset, z, depth_state);
}
const depth_value = blitter.readFloat4(depth.base[depth_offset..], depth.format);
if (z >= depth_value[0])
return false;
blitter.writeFloat4(zm.f32x4s(z), depth.base[depth_offset..], depth.format);
return true;
}
fn depthTestAndUpdateAtOffset(depth: *RenderTargetAccess, offset: usize, z: f32, state: vk.PipelineDepthStencilStateCreateInfo) bool { fn depthTestAndUpdateAtOffset(depth: *RenderTargetAccess, offset: usize, z: f32, state: vk.PipelineDepthStencilStateCreateInfo) bool {
if (state.depth_test_enable == .false) if (state.depth_test_enable == .false)
return true; return true;
@@ -477,6 +502,7 @@ pub fn writeToTargets(
z: f32, z: f32,
coverage_sample_mask: ?vk.SampleMask, coverage_sample_mask: ?vk.SampleMask,
fragment_sample_mask: ?vk.SampleMask, fragment_sample_mask: ?vk.SampleMask,
depth_already_applied: bool,
) VkError!void { ) VkError!void {
const io = draw_call.renderer.device.interface.io(); const io = draw_call.renderer.device.interface.io();
const pipeline_data = draw_call.renderer.state.pipeline.?.interface.mode.graphics; const pipeline_data = draw_call.renderer.state.pipeline.?.interface.mode.graphics;
@@ -523,7 +549,8 @@ pub fn writeToTargets(
// After work depth test to avoid overwritten depth pixels during fragment invocations. // After work depth test to avoid overwritten depth pixels during fragment invocations.
var depth_passed: ?bool = null; var depth_passed: ?bool = null;
if (depth_attachment_access) |depth| { if (!depth_already_applied and depth_attachment_access != null) {
const depth = depth_attachment_access.?;
const depth_offset = targetSampleOffset(depth.*, x, y, sample_index) orelse continue; const depth_offset = targetSampleOffset(depth.*, x, y, sample_index) orelse continue;
depth.mutex.lock(io) catch return VkError.DeviceLost; depth.mutex.lock(io) catch return VkError.DeviceLost;
@@ -36,6 +36,7 @@ const RunData = struct {
stencil_attachment_access: ?*common.RenderTargetAccess, stencil_attachment_access: ?*common.RenderTargetAccess,
front_face: bool, front_face: bool,
has_fragment_shader: bool, has_fragment_shader: bool,
early_fragment_tests: bool,
fragment_uses_derivatives: bool, fragment_uses_derivatives: bool,
fragment_uses_sample_id: bool, fragment_uses_sample_id: bool,
fragment_uses_centroid: bool, fragment_uses_centroid: bool,
@@ -70,6 +71,10 @@ pub fn drawTriangle(
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|
stage.module.module.reflection_infos.early_fragment_tests
else
false;
const fragment_uses_sample_id = if (fragment_stage) |stage| const fragment_uses_sample_id = if (fragment_stage) |stage|
stage.module.module.builtins.get(.SampleId) != null stage.module.module.builtins.get(.SampleId) != null
else else
@@ -130,6 +135,7 @@ pub fn drawTriangle(
.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 = fragment_stage != null,
.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,
.fragment_uses_centroid = fragment_uses_centroid, .fragment_uses_centroid = fragment_uses_centroid,
@@ -235,6 +241,33 @@ fn triangleCoverageMask(data: RunData, x: i32, y: i32, sample_count: usize) vk.S
return mask; return mask;
} }
fn applyEarlyDepth(data: RunData, coverage_sample_mask: vk.SampleMask, x: i32, y: i32, z: f32, sample_count: usize) VkError!struct {
mask: vk.SampleMask,
applied: bool,
} {
if (!data.early_fragment_tests)
return .{ .mask = coverage_sample_mask, .applied = false };
const depth = data.depth_attachment_access orelse return .{ .mask = coverage_sample_mask, .applied = false };
const pipeline_data = data.draw_call.renderer.state.pipeline.?.interface.mode.graphics;
const io = data.draw_call.renderer.device.interface.io();
var passed_mask: vk.SampleMask = 0;
for (0..sample_count) |sample_index| {
if (sample_index >= @bitSizeOf(vk.SampleMask))
break;
const bit = @as(vk.SampleMask, 1) << @as(u5, @intCast(sample_index));
if ((coverage_sample_mask & bit) == 0)
continue;
if (try common.depthTestSampleAndUpdate(io, depth, @intCast(x), @intCast(y), sample_index, z, pipeline_data.depth_stencil))
passed_mask |= bit;
}
return .{ .mask = passed_mask, .applied = true };
}
fn runWrapper(data: RunData) void { fn runWrapper(data: RunData) void {
@call(.always_inline, run, .{data}) catch |err| { @call(.always_inline, run, .{data}) catch |err| {
std.log.scoped(.@"Rasterization stage").err("triangle fill mode catched a '{s}'", .{@errorName(err)}); std.log.scoped(.@"Rasterization stage").err("triangle fill mode catched a '{s}'", .{@errorName(err)});
@@ -272,8 +305,12 @@ inline fn run(data: RunData) !void {
const b2 = w2 / data.area; const b2 = w2 / data.area;
const z = (b0 * data.v0.position[2]) + (b1 * data.v1.position[2]) + (b2 * data.v2.position[2]); const z = (b0 * data.v0.position[2]) + (b1 * data.v1.position[2]) + (b2 * data.v2.position[2]);
const frag_w = (b0 / data.v0.position[3]) + (b1 / data.v1.position[3]) + (b2 / data.v2.position[3]); const frag_w = (b0 / data.v0.position[3]) + (b1 / data.v1.position[3]) + (b2 / data.v2.position[3]);
const early_depth = try applyEarlyDepth(data, coverage_sample_mask, x, y, z, sample_count);
if (early_depth.mask == 0)
continue;
const interpolation_barycentrics = if (data.fragment_uses_centroid and sample_count > 1) blk: { const interpolation_barycentrics = if (data.fragment_uses_centroid and sample_count > 1) blk: {
const sample_pos = firstCoveredSamplePosition(sample_count, coverage_sample_mask); const sample_pos = firstCoveredSamplePosition(sample_count, early_depth.mask);
const centroid_p = zm.f32x4( const centroid_p = zm.f32x4(
@as(f32, @floatFromInt(x)) + sample_pos.x, @as(f32, @floatFromInt(x)) + sample_pos.x,
@as(f32, @floatFromInt(y)) + sample_pos.y, @as(f32, @floatFromInt(y)) + sample_pos.y,
@@ -305,7 +342,7 @@ inline fn run(data: RunData) !void {
const bit_index: u5 = @intCast(sample_index); const bit_index: u5 = @intCast(sample_index);
const sample_coverage_mask = @as(vk.SampleMask, 1) << bit_index; const sample_coverage_mask = @as(vk.SampleMask, 1) << bit_index;
if ((coverage_sample_mask & sample_coverage_mask) == 0) if ((early_depth.mask & sample_coverage_mask) == 0)
continue; continue;
const inputs = try common.interpolateVertexOutputs(data.allocator, &data.v0, &data.v1, &data.v2, &data.provoking_vertex, input_b0, input_b1, input_b2); const inputs = try common.interpolateVertexOutputs(data.allocator, &data.v0, &data.v1, &data.v2, &data.provoking_vertex, input_b0, input_b1, input_b2);
@@ -344,6 +381,7 @@ inline fn run(data: RunData) !void {
sample_result.depth orelse z, sample_result.depth orelse z,
sample_coverage_mask, sample_coverage_mask,
sample_result.sample_mask, sample_result.sample_mask,
early_depth.applied,
); );
} }
continue; continue;
@@ -423,8 +461,9 @@ inline fn run(data: RunData) !void {
@intCast(x), @intCast(x),
@intCast(y), @intCast(y),
fragment_result.depth orelse z, fragment_result.depth orelse z,
coverage_sample_mask, early_depth.mask,
fragment_result.sample_mask, fragment_result.sample_mask,
early_depth.applied,
); );
} }
} }
+86 -13
View File
@@ -60,8 +60,10 @@ inline fn run(data: RunData) !void {
continue; continue;
} }
} }
rt.resetInvocation(data.allocator); rt.resetInvocation(data.allocator);
if (rt.specialization_constants.count() != 0)
try rt.applySpecializationInvocationLayout(data.allocator);
try @import("Device.zig").writeDescriptorSets(data.draw_call.renderer.state, rt);
try rt.populatePushConstants(data.draw_call.renderer.state.push_constant_blob[0..]); try rt.populatePushConstants(data.draw_call.renderer.state.push_constant_blob[0..]);
const vertex_index_u32: u32 = if (data.indices) |indices| indices[invocation_index] else @intCast(data.first_vertex + invocation_index); const vertex_index_u32: u32 = if (data.indices) |indices| indices[invocation_index] else @intCast(data.first_vertex + invocation_index);
@@ -107,30 +109,101 @@ inline fn run(data: RunData) !void {
try readPointSize(rt, &output.point_size); try readPointSize(rt, &output.point_size);
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| { for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
for (0..4) |component| { const location_result = rt.getResultByLocation(@intCast(location), .output) catch |err| switch (err) {
const result_word = rt.getResultByLocationComponent(@intCast(location), @intCast(component), .output) catch |err| switch (err) { SpvRuntimeError.NotFound => continue,
else => return err,
};
try readVertexOutput(data, output, rt, location, 0, location_result);
for (1..4) |component| {
const component_result = rt.getResultByLocationComponent(@intCast(location), @intCast(component), .output) catch |err| switch (err) {
SpvRuntimeError.NotFound => continue, SpvRuntimeError.NotFound => continue,
else => return err, else => return err,
}; };
const memory_size = try rt.getResultMemorySize(result_word); if (component_result == location_result)
continue;
const result_is_integer = resultIsInteger(rt, result_word); try readVertexOutput(data, output, rt, location, component, component_result);
output.outputs[location][component] = .{
.interpolation_type = if (rt.hasResultDecoration(result_word, .Flat) or result_is_integer) .flat else .smooth, // TODO : handle noperspective
.blob = data.allocator.alloc(u8, memory_size + INTERFACE_BLOB_PADDING) catch return VkError.OutOfDeviceMemory,
.size = memory_size,
};
@memset(output.outputs[location][component].?.blob, 0);
try rt.readOutput(output.outputs[location][component].?.blob, result_word);
} }
} }
try readActiveInterfaceOutputs(data, output, rt, entry);
try rt.flushDescriptorSets(data.allocator); try rt.flushDescriptorSets(data.allocator);
} }
} }
fn readActiveInterfaceOutputs(data: RunData, output: *Renderer.Vertex, rt: *spv.Runtime, entry: spv.SpvWord) !void {
if (entry >= rt.mod.entry_points.items.len)
return;
for (rt.mod.entry_points.items[entry].globals) |global| {
if (global >= rt.results.len)
continue;
const variable = switch (rt.results[global].variant orelse continue) {
.Variable => |v| v,
else => continue,
};
if (variable.storage_class != .Output)
continue;
var location: ?usize = null;
var component: usize = 0;
for (rt.results[global].decorations.items) |decoration| switch (decoration.rtype) {
.Location => location = decoration.literal_1,
.Component => component = decoration.literal_1,
else => {},
};
const target_location = location orelse continue;
if (target_location >= spv.SPIRV_MAX_OUTPUT_LOCATIONS or component >= 4)
continue;
if (output.outputs[target_location][component] != null)
continue;
try readVertexOutput(data, output, rt, target_location, component, global);
}
}
fn readVertexOutput(data: RunData, output: *Renderer.Vertex, rt: *spv.Runtime, location: usize, component: usize, result_word: spv.SpvWord) !void {
const memory_size = try rt.getResultMemorySize(result_word);
const interpolation_type = vertexOutputInterpolationType(data, rt, location, component, result_word);
output.outputs[location][component] = .{
.interpolation_type = interpolation_type,
.blob = data.allocator.alloc(u8, memory_size + INTERFACE_BLOB_PADDING) catch return VkError.OutOfDeviceMemory,
.size = memory_size,
};
@memset(output.outputs[location][component].?.blob, 0);
try rt.readOutput(output.outputs[location][component].?.blob, result_word);
}
fn vertexOutputInterpolationType(data: RunData, rt: *spv.Runtime, location: usize, component: usize, result_word: spv.SpvWord) Renderer.InterpolationType {
const result_is_integer = resultIsInteger(rt, result_word);
const fragment_input_word = if (data.pipeline.stages.getPtr(.fragment)) |fragment_shader|
fragment_shader.runtimes[0].rt.getResultByLocationComponent(@intCast(location), @intCast(component), .input) catch null
else
null;
const fragment_input_is_flat = if (fragment_input_word) |input_word|
data.pipeline.stages.getPtrAssertContains(.fragment).runtimes[0].rt.hasResultOrMemberDecoration(input_word, .Flat)
else
false;
const fragment_input_is_noperspective = if (fragment_input_word) |input_word|
data.pipeline.stages.getPtrAssertContains(.fragment).runtimes[0].rt.hasResultOrMemberDecoration(input_word, .NoPerspective)
else
false;
if (fragment_input_is_flat or result_is_integer)
return .flat;
if (fragment_input_is_noperspective)
return .noperspective;
return .smooth;
}
fn findBindingDescription(binding_descriptions: []const vk.VertexInputBindingDescription, binding: u32) ?vk.VertexInputBindingDescription { fn findBindingDescription(binding_descriptions: []const vk.VertexInputBindingDescription, binding: u32) ?vk.VertexInputBindingDescription {
for (binding_descriptions) |description| { for (binding_descriptions) |description| {
if (description.binding == binding) if (description.binding == binding)