adding BC compressed format support
This commit is contained in:
+2
-2
@@ -32,8 +32,8 @@
|
|||||||
|
|
||||||
// Soft dependencies
|
// Soft dependencies
|
||||||
.SPIRV_Interpreter = .{
|
.SPIRV_Interpreter = .{
|
||||||
.url = "git+https://github.com/Kbz-8/SPIRV-Interpreter#50a2de5bb756f96f0e51277fb98825a6fe829433",
|
.url = "git+https://github.com/Kbz-8/SPIRV-Interpreter#6e1fa9db32f3e596e30d60f94b2909f3609ec03a",
|
||||||
.hash = "SPIRV_Interpreter-0.0.1-ajmpnwGMCQBT5uWJ847m9Mpa-0NrGaugEN0FewoXgC_2",
|
.hash = "SPIRV_Interpreter-0.0.1-ajmpn_CXCQB0GIcZz8ioOFOW9yLr5Ka-Qir3v3Y1r5Bi",
|
||||||
.lazy = true,
|
.lazy = true,
|
||||||
},
|
},
|
||||||
//.SPIRV_Interpreter = .{
|
//.SPIRV_Interpreter = .{
|
||||||
|
|||||||
+45
-13
@@ -55,6 +55,7 @@ const vk = @import("vulkan");
|
|||||||
const base = @import("base");
|
const base = @import("base");
|
||||||
const lib = @import("lib.zig");
|
const lib = @import("lib.zig");
|
||||||
const blitter = @import("device/blitter.zig");
|
const blitter = @import("device/blitter.zig");
|
||||||
|
const compressed = @import("device/compressed.zig");
|
||||||
|
|
||||||
const F32x4 = blitter.F32x4;
|
const F32x4 = blitter.F32x4;
|
||||||
const U32x4 = blitter.U32x4;
|
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 src_format = self.interface.formatFromAspect(region.src_subresource.aspect_mask);
|
||||||
const bytes_per_block = base.format.texelSize(src_format);
|
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 src_extent = self.getMipLevelExtent(region.src_subresource.mip_level);
|
||||||
const dst_extent = dst.getMipLevelExtent(region.dst_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 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 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 slice_count = if (both_are_3D) region.extent.depth else self.interface.samples.toInt();
|
||||||
|
|
||||||
const is_single_slice = (slice_count == 1);
|
const is_single_slice = (slice_count == 1);
|
||||||
const is_single_row = (region.extent.height == 1) and is_single_slice;
|
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);
|
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
|
const is_entire_slice = is_entire_row and
|
||||||
(region.extent.height == src_extent.height) and
|
(region.extent.height == src_extent.height) and
|
||||||
(region.extent.height == dst_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);
|
(src_depth_pitch_bytes == dst_depth_pitch_bytes);
|
||||||
|
|
||||||
const src_texel_offset = try self.getTexelMemoryOffset(region.src_offset, .{
|
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) |_| {
|
for (0..layer_count) |_| {
|
||||||
if (is_single_row) {
|
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)
|
if (dst_map.len < copy_size or src_map.len < copy_size)
|
||||||
break;
|
break;
|
||||||
@memcpy(dst_map[0..copy_size], src_map[0..copy_size]);
|
@memcpy(dst_map[0..copy_size], src_map[0..copy_size]);
|
||||||
} else if (is_entire_row and is_single_slice) {
|
} 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)
|
if (dst_map.len < copy_size or src_map.len < copy_size)
|
||||||
break;
|
break;
|
||||||
@memcpy(dst_map[0..copy_size], src_map[0..copy_size]);
|
@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;
|
break;
|
||||||
@memcpy(dst_map[0..copy_size], src_map[0..copy_size]);
|
@memcpy(dst_map[0..copy_size], src_map[0..copy_size]);
|
||||||
} else if (is_entire_row) {
|
} 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 src_slice_memory = src_map[0..];
|
||||||
var dst_slice_memory = dst_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..];
|
dst_slice_memory = if (dst_slice_memory.len < dst_depth_pitch_bytes) break else dst_slice_memory[dst_depth_pitch_bytes..];
|
||||||
}
|
}
|
||||||
} else {
|
} 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 src_slice_memory = src_map[0..];
|
||||||
var dst_slice_memory = dst_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 src_row_memory = src_slice_memory[0..];
|
||||||
var dst_row_memory = dst_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)
|
if (dst_row_memory.len < row_size or src_row_memory.len < row_size)
|
||||||
break;
|
break;
|
||||||
@memcpy(dst_row_memory[0..row_size], src_row_memory[0..row_size]);
|
@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 bytes_per_block = base.format.texelSize(format);
|
||||||
const memory_row_pitch_bytes = extent.width * bytes_per_block;
|
const copy_block_width = base.format.blockCountX(format, image_extent.width);
|
||||||
const memory_slice_pitch_bytes = extent.height * memory_row_pitch_bytes;
|
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, .{
|
const image_texel_offset = try self.getTexelMemoryOffset(image_offset, .{
|
||||||
.aspect_mask = image_subresource.aspect_mask,
|
.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 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) |_| {
|
for (0..layer_count) |_| {
|
||||||
var src_layer_memory = src_memory[0..];
|
var src_layer_memory = src_memory[0..];
|
||||||
@@ -356,7 +369,7 @@ pub fn copy(
|
|||||||
var src_slice_memory = src_layer_memory[0..];
|
var src_slice_memory = src_layer_memory[0..];
|
||||||
var dst_slice_memory = dst_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)
|
if (dst_slice_memory.len < copy_size or src_slice_memory.len < copy_size)
|
||||||
break;
|
break;
|
||||||
@memcpy(dst_slice_memory[0..copy_size], src_slice_memory[0..copy_size]);
|
@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_size = base.format.texelSize(format);
|
||||||
const texel_offset = try self.getTexelMemoryOffset(offset, subresource);
|
const texel_offset = try self.getTexelMemoryOffset(offset, subresource);
|
||||||
const map = try self.mapAsSliceWithAddedOffset(u8, texel_offset, texel_size);
|
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);
|
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_size = base.format.texelSize(format);
|
||||||
const texel_offset = try self.getTexelMemoryOffset(offset, subresource);
|
const texel_offset = try self.getTexelMemoryOffset(offset, subresource);
|
||||||
const map = try self.mapAsSliceWithAddedOffset(u8, texel_offset, texel_size);
|
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);
|
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 {
|
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) +
|
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) +
|
@divFloor(@as(usize, @intCast(offset.y)), base.format.blockHeight(format)) * 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.x)), base.format.blockWidth(format)) * base.format.texelSize(format);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getTexelMemoryOffset(self: *const Self, offset: vk.Offset3D, subresource: vk.ImageSubresource) VkError!usize {
|
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,
|
.r32g32b32a32_sfloat,
|
||||||
.b10g11r11_ufloat_pack32,
|
.b10g11r11_ufloat_pack32,
|
||||||
.e5b9g9r9_ufloat_pack32,
|
.e5b9g9r9_ufloat_pack32,
|
||||||
//.bc1_rgb_unorm_block,
|
.bc1_rgb_unorm_block,
|
||||||
//.bc1_rgb_srgb_block,
|
.bc1_rgb_srgb_block,
|
||||||
//.bc1_rgba_unorm_block,
|
.bc1_rgba_unorm_block,
|
||||||
//.bc1_rgba_srgb_block,
|
.bc1_rgba_srgb_block,
|
||||||
//.bc2_unorm_block,
|
.bc2_unorm_block,
|
||||||
//.bc2_srgb_block,
|
.bc2_srgb_block,
|
||||||
//.bc3_unorm_block,
|
.bc3_unorm_block,
|
||||||
//.bc3_srgb_block,
|
.bc3_srgb_block,
|
||||||
//.bc4_unorm_block,
|
.bc4_unorm_block,
|
||||||
//.bc4_snorm_block,
|
.bc4_snorm_block,
|
||||||
//.bc5_unorm_block,
|
.bc5_unorm_block,
|
||||||
//.bc5_snorm_block,
|
.bc5_snorm_block,
|
||||||
//.bc6h_ufloat_block,
|
.bc6h_ufloat_block,
|
||||||
//.bc6h_sfloat_block,
|
.bc6h_sfloat_block,
|
||||||
//.bc7_unorm_block,
|
.bc7_unorm_block,
|
||||||
//.bc7_srgb_block,
|
.bc7_srgb_block,
|
||||||
//.etc2_r8g8b8_unorm_block,
|
//.etc2_r8g8b8_unorm_block,
|
||||||
//.etc2_r8g8b8_srgb_block,
|
//.etc2_r8g8b8_srgb_block,
|
||||||
//.etc2_r8g8b8a1_unorm_block,
|
//.etc2_r8g8b8a1_unorm_block,
|
||||||
@@ -446,6 +446,33 @@ pub fn getFormatProperties(interface: *Interface, format: vk.Format) VkError!vk.
|
|||||||
else => {},
|
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) {
|
switch (format) {
|
||||||
// Vulkan 1.0 mandatory storage image formats supporting atomic operations
|
// Vulkan 1.0 mandatory storage image formats supporting atomic operations
|
||||||
.r32_uint,
|
.r32_uint,
|
||||||
@@ -819,6 +846,9 @@ fn isFormatSupported(
|
|||||||
tiling: vk.ImageTiling,
|
tiling: vk.ImageTiling,
|
||||||
usage: vk.ImageUsageFlags,
|
usage: vk.ImageUsageFlags,
|
||||||
) VkError!bool {
|
) 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_properties = try self.interface.getFormatProperties(format);
|
||||||
const format_features = switch (tiling) {
|
const format_features = switch (tiling) {
|
||||||
.linear => format_properties.linear_tiling_features,
|
.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| {
|
if (rt.getBuiltinResult(.FragCoord)) |frag_coord_word| {
|
||||||
const pixel_x: i32 = @intFromFloat(position[0]);
|
const frag_coord_dx = zm.f32x4(1.0, 0.0, 0.0, 0.0);
|
||||||
const pixel_y: i32 = @intFromFloat(position[1]);
|
const frag_coord_dy = zm.f32x4(0.0, 1.0, 0.0, 0.0);
|
||||||
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);
|
|
||||||
try rt.setDerivativeFromMemory(allocator, frag_coord_word, std.mem.asBytes(&frag_coord_dx), std.mem.asBytes(&frag_coord_dy));
|
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)));
|
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 {
|
pub inline fn componentCount(format: vk.Format) usize {
|
||||||
return @intCast(lib.c.vkuFormatComponentCount(@intCast(@intFromEnum(format))));
|
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 {
|
pub inline fn pitchMemSize(format: vk.Format, width: usize) usize {
|
||||||
// To be updated for compressed formats handling
|
return texelSize(format) * blockCountX(format, width);
|
||||||
return texelSize(format) * width;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn sliceMemSize(format: vk.Format, width: usize, height: usize) usize {
|
pub inline fn sliceMemSize(format: vk.Format, width: usize, height: usize) usize {
|
||||||
// To be updated for compressed formats handling
|
return pitchMemSize(format, width) * blockCountY(format, height);
|
||||||
return pitchMemSize(format, width) * height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn isDepthAndStencil(format: vk.Format) bool {
|
pub inline fn isDepthAndStencil(format: vk.Format) bool {
|
||||||
|
|||||||
Reference in New Issue
Block a user