adding fragdepth propagation, adjusting attachment sizing
Build / build (push) Failing after 0s
Test / build_and_test (push) Failing after 0s

This commit is contained in:
2026-06-19 22:20:59 +02:00
parent 59716cc794
commit 4d4578ff46
9 changed files with 129 additions and 65 deletions
+1 -1
View File
@@ -529,7 +529,7 @@ pub fn clearAttachment(interface: *Interface, attachment: vk.ClearAttachment, re
const fb_attachment_index = (subpass.color_attachments orelse return)[impl.attachment.color_attachment].attachment;
if (fb_attachment_index != vk.ATTACHMENT_UNUSED)
break :blk framebuffer.interface.attachments[impl.attachment.color_attachment];
break :blk framebuffer.interface.attachments[fb_attachment_index];
} else if (impl.attachment.aspect_mask.depth_bit or impl.attachment.aspect_mask.stencil_bit) {
if (render_pass.interface.subpasses[device.renderer.subpass_index].depth_stencil_attachments) |desc| {
if (desc.attachment != vk.ATTACHMENT_UNUSED)
+2 -2
View File
@@ -56,14 +56,14 @@ pub fn resolveAttachments(self: *Self, render_pass: *SoftRenderPass, subpass_ind
.src_subresource = .{
.aspect_mask = src_image_view.subresource_range.aspect_mask,
.base_array_layer = src_image_view.subresource_range.base_array_layer,
.layer_count = src_image_view.subresource_range.layer_count,
.layer_count = src_image_view.layerCount(),
.mip_level = src_image_view.subresource_range.base_mip_level,
},
.src_offset = .{ .x = 0, .y = 0, .z = 0 },
.dst_subresource = .{
.aspect_mask = dst_image_view.subresource_range.aspect_mask,
.base_array_layer = dst_image_view.subresource_range.base_array_layer,
.layer_count = dst_image_view.subresource_range.layer_count,
.layer_count = dst_image_view.layerCount(),
.mip_level = dst_image_view.subresource_range.base_mip_level,
},
.dst_offset = .{ .x = 0, .y = 0, .z = 0 },
+18 -25
View File
@@ -11,6 +11,8 @@ const Device = base.Device;
const VkError = base.VkError;
const SpvRuntimeError = spv.Runtime.RuntimeError;
pub threadlocal var current_fragment_coord: ?vk.Offset3D = null; // Ugly hack
const NonDispatchable = base.NonDispatchable;
const ShaderModule = base.ShaderModule;
@@ -294,16 +296,18 @@ fn imageApi() spv.Runtime.ImageAPI {
};
}
fn imageMipLevel(image: *SoftImage, image_view: *SoftImageView, lod: ?i32) u32 {
const range = image_view.interface.subresource_range;
fn imageMipLevel(image_view: *SoftImageView, lod: ?i32) u32 {
const mip_lod: u32 = if (lod) |level| @intCast(@max(level, 0)) else 0;
const max_lod = if (range.level_count == vk.REMAINING_MIP_LEVELS)
image.interface.mip_levels - range.base_mip_level - 1
else
range.level_count - 1;
const range = image_view.interface.subresource_range;
const max_lod = image_view.interface.levelCount() - 1;
return range.base_mip_level + @min(mip_lod, max_lod);
}
fn subpassDataCoord(x: i32, y: i32, z: i32) SpvRuntimeError!vk.Offset3D {
const coord = current_fragment_coord orelse return SpvRuntimeError.Unknown;
return .{ .x = coord.x + x, .y = coord.y + y, .z = coord.z + z };
}
fn readImageFloat4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32, lod: ?i32) SpvRuntimeError!spv.Runtime.Vec4(f32) {
var pixel = zm.f32x4s(0.0);
if (dim == .Buffer) {
@@ -315,8 +319,8 @@ fn readImageFloat4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32,
const image_view: *SoftImageView = @ptrCast(@alignCast(context));
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image));
const cube_face: u32 = if (dim == .Cube) @intCast(z) else 0;
const mip_level = imageMipLevel(image, image_view, lod);
const image_coord: vk.Offset3D = switch (image_view.interface.view_type) {
const mip_level = imageMipLevel(image_view, lod);
const image_coord: vk.Offset3D = if (dim == .SubpassData) try subpassDataCoord(x, y, z) else switch (image_view.interface.view_type) {
.@"1d", .@"1d_array" => .{ .x = x, .y = 0, .z = 0 },
.@"2d", .@"2d_array", .cube, .cube_array => .{ .x = x, .y = y, .z = 0 },
else => .{ .x = x, .y = y, .z = z },
@@ -356,8 +360,8 @@ fn readImageInt4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32, l
const image_view: *SoftImageView = @ptrCast(@alignCast(context));
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image));
const cube_face: u32 = if (dim == .Cube) @intCast(z) else 0;
const mip_level = imageMipLevel(image, image_view, lod);
const image_coord: vk.Offset3D = switch (image_view.interface.view_type) {
const mip_level = imageMipLevel(image_view, lod);
const image_coord: vk.Offset3D = if (dim == .SubpassData) try subpassDataCoord(x, y, z) else switch (image_view.interface.view_type) {
.@"1d", .@"1d_array" => .{ .x = x, .y = 0, .z = 0 },
.@"2d", .@"2d_array", .cube, .cube_array => .{ .x = x, .y = y, .z = 0 },
else => .{ .x = x, .y = y, .z = z },
@@ -516,19 +520,13 @@ fn queryImageSize(context: *anyopaque, dim: spv.SpvDim, arrayed: bool, lod: ?i32
}
const image_view: *SoftImageView = @ptrCast(@alignCast(context));
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image));
const range = image_view.interface.subresource_range;
const mip_lod: u32 = if (lod) |level| @intCast(@max(level, 0)) else 0;
const max_lod = if (range.level_count == vk.REMAINING_MIP_LEVELS)
image.interface.mip_levels - range.base_mip_level - 1
else
range.level_count - 1;
const max_lod = image_view.interface.levelCount() - 1;
const mip_level = range.base_mip_level + @min(mip_lod, max_lod);
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image));
const extent = image.getMipLevelExtent(mip_level);
const layers = if (range.layer_count == vk.REMAINING_ARRAY_LAYERS)
image.interface.array_layers - range.base_array_layer
else
range.layer_count;
const layers = image_view.interface.layerCount();
return switch (dim) {
.@"1D" => if (arrayed)
.{ .x = extent.width, .y = layers, .z = 0, .w = 0 }
@@ -551,12 +549,7 @@ fn queryImageSamples(context: *anyopaque) SpvRuntimeError!u32 {
fn queryImageLevels(context: *anyopaque) SpvRuntimeError!u32 {
const image_view: *SoftImageView = @ptrCast(@alignCast(context));
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image));
const range = image_view.interface.subresource_range;
return if (range.level_count == vk.REMAINING_MIP_LEVELS)
image.interface.mip_levels - range.base_mip_level
else
range.level_count;
return image_view.interface.levelCount();
}
fn queryImageLod(context: *anyopaque, context2: *anyopaque, dim: spv.SpvDim, derivatives: spv.Runtime.ImageDerivatives) SpvRuntimeError!spv.Runtime.Vec4(f32) {
+15 -21
View File
@@ -191,23 +191,17 @@ fn samplerBorderColorInt(sampler: *Self, format: vk.Format) U32x4 {
return color;
}
fn viewLayerCount(image: *SoftImage, range: vk.ImageSubresourceRange) u32 {
return if (range.layer_count == vk.REMAINING_ARRAY_LAYERS)
image.interface.array_layers - range.base_array_layer
else
range.layer_count;
fn viewLayerCount(image_view: *SoftImageView) u32 {
return image_view.interface.layerCount();
}
fn viewMipCount(image: *SoftImage, range: vk.ImageSubresourceRange) u32 {
return if (range.level_count == vk.REMAINING_MIP_LEVELS)
image.interface.mip_levels - range.base_mip_level
else
range.level_count;
fn viewMipCount(image_view: *SoftImageView) u32 {
return image_view.interface.levelCount();
}
fn sampleMipLevel(image: *SoftImage, image_view: *SoftImageView, sampler: *Self, lod: ?f32) u32 {
fn sampleMipLevel(image_view: *SoftImageView, sampler: *Self, lod: ?f32) u32 {
const range = image_view.interface.subresource_range;
const mip_count = viewMipCount(image, range);
const mip_count = viewMipCount(image_view);
if (mip_count <= 1)
return range.base_mip_level;
@@ -256,7 +250,7 @@ pub fn queryImageLod(image: *SoftImage, image_view: *SoftImageView, sampler: *Se
const lod = if (rho > 0.0) @log2(rho) else -std.math.inf(f32);
const biased_lod = lod + sampler.interface.mip_lod_bias;
const clamped_lod = std.math.clamp(biased_lod, sampler.interface.min_lod, sampler.interface.max_lod);
const max_level: f32 = @floatFromInt(viewMipCount(image, range) - 1);
const max_level: f32 = @floatFromInt(viewMipCount(image_view) - 1);
const level = std.math.clamp(mipmapModeLevel(sampler, clamped_lod), 0.0, max_level);
return .{ level, lod, 0.0, 0.0 };
@@ -357,9 +351,9 @@ fn readSampledFloat4(
} else coord;
const z: i32, const layer: u32 = switch (image_view.interface.view_type) {
.@"1d_array" => .{ 0, range.base_array_layer + sampleArrayLayer(coord.v, viewLayerCount(image, range)) },
.@"2d_array" => .{ 0, range.base_array_layer + sampleArrayLayer(coord.w, viewLayerCount(image, range)) },
.cube_array => .{ 0, range.base_array_layer + sampleArrayLayer(coord.w, @divTrunc(viewLayerCount(image, range), 6)) * 6 + texel.face },
.@"1d_array" => .{ 0, range.base_array_layer + sampleArrayLayer(coord.v, viewLayerCount(image_view)) },
.@"2d_array" => .{ 0, range.base_array_layer + sampleArrayLayer(coord.w, viewLayerCount(image_view)) },
.cube_array => .{ 0, range.base_array_layer + sampleArrayLayer(coord.w, @divTrunc(viewLayerCount(image_view), 6)) * 6 + texel.face },
.@"3d" => .{ sampleAddressOrBorder(iz, extent.depth, sampler.interface.address_mode_w) orelse return samplerBorderColor(sampler, format), range.base_array_layer },
.cube => .{ 0, range.base_array_layer + texel.face },
else => .{ 0, range.base_array_layer },
@@ -432,9 +426,9 @@ fn readSampledInt4(
} else coord;
const z: i32, const layer: u32 = switch (image_view.interface.view_type) {
.@"1d_array" => .{ 0, range.base_array_layer + sampleArrayLayer(coord.v, viewLayerCount(image, range)) },
.@"2d_array" => .{ 0, range.base_array_layer + sampleArrayLayer(coord.w, viewLayerCount(image, range)) },
.cube_array => .{ 0, range.base_array_layer + sampleArrayLayer(coord.w, @divTrunc(viewLayerCount(image, range), 6)) * 6 + texel.face },
.@"1d_array" => .{ 0, range.base_array_layer + sampleArrayLayer(coord.v, viewLayerCount(image_view)) },
.@"2d_array" => .{ 0, range.base_array_layer + sampleArrayLayer(coord.w, viewLayerCount(image_view)) },
.cube_array => .{ 0, range.base_array_layer + sampleArrayLayer(coord.w, @divTrunc(viewLayerCount(image_view), 6)) * 6 + texel.face },
.@"3d" => .{ sampleAddressOrBorder(iz, extent.depth, sampler.interface.address_mode_w) orelse return samplerBorderColorInt(sampler, format), range.base_array_layer },
.cube => .{ 0, range.base_array_layer + texel.face },
else => .{ 0, range.base_array_layer },
@@ -465,7 +459,7 @@ fn readSampledInt4(
}
pub fn sampleImageFloat4(image: *SoftImage, image_view: *SoftImageView, sampler: *Self, dim: spv.SpvDim, x: f32, y: f32, z: f32, lod: ?f32, offset: ImageOffset) VkError!F32x4 {
const mip_level = sampleMipLevel(image, image_view, sampler, lod);
const mip_level = sampleMipLevel(image_view, sampler, lod);
const extent = image.getMipLevelExtent(mip_level);
const coord: CubeCoordinate = switch (image_view.interface.view_type) {
.@"1d_array" => .{
@@ -523,7 +517,7 @@ pub fn sampleImageFloat4(image: *SoftImage, image_view: *SoftImageView, sampler:
}
pub fn sampleImageInt4(image: *SoftImage, image_view: *SoftImageView, sampler: *Self, dim: spv.SpvDim, x: f32, y: f32, z: f32, lod: ?f32, offset: ImageOffset) VkError!U32x4 {
const mip_level = sampleMipLevel(image, image_view, sampler, lod);
const mip_level = sampleMipLevel(image_view, sampler, lod);
const extent = image.getMipLevelExtent(mip_level);
const coord: CubeCoordinate = switch (image_view.interface.view_type) {
.@"1d_array" => .{
+28 -2
View File
@@ -13,6 +13,11 @@ const VkError = base.VkError;
const SpvRuntimeError = spv.Runtime.RuntimeError;
const INTERFACE_BLOB_PADDING = @sizeOf(zm.F32x4);
pub const InvocationResult = struct {
outputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(zm.F32x4)]u8,
depth: ?f32,
};
pub const DerivativeInputs = struct {
dx: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolationLocation,
dy: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolationLocation,
@@ -27,7 +32,7 @@ pub fn shaderInvocation(
front_face: bool,
inputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolationLocation,
derivative_inputs: ?DerivativeInputs,
) SpvRuntimeError![spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(zm.F32x4)]u8 {
) SpvRuntimeError!InvocationResult {
var fragment_inputs = inputs;
errdefer freeOwnedInputs(allocator, fragment_inputs);
@@ -66,6 +71,15 @@ pub fn shaderInvocation(
else => return err,
};
const SoftPipeline = @import("../SoftPipeline.zig");
const previous_fragment_coord = SoftPipeline.current_fragment_coord;
SoftPipeline.current_fragment_coord = .{
.x = @intFromFloat(position[0]),
.y = @intFromFloat(position[1]),
.z = @intFromFloat(position[2]),
};
defer SoftPipeline.current_fragment_coord = previous_fragment_coord;
const entry = try rt.getEntryPointByName(shader.entry);
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
@@ -142,6 +156,15 @@ pub fn shaderInvocation(
try readFragmentOutput(allocator, rt, &outputs, location, 0, result_word);
}
var depth: ?f32 = null;
var frag_depth: f32 = undefined;
if (rt.readBuiltIn(std.mem.asBytes(&frag_depth), .FragDepth)) {
depth = frag_depth;
} else |err| switch (err) {
SpvRuntimeError.InvalidSpirV, SpvRuntimeError.NotFound => {},
else => return err,
}
try rt.flushDescriptorSets(allocator);
freeOwnedInputs(allocator, fragment_inputs);
if (derivatives) |owned_derivatives| {
@@ -149,7 +172,10 @@ pub fn shaderInvocation(
freeOwnedInputs(allocator, owned_derivatives.dy);
}
return outputs;
return .{
.outputs = outputs,
.depth = depth,
};
}
fn readFragmentOutput(
+21 -6
View File
@@ -19,6 +19,18 @@ const SoftImage = @import("../SoftImage.zig");
const VkError = base.VkError;
const SpvRuntimeError = spv.Runtime.RuntimeError;
fn renderTargetSubresourceSize(image: *const SoftImage, image_view: *const base.ImageView, aspect_mask: vk.ImageAspectFlags, mip_level: u32) usize {
if (image.interface.image_type == .@"3d" and image.interface.flags.@"2d_array_compatible_bit" and
(image_view.view_type == .@"2d" or image_view.view_type == .@"2d_array"))
{
return image.interface.getSliceMemSizeForMipLevel(aspect_mask, mip_level) *
image.interface.samples.toInt() *
image_view.layerCount();
}
return image.getMultiSampledLevelSize(aspect_mask, mip_level);
}
pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocator, draw_call: *DrawCall) VkError!void {
const io = draw_call.renderer.device.interface.io();
@@ -45,7 +57,7 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
color_range.base_mip_level,
color_range.base_array_layer,
);
const color_attachment_subresource_size = render_target.getLayerSize(color_range.aspect_mask);
const color_attachment_subresource_size = renderTargetSubresourceSize(render_target, render_target_view, color_range.aspect_mask, color_range.base_mip_level);
access.* = .{
.mutex = undefined,
.base = try render_target.mapAsSliceWithAddedOffset(u8, color_attachment_subresource_offset, color_attachment_subresource_size),
@@ -78,7 +90,7 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
depth_range.base_mip_level,
depth_range.base_array_layer,
);
const attachment_subresource_size = depth_attachment.?.getLayerSize(depth_aspect);
const attachment_subresource_size = renderTargetSubresourceSize(depth_attachment.?, depth_attachment_view.?, depth_aspect, depth_range.base_mip_level);
break :blk .{
.mutex = .init,
.base = try depth_attachment.?.mapAsSliceWithAddedOffset(u8, attachment_subresource_offset, attachment_subresource_size),
@@ -108,7 +120,7 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
stencil_range.base_mip_level,
stencil_range.base_array_layer,
);
const attachment_subresource_size = depth_attachment.?.getLayerSize(stencil_aspect);
const attachment_subresource_size = renderTargetSubresourceSize(depth_attachment.?, depth_attachment_view.?, stencil_aspect, stencil_range.base_mip_level);
break :blk .{
.mutex = .init,
.base = try depth_attachment.?.mapAsSliceWithAddedOffset(u8, attachment_subresource_offset, attachment_subresource_size),
@@ -340,7 +352,10 @@ fn clipTransformAndRasterizePoint(
if (!common.scissorContainsPixel(draw_call.scissor, px, py))
continue;
var outputs = std.mem.zeroes([spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(zm.F32x4)]u8);
var fragment_result: fragment.InvocationResult = .{
.outputs = std.mem.zeroes([spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(zm.F32x4)]u8),
.depth = null,
};
if (has_fragment_shader) {
const frag_x = @as(f32, @floatFromInt(px)) + 0.5;
const frag_y = @as(f32, @floatFromInt(py)) + 0.5;
@@ -349,7 +364,7 @@ fn clipTransformAndRasterizePoint(
(frag_y - point_min_y) / point_size,
};
outputs = fragment.shaderInvocation(
fragment_result = fragment.shaderInvocation(
allocator,
draw_call,
0,
@@ -372,7 +387,7 @@ fn clipTransformAndRasterizePoint(
};
}
try common.writeToTargets(outputs, draw_call, color_attachment_access, depth_attachment_access, stencil_attachment_access, true, @intCast(px), @intCast(py), transformed.position[2]);
try common.writeToTargets(fragment_result.outputs, draw_call, color_attachment_access, depth_attachment_access, stencil_attachment_access, true, @intCast(px), @intCast(py), fragment_result.depth orelse transformed.position[2]);
}
}
}
+7 -4
View File
@@ -153,9 +153,12 @@ inline fn run(data: RunData) !void {
const z = ((1.0 - t) * data.start_vertex.position[2]) + (t * data.end_vertex.position[2]);
const frag_w = ((1.0 - t) / data.start_vertex.position[3]) + (t / data.end_vertex.position[3]);
var outputs = std.mem.zeroes([spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(F32x4)]u8);
var fragment_result: fragment.InvocationResult = .{
.outputs = std.mem.zeroes([spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(F32x4)]u8),
.depth = null,
};
if (data.has_fragment_shader) {
outputs = fragment.shaderInvocation(
fragment_result = fragment.shaderInvocation(
data.allocator,
data.draw_call,
data.batch_id,
@@ -180,7 +183,7 @@ inline fn run(data: RunData) !void {
}
try common.writeToTargets(
outputs,
fragment_result.outputs,
data.draw_call,
data.color_attachment_access,
data.depth_attachment_access,
@@ -188,7 +191,7 @@ inline fn run(data: RunData) !void {
true,
@intCast(pixel_x),
@intCast(pixel_y),
z,
fragment_result.depth orelse z,
);
}
}
@@ -179,7 +179,10 @@ inline fn run(data: RunData) !void {
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]);
var outputs = std.mem.zeroes([spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(F32x4)]u8);
var fragment_result: fragment.InvocationResult = .{
.outputs = std.mem.zeroes([spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(F32x4)]u8),
.depth = null,
};
if (data.has_fragment_shader) {
const inputs = try common.interpolateVertexOutputs(data.allocator, &data.v0, &data.v1, &data.v2, b0, b1, b2);
const derivative_inputs: ?fragment.DerivativeInputs = if (data.fragment_uses_derivatives) blk: {
@@ -215,7 +218,7 @@ inline fn run(data: RunData) !void {
break :blk derivatives;
} else null;
outputs = fragment.shaderInvocation(
fragment_result = fragment.shaderInvocation(
data.allocator,
data.draw_call,
data.batch_id,
@@ -239,7 +242,7 @@ inline fn run(data: RunData) !void {
}
try common.writeToTargets(
outputs,
fragment_result.outputs,
data.draw_call,
data.color_attachment_access,
data.depth_attachment_access,
@@ -247,7 +250,7 @@ inline fn run(data: RunData) !void {
data.front_face,
@intCast(x),
@intCast(y),
z,
fragment_result.depth orelse z,
);
}
}
+30
View File
@@ -40,3 +40,33 @@ pub fn init(device: *Device, allocator: std.mem.Allocator, info: *const vk.Image
pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void {
self.vtable.destroy(self, allocator);
}
pub fn levelCount(self: *const Self) u32 {
return if (self.subresource_range.level_count == vk.REMAINING_MIP_LEVELS)
self.image.mip_levels - self.subresource_range.base_mip_level
else
self.subresource_range.level_count;
}
pub fn layerCount(self: *const Self) u32 {
return if (self.subresource_range.layer_count == vk.REMAINING_ARRAY_LAYERS)
self.remainingLayerCount()
else
self.subresource_range.layer_count;
}
pub fn resolvedSubresourceRange(self: *const Self) vk.ImageSubresourceRange {
var range = self.subresource_range;
range.level_count = self.levelCount();
range.layer_count = self.layerCount();
return range;
}
fn remainingLayerCount(self: *const Self) u32 {
if (self.image.image_type == .@"3d" and self.image.flags.@"2d_array_compatible_bit") {
const depth = @max(@as(u32, 1), self.image.extent.depth >> @intCast(self.subresource_range.base_mip_level));
return depth - self.subresource_range.base_array_layer;
}
return self.image.array_layers - self.subresource_range.base_array_layer;
}