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