• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 mod math_util;
6 #[allow(dead_code)]
7 mod message_relay_thread;
8 pub mod surface;
9 mod thread_message_util;
10 mod window;
11 mod window_message_dispatcher;
12 mod window_message_processor;
13 pub mod window_procedure_thread;
14 
15 use std::num::NonZeroU32;
16 use std::sync::mpsc::channel;
17 #[cfg(feature = "kiwi")]
18 use std::sync::Arc;
19 use std::sync::Weak;
20 use std::time::Duration;
21 
22 use anyhow::bail;
23 use anyhow::Context;
24 use anyhow::Result;
25 use base::error;
26 use base::AsRawDescriptor;
27 use base::Event;
28 use base::EventWaitResult;
29 use base::RawDescriptor;
30 #[cfg(feature = "kiwi")]
31 use base::Tube;
32 use euclid::size2;
33 use euclid::Size2D;
34 use math_util::Size2DCheckedCast;
35 use metrics::Metrics;
36 pub use surface::NoopSurface as Surface;
37 #[cfg(feature = "kiwi")]
38 use sync::Mutex;
39 use vm_control::gpu::DisplayMode;
40 use vm_control::gpu::DisplayParameters;
41 use window_message_processor::DisplaySendToWndProc;
42 pub use window_procedure_thread::WindowProcedureThread;
43 
44 use crate::DisplayT;
45 use crate::EventDevice;
46 use crate::GpuDisplayError;
47 use crate::GpuDisplayResult;
48 use crate::GpuDisplaySurface;
49 use crate::SurfaceType;
50 use crate::SysDisplayT;
51 
52 pub(crate) type ObjectId = NonZeroU32;
53 
54 pub struct VirtualDisplaySpace;
55 pub struct HostWindowSpace;
56 
57 #[derive(Clone)]
58 pub struct DisplayProperties {
59     pub start_hidden: bool,
60     pub is_fullscreen: bool,
61     pub window_width: u32,
62     pub window_height: u32,
63 }
64 
65 impl From<&DisplayParameters> for DisplayProperties {
from(params: &DisplayParameters) -> Self66     fn from(params: &DisplayParameters) -> Self {
67         let is_fullscreen = matches!(params.mode, DisplayMode::BorderlessFullScreen(_));
68         let (window_width, window_height) = params.get_window_size();
69 
70         Self {
71             start_hidden: params.hidden,
72             is_fullscreen,
73             window_width,
74             window_height,
75         }
76     }
77 }
78 
79 pub struct DisplayWin {
80     wndproc_thread: WindowProcedureThread<Surface>,
81     display_closed_event: Event,
82     win_metrics: Option<Weak<Metrics>>,
83     display_properties: DisplayProperties,
84     is_surface_created: bool,
85 }
86 
87 impl DisplayWin {
new( wndproc_thread: WindowProcedureThread<Surface>, win_metrics: Option<Weak<Metrics>>, display_properties: DisplayProperties, ) -> Result<DisplayWin, GpuDisplayError>88     pub fn new(
89         wndproc_thread: WindowProcedureThread<Surface>,
90         win_metrics: Option<Weak<Metrics>>,
91         display_properties: DisplayProperties,
92     ) -> Result<DisplayWin, GpuDisplayError> {
93         // The display should be closed once the WndProc thread terminates.
94         let display_closed_event =
95             wndproc_thread
96                 .try_clone_thread_terminated_event()
97                 .map_err(|e| {
98                     error!("Failed to create DisplayWin: {:?}", e);
99                     GpuDisplayError::Allocate
100                 })?;
101         Ok(Self {
102             wndproc_thread,
103             display_closed_event,
104             win_metrics,
105             display_properties,
106             is_surface_created: false,
107         })
108     }
109 
110     /// Posts a create surface command to the WndProc thread and waits until the creation finishes
111     /// to check the result.
create_surface_internal( &mut self, virtual_display_size: Size2D<i32, VirtualDisplaySpace>, ) -> Result<()>112     fn create_surface_internal(
113         &mut self,
114         virtual_display_size: Size2D<i32, VirtualDisplaySpace>,
115     ) -> Result<()> {
116         let metrics = self.win_metrics.clone();
117         let display_properties = self.display_properties.clone();
118         // This function should not return until surface creation finishes. Besides, we would like
119         // to know if the creation succeeds. Hence, we use channels to wait to see the result.
120         let (result_sender, result_receiver) = channel();
121 
122         // Post a message to the WndProc thread to create the surface.
123         self.wndproc_thread
124             .post_display_command(DisplaySendToWndProc::CreateSurface {
125                 function: Box::new(move |window, display_event_dispatcher| {
126                     Surface::create(
127                         window,
128                         &virtual_display_size,
129                         metrics,
130                         &display_properties,
131                         display_event_dispatcher,
132                     )
133                 }),
134                 callback: Box::new(move |success| {
135                     if let Err(e) = result_sender.send(success) {
136                         error!("Failed to send surface creation result: {}", e);
137                     }
138                 }),
139             })?;
140 
141         // Block until the surface creation finishes and check the result.
142         match result_receiver.recv() {
143             Ok(true) => Ok(()),
144             Ok(false) => bail!("WndProc thread failed to create surface!"),
145             Err(e) => bail!("Failed to receive surface creation result: {}", e),
146         }
147     }
148 
import_event_device_internal( &mut self, event_device_id: u32, event_device: EventDevice, ) -> Result<()>149     fn import_event_device_internal(
150         &mut self,
151         event_device_id: u32,
152         event_device: EventDevice,
153     ) -> Result<()> {
154         match ObjectId::new(event_device_id) {
155             Some(event_device_id) => {
156                 self.wndproc_thread
157                     .post_display_command(DisplaySendToWndProc::ImportEventDevice {
158                         event_device_id,
159                         event_device,
160                     })
161                     .context("Failed to send ImportEventDevice message")?;
162                 Ok(())
163             }
164             None => bail!("{} cannot be converted to ObjectId", event_device_id),
165         }
166     }
167 }
168 
169 impl AsRawDescriptor for DisplayWin {
170     /// Event handling is done on the GPU worker thread on other platforms. However, due to the way
171     /// Windows GUI system works, we have to do that on the WndProc thread instead, and we only
172     /// notify the event loop in the GPU worker thread of the display closure event.
as_raw_descriptor(&self) -> RawDescriptor173     fn as_raw_descriptor(&self) -> RawDescriptor {
174         self.display_closed_event.as_raw_descriptor()
175     }
176 }
177 
178 impl DisplayT for DisplayWin {
create_surface( &mut self, parent_surface_id: Option<u32>, _surface_id: u32, virtual_display_width: u32, virtual_display_height: u32, surface_type: SurfaceType, ) -> GpuDisplayResult<Box<dyn GpuDisplaySurface>>179     fn create_surface(
180         &mut self,
181         parent_surface_id: Option<u32>,
182         _surface_id: u32,
183         virtual_display_width: u32,
184         virtual_display_height: u32,
185         surface_type: SurfaceType,
186     ) -> GpuDisplayResult<Box<dyn GpuDisplaySurface>> {
187         if parent_surface_id.is_some() {
188             return Err(GpuDisplayError::Unsupported);
189         }
190 
191         if !matches!(surface_type, SurfaceType::Scanout) {
192             return Err(GpuDisplayError::Unsupported);
193         }
194 
195         // Gfxstream allows for attaching a window only once along the initialization, so we only
196         // create the surface once. See details in b/179319775.
197         if !self.is_surface_created {
198             match self.create_surface_internal(
199                 size2(virtual_display_width, virtual_display_height).checked_cast(),
200             ) {
201                 Ok(_) => self.is_surface_created = true,
202                 Err(e) => {
203                     error!("Failed to create surface: {:?}", e);
204                     return Err(GpuDisplayError::Allocate);
205                 }
206             }
207         }
208 
209         Ok(Box::new(SurfaceWin {
210             display_closed_event: self.display_closed_event.try_clone().map_err(|e| {
211                 error!("Failed to clone display_closed_event: {}", e);
212                 GpuDisplayError::Allocate
213             })?,
214         }))
215     }
216 }
217 
218 impl SysDisplayT for DisplayWin {
import_event_device( &mut self, event_device_id: u32, event_device: EventDevice, ) -> GpuDisplayResult<()>219     fn import_event_device(
220         &mut self,
221         event_device_id: u32,
222         event_device: EventDevice,
223     ) -> GpuDisplayResult<()> {
224         self.import_event_device_internal(event_device_id, event_device)
225             .map_err(|e| {
226                 GpuDisplayError::FailedEventDeviceImport(format!(
227                     "Failed to import event device (ID: {}): {:?}",
228                     event_device_id, e
229                 ))
230             })
231     }
232 }
233 
234 /// The display logic for Windows is quite different from other platforms since display events are
235 /// not handled on the GPU worker thread, but handled by `Surface` class that lives in the WndProc
236 /// thread. `SurfaceWin` will live in the GPU worker thread and provide limited functionalities.
237 pub(crate) struct SurfaceWin {
238     display_closed_event: Event,
239 }
240 
241 impl GpuDisplaySurface for SurfaceWin {
242     /// The entire VM will be shut down when this function returns true. We don't want that happen
243     /// until we know our display is expected to be closed.
close_requested(&self) -> bool244     fn close_requested(&self) -> bool {
245         match self
246             .display_closed_event
247             .wait_timeout(Duration::from_secs(0))
248         {
249             Ok(EventWaitResult::Signaled) => true,
250             Ok(EventWaitResult::TimedOut) => false,
251             Err(e) => {
252                 error!("Failed to read whether display is closed: {}", e);
253                 false
254             }
255         }
256     }
257 }
258