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
.SPIRV_Interpreter = .{
.url = "git+https://github.com/Kbz-8/SPIRV-Interpreter#b9b6087fef5a61a3d9e3187ff7c66dffc54603da",
.hash = "SPIRV_Interpreter-0.0.1-ajmpn9JxCADthSmyktTCV_jIA8iJU_aiM_kboXqfPDDD",
.url = "git+https://github.com/Kbz-8/SPIRV-Interpreter#fcf303a0c62a66ff32fc92fe9c92ca9294f72caf",
.hash = "SPIRV_Interpreter-0.0.1-ajmpnx3dCAAfuabTUWPWdTVqPGBPeSUCCTI-ncKowi5D",
.lazy = true,
},
//.SPIRV_Interpreter = .{
+23 -3
View File
@@ -194,6 +194,7 @@ fn createShader(
runtimes_count: usize,
) VkError!Shader {
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;
var initialized: usize = 0;
var module_ref = false;
@@ -212,7 +213,7 @@ fn createShader(
const image_api = imageApi();
var cache_hit = false;
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] = .{
.mutex = .init,
.rt = runtime,
@@ -226,7 +227,7 @@ fn createShader(
for (runtimes[initialized..]) |*runtime| {
runtime.* = .{
.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;
}
@@ -240,7 +241,7 @@ fn createShader(
}
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);
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);
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 {
const info = specialization 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 {
module: *SoftShaderModule,
entry: []u8,
execution_model: spv.spv.SpvExecutionModel,
specs: []SpecConstant,
runtime: spv.Runtime,
@@ -81,6 +82,7 @@ pub fn cloneRuntime(
allocator: std.mem.Allocator,
module: *SoftShaderModule,
entry: []const u8,
execution_model: spv.spv.SpvExecutionModel,
specialization: ?*const vk.SpecializationInfo,
image_api: spv.Runtime.ImageAPI,
) VkError!?spv.Runtime {
@@ -89,8 +91,14 @@ pub fn cloneRuntime(
defer self.mutex.unlock(io);
for (self.shaders.items) |*shader| {
if (shader.module == module 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;
if (shader.module == module and shader.execution_model == execution_model and std.mem.eql(u8, shader.entry, entry) and specsEqual(shader.specs, specialization)) {
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,
module: *SoftShaderModule,
entry: []const u8,
execution_model: spv.spv.SpvExecutionModel,
specialization: ?*const vk.SpecializationInfo,
image_api: spv.Runtime.ImageAPI,
) VkError!void {
@@ -112,6 +121,7 @@ pub fn storeRuntimeTemplate(
for (self.shaders.items) |*shader| {
if (shader.module == module and
shader.execution_model == execution_model and
std.mem.eql(u8, shader.entry, entry) and
specsEqual(shader.specs, specialization))
{
@@ -122,6 +132,7 @@ pub fn storeRuntimeTemplate(
var cached: CachedShader = .{
.module = module,
.entry = data_allocator.dupe(u8, entry) catch return VkError.OutOfDeviceMemory,
.execution_model = execution_model,
.specs = try cloneSpecs(data_allocator, specialization),
.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 = 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 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_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(
image,
image_view,
@@ -601,9 +604,9 @@ pub fn sampleImageInt4(image: *SoftImage, image_view: *SoftImageView, sampler: *
dim,
coord,
mip_level,
@as(i32, @intFromFloat(@floor(coord.u * scale_u))) + offset.x,
@as(i32, @intFromFloat(@floor(coord.v * scale_v))) + offset.y,
@as(i32, @intFromFloat(@floor(coord.w * scale_w))) + offset.z,
ix,
iy,
iz,
);
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 shader = pipeline.stages.getPtr(.compute) orelse return VkError.InvalidPipelineDrv;
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 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 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_statuses: []spv.Runtime.EntryPointStatus = &.{};
@@ -146,17 +146,13 @@ inline fn run(data: RunData) !void {
defer {
for (barrier_runtimes) |*barrier_rt| {
barrier_rt.resetInvocation(allocator);
barrier_rt.deinit(allocator);
}
allocator.free(barrier_runtimes);
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;
while (group_index < data.group_count) : (group_index += data.self.batch_size) {
var modulo: usize = group_index;
@@ -185,8 +181,16 @@ inline fn run(data: RunData) !void {
continue;
}
const workgroup_memory = try rt.createWorkgroupMemory(allocator);
defer rt.destroyWorkgroupMemory(allocator, workgroup_memory);
for (0..data.invocations_per_workgroup) |i| {
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);
const invocation_index = data.self.invocation_index.fetchAdd(1, .monotonic);
@@ -208,13 +212,13 @@ inline fn run(data: RunData) !void {
SpvRuntimeError.Killed => continue,
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) {
@branchHint(.cold);
try dumpResultsTable(allocator, io, rt, false);
}
try rt.flushDescriptorSets(allocator);
}
}
}
@@ -228,14 +232,19 @@ fn runBarrierWorkgroup(
group_id: @Vector(3, u32),
) !void {
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| {
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, group_id);
try setupSubgroupBuiltins(data.self, rt, data.local_size, group_id, i);
statuses[i] = try rt.beginEntryPoint(allocator, entry);
try flushWorkgroupMemory(rt, workgroup_memory);
try rt.flushDescriptorSets(allocator);
}
@@ -253,12 +262,20 @@ fn runBarrierWorkgroup(
for (runtimes, 0..) |*rt, i| {
if (statuses[i] == .completed)
continue;
try rt.bindWorkgroupMemory(workgroup_memory);
statuses[i] = try rt.continueEntryPoint(allocator);
try flushWorkgroupMemory(rt, workgroup_memory);
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 {
@branchHint(.cold);
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) {
.raw => |data| _ = try dst.write(data),
.raw => |data| {
_ = try dst.write(data);
},
.sampled_image => |data| switch (dst.*) {
.Image => _ = try dst.write(std.mem.asBytes(&data.image)),
.Sampler => _ = try dst.write(std.mem.asBytes(&data.sampler)),
@@ -144,6 +146,10 @@ pub fn writeDescriptorSets(state: *PipelineState, rt: *spv.Runtime) !void {
switch (binding) {
.buffer => |buffer_data_array| for (buffer_data_array, 0..) |buffer_data, descriptor_index| {
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 dynamic_offset: vk.DeviceSize = switch (binding_layout.descriptor_type) {
.uniform_buffer_dynamic,
+3 -1
View File
@@ -39,6 +39,8 @@ pub const IndexBuffer = struct {
index_type: vk.IndexType,
};
pub const InterpolationType = enum { smooth, flat, noperspective };
pub const DynamicState = struct {
viewports: ?[]const vk.Viewport,
scissor: ?[]const vk.Rect2D,
@@ -57,7 +59,7 @@ pub const Vertex = struct {
position: F32x4,
point_size: f32,
outputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS][4]?struct {
interpolation_type: enum { smooth, flat, noperspective },
interpolation_type: InterpolationType,
blob: []u8,
size: usize,
},
+15 -11
View File
@@ -57,6 +57,9 @@ pub fn shaderInvocation(
defer mutex.unlock(io);
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..]);
rt.writeBuiltIn(allocator, std.mem.asBytes(&position), .FragCoord) catch |err| switch (err) {
SpvRuntimeError.NotFound => {},
@@ -96,8 +99,8 @@ pub fn shaderInvocation(
var input = fragment_inputs[location][component];
const result_word = rt.getResultByLocationComponent(@intCast(location), @intCast(component), .input) catch |err| switch (err) {
SpvRuntimeError.NotFound => {
if (input.blob.len != 0) {
rt.writeInputLocation(input.blob, @intCast(location)) catch |write_err| switch (write_err) {
if (input.size != 0) {
rt.writeInputLocation(input.blob[0..input.size], @intCast(location)) catch |write_err| switch (write_err) {
SpvRuntimeError.NotFound => {},
else => return write_err,
};
@@ -110,11 +113,11 @@ pub fn shaderInvocation(
const has_result_value = rt.results[result_word].variant != null;
const memory_size = if (has_result_value)
try rt.getResultMemorySize(result_word)
else if (input.blob.len == 0)
else if (input.size == 0)
try rt.getInputLocationMemorySize(@intCast(location))
else
input.blob.len;
if (input.blob.len == 0) {
input.size;
if (input.size == 0) {
const zeroes = allocator.alloc(u8, memory_size + INTERFACE_BLOB_PADDING) catch return SpvRuntimeError.OutOfMemory;
@memset(zeroes, 0);
fragment_inputs[location][component] = .{
@@ -125,16 +128,17 @@ pub fn shaderInvocation(
input = fragment_inputs[location][component];
}
if (input.blob.len != 0) {
if (!has_result_value or input.blob.len < memory_size)
try rt.writeInputLocation(input.blob, @intCast(location))
if (input.size != 0) {
const input_bytes = input.blob[0..input.size];
if (!has_result_value or input.size < memory_size)
try rt.writeInputLocation(input_bytes, @intCast(location))
else
try rt.writeInput(allocator, input.blob, result_word);
try rt.writeInput(allocator, input_bytes, result_word);
if (derivatives) |derivative| {
const dx = derivative.dx[location][component];
const dy = derivative.dy[location][component];
if (dx.blob.len != 0 and dy.blob.len != 0) {
try rt.setDerivativeFromMemory(allocator, result_word, dx.blob, dy.blob);
if (dx.size != 0 and dy.size != 0) {
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],
null,
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(),
),
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);
}
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 {
if (state.depth_test_enable == .false)
return true;
@@ -477,6 +502,7 @@ pub fn writeToTargets(
z: f32,
coverage_sample_mask: ?vk.SampleMask,
fragment_sample_mask: ?vk.SampleMask,
depth_already_applied: bool,
) VkError!void {
const io = draw_call.renderer.device.interface.io();
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.
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;
depth.mutex.lock(io) catch return VkError.DeviceLost;
@@ -36,6 +36,7 @@ const RunData = struct {
stencil_attachment_access: ?*common.RenderTargetAccess,
front_face: bool,
has_fragment_shader: bool,
early_fragment_tests: bool,
fragment_uses_derivatives: bool,
fragment_uses_sample_id: bool,
fragment_uses_centroid: bool,
@@ -70,6 +71,10 @@ pub fn drawTriangle(
stage.module.module.reflection_infos.needs_derivatives
else
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|
stage.module.module.builtins.get(.SampleId) != null
else
@@ -130,6 +135,7 @@ pub fn drawTriangle(
.stencil_attachment_access = stencil_attachment_access,
.front_face = front_face,
.has_fragment_shader = fragment_stage != null,
.early_fragment_tests = early_fragment_tests,
.fragment_uses_derivatives = fragment_uses_derivatives,
.fragment_uses_sample_id = fragment_uses_sample_id,
.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;
}
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 {
@call(.always_inline, run, .{data}) catch |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 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 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 sample_pos = firstCoveredSamplePosition(sample_count, coverage_sample_mask);
const sample_pos = firstCoveredSamplePosition(sample_count, early_depth.mask);
const centroid_p = zm.f32x4(
@as(f32, @floatFromInt(x)) + sample_pos.x,
@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 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;
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_coverage_mask,
sample_result.sample_mask,
early_depth.applied,
);
}
continue;
@@ -423,8 +461,9 @@ inline fn run(data: RunData) !void {
@intCast(x),
@intCast(y),
fragment_result.depth orelse z,
coverage_sample_mask,
early_depth.mask,
fragment_result.sample_mask,
early_depth.applied,
);
}
}
+83 -10
View File
@@ -60,8 +60,10 @@ inline fn run(data: RunData) !void {
continue;
}
}
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..]);
const vertex_index_u32: u32 = if (data.indices) |indices| indices[invocation_index] else @intCast(data.first_vertex + invocation_index);
@@ -107,28 +109,99 @@ inline fn run(data: RunData) !void {
try readPointSize(rt, &output.point_size);
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
for (0..4) |component| {
const result_word = rt.getResultByLocationComponent(@intCast(location), @intCast(component), .output) catch |err| switch (err) {
const location_result = rt.getResultByLocation(@intCast(location), .output) catch |err| switch (err) {
SpvRuntimeError.NotFound => continue,
else => return err,
};
const memory_size = try rt.getResultMemorySize(result_word);
try readVertexOutput(data, output, rt, location, 0, location_result);
const result_is_integer = resultIsInteger(rt, result_word);
for (1..4) |component| {
const component_result = rt.getResultByLocationComponent(@intCast(location), @intCast(component), .output) catch |err| switch (err) {
SpvRuntimeError.NotFound => continue,
else => return err,
};
if (component_result == location_result)
continue;
try readVertexOutput(data, output, rt, location, component, component_result);
}
}
try readActiveInterfaceOutputs(data, output, rt, entry);
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 = if (rt.hasResultDecoration(result_word, .Flat) or result_is_integer) .flat else .smooth, // TODO : handle noperspective
.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);
}
}
}
try rt.flushDescriptorSets(data.allocator);
}
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 {