1 // Copyright © 2024 Collabora, Ltd.
2 // SPDX-License-Identifier: MIT
3
4 use crate::extent::{units, Extent4D};
5 use crate::format::Format;
6 use crate::image::{
7 ImageDim, ImageUsageFlags, SampleLayout, IMAGE_USAGE_2D_VIEW_BIT,
8 IMAGE_USAGE_LINEAR_BIT,
9 };
10 use crate::ILog2Ceil;
11
12 pub const GOB_WIDTH_B: u32 = 64;
13 pub const GOB_DEPTH: u32 = 1;
14
gob_height(gob_height_is_8: bool) -> u3215 pub fn gob_height(gob_height_is_8: bool) -> u32 {
16 if gob_height_is_8 {
17 8
18 } else {
19 4
20 }
21 }
22
23 #[derive(Clone, Debug, Default, Copy, PartialEq)]
24 #[repr(C)]
25 pub struct Tiling {
26 pub is_tiled: bool,
27 /// Whether the GOB height is 4 or 8
28 pub gob_height_is_8: bool,
29 /// log2 of the X tile dimension in GOBs
30 pub x_log2: u8,
31 /// log2 of the Y tile dimension in GOBs
32 pub y_log2: u8,
33 /// log2 of the z tile dimension in GOBs
34 pub z_log2: u8,
35 }
36
37 impl Tiling {
38 /// Clamps the tiling to less than 2x the given extent in each dimension.
39 ///
40 /// This operation is done by the hardware at each LOD.
clamp(&self, extent_B: Extent4D<units::Bytes>) -> Self41 pub fn clamp(&self, extent_B: Extent4D<units::Bytes>) -> Self {
42 let mut tiling = *self;
43
44 if !self.is_tiled {
45 return tiling;
46 }
47
48 let tiling_extent_B = self.extent_B();
49
50 if extent_B.width < tiling_extent_B.width
51 || extent_B.height < tiling_extent_B.height
52 || extent_B.depth < tiling_extent_B.depth
53 {
54 tiling.x_log2 = 0;
55 }
56
57 let extent_GOB = extent_B.to_GOB(tiling.gob_height_is_8);
58
59 let ceil_h = extent_GOB.height.ilog2_ceil() as u8;
60 let ceil_d = extent_GOB.depth.ilog2_ceil() as u8;
61
62 tiling.y_log2 = std::cmp::min(tiling.y_log2, ceil_h);
63 tiling.z_log2 = std::cmp::min(tiling.z_log2, ceil_d);
64 tiling
65 }
66
size_B(&self) -> u3267 pub fn size_B(&self) -> u32 {
68 let extent_B = self.extent_B();
69 extent_B.width * extent_B.height * extent_B.depth * extent_B.array_len
70 }
71
72 #[no_mangle]
nil_tiling_size_B(&self) -> u3273 pub extern "C" fn nil_tiling_size_B(&self) -> u32 {
74 self.size_B()
75 }
76
extent_B(&self) -> Extent4D<units::Bytes>77 pub fn extent_B(&self) -> Extent4D<units::Bytes> {
78 if self.is_tiled {
79 Extent4D::new(
80 GOB_WIDTH_B << self.x_log2,
81 gob_height(self.gob_height_is_8) << self.y_log2,
82 GOB_DEPTH << self.z_log2,
83 1,
84 )
85 } else {
86 // We handle linear images in Image::new()
87 Extent4D::new(1, 1, 1, 1)
88 }
89 }
90 }
91
sparse_block_extent_el( format: Format, dim: ImageDim, ) -> Extent4D<units::Elements>92 pub fn sparse_block_extent_el(
93 format: Format,
94 dim: ImageDim,
95 ) -> Extent4D<units::Elements> {
96 let bits = format.el_size_B() * 8;
97
98 // Taken from Vulkan 1.3.279 spec section entitled "Standard Sparse
99 // Image Block Shapes".
100 match dim {
101 ImageDim::_2D => match bits {
102 8 => Extent4D::new(256, 256, 1, 1),
103 16 => Extent4D::new(256, 128, 1, 1),
104 32 => Extent4D::new(128, 128, 1, 1),
105 64 => Extent4D::new(128, 64, 1, 1),
106 128 => Extent4D::new(64, 64, 1, 1),
107 other => panic!("Invalid texel size {other}"),
108 },
109 ImageDim::_3D => match bits {
110 8 => Extent4D::new(64, 32, 32, 1),
111 16 => Extent4D::new(32, 32, 32, 1),
112 32 => Extent4D::new(32, 32, 16, 1),
113 64 => Extent4D::new(32, 16, 16, 1),
114 128 => Extent4D::new(16, 16, 16, 1),
115 _ => panic!("Invalid texel size"),
116 },
117 _ => panic!("Invalid sparse image dimension"),
118 }
119 }
120
sparse_block_extent_px( format: Format, dim: ImageDim, sample_layout: SampleLayout, ) -> Extent4D<units::Pixels>121 pub fn sparse_block_extent_px(
122 format: Format,
123 dim: ImageDim,
124 sample_layout: SampleLayout,
125 ) -> Extent4D<units::Pixels> {
126 sparse_block_extent_el(format, dim)
127 .to_sa(format)
128 .to_px(sample_layout)
129 }
130
sparse_block_extent_B( format: Format, dim: ImageDim, ) -> Extent4D<units::Bytes>131 pub fn sparse_block_extent_B(
132 format: Format,
133 dim: ImageDim,
134 ) -> Extent4D<units::Bytes> {
135 sparse_block_extent_el(format, dim).to_B(format)
136 }
137
138 #[no_mangle]
nil_sparse_block_extent_px( format: Format, dim: ImageDim, sample_layout: SampleLayout, ) -> Extent4D<units::Pixels>139 pub extern "C" fn nil_sparse_block_extent_px(
140 format: Format,
141 dim: ImageDim,
142 sample_layout: SampleLayout,
143 ) -> Extent4D<units::Pixels> {
144 sparse_block_extent_px(format, dim, sample_layout)
145 }
146
147 impl Tiling {
sparse(format: Format, dim: ImageDim) -> Self148 pub fn sparse(format: Format, dim: ImageDim) -> Self {
149 let sparse_block_extent_B = sparse_block_extent_B(format, dim);
150
151 assert!(sparse_block_extent_B.width.is_power_of_two());
152 assert!(sparse_block_extent_B.height.is_power_of_two());
153 assert!(sparse_block_extent_B.depth.is_power_of_two());
154
155 let gob_height_is_8 = true;
156 let sparse_block_extent_gob =
157 sparse_block_extent_B.to_GOB(gob_height_is_8);
158
159 Self {
160 is_tiled: true,
161 gob_height_is_8,
162 x_log2: sparse_block_extent_gob.width.ilog2().try_into().unwrap(),
163 y_log2: sparse_block_extent_gob.height.ilog2().try_into().unwrap(),
164 z_log2: sparse_block_extent_gob.depth.ilog2().try_into().unwrap(),
165 }
166 }
167
choose( extent_px: Extent4D<units::Pixels>, format: Format, sample_layout: SampleLayout, usage: ImageUsageFlags, ) -> Tiling168 pub fn choose(
169 extent_px: Extent4D<units::Pixels>,
170 format: Format,
171 sample_layout: SampleLayout,
172 usage: ImageUsageFlags,
173 ) -> Tiling {
174 if (usage & IMAGE_USAGE_LINEAR_BIT) != 0 {
175 return Default::default();
176 }
177
178 let mut tiling = Tiling {
179 is_tiled: true,
180 gob_height_is_8: true,
181 x_log2: 0,
182 y_log2: 5,
183 z_log2: 5,
184 };
185
186 if (usage & IMAGE_USAGE_2D_VIEW_BIT) != 0 {
187 tiling.z_log2 = 0;
188 }
189
190 tiling.clamp(extent_px.to_B(format, sample_layout))
191 }
192 }
193