fixing compute shared atomic memory, fixing vertex/fragment payloads, implementing early fragment depth tests
This commit is contained in:
+2
-2
@@ -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 = .{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user