From d145f85d170c0c468f2d3433fa351ab834f88707 Mon Sep 17 00:00:00 2001 From: Kbz-8 Date: Mon, 29 Jun 2026 23:22:31 +0200 Subject: [PATCH] improving robust buffer support --- build.zig.zon | 4 +- src/software/SoftCommandBuffer.zig | 3 + src/software/SoftPipeline.zig | 107 +++++++++++++++++----- src/software/SoftSampler.zig | 105 ++++++++++++++++++++- src/software/device/Renderer.zig | 13 +++ src/software/device/blitter.zig | 11 ++- src/software/device/clip.zig | 5 +- src/software/device/fragment.zig | 3 + src/software/device/rasterizer.zig | 74 +++++++++++++++ src/software/device/vertex_dispatcher.zig | 11 ++- 10 files changed, 300 insertions(+), 36 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index 4e3ea45..5373dee 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -32,8 +32,8 @@ // Soft dependencies .SPIRV_Interpreter = .{ - .url = "git+https://github.com/Kbz-8/SPIRV-Interpreter#fcf303a0c62a66ff32fc92fe9c92ca9294f72caf", - .hash = "SPIRV_Interpreter-0.0.1-ajmpnx3dCAAfuabTUWPWdTVqPGBPeSUCCTI-ncKowi5D", + .url = "git+https://github.com/Kbz-8/SPIRV-Interpreter#e49383f07ec20d194811a48986f9ffd3483aed2b", + .hash = "SPIRV_Interpreter-0.0.1-ajmpn8gICQCM5IcZkaGnupby40N0Xi7-m6U4jxK8mKoS", .lazy = true, }, //.SPIRV_Interpreter = .{ diff --git a/src/software/SoftCommandBuffer.zig b/src/software/SoftCommandBuffer.zig index 0585980..1bfdb88 100644 --- a/src/software/SoftCommandBuffer.zig +++ b/src/software/SoftCommandBuffer.zig @@ -301,6 +301,7 @@ pub fn beginRenderPass(interface: *Interface, render_pass: *base.RenderPass, fra device.renderer.framebuffer = impl.framebuffer; device.renderer.render_area = impl.render_area; device.renderer.subpass_index = 0; + device.renderer.resetInputAttachmentSnapshots(); for (impl.render_pass.interface.attachments, impl.framebuffer.interface.attachments, 0..) |desc, attachment, index| { if (!attachmentIsReferencedBySubpass(impl.render_pass, @intCast(index))) @@ -1021,6 +1022,7 @@ pub fn endRenderPass(interface: *Interface) VkError!void { try framebuffer.resolveAttachments(render_pass, device.renderer.subpass_index); + device.renderer.resetInputAttachmentSnapshots(); device.renderer.render_pass = null; device.renderer.framebuffer = null; device.renderer.render_area = null; @@ -1125,6 +1127,7 @@ pub fn nextSubpass(interface: *Interface, _: vk.SubpassContents) VkError!void { try framebuffer.resolveAttachments(render_pass, device.renderer.subpass_index); + device.renderer.resetInputAttachmentSnapshots(); device.renderer.subpass_index += 1; } }; diff --git a/src/software/SoftPipeline.zig b/src/software/SoftPipeline.zig index 1c81ef2..4f6dbea 100644 --- a/src/software/SoftPipeline.zig +++ b/src/software/SoftPipeline.zig @@ -11,7 +11,19 @@ const Device = base.Device; const VkError = base.VkError; const SpvRuntimeError = spv.Runtime.RuntimeError; +pub const InputAttachmentSnapshot = struct { + image: *base.Image, + aspect_mask: vk.ImageAspectFlags, + mip_level: u32, + array_layer: u32, + data: []const u8, + row_pitch: usize, + slice_pitch: usize, + sample_stride: usize, +}; + pub threadlocal var current_fragment_coord: ?vk.Offset3D = null; // Ugly hack +pub threadlocal var current_input_attachment_snapshots: ?[]const InputAttachmentSnapshot = null; const NonDispatchable = base.NonDispatchable; const ShaderModule = base.ShaderModule; @@ -336,6 +348,32 @@ fn sampledTexelOffset(image: *SoftImage, offset: vk.Offset3D, subresource: vk.Im @as(usize, sample_index) * image.getMipLevelSize(subresource.aspect_mask, subresource.mip_level); } +fn sampledSnapshotTexel(snapshot: InputAttachmentSnapshot, offset: vk.Offset3D, format: vk.Format, sample_index: u32) SpvRuntimeError![]const u8 { + const texel_size = base.format.texelSize(format); + const texel_offset = + @as(usize, @intCast(offset.z)) * snapshot.slice_pitch + + @as(usize, @intCast(offset.y)) * snapshot.row_pitch + + @as(usize, @intCast(offset.x)) * texel_size + + @as(usize, sample_index) * snapshot.sample_stride; + if (texel_offset > snapshot.data.len or texel_size > snapshot.data.len - texel_offset) + return SpvRuntimeError.OutOfBounds; + return snapshot.data[texel_offset .. texel_offset + texel_size]; +} + +fn findInputAttachmentSnapshot(image_view: *SoftImageView, subresource: vk.ImageSubresource) ?InputAttachmentSnapshot { + const snapshots = current_input_attachment_snapshots orelse return null; + for (snapshots) |snapshot| { + if (snapshot.image == image_view.interface.image and + snapshot.aspect_mask.toInt() == subresource.aspect_mask.toInt() and + snapshot.mip_level == subresource.mip_level and + snapshot.array_layer == subresource.array_layer) + { + return snapshot; + } + } + return null; +} + fn readImageFloat4Sample(image: *SoftImage, offset: vk.Offset3D, subresource: vk.ImageSubresource, format: vk.Format, sample_index: u32) VkError!zm.F32x4 { if (image.interface.samples.toInt() == 1) return image.readFloat4(offset, subresource, format); @@ -356,18 +394,27 @@ fn readImageInt4Sample(image: *SoftImage, offset: vk.Offset3D, subresource: vk.I 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 }; + _ = z; + return .{ .x = coord.x + x, .y = coord.y + y, .z = coord.z }; } fn bufferViewRange(buffer_view: *const SoftBufferView) SpvRuntimeError!usize { const offset: usize = @intCast(buffer_view.interface.offset); - if (offset > buffer_view.interface.buffer.size) + const buffer = buffer_view.interface.buffer; + const bound_size: usize = if (buffer.memory) |memory| blk: { + const buffer_offset: usize = @intCast(buffer.offset); + if (buffer_offset >= memory.size) + break :blk 0; + break :blk @min(@as(usize, @intCast(buffer.size)), @as(usize, @intCast(memory.size - buffer.offset))); + } else @intCast(buffer.size); + + if (offset > bound_size) return SpvRuntimeError.Unknown; if (buffer_view.interface.range == vk.WHOLE_SIZE) - return @intCast(buffer_view.interface.buffer.size - offset); + return bound_size - offset; - return @intCast(buffer_view.interface.range); + return @min(@as(usize, @intCast(buffer_view.interface.range)), bound_size - offset); } fn mapBufferViewTexel(buffer_view: *const SoftBufferView, x: i32) SpvRuntimeError![]u8 { @@ -400,7 +447,10 @@ fn readImageFloat4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32, var pixel = zm.f32x4s(0.0); if (dim == .Buffer) { const buffer_view = try bufferViewFromContext(context); - pixel = blitter.readFloat4(try mapBufferViewTexel(buffer_view, x), buffer_view.interface.format); + pixel = if (mapBufferViewTexel(buffer_view, x)) |texel| + blitter.readFloat4(texel, buffer_view.interface.format) + else |_| + zm.f32x4s(0.0); } else { const image_view: *SoftImageView = @ptrCast(@alignCast(context)); const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image)); @@ -424,13 +474,14 @@ fn readImageFloat4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32, .array_layer = array_layer, }; const sample_index: u32 = if (image.interface.samples.toInt() > 1) @intCast(z) else 0; - pixel = SoftSampler.swizzleFloat4(readImageFloat4Sample( - image, - image_coord, - subresource, - base.format.fromAspect(image_view.interface.format, aspect_mask), - sample_index, - ) catch return SpvRuntimeError.Unknown, image_view.interface.components); + const format = base.format.fromAspect(image_view.interface.format, aspect_mask); + const raw_pixel = if (dim == .SubpassData) blk: { + if (findInputAttachmentSnapshot(image_view, subresource)) |snapshot| { + break :blk blitter.readFloat4(try sampledSnapshotTexel(snapshot, image_coord, format, sample_index), format); + } + break :blk readImageFloat4Sample(image, image_coord, subresource, format, sample_index) catch return SpvRuntimeError.Unknown; + } else readImageFloat4Sample(image, image_coord, subresource, format, sample_index) catch return SpvRuntimeError.Unknown; + pixel = SoftSampler.swizzleFloat4(raw_pixel, image_view.interface.components); } return .{ .x = pixel[0], @@ -444,7 +495,10 @@ fn readImageInt4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32, l var pixel = @Vector(4, u32){ 0, 0, 0, 0 }; if (dim == .Buffer) { const buffer_view = try bufferViewFromContext(context); - pixel = blitter.readInt4(try mapBufferViewTexel(buffer_view, x), buffer_view.interface.format); + pixel = if (mapBufferViewTexel(buffer_view, x)) |texel| + blitter.readInt4(texel, buffer_view.interface.format) + else |_| + @Vector(4, u32){ 0, 0, 0, 0 }; } else { const image_view: *SoftImageView = @ptrCast(@alignCast(context)); const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image)); @@ -468,13 +522,14 @@ fn readImageInt4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32, l .array_layer = array_layer, }; const sample_index: u32 = if (image.interface.samples.toInt() > 1) @intCast(z) else 0; - pixel = SoftSampler.swizzleInt4(readImageInt4Sample( - image, - image_coord, - subresource, - base.format.fromAspect(image_view.interface.format, aspect_mask), - sample_index, - ) catch return SpvRuntimeError.Unknown, image_view.interface.components); + const format = base.format.fromAspect(image_view.interface.format, aspect_mask); + const raw_pixel = if (dim == .SubpassData) blk: { + if (findInputAttachmentSnapshot(image_view, subresource)) |snapshot| { + break :blk blitter.readInt4(try sampledSnapshotTexel(snapshot, image_coord, format, sample_index), format); + } + break :blk readImageInt4Sample(image, image_coord, subresource, format, sample_index) catch return SpvRuntimeError.Unknown; + } else readImageInt4Sample(image, image_coord, subresource, format, sample_index) catch return SpvRuntimeError.Unknown; + pixel = SoftSampler.swizzleInt4(raw_pixel, image_view.interface.components); } return .{ .x = pixel[0], @@ -488,7 +543,9 @@ fn writeImageFloat4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32 const vec_pixel = zm.f32x4(pixel.x, pixel.y, pixel.z, pixel.w); if (dim == .Buffer) { const buffer_view = try bufferViewFromContext(context); - blitter.writeFloat4(vec_pixel, try mapBufferViewTexel(buffer_view, x), buffer_view.interface.format); + if (mapBufferViewTexel(buffer_view, x)) |texel| { + blitter.writeFloat4(vec_pixel, texel, buffer_view.interface.format); + } else |_| {} } else { const image_view: *SoftImageView = @ptrCast(@alignCast(context)); const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image)); @@ -514,7 +571,9 @@ fn writeImageInt4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32, const vec_pixel = @Vector(4, u32){ pixel.x, pixel.y, pixel.z, pixel.w }; if (dim == .Buffer) { const buffer_view = try bufferViewFromContext(context); - blitter.writeInt4(vec_pixel, try mapBufferViewTexel(buffer_view, x), buffer_view.interface.format); + if (mapBufferViewTexel(buffer_view, x)) |texel| { + blitter.writeInt4(vec_pixel, texel, buffer_view.interface.format); + } else |_| {} } else { const image_view: *SoftImageView = @ptrCast(@alignCast(context)); const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image)); @@ -580,14 +639,14 @@ fn sampleImageInt4(context: *anyopaque, context2: *anyopaque, dim: spv.SpvDim, x }; } -fn sampleImageDref(context: *anyopaque, context2: *anyopaque, dim: spv.SpvDim, x: f32, y: f32, z: f32, dref: f32, lod: ?f32, offset: spv.Runtime.ImageOffset) SpvRuntimeError!f32 { +fn sampleImageDref(context: *anyopaque, context2: *anyopaque, dim: spv.SpvDim, x: f32, y: f32, z: f32, w: f32, dref: f32, lod: ?f32, offset: spv.Runtime.ImageOffset) SpvRuntimeError!f32 { if (dim == .Buffer) return SpvRuntimeError.UnsupportedSpirV; const image_view: *SoftImageView = @ptrCast(@alignCast(context)); const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image)); const sampler: *SoftSampler = @ptrCast(@alignCast(context2)); - return SoftSampler.sampleImageDref(image, image_view, sampler, dim, x, y, z, dref, lod, offset) catch return SpvRuntimeError.Unknown; + return SoftSampler.sampleImageDref(image, image_view, sampler, dim, x, y, z, w, dref, lod, offset) catch return SpvRuntimeError.Unknown; } fn queryImageSize(context: *anyopaque, dim: spv.SpvDim, arrayed: bool, lod: ?i32) SpvRuntimeError!spv.Runtime.Vec4(u32) { diff --git a/src/software/SoftSampler.zig b/src/software/SoftSampler.zig index 6f76d24..affbc7f 100644 --- a/src/software/SoftSampler.zig +++ b/src/software/SoftSampler.zig @@ -422,6 +422,17 @@ fn readSampledFloat4At(context: *const ImageSamplingContext, ix: i32, iy: i32, i return swizzleFloat4(color, context.image_view.interface.components); } +const DepthCompareSamplingContext = struct { + image_context: ImageSamplingContext, + dref: f32, +}; + +fn readDepthCompareAt(context: *const DepthCompareSamplingContext, ix: i32, iy: i32, iz: i32) VkError!F32x4 { + const color = try readSampledFloat4At(&context.image_context, ix, iy, iz); + const result: f32 = if (compareDepth(context.image_context.sampler.interface.compare_op, context.dref, color[0])) 1.0 else 0.0; + return zm.f32x4s(result); +} + fn readSampledInt4( image: *SoftImage, image_view: *SoftImageView, @@ -562,6 +573,70 @@ pub fn sampleImageFloat4(image: *SoftImage, image_view: *SoftImageView, sampler: return sampleImageFloat4Level(image, image_view, sampler, dim, x, y, z, sampleMipLevel(image_view, sampler, lod), filter, offset); } +fn sampleImageDrefLevel(image: *SoftImage, image_view: *SoftImageView, sampler: *Self, dim: spv.SpvDim, x: f32, y: f32, z: f32, w: f32, dref: f32, mip_level: u32, filter: vk.Filter, offset: ImageOffset) VkError!f32 { + const extent = image.getMipLevelExtent(mip_level); + const coord: CubeCoordinate = switch (image_view.interface.view_type) { + .@"1d_array" => .{ + .u = x, + .v = y, + .face = 0, + }, + .@"1d" => .{ + .u = x, + .v = 0.0, + .face = 0, + }, + .@"2d_array" => .{ + .u = x, + .v = y, + .w = z, + .face = 0, + }, + .cube => resolveCubeCoordinate(x, y, z), + .cube_array => blk: { + var coord = resolveCubeCoordinate(x, y, z); + coord.w = w; + break :blk coord; + }, + else => .{ + .u = x, + .v = y, + .w = z, + .face = 0, + }, + }; + const scale_u: f32 = if (sampler.interface.unnormalized_coordinates == .true) 1.0 else @floatFromInt(extent.width); + 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 image_context: ImageSamplingContext = .{ + .image = image, + .image_view = image_view, + .sampler = sampler, + .dim = dim, + .coord = coord, + .mip_level = mip_level, + }; + const context: DepthCompareSamplingContext = .{ + .image_context = image_context, + .dref = dref, + }; + + const result = try sampleFloat4( + *const DepthCompareSamplingContext, + &context, + zm.f32x4( + coord.u * scale_u + @as(f32, @floatFromInt(offset.x)), + coord.v * scale_v + @as(f32, @floatFromInt(offset.y)), + coord.w * scale_w + @as(f32, @floatFromInt(offset.z)), + 0.0, + ), + filter, + image_view.interface.view_type == .@"3d", + readDepthCompareAt, + ); + return result[0]; +} + 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_view, sampler, lod); const extent = image.getMipLevelExtent(mip_level); @@ -611,11 +686,33 @@ pub fn sampleImageInt4(image: *SoftImage, image_view: *SoftImageView, sampler: * return swizzleInt4(color, image_view.interface.components); } -pub fn sampleImageDref(image: *SoftImage, image_view: *SoftImageView, sampler: *Self, dim: spv.SpvDim, x: f32, y: f32, z: f32, dref: f32, lod: ?f32, offset: ImageOffset) VkError!f32 { - const color = try sampleImageFloat4(image, image_view, sampler, dim, x, y, z, lod, offset); - if (sampler.interface.compare_enable == .false) +pub fn sampleImageDref(image: *SoftImage, image_view: *SoftImageView, sampler: *Self, dim: spv.SpvDim, x: f32, y: f32, z: f32, w: f32, dref: f32, lod: ?f32, offset: ImageOffset) VkError!f32 { + if (sampler.interface.compare_enable == .false) { + const color = try sampleImageFloat4(image, image_view, sampler, dim, x, y, z, lod, offset); return color[0]; - return if (compareDepth(sampler.interface.compare_op, dref, color[0])) 1.0 else 0.0; + } + + const range = image_view.interface.subresource_range; + const mip_count = viewMipCount(image_view); + const clamped_lod = sampleLod(image_view, sampler, lod); + const filter = sampleFilter(sampler, clamped_lod); + + if (mip_count > 1 and sampler.interface.mipmap_mode == .linear) { + const lower_lod = @floor(clamped_lod); + const upper_lod = @min(lower_lod + 1.0, @as(f32, @floatFromInt(mip_count - 1))); + const lower_level = range.base_mip_level + @as(u32, @intFromFloat(lower_lod)); + const upper_level = range.base_mip_level + @as(u32, @intFromFloat(upper_lod)); + const lower = try sampleImageDrefLevel(image, image_view, sampler, dim, x, y, z, w, dref, lower_level, filter, offset); + + if (upper_level == lower_level) + return lower; + + const upper = try sampleImageDrefLevel(image, image_view, sampler, dim, x, y, z, w, dref, upper_level, filter, offset); + const weight = clamped_lod - lower_lod; + return lower * (1.0 - weight) + upper * weight; + } + + return sampleImageDrefLevel(image, image_view, sampler, dim, x, y, z, w, dref, sampleMipLevel(image_view, sampler, lod), filter, offset); } pub fn sampleFloat4( diff --git a/src/software/device/Renderer.zig b/src/software/device/Renderer.zig index 43f8cd1..fdbffd3 100644 --- a/src/software/device/Renderer.zig +++ b/src/software/device/Renderer.zig @@ -76,6 +76,7 @@ pub const DrawCall = struct { color_attachments: []*base.ImageView, depth_attachment: ?*base.ImageView, + input_attachment_snapshots: []const SoftPipeline.InputAttachmentSnapshot, render_pass: *SoftRenderPass, framebuffer: *SoftFramebuffer, @@ -99,6 +100,7 @@ pub const DrawCall = struct { .scissor = undefined, .color_attachments = framebuffer.interface.attachments[0..], .depth_attachment = if (render_pass.interface.subpasses[renderer.subpass_index].depth_stencil_attachments) |desc| framebuffer.interface.attachments[desc.attachment] else null, + .input_attachment_snapshots = &.{}, .render_pass = render_pass, .framebuffer = framebuffer, .rasterizer_wait_group = .init, @@ -139,6 +141,7 @@ render_pass: ?*SoftRenderPass, framebuffer: ?*SoftFramebuffer, render_area: ?vk.Rect2D, dynamic_state: DynamicState, +input_attachment_snapshots: []const SoftPipeline.InputAttachmentSnapshot, subpass_index: usize, active_occlusion_queries: *std.ArrayList(ExecutionDevice.ActiveOcclusionQuery), @@ -150,6 +153,7 @@ pub fn init(device: *SoftDevice, state: *PipelineState, active_occlusion_queries .render_pass = null, .framebuffer = null, .render_area = null, + .input_attachment_snapshots = &.{}, .dynamic_state = .{ .viewports = null, .scissor = null, @@ -167,6 +171,15 @@ pub fn init(device: *SoftDevice, state: *PipelineState, active_occlusion_queries }; } +pub fn resetInputAttachmentSnapshots(self: *Self) void { + const allocator = self.device.device_allocator.allocator(); + for (self.input_attachment_snapshots) |snapshot| { + allocator.free(snapshot.data); + } + allocator.free(self.input_attachment_snapshots); + self.input_attachment_snapshots = &.{}; +} + pub fn draw(self: *Self, vertex_count: usize, instance_count: usize, first_vertex: usize, first_instance: usize) VkError!void { var bounded_allocator: BoundedAllocator = .init(self.device.device_allocator.allocator(), 4 * @"1GiB"); try self.drawCall(&bounded_allocator, vertex_count, instance_count, first_vertex, first_instance, null, null); diff --git a/src/software/device/blitter.zig b/src/software/device/blitter.zig index daf7b80..1523e6a 100644 --- a/src/software/device/blitter.zig +++ b/src/software/device/blitter.zig @@ -193,7 +193,12 @@ fn sample(src: []const u8, pos: F32x4, dim: F32x4, slice_bytes: usize, pitch_byt const sample_stride = slice_bytes * @as(usize, @intFromFloat(dim[2])); color = zm.f32x4s(0.0); for (0..state.src_samples) |sample_index| { - color += readFloat4(src_map[sample_index * sample_stride ..], state.src_format); + var sample_color = readFloat4(src_map[sample_index * sample_stride ..], state.src_format); + if (state.allow_srgb_conversion and base.format.isSrgb(state.src_format)) { + sample_color = applyScaleAndClamp(sample_color, state, true); + apply_srgb_convertion = false; + } + color += sample_color; } color /= zm.f32x4s(@floatFromInt(state.src_samples)); } else { @@ -350,7 +355,9 @@ pub fn blitRegionWithFormats(src: *const SoftImage, dst: *SoftImage, region: vk. const dst_row_pitch_bytes = dst.getRowPitchMemSizeForMipLevelWithFormat(region.dst_subresource.aspect_mask, region.dst_subresource.mip_level, dst_format); const apply_filter = (filter != .nearest); - const allow_srgb_conversion = apply_filter or base.format.isSrgb(src_format) != base.format.isSrgb(dst_format); + const resolve_srgb = src.interface.samples.toInt() > 1 and dst.interface.samples.toInt() == 1 and + base.format.isSrgb(src_format) and base.format.isSrgb(dst_format); + const allow_srgb_conversion = apply_filter or resolve_srgb or base.format.isSrgb(src_format) != base.format.isSrgb(dst_format); var src_subresource = vk.ImageSubresource{ .aspect_mask = region.src_subresource.aspect_mask, diff --git a/src/software/device/clip.zig b/src/software/device/clip.zig index 90db196..c208937 100644 --- a/src/software/device/clip.zig +++ b/src/software/device/clip.zig @@ -120,8 +120,9 @@ pub fn viewportTransformVertex(viewport: vk.Viewport, vertex: *Vertex) void { const o_y = viewport.y + viewport.height / 2.0; const o_z = viewport.min_depth; - const x_screen = ((p_x / 2.0) * x_ndc) + o_x; - const y_screen = ((p_y / 2.0) * y_ndc) + o_y; + const subpixel_scale = 16.0; + const x_screen = @round((((p_x / 2.0) * x_ndc) + o_x) * subpixel_scale) / subpixel_scale; + const y_screen = @round((((p_y / 2.0) * y_ndc) + o_y) * subpixel_scale) / subpixel_scale; const z_screen = (p_z * z_ndc) + o_z; vertex.position = zm.f32x4(x_screen, y_screen, z_screen, w); diff --git a/src/software/device/fragment.zig b/src/software/device/fragment.zig index 6197159..c01ae98 100644 --- a/src/software/device/fragment.zig +++ b/src/software/device/fragment.zig @@ -85,12 +85,15 @@ pub fn shaderInvocation( const SoftPipeline = @import("../SoftPipeline.zig"); const previous_fragment_coord = SoftPipeline.current_fragment_coord; + const previous_input_attachment_snapshots = SoftPipeline.current_input_attachment_snapshots; SoftPipeline.current_fragment_coord = .{ .x = @intFromFloat(position[0]), .y = @intFromFloat(position[1]), .z = 0, }; + SoftPipeline.current_input_attachment_snapshots = draw_call.input_attachment_snapshots; defer SoftPipeline.current_fragment_coord = previous_fragment_coord; + defer SoftPipeline.current_input_attachment_snapshots = previous_input_attachment_snapshots; const entry = try rt.getEntryPointByName(shader.entry); diff --git a/src/software/device/rasterizer.zig b/src/software/device/rasterizer.zig index c90bdc0..45edb9d 100644 --- a/src/software/device/rasterizer.zig +++ b/src/software/device/rasterizer.zig @@ -15,6 +15,7 @@ const Renderer = @import("Renderer.zig"); const Vertex = Renderer.Vertex; const DrawCall = Renderer.DrawCall; const SoftImage = @import("../SoftImage.zig"); +const SoftPipeline = @import("../SoftPipeline.zig"); const VkError = base.VkError; const SpvRuntimeError = spv.Runtime.RuntimeError; @@ -31,11 +32,84 @@ fn renderTargetSubresourceSize(image: *const SoftImage, image_view: *const base. return image.getMultiSampledLevelSize(aspect_mask, mip_level); } +fn snapshotInputAttachments(allocator: std.mem.Allocator, draw_call: *DrawCall) VkError![]SoftPipeline.InputAttachmentSnapshot { + const subpass = draw_call.render_pass.interface.subpasses[draw_call.renderer.subpass_index]; + const input_attachments = subpass.input_attachments orelse return &.{}; + + var snapshot_count: usize = 0; + for (input_attachments) |attachment_ref| { + if (attachment_ref.attachment == vk.ATTACHMENT_UNUSED) + continue; + + const image_view = draw_call.framebuffer.interface.attachments[attachment_ref.attachment]; + if (image_view.image.samples.toInt() == 1) + continue; + + const range = image_view.subresource_range; + if (range.aspect_mask.depth_bit and range.aspect_mask.stencil_bit) + snapshot_count += 2 + else + snapshot_count += 1; + } + if (snapshot_count == 0) + return &.{}; + + const snapshots = allocator.alloc(SoftPipeline.InputAttachmentSnapshot, snapshot_count) catch return VkError.OutOfDeviceMemory; + var snapshot_index: usize = 0; + errdefer { + for (snapshots[0..snapshot_index]) |snapshot| { + allocator.free(snapshot.data); + } + allocator.free(snapshots); + } + + for (input_attachments) |attachment_ref| { + if (attachment_ref.attachment == vk.ATTACHMENT_UNUSED) + continue; + + const image_view: *base.ImageView = draw_call.framebuffer.interface.attachments[attachment_ref.attachment]; + const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.image)); + if (image.interface.samples.toInt() == 1) + continue; + + const range = image_view.subresource_range; + const aspects: []const vk.ImageAspectFlags = if (range.aspect_mask.depth_bit and range.aspect_mask.stencil_bit) + &.{ .{ .depth_bit = true }, .{ .stencil_bit = true } } + else + &.{range.aspect_mask}; + + for (aspects) |aspect_mask| { + const offset = try image.getSubresourceOffset(aspect_mask, range.base_mip_level, range.base_array_layer); + const size = renderTargetSubresourceSize(image, image_view, aspect_mask, range.base_mip_level); + const live_data = try image.mapAsSliceWithAddedOffset(u8, offset, size); + const data = allocator.dupe(u8, live_data) catch return VkError.OutOfDeviceMemory; + + snapshots[snapshot_index] = .{ + .image = image_view.image, + .aspect_mask = aspect_mask, + .mip_level = range.base_mip_level, + .array_layer = range.base_array_layer, + .data = data, + .row_pitch = image.getRowPitchMemSizeForMipLevelWithFormat(aspect_mask, range.base_mip_level, image_view.format), + .slice_pitch = image.getSliceMemSizeForMipLevelWithFormat(aspect_mask, range.base_mip_level, image_view.format), + .sample_stride = image.getMipLevelSize(aspect_mask, range.base_mip_level), + }; + snapshot_index += 1; + } + } + + return snapshots; +} + pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocator, draw_call: *DrawCall) VkError!void { const io = draw_call.renderer.device.interface.io(); const pipeline_data = (renderer.state.pipeline orelse return VkError.InvalidHandleDrv).interface.mode.graphics; const topology = pipeline_data.input_assembly.topology; + if (renderer.input_attachment_snapshots.len == 0) { + renderer.input_attachment_snapshots = try snapshotInputAttachments(renderer.device.device_allocator.allocator(), draw_call); + } + draw_call.input_attachment_snapshots = renderer.input_attachment_snapshots; const color_attachments = draw_call.render_pass.interface.subpasses[renderer.subpass_index].color_attachments orelse &.{}; const color_attachment_access = allocator.alloc(?common.RenderTargetAccess, color_attachments.len) catch return VkError.OutOfDeviceMemory; diff --git a/src/software/device/vertex_dispatcher.zig b/src/software/device/vertex_dispatcher.zig index b88cd0e..20da710 100644 --- a/src/software/device/vertex_dispatcher.zig +++ b/src/software/device/vertex_dispatcher.zig @@ -90,9 +90,16 @@ inline fn run(data: RunData) !void { }; const offset = buffer.interface.offset + vertex_buffer.offset + (binding_info.stride * input_index) + attribute.offset; - const buffer_memory_map: []u8 = try buffer_memory.map(offset, buffer_memory_size); + var robust_vertex_bytes: [64]u8 = @splat(0); + if (buffer_memory_size > robust_vertex_bytes.len) + return VkError.Unknown; + if (offset < buffer_memory.size) { + const available = @min(buffer_memory_size, @as(usize, @intCast(buffer_memory.size - offset))); + const buffer_memory_map: []const u8 = buffer_memory.map(offset, available) catch &.{}; + @memcpy(robust_vertex_bytes[0..buffer_memory_map.len], buffer_memory_map); + } - try writeVertexInput(rt, data.allocator, buffer_memory_map, attribute.format, attribute.location); + try writeVertexInput(rt, data.allocator, robust_vertex_bytes[0..buffer_memory_size], attribute.format, attribute.location); } }