adding buffer-image copies
All checks were successful
Build / build (push) Successful in 2m11s
Test / build_and_test (push) Successful in 48m53s

This commit is contained in:
2026-04-12 22:34:58 +02:00
parent 3717fb3790
commit d54c28dcaa
11 changed files with 351 additions and 110 deletions

View File

@@ -24,6 +24,7 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v
interface.vtable = &.{
.destroy = destroy,
.getMemoryRequirements = getMemoryRequirements,
.getTotalSizeForAspect = getTotalSizeForAspect,
};
self.* = .{
@@ -37,17 +38,16 @@ pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void {
allocator.destroy(self);
}
pub fn getMemoryRequirements(interface: *Interface, requirements: *vk.MemoryRequirements) void {
_ = interface;
pub fn getMemoryRequirements(_: *Interface, requirements: *vk.MemoryRequirements) VkError!void {
requirements.alignment = lib.MEMORY_REQUIREMENTS_IMAGE_ALIGNMENT;
}
inline fn clear(self: *Self, pixel: vk.ClearValue, format: vk.Format, view_format: vk.Format, range: vk.ImageSubresourceRange, area: ?vk.Rect2D) void {
inline fn clear(self: *Self, pixel: vk.ClearValue, format: vk.Format, view_format: vk.Format, range: vk.ImageSubresourceRange, area: ?vk.Rect2D) VkError!void {
const soft_device: *SoftDevice = @alignCast(@fieldParentPtr("interface", self.interface.owner));
soft_device.blitter.clear(pixel, format, self, view_format, range, area);
try soft_device.blitter.clear(pixel, format, self, view_format, range, area);
}
pub fn clearRange(self: *Self, color: vk.ClearColorValue, range: vk.ImageSubresourceRange) void {
pub fn clearRange(self: *Self, color: vk.ClearColorValue, range: vk.ImageSubresourceRange) VkError!void {
std.debug.assert(range.aspect_mask == vk.ImageAspectFlags{ .color_bit = true });
const clear_format: vk.Format = if (base.vku.vkuFormatIsSINT(@intCast(@intFromEnum(self.interface.format))))
@@ -56,7 +56,7 @@ pub fn clearRange(self: *Self, color: vk.ClearColorValue, range: vk.ImageSubreso
.r32g32b32a32_uint
else
.r32g32b32a32_sfloat;
self.clear(.{ .color = color }, clear_format, self.interface.format, range, null);
try self.clear(.{ .color = color }, clear_format, self.interface.format, range, null);
}
pub fn copyImage(self: *const Self, self_layout: vk.ImageLayout, dst: *Self, dst_layout: vk.ImageLayout, regions: []const vk.ImageCopy) VkError!void {
@@ -70,13 +70,12 @@ pub fn copyImage(self: *const Self, self_layout: vk.ImageLayout, dst: *Self, dst
pub fn copyToBuffer(self: *const Self, dst: *SoftBuffer, region: vk.BufferImageCopy) VkError!void {
const dst_size = dst.interface.size - region.buffer_offset;
const dst_offset = dst.interface.offset + region.buffer_offset;
const dst_memory = if (dst.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv;
const dst_map: []u8 = @as([*]u8, @ptrCast(try dst_memory.map(region.buffer_offset, dst_size)))[0..dst_size];
const dst_map: []u8 = @as([*]u8, @ptrCast(try dst_memory.map(dst_offset, dst_size)))[0..dst_size];
try self.copy(
null,
dst_map,
@intCast(region.buffer_row_length),
@intCast(region.buffer_image_height),
region.image_subresource,
region.image_offset,
region.image_extent,
@@ -85,35 +84,200 @@ pub fn copyToBuffer(self: *const Self, dst: *SoftBuffer, region: vk.BufferImageC
pub fn copyFromBuffer(self: *const Self, src: *const SoftBuffer, region: vk.BufferImageCopy) VkError!void {
const src_size = src.interface.size - region.buffer_offset;
const src_offset = src.interface.offset + region.buffer_offset;
const src_memory = if (src.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv;
const src_map: []u8 = @as([*]u8, @ptrCast(try src_memory.map(region.buffer_offset, src_size)))[0..src_size];
const src_map: []u8 = @as([*]u8, @ptrCast(try src_memory.map(src_offset, src_size)))[0..src_size];
try self.copy(
src_map,
null,
@intCast(region.buffer_row_length),
@intCast(region.buffer_image_height),
region.image_subresource,
region.image_offset,
region.image_extent,
);
}
/// Based on SwiftShader vk::Image::copy
pub fn copy(
self: *const Self,
src_memory: ?[]const u8,
dst_memory: ?[]u8,
row_len: usize,
image_height: usize,
base_src_memory: ?[]const u8,
base_dst_memory: ?[]u8,
image_subresource: vk.ImageSubresourceLayers,
image_copy_offset: vk.Offset3D,
image_copy_extent: vk.Extent3D,
image_offset: vk.Offset3D,
image_extent: vk.Extent3D,
) VkError!void {
_ = self;
_ = src_memory;
_ = dst_memory;
_ = row_len;
_ = image_height;
_ = image_subresource;
_ = image_copy_offset;
_ = image_copy_extent;
std.debug.assert((base_src_memory == null) != (base_dst_memory == null));
const is_source: bool = base_src_memory != null;
if (image_subresource.aspect_mask.subtract(.{
.color_bit = true,
.depth_bit = true,
.stencil_bit = true,
}).toInt() != 0) {
base.unsupported("aspectMask {f}", .{image_subresource.aspect_mask});
return VkError.ValidationFailed;
}
const format = self.interface.formatFromAspect(image_subresource.aspect_mask);
// TODO: handle extent of compressed formats
if (image_extent.width == 0 or image_extent.height == 0 or image_extent.depth == 0) {
return;
}
const bytes_per_block = base.format.texelSize(format);
const memory_row_pitch_bytes = image_extent.width * bytes_per_block;
const memory_slice_pitch_bytes = image_extent.height * memory_row_pitch_bytes;
const image_texel_offset = try self.getTexelMemoryOffset(image_offset, .{
.aspect_mask = image_subresource.aspect_mask,
.mip_level = image_subresource.mip_level,
.array_layer = image_subresource.base_array_layer,
});
const image_size = self.getLayerSize(image_subresource.aspect_mask) - self.getTexelMemoryOffsetInSubresource(image_offset, .{
.aspect_mask = image_subresource.aspect_mask,
.mip_level = image_subresource.mip_level,
.array_layer = image_subresource.base_array_layer,
});
const image_memory = if (self.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv;
const image_map: []u8 = @as([*]u8, @ptrCast(try image_memory.map(self.interface.memory_offset + image_texel_offset, image_size)))[0..image_size];
var src_memory = if (is_source) base_src_memory orelse return VkError.InvalidDeviceMemoryDrv else image_map;
var dst_memory = if (is_source) image_map else base_dst_memory orelse return VkError.InvalidDeviceMemoryDrv;
const src_slice_pitch_bytes = if (is_source) memory_slice_pitch_bytes else self.getSliceMemSizeForMipLevel(image_subresource.aspect_mask, image_subresource.mip_level);
const dst_slice_pitch_bytes = if (is_source) self.getSliceMemSizeForMipLevel(image_subresource.aspect_mask, image_subresource.mip_level) else memory_slice_pitch_bytes;
const src_row_pitch_bytes = if (is_source) memory_row_pitch_bytes else self.getRowPitchMemSizeForMipLevel(image_subresource.aspect_mask, image_subresource.mip_level);
const dst_row_pitch_bytes = if (is_source) self.getRowPitchMemSizeForMipLevel(image_subresource.aspect_mask, image_subresource.mip_level) else memory_row_pitch_bytes;
const src_layer_size = if (is_source) memory_slice_pitch_bytes else self.getLayerSize(image_subresource.aspect_mask);
const dst_layer_size = if (is_source) self.getLayerSize(image_subresource.aspect_mask) else memory_slice_pitch_bytes;
const layer_count = if (image_subresource.layer_count == vk.REMAINING_ARRAY_LAYERS) self.interface.array_layers - image_subresource.base_array_layer else image_subresource.layer_count;
const copy_size = image_extent.width * bytes_per_block;
for (0..layer_count) |_| {
var src_layer_memory = src_memory[0..];
var dst_layer_memory = dst_memory[0..];
for (0..image_extent.depth) |_| {
var src_slice_memory = src_layer_memory[0..];
var dst_slice_memory = dst_layer_memory[0..];
for (0..image_extent.height) |_| {
@memcpy(dst_slice_memory[0..copy_size], src_slice_memory[0..copy_size]);
src_slice_memory = src_slice_memory[src_row_pitch_bytes..];
dst_slice_memory = dst_slice_memory[dst_row_pitch_bytes..];
}
src_layer_memory = src_layer_memory[src_slice_pitch_bytes..];
dst_layer_memory = dst_layer_memory[dst_slice_pitch_bytes..];
}
src_memory = src_memory[src_layer_size..];
dst_memory = dst_memory[dst_layer_size..];
}
}
fn getTexelMemoryOffsetInSubresource(self: *const Self, offset: vk.Offset3D, subresource: vk.ImageSubresource) usize {
return @as(usize, @intCast(offset.z)) * self.getSliceMemSizeForMipLevel(subresource.aspect_mask, subresource.mip_level) +
@as(usize, @intCast(offset.y)) * self.getRowPitchMemSizeForMipLevel(subresource.aspect_mask, subresource.mip_level) +
@as(usize, @intCast(offset.x)) * base.format.texelSize(base.format.fromAspect(self.interface.format, subresource.aspect_mask));
}
fn getTexelMemoryOffset(self: *const Self, offset: vk.Offset3D, subresource: vk.ImageSubresource) VkError!usize {
return self.getTexelMemoryOffsetInSubresource(offset, subresource) + try self.getSubresourceOffset(subresource.aspect_mask, subresource.mip_level, subresource.array_layer);
}
fn getSubresourceOffset(self: *const Self, aspect_mask: vk.ImageAspectFlags, mip_level: u32, layer: u32) VkError!usize {
var offset = try self.getAspectOffset(aspect_mask);
for (0..mip_level) |mip| {
offset += self.getMultiSampledLevelSize(aspect_mask, @intCast(mip));
}
const is_3D = (self.interface.image_type == .@"3d") and self.interface.flags.@"2d_array_compatible_bit";
const layer_offset = if (is_3D)
self.getSliceMemSizeForMipLevel(aspect_mask, mip_level)
else
self.getLayerSize(aspect_mask);
return offset + layer * layer_offset;
}
fn getAspectOffset(self: *const Self, aspect_mask: vk.ImageAspectFlags) VkError!usize {
return switch (self.interface.format) {
.d16_unorm_s8_uint,
.d24_unorm_s8_uint,
.d32_sfloat_s8_uint,
=> if (aspect_mask.stencil_bit)
try self.interface.getTotalSizeForAspect(.{ .depth_bit = true })
else
0,
else => 0,
};
}
fn getTotalSizeForAspect(interface: *const Interface, aspect_mask: vk.ImageAspectFlags) VkError!usize {
const self: *const Self = @alignCast(@fieldParentPtr("interface", interface));
if (aspect_mask.subtract(.{
.color_bit = true,
.depth_bit = true,
.stencil_bit = true,
}).toInt() != 0) {
base.unsupported("aspectMask {f}", .{aspect_mask});
return VkError.ValidationFailed;
}
var size: usize = 0;
if (aspect_mask.color_bit)
size += self.getLayerSize(.{ .color_bit = true });
if (aspect_mask.depth_bit)
size += self.getLayerSize(.{ .depth_bit = true });
if (aspect_mask.stencil_bit)
size += self.getLayerSize(.{ .stencil_bit = true });
return size * self.interface.array_layers;
}
fn getLayerSize(self: *const Self, aspect_mask: vk.ImageAspectFlags) usize {
var size: usize = 0;
for (0..self.interface.mip_levels) |mip_level| {
size += self.getMultiSampledLevelSize(aspect_mask, @intCast(mip_level));
}
return size;
}
inline fn getMultiSampledLevelSize(self: *const Self, aspect_mask: vk.ImageAspectFlags, mip_level: u32) usize {
return self.getMipLevelSize(aspect_mask, mip_level) * self.interface.samples.toInt();
}
inline fn getMipLevelSize(self: *const Self, aspect_mask: vk.ImageAspectFlags, mip_level: u32) usize {
return self.getSliceMemSizeForMipLevel(aspect_mask, mip_level) * self.getMipLevelExtent(mip_level).depth;
}
fn getMipLevelExtent(self: *const Self, mip_level: u32) vk.Extent3D {
var extent: vk.Extent3D = .{
.width = self.interface.extent.width >> @intCast(mip_level),
.height = self.interface.extent.height >> @intCast(mip_level),
.depth = self.interface.extent.depth >> @intCast(mip_level),
};
if (extent.width == 0) extent.width = 1;
if (extent.height == 0) extent.height = 1;
if (extent.depth == 0) extent.depth = 1;
return extent;
}
fn getSliceMemSizeForMipLevel(self: *const Self, aspect_mask: vk.ImageAspectFlags, mip_level: u32) usize {
const mip_extent = self.getMipLevelExtent(mip_level);
const format = self.interface.formatFromAspect(aspect_mask);
return base.format.sliceMemSize(format, mip_extent.width, mip_extent.height);
}
fn getRowPitchMemSizeForMipLevel(self: *const Self, aspect_mask: vk.ImageAspectFlags, mip_level: u32) usize {
const mip_extent = self.getMipLevelExtent(mip_level);
const format = self.interface.formatFromAspect(aspect_mask);
return base.format.pitchMemSize(format, mip_extent.width);
}