xref: /aosp_15_r20/external/mesa3d/src/nouveau/nil/modifiers.rs (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 // Copyright © 2024 Valve Corporation
2 // SPDX-License-Identifier: MIT
3 
4 use crate::format::Format;
5 use crate::image::Image;
6 use crate::tiling::Tiling;
7 
8 use bitview::*;
9 use nvidia_headers::classes::{cl9097, clc597};
10 
11 pub const MAX_DRM_FORMAT_MODS: usize = 7;
12 
13 #[repr(u8)]
14 #[derive(Clone, Copy, Eq, PartialEq)]
15 pub enum GOBKindVersion {
16     Fermi = 0,
17     G80 = 1,
18     Turing = 2,
19 }
20 
21 impl TryFrom<u64> for GOBKindVersion {
22     type Error = &'static str;
23 
try_from(gob_kind_version: u64) -> Result<Self, Self::Error>24     fn try_from(gob_kind_version: u64) -> Result<Self, Self::Error> {
25         match gob_kind_version {
26             0 => Ok(GOBKindVersion::Fermi),
27             1 => Ok(GOBKindVersion::G80),
28             2 => Ok(GOBKindVersion::Turing),
29             _ => Err("Invalid gob/kind version"),
30         }
31     }
32 }
33 
34 impl GOBKindVersion {
for_dev(dev: &nil_rs_bindings::nv_device_info) -> GOBKindVersion35     pub fn for_dev(dev: &nil_rs_bindings::nv_device_info) -> GOBKindVersion {
36         if dev.cls_eng3d >= clc597::TURING_A {
37             GOBKindVersion::Turing
38         } else if dev.cls_eng3d >= cl9097::FERMI_A {
39             GOBKindVersion::Fermi
40         } else {
41             GOBKindVersion::G80
42         }
43     }
44 }
45 
46 #[repr(u8)]
47 #[derive(Clone, Copy, Eq, PartialEq)]
48 pub enum SectorLayout {
49     TegraK1 = 0,
50     Desktop = 1,
51 }
52 
53 impl TryFrom<u64> for SectorLayout {
54     type Error = &'static str;
55 
try_from(sector_layout: u64) -> Result<Self, Self::Error>56     fn try_from(sector_layout: u64) -> Result<Self, Self::Error> {
57         match sector_layout {
58             0 => Ok(SectorLayout::TegraK1),
59             1 => Ok(SectorLayout::Desktop),
60             _ => Err("Invalid gob/kind version"),
61         }
62     }
63 }
64 
65 impl SectorLayout {
66     // For now, this always returns desktop, but will be different for Tegra
for_dev(_dev: &nil_rs_bindings::nv_device_info) -> SectorLayout67     pub fn for_dev(_dev: &nil_rs_bindings::nv_device_info) -> SectorLayout {
68         SectorLayout::Desktop
69     }
70 }
71 
72 #[repr(u8)]
73 #[allow(dead_code)]
74 #[derive(Clone, Copy, Eq, PartialEq)]
75 pub enum CompressionType {
76     None = 0,
77     ROP3DOne = 1,
78     ROP3DTwo = 2,
79     CDEHorizontal = 3,
80     CDEVertical = 4,
81 }
82 
83 impl TryFrom<u64> for CompressionType {
84     type Error = &'static str;
85 
try_from(compression_type: u64) -> Result<Self, Self::Error>86     fn try_from(compression_type: u64) -> Result<Self, Self::Error> {
87         match compression_type {
88             0 => Ok(CompressionType::None),
89             1 => Ok(CompressionType::ROP3DOne),
90             2 => Ok(CompressionType::ROP3DTwo),
91             3 => Ok(CompressionType::CDEHorizontal),
92             4 => Ok(CompressionType::CDEVertical),
93             _ => Err("Invalid gob/kind version"),
94         }
95     }
96 }
97 
98 pub const DRM_FORMAT_MOD_LINEAR: u64 = 0;
99 pub const DRM_FORMAT_MOD_INVALID: u64 = 0x00ffffff_ffffffff;
100 
101 const DRM_FORMAT_MOD_VENDOR_NVIDIA: u8 = 0x03;
102 
103 pub struct BlockLinearModifier {
104     drm_modifier: u64,
105 }
106 
107 impl TryFrom<u64> for BlockLinearModifier {
108     type Error = &'static str;
109 
try_from(drm_modifier: u64) -> Result<Self, Self::Error>110     fn try_from(drm_modifier: u64) -> Result<Self, Self::Error> {
111         let bv = BitView::new(&drm_modifier);
112         let vendor: u8 = bv.get_bit_range_u64(56..64).try_into().unwrap();
113         if vendor != DRM_FORMAT_MOD_VENDOR_NVIDIA {
114             Err("modifier does not have DRM_FORMAT_MOD_VENDOR_NVIDIA")
115         } else if !bv.get_bit(4) {
116             Err("modifier is not block linear")
117         } else if bv.get_bit_range_u64(5..12) != 0
118             || bv.get_bit_range_u64(26..56) != 0
119         {
120             Err("unknown reserved bits")
121         } else {
122             Ok(BlockLinearModifier { drm_modifier })
123         }
124     }
125 }
126 
127 impl BlockLinearModifier {
block_linear_2d( compression_type: CompressionType, sector_layout: SectorLayout, gob_kind_version: GOBKindVersion, pte_kind: u8, height_log2: u8, ) -> BlockLinearModifier128     pub fn block_linear_2d(
129         compression_type: CompressionType,
130         sector_layout: SectorLayout,
131         gob_kind_version: GOBKindVersion,
132         pte_kind: u8,
133         height_log2: u8,
134     ) -> BlockLinearModifier {
135         let mut drm_modifier = 0_u64;
136         let mut bv = BitMutView::new(&mut drm_modifier);
137         bv.set_field(0..4, height_log2);
138         bv.set_bit(4, true); // Must be 1, to indicate block-linear layout.
139         bv.set_field(12..20, pte_kind);
140         bv.set_field(20..22, gob_kind_version as u8);
141         bv.set_field(22..23, sector_layout as u8);
142         bv.set_field(23..26, compression_type as u8);
143         bv.set_field(56..64, DRM_FORMAT_MOD_VENDOR_NVIDIA);
144         BlockLinearModifier { drm_modifier }
145     }
146 
height_log2(&self) -> u8147     pub fn height_log2(&self) -> u8 {
148         let bv = BitView::new(&self.drm_modifier);
149         bv.get_bit_range_u64(0..4).try_into().unwrap()
150     }
151 
pte_kind(&self) -> u8152     pub fn pte_kind(&self) -> u8 {
153         let bv = BitView::new(&self.drm_modifier);
154         bv.get_bit_range_u64(12..20).try_into().unwrap()
155     }
156 
gob_kind_version(&self) -> GOBKindVersion157     pub fn gob_kind_version(&self) -> GOBKindVersion {
158         let bv = BitView::new(&self.drm_modifier);
159         bv.get_bit_range_u64(20..22).try_into().unwrap()
160     }
161 
sector_layout(&self) -> SectorLayout162     pub fn sector_layout(&self) -> SectorLayout {
163         let bv = BitView::new(&self.drm_modifier);
164         bv.get_bit_range_u64(22..23).try_into().unwrap()
165     }
166 
compression_type(&self) -> CompressionType167     pub fn compression_type(&self) -> CompressionType {
168         let bv = BitView::new(&self.drm_modifier);
169         bv.get_bit_range_u64(23..26).try_into().unwrap()
170     }
171 
tiling(&self) -> Tiling172     pub fn tiling(&self) -> Tiling {
173         Tiling {
174             is_tiled: true,
175             gob_height_is_8: self.gob_kind_version() != GOBKindVersion::G80,
176             x_log2: 0,
177             y_log2: self.height_log2(),
178             z_log2: 0,
179         }
180     }
181 }
182 
183 #[no_mangle]
nil_drm_format_mods_for_format( dev: &nil_rs_bindings::nv_device_info, format: Format, mod_count: &mut usize, mods: &mut [u64; MAX_DRM_FORMAT_MODS], )184 pub extern "C" fn nil_drm_format_mods_for_format(
185     dev: &nil_rs_bindings::nv_device_info,
186     format: Format,
187     mod_count: &mut usize,
188     mods: &mut [u64; MAX_DRM_FORMAT_MODS],
189 ) {
190     drm_format_mods_for_format(dev, format, mod_count, mods)
191 }
192 
drm_format_mods_for_format( dev: &nil_rs_bindings::nv_device_info, format: Format, mod_count: &mut usize, mods: &mut [u64; MAX_DRM_FORMAT_MODS], )193 pub fn drm_format_mods_for_format(
194     dev: &nil_rs_bindings::nv_device_info,
195     format: Format,
196     mod_count: &mut usize,
197     mods: &mut [u64; MAX_DRM_FORMAT_MODS],
198 ) {
199     let max_mod_count = *mod_count;
200     *mod_count = 0;
201 
202     if format.is_depth_or_stencil() {
203         return;
204     }
205 
206     if !format.supports_color_targets(dev) {
207         return;
208     }
209 
210     // These formats don't have a corresponding fourcc format
211     let p_format: nil_rs_bindings::pipe_format = format.into();
212     if p_format == nil_rs_bindings::PIPE_FORMAT_R11G11B10_FLOAT
213         || p_format == nil_rs_bindings::PIPE_FORMAT_R9G9B9E5_FLOAT
214     {
215         return;
216     }
217 
218     let compression_type = CompressionType::None;
219     let sector_layout = SectorLayout::for_dev(dev);
220     let gob_kind_version = GOBKindVersion::for_dev(dev);
221     let pte_kind = Image::choose_pte_kind(dev, format, 1, false);
222 
223     // We assume bigger tiling is better
224     for i in 0..6 {
225         let height_log2 = 5 - i;
226 
227         let bl_mod = BlockLinearModifier::block_linear_2d(
228             compression_type,
229             sector_layout,
230             gob_kind_version,
231             pte_kind,
232             height_log2,
233         );
234 
235         assert!(*mod_count < max_mod_count);
236         mods[*mod_count] = bl_mod.drm_modifier;
237         *mod_count += 1;
238     }
239 
240     assert!(*mod_count < max_mod_count);
241     mods[*mod_count] = DRM_FORMAT_MOD_LINEAR;
242     *mod_count += 1;
243 }
244 
drm_format_mod_is_supported( dev: &nil_rs_bindings::nv_device_info, format: Format, modifier: u64, ) -> bool245 pub fn drm_format_mod_is_supported(
246     dev: &nil_rs_bindings::nv_device_info,
247     format: Format,
248     modifier: u64,
249 ) -> bool {
250     if modifier == DRM_FORMAT_MOD_LINEAR {
251         return true;
252     }
253 
254     let Ok(bl_mod) = BlockLinearModifier::try_from(modifier) else {
255         return false;
256     };
257 
258     if bl_mod.height_log2() > 5 {
259         return false;
260     }
261 
262     if bl_mod.gob_kind_version() != GOBKindVersion::for_dev(dev) {
263         return false;
264     }
265 
266     if bl_mod.sector_layout() != SectorLayout::for_dev(dev) {
267         return false;
268     }
269 
270     if bl_mod.compression_type() != CompressionType::None {
271         return false;
272     }
273 
274     let pte_kind = Image::choose_pte_kind(dev, format, 1, false);
275     if bl_mod.pte_kind() != pte_kind {
276         return false;
277     }
278 
279     return true;
280 }
281 
score_drm_format_mod(modifier: u64) -> u32282 fn score_drm_format_mod(modifier: u64) -> u32 {
283     if modifier == DRM_FORMAT_MOD_LINEAR {
284         return 1;
285     }
286 
287     let bl_mod = BlockLinearModifier::try_from(modifier).unwrap();
288 
289     // Assume bigger Y-tiling is better
290     let mut score = 10 + u32::from(bl_mod.height_log2());
291 
292     if bl_mod.compression_type() != CompressionType::None {
293         score += 10;
294     }
295 
296     score
297 }
298 
select_best_drm_format_mod( dev: &nil_rs_bindings::nv_device_info, format: Format, modifiers: &[u64], ) -> u64299 pub fn select_best_drm_format_mod(
300     dev: &nil_rs_bindings::nv_device_info,
301     format: Format,
302     modifiers: &[u64],
303 ) -> u64 {
304     let mut best = DRM_FORMAT_MOD_INVALID;
305     let mut best_score = 0;
306 
307     for &modifier in modifiers {
308         if !drm_format_mod_is_supported(dev, format, modifier) {
309             continue;
310         }
311 
312         let score = score_drm_format_mod(modifier);
313         if score > best_score {
314             best = modifier;
315             best_score = score;
316         }
317     }
318 
319     return best;
320 }
321 
322 #[no_mangle]
nil_select_best_drm_format_mod( dev: &nil_rs_bindings::nv_device_info, format: Format, modifier_count: usize, modifiers: *const u64, ) -> u64323 pub extern "C" fn nil_select_best_drm_format_mod(
324     dev: &nil_rs_bindings::nv_device_info,
325     format: Format,
326     modifier_count: usize,
327     modifiers: *const u64,
328 ) -> u64 {
329     let modifiers =
330         unsafe { std::slice::from_raw_parts(modifiers, modifier_count) };
331     select_best_drm_format_mod(dev, format, modifiers)
332 }
333