improving robust buffer support
Build / build (push) Successful in 2m33s
Test / build_and_test (push) Successful in 2m11s

This commit is contained in:
2026-06-29 23:22:31 +02:00
parent 14ce2c6f9b
commit d145f85d17
10 changed files with 300 additions and 36 deletions
+2 -2
View File
@@ -32,8 +32,8 @@
// Soft dependencies // Soft dependencies
.SPIRV_Interpreter = .{ .SPIRV_Interpreter = .{
.url = "git+https://github.com/Kbz-8/SPIRV-Interpreter#fcf303a0c62a66ff32fc92fe9c92ca9294f72caf", .url = "git+https://github.com/Kbz-8/SPIRV-Interpreter#e49383f07ec20d194811a48986f9ffd3483aed2b",
.hash = "SPIRV_Interpreter-0.0.1-ajmpnx3dCAAfuabTUWPWdTVqPGBPeSUCCTI-ncKowi5D", .hash = "SPIRV_Interpreter-0.0.1-ajmpn8gICQCM5IcZkaGnupby40N0Xi7-m6U4jxK8mKoS",
.lazy = true, .lazy = true,
}, },
//.SPIRV_Interpreter = .{ //.SPIRV_Interpreter = .{
+3
View File
@@ -301,6 +301,7 @@ pub fn beginRenderPass(interface: *Interface, render_pass: *base.RenderPass, fra
device.renderer.framebuffer = impl.framebuffer; device.renderer.framebuffer = impl.framebuffer;
device.renderer.render_area = impl.render_area; device.renderer.render_area = impl.render_area;
device.renderer.subpass_index = 0; device.renderer.subpass_index = 0;
device.renderer.resetInputAttachmentSnapshots();
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))) 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); try framebuffer.resolveAttachments(render_pass, device.renderer.subpass_index);
device.renderer.resetInputAttachmentSnapshots();
device.renderer.render_pass = null; device.renderer.render_pass = null;
device.renderer.framebuffer = null; device.renderer.framebuffer = null;
device.renderer.render_area = 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); try framebuffer.resolveAttachments(render_pass, device.renderer.subpass_index);
device.renderer.resetInputAttachmentSnapshots();
device.renderer.subpass_index += 1; device.renderer.subpass_index += 1;
} }
}; };
+83 -24
View File
@@ -11,7 +11,19 @@ const Device = base.Device;
const VkError = base.VkError; const VkError = base.VkError;
const SpvRuntimeError = spv.Runtime.RuntimeError; 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_fragment_coord: ?vk.Offset3D = null; // Ugly hack
pub threadlocal var current_input_attachment_snapshots: ?[]const InputAttachmentSnapshot = null;
const NonDispatchable = base.NonDispatchable; const NonDispatchable = base.NonDispatchable;
const ShaderModule = base.ShaderModule; 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); @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 { 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) if (image.interface.samples.toInt() == 1)
return image.readFloat4(offset, subresource, format); 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 { 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 }; _ = z;
return .{ .x = coord.x + x, .y = coord.y + y, .z = coord.z };
} }
fn bufferViewRange(buffer_view: *const SoftBufferView) SpvRuntimeError!usize { fn bufferViewRange(buffer_view: *const SoftBufferView) SpvRuntimeError!usize {
const offset: usize = @intCast(buffer_view.interface.offset); 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; return SpvRuntimeError.Unknown;
if (buffer_view.interface.range == vk.WHOLE_SIZE) 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 { 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); var pixel = zm.f32x4s(0.0);
if (dim == .Buffer) { if (dim == .Buffer) {
const buffer_view = try bufferViewFromContext(context); 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 { } else {
const image_view: *SoftImageView = @ptrCast(@alignCast(context)); const image_view: *SoftImageView = @ptrCast(@alignCast(context));
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image)); 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, .array_layer = array_layer,
}; };
const sample_index: u32 = if (image.interface.samples.toInt() > 1) @intCast(z) else 0; const sample_index: u32 = if (image.interface.samples.toInt() > 1) @intCast(z) else 0;
pixel = SoftSampler.swizzleFloat4(readImageFloat4Sample( const format = base.format.fromAspect(image_view.interface.format, aspect_mask);
image, const raw_pixel = if (dim == .SubpassData) blk: {
image_coord, if (findInputAttachmentSnapshot(image_view, subresource)) |snapshot| {
subresource, break :blk blitter.readFloat4(try sampledSnapshotTexel(snapshot, image_coord, format, sample_index), format);
base.format.fromAspect(image_view.interface.format, aspect_mask), }
sample_index, break :blk readImageFloat4Sample(image, image_coord, subresource, format, sample_index) catch return SpvRuntimeError.Unknown;
) catch return SpvRuntimeError.Unknown, image_view.interface.components); } else readImageFloat4Sample(image, image_coord, subresource, format, sample_index) catch return SpvRuntimeError.Unknown;
pixel = SoftSampler.swizzleFloat4(raw_pixel, image_view.interface.components);
} }
return .{ return .{
.x = pixel[0], .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 }; var pixel = @Vector(4, u32){ 0, 0, 0, 0 };
if (dim == .Buffer) { if (dim == .Buffer) {
const buffer_view = try bufferViewFromContext(context); 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 { } else {
const image_view: *SoftImageView = @ptrCast(@alignCast(context)); const image_view: *SoftImageView = @ptrCast(@alignCast(context));
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image)); 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, .array_layer = array_layer,
}; };
const sample_index: u32 = if (image.interface.samples.toInt() > 1) @intCast(z) else 0; const sample_index: u32 = if (image.interface.samples.toInt() > 1) @intCast(z) else 0;
pixel = SoftSampler.swizzleInt4(readImageInt4Sample( const format = base.format.fromAspect(image_view.interface.format, aspect_mask);
image, const raw_pixel = if (dim == .SubpassData) blk: {
image_coord, if (findInputAttachmentSnapshot(image_view, subresource)) |snapshot| {
subresource, break :blk blitter.readInt4(try sampledSnapshotTexel(snapshot, image_coord, format, sample_index), format);
base.format.fromAspect(image_view.interface.format, aspect_mask), }
sample_index, break :blk readImageInt4Sample(image, image_coord, subresource, format, sample_index) catch return SpvRuntimeError.Unknown;
) catch return SpvRuntimeError.Unknown, image_view.interface.components); } else readImageInt4Sample(image, image_coord, subresource, format, sample_index) catch return SpvRuntimeError.Unknown;
pixel = SoftSampler.swizzleInt4(raw_pixel, image_view.interface.components);
} }
return .{ return .{
.x = pixel[0], .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); const vec_pixel = zm.f32x4(pixel.x, pixel.y, pixel.z, pixel.w);
if (dim == .Buffer) { if (dim == .Buffer) {
const buffer_view = try bufferViewFromContext(context); 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 { } else {
const image_view: *SoftImageView = @ptrCast(@alignCast(context)); const image_view: *SoftImageView = @ptrCast(@alignCast(context));
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image)); 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 }; const vec_pixel = @Vector(4, u32){ pixel.x, pixel.y, pixel.z, pixel.w };
if (dim == .Buffer) { if (dim == .Buffer) {
const buffer_view = try bufferViewFromContext(context); 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 { } else {
const image_view: *SoftImageView = @ptrCast(@alignCast(context)); const image_view: *SoftImageView = @ptrCast(@alignCast(context));
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image)); 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) if (dim == .Buffer)
return SpvRuntimeError.UnsupportedSpirV; return SpvRuntimeError.UnsupportedSpirV;
const image_view: *SoftImageView = @ptrCast(@alignCast(context)); const image_view: *SoftImageView = @ptrCast(@alignCast(context));
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image)); const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image));
const sampler: *SoftSampler = @ptrCast(@alignCast(context2)); 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) { fn queryImageSize(context: *anyopaque, dim: spv.SpvDim, arrayed: bool, lod: ?i32) SpvRuntimeError!spv.Runtime.Vec4(u32) {
+100 -3
View File
@@ -422,6 +422,17 @@ fn readSampledFloat4At(context: *const ImageSamplingContext, ix: i32, iy: i32, i
return swizzleFloat4(color, context.image_view.interface.components); 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( fn readSampledInt4(
image: *SoftImage, image: *SoftImage,
image_view: *SoftImageView, 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); 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 { 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 mip_level = sampleMipLevel(image_view, sampler, lod);
const extent = image.getMipLevelExtent(mip_level); 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); 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 { 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); const color = try sampleImageFloat4(image, image_view, sampler, dim, x, y, z, lod, offset);
if (sampler.interface.compare_enable == .false)
return color[0]; 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( pub fn sampleFloat4(
+13
View File
@@ -76,6 +76,7 @@ pub const DrawCall = struct {
color_attachments: []*base.ImageView, color_attachments: []*base.ImageView,
depth_attachment: ?*base.ImageView, depth_attachment: ?*base.ImageView,
input_attachment_snapshots: []const SoftPipeline.InputAttachmentSnapshot,
render_pass: *SoftRenderPass, render_pass: *SoftRenderPass,
framebuffer: *SoftFramebuffer, framebuffer: *SoftFramebuffer,
@@ -99,6 +100,7 @@ pub const DrawCall = struct {
.scissor = undefined, .scissor = undefined,
.color_attachments = framebuffer.interface.attachments[0..], .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, .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, .render_pass = render_pass,
.framebuffer = framebuffer, .framebuffer = framebuffer,
.rasterizer_wait_group = .init, .rasterizer_wait_group = .init,
@@ -139,6 +141,7 @@ render_pass: ?*SoftRenderPass,
framebuffer: ?*SoftFramebuffer, framebuffer: ?*SoftFramebuffer,
render_area: ?vk.Rect2D, render_area: ?vk.Rect2D,
dynamic_state: DynamicState, dynamic_state: DynamicState,
input_attachment_snapshots: []const SoftPipeline.InputAttachmentSnapshot,
subpass_index: usize, subpass_index: usize,
active_occlusion_queries: *std.ArrayList(ExecutionDevice.ActiveOcclusionQuery), active_occlusion_queries: *std.ArrayList(ExecutionDevice.ActiveOcclusionQuery),
@@ -150,6 +153,7 @@ pub fn init(device: *SoftDevice, state: *PipelineState, active_occlusion_queries
.render_pass = null, .render_pass = null,
.framebuffer = null, .framebuffer = null,
.render_area = null, .render_area = null,
.input_attachment_snapshots = &.{},
.dynamic_state = .{ .dynamic_state = .{
.viewports = null, .viewports = null,
.scissor = 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 { 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"); 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); try self.drawCall(&bounded_allocator, vertex_count, instance_count, first_vertex, first_instance, null, null);
+9 -2
View File
@@ -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])); const sample_stride = slice_bytes * @as(usize, @intFromFloat(dim[2]));
color = zm.f32x4s(0.0); color = zm.f32x4s(0.0);
for (0..state.src_samples) |sample_index| { 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)); color /= zm.f32x4s(@floatFromInt(state.src_samples));
} else { } 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 dst_row_pitch_bytes = dst.getRowPitchMemSizeForMipLevelWithFormat(region.dst_subresource.aspect_mask, region.dst_subresource.mip_level, dst_format);
const apply_filter = (filter != .nearest); 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{ var src_subresource = vk.ImageSubresource{
.aspect_mask = region.src_subresource.aspect_mask, .aspect_mask = region.src_subresource.aspect_mask,
+3 -2
View File
@@ -120,8 +120,9 @@ pub fn viewportTransformVertex(viewport: vk.Viewport, vertex: *Vertex) void {
const o_y = viewport.y + viewport.height / 2.0; const o_y = viewport.y + viewport.height / 2.0;
const o_z = viewport.min_depth; const o_z = viewport.min_depth;
const x_screen = ((p_x / 2.0) * x_ndc) + o_x; const subpixel_scale = 16.0;
const y_screen = ((p_y / 2.0) * y_ndc) + o_y; 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; const z_screen = (p_z * z_ndc) + o_z;
vertex.position = zm.f32x4(x_screen, y_screen, z_screen, w); vertex.position = zm.f32x4(x_screen, y_screen, z_screen, w);
+3
View File
@@ -85,12 +85,15 @@ pub fn shaderInvocation(
const SoftPipeline = @import("../SoftPipeline.zig"); const SoftPipeline = @import("../SoftPipeline.zig");
const previous_fragment_coord = SoftPipeline.current_fragment_coord; const previous_fragment_coord = SoftPipeline.current_fragment_coord;
const previous_input_attachment_snapshots = SoftPipeline.current_input_attachment_snapshots;
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 = 0, .z = 0,
}; };
SoftPipeline.current_input_attachment_snapshots = draw_call.input_attachment_snapshots;
defer SoftPipeline.current_fragment_coord = previous_fragment_coord; 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); const entry = try rt.getEntryPointByName(shader.entry);
+74
View File
@@ -15,6 +15,7 @@ const Renderer = @import("Renderer.zig");
const Vertex = Renderer.Vertex; const Vertex = Renderer.Vertex;
const DrawCall = Renderer.DrawCall; const DrawCall = Renderer.DrawCall;
const SoftImage = @import("../SoftImage.zig"); const SoftImage = @import("../SoftImage.zig");
const SoftPipeline = @import("../SoftPipeline.zig");
const VkError = base.VkError; const VkError = base.VkError;
const SpvRuntimeError = spv.Runtime.RuntimeError; 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); 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 { pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocator, draw_call: *DrawCall) VkError!void {
const io = draw_call.renderer.device.interface.io(); const io = draw_call.renderer.device.interface.io();
const pipeline_data = (renderer.state.pipeline orelse return VkError.InvalidHandleDrv).interface.mode.graphics; const pipeline_data = (renderer.state.pipeline orelse return VkError.InvalidHandleDrv).interface.mode.graphics;
const topology = pipeline_data.input_assembly.topology; 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_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; const color_attachment_access = allocator.alloc(?common.RenderTargetAccess, color_attachments.len) catch return VkError.OutOfDeviceMemory;
+9 -2
View File
@@ -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 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);
} }
} }