Files
VulkanDriver/src/soft/SoftImage.zig
Kbz-8 ee0ffbe09d
All checks were successful
Test / build_and_test (push) Successful in 55m18s
Build / build (push) Successful in 1m54s
adding base blit
2026-04-14 02:46:25 +02:00

408 lines
19 KiB
Zig

const std = @import("std");
const vk = @import("vulkan");
const base = @import("base");
const lib = @import("lib.zig");
const VkError = base.VkError;
const Device = base.Device;
const SoftBuffer = @import("SoftBuffer.zig");
const SoftDevice = @import("SoftDevice.zig");
const Self = @This();
pub const Interface = base.Image;
interface: Interface,
pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const vk.ImageCreateInfo) VkError!*Self {
const self = allocator.create(Self) catch return VkError.OutOfHostMemory;
errdefer allocator.destroy(self);
var interface = try Interface.init(device, allocator, info);
interface.vtable = &.{
.destroy = destroy,
.getMemoryRequirements = getMemoryRequirements,
.getTotalSizeForAspect = getTotalSizeForAspect,
};
self.* = .{
.interface = interface,
};
return self;
}
pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
allocator.destroy(self);
}
pub fn getMemoryRequirements(_: *Interface, requirements: *vk.MemoryRequirements) VkError!void {
requirements.alignment = lib.MEMORY_REQUIREMENTS_IMAGE_ALIGNMENT;
}
pub fn getClearFormat(self: *Self) VkError!vk.Format {
return if (base.vku.vkuFormatIsSINT(@intCast(@intFromEnum(self.interface.format))))
.r32g32b32a32_sint
else if (base.vku.vkuFormatIsUINT(@intCast(@intFromEnum(self.interface.format))))
.r32g32b32a32_uint
else
.r32g32b32a32_sfloat;
}
/// Based on SwiftShader vk::Image::copyTo
pub fn copyToImage(self: *const Self, dst: *Self, region: vk.ImageCopy) VkError!void {
const combined_depth_stencil_aspect: vk.ImageAspectFlags = .{
.depth_bit = true,
.stencil_bit = true,
};
if (region.src_subresource.aspect_mask == combined_depth_stencil_aspect and
region.dst_subresource.aspect_mask == combined_depth_stencil_aspect)
{
var single_aspect_region = region;
single_aspect_region.src_subresource.aspect_mask = .{ .depth_bit = true };
single_aspect_region.dst_subresource.aspect_mask = .{ .depth_bit = true };
try self.copyToImageSingleAspect(dst, single_aspect_region);
single_aspect_region.src_subresource.aspect_mask = .{ .stencil_bit = true };
single_aspect_region.dst_subresource.aspect_mask = .{ .stencil_bit = true };
try self.copyToImageSingleAspect(dst, single_aspect_region);
} else {
try self.copyToImageSingleAspect(dst, region);
}
}
/// Based on SwiftShader vk::Image::copySingleAspectTo
pub fn copyToImageSingleAspect(self: *const Self, dst: *Self, region: vk.ImageCopy) VkError!void {
if (!(region.src_subresource.aspect_mask == vk.ImageAspectFlags{ .color_bit = true } or
region.src_subresource.aspect_mask == vk.ImageAspectFlags{ .depth_bit = true } or
region.src_subresource.aspect_mask == vk.ImageAspectFlags{ .stencil_bit = true }))
{
base.unsupported("src subresource aspectMask {f}", .{region.src_subresource.aspect_mask});
return VkError.ValidationFailed;
}
if (!(region.dst_subresource.aspect_mask == vk.ImageAspectFlags{ .color_bit = true } or
region.dst_subresource.aspect_mask == vk.ImageAspectFlags{ .depth_bit = true } or
region.dst_subresource.aspect_mask == vk.ImageAspectFlags{ .stencil_bit = true }))
{
base.unsupported("dst subresource aspectMask {f}", .{region.dst_subresource.aspect_mask});
return VkError.ValidationFailed;
}
const src_format = self.interface.formatFromAspect(region.src_subresource.aspect_mask);
const bytes_per_block = base.format.texelSize(src_format);
const src_extent = self.getMipLevelExtent(region.src_subresource.mip_level);
const dst_extent = dst.getMipLevelExtent(region.src_subresource.mip_level);
const one_is_3D = (self.interface.image_type == .@"3d") != (dst.interface.image_type == .@"3d");
const both_are_3D = (self.interface.image_type == .@"3d") and (dst.interface.image_type == .@"3d");
const src_row_pitch_bytes = self.getRowPitchMemSizeForMipLevel(region.src_subresource.aspect_mask, region.src_subresource.mip_level);
const src_depth_pitch_bytes = self.getSliceMemSizeForMipLevel(region.src_subresource.aspect_mask, region.src_subresource.mip_level);
const dst_row_pitch_bytes = dst.getRowPitchMemSizeForMipLevel(region.dst_subresource.aspect_mask, region.dst_subresource.mip_level);
const dst_depth_pitch_bytes = dst.getSliceMemSizeForMipLevel(region.dst_subresource.aspect_mask, region.dst_subresource.mip_level);
const src_array_pitch = self.getLayerSize(region.src_subresource.aspect_mask);
const dst_array_pitch = dst.getLayerSize(region.dst_subresource.aspect_mask);
const src_layer_pitch = if (self.interface.image_type == .@"3d") src_depth_pitch_bytes else src_array_pitch;
const dst_layer_pitch = if (dst.interface.image_type == .@"3d") dst_depth_pitch_bytes else dst_array_pitch;
// If one image is 3D, extent.depth must match the layer count. If both images are 2D,
// depth is 1 but the source and destination subresource layer count must match.
const layer_count = if (one_is_3D) region.extent.depth else region.src_subresource.layer_count;
// Copies between 2D and 3D images are treated as layers, so only use depth as the slice count when both images are 3D.
const slice_count = if (both_are_3D) region.extent.depth else self.interface.samples.toInt();
const is_single_slice = (slice_count == 1);
const is_single_row = (region.extent.height == 1) and is_single_slice;
// In order to copy multiple rows using a single memcpy call, we
// have to make sure that we need to copy the entire row and that
// both source and destination rows have the same size in bytes
const is_entire_row = (region.extent.width == src_extent.width) and (region.extent.width == dst_extent.width);
// In order to copy multiple slices using a single memcpy call, we
// have to make sure that we need to copy the entire slice and that
// both source and destination slices have the same size in bytes
const is_entire_slice = is_entire_row and
(region.extent.height == src_extent.height) and
(region.extent.height == dst_extent.height) and
(src_depth_pitch_bytes == dst_depth_pitch_bytes);
const src_texel_offset = try self.getTexelMemoryOffset(region.src_offset, .{
.aspect_mask = region.src_subresource.aspect_mask,
.mip_level = region.src_subresource.mip_level,
.array_layer = region.src_subresource.base_array_layer,
});
const src_size = try self.interface.getTotalSizeForAspect(region.src_subresource.aspect_mask) - src_texel_offset;
const src_memory = if (self.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv;
var src_map: []u8 = @as([*]u8, @ptrCast(try src_memory.map(self.interface.memory_offset + src_texel_offset, src_size)))[0..src_size];
const dst_texel_offset = try self.getTexelMemoryOffset(region.dst_offset, .{
.aspect_mask = region.dst_subresource.aspect_mask,
.mip_level = region.dst_subresource.mip_level,
.array_layer = region.dst_subresource.base_array_layer,
});
const dst_size = try dst.interface.getTotalSizeForAspect(region.dst_subresource.aspect_mask) - dst_texel_offset;
const dst_memory = if (dst.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv;
var dst_map: []u8 = @as([*]u8, @ptrCast(try dst_memory.map(self.interface.memory_offset + dst_texel_offset, dst_size)))[0..dst_size];
for (0..layer_count) |_| {
if (is_single_row) {
const copy_size = region.extent.width * bytes_per_block;
@memcpy(dst_map[0..copy_size], src_map[0..copy_size]);
} else if (is_entire_row and is_single_slice) {
const copy_size = region.extent.height * src_row_pitch_bytes;
@memcpy(dst_map[0..copy_size], src_map[0..copy_size]);
} else if (is_entire_slice) {
const copy_size = slice_count * src_depth_pitch_bytes;
@memcpy(dst_map[0..copy_size], src_map[0..copy_size]);
} else if (is_entire_row) {
const slice_size = region.extent.height * src_row_pitch_bytes;
var src_slice_memory = src_map[0..];
var dst_slice_memory = dst_map[0..];
for (0..slice_count) |_| {
@memcpy(dst_slice_memory[0..slice_size], src_slice_memory[0..slice_size]);
src_slice_memory = src_slice_memory[src_depth_pitch_bytes..];
dst_slice_memory = dst_slice_memory[dst_depth_pitch_bytes..];
}
} else {
const row_size = region.extent.width * bytes_per_block;
var src_slice_memory = src_map[0..];
var dst_slice_memory = dst_map[0..];
for (0..slice_count) |_| {
var src_row_memory = src_slice_memory[0..];
var dst_row_memory = dst_slice_memory[0..];
for (0..region.extent.height) |_| {
@memcpy(dst_row_memory[0..row_size], src_row_memory[0..row_size]);
src_row_memory = src_row_memory[src_row_pitch_bytes..];
dst_row_memory = dst_row_memory[dst_row_pitch_bytes..];
}
}
}
src_map = src_map[src_layer_pitch..];
dst_map = dst_map[dst_layer_pitch..];
}
}
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(dst_offset, dst_size)))[0..dst_size];
try self.copy(
null,
dst_map,
region.image_subresource,
region.image_offset,
region.image_extent,
);
}
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(src_offset, src_size)))[0..src_size];
try self.copy(
src_map,
null,
region.image_subresource,
region.image_offset,
region.image_extent,
);
}
/// Based on SwiftShader vk::Image::copy
pub fn copy(
self: *const Self,
base_src_memory: ?[]const u8,
base_dst_memory: ?[]u8,
image_subresource: vk.ImageSubresourceLayers,
image_offset: vk.Offset3D,
image_extent: vk.Extent3D,
) VkError!void {
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 = try self.interface.getTotalSizeForAspect(image_subresource.aspect_mask) - image_texel_offset;
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..];
}
}
pub 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));
}
pub 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;
}
pub 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;
}
pub 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();
}
pub 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;
}
pub 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;
}
pub 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);
}
pub 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);
}