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