1 // Copyright 2022 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 use base::warn;
6 use devices::virtio::gpu::VIRTIO_GPU_MAX_SCANOUTS;
7 use devices::virtio::GpuDisplayMode;
8 use devices::virtio::GpuDisplayParameters;
9 #[cfg(feature = "gfxstream")]
10 use devices::virtio::GpuMode;
11 use devices::virtio::GpuParameters;
12 use vm_control::gpu::DEFAULT_DPI;
13
14 use crate::crosvm::cmdline::FixedGpuDisplayParameters;
15 use crate::crosvm::cmdline::FixedGpuParameters;
16 use crate::crosvm::config::Config;
17
18 #[cfg(feature = "gfxstream")]
default_use_vulkan() -> bool19 fn default_use_vulkan() -> bool {
20 !cfg!(windows)
21 }
22
fixup_gpu_options( mut gpu_params: GpuParameters, ) -> Result<FixedGpuParameters, String>23 pub(crate) fn fixup_gpu_options(
24 mut gpu_params: GpuParameters,
25 ) -> Result<FixedGpuParameters, String> {
26 // Fix up `gpu_params.display_params` parsed from command-line.
27 gpu_params.display_params = gpu_params
28 .display_params
29 .into_iter()
30 .map(|p| fixup_gpu_display_options(p).map(|p| p.0))
31 .collect::<Result<Vec<_>, _>>()?;
32
33 if gpu_params.__width_compat.is_some() || gpu_params.__height_compat.is_some() {
34 warn!("'width' and 'height' in '--gpu' are deprecated; please use `displays=[...]`");
35 }
36
37 match (
38 gpu_params.__width_compat.take(),
39 gpu_params.__height_compat.take(),
40 ) {
41 (Some(width), Some(height)) => {
42 let display_mode = GpuDisplayMode::Windowed(width, height);
43 gpu_params
44 .display_params
45 .push(GpuDisplayParameters::default_with_mode(display_mode));
46 }
47 (None, None) => {}
48 _ => {
49 return Err("must include both 'width' and 'height' if either is supplied".to_string())
50 }
51 }
52
53 #[cfg(feature = "gfxstream")]
54 if gpu_params.mode == GpuMode::ModeGfxstream {
55 if gpu_params.use_vulkan.is_none() {
56 gpu_params.use_vulkan = Some(default_use_vulkan());
57 }
58 } else {
59 #[cfg(windows)]
60 return Err(format!(
61 "backend type {:?} is deprecated, please use gfxstream",
62 gpu_params.mode
63 ));
64 }
65
66 Ok(FixedGpuParameters(gpu_params))
67 }
68
69 /// Fixes `GpuDisplayParameters` after parsing using serde.
70 ///
71 /// The `dpi` field is guaranteed to be populated after this is called.
fixup_gpu_display_options( mut display_params: GpuDisplayParameters, ) -> Result<FixedGpuDisplayParameters, String>72 pub(crate) fn fixup_gpu_display_options(
73 mut display_params: GpuDisplayParameters,
74 ) -> Result<FixedGpuDisplayParameters, String> {
75 let (horizontal_dpi_compat, vertical_dpi_compat) = (
76 display_params.__horizontal_dpi_compat.take(),
77 display_params.__vertical_dpi_compat.take(),
78 );
79 if horizontal_dpi_compat.is_some() || vertical_dpi_compat.is_some() {
80 warn!("'horizontal-dpi' and 'vertical-dpi' are deprecated; please use `dpi=[...]`");
81 }
82 // Make sure `display_params.dpi` is always populated.
83 display_params.dpi = Some(match display_params.dpi {
84 Some(dpi) => {
85 if horizontal_dpi_compat.is_some() || vertical_dpi_compat.is_some() {
86 return Err(
87 "if 'dpi' is supplied, 'horizontal-dpi' and 'vertical-dpi' must not be supplied"
88 .to_string(),
89 );
90 }
91 dpi
92 }
93 None => (
94 horizontal_dpi_compat.unwrap_or(DEFAULT_DPI),
95 vertical_dpi_compat.unwrap_or(DEFAULT_DPI),
96 ),
97 });
98
99 Ok(FixedGpuDisplayParameters(display_params))
100 }
101
validate_gpu_config(cfg: &mut Config) -> Result<(), String>102 pub(crate) fn validate_gpu_config(cfg: &mut Config) -> Result<(), String> {
103 if let Some(gpu_parameters) = cfg.gpu_parameters.as_mut() {
104 if !gpu_parameters.pci_bar_size.is_power_of_two() {
105 return Err(format!(
106 "`pci-bar-size` must be a power of two but is {}",
107 gpu_parameters.pci_bar_size
108 ));
109 }
110
111 if gpu_parameters.max_num_displays < 1
112 || gpu_parameters.max_num_displays > VIRTIO_GPU_MAX_SCANOUTS as u32
113 {
114 return Err(format!(
115 "`max_num_displays` must be in range [1, {}]",
116 VIRTIO_GPU_MAX_SCANOUTS
117 ));
118 }
119 if gpu_parameters.display_params.len() as u32 > gpu_parameters.max_num_displays {
120 return Err(format!(
121 "Provided more `display_params` ({}) than `max_num_displays` ({})",
122 gpu_parameters.display_params.len(),
123 gpu_parameters.max_num_displays
124 ));
125 }
126
127 // Add a default display if no display is specified.
128 if gpu_parameters.display_params.is_empty() {
129 gpu_parameters.display_params.push(Default::default());
130 }
131
132 let is_4k_uhd_enabled = false;
133 let (width, height) =
134 gpu_parameters.display_params[0].get_virtual_display_size_4k_uhd(is_4k_uhd_enabled);
135 cfg.display_input_width = Some(width);
136 cfg.display_input_height = Some(height);
137 }
138 Ok(())
139 }
140
141 #[cfg(test)]
142 mod tests {
143 use argh::FromArgs;
144 #[cfg(feature = "gfxstream")]
145 use devices::virtio::GpuWsi;
146
147 use super::*;
148 use crate::crosvm::config::from_key_values;
149
get_backend_name() -> &'static str150 const fn get_backend_name() -> &'static str {
151 if cfg!(feature = "gfxstream") {
152 "gfxstream"
153 } else if cfg!(feature = "virgl_renderer") {
154 "virglrenderer"
155 } else {
156 "2d"
157 }
158 }
159
160 /// Parses and fix up a `GpuParameters` from a command-line option string.
parse_gpu_options(s: &str) -> Result<GpuParameters, String>161 fn parse_gpu_options(s: &str) -> Result<GpuParameters, String> {
162 from_key_values::<FixedGpuParameters>(s).map(|p| p.0)
163 }
164
parse_gpu_display_options(s: &str) -> Result<GpuDisplayParameters, String>165 fn parse_gpu_display_options(s: &str) -> Result<GpuDisplayParameters, String> {
166 from_key_values::<GpuDisplayParameters>(s)
167 }
168
169 #[test]
parse_gpu_options_max_num_displays()170 fn parse_gpu_options_max_num_displays() {
171 {
172 let gpu_params = parse_gpu_options("").unwrap();
173 assert_eq!(gpu_params.max_num_displays, VIRTIO_GPU_MAX_SCANOUTS as u32);
174 }
175 {
176 let gpu_params = parse_gpu_options("max-num-displays=5").unwrap();
177 assert_eq!(gpu_params.max_num_displays, 5);
178 }
179 {
180 let command = crate::crosvm::cmdline::RunCommand::from_args(
181 &[],
182 &["--gpu", "max-num-displays=0", "/dev/null"],
183 )
184 .unwrap();
185 assert!(Config::try_from(command).is_err());
186 }
187 {
188 let command = crate::crosvm::cmdline::RunCommand::from_args(
189 &[],
190 &[
191 "--gpu",
192 format!("max-num-displays={}", VIRTIO_GPU_MAX_SCANOUTS + 1).as_str(),
193 "/dev/null",
194 ],
195 )
196 .unwrap();
197 assert!(Config::try_from(command).is_err());
198 }
199 // TODO(b/332910955): Remove the single display restriction on Windows and enable this test.
200 #[cfg(any(target_os = "android", target_os = "linux"))]
201 {
202 let command = crate::crosvm::cmdline::RunCommand::from_args(
203 &[],
204 &[
205 "--gpu",
206 "max-num-displays=1,displays=[[mode=windowed[1920,1080]],\
207 [mode=windowed[1280,720]]]",
208 "/dev/null",
209 ],
210 )
211 .unwrap();
212 assert!(Config::try_from(command).is_err());
213 }
214 #[cfg(any(target_os = "android", target_os = "linux"))]
215 {
216 let config: Config = crate::crosvm::cmdline::RunCommand::from_args(
217 &[],
218 &[
219 "--gpu",
220 "max-num-displays=3,displays=[[mode=windowed[1920,1080]],\
221 [mode=windowed[1280,720]]]",
222 "/dev/null",
223 ],
224 )
225 .unwrap()
226 .try_into()
227 .unwrap();
228
229 let gpu_params = config.gpu_parameters.unwrap();
230
231 assert_eq!(gpu_params.max_num_displays, 3);
232 assert_eq!(gpu_params.display_params.len(), 2);
233 assert_eq!(
234 gpu_params.display_params[0].mode,
235 GpuDisplayMode::Windowed(1920, 1080)
236 );
237 assert_eq!(
238 gpu_params.display_params[1].mode,
239 GpuDisplayMode::Windowed(1280, 720)
240 );
241 }
242 }
243
244 #[test]
parse_gpu_options_mode()245 fn parse_gpu_options_mode() {
246 use devices::virtio::gpu::GpuMode;
247
248 let gpu_params = parse_gpu_options("backend=2d").unwrap();
249 assert_eq!(gpu_params.mode, GpuMode::Mode2D);
250
251 let gpu_params = parse_gpu_options("backend=2D").unwrap();
252 assert_eq!(gpu_params.mode, GpuMode::Mode2D);
253
254 #[cfg(feature = "virgl_renderer")]
255 {
256 let gpu_params = parse_gpu_options("backend=3d").unwrap();
257 assert_eq!(gpu_params.mode, GpuMode::ModeVirglRenderer);
258
259 let gpu_params = parse_gpu_options("backend=3D").unwrap();
260 assert_eq!(gpu_params.mode, GpuMode::ModeVirglRenderer);
261
262 let gpu_params = parse_gpu_options("backend=virglrenderer").unwrap();
263 assert_eq!(gpu_params.mode, GpuMode::ModeVirglRenderer);
264 }
265
266 #[cfg(feature = "gfxstream")]
267 {
268 let gpu_params = parse_gpu_options("backend=gfxstream").unwrap();
269 assert_eq!(gpu_params.mode, GpuMode::ModeGfxstream);
270 }
271 }
272
273 #[test]
parse_gpu_options_flags()274 fn parse_gpu_options_flags() {
275 macro_rules! assert_default {
276 ($p:ident.$a:ident) => {
277 assert_eq!($p.$a, GpuParameters::default().$a)
278 };
279 }
280
281 let gpu_params = parse_gpu_options("").unwrap();
282 assert_default!(gpu_params.renderer_use_egl);
283 assert_default!(gpu_params.renderer_use_gles);
284 assert_default!(gpu_params.renderer_use_glx);
285 assert_default!(gpu_params.renderer_use_surfaceless);
286 assert_default!(gpu_params.use_vulkan);
287 assert_default!(gpu_params.udmabuf);
288
289 let gpu_params = parse_gpu_options("egl=false,gles=false").unwrap();
290 assert_eq!(gpu_params.renderer_use_egl, false);
291 assert_eq!(gpu_params.renderer_use_gles, false);
292 assert_default!(gpu_params.renderer_use_glx);
293 assert_default!(gpu_params.renderer_use_surfaceless);
294 assert_default!(gpu_params.use_vulkan);
295 assert_default!(gpu_params.udmabuf);
296
297 let gpu_params = parse_gpu_options("surfaceless=false,glx").unwrap();
298 assert_default!(gpu_params.renderer_use_egl);
299 assert_default!(gpu_params.renderer_use_gles);
300 assert_eq!(gpu_params.renderer_use_surfaceless, false);
301 assert_eq!(gpu_params.renderer_use_glx, true);
302 assert_default!(gpu_params.use_vulkan);
303 assert_default!(gpu_params.udmabuf);
304
305 let gpu_params = parse_gpu_options("vulkan,udmabuf").unwrap();
306 assert_default!(gpu_params.renderer_use_egl);
307 assert_default!(gpu_params.renderer_use_gles);
308 assert_default!(gpu_params.renderer_use_glx);
309 assert_default!(gpu_params.renderer_use_surfaceless);
310 assert_eq!(gpu_params.use_vulkan, Some(true));
311 assert_eq!(gpu_params.udmabuf, true);
312
313 assert!(parse_gpu_options("egl=false,gles=true,foomatic").is_err());
314 }
315
316 #[cfg(feature = "gfxstream")]
317 #[test]
parse_gpu_options_gfxstream_with_wsi_specified()318 fn parse_gpu_options_gfxstream_with_wsi_specified() {
319 {
320 let gpu_params = parse_gpu_options("backend=gfxstream,wsi=vk").unwrap();
321 assert!(matches!(gpu_params.wsi, Some(GpuWsi::Vulkan)));
322 }
323 {
324 let gpu_params = parse_gpu_options("wsi=vk,backend=gfxstream").unwrap();
325 assert!(matches!(gpu_params.wsi, Some(GpuWsi::Vulkan)));
326 }
327 {
328 assert!(parse_gpu_options("backend=gfxstream,wsi=invalid_value").is_err());
329 }
330 {
331 assert!(parse_gpu_options("wsi=invalid_value,backend=gfxstream").is_err());
332 }
333 }
334
335 #[test]
parse_gpu_options_default_vulkan_support()336 fn parse_gpu_options_default_vulkan_support() {
337 let gpu_params = parse_gpu_options("backend=2d").unwrap();
338 assert_eq!(gpu_params.use_vulkan, None);
339
340 #[cfg(feature = "virgl_renderer")]
341 {
342 let gpu_params = parse_gpu_options("backend=virglrenderer").unwrap();
343 assert_eq!(gpu_params.use_vulkan, None);
344 }
345
346 #[cfg(feature = "gfxstream")]
347 {
348 let gpu_params = parse_gpu_options("backend=gfxstream").unwrap();
349 assert_eq!(gpu_params.use_vulkan, Some(default_use_vulkan()));
350 }
351 }
352
353 #[test]
parse_gpu_options_with_vulkan_specified()354 fn parse_gpu_options_with_vulkan_specified() {
355 const BACKEND: &str = get_backend_name();
356 {
357 let gpu_params = parse_gpu_options("vulkan=true").unwrap();
358 assert_eq!(gpu_params.use_vulkan, Some(true));
359 }
360 {
361 let gpu_params =
362 parse_gpu_options(format!("backend={},vulkan=true", BACKEND).as_str()).unwrap();
363 assert_eq!(gpu_params.use_vulkan, Some(true));
364 }
365 {
366 let gpu_params =
367 parse_gpu_options(format!("vulkan=true,backend={}", BACKEND).as_str()).unwrap();
368 assert_eq!(gpu_params.use_vulkan, Some(true));
369 }
370 {
371 let gpu_params = parse_gpu_options("vulkan=false").unwrap();
372 assert_eq!(gpu_params.use_vulkan, Some(false));
373 }
374 {
375 let gpu_params =
376 parse_gpu_options(format!("backend={},vulkan=false", BACKEND).as_str()).unwrap();
377 assert_eq!(gpu_params.use_vulkan, Some(false));
378 }
379 {
380 let gpu_params =
381 parse_gpu_options(format!("vulkan=false,backend={}", BACKEND).as_str()).unwrap();
382 assert_eq!(gpu_params.use_vulkan, Some(false));
383 }
384 {
385 assert!(parse_gpu_options(
386 format!("backend={},vulkan=invalid_value", BACKEND).as_str()
387 )
388 .is_err());
389 }
390 {
391 assert!(parse_gpu_options(
392 format!("vulkan=invalid_value,backend={}", BACKEND).as_str()
393 )
394 .is_err());
395 }
396 }
397
398 #[test]
parse_gpu_options_context_types()399 fn parse_gpu_options_context_types() {
400 use rutabaga_gfx::RUTABAGA_CAPSET_CROSS_DOMAIN;
401 use rutabaga_gfx::RUTABAGA_CAPSET_VIRGL;
402
403 let gpu_params = parse_gpu_options("context-types=virgl:cross-domain").unwrap();
404 assert_eq!(
405 gpu_params.capset_mask,
406 (1 << RUTABAGA_CAPSET_VIRGL) | (1 << RUTABAGA_CAPSET_CROSS_DOMAIN)
407 );
408 }
409
410 #[test]
parse_gpu_options_cache()411 fn parse_gpu_options_cache() {
412 let gpu_params = parse_gpu_options("cache-path=/path/to/cache,cache-size=16384").unwrap();
413 assert_eq!(gpu_params.cache_path, Some("/path/to/cache".into()));
414 assert_eq!(gpu_params.cache_size, Some("16384".into()));
415 }
416
417 #[test]
parse_gpu_options_pci_bar()418 fn parse_gpu_options_pci_bar() {
419 let gpu_params = parse_gpu_options("pci-bar-size=0x100000").unwrap();
420 assert_eq!(gpu_params.pci_bar_size, 0x100000);
421 }
422
423 #[test]
parse_gpu_options_no_display_specified()424 fn parse_gpu_options_no_display_specified() {
425 let display_params = parse_gpu_options("").unwrap().display_params;
426 assert!(display_params.is_empty());
427 }
428
429 #[test]
parse_gpu_options_display_size_valid()430 fn parse_gpu_options_display_size_valid() {
431 const WIDTH: u32 = 1720;
432 const HEIGHT: u32 = 1800;
433
434 let display_params = parse_gpu_options(format!("width={},height=720", WIDTH).as_str())
435 .unwrap()
436 .display_params;
437 assert_eq!(display_params.len(), 1);
438 assert!(
439 matches!(display_params[0].mode, GpuDisplayMode::Windowed(width, _) if width == WIDTH)
440 );
441
442 let display_params = parse_gpu_options(format!("width=1280,height={}", HEIGHT).as_str())
443 .unwrap()
444 .display_params;
445 assert_eq!(display_params.len(), 1);
446 assert!(
447 matches!(display_params[0].mode, GpuDisplayMode::Windowed(_, height) if height == HEIGHT)
448 );
449 }
450
451 #[test]
parse_gpu_options_display_size_incomplete()452 fn parse_gpu_options_display_size_incomplete() {
453 assert!(parse_gpu_options("width=1280").is_err());
454 assert!(parse_gpu_options("height=720").is_err());
455 }
456
457 #[test]
parse_gpu_options_display_size_duplicated()458 fn parse_gpu_options_display_size_duplicated() {
459 assert!(parse_gpu_options("width=1280,width=1280,height=720").is_err());
460 assert!(parse_gpu_options("width=1280,height=720,height=720").is_err());
461 }
462
463 #[test]
parse_gpu_display_options_mode()464 fn parse_gpu_display_options_mode() {
465 let display_params = parse_gpu_display_options("mode=windowed[1280,720]").unwrap();
466 assert!(matches!(
467 display_params.mode,
468 GpuDisplayMode::Windowed(_, _)
469 ));
470
471 #[cfg(windows)]
472 {
473 let display_params = parse_gpu_display_options("mode=borderless_full_screen").unwrap();
474 assert!(matches!(
475 display_params.mode,
476 GpuDisplayMode::BorderlessFullScreen(_)
477 ));
478 }
479
480 assert!(parse_gpu_display_options("mode=invalid_mode").is_err());
481 }
482
483 #[test]
parse_gpu_display_options_mode_duplicated()484 fn parse_gpu_display_options_mode_duplicated() {
485 assert!(
486 parse_gpu_display_options("mode=windowed[1280,720],mode=windowed[1280,720]").is_err()
487 );
488 }
489
490 #[cfg(windows)]
491 #[test]
parse_gpu_display_options_borderless_full_screen_should_not_be_specified_with_size()492 fn parse_gpu_display_options_borderless_full_screen_should_not_be_specified_with_size() {
493 assert!(parse_gpu_display_options("mode=borderless_full_screen[1280,720]").is_err());
494 }
495
496 #[test]
parse_gpu_display_options_windowed_with_size()497 fn parse_gpu_display_options_windowed_with_size() {
498 const WIDTH: u32 = 1720;
499 const HEIGHT: u32 = 1800;
500
501 let display_params =
502 parse_gpu_display_options(format!("mode=windowed[{},720]", WIDTH).as_str()).unwrap();
503 assert!(matches!(
504 display_params.mode,
505 GpuDisplayMode::Windowed(width, _) if width == WIDTH
506 ));
507
508 let display_params =
509 parse_gpu_display_options(format!("mode=windowed[1280,{}]", HEIGHT).as_str()).unwrap();
510 assert!(matches!(
511 display_params.mode,
512 GpuDisplayMode::Windowed(_, height) if height == HEIGHT
513 ));
514
515 assert!(parse_gpu_display_options("mode=windowed[]").is_err());
516 assert!(parse_gpu_display_options("mode=windowed[1280]").is_err());
517 }
518
519 #[test]
parse_gpu_display_options_hidden()520 fn parse_gpu_display_options_hidden() {
521 let display_params = parse_gpu_display_options("hidden").unwrap();
522 assert!(display_params.hidden);
523
524 let display_params = parse_gpu_display_options("hidden=true").unwrap();
525 assert!(display_params.hidden);
526
527 let display_params = parse_gpu_display_options("hidden=false").unwrap();
528 assert!(!display_params.hidden);
529 }
530
531 #[test]
parse_gpu_display_options_hidden_duplicated()532 fn parse_gpu_display_options_hidden_duplicated() {
533 assert!(parse_gpu_display_options("hidden,hidden").is_err());
534 }
535
536 #[test]
parse_gpu_display_options_refresh_rate()537 fn parse_gpu_display_options_refresh_rate() {
538 const REFRESH_RATE: u32 = 30;
539
540 let display_params =
541 parse_gpu_display_options(format!("refresh-rate={}", REFRESH_RATE).as_str()).unwrap();
542 assert_eq!(display_params.refresh_rate, REFRESH_RATE);
543 }
544
545 #[test]
parse_gpu_display_options_refresh_rate_duplicated()546 fn parse_gpu_display_options_refresh_rate_duplicated() {
547 assert!(parse_gpu_display_options("refresh-rate=30,refresh-rate=60").is_err());
548 }
549
550 #[test]
parse_gpu_display_options_dpi()551 fn parse_gpu_display_options_dpi() {
552 const HORIZONTAL_DPI: u32 = 160;
553 const VERTICAL_DPI: u32 = 25;
554
555 let config: Config = crate::crosvm::cmdline::RunCommand::from_args(
556 &[],
557 &[
558 "--gpu-display",
559 format!("dpi=[{},{}]", HORIZONTAL_DPI, VERTICAL_DPI).as_str(),
560 "/dev/null",
561 ],
562 )
563 .unwrap()
564 .try_into()
565 .unwrap();
566
567 let gpu_params = config.gpu_parameters.unwrap();
568
569 assert_eq!(gpu_params.display_params.len(), 1);
570 assert_eq!(
571 gpu_params.display_params[0].horizontal_dpi(),
572 HORIZONTAL_DPI
573 );
574 assert_eq!(gpu_params.display_params[0].vertical_dpi(), VERTICAL_DPI);
575 }
576
577 #[test]
parse_gpu_display_options_default_dpi()578 fn parse_gpu_display_options_default_dpi() {
579 {
580 let config: Config = crate::crosvm::cmdline::RunCommand::from_args(
581 &[],
582 &["--gpu-display", "mode=windowed[800,600]", "/dev/null"],
583 )
584 .unwrap()
585 .try_into()
586 .unwrap();
587
588 let gpu_params = config.gpu_parameters.unwrap();
589
590 assert_eq!(gpu_params.display_params.len(), 1);
591 assert_eq!(gpu_params.display_params[0].horizontal_dpi(), DEFAULT_DPI);
592 assert_eq!(gpu_params.display_params[0].vertical_dpi(), DEFAULT_DPI);
593 }
594
595 {
596 let config: Config = crate::crosvm::cmdline::RunCommand::from_args(
597 &[],
598 &["--gpu", "displays=[[mode=windowed[800,600]]]", "/dev/null"],
599 )
600 .unwrap()
601 .try_into()
602 .unwrap();
603
604 let gpu_params = config.gpu_parameters.unwrap();
605
606 assert_eq!(gpu_params.display_params.len(), 1);
607 assert_eq!(gpu_params.display_params[0].horizontal_dpi(), DEFAULT_DPI);
608 assert_eq!(gpu_params.display_params[0].vertical_dpi(), DEFAULT_DPI);
609 }
610 }
611
612 #[test]
parse_gpu_display_options_dpi_compat()613 fn parse_gpu_display_options_dpi_compat() {
614 const HORIZONTAL_DPI: u32 = 160;
615 const VERTICAL_DPI: u32 = 25;
616
617 let config: Config = crate::crosvm::cmdline::RunCommand::from_args(
618 &[],
619 &[
620 "--gpu-display",
621 format!(
622 "horizontal-dpi={},vertical-dpi={}",
623 HORIZONTAL_DPI, VERTICAL_DPI
624 )
625 .as_str(),
626 "/dev/null",
627 ],
628 )
629 .unwrap()
630 .try_into()
631 .unwrap();
632
633 let gpu_params = config.gpu_parameters.unwrap();
634
635 assert_eq!(gpu_params.display_params.len(), 1);
636 assert_eq!(
637 gpu_params.display_params[0].horizontal_dpi(),
638 HORIZONTAL_DPI
639 );
640 assert_eq!(gpu_params.display_params[0].vertical_dpi(), VERTICAL_DPI);
641 }
642
643 #[test]
parse_gpu_display_options_dpi_duplicated()644 fn parse_gpu_display_options_dpi_duplicated() {
645 assert!(crate::crosvm::cmdline::RunCommand::from_args(
646 &[],
647 &[
648 "--gpu-display",
649 "horizontal-dpi=160,horizontal-dpi=320",
650 "/dev/null",
651 ],
652 )
653 .is_err());
654
655 assert!(crate::crosvm::cmdline::RunCommand::from_args(
656 &[],
657 &[
658 "--gpu-display",
659 "vertical-dpi=25,vertical-dpi=50",
660 "/dev/null",
661 ],
662 )
663 .is_err());
664
665 assert!(crate::crosvm::cmdline::RunCommand::from_args(
666 &[],
667 &[
668 "--gpu-display",
669 "dpi=[160,320],horizontal-dpi=160,vertical-dpi=25",
670 "/dev/null",
671 ],
672 )
673 .is_err());
674 }
675
676 #[test]
parse_gpu_options_single_display()677 fn parse_gpu_options_single_display() {
678 {
679 let gpu_params = parse_gpu_options("displays=[[mode=windowed[800,600]]]").unwrap();
680 assert_eq!(gpu_params.display_params.len(), 1);
681 assert_eq!(
682 gpu_params.display_params[0].mode,
683 GpuDisplayMode::Windowed(800, 600)
684 );
685 }
686
687 #[cfg(windows)]
688 {
689 let gpu_params = parse_gpu_options("displays=[[mode=borderless_full_screen]]").unwrap();
690 assert_eq!(gpu_params.display_params.len(), 1);
691 assert!(matches!(
692 gpu_params.display_params[0].mode,
693 GpuDisplayMode::BorderlessFullScreen(_)
694 ));
695 }
696 }
697
698 #[test]
parse_gpu_options_multi_display()699 fn parse_gpu_options_multi_display() {
700 {
701 let gpu_params =
702 parse_gpu_options("displays=[[mode=windowed[500,600]],[mode=windowed[700,800]]]")
703 .unwrap();
704 assert_eq!(gpu_params.display_params.len(), 2);
705 assert_eq!(
706 gpu_params.display_params[0].mode,
707 GpuDisplayMode::Windowed(500, 600)
708 );
709 assert_eq!(
710 gpu_params.display_params[1].mode,
711 GpuDisplayMode::Windowed(700, 800)
712 );
713 }
714
715 #[cfg(windows)]
716 {
717 let gpu_params = parse_gpu_options(
718 "displays=[[mode=windowed[800,600]],[mode=borderless_full_screen]]",
719 )
720 .unwrap();
721 assert_eq!(gpu_params.display_params.len(), 2);
722 assert_eq!(
723 gpu_params.display_params[0].mode,
724 GpuDisplayMode::Windowed(800, 600)
725 );
726 assert!(matches!(
727 gpu_params.display_params[1].mode,
728 GpuDisplayMode::BorderlessFullScreen(_)
729 ));
730 }
731 }
732
733 #[test]
parse_gpu_options_single_display_compat()734 fn parse_gpu_options_single_display_compat() {
735 const BACKEND: &str = get_backend_name();
736
737 let config: Config = crate::crosvm::cmdline::RunCommand::from_args(
738 &[],
739 &[
740 "--gpu",
741 format!("backend={},width=500,height=600", BACKEND,).as_str(),
742 "/dev/null",
743 ],
744 )
745 .unwrap()
746 .try_into()
747 .unwrap();
748
749 let gpu_params = config.gpu_parameters.unwrap();
750
751 assert_eq!(gpu_params.display_params.len(), 1);
752 assert_eq!(
753 gpu_params.display_params[0].mode,
754 GpuDisplayMode::Windowed(500, 600)
755 );
756
757 let config: Config = crate::crosvm::cmdline::RunCommand::from_args(
758 &[],
759 &[
760 "--gpu",
761 format!("backend={}", BACKEND,).as_str(),
762 "--gpu-display",
763 "mode=windowed[700,800]",
764 "/dev/null",
765 ],
766 )
767 .unwrap()
768 .try_into()
769 .unwrap();
770
771 let gpu_params = config.gpu_parameters.unwrap();
772
773 assert_eq!(gpu_params.display_params.len(), 1);
774 assert_eq!(
775 gpu_params.display_params[0].mode,
776 GpuDisplayMode::Windowed(700, 800)
777 );
778 }
779
780 #[cfg(any(target_os = "android", target_os = "linux"))]
781 #[test]
parse_gpu_options_and_gpu_display_options_multi_display_supported_on_unix()782 fn parse_gpu_options_and_gpu_display_options_multi_display_supported_on_unix() {
783 {
784 let config: Config = crate::crosvm::cmdline::RunCommand::from_args(
785 &[],
786 &[
787 "--gpu",
788 format!(
789 "backend={},width=500,height=600,displays=[[mode=windowed[700,800]]]",
790 get_backend_name()
791 )
792 .as_str(),
793 "--gpu-display",
794 "mode=windowed[900,1000]",
795 "/dev/null",
796 ],
797 )
798 .unwrap()
799 .try_into()
800 .unwrap();
801
802 let gpu_params = config.gpu_parameters.unwrap();
803
804 assert_eq!(gpu_params.display_params.len(), 3);
805 assert_eq!(
806 gpu_params.display_params[0].mode,
807 GpuDisplayMode::Windowed(700, 800)
808 );
809 assert_eq!(
810 gpu_params.display_params[1].mode,
811 GpuDisplayMode::Windowed(500, 600)
812 );
813 assert_eq!(
814 gpu_params.display_params[2].mode,
815 GpuDisplayMode::Windowed(900, 1000)
816 );
817 }
818 }
819
820 #[cfg(windows)]
821 #[test]
parse_gpu_options_and_gpu_display_options_multi_display_unsupported_on_windows()822 fn parse_gpu_options_and_gpu_display_options_multi_display_unsupported_on_windows() {
823 let command = crate::crosvm::cmdline::RunCommand::from_args(
824 &[],
825 &[
826 "--gpu-display",
827 "mode=borderless_full_screen",
828 "--gpu-display",
829 "mode=borderless_full_screen",
830 "/dev/null",
831 ],
832 )
833 .unwrap();
834 assert!(Config::try_from(command).is_err());
835
836 let command = crate::crosvm::cmdline::RunCommand::from_args(
837 &[],
838 &[
839 "--gpu",
840 "width=1280,height=720",
841 "--gpu-display",
842 "mode=borderless_full_screen",
843 "/dev/null",
844 ],
845 )
846 .unwrap();
847 assert!(Config::try_from(command).is_err());
848
849 let command = crate::crosvm::cmdline::RunCommand::from_args(
850 &[],
851 &[
852 "--gpu",
853 "displays=[[mode=windowed[1280,720]]]",
854 "--gpu-display",
855 "mode=borderless_full_screen",
856 "/dev/null",
857 ],
858 )
859 .unwrap();
860 assert!(Config::try_from(command).is_err());
861
862 let command = crate::crosvm::cmdline::RunCommand::from_args(
863 &[],
864 &[
865 "--gpu",
866 "displays=[[mode=windowed[500,600]],[mode=windowed[700,800]]]",
867 "/dev/null",
868 ],
869 )
870 .unwrap();
871 assert!(Config::try_from(command).is_err());
872 }
873
874 #[test]
parse_gpu_options_cache_size()875 fn parse_gpu_options_cache_size() {
876 {
877 let config: Config = crate::crosvm::cmdline::RunCommand::from_args(
878 &[],
879 &[
880 "--gpu",
881 "vulkan=false,cache-path=/some/path,cache-size=50M",
882 "/dev/null",
883 ],
884 )
885 .unwrap()
886 .try_into()
887 .unwrap();
888
889 let gpu_params = config.gpu_parameters.unwrap();
890 assert_eq!(gpu_params.cache_path, Some("/some/path".into()));
891 assert_eq!(gpu_params.cache_size, Some("50M".into()));
892 }
893 }
894 }
895