adding BC compressed format support
This commit is contained in:
+2
-2
@@ -32,8 +32,8 @@
|
||||
|
||||
// Soft dependencies
|
||||
.SPIRV_Interpreter = .{
|
||||
.url = "git+https://github.com/Kbz-8/SPIRV-Interpreter#50a2de5bb756f96f0e51277fb98825a6fe829433",
|
||||
.hash = "SPIRV_Interpreter-0.0.1-ajmpnwGMCQBT5uWJ847m9Mpa-0NrGaugEN0FewoXgC_2",
|
||||
.url = "git+https://github.com/Kbz-8/SPIRV-Interpreter#6e1fa9db32f3e596e30d60f94b2909f3609ec03a",
|
||||
.hash = "SPIRV_Interpreter-0.0.1-ajmpn_CXCQB0GIcZz8ioOFOW9yLr5Ka-Qir3v3Y1r5Bi",
|
||||
.lazy = true,
|
||||
},
|
||||
//.SPIRV_Interpreter = .{
|
||||
|
||||
+45
-13
@@ -55,6 +55,7 @@ const vk = @import("vulkan");
|
||||
const base = @import("base");
|
||||
const lib = @import("lib.zig");
|
||||
const blitter = @import("device/blitter.zig");
|
||||
const compressed = @import("device/compressed.zig");
|
||||
|
||||
const F32x4 = blitter.F32x4;
|
||||
const U32x4 = blitter.U32x4;
|
||||
@@ -156,9 +157,13 @@ pub fn copyToImageSingleAspect(self: *const Self, dst: *Self, region: vk.ImageCo
|
||||
|
||||
const src_format = self.interface.formatFromAspect(region.src_subresource.aspect_mask);
|
||||
const bytes_per_block = base.format.texelSize(src_format);
|
||||
const block_width = base.format.blockWidth(src_format);
|
||||
const block_height = base.format.blockHeight(src_format);
|
||||
|
||||
const src_extent = self.getMipLevelExtent(region.src_subresource.mip_level);
|
||||
const dst_extent = dst.getMipLevelExtent(region.dst_subresource.mip_level);
|
||||
const copy_block_width = base.format.blockCountX(src_format, region.extent.width);
|
||||
const copy_block_height = base.format.blockCountY(src_format, region.extent.height);
|
||||
|
||||
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");
|
||||
@@ -178,12 +183,16 @@ pub fn copyToImageSingleAspect(self: *const Self, dst: *Self, region: vk.ImageCo
|
||||
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;
|
||||
const is_entire_row = (region.extent.width == src_extent.width) and (region.extent.width == dst_extent.width);
|
||||
const is_single_row = (copy_block_height == 1) and is_single_slice;
|
||||
const is_entire_row = (region.extent.width == src_extent.width) and (region.extent.width == dst_extent.width) and
|
||||
(@mod(@as(usize, @intCast(region.src_offset.x)), block_width) == 0) and
|
||||
(@mod(@as(usize, @intCast(region.dst_offset.x)), block_width) == 0);
|
||||
|
||||
const is_entire_slice = is_entire_row and
|
||||
(region.extent.height == src_extent.height) and
|
||||
(region.extent.height == dst_extent.height) and
|
||||
(@mod(@as(usize, @intCast(region.src_offset.y)), block_height) == 0) and
|
||||
(@mod(@as(usize, @intCast(region.dst_offset.y)), block_height) == 0) and
|
||||
(src_depth_pitch_bytes == dst_depth_pitch_bytes);
|
||||
|
||||
const src_texel_offset = try self.getTexelMemoryOffset(region.src_offset, .{
|
||||
@@ -202,12 +211,12 @@ pub fn copyToImageSingleAspect(self: *const Self, dst: *Self, region: vk.ImageCo
|
||||
|
||||
for (0..layer_count) |_| {
|
||||
if (is_single_row) {
|
||||
const copy_size = region.extent.width * bytes_per_block;
|
||||
const copy_size = copy_block_width * bytes_per_block;
|
||||
if (dst_map.len < copy_size or src_map.len < copy_size)
|
||||
break;
|
||||
@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;
|
||||
const copy_size = copy_block_height * src_row_pitch_bytes;
|
||||
if (dst_map.len < copy_size or src_map.len < copy_size)
|
||||
break;
|
||||
@memcpy(dst_map[0..copy_size], src_map[0..copy_size]);
|
||||
@@ -217,7 +226,7 @@ pub fn copyToImageSingleAspect(self: *const Self, dst: *Self, region: vk.ImageCo
|
||||
break;
|
||||
@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;
|
||||
const slice_size = copy_block_height * src_row_pitch_bytes;
|
||||
var src_slice_memory = src_map[0..];
|
||||
var dst_slice_memory = dst_map[0..];
|
||||
|
||||
@@ -229,7 +238,7 @@ pub fn copyToImageSingleAspect(self: *const Self, dst: *Self, region: vk.ImageCo
|
||||
dst_slice_memory = if (dst_slice_memory.len < dst_depth_pitch_bytes) break else dst_slice_memory[dst_depth_pitch_bytes..];
|
||||
}
|
||||
} else {
|
||||
const row_size = region.extent.width * bytes_per_block;
|
||||
const row_size = copy_block_width * bytes_per_block;
|
||||
var src_slice_memory = src_map[0..];
|
||||
var dst_slice_memory = dst_map[0..];
|
||||
|
||||
@@ -237,7 +246,7 @@ pub fn copyToImageSingleAspect(self: *const Self, dst: *Self, region: vk.ImageCo
|
||||
var src_row_memory = src_slice_memory[0..];
|
||||
var dst_row_memory = dst_slice_memory[0..];
|
||||
|
||||
for (0..region.extent.height) |_| {
|
||||
for (0..copy_block_height) |_| {
|
||||
if (dst_row_memory.len < row_size or src_row_memory.len < row_size)
|
||||
break;
|
||||
@memcpy(dst_row_memory[0..row_size], src_row_memory[0..row_size]);
|
||||
@@ -323,8 +332,12 @@ pub fn copy(
|
||||
};
|
||||
|
||||
const bytes_per_block = base.format.texelSize(format);
|
||||
const memory_row_pitch_bytes = extent.width * bytes_per_block;
|
||||
const memory_slice_pitch_bytes = extent.height * memory_row_pitch_bytes;
|
||||
const copy_block_width = base.format.blockCountX(format, image_extent.width);
|
||||
const copy_block_height = base.format.blockCountY(format, image_extent.height);
|
||||
const memory_block_width = base.format.blockCountX(format, extent.width);
|
||||
const memory_block_height = base.format.blockCountY(format, extent.height);
|
||||
const memory_row_pitch_bytes = memory_block_width * bytes_per_block;
|
||||
const memory_slice_pitch_bytes = memory_block_height * memory_row_pitch_bytes;
|
||||
|
||||
const image_texel_offset = try self.getTexelMemoryOffset(image_offset, .{
|
||||
.aspect_mask = image_subresource.aspect_mask,
|
||||
@@ -346,7 +359,7 @@ pub fn copy(
|
||||
|
||||
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;
|
||||
const copy_size = copy_block_width * bytes_per_block;
|
||||
|
||||
for (0..layer_count) |_| {
|
||||
var src_layer_memory = src_memory[0..];
|
||||
@@ -356,7 +369,7 @@ pub fn copy(
|
||||
var src_slice_memory = src_layer_memory[0..];
|
||||
var dst_slice_memory = dst_layer_memory[0..];
|
||||
|
||||
for (0..image_extent.height) |_| {
|
||||
for (0..copy_block_height) |_| {
|
||||
if (dst_slice_memory.len < copy_size or src_slice_memory.len < copy_size)
|
||||
break;
|
||||
@memcpy(dst_slice_memory[0..copy_size], src_slice_memory[0..copy_size]);
|
||||
@@ -375,6 +388,14 @@ pub fn readFloat4(self: *Self, offset: vk.Offset3D, subresource: vk.ImageSubreso
|
||||
const texel_size = base.format.texelSize(format);
|
||||
const texel_offset = try self.getTexelMemoryOffset(offset, subresource);
|
||||
const map = try self.mapAsSliceWithAddedOffset(u8, texel_offset, texel_size);
|
||||
if (base.format.isCompressed(format)) {
|
||||
return compressed.readFloat4(
|
||||
map,
|
||||
format,
|
||||
@mod(@as(usize, @intCast(offset.x)), base.format.blockWidth(format)),
|
||||
@mod(@as(usize, @intCast(offset.y)), base.format.blockHeight(format)),
|
||||
);
|
||||
}
|
||||
return blitter.readFloat4(map, format);
|
||||
}
|
||||
|
||||
@@ -389,6 +410,16 @@ pub fn writeFloat4(self: *Self, offset: vk.Offset3D, subresource: vk.ImageSubres
|
||||
const texel_size = base.format.texelSize(format);
|
||||
const texel_offset = try self.getTexelMemoryOffset(offset, subresource);
|
||||
const map = try self.mapAsSliceWithAddedOffset(u8, texel_offset, texel_size);
|
||||
if (base.format.isCompressed(format)) {
|
||||
compressed.writeFloat4(
|
||||
map,
|
||||
format,
|
||||
@mod(@as(usize, @intCast(offset.x)), base.format.blockWidth(format)),
|
||||
@mod(@as(usize, @intCast(offset.y)), base.format.blockHeight(format)),
|
||||
pixel,
|
||||
);
|
||||
return;
|
||||
}
|
||||
blitter.writeFloat4(pixel, map, format);
|
||||
}
|
||||
|
||||
@@ -400,9 +431,10 @@ pub fn writeInt4(self: *Self, offset: vk.Offset3D, subresource: vk.ImageSubresou
|
||||
}
|
||||
|
||||
pub fn getTexelMemoryOffsetInSubresource(self: *const Self, offset: vk.Offset3D, subresource: vk.ImageSubresource) usize {
|
||||
const format = base.format.fromAspect(self.interface.format, subresource.aspect_mask);
|
||||
return @as(usize, @intCast(offset.z)) * self.interface.getSliceMemSizeForMipLevel(subresource.aspect_mask, subresource.mip_level) +
|
||||
@as(usize, @intCast(offset.y)) * self.interface.getRowPitchMemSizeForMipLevel(subresource.aspect_mask, subresource.mip_level) +
|
||||
@as(usize, @intCast(offset.x)) * base.format.texelSize(base.format.fromAspect(self.interface.format, subresource.aspect_mask));
|
||||
@divFloor(@as(usize, @intCast(offset.y)), base.format.blockHeight(format)) * self.interface.getRowPitchMemSizeForMipLevel(subresource.aspect_mask, subresource.mip_level) +
|
||||
@divFloor(@as(usize, @intCast(offset.x)), base.format.blockWidth(format)) * base.format.texelSize(format);
|
||||
}
|
||||
|
||||
pub fn getTexelMemoryOffset(self: *const Self, offset: vk.Offset3D, subresource: vk.ImageSubresource) VkError!usize {
|
||||
|
||||
@@ -335,22 +335,22 @@ pub fn getFormatProperties(interface: *Interface, format: vk.Format) VkError!vk.
|
||||
.r32g32b32a32_sfloat,
|
||||
.b10g11r11_ufloat_pack32,
|
||||
.e5b9g9r9_ufloat_pack32,
|
||||
//.bc1_rgb_unorm_block,
|
||||
//.bc1_rgb_srgb_block,
|
||||
//.bc1_rgba_unorm_block,
|
||||
//.bc1_rgba_srgb_block,
|
||||
//.bc2_unorm_block,
|
||||
//.bc2_srgb_block,
|
||||
//.bc3_unorm_block,
|
||||
//.bc3_srgb_block,
|
||||
//.bc4_unorm_block,
|
||||
//.bc4_snorm_block,
|
||||
//.bc5_unorm_block,
|
||||
//.bc5_snorm_block,
|
||||
//.bc6h_ufloat_block,
|
||||
//.bc6h_sfloat_block,
|
||||
//.bc7_unorm_block,
|
||||
//.bc7_srgb_block,
|
||||
.bc1_rgb_unorm_block,
|
||||
.bc1_rgb_srgb_block,
|
||||
.bc1_rgba_unorm_block,
|
||||
.bc1_rgba_srgb_block,
|
||||
.bc2_unorm_block,
|
||||
.bc2_srgb_block,
|
||||
.bc3_unorm_block,
|
||||
.bc3_srgb_block,
|
||||
.bc4_unorm_block,
|
||||
.bc4_snorm_block,
|
||||
.bc5_unorm_block,
|
||||
.bc5_snorm_block,
|
||||
.bc6h_ufloat_block,
|
||||
.bc6h_sfloat_block,
|
||||
.bc7_unorm_block,
|
||||
.bc7_srgb_block,
|
||||
//.etc2_r8g8b8_unorm_block,
|
||||
//.etc2_r8g8b8_srgb_block,
|
||||
//.etc2_r8g8b8a1_unorm_block,
|
||||
@@ -446,6 +446,33 @@ pub fn getFormatProperties(interface: *Interface, format: vk.Format) VkError!vk.
|
||||
else => {},
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
.bc1_rgb_unorm_block,
|
||||
.bc1_rgb_srgb_block,
|
||||
.bc1_rgba_unorm_block,
|
||||
.bc1_rgba_srgb_block,
|
||||
.bc2_unorm_block,
|
||||
.bc2_srgb_block,
|
||||
.bc3_unorm_block,
|
||||
.bc3_srgb_block,
|
||||
.bc4_unorm_block,
|
||||
.bc4_snorm_block,
|
||||
.bc5_unorm_block,
|
||||
.bc5_snorm_block,
|
||||
.bc6h_ufloat_block,
|
||||
.bc6h_sfloat_block,
|
||||
.bc7_unorm_block,
|
||||
.bc7_srgb_block,
|
||||
=> {
|
||||
properties.optimal_tiling_features.blit_src_bit = true;
|
||||
properties.optimal_tiling_features.sampled_image_bit = true;
|
||||
properties.optimal_tiling_features.sampled_image_filter_linear_bit = true;
|
||||
properties.optimal_tiling_features.transfer_dst_bit = true;
|
||||
properties.optimal_tiling_features.transfer_src_bit = true;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
// Vulkan 1.0 mandatory storage image formats supporting atomic operations
|
||||
.r32_uint,
|
||||
@@ -819,6 +846,9 @@ fn isFormatSupported(
|
||||
tiling: vk.ImageTiling,
|
||||
usage: vk.ImageUsageFlags,
|
||||
) VkError!bool {
|
||||
if (base.format.isCompressed(format) and (usage.transfer_src_bit or usage.transfer_dst_bit))
|
||||
return false;
|
||||
|
||||
const format_properties = try self.interface.getFormatProperties(format);
|
||||
const format_features = switch (tiling) {
|
||||
.linear => format_properties.linear_tiling_features,
|
||||
|
||||
@@ -0,0 +1,273 @@
|
||||
const std = @import("std");
|
||||
const vk = @import("vulkan");
|
||||
const base = @import("base");
|
||||
const zm = base.zm;
|
||||
|
||||
pub const F32x4 = zm.F32x4;
|
||||
|
||||
pub fn isBc1(format: vk.Format) bool {
|
||||
return switch (format) {
|
||||
.bc1_rgb_unorm_block,
|
||||
.bc1_rgb_srgb_block,
|
||||
.bc1_rgba_unorm_block,
|
||||
.bc1_rgba_srgb_block,
|
||||
=> true,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn readFloat4(block: []const u8, format: vk.Format, x_in_block: usize, y_in_block: usize) F32x4 {
|
||||
return switch (format) {
|
||||
.bc1_rgb_unorm_block,
|
||||
.bc1_rgb_srgb_block,
|
||||
.bc1_rgba_unorm_block,
|
||||
.bc1_rgba_srgb_block,
|
||||
=> readBc1(block, format, x_in_block, y_in_block),
|
||||
.bc4_unorm_block,
|
||||
.bc4_snorm_block,
|
||||
=> .{ readBc4(block, format, x_in_block, y_in_block), 0.0, 0.0, 1.0 },
|
||||
.bc5_unorm_block,
|
||||
.bc5_snorm_block,
|
||||
=> readBc5(block, format, x_in_block, y_in_block),
|
||||
else => blk: {
|
||||
base.unsupported("Compressed read from format {any}", .{format});
|
||||
break :blk .{ 0.0, 0.0, 0.0, 1.0 };
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn writeFloat4(block: []u8, format: vk.Format, x_in_block: usize, y_in_block: usize, color: F32x4) void {
|
||||
switch (format) {
|
||||
.bc1_rgb_unorm_block,
|
||||
.bc1_rgb_srgb_block,
|
||||
.bc1_rgba_unorm_block,
|
||||
.bc1_rgba_srgb_block,
|
||||
=> writeBc1Texel(block, format, x_in_block, y_in_block, color),
|
||||
else => base.unsupported("Compressed write to format {any}", .{format}),
|
||||
}
|
||||
}
|
||||
|
||||
fn readBc1(block: []const u8, format: vk.Format, x_in_block: usize, y_in_block: usize) F32x4 {
|
||||
std.debug.assert(block.len >= 8);
|
||||
std.debug.assert(x_in_block < 4);
|
||||
std.debug.assert(y_in_block < 4);
|
||||
|
||||
const color_0 = std.mem.bytesToValue(u16, block[0..2]);
|
||||
const color_1 = std.mem.bytesToValue(u16, block[2..4]);
|
||||
const selectors = std.mem.bytesToValue(u32, block[4..8]);
|
||||
const selector_shift: u5 = @intCast(2 * (y_in_block * 4 + x_in_block));
|
||||
const selector = (selectors >> selector_shift) & 0x3;
|
||||
const has_alpha = format == .bc1_rgba_unorm_block or format == .bc1_rgba_srgb_block;
|
||||
|
||||
var palette: [4]F32x4 = undefined;
|
||||
palette[0] = decodeRgb565(color_0);
|
||||
palette[1] = decodeRgb565(color_1);
|
||||
|
||||
if (color_0 > color_1 or !has_alpha) {
|
||||
palette[2] = mix(palette[0], palette[1], 2.0 / 3.0, 1.0 / 3.0, 1.0);
|
||||
palette[3] = mix(palette[0], palette[1], 1.0 / 3.0, 2.0 / 3.0, 1.0);
|
||||
} else {
|
||||
palette[2] = mix(palette[0], palette[1], 0.5, 0.5, 1.0);
|
||||
palette[3] = .{ 0.0, 0.0, 0.0, 0.0 };
|
||||
}
|
||||
|
||||
return palette[@intCast(selector)];
|
||||
}
|
||||
|
||||
fn readBc4(block: []const u8, format: vk.Format, x_in_block: usize, y_in_block: usize) f32 {
|
||||
std.debug.assert(block.len >= 8);
|
||||
std.debug.assert(x_in_block < 4);
|
||||
std.debug.assert(y_in_block < 4);
|
||||
|
||||
const selector_shift: u6 = @intCast(3 * (y_in_block * 4 + x_in_block));
|
||||
const selectors = std.mem.bytesToValue(u64, block[0..8]) >> 16;
|
||||
const selector: usize = @intCast((selectors >> selector_shift) & 0x7);
|
||||
|
||||
return switch (format) {
|
||||
.bc4_unorm_block,
|
||||
.bc5_unorm_block,
|
||||
=> readBc4Unorm(block, selector),
|
||||
.bc4_snorm_block,
|
||||
.bc5_snorm_block,
|
||||
=> readBc4Snorm(block, selector),
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
fn readBc5(block: []const u8, format: vk.Format, x_in_block: usize, y_in_block: usize) F32x4 {
|
||||
std.debug.assert(block.len >= 16);
|
||||
return .{
|
||||
readBc4(block[0..8], format, x_in_block, y_in_block),
|
||||
readBc4(block[8..16], format, x_in_block, y_in_block),
|
||||
0.0,
|
||||
1.0,
|
||||
};
|
||||
}
|
||||
|
||||
fn readBc4Unorm(block: []const u8, selector: usize) f32 {
|
||||
const endpoint_0 = @as(f32, @floatFromInt(block[0])) / 255.0;
|
||||
const endpoint_1 = @as(f32, @floatFromInt(block[1])) / 255.0;
|
||||
|
||||
var palette: [8]f32 = undefined;
|
||||
palette[0] = endpoint_0;
|
||||
palette[1] = endpoint_1;
|
||||
|
||||
if (block[0] > block[1]) {
|
||||
palette[2] = (6.0 * endpoint_0 + 1.0 * endpoint_1) / 7.0;
|
||||
palette[3] = (5.0 * endpoint_0 + 2.0 * endpoint_1) / 7.0;
|
||||
palette[4] = (4.0 * endpoint_0 + 3.0 * endpoint_1) / 7.0;
|
||||
palette[5] = (3.0 * endpoint_0 + 4.0 * endpoint_1) / 7.0;
|
||||
palette[6] = (2.0 * endpoint_0 + 5.0 * endpoint_1) / 7.0;
|
||||
palette[7] = (1.0 * endpoint_0 + 6.0 * endpoint_1) / 7.0;
|
||||
} else {
|
||||
palette[2] = (4.0 * endpoint_0 + 1.0 * endpoint_1) / 5.0;
|
||||
palette[3] = (3.0 * endpoint_0 + 2.0 * endpoint_1) / 5.0;
|
||||
palette[4] = (2.0 * endpoint_0 + 3.0 * endpoint_1) / 5.0;
|
||||
palette[5] = (1.0 * endpoint_0 + 4.0 * endpoint_1) / 5.0;
|
||||
palette[6] = 0.0;
|
||||
palette[7] = 1.0;
|
||||
}
|
||||
|
||||
return palette[selector];
|
||||
}
|
||||
|
||||
fn readBc4Snorm(block: []const u8, selector: usize) f32 {
|
||||
const raw_0: i8 = @bitCast(block[0]);
|
||||
const raw_1: i8 = @bitCast(block[1]);
|
||||
const endpoint_0 = decodeBcSnorm(raw_0);
|
||||
const endpoint_1 = decodeBcSnorm(raw_1);
|
||||
|
||||
var palette: [8]f32 = undefined;
|
||||
palette[0] = endpoint_0;
|
||||
palette[1] = endpoint_1;
|
||||
|
||||
if (raw_0 > raw_1) {
|
||||
palette[2] = (6.0 * endpoint_0 + 1.0 * endpoint_1) / 7.0;
|
||||
palette[3] = (5.0 * endpoint_0 + 2.0 * endpoint_1) / 7.0;
|
||||
palette[4] = (4.0 * endpoint_0 + 3.0 * endpoint_1) / 7.0;
|
||||
palette[5] = (3.0 * endpoint_0 + 4.0 * endpoint_1) / 7.0;
|
||||
palette[6] = (2.0 * endpoint_0 + 5.0 * endpoint_1) / 7.0;
|
||||
palette[7] = (1.0 * endpoint_0 + 6.0 * endpoint_1) / 7.0;
|
||||
} else {
|
||||
palette[2] = (4.0 * endpoint_0 + 1.0 * endpoint_1) / 5.0;
|
||||
palette[3] = (3.0 * endpoint_0 + 2.0 * endpoint_1) / 5.0;
|
||||
palette[4] = (2.0 * endpoint_0 + 3.0 * endpoint_1) / 5.0;
|
||||
palette[5] = (1.0 * endpoint_0 + 4.0 * endpoint_1) / 5.0;
|
||||
palette[6] = -1.0;
|
||||
palette[7] = 1.0;
|
||||
}
|
||||
|
||||
return palette[selector];
|
||||
}
|
||||
|
||||
fn decodeBcSnorm(value: i8) f32 {
|
||||
return @max(@as(f32, @floatFromInt(value)) / 127.0, -1.0);
|
||||
}
|
||||
|
||||
fn writeBc1Texel(block: []u8, format: vk.Format, x_in_block: usize, y_in_block: usize, color: F32x4) void {
|
||||
var pixels: [16]F32x4 = undefined;
|
||||
for (&pixels, 0..) |*pixel, i| {
|
||||
pixel.* = readBc1(block, format, i & 3, i >> 2);
|
||||
}
|
||||
pixels[y_in_block * 4 + x_in_block] = color;
|
||||
encodeBc1(block, format, pixels);
|
||||
}
|
||||
|
||||
pub fn encodeBc1(block: []u8, format: vk.Format, pixels: [16]F32x4) void {
|
||||
std.debug.assert(block.len >= 8);
|
||||
|
||||
var min_color: F32x4 = .{ 1.0, 1.0, 1.0, 1.0 };
|
||||
var max_color: F32x4 = .{ 0.0, 0.0, 0.0, 1.0 };
|
||||
var has_alpha = false;
|
||||
|
||||
for (pixels) |pixel| {
|
||||
if (pixel[3] < 0.5) {
|
||||
has_alpha = true;
|
||||
continue;
|
||||
}
|
||||
min_color[0] = @min(min_color[0], pixel[0]);
|
||||
min_color[1] = @min(min_color[1], pixel[1]);
|
||||
min_color[2] = @min(min_color[2], pixel[2]);
|
||||
max_color[0] = @max(max_color[0], pixel[0]);
|
||||
max_color[1] = @max(max_color[1], pixel[1]);
|
||||
max_color[2] = @max(max_color[2], pixel[2]);
|
||||
}
|
||||
|
||||
var color_0 = encodeRgb565(max_color);
|
||||
var color_1 = encodeRgb565(min_color);
|
||||
const supports_alpha = format == .bc1_rgba_unorm_block or format == .bc1_rgba_srgb_block;
|
||||
if (has_alpha and supports_alpha) {
|
||||
if (color_0 > color_1)
|
||||
std.mem.swap(u16, &color_0, &color_1);
|
||||
} else if (color_0 <= color_1) {
|
||||
std.mem.swap(u16, &color_0, &color_1);
|
||||
}
|
||||
|
||||
std.mem.bytesAsValue(u16, block[0..2]).* = color_0;
|
||||
std.mem.bytesAsValue(u16, block[2..4]).* = color_1;
|
||||
|
||||
var selectors: u32 = 0;
|
||||
for (pixels, 0..) |pixel, i| {
|
||||
const selector = nearestSelector(pixel, color_0, color_1, supports_alpha);
|
||||
selectors |= @as(u32, selector) << @intCast(2 * i);
|
||||
}
|
||||
std.mem.bytesAsValue(u32, block[4..8]).* = selectors;
|
||||
}
|
||||
|
||||
fn nearestSelector(color: F32x4, color_0: u16, color_1: u16, supports_alpha: bool) u2 {
|
||||
if (supports_alpha and color[3] < 0.5 and color_0 <= color_1)
|
||||
return 3;
|
||||
|
||||
var palette: [4]F32x4 = undefined;
|
||||
palette[0] = decodeRgb565(color_0);
|
||||
palette[1] = decodeRgb565(color_1);
|
||||
if (color_0 > color_1 or !supports_alpha) {
|
||||
palette[2] = mix(palette[0], palette[1], 2.0 / 3.0, 1.0 / 3.0, 1.0);
|
||||
palette[3] = mix(palette[0], palette[1], 1.0 / 3.0, 2.0 / 3.0, 1.0);
|
||||
} else {
|
||||
palette[2] = mix(palette[0], palette[1], 0.5, 0.5, 1.0);
|
||||
palette[3] = .{ 0.0, 0.0, 0.0, 0.0 };
|
||||
}
|
||||
|
||||
var best_selector: u2 = 0;
|
||||
var best_distance = std.math.floatMax(f32);
|
||||
for (palette, 0..) |entry, i| {
|
||||
if (i == 3 and supports_alpha and color_0 <= color_1 and color[3] >= 0.5)
|
||||
continue;
|
||||
const dr = color[0] - entry[0];
|
||||
const dg = color[1] - entry[1];
|
||||
const db = color[2] - entry[2];
|
||||
const distance = dr * dr + dg * dg + db * db;
|
||||
if (distance < best_distance) {
|
||||
best_distance = distance;
|
||||
best_selector = @intCast(i);
|
||||
}
|
||||
}
|
||||
return best_selector;
|
||||
}
|
||||
|
||||
fn decodeRgb565(value: u16) F32x4 {
|
||||
return .{
|
||||
@as(f32, @floatFromInt((value >> 11) & 0x1f)) / 31.0,
|
||||
@as(f32, @floatFromInt((value >> 5) & 0x3f)) / 63.0,
|
||||
@as(f32, @floatFromInt(value & 0x1f)) / 31.0,
|
||||
1.0,
|
||||
};
|
||||
}
|
||||
|
||||
fn encodeRgb565(color: F32x4) u16 {
|
||||
const clamped = std.math.clamp(color, zm.f32x4s(0.0), zm.f32x4s(1.0));
|
||||
const r: u16 = @intFromFloat(@round(clamped[0] * 31.0));
|
||||
const g: u16 = @intFromFloat(@round(clamped[1] * 63.0));
|
||||
const b: u16 = @intFromFloat(@round(clamped[2] * 31.0));
|
||||
return (r << 11) | (g << 5) | b;
|
||||
}
|
||||
|
||||
fn mix(a: F32x4, b: F32x4, a_weight: f32, b_weight: f32, alpha: f32) F32x4 {
|
||||
return .{
|
||||
a[0] * a_weight + b[0] * b_weight,
|
||||
a[1] * a_weight + b[1] * b_weight,
|
||||
a[2] * a_weight + b[2] * b_weight,
|
||||
alpha,
|
||||
};
|
||||
}
|
||||
@@ -70,12 +70,8 @@ pub fn shaderInvocation(
|
||||
};
|
||||
|
||||
if (rt.getBuiltinResult(.FragCoord)) |frag_coord_word| {
|
||||
const pixel_x: i32 = @intFromFloat(position[0]);
|
||||
const pixel_y: i32 = @intFromFloat(position[1]);
|
||||
const dx: f32 = if (@mod(pixel_x, 2) == 0) 1.0 else -1.0;
|
||||
const dy: f32 = if (@mod(pixel_y, 2) == 0) 1.0 else -1.0;
|
||||
const frag_coord_dx = zm.f32x4(dx, 0.0, 0.0, 0.0);
|
||||
const frag_coord_dy = zm.f32x4(0.0, dy, 0.0, 0.0);
|
||||
const frag_coord_dx = zm.f32x4(1.0, 0.0, 0.0, 0.0);
|
||||
const frag_coord_dy = zm.f32x4(0.0, 1.0, 0.0, 0.0);
|
||||
try rt.setDerivativeFromMemory(allocator, frag_coord_word, std.mem.asBytes(&frag_coord_dx), std.mem.asBytes(&frag_coord_dy));
|
||||
}
|
||||
|
||||
|
||||
+146
-4
@@ -40,6 +40,150 @@ pub inline fn texelSize(format: vk.Format) usize {
|
||||
return lib.c.vkuFormatTexelBlockSize(@intCast(@intFromEnum(format)));
|
||||
}
|
||||
|
||||
pub inline fn isCompressed(format: vk.Format) bool {
|
||||
return blockWidth(format) > 1 or blockHeight(format) > 1;
|
||||
}
|
||||
|
||||
pub inline fn blockWidth(format: vk.Format) usize {
|
||||
return switch (format) {
|
||||
.bc1_rgb_unorm_block,
|
||||
.bc1_rgb_srgb_block,
|
||||
.bc1_rgba_unorm_block,
|
||||
.bc1_rgba_srgb_block,
|
||||
.bc2_unorm_block,
|
||||
.bc2_srgb_block,
|
||||
.bc3_unorm_block,
|
||||
.bc3_srgb_block,
|
||||
.bc4_unorm_block,
|
||||
.bc4_snorm_block,
|
||||
.bc5_unorm_block,
|
||||
.bc5_snorm_block,
|
||||
.bc6h_ufloat_block,
|
||||
.bc6h_sfloat_block,
|
||||
.bc7_unorm_block,
|
||||
.bc7_srgb_block,
|
||||
.eac_r11_unorm_block,
|
||||
.eac_r11_snorm_block,
|
||||
.eac_r11g11_unorm_block,
|
||||
.eac_r11g11_snorm_block,
|
||||
.etc2_r8g8b8_unorm_block,
|
||||
.etc2_r8g8b8_srgb_block,
|
||||
.etc2_r8g8b8a1_unorm_block,
|
||||
.etc2_r8g8b8a1_srgb_block,
|
||||
.etc2_r8g8b8a8_unorm_block,
|
||||
.etc2_r8g8b8a8_srgb_block,
|
||||
.astc_4x_4_unorm_block,
|
||||
.astc_4x_4_srgb_block,
|
||||
=> 4,
|
||||
.astc_5x_4_unorm_block,
|
||||
.astc_5x_5_unorm_block,
|
||||
.astc_5x_4_srgb_block,
|
||||
.astc_5x_5_srgb_block,
|
||||
=> 5,
|
||||
.astc_6x_5_unorm_block,
|
||||
.astc_6x_6_unorm_block,
|
||||
.astc_6x_5_srgb_block,
|
||||
.astc_6x_6_srgb_block,
|
||||
=> 6,
|
||||
.astc_8x_5_unorm_block,
|
||||
.astc_8x_6_unorm_block,
|
||||
.astc_8x_8_unorm_block,
|
||||
.astc_8x_5_srgb_block,
|
||||
.astc_8x_6_srgb_block,
|
||||
.astc_8x_8_srgb_block,
|
||||
=> 8,
|
||||
.astc_1_0x_5_unorm_block,
|
||||
.astc_1_0x_6_unorm_block,
|
||||
.astc_1_0x_8_unorm_block,
|
||||
.astc_1_0x_10_unorm_block,
|
||||
.astc_1_0x_5_srgb_block,
|
||||
.astc_1_0x_6_srgb_block,
|
||||
.astc_1_0x_8_srgb_block,
|
||||
.astc_1_0x_10_srgb_block,
|
||||
=> 10,
|
||||
.astc_1_2x_10_unorm_block,
|
||||
.astc_1_2x_12_unorm_block,
|
||||
.astc_1_2x_10_srgb_block,
|
||||
.astc_1_2x_12_srgb_block,
|
||||
=> 12,
|
||||
else => 1,
|
||||
};
|
||||
}
|
||||
|
||||
pub inline fn blockHeight(format: vk.Format) usize {
|
||||
return switch (format) {
|
||||
.bc1_rgb_unorm_block,
|
||||
.bc1_rgb_srgb_block,
|
||||
.bc1_rgba_unorm_block,
|
||||
.bc1_rgba_srgb_block,
|
||||
.bc2_unorm_block,
|
||||
.bc2_srgb_block,
|
||||
.bc3_unorm_block,
|
||||
.bc3_srgb_block,
|
||||
.bc4_unorm_block,
|
||||
.bc4_snorm_block,
|
||||
.bc5_unorm_block,
|
||||
.bc5_snorm_block,
|
||||
.bc6h_ufloat_block,
|
||||
.bc6h_sfloat_block,
|
||||
.bc7_unorm_block,
|
||||
.bc7_srgb_block,
|
||||
.eac_r11_unorm_block,
|
||||
.eac_r11_snorm_block,
|
||||
.eac_r11g11_unorm_block,
|
||||
.eac_r11g11_snorm_block,
|
||||
.etc2_r8g8b8_unorm_block,
|
||||
.etc2_r8g8b8_srgb_block,
|
||||
.etc2_r8g8b8a1_unorm_block,
|
||||
.etc2_r8g8b8a1_srgb_block,
|
||||
.etc2_r8g8b8a8_unorm_block,
|
||||
.etc2_r8g8b8a8_srgb_block,
|
||||
.astc_4x_4_unorm_block,
|
||||
.astc_5x_4_unorm_block,
|
||||
.astc_4x_4_srgb_block,
|
||||
.astc_5x_4_srgb_block,
|
||||
=> 4,
|
||||
.astc_5x_5_unorm_block,
|
||||
.astc_6x_5_unorm_block,
|
||||
.astc_8x_5_unorm_block,
|
||||
.astc_1_0x_5_unorm_block,
|
||||
.astc_5x_5_srgb_block,
|
||||
.astc_6x_5_srgb_block,
|
||||
.astc_8x_5_srgb_block,
|
||||
.astc_1_0x_5_srgb_block,
|
||||
=> 5,
|
||||
.astc_6x_6_unorm_block,
|
||||
.astc_8x_6_unorm_block,
|
||||
.astc_1_0x_6_unorm_block,
|
||||
.astc_6x_6_srgb_block,
|
||||
.astc_8x_6_srgb_block,
|
||||
.astc_1_0x_6_srgb_block,
|
||||
=> 6,
|
||||
.astc_8x_8_unorm_block,
|
||||
.astc_1_0x_8_unorm_block,
|
||||
.astc_8x_8_srgb_block,
|
||||
.astc_1_0x_8_srgb_block,
|
||||
=> 8,
|
||||
.astc_1_0x_10_unorm_block,
|
||||
.astc_1_2x_10_unorm_block,
|
||||
.astc_1_0x_10_srgb_block,
|
||||
.astc_1_2x_10_srgb_block,
|
||||
=> 10,
|
||||
.astc_1_2x_12_unorm_block,
|
||||
.astc_1_2x_12_srgb_block,
|
||||
=> 12,
|
||||
else => 1,
|
||||
};
|
||||
}
|
||||
|
||||
pub inline fn blockCountX(format: vk.Format, width: usize) usize {
|
||||
return std.math.divCeil(usize, width, blockWidth(format)) catch unreachable;
|
||||
}
|
||||
|
||||
pub inline fn blockCountY(format: vk.Format, height: usize) usize {
|
||||
return std.math.divCeil(usize, height, blockHeight(format)) catch unreachable;
|
||||
}
|
||||
|
||||
pub inline fn componentCount(format: vk.Format) usize {
|
||||
return @intCast(lib.c.vkuFormatComponentCount(@intCast(@intFromEnum(format))));
|
||||
}
|
||||
@@ -83,13 +227,11 @@ pub fn supportsColorAttachemendBlend(format: vk.Format) bool {
|
||||
}
|
||||
|
||||
pub inline fn pitchMemSize(format: vk.Format, width: usize) usize {
|
||||
// To be updated for compressed formats handling
|
||||
return texelSize(format) * width;
|
||||
return texelSize(format) * blockCountX(format, width);
|
||||
}
|
||||
|
||||
pub inline fn sliceMemSize(format: vk.Format, width: usize, height: usize) usize {
|
||||
// To be updated for compressed formats handling
|
||||
return pitchMemSize(format, width) * height;
|
||||
return pitchMemSize(format, width) * blockCountY(format, height);
|
||||
}
|
||||
|
||||
pub inline fn isDepthAndStencil(format: vk.Format) bool {
|
||||
|
||||
Reference in New Issue
Block a user