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