improving renderpasses support
Build / build (push) Failing after 0s
Test / build_and_test (push) Failing after 7s

This commit is contained in:
2026-06-20 02:41:37 +02:00
parent 4d4578ff46
commit 291c65ed18
10 changed files with 265 additions and 71 deletions
+37
View File
@@ -33,6 +33,38 @@ interface: Interface,
command_allocator: std.heap.ArenaAllocator, command_allocator: std.heap.ArenaAllocator,
commands: std.ArrayList(Command), commands: std.ArrayList(Command),
fn attachmentIsReferencedBySubpass(render_pass: *SoftRenderPass, attachment_index: u32) bool {
for (render_pass.interface.subpasses) |subpass| {
if (subpass.input_attachments) |attachments| {
for (attachments) |attachment| {
if (attachment.attachment == attachment_index)
return true;
}
}
if (subpass.color_attachments) |attachments| {
for (attachments) |attachment| {
if (attachment.attachment == attachment_index)
return true;
}
}
if (subpass.resolve_attachments) |attachments| {
for (attachments) |attachment| {
if (attachment.attachment == attachment_index)
return true;
}
}
if (subpass.depth_stencil_attachments) |attachment| {
if (attachment.attachment == attachment_index)
return true;
}
}
return false;
}
pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const vk.CommandBufferAllocateInfo) VkError!*Self { pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const vk.CommandBufferAllocateInfo) VkError!*Self {
const self = allocator.create(Self) catch return VkError.OutOfHostMemory; const self = allocator.create(Self) catch return VkError.OutOfHostMemory;
errdefer allocator.destroy(self); errdefer allocator.destroy(self);
@@ -267,9 +299,13 @@ pub fn beginRenderPass(interface: *Interface, render_pass: *base.RenderPass, fra
const impl: *Impl = @ptrCast(@alignCast(context)); const impl: *Impl = @ptrCast(@alignCast(context));
device.renderer.render_pass = impl.render_pass; device.renderer.render_pass = impl.render_pass;
device.renderer.framebuffer = impl.framebuffer; device.renderer.framebuffer = impl.framebuffer;
device.renderer.render_area = impl.render_area;
device.renderer.subpass_index = 0; device.renderer.subpass_index = 0;
for (impl.render_pass.interface.attachments, impl.framebuffer.interface.attachments, 0..) |desc, attachment, index| { for (impl.render_pass.interface.attachments, impl.framebuffer.interface.attachments, 0..) |desc, attachment, index| {
if (!attachmentIsReferencedBySubpass(impl.render_pass, @intCast(index)))
continue;
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", attachment.image)); const image: *SoftImage = @alignCast(@fieldParentPtr("interface", attachment.image));
var clear_mask: vk.ImageAspectFlags = .{}; var clear_mask: vk.ImageAspectFlags = .{};
@@ -987,6 +1023,7 @@ pub fn endRenderPass(interface: *Interface) VkError!void {
device.renderer.render_pass = null; device.renderer.render_pass = null;
device.renderer.framebuffer = null; device.renderer.framebuffer = null;
device.renderer.render_area = null;
} }
}; };
+14 -4
View File
@@ -303,6 +303,14 @@ fn imageMipLevel(image_view: *SoftImageView, lod: ?i32) u32 {
return range.base_mip_level + @min(mip_lod, max_lod); return range.base_mip_level + @min(mip_lod, max_lod);
} }
fn imageReadAspect(image_view: *SoftImageView, comptime int_read: bool) vk.ImageAspectFlags {
const aspect = image_view.interface.subresource_range.aspect_mask;
if (aspect.depth_bit and aspect.stencil_bit) {
return if (int_read) .{ .stencil_bit = true } else .{ .depth_bit = true };
}
return aspect;
}
fn subpassDataCoord(x: i32, y: i32, z: i32) SpvRuntimeError!vk.Offset3D { fn subpassDataCoord(x: i32, y: i32, z: i32) SpvRuntimeError!vk.Offset3D {
const coord = current_fragment_coord orelse return SpvRuntimeError.Unknown; const coord = current_fragment_coord orelse return SpvRuntimeError.Unknown;
return .{ .x = coord.x + x, .y = coord.y + y, .z = coord.z + z }; return .{ .x = coord.x + x, .y = coord.y + y, .z = coord.z + z };
@@ -331,14 +339,15 @@ fn readImageFloat4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32,
.cube => cube_face, .cube => cube_face,
else => 0, else => 0,
}; };
const aspect_mask = imageReadAspect(image_view, false);
pixel = SoftSampler.swizzleFloat4(image.readFloat4( pixel = SoftSampler.swizzleFloat4(image.readFloat4(
image_coord, image_coord,
.{ .{
.aspect_mask = image_view.interface.subresource_range.aspect_mask, .aspect_mask = aspect_mask,
.mip_level = mip_level, .mip_level = mip_level,
.array_layer = array_layer, .array_layer = array_layer,
}, },
image_view.interface.format, base.format.fromAspect(image_view.interface.format, aspect_mask),
) catch return SpvRuntimeError.Unknown, image_view.interface.components); ) catch return SpvRuntimeError.Unknown, image_view.interface.components);
} }
return .{ return .{
@@ -372,14 +381,15 @@ fn readImageInt4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32, l
.cube => cube_face, .cube => cube_face,
else => 0, else => 0,
}; };
const aspect_mask = imageReadAspect(image_view, true);
pixel = SoftSampler.swizzleInt4(image.readInt4( pixel = SoftSampler.swizzleInt4(image.readInt4(
image_coord, image_coord,
.{ .{
.aspect_mask = image_view.interface.subresource_range.aspect_mask, .aspect_mask = aspect_mask,
.mip_level = mip_level, .mip_level = mip_level,
.array_layer = array_layer, .array_layer = array_layer,
}, },
image_view.interface.format, base.format.fromAspect(image_view.interface.format, aspect_mask),
) catch return SpvRuntimeError.Unknown, image_view.interface.components); ) catch return SpvRuntimeError.Unknown, image_view.interface.components);
} }
return .{ return .{
+2
View File
@@ -135,6 +135,7 @@ state: *PipelineState,
render_pass: ?*SoftRenderPass, render_pass: ?*SoftRenderPass,
framebuffer: ?*SoftFramebuffer, framebuffer: ?*SoftFramebuffer,
render_area: ?vk.Rect2D,
dynamic_state: DynamicState, dynamic_state: DynamicState,
subpass_index: usize, subpass_index: usize,
@@ -146,6 +147,7 @@ pub fn init(device: *SoftDevice, state: *PipelineState, active_occlusion_queries
.state = state, .state = state,
.render_pass = null, .render_pass = null,
.framebuffer = null, .framebuffer = null,
.render_area = null,
.dynamic_state = .{ .dynamic_state = .{
.viewports = null, .viewports = null,
.scissor = null, .scissor = null,
+12 -1
View File
@@ -16,6 +16,7 @@ const INTERFACE_BLOB_PADDING = @sizeOf(zm.F32x4);
pub const InvocationResult = struct { pub const InvocationResult = struct {
outputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(zm.F32x4)]u8, outputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(zm.F32x4)]u8,
depth: ?f32, depth: ?f32,
sample_mask: ?vk.SampleMask,
}; };
pub const DerivativeInputs = struct { pub const DerivativeInputs = struct {
@@ -76,7 +77,7 @@ pub fn shaderInvocation(
SoftPipeline.current_fragment_coord = .{ SoftPipeline.current_fragment_coord = .{
.x = @intFromFloat(position[0]), .x = @intFromFloat(position[0]),
.y = @intFromFloat(position[1]), .y = @intFromFloat(position[1]),
.z = @intFromFloat(position[2]), .z = 0,
}; };
defer SoftPipeline.current_fragment_coord = previous_fragment_coord; defer SoftPipeline.current_fragment_coord = previous_fragment_coord;
@@ -165,6 +166,15 @@ pub fn shaderInvocation(
else => return err, else => return err,
} }
var sample_mask: ?vk.SampleMask = null;
var frag_sample_mask: [1]vk.SampleMask = undefined;
if (rt.readBuiltIn(std.mem.asBytes(&frag_sample_mask), .SampleMask)) {
sample_mask = frag_sample_mask[0];
} else |err| switch (err) {
SpvRuntimeError.InvalidSpirV, SpvRuntimeError.NotFound => {},
else => return err,
}
try rt.flushDescriptorSets(allocator); try rt.flushDescriptorSets(allocator);
freeOwnedInputs(allocator, fragment_inputs); freeOwnedInputs(allocator, fragment_inputs);
if (derivatives) |owned_derivatives| { if (derivatives) |owned_derivatives| {
@@ -175,6 +185,7 @@ pub fn shaderInvocation(
return .{ return .{
.outputs = outputs, .outputs = outputs,
.depth = depth, .depth = depth,
.sample_mask = sample_mask,
}; };
} }
+8 -1
View File
@@ -63,6 +63,8 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
.base = try render_target.mapAsSliceWithAddedOffset(u8, color_attachment_subresource_offset, color_attachment_subresource_size), .base = try render_target.mapAsSliceWithAddedOffset(u8, color_attachment_subresource_offset, color_attachment_subresource_size),
.row_pitch = render_target.getRowPitchMemSizeForMipLevelWithFormat(color_range.aspect_mask, color_range.base_mip_level, color_format), .row_pitch = render_target.getRowPitchMemSizeForMipLevelWithFormat(color_range.aspect_mask, color_range.base_mip_level, color_format),
.texel_size = base.format.texelSize(color_format), .texel_size = base.format.texelSize(color_format),
.sample_count = render_target.interface.samples.toInt(),
.sample_stride = render_target.getMipLevelSize(color_range.aspect_mask, color_range.base_mip_level),
.width = color_extent.width, .width = color_extent.width,
.height = color_extent.height, .height = color_extent.height,
.format = color_format, .format = color_format,
@@ -96,6 +98,8 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
.base = try depth_attachment.?.mapAsSliceWithAddedOffset(u8, attachment_subresource_offset, attachment_subresource_size), .base = try depth_attachment.?.mapAsSliceWithAddedOffset(u8, attachment_subresource_offset, attachment_subresource_size),
.row_pitch = depth_attachment.?.getRowPitchMemSizeForMipLevelWithFormat(depth_aspect, depth_range.base_mip_level, depth_format), .row_pitch = depth_attachment.?.getRowPitchMemSizeForMipLevelWithFormat(depth_aspect, depth_range.base_mip_level, depth_format),
.texel_size = base.format.texelSize(depth_aspect_format), .texel_size = base.format.texelSize(depth_aspect_format),
.sample_count = depth_attachment.?.interface.samples.toInt(),
.sample_stride = depth_attachment.?.getMipLevelSize(depth_aspect, depth_range.base_mip_level),
.width = depth_extent.width, .width = depth_extent.width,
.height = depth_extent.height, .height = depth_extent.height,
.format = depth_aspect_format, .format = depth_aspect_format,
@@ -126,6 +130,8 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
.base = try depth_attachment.?.mapAsSliceWithAddedOffset(u8, attachment_subresource_offset, attachment_subresource_size), .base = try depth_attachment.?.mapAsSliceWithAddedOffset(u8, attachment_subresource_offset, attachment_subresource_size),
.row_pitch = depth_attachment.?.getRowPitchMemSizeForMipLevelWithFormat(stencil_aspect, stencil_range.base_mip_level, stencil_format), .row_pitch = depth_attachment.?.getRowPitchMemSizeForMipLevelWithFormat(stencil_aspect, stencil_range.base_mip_level, stencil_format),
.texel_size = base.format.texelSize(stencil_aspect_format), .texel_size = base.format.texelSize(stencil_aspect_format),
.sample_count = depth_attachment.?.interface.samples.toInt(),
.sample_stride = depth_attachment.?.getMipLevelSize(stencil_aspect, stencil_range.base_mip_level),
.width = stencil_extent.width, .width = stencil_extent.width,
.height = stencil_extent.height, .height = stencil_extent.height,
.format = stencil_aspect_format, .format = stencil_aspect_format,
@@ -355,6 +361,7 @@ fn clipTransformAndRasterizePoint(
var fragment_result: fragment.InvocationResult = .{ var fragment_result: fragment.InvocationResult = .{
.outputs = std.mem.zeroes([spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(zm.F32x4)]u8), .outputs = std.mem.zeroes([spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(zm.F32x4)]u8),
.depth = null, .depth = null,
.sample_mask = null,
}; };
if (has_fragment_shader) { if (has_fragment_shader) {
const frag_x = @as(f32, @floatFromInt(px)) + 0.5; const frag_x = @as(f32, @floatFromInt(px)) + 0.5;
@@ -387,7 +394,7 @@ fn clipTransformAndRasterizePoint(
}; };
} }
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]); 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], fragment_result.sample_mask);
} }
} }
} }
@@ -156,6 +156,7 @@ inline fn run(data: RunData) !void {
var fragment_result: fragment.InvocationResult = .{ var fragment_result: fragment.InvocationResult = .{
.outputs = std.mem.zeroes([spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(F32x4)]u8), .outputs = std.mem.zeroes([spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(F32x4)]u8),
.depth = null, .depth = null,
.sample_mask = null,
}; };
if (data.has_fragment_shader) { if (data.has_fragment_shader) {
fragment_result = fragment.shaderInvocation( fragment_result = fragment.shaderInvocation(
@@ -192,6 +193,7 @@ inline fn run(data: RunData) !void {
@intCast(pixel_x), @intCast(pixel_x),
@intCast(pixel_y), @intCast(pixel_y),
fragment_result.depth orelse z, fragment_result.depth orelse z,
fragment_result.sample_mask,
); );
} }
} }
+160 -64
View File
@@ -16,6 +16,8 @@ pub const RenderTargetAccess = struct {
base: []u8, base: []u8,
row_pitch: usize, row_pitch: usize,
texel_size: usize, texel_size: usize,
sample_count: usize,
sample_stride: usize,
width: u32, width: u32,
height: u32, height: u32,
format: vk.Format, format: vk.Format,
@@ -45,6 +47,22 @@ pub fn scissorContainsPixel(scissor: vk.Rect2D, x: i32, y: i32) bool {
pixel_y < max_y; pixel_y < max_y;
} }
pub fn rectContainsPixel(rect: vk.Rect2D, x: usize, y: usize) bool {
const min_x: i64 = @as(i64, rect.offset.x);
const min_y: i64 = @as(i64, rect.offset.y);
const max_x: i64 = min_x + @as(i64, @intCast(rect.extent.width));
const max_y: i64 = min_y + @as(i64, @intCast(rect.extent.height));
const pixel_x: i64 = @intCast(x);
const pixel_y: i64 = @intCast(y);
return pixel_x >= min_x and
pixel_x < max_x and
pixel_y >= min_y and
pixel_y < max_y;
}
pub fn targetContainsPixel(target: RenderTargetAccess, x: i32, y: i32) bool { pub fn targetContainsPixel(target: RenderTargetAccess, x: i32, y: i32) bool {
if (x < 0 or y < 0) if (x < 0 or y < 0)
return false; return false;
@@ -62,6 +80,14 @@ pub fn targetOffset(target: RenderTargetAccess, x: usize, y: usize) ?usize {
return offset; return offset;
} }
pub fn targetSampleOffset(target: RenderTargetAccess, x: usize, y: usize, sample_index: usize) ?usize {
const base_offset = targetOffset(target, x, y) orelse return null;
const offset = base_offset + sample_index * target.sample_stride;
if (offset > target.base.len or target.texel_size > target.base.len - offset)
return null;
return offset;
}
pub fn compare(comptime T: type, op: vk.CompareOp, reference: T, value: T) bool { pub fn compare(comptime T: type, op: vk.CompareOp, reference: T, value: T) bool {
return switch (op) { return switch (op) {
.never => false, .never => false,
@@ -100,6 +126,10 @@ fn updateStencilValue(stencil: *RenderTargetAccess, offset: usize, state: vk.Ste
pub fn stencilTestAndUpdate(stencil: *RenderTargetAccess, x: usize, y: usize, state: vk.StencilOpState, depth_passed: ?bool) bool { pub fn stencilTestAndUpdate(stencil: *RenderTargetAccess, x: usize, y: usize, state: vk.StencilOpState, depth_passed: ?bool) bool {
const offset = targetOffset(stencil.*, x, y) orelse return false; const offset = targetOffset(stencil.*, x, y) orelse return false;
return stencilTestAndUpdateAtOffset(stencil, offset, state, depth_passed);
}
fn stencilTestAndUpdateAtOffset(stencil: *RenderTargetAccess, offset: usize, state: vk.StencilOpState, depth_passed: ?bool) bool {
const current = blitter.readInt4(stencil.base[offset..], stencil.format)[0] & std.math.maxInt(u8); const current = blitter.readInt4(stencil.base[offset..], stencil.format)[0] & std.math.maxInt(u8);
const reference = state.reference & std.math.maxInt(u8); const reference = state.reference & std.math.maxInt(u8);
const compare_mask = state.compare_mask & std.math.maxInt(u8); const compare_mask = state.compare_mask & std.math.maxInt(u8);
@@ -138,10 +168,14 @@ fn quantizeDepthForFormat(format: vk.Format, z: f32) f32 {
} }
pub fn depthTestAndUpdate(depth: *RenderTargetAccess, x: usize, y: usize, z: f32, state: vk.PipelineDepthStencilStateCreateInfo) bool { pub fn depthTestAndUpdate(depth: *RenderTargetAccess, x: usize, y: usize, z: f32, state: vk.PipelineDepthStencilStateCreateInfo) bool {
const offset = targetOffset(depth.*, x, y) orelse return false;
return depthTestAndUpdateAtOffset(depth, offset, z, state);
}
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;
const offset = targetOffset(depth.*, x, y) orelse return false;
const reference = quantizeDepthForFormat(depth.format, z); const reference = quantizeDepthForFormat(depth.format, z);
const depth_value = blitter.readFloat4(depth.base[offset..], depth.format); const depth_value = blitter.readFloat4(depth.base[offset..], depth.format);
const passed = compare(f32, state.depth_compare_op, reference, depth_value[0]); const passed = compare(f32, state.depth_compare_op, reference, depth_value[0]);
@@ -373,86 +407,148 @@ pub fn writeToTargets(
x: usize, x: usize,
y: usize, y: usize,
z: f32, z: f32,
fragment_sample_mask: ?vk.SampleMask,
) VkError!void { ) VkError!void {
const io = draw_call.renderer.device.interface.io(); const io = draw_call.renderer.device.interface.io();
const depth_stencil_state = draw_call.renderer.state.pipeline.?.interface.mode.graphics.depth_stencil; const pipeline_data = draw_call.renderer.state.pipeline.?.interface.mode.graphics;
const depth_stencil_state = pipeline_data.depth_stencil;
if (!sampleMaskEnablesAnySample(pipeline_data.multisample, fragment_sample_mask))
return;
if (x >= draw_call.framebuffer.interface.width or y >= draw_call.framebuffer.interface.height) if (x >= draw_call.framebuffer.interface.width or y >= draw_call.framebuffer.interface.height)
return; return;
var stencil_state: ?vk.StencilOpState = null; if (draw_call.renderer.render_area) |render_area| {
var stencil_offset: ?usize = null; if (!rectContainsPixel(render_area, x, y))
if (stencil_attachment_access) |stencil| { return;
if (depth_stencil_state) |state| { }
if (state.stencil_test_enable == .true) {
stencil_state = if (front_face) const sample_count = pipeline_data.multisample.rasterization_samples.toInt();
resolveStencilState(draw_call, state.front, true) for (0..sample_count) |sample_index| {
else if (!sampleMaskEnablesSample(pipeline_data.multisample, fragment_sample_mask, sample_index))
resolveStencilState(draw_call, state.back, false); continue;
stencil_offset = targetOffset(stencil.*, x, y) orelse return;
if (!stencilTest(stencil, stencil_offset.?, stencil_state.?)) { var stencil_state: ?vk.StencilOpState = null;
updateStencilValue(stencil, stencil_offset.?, stencil_state.?, stencil_state.?.fail_op); var stencil_offset: ?usize = null;
return; if (stencil_attachment_access) |stencil| {
if (depth_stencil_state) |state| {
if (state.stencil_test_enable == .true) {
stencil_state = if (front_face)
resolveStencilState(draw_call, state.front, true)
else
resolveStencilState(draw_call, state.back, false);
stencil_offset = targetSampleOffset(stencil.*, x, y, sample_index) orelse continue;
if (!stencilTest(stencil, stencil_offset.?, stencil_state.?)) {
updateStencilValue(stencil, stencil_offset.?, stencil_state.?, stencil_state.?.fail_op);
continue;
}
} }
} }
} }
}
// 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_attachment_access) |depth| {
const depth_offset = targetOffset(depth.*, x, y) orelse return; 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;
defer depth.mutex.unlock(io); defer depth.mutex.unlock(io);
if (depth_stencil_state) |state| { if (depth_stencil_state) |state| {
depth_passed = depthTestAndUpdate(depth, x, y, z, state); depth_passed = depthTestAndUpdateAtOffset(depth, depth_offset, z, state);
if (!depth_passed.? and stencil_state == null) if (!depth_passed.? and stencil_state == null)
return; continue;
} else { } else {
const depth_value = blitter.readFloat4(depth.base[depth_offset..], depth.format); const depth_value = blitter.readFloat4(depth.base[depth_offset..], depth.format);
if (z >= depth_value[0]) if (z >= depth_value[0])
return; continue;
blitter.writeFloat4(zm.f32x4s(z), depth.base[depth_offset..], depth.format); blitter.writeFloat4(zm.f32x4s(z), depth.base[depth_offset..], depth.format);
depth_passed = true; depth_passed = true;
}
}
if (stencil_attachment_access) |stencil| {
if (stencil_state) |state| {
if (depth_passed != null and !depth_passed.?) {
updateStencilValue(stencil, stencil_offset.?, state, state.depth_fail_op);
return;
} }
updateStencilValue(stencil, stencil_offset.?, state, state.pass_op);
} }
}
for (draw_call.renderer.active_occlusion_queries.items) |active| { if (stencil_attachment_access) |stencil| {
try active.pool.addSamples(active.query, 1); if (stencil_state) |state| {
} if (depth_passed != null and !depth_passed.?) {
updateStencilValue(stencil, stencil_offset.?, state, state.depth_fail_op);
continue;
}
updateStencilValue(stencil, stencil_offset.?, state, state.pass_op);
}
}
for (color_attachment_access, 0..) |maybe_color, location| { for (draw_call.renderer.active_occlusion_queries.items) |active| {
const color = maybe_color orelse continue; try active.pool.addSamples(active.query, 1);
const color_offset = targetOffset(color, x, y) orelse continue; }
if (base.format.isUnnormalizedInteger(color.format)) { for (color_attachment_access, 0..) |maybe_color, location| {
blitter.writeInt4(std.mem.bytesToValue(U32x4, &outputs[location]), color.base[color_offset..], color.format); const color = maybe_color orelse continue;
} else { const color_offset = targetSampleOffset(color, x, y, sample_index) orelse continue;
const pipeline_data = draw_call.renderer.state.pipeline.?.interface.mode.graphics; if (base.format.isUnnormalizedInteger(color.format)) {
const src = fragmentOutputFloat4(outputs[location], color.format); blitter.writeInt4(std.mem.bytesToValue(U32x4, &outputs[location]), color.base[color_offset..], color.format);
const encoded_dst = blitter.readFloat4(color.base[color_offset..], color.format); } else {
const dst = if (base.format.isSrgb(color.format)) zm.srgbToRgb(encoded_dst) else encoded_dst; const src = fragmentOutputFloat4(outputs[location], color.format);
const final_color = if (pipeline_data.color_blend.attachments) |attachments| blk: { const encoded_dst = blitter.readFloat4(color.base[color_offset..], color.format);
if (location >= attachments.len) const dst = if (base.format.isSrgb(color.format)) zm.srgbToRgb(encoded_dst) else encoded_dst;
break :blk src; const final_color = if (pipeline_data.color_blend.attachments) |attachments| blk: {
const constants = draw_call.renderer.dynamic_state.blend_constants orelse pipeline_data.color_blend.constants; if (location >= attachments.len)
const blended = blendColor(src, dst, attachments[location], constants, color.format); break :blk src;
break :blk applyColorWriteMask(blended, dst, attachments[location].color_write_mask); const constants = draw_call.renderer.dynamic_state.blend_constants orelse pipeline_data.color_blend.constants;
} else src; const blended = blendColor(src, dst, attachments[location], constants, color.format);
const encoded_color = if (base.format.isSrgb(color.format)) zm.rgbToSrgb(final_color) else final_color; break :blk applyColorWriteMask(blended, dst, attachments[location].color_write_mask);
blitter.writeFloat4(encoded_color, color.base[color_offset..], color.format); } else src;
const encoded_color = if (base.format.isSrgb(color.format)) zm.rgbToSrgb(final_color) else final_color;
blitter.writeFloat4(encoded_color, color.base[color_offset..], color.format);
}
} }
} }
} }
fn sampleMaskEnablesAnySample(multisample: anytype, fragment_sample_mask: ?vk.SampleMask) bool {
const sample_count = multisample.rasterization_samples.toInt();
if (multisample.sample_mask == null and fragment_sample_mask == null)
return true;
if (multisample.sample_mask) |pipeline_sample_mask| {
for (pipeline_sample_mask, 0..) |word, word_index| {
if (sampleMaskWordEnablesAnySample(sample_count, word_index, word, fragment_sample_mask))
return true;
}
return false;
}
return sampleMaskWordEnablesAnySample(sample_count, 0, std.math.maxInt(vk.SampleMask), fragment_sample_mask);
}
fn sampleMaskWordEnablesAnySample(sample_count: usize, word_index: usize, pipeline_word: vk.SampleMask, fragment_sample_mask: ?vk.SampleMask) bool {
const remaining_samples = sample_count -| (word_index * 32);
const active_bits = @min(remaining_samples, 32);
if (active_bits == 0)
return false;
const active_mask: vk.SampleMask = if (active_bits == 32)
std.math.maxInt(vk.SampleMask)
else
(@as(vk.SampleMask, 1) << @intCast(active_bits)) - 1;
const fragment_word = if (word_index == 0) fragment_sample_mask orelse std.math.maxInt(vk.SampleMask) else std.math.maxInt(vk.SampleMask);
return ((pipeline_word & fragment_word) & active_mask) != 0;
}
fn sampleMaskEnablesSample(multisample: anytype, fragment_sample_mask: ?vk.SampleMask, sample_index: usize) bool {
if (sample_index >= multisample.rasterization_samples.toInt())
return false;
const word_index = sample_index / 32;
const bit_index: u5 = @intCast(sample_index % 32);
const bit = @as(vk.SampleMask, 1) << bit_index;
const pipeline_word = if (multisample.sample_mask) |pipeline_sample_mask|
if (word_index < pipeline_sample_mask.len) pipeline_sample_mask[word_index] else @as(vk.SampleMask, 0)
else
std.math.maxInt(vk.SampleMask);
const fragment_word = if (word_index == 0) fragment_sample_mask orelse std.math.maxInt(vk.SampleMask) else std.math.maxInt(vk.SampleMask);
return (pipeline_word & fragment_word & bit) != 0;
}
@@ -182,6 +182,7 @@ inline fn run(data: RunData) !void {
var fragment_result: fragment.InvocationResult = .{ var fragment_result: fragment.InvocationResult = .{
.outputs = std.mem.zeroes([spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(F32x4)]u8), .outputs = std.mem.zeroes([spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(F32x4)]u8),
.depth = null, .depth = null,
.sample_mask = null,
}; };
if (data.has_fragment_shader) { if (data.has_fragment_shader) {
const inputs = try common.interpolateVertexOutputs(data.allocator, &data.v0, &data.v1, &data.v2, b0, b1, b2); const inputs = try common.interpolateVertexOutputs(data.allocator, &data.v0, &data.v1, &data.v2, b0, b1, b2);
@@ -251,6 +252,7 @@ inline fn run(data: RunData) !void {
@intCast(x), @intCast(x),
@intCast(y), @intCast(y),
fragment_result.depth orelse z, fragment_result.depth orelse z,
fragment_result.sample_mask,
); );
} }
} }
+28
View File
@@ -50,6 +50,10 @@ mode: union(enum) {
front_face: vk.FrontFace, front_face: vk.FrontFace,
line_width: f32, line_width: f32,
}, },
multisample: struct {
rasterization_samples: vk.SampleCountFlags,
sample_mask: ?[]vk.SampleMask,
},
color_blend: struct { color_blend: struct {
attachments: ?[]vk.PipelineColorBlendAttachmentState, attachments: ?[]vk.PipelineColorBlendAttachmentState,
constants: [4]f32, constants: [4]f32,
@@ -106,6 +110,9 @@ pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipe
var scissors: ?[]vk.Rect2D = null; var scissors: ?[]vk.Rect2D = null;
errdefer if (scissors) |value| allocator.free(value); errdefer if (scissors) |value| allocator.free(value);
var sample_mask: ?[]vk.SampleMask = null;
errdefer if (sample_mask) |value| allocator.free(value);
var color_attachments: ?[]vk.PipelineColorBlendAttachmentState = null; var color_attachments: ?[]vk.PipelineColorBlendAttachmentState = null;
errdefer if (color_attachments) |value| allocator.free(value); errdefer if (color_attachments) |value| allocator.free(value);
@@ -186,6 +193,24 @@ pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipe
.front_face = if (info.p_rasterization_state) |state| state.front_face else return VkError.ValidationFailed, .front_face = if (info.p_rasterization_state) |state| state.front_face else return VkError.ValidationFailed,
.line_width = if (info.p_rasterization_state) |state| state.line_width else return VkError.ValidationFailed, .line_width = if (info.p_rasterization_state) |state| state.line_width else return VkError.ValidationFailed,
}, },
.multisample = blk: {
if (rasterizer_discard_enable) {
break :blk .{
.rasterization_samples = .{ .@"1_bit" = true },
.sample_mask = null,
};
}
const state = info.p_multisample_state orelse return VkError.ValidationFailed;
const mask_word_count: usize = @divTrunc(state.rasterization_samples.toInt() + 31, 32);
break :blk .{
.rasterization_samples = state.rasterization_samples,
.sample_mask = if (state.p_sample_mask) |mask| blk_mask: {
sample_mask = allocator.dupe(vk.SampleMask, mask[0..mask_word_count]) catch return VkError.OutOfHostMemory;
break :blk_mask sample_mask;
} else null,
};
},
.color_blend = blk: { .color_blend = blk: {
if (rasterizer_discard_enable or !has_color_attachments) { if (rasterizer_discard_enable or !has_color_attachments) {
break :blk .{ break :blk .{
@@ -276,6 +301,9 @@ pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void {
if (graphics.viewport_state.scissor) |scissor| { if (graphics.viewport_state.scissor) |scissor| {
allocator.free(scissor); allocator.free(scissor);
} }
if (graphics.multisample.sample_mask) |sample_mask| {
allocator.free(sample_mask);
}
if (graphics.color_blend.attachments) |attachments| { if (graphics.color_blend.attachments) |attachments| {
allocator.free(attachments); allocator.free(attachments);
} }
-1
View File
@@ -140,7 +140,6 @@ const device_pfn_map = block: {
functionMapEntryPoint("vkAcquireNextImage2KHR"), functionMapEntryPoint("vkAcquireNextImage2KHR"),
functionMapEntryPoint("vkAllocateCommandBuffers"), functionMapEntryPoint("vkAllocateCommandBuffers"),
functionMapEntryPoint("vkAllocateDescriptorSets"), functionMapEntryPoint("vkAllocateDescriptorSets"),
functionMapEntryPoint("vkAllocateDescriptorSets"),
functionMapEntryPoint("vkAllocateMemory"), functionMapEntryPoint("vkAllocateMemory"),
functionMapEntryPoint("vkBeginCommandBuffer"), functionMapEntryPoint("vkBeginCommandBuffer"),
functionMapEntryPoint("vkBindBufferMemory"), functionMapEntryPoint("vkBindBufferMemory"),