1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 use gleam::{gl, gl::Gl};
6 use std::cell::{Cell, UnsafeCell};
7 use std::collections::{hash_map::HashMap, VecDeque};
8 use std::ops::{Deref, DerefMut, Range};
9 use std::ptr;
10 use std::sync::atomic::{AtomicBool, AtomicI8, AtomicPtr, AtomicU32, AtomicU8, Ordering};
11 use std::sync::{Arc, Condvar, Mutex, MutexGuard};
12 use std::thread;
13 use crate::{
14     api::units::*, api::ColorDepth, api::ColorF, api::ExternalImageId, api::ImageRendering, api::YuvRangedColorSpace,
15     Compositor, CompositorCapabilities, CompositorSurfaceTransform, NativeSurfaceId, NativeSurfaceInfo, NativeTileId,
16     profiler, MappableCompositor, SWGLCompositeSurfaceInfo,
17 };
18 
19 pub struct SwTile {
20     x: i32,
21     y: i32,
22     fbo_id: u32,
23     color_id: u32,
24     valid_rect: DeviceIntRect,
25     /// Composition of tiles must be ordered such that any tiles that may overlap
26     /// an invalidated tile in an earlier surface only get drawn after that tile
27     /// is actually updated. We store a count of the number of overlapping invalid
28     /// here, that gets decremented when the invalid tiles are finally updated so
29     /// that we know when it is finally safe to draw. Must use a Cell as we might
30     /// be analyzing multiple tiles and surfaces
31     overlaps: Cell<u32>,
32     /// Whether the tile's contents has been invalidated
33     invalid: Cell<bool>,
34     /// Graph node for job dependencies of this tile
35     graph_node: SwCompositeGraphNodeRef,
36 }
37 
38 impl SwTile {
new(x: i32, y: i32) -> Self39     fn new(x: i32, y: i32) -> Self {
40         SwTile {
41             x,
42             y,
43             fbo_id: 0,
44             color_id: 0,
45             valid_rect: DeviceIntRect::zero(),
46             overlaps: Cell::new(0),
47             invalid: Cell::new(false),
48             graph_node: SwCompositeGraphNode::new(),
49         }
50     }
51 
52     /// The offset of the tile in the local space of the surface before any
53     /// transform is applied.
origin(&self, surface: &SwSurface) -> DeviceIntPoint54     fn origin(&self, surface: &SwSurface) -> DeviceIntPoint {
55         DeviceIntPoint::new(self.x * surface.tile_size.width, self.y * surface.tile_size.height)
56     }
57 
58     /// The offset valid rect positioned within the local space of the surface
59     /// before any transform is applied.
local_bounds(&self, surface: &SwSurface) -> DeviceIntRect60     fn local_bounds(&self, surface: &SwSurface) -> DeviceIntRect {
61         self.valid_rect.translate(self.origin(surface).to_vector())
62     }
63 
64     /// Bounds used for determining overlap dependencies. This may either be the
65     /// full tile bounds or the actual valid rect, depending on whether the tile
66     /// is invalidated this frame. These bounds are more conservative as such and
67     /// may differ from the precise bounds used to actually composite the tile.
overlap_rect( &self, surface: &SwSurface, transform: &CompositorSurfaceTransform, clip_rect: &DeviceIntRect, ) -> Option<DeviceIntRect>68     fn overlap_rect(
69         &self,
70         surface: &SwSurface,
71         transform: &CompositorSurfaceTransform,
72         clip_rect: &DeviceIntRect,
73     ) -> Option<DeviceIntRect> {
74         let bounds = self.local_bounds(surface);
75         let device_rect = transform.outer_transformed_box2d(&bounds.to_f32())?.round_out().to_i32();
76         device_rect.intersection(clip_rect)
77     }
78 
79     /// Determine if the tile's bounds may overlap the dependency rect if it were
80     /// to be composited at the given position.
may_overlap( &self, surface: &SwSurface, transform: &CompositorSurfaceTransform, clip_rect: &DeviceIntRect, dep_rect: &DeviceIntRect, ) -> bool81     fn may_overlap(
82         &self,
83         surface: &SwSurface,
84         transform: &CompositorSurfaceTransform,
85         clip_rect: &DeviceIntRect,
86         dep_rect: &DeviceIntRect,
87     ) -> bool {
88         self.overlap_rect(surface, transform, clip_rect)
89             .map_or(false, |r| r.intersects(dep_rect))
90     }
91 
92     /// Get valid source and destination rectangles for composition of the tile
93     /// within a surface, bounded by the clipping rectangle. May return None if
94     /// it falls outside of the clip rect.
composite_rects( &self, surface: &SwSurface, transform: &CompositorSurfaceTransform, clip_rect: &DeviceIntRect, ) -> Option<(DeviceIntRect, DeviceIntRect, bool)>95     fn composite_rects(
96         &self,
97         surface: &SwSurface,
98         transform: &CompositorSurfaceTransform,
99         clip_rect: &DeviceIntRect,
100     ) -> Option<(DeviceIntRect, DeviceIntRect, bool)> {
101         // Offset the valid rect to the appropriate surface origin.
102         let valid = self.local_bounds(surface);
103         // The destination rect is the valid rect transformed and then clipped.
104         let dest_rect = transform.outer_transformed_box2d(&valid.to_f32())?.round_out().to_i32();
105         if !dest_rect.intersects(clip_rect) {
106             return None;
107         }
108         // To get a valid source rect, we need to inverse transform the clipped destination rect to find out the effect
109         // of the clip rect in source-space. After this, we subtract off the source-space valid rect origin to get
110         // a source rect that is now relative to the surface origin rather than absolute.
111         let inv_transform = transform.inverse()?;
112         let src_rect = inv_transform
113             .outer_transformed_box2d(&dest_rect.to_f32())?
114             .round()
115             .to_i32()
116             .translate(-valid.min.to_vector());
117         Some((src_rect, dest_rect, transform.m22 < 0.0))
118     }
119 }
120 
121 pub struct SwSurface {
122     tile_size: DeviceIntSize,
123     is_opaque: bool,
124     tiles: Vec<SwTile>,
125     /// An attached external image for this surface.
126     external_image: Option<ExternalImageId>,
127     /// Descriptor for the external image if successfully locked for composite.
128     composite_surface: Option<SWGLCompositeSurfaceInfo>,
129 }
130 
131 impl SwSurface {
new(tile_size: DeviceIntSize, is_opaque: bool) -> Self132     fn new(tile_size: DeviceIntSize, is_opaque: bool) -> Self {
133         SwSurface {
134             tile_size,
135             is_opaque,
136             tiles: Vec::new(),
137             external_image: None,
138             composite_surface: None,
139         }
140     }
141 
142     /// Conserative approximation of local bounds of the surface by combining
143     /// the local bounds of all enclosed tiles.
local_bounds(&self) -> DeviceIntRect144     fn local_bounds(&self) -> DeviceIntRect {
145         let mut bounds = DeviceIntRect::zero();
146         for tile in &self.tiles {
147             bounds = bounds.union(&tile.local_bounds(self));
148         }
149         bounds
150     }
151 
152     /// The transformed and clipped conservative device-space bounds of the
153     /// surface.
device_bounds( &self, transform: &CompositorSurfaceTransform, clip_rect: &DeviceIntRect, ) -> Option<DeviceIntRect>154     fn device_bounds(
155         &self,
156         transform: &CompositorSurfaceTransform,
157         clip_rect: &DeviceIntRect,
158     ) -> Option<DeviceIntRect> {
159         let bounds = self.local_bounds();
160         let device_rect = transform.outer_transformed_box2d(&bounds.to_f32())?.round_out().to_i32();
161         device_rect.intersection(clip_rect)
162     }
163 }
164 
image_rendering_to_gl_filter(filter: ImageRendering) -> gl::GLenum165 fn image_rendering_to_gl_filter(filter: ImageRendering) -> gl::GLenum {
166     match filter {
167         ImageRendering::Pixelated => gl::NEAREST,
168         ImageRendering::Auto | ImageRendering::CrispEdges => gl::LINEAR,
169     }
170 }
171 
172 /// A source for a composite job which can either be a single BGRA locked SWGL
173 /// resource or a collection of SWGL resources representing a YUV surface.
174 #[derive(Clone)]
175 enum SwCompositeSource {
176     BGRA(swgl::LockedResource),
177     YUV(
178         swgl::LockedResource,
179         swgl::LockedResource,
180         swgl::LockedResource,
181         YuvRangedColorSpace,
182         ColorDepth,
183     ),
184 }
185 
186 /// Mark ExternalImage's renderer field as safe to send to SwComposite thread.
187 unsafe impl Send for SwCompositeSource {}
188 
189 /// A tile composition job to be processed by the SwComposite thread.
190 /// Stores relevant details about the tile and where to composite it.
191 #[derive(Clone)]
192 struct SwCompositeJob {
193     /// Locked texture that will be unlocked immediately following the job
194     locked_src: SwCompositeSource,
195     /// Locked framebuffer that may be shared among many jobs
196     locked_dst: swgl::LockedResource,
197     src_rect: DeviceIntRect,
198     dst_rect: DeviceIntRect,
199     clipped_dst: DeviceIntRect,
200     opaque: bool,
201     flip_y: bool,
202     filter: ImageRendering,
203     /// The total number of bands for this job
204     num_bands: u8,
205 }
206 
207 impl SwCompositeJob {
208     /// Process a composite job
process(&self, band_index: i32)209     fn process(&self, band_index: i32) {
210         // Bands are allocated in reverse order, but we want to process them in increasing order.
211         let num_bands = self.num_bands as i32;
212         let band_index = num_bands - 1 - band_index;
213         // Calculate the Y extents for the job's band, starting at the current index and spanning to
214         // the following index.
215         let band_offset = (self.clipped_dst.height() * band_index) / num_bands;
216         let band_height = (self.clipped_dst.height() * (band_index + 1)) / num_bands - band_offset;
217         // Create a rect that is the intersection of the band with the clipped dest
218         let band_clip = DeviceIntRect::from_origin_and_size(
219             DeviceIntPoint::new(self.clipped_dst.min.x, self.clipped_dst.min.y + band_offset),
220             DeviceIntSize::new(self.clipped_dst.width(), band_height),
221         );
222         match self.locked_src {
223             SwCompositeSource::BGRA(ref resource) => {
224                 self.locked_dst.composite(
225                     resource,
226                     self.src_rect.min.x,
227                     self.src_rect.min.y,
228                     self.src_rect.width(),
229                     self.src_rect.height(),
230                     self.dst_rect.min.x,
231                     self.dst_rect.min.y,
232                     self.dst_rect.width(),
233                     self.dst_rect.height(),
234                     self.opaque,
235                     self.flip_y,
236                     image_rendering_to_gl_filter(self.filter),
237                     band_clip.min.x,
238                     band_clip.min.y,
239                     band_clip.width(),
240                     band_clip.height(),
241                 );
242             }
243             SwCompositeSource::YUV(ref y, ref u, ref v, color_space, color_depth) => {
244                 let swgl_color_space = match color_space {
245                     YuvRangedColorSpace::Rec601Narrow => swgl::YuvRangedColorSpace::Rec601Narrow,
246                     YuvRangedColorSpace::Rec601Full => swgl::YuvRangedColorSpace::Rec601Full,
247                     YuvRangedColorSpace::Rec709Narrow => swgl::YuvRangedColorSpace::Rec709Narrow,
248                     YuvRangedColorSpace::Rec709Full => swgl::YuvRangedColorSpace::Rec709Full,
249                     YuvRangedColorSpace::Rec2020Narrow => swgl::YuvRangedColorSpace::Rec2020Narrow,
250                     YuvRangedColorSpace::Rec2020Full => swgl::YuvRangedColorSpace::Rec2020Full,
251                     YuvRangedColorSpace::GbrIdentity => swgl::YuvRangedColorSpace::GbrIdentity,
252                 };
253                 self.locked_dst.composite_yuv(
254                     y,
255                     u,
256                     v,
257                     swgl_color_space,
258                     color_depth.bit_depth(),
259                     self.src_rect.min.x,
260                     self.src_rect.min.y,
261                     self.src_rect.width(),
262                     self.src_rect.height(),
263                     self.dst_rect.min.x,
264                     self.dst_rect.min.y,
265                     self.dst_rect.width(),
266                     self.dst_rect.height(),
267                     self.flip_y,
268                     band_clip.min.x,
269                     band_clip.min.y,
270                     band_clip.width(),
271                     band_clip.height(),
272                 );
273             }
274         }
275     }
276 }
277 
278 /// A reference to a SwCompositeGraph node that can be passed from the render
279 /// thread to the SwComposite thread. Consistency of mutation is ensured in
280 /// SwCompositeGraphNode via use of Atomic operations that prevent more than
281 /// one thread from mutating SwCompositeGraphNode at once. This avoids using
282 /// messy and not-thread-safe RefCells or expensive Mutexes inside the graph
283 /// node and at least signals to the compiler that potentially unsafe coercions
284 /// are occurring.
285 #[derive(Clone)]
286 struct SwCompositeGraphNodeRef(Arc<UnsafeCell<SwCompositeGraphNode>>);
287 
288 impl SwCompositeGraphNodeRef {
new(graph_node: SwCompositeGraphNode) -> Self289     fn new(graph_node: SwCompositeGraphNode) -> Self {
290         SwCompositeGraphNodeRef(Arc::new(UnsafeCell::new(graph_node)))
291     }
292 
get(&self) -> &SwCompositeGraphNode293     fn get(&self) -> &SwCompositeGraphNode {
294         unsafe { &*self.0.get() }
295     }
296 
get_mut(&self) -> &mut SwCompositeGraphNode297     fn get_mut(&self) -> &mut SwCompositeGraphNode {
298         unsafe { &mut *self.0.get() }
299     }
300 
get_ptr_mut(&self) -> *mut SwCompositeGraphNode301     fn get_ptr_mut(&self) -> *mut SwCompositeGraphNode {
302         self.0.get()
303     }
304 }
305 
306 unsafe impl Send for SwCompositeGraphNodeRef {}
307 
308 impl Deref for SwCompositeGraphNodeRef {
309     type Target = SwCompositeGraphNode;
310 
deref(&self) -> &Self::Target311     fn deref(&self) -> &Self::Target {
312         self.get()
313     }
314 }
315 
316 impl DerefMut for SwCompositeGraphNodeRef {
deref_mut(&mut self) -> &mut Self::Target317     fn deref_mut(&mut self) -> &mut Self::Target {
318         self.get_mut()
319     }
320 }
321 
322 /// Dependency graph of composite jobs to be completed. Keeps a list of child jobs that are dependent on the completion of this job.
323 /// Also keeps track of the number of parent jobs that this job is dependent upon before it can be processed. Once there are no more
324 /// in-flight parent jobs that it depends on, the graph node is finally added to the job queue for processing.
325 struct SwCompositeGraphNode {
326     /// Job to be queued for this graph node once ready.
327     job: Option<SwCompositeJob>,
328     /// The number of remaining bands associated with this job. When this is
329     /// non-zero and the node has no more parents left, then the node is being
330     /// actively used by the composite thread to process jobs. Once it hits
331     /// zero, the owning thread (which brought it to zero) can safely retire
332     /// the node as no other thread is using it.
333     remaining_bands: AtomicU8,
334     /// The number of bands that are available for processing.
335     available_bands: AtomicI8,
336     /// Count of parents this graph node depends on. While this is non-zero the
337     /// node must ensure that it is only being actively mutated by the render
338     /// thread and otherwise never being accessed by the render thread.
339     parents: AtomicU32,
340     /// Graph nodes of child jobs that are dependent on this job
341     children: Vec<SwCompositeGraphNodeRef>,
342 }
343 
344 unsafe impl Sync for SwCompositeGraphNode {}
345 
346 impl SwCompositeGraphNode {
new() -> SwCompositeGraphNodeRef347     fn new() -> SwCompositeGraphNodeRef {
348         SwCompositeGraphNodeRef::new(SwCompositeGraphNode {
349             job: None,
350             remaining_bands: AtomicU8::new(0),
351             available_bands: AtomicI8::new(0),
352             parents: AtomicU32::new(0),
353             children: Vec::new(),
354         })
355     }
356 
357     /// Reset the node's state for a new frame
reset(&mut self)358     fn reset(&mut self) {
359         self.job = None;
360         self.remaining_bands.store(0, Ordering::SeqCst);
361         self.available_bands.store(0, Ordering::SeqCst);
362         // Initialize parents to 1 as sentinel dependency for uninitialized job
363         // to avoid queuing unitialized job as unblocked child dependency.
364         self.parents.store(1, Ordering::SeqCst);
365         self.children.clear();
366     }
367 
368     /// Add a dependent child node to dependency list. Update its parent count.
add_child(&mut self, child: SwCompositeGraphNodeRef)369     fn add_child(&mut self, child: SwCompositeGraphNodeRef) {
370         child.parents.fetch_add(1, Ordering::SeqCst);
371         self.children.push(child);
372     }
373 
374     /// Install a job for this node. Return whether or not the job has any unprocessed parents
375     /// that would block immediate composition.
set_job(&mut self, job: SwCompositeJob, num_bands: u8) -> bool376     fn set_job(&mut self, job: SwCompositeJob, num_bands: u8) -> bool {
377         self.job = Some(job);
378         self.remaining_bands.store(num_bands, Ordering::SeqCst);
379         self.available_bands.store(num_bands as _, Ordering::SeqCst);
380         // Subtract off the sentinel parent dependency now that job is initialized and check
381         // whether there are any remaining parent dependencies to see if this job is ready.
382         self.parents.fetch_sub(1, Ordering::SeqCst) <= 1
383     }
384 
385     /// Take an available band if possible. Also return whether there are no more bands left
386     /// so the caller may properly clean up after.
take_band(&self) -> (Option<i32>, bool)387     fn take_band(&self) -> (Option<i32>, bool) {
388         let available = self.available_bands.fetch_sub(1, Ordering::SeqCst);
389         if available > 0 {
390             (Some(available as i32 - 1), available == 1)
391         } else {
392             (None, true)
393         }
394     }
395 
396     /// Try to take the job from this node for processing and then process it within the current band.
process_job(&self, band_index: i32)397     fn process_job(&self, band_index: i32) {
398         if let Some(ref job) = self.job {
399             job.process(band_index);
400         }
401     }
402 
403     /// After processing a band, check all child dependencies and remove this parent from
404     /// their dependency counts. If applicable, queue the new child bands for composition.
unblock_children(&mut self, thread: &SwCompositeThread)405     fn unblock_children(&mut self, thread: &SwCompositeThread) {
406         if self.remaining_bands.fetch_sub(1, Ordering::SeqCst) > 1 {
407             return;
408         }
409         // Clear the job to release any locked resources.
410         self.job = None;
411         let mut lock = None;
412         for child in self.children.drain(..) {
413             // Remove the child's parent dependency on this node. If there are no more
414             // parent dependencies left, send the child job bands for composition.
415             if child.parents.fetch_sub(1, Ordering::SeqCst) <= 1 {
416                 if lock.is_none() {
417                     lock = Some(thread.lock());
418                 }
419                 thread.send_job(lock.as_mut().unwrap(), child);
420             }
421         }
422     }
423 }
424 
425 /// The SwComposite thread processes a queue of composite jobs, also signaling
426 /// via a condition when all available jobs have been processed, as tracked by
427 /// the job count.
428 struct SwCompositeThread {
429     /// Queue of available composite jobs
430     jobs: Mutex<SwCompositeJobQueue>,
431     /// Cache of the current job being processed. This maintains a pointer to
432     /// the contents of the SwCompositeGraphNodeRef, which is safe due to the
433     /// fact that SwCompositor maintains a strong reference to the contents
434     /// in an SwTile to keep it alive while this is in use.
435     current_job: AtomicPtr<SwCompositeGraphNode>,
436     /// Condition signaled when either there are jobs available to process or
437     /// there are no more jobs left to process. Otherwise stated, this signals
438     /// when the job queue transitions from an empty to non-empty state or from
439     /// a non-empty to empty state.
440     jobs_available: Condvar,
441     /// Whether all available jobs have been processed.
442     jobs_completed: AtomicBool,
443     /// Whether the main thread is waiting for for job completeion.
444     waiting_for_jobs: AtomicBool,
445     /// Whether the SwCompositor is shutting down
446     shutting_down: AtomicBool,
447 }
448 
449 /// The SwCompositeThread struct is shared between the SwComposite thread
450 /// and the rendering thread so that both ends can access the job queue.
451 unsafe impl Sync for SwCompositeThread {}
452 
453 /// A FIFO queue of composite jobs to be processed.
454 type SwCompositeJobQueue = VecDeque<SwCompositeGraphNodeRef>;
455 
456 /// Locked access to the composite job queue.
457 type SwCompositeThreadLock<'a> = MutexGuard<'a, SwCompositeJobQueue>;
458 
459 impl SwCompositeThread {
460     /// Create the SwComposite thread. Requires a SWGL context in which
461     /// to do the composition.
new() -> Arc<SwCompositeThread>462     fn new() -> Arc<SwCompositeThread> {
463         let info = Arc::new(SwCompositeThread {
464             jobs: Mutex::new(SwCompositeJobQueue::new()),
465             current_job: AtomicPtr::new(ptr::null_mut()),
466             jobs_available: Condvar::new(),
467             jobs_completed: AtomicBool::new(true),
468             waiting_for_jobs: AtomicBool::new(false),
469             shutting_down: AtomicBool::new(false),
470         });
471         let result = info.clone();
472         let thread_name = "SwComposite";
473         thread::Builder::new()
474             .name(thread_name.into())
475             // The composite thread only calls into SWGL to composite, and we
476             // have potentially many composite threads for different windows,
477             // so using the default stack size is excessive. A reasonably small
478             // stack size should be more than enough for SWGL and reduce memory
479             // overhead.
480             .stack_size(32 * 1024)
481             .spawn(move || {
482                 profiler::register_thread(thread_name);
483                 // Process any available jobs. This will return a non-Ok
484                 // result when the job queue is dropped, causing the thread
485                 // to eventually exit.
486                 while let Some((job, band)) = info.take_job(true) {
487                     info.process_job(job, band);
488                 }
489                 profiler::unregister_thread();
490             })
491             .expect("Failed creating SwComposite thread");
492         result
493     }
494 
deinit(&self)495     fn deinit(&self) {
496         // Signal that the thread needs to exit.
497         self.shutting_down.store(true, Ordering::SeqCst);
498         // Wake up the thread in case it is blocked waiting for new jobs
499         self.jobs_available.notify_all();
500     }
501 
502     /// Process a job contained in a dependency graph node received from the job queue.
503     /// Any child dependencies will be unblocked as appropriate after processing. The
504     /// job count will be updated to reflect this.
process_job(&self, graph_node: &mut SwCompositeGraphNode, band: i32)505     fn process_job(&self, graph_node: &mut SwCompositeGraphNode, band: i32) {
506         // Do the actual processing of the job contained in this node.
507         graph_node.process_job(band);
508         // Unblock any child dependencies now that this job has been processed.
509         graph_node.unblock_children(self);
510     }
511 
512     /// Queue a tile for composition by adding to the queue and increasing the job count.
queue_composite( &self, locked_src: SwCompositeSource, locked_dst: swgl::LockedResource, src_rect: DeviceIntRect, dst_rect: DeviceIntRect, clip_rect: DeviceIntRect, opaque: bool, flip_y: bool, filter: ImageRendering, mut graph_node: SwCompositeGraphNodeRef, job_queue: &mut SwCompositeJobQueue, )513     fn queue_composite(
514         &self,
515         locked_src: SwCompositeSource,
516         locked_dst: swgl::LockedResource,
517         src_rect: DeviceIntRect,
518         dst_rect: DeviceIntRect,
519         clip_rect: DeviceIntRect,
520         opaque: bool,
521         flip_y: bool,
522         filter: ImageRendering,
523         mut graph_node: SwCompositeGraphNodeRef,
524         job_queue: &mut SwCompositeJobQueue,
525     ) {
526         // For jobs that would span a sufficiently large destination rectangle, split
527         // it into multiple horizontal bands so that multiple threads can process them.
528         let clipped_dst = match dst_rect.intersection(&clip_rect) {
529             Some(clipped_dst) => clipped_dst,
530             None => return,
531         };
532 
533         let num_bands = if clipped_dst.width() >= 64 && clipped_dst.height() >= 64 {
534             (clipped_dst.height() / 64).min(4) as u8
535         } else {
536             1
537         };
538         let job = SwCompositeJob {
539             locked_src,
540             locked_dst,
541             src_rect,
542             dst_rect,
543             clipped_dst,
544             opaque,
545             flip_y,
546             filter,
547             num_bands,
548         };
549         if graph_node.set_job(job, num_bands) {
550             self.send_job(job_queue, graph_node);
551         }
552     }
553 
prepare_for_composites(&self)554     fn prepare_for_composites(&self) {
555         // Initially, the job queue is empty. Trivially, this means we consider all
556         // jobs queued so far as completed.
557         self.jobs_completed.store(true, Ordering::SeqCst);
558     }
559 
560     /// Lock the thread for access to the job queue.
lock(&self) -> SwCompositeThreadLock561     fn lock(&self) -> SwCompositeThreadLock {
562         self.jobs.lock().unwrap()
563     }
564 
565     /// Send a job to the composite thread by adding it to the job queue.
566     /// Signal that this job has been added in case the queue was empty and the
567     /// SwComposite thread is waiting for jobs.
send_job(&self, queue: &mut SwCompositeJobQueue, job: SwCompositeGraphNodeRef)568     fn send_job(&self, queue: &mut SwCompositeJobQueue, job: SwCompositeGraphNodeRef) {
569         if queue.is_empty() {
570             self.jobs_completed.store(false, Ordering::SeqCst);
571             self.jobs_available.notify_all();
572         }
573         queue.push_back(job);
574     }
575 
576     /// Try to get a band of work from the currently cached job when available.
577     /// If there is a job, but it has no available bands left, null out the job
578     /// so that other threads do not bother checking the job.
try_take_job(&self) -> Option<(&mut SwCompositeGraphNode, i32)>579     fn try_take_job(&self) -> Option<(&mut SwCompositeGraphNode, i32)> {
580         let current_job_ptr = self.current_job.load(Ordering::SeqCst);
581         if let Some(current_job) = unsafe { current_job_ptr.as_mut() } {
582             let (band, done) = current_job.take_band();
583             if done {
584                 let _ = self.current_job.compare_exchange(
585                     current_job_ptr,
586                     ptr::null_mut(),
587                     Ordering::SeqCst,
588                     Ordering::SeqCst,
589                 );
590             }
591             if let Some(band) = band {
592                 return Some((current_job, band));
593             }
594         }
595         return None;
596     }
597 
598     /// Take a job from the queue. Optionally block waiting for jobs to become
599     /// available if this is called from the SwComposite thread.
take_job(&self, wait: bool) -> Option<(&mut SwCompositeGraphNode, i32)>600     fn take_job(&self, wait: bool) -> Option<(&mut SwCompositeGraphNode, i32)> {
601         // First try checking the cached job outside the scope of the mutex.
602         // For jobs that have multiple bands, this allows us to avoid having
603         // to lock the mutex multiple times to check the job for each band.
604         if let Some((job, band)) = self.try_take_job() {
605             return Some((job, band));
606         }
607         // Lock the job queue while checking for available jobs. The lock
608         // won't be held while the job is processed later outside of this
609         // function so that other threads can pull from the queue meanwhile.
610         let mut jobs = self.lock();
611         loop {
612             // While inside the mutex, check the cached job again to see if it
613             // has been updated.
614             if let Some((job, band)) = self.try_take_job() {
615                 return Some((job, band));
616             }
617             // If no cached job was available, try to take a job from the queue
618             // and install it as the current job.
619             if let Some(job) = jobs.pop_front() {
620                 self.current_job.store(job.get_ptr_mut(), Ordering::SeqCst);
621                 continue;
622             }
623             // Otherwise, the job queue is currently empty. Depending on the
624             // job status, we may either wait for jobs to become available or exit.
625             if wait {
626                 // For the SwComposite thread, if we arrive here, the job queue
627                 // is empty. Signal that all available jobs have been completed.
628                 self.jobs_completed.store(true, Ordering::SeqCst);
629                 if self.waiting_for_jobs.load(Ordering::SeqCst) {
630                     // Wake the main thread if it is waiting for a change in job status.
631                     self.jobs_available.notify_all();
632                 } else if self.shutting_down.load(Ordering::SeqCst) {
633                     // If SwComposite thread needs to shut down, then exit and stop
634                     // waiting for jobs.
635                     return None;
636                 }
637             } else {
638                 // If all available jobs have been completed by the SwComposite
639                 // thread, then the main thread no longer needs to wait for any
640                 // new jobs to appear in the queue and should exit.
641                 if self.jobs_completed.load(Ordering::SeqCst) {
642                     return None;
643                 }
644                 // Otherwise, signal that the main thread is waiting for jobs.
645                 self.waiting_for_jobs.store(true, Ordering::SeqCst);
646             }
647             // Wait until jobs are added before checking the job queue again.
648             jobs = self.jobs_available.wait(jobs).unwrap();
649             if !wait {
650                 // The main thread is done waiting for jobs.
651                 self.waiting_for_jobs.store(false, Ordering::SeqCst);
652             }
653         }
654     }
655 
656     /// Wait for all queued composition jobs to be processed.
657     /// Instead of blocking on the SwComposite thread to complete all jobs,
658     /// this may steal some jobs and attempt to process them while waiting.
659     /// This may optionally process jobs synchronously. When normally doing
660     /// asynchronous processing, the graph dependencies are relied upon to
661     /// properly order the jobs, which makes it safe for the render thread
662     /// to steal jobs from the composite thread without violating those
663     /// dependencies. Synchronous processing just disables this job stealing
664     /// so that the composite thread always handles the jobs in the order
665     /// they were queued without having to rely upon possibly unavailable
666     /// graph dependencies.
wait_for_composites(&self, sync: bool)667     fn wait_for_composites(&self, sync: bool) {
668         // If processing asynchronously, try to steal jobs from the composite
669         // thread if it is busy.
670         if !sync {
671             while let Some((job, band)) = self.take_job(false) {
672                 self.process_job(job, band);
673             }
674             // Once there are no more jobs, just fall through to waiting
675             // synchronously for the composite thread to finish processing.
676         }
677         // If processing synchronously, just wait for the composite thread
678         // to complete processing any in-flight jobs, then bail.
679         let mut jobs = self.lock();
680         // Signal that the main thread may wait for job completion so that the
681         // SwComposite thread can wake it up if necessary.
682         self.waiting_for_jobs.store(true, Ordering::SeqCst);
683         // Wait for job completion to ensure there are no more in-flight jobs.
684         while !self.jobs_completed.load(Ordering::SeqCst) {
685             jobs = self.jobs_available.wait(jobs).unwrap();
686         }
687         // Done waiting for job completion.
688         self.waiting_for_jobs.store(false, Ordering::SeqCst);
689     }
690 
691     /// Check if all in-flight jobs have not been completed yet. If they have
692     /// not, then we assume the SwComposite thread is currently busy compositing.
is_busy_compositing(&self) -> bool693     fn is_busy_compositing(&self) -> bool {
694         !self.jobs_completed.load(Ordering::SeqCst)
695     }
696 }
697 
698 /// Parameters describing how to composite a surface within a frame
699 type FrameSurface = (
700     NativeSurfaceId,
701     CompositorSurfaceTransform,
702     DeviceIntRect,
703     ImageRendering,
704 );
705 
706 /// Adapter for RenderCompositors to work with SWGL that shuttles between
707 /// WebRender and the RenderCompositr via the Compositor API.
708 pub struct SwCompositor {
709     gl: swgl::Context,
710     compositor: Box<dyn MappableCompositor>,
711     use_native_compositor: bool,
712     surfaces: HashMap<NativeSurfaceId, SwSurface>,
713     frame_surfaces: Vec<FrameSurface>,
714     /// Any surface added after we're already compositing (i.e. debug overlay)
715     /// needs to be processed after those frame surfaces. For simplicity we
716     /// store them in a separate queue that gets processed later.
717     late_surfaces: Vec<FrameSurface>,
718     cur_tile: NativeTileId,
719     /// The maximum tile size required for any of the allocated surfaces.
720     max_tile_size: DeviceIntSize,
721     /// Reuse the same depth texture amongst all tiles in all surfaces.
722     /// This depth texture must be big enough to accommodate the largest used
723     /// tile size for any surface. The maximum requested tile size is tracked
724     /// to ensure that this depth texture is at least that big.
725     depth_id: u32,
726     /// Instance of the SwComposite thread, only created if we are not relying
727     /// on a native RenderCompositor.
728     composite_thread: Option<Arc<SwCompositeThread>>,
729     /// SWGL locked resource for sharing framebuffer with SwComposite thread
730     locked_framebuffer: Option<swgl::LockedResource>,
731 }
732 
733 impl SwCompositor {
new( gl: swgl::Context, compositor: Box<dyn MappableCompositor>, use_native_compositor: bool, ) -> Self734     pub fn new(
735         gl: swgl::Context,
736         compositor: Box<dyn MappableCompositor>,
737         use_native_compositor: bool,
738     ) -> Self {
739         let depth_id = gl.gen_textures(1)[0];
740         // Only create the SwComposite thread if we're not using a native render
741         // compositor. Thus, we are compositing into the main software framebuffer,
742         // which benefits from compositing asynchronously while updating tiles.
743         let composite_thread = if !use_native_compositor {
744             Some(SwCompositeThread::new())
745         } else {
746             None
747         };
748         SwCompositor {
749             gl,
750             compositor,
751             use_native_compositor,
752             surfaces: HashMap::new(),
753             frame_surfaces: Vec::new(),
754             late_surfaces: Vec::new(),
755             cur_tile: NativeTileId {
756                 surface_id: NativeSurfaceId(0),
757                 x: 0,
758                 y: 0,
759             },
760             max_tile_size: DeviceIntSize::zero(),
761             depth_id,
762             composite_thread,
763             locked_framebuffer: None,
764         }
765     }
766 
deinit_tile(&self, tile: &SwTile)767     fn deinit_tile(&self, tile: &SwTile) {
768         self.gl.delete_framebuffers(&[tile.fbo_id]);
769         self.gl.delete_textures(&[tile.color_id]);
770     }
771 
deinit_surface(&self, surface: &SwSurface)772     fn deinit_surface(&self, surface: &SwSurface) {
773         for tile in &surface.tiles {
774             self.deinit_tile(tile);
775         }
776     }
777 
778     /// Attempt to occlude any queued surfaces with an opaque occluder rect. If
779     /// an existing surface is occluded, we attempt to restrict its clip rect
780     /// so long as it can remain a single clip rect. Existing frame surfaces
781     /// that are opaque will be fused if possible with the supplied occluder
782     /// rect to further try and restrict any underlying surfaces.
occlude_surfaces(&mut self)783     fn occlude_surfaces(&mut self) {
784         // Check if inner rect is fully included in outer rect
785         fn includes(outer: &Range<i32>, inner: &Range<i32>) -> bool {
786             outer.start <= inner.start && outer.end >= inner.end
787         }
788 
789         // Check if outer range overlaps either the start or end of a range. If
790         // there is overlap, return the portion of the inner range remaining
791         // after the overlap has been removed.
792         fn overlaps(outer: &Range<i32>, inner: &Range<i32>) -> Option<Range<i32>> {
793             if outer.start <= inner.start && outer.end >= inner.start {
794                 Some(outer.end..inner.end.max(outer.end))
795             } else if outer.start <= inner.end && outer.end >= inner.end {
796                 Some(inner.start..outer.start.max(inner.start))
797             } else {
798                 None
799             }
800         }
801 
802         fn set_x_range(rect: &mut DeviceIntRect, range: &Range<i32>) {
803             rect.min.x = range.start;
804             rect.max.x = range.end;
805         }
806 
807         fn set_y_range(rect: &mut DeviceIntRect, range: &Range<i32>) {
808             rect.min.y = range.start;
809             rect.max.y = range.end;
810         }
811 
812         fn union(base: Range<i32>, extra: Range<i32>) -> Range<i32> {
813             base.start.min(extra.start)..base.end.max(extra.end)
814         }
815 
816         // Before we can try to occlude any surfaces, we need to fix their clip rects to tightly
817         // bound the valid region. The clip rect might otherwise enclose an invalid area that
818         // can't fully occlude anything even if the surface is opaque.
819         for &mut (ref id, ref transform, ref mut clip_rect, _) in &mut self.frame_surfaces {
820             if let Some(surface) = self.surfaces.get(id) {
821                 // Restrict the clip rect to fall within the valid region of the surface.
822                 *clip_rect = surface.device_bounds(transform, clip_rect).unwrap_or_default();
823             }
824         }
825 
826         // For each frame surface, treat it as an occluder if it is non-empty and opaque. Look
827         // through the preceding surfaces to see if any can be occluded.
828         for occlude_index in 0..self.frame_surfaces.len() {
829             let (ref occlude_id, _, ref occlude_rect, _) = self.frame_surfaces[occlude_index];
830             match self.surfaces.get(occlude_id) {
831                 Some(occluder) if occluder.is_opaque && !occlude_rect.is_empty() => {}
832                 _ => continue,
833             }
834 
835             // Traverse the queued surfaces for this frame in the reverse order of
836             // how they are composited, or rather, in order of visibility. For each
837             // surface, check if the occluder can restrict the clip rect such that
838             // the clip rect can remain a single rect. If the clip rect overlaps
839             // the occluder on one axis interval while remaining fully included in
840             // the occluder's other axis interval, then we can chop down the edge
841             // of the clip rect on the overlapped axis. Further, if the surface is
842             // opaque and its clip rect exactly matches the occluder rect on one
843             // axis interval while overlapping on the other, fuse it with the
844             // occluder rect before considering any underlying surfaces.
845             let (mut occlude_x, mut occlude_y) = (occlude_rect.x_range(), occlude_rect.y_range());
846             for &mut (ref id, _, ref mut clip_rect, _) in self.frame_surfaces[..occlude_index].iter_mut().rev() {
847                 if let Some(surface) = self.surfaces.get(id) {
848                     let (clip_x, clip_y) = (clip_rect.x_range(), clip_rect.y_range());
849                     if includes(&occlude_x, &clip_x) {
850                         if let Some(visible) = overlaps(&occlude_y, &clip_y) {
851                             set_y_range(clip_rect, &visible);
852                             if surface.is_opaque && occlude_x == clip_x {
853                                 occlude_y = union(occlude_y, visible);
854                             }
855                         }
856                     } else if includes(&occlude_y, &clip_y) {
857                         if let Some(visible) = overlaps(&occlude_x, &clip_x) {
858                             set_x_range(clip_rect, &visible);
859                             if surface.is_opaque && occlude_y == clip_y {
860                                 occlude_x = union(occlude_x, visible);
861                             }
862                         }
863                     }
864                 }
865             }
866         }
867     }
868 
869     /// Reset tile dependency state for a new frame.
reset_overlaps(&mut self)870     fn reset_overlaps(&mut self) {
871         for surface in self.surfaces.values_mut() {
872             for tile in &mut surface.tiles {
873                 tile.overlaps.set(0);
874                 tile.invalid.set(false);
875                 tile.graph_node.reset();
876             }
877         }
878     }
879 
880     /// Computes an overlap count for a tile that falls within the given composite
881     /// destination rectangle. This requires checking all surfaces currently queued for
882     /// composition so far in this frame and seeing if they have any invalidated tiles
883     /// whose destination rectangles would also overlap the supplied tile. If so, then the
884     /// increment the overlap count to account for all such dependencies on invalid tiles.
885     /// Tiles with the same overlap count will still be drawn with a stable ordering in
886     /// the order the surfaces were queued, so it is safe to ignore other possible sources
887     /// of composition ordering dependencies, as the later queued tile will still be drawn
888     /// later than the blocking tiles within that stable order. We assume that the tile's
889     /// surface hasn't yet been added to the current frame list of surfaces to composite
890     /// so that we only process potential blockers from surfaces that would come earlier
891     /// in composition.
init_overlaps( &self, overlap_id: &NativeSurfaceId, overlap_surface: &SwSurface, overlap_tile: &SwTile, overlap_transform: &CompositorSurfaceTransform, overlap_clip_rect: &DeviceIntRect, )892     fn init_overlaps(
893         &self,
894         overlap_id: &NativeSurfaceId,
895         overlap_surface: &SwSurface,
896         overlap_tile: &SwTile,
897         overlap_transform: &CompositorSurfaceTransform,
898         overlap_clip_rect: &DeviceIntRect,
899     ) {
900         // Record an extra overlap for an invalid tile to track the tile's dependency
901         // on its own future update.
902         let mut overlaps = if overlap_tile.invalid.get() { 1 } else { 0 };
903 
904         let overlap_rect = match overlap_tile.overlap_rect(overlap_surface, overlap_transform, overlap_clip_rect) {
905             Some(overlap_rect) => overlap_rect,
906             None => {
907                 overlap_tile.overlaps.set(overlaps);
908                 return;
909             }
910         };
911 
912         for &(ref id, ref transform, ref clip_rect, _) in &self.frame_surfaces {
913             // We only want to consider surfaces that were added before the current one we're
914             // checking for overlaps. If we find that surface, then we're done.
915             if id == overlap_id {
916                 break;
917             }
918             // If the surface's clip rect doesn't overlap the tile's rect,
919             // then there is no need to check any tiles within the surface.
920             if !overlap_rect.intersects(clip_rect) {
921                 continue;
922             }
923             if let Some(surface) = self.surfaces.get(id) {
924                 for tile in &surface.tiles {
925                     // If there is a deferred tile that might overlap the destination rectangle,
926                     // record the overlap.
927                     if tile.may_overlap(surface, transform, clip_rect, &overlap_rect) {
928                         if tile.overlaps.get() > 0 {
929                             overlaps += 1;
930                         }
931                         // Regardless of whether this tile is deferred, if it has dependency
932                         // overlaps, then record that it is potentially a dependency parent.
933                         tile.graph_node.get_mut().add_child(overlap_tile.graph_node.clone());
934                     }
935                 }
936             }
937         }
938         if overlaps > 0 {
939             // Has a dependency on some invalid tiles, so need to defer composition.
940             overlap_tile.overlaps.set(overlaps);
941         }
942     }
943 
944     /// Helper function that queues a composite job to the current locked framebuffer
queue_composite( &self, surface: &SwSurface, transform: &CompositorSurfaceTransform, clip_rect: &DeviceIntRect, filter: ImageRendering, tile: &SwTile, job_queue: &mut SwCompositeJobQueue, )945     fn queue_composite(
946         &self,
947         surface: &SwSurface,
948         transform: &CompositorSurfaceTransform,
949         clip_rect: &DeviceIntRect,
950         filter: ImageRendering,
951         tile: &SwTile,
952         job_queue: &mut SwCompositeJobQueue,
953     ) {
954         if let Some(ref composite_thread) = self.composite_thread {
955             if let Some((src_rect, dst_rect, flip_y)) = tile.composite_rects(surface, transform, clip_rect) {
956                 let source = if surface.external_image.is_some() {
957                     // If the surface has an attached external image, lock any textures supplied in the descriptor.
958                     match surface.composite_surface {
959                         Some(ref info) => match info.yuv_planes {
960                             0 => match self.gl.lock_texture(info.textures[0]) {
961                                 Some(texture) => SwCompositeSource::BGRA(texture),
962                                 None => return,
963                             },
964                             3 => match (
965                                 self.gl.lock_texture(info.textures[0]),
966                                 self.gl.lock_texture(info.textures[1]),
967                                 self.gl.lock_texture(info.textures[2]),
968                             ) {
969                                 (Some(y_texture), Some(u_texture), Some(v_texture)) => SwCompositeSource::YUV(
970                                     y_texture,
971                                     u_texture,
972                                     v_texture,
973                                     info.color_space,
974                                     info.color_depth,
975                                 ),
976                                 _ => return,
977                             },
978                             _ => panic!("unsupported number of YUV planes: {}", info.yuv_planes),
979                         },
980                         None => return,
981                     }
982                 } else if let Some(texture) = self.gl.lock_texture(tile.color_id) {
983                     // Lock the texture representing the picture cache tile.
984                     SwCompositeSource::BGRA(texture)
985                 } else {
986                     return;
987                 };
988                 if let Some(ref framebuffer) = self.locked_framebuffer {
989                     composite_thread.queue_composite(
990                         source,
991                         framebuffer.clone(),
992                         src_rect,
993                         dst_rect,
994                         *clip_rect,
995                         surface.is_opaque,
996                         flip_y,
997                         filter,
998                         tile.graph_node.clone(),
999                         job_queue,
1000                     );
1001                 }
1002             }
1003         }
1004     }
1005 
1006     /// Lock a surface with an attached external image for compositing.
try_lock_composite_surface(&mut self, id: &NativeSurfaceId)1007     fn try_lock_composite_surface(&mut self, id: &NativeSurfaceId) {
1008         if let Some(surface) = self.surfaces.get_mut(id) {
1009             if let Some(external_image) = surface.external_image {
1010                 // If the surface has an attached external image, attempt to lock the external image
1011                 // for compositing. Yields a descriptor of textures and data necessary for their
1012                 // interpretation on success.
1013                 let mut info = SWGLCompositeSurfaceInfo {
1014                     yuv_planes: 0,
1015                     textures: [0; 3],
1016                     color_space: YuvRangedColorSpace::GbrIdentity,
1017                     color_depth: ColorDepth::Color8,
1018                     size: DeviceIntSize::zero(),
1019                 };
1020                 assert!(!surface.tiles.is_empty());
1021                 let mut tile = &mut surface.tiles[0];
1022                 if self.compositor.lock_composite_surface(self.gl.into(), external_image, &mut info) {
1023                     tile.valid_rect = DeviceIntRect::from_size(info.size);
1024                     surface.composite_surface = Some(info);
1025                 } else {
1026                     tile.valid_rect = DeviceIntRect::zero();
1027                     surface.composite_surface = None;
1028                 }
1029             }
1030         }
1031     }
1032 
1033     /// Look for any attached external images that have been locked and then unlock them.
unlock_composite_surfaces(&mut self)1034     fn unlock_composite_surfaces(&mut self) {
1035         for &(ref id, _, _, _) in self.frame_surfaces.iter().chain(self.late_surfaces.iter()) {
1036             if let Some(surface) = self.surfaces.get_mut(id) {
1037                 if let Some(external_image) = surface.external_image {
1038                     if surface.composite_surface.is_some() {
1039                         self.compositor.unlock_composite_surface(self.gl.into(), external_image);
1040                         surface.composite_surface = None;
1041                     }
1042                 }
1043             }
1044         }
1045     }
1046 
1047     /// Issue composites for any tiles that are no longer blocked following a tile update.
1048     /// We process all surfaces and tiles in the order they were queued.
flush_composites(&self, tile_id: &NativeTileId, surface: &SwSurface, tile: &SwTile)1049     fn flush_composites(&self, tile_id: &NativeTileId, surface: &SwSurface, tile: &SwTile) {
1050         let composite_thread = match &self.composite_thread {
1051             Some(composite_thread) => composite_thread,
1052             None => return,
1053         };
1054 
1055         // Look for the tile in the frame list and composite it if it has no dependencies.
1056         let mut frame_surfaces = self
1057             .frame_surfaces
1058             .iter()
1059             .skip_while(|&(ref id, _, _, _)| *id != tile_id.surface_id);
1060         let (overlap_rect, mut lock) = match frame_surfaces.next() {
1061             Some(&(_, ref transform, ref clip_rect, filter)) => {
1062                 // Remove invalid tile's update dependency.
1063                 if tile.invalid.get() {
1064                     tile.overlaps.set(tile.overlaps.get() - 1);
1065                 }
1066                 // If the tile still has overlaps, keep deferring it till later.
1067                 if tile.overlaps.get() > 0 {
1068                     return;
1069                 }
1070                 // Otherwise, the tile's dependencies are all resolved, so composite it.
1071                 let mut lock = composite_thread.lock();
1072                 self.queue_composite(surface, transform, clip_rect, filter, tile, &mut lock);
1073                 // Finally, get the tile's overlap rect used for tracking dependencies
1074                 match tile.overlap_rect(surface, transform, clip_rect) {
1075                     Some(overlap_rect) => (overlap_rect, lock),
1076                     None => return,
1077                 }
1078             }
1079             None => return,
1080         };
1081 
1082         // Accumulate rects whose dependencies have been satisfied from this update.
1083         // Store the union of all these bounds to quickly reject unaffected tiles.
1084         let mut flushed_bounds = overlap_rect;
1085         let mut flushed_rects = vec![overlap_rect];
1086 
1087         // Check surfaces following the update in the frame list and see if they would overlap it.
1088         for &(ref id, ref transform, ref clip_rect, filter) in frame_surfaces {
1089             // If the clip rect doesn't overlap the conservative bounds, we can skip the whole surface.
1090             if !flushed_bounds.intersects(clip_rect) {
1091                 continue;
1092             }
1093             if let Some(surface) = self.surfaces.get(&id) {
1094                 // Search through the surface's tiles for any blocked on this update and queue jobs for them.
1095                 for tile in &surface.tiles {
1096                     let mut overlaps = tile.overlaps.get();
1097                     // Only check tiles that have existing unresolved dependencies
1098                     if overlaps == 0 {
1099                         continue;
1100                     }
1101                     // Get this tile's overlap rect for tracking dependencies
1102                     let overlap_rect = match tile.overlap_rect(surface, transform, clip_rect) {
1103                         Some(overlap_rect) => overlap_rect,
1104                         None => continue,
1105                     };
1106                     // Do a quick check to see if the tile overlaps the conservative bounds.
1107                     if !overlap_rect.intersects(&flushed_bounds) {
1108                         continue;
1109                     }
1110                     // Decrement the overlap count if this tile is dependent on any flushed rects.
1111                     for flushed_rect in &flushed_rects {
1112                         if overlap_rect.intersects(flushed_rect) {
1113                             overlaps -= 1;
1114                         }
1115                     }
1116                     if overlaps != tile.overlaps.get() {
1117                         // If the overlap count changed, this tile had a dependency on some flush rects.
1118                         // If the count hit zero, it is ready to composite.
1119                         tile.overlaps.set(overlaps);
1120                         if overlaps == 0 {
1121                             self.queue_composite(surface, transform, clip_rect, filter, tile, &mut lock);
1122                             // Record that the tile got flushed to update any downwind dependencies.
1123                             flushed_bounds = flushed_bounds.union(&overlap_rect);
1124                             flushed_rects.push(overlap_rect);
1125                         }
1126                     }
1127                 }
1128             }
1129         }
1130     }
1131 }
1132 
1133 impl Compositor for SwCompositor {
create_surface( &mut self, id: NativeSurfaceId, virtual_offset: DeviceIntPoint, tile_size: DeviceIntSize, is_opaque: bool, )1134     fn create_surface(
1135         &mut self,
1136         id: NativeSurfaceId,
1137         virtual_offset: DeviceIntPoint,
1138         tile_size: DeviceIntSize,
1139         is_opaque: bool,
1140     ) {
1141         if self.use_native_compositor {
1142             self.compositor.create_surface(id, virtual_offset, tile_size, is_opaque);
1143         }
1144         self.max_tile_size = DeviceIntSize::new(
1145             self.max_tile_size.width.max(tile_size.width),
1146             self.max_tile_size.height.max(tile_size.height),
1147         );
1148         self.surfaces.insert(id, SwSurface::new(tile_size, is_opaque));
1149     }
1150 
create_external_surface(&mut self, id: NativeSurfaceId, is_opaque: bool)1151     fn create_external_surface(&mut self, id: NativeSurfaceId, is_opaque: bool) {
1152         if self.use_native_compositor {
1153             self.compositor.create_external_surface(id, is_opaque);
1154         }
1155         self.surfaces
1156             .insert(id, SwSurface::new(DeviceIntSize::zero(), is_opaque));
1157     }
1158 
destroy_surface(&mut self, id: NativeSurfaceId)1159     fn destroy_surface(&mut self, id: NativeSurfaceId) {
1160         if let Some(surface) = self.surfaces.remove(&id) {
1161             self.deinit_surface(&surface);
1162         }
1163         if self.use_native_compositor {
1164             self.compositor.destroy_surface(id);
1165         }
1166     }
1167 
deinit(&mut self)1168     fn deinit(&mut self) {
1169         if let Some(ref composite_thread) = self.composite_thread {
1170             composite_thread.deinit();
1171         }
1172 
1173         for surface in self.surfaces.values() {
1174             self.deinit_surface(surface);
1175         }
1176 
1177         self.gl.delete_textures(&[self.depth_id]);
1178 
1179         if self.use_native_compositor {
1180             self.compositor.deinit();
1181         }
1182     }
1183 
create_tile(&mut self, id: NativeTileId)1184     fn create_tile(&mut self, id: NativeTileId) {
1185         if self.use_native_compositor {
1186             self.compositor.create_tile(id);
1187         }
1188         if let Some(surface) = self.surfaces.get_mut(&id.surface_id) {
1189             let mut tile = SwTile::new(id.x, id.y);
1190             tile.color_id = self.gl.gen_textures(1)[0];
1191             tile.fbo_id = self.gl.gen_framebuffers(1)[0];
1192             let mut prev_fbo = [0];
1193             unsafe {
1194                 self.gl.get_integer_v(gl::DRAW_FRAMEBUFFER_BINDING, &mut prev_fbo);
1195             }
1196             self.gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, tile.fbo_id);
1197             self.gl.framebuffer_texture_2d(
1198                 gl::DRAW_FRAMEBUFFER,
1199                 gl::COLOR_ATTACHMENT0,
1200                 gl::TEXTURE_2D,
1201                 tile.color_id,
1202                 0,
1203             );
1204             self.gl.framebuffer_texture_2d(
1205                 gl::DRAW_FRAMEBUFFER,
1206                 gl::DEPTH_ATTACHMENT,
1207                 gl::TEXTURE_2D,
1208                 self.depth_id,
1209                 0,
1210             );
1211             self.gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, prev_fbo[0] as gl::GLuint);
1212 
1213             surface.tiles.push(tile);
1214         }
1215     }
1216 
destroy_tile(&mut self, id: NativeTileId)1217     fn destroy_tile(&mut self, id: NativeTileId) {
1218         if let Some(surface) = self.surfaces.get_mut(&id.surface_id) {
1219             if let Some(idx) = surface.tiles.iter().position(|t| t.x == id.x && t.y == id.y) {
1220                 let tile = surface.tiles.remove(idx);
1221                 self.deinit_tile(&tile);
1222             }
1223         }
1224         if self.use_native_compositor {
1225             self.compositor.destroy_tile(id);
1226         }
1227     }
1228 
attach_external_image(&mut self, id: NativeSurfaceId, external_image: ExternalImageId)1229     fn attach_external_image(&mut self, id: NativeSurfaceId, external_image: ExternalImageId) {
1230         if self.use_native_compositor {
1231             self.compositor.attach_external_image(id, external_image);
1232         }
1233         if let Some(surface) = self.surfaces.get_mut(&id) {
1234             // Surfaces with attached external images have a single tile at the origin encompassing
1235             // the entire surface.
1236             assert!(surface.tile_size.is_empty());
1237             surface.external_image = Some(external_image);
1238             if surface.tiles.is_empty() {
1239                 surface.tiles.push(SwTile::new(0, 0));
1240             }
1241         }
1242     }
1243 
invalidate_tile(&mut self, id: NativeTileId, valid_rect: DeviceIntRect)1244     fn invalidate_tile(&mut self, id: NativeTileId, valid_rect: DeviceIntRect) {
1245         if self.use_native_compositor {
1246             self.compositor.invalidate_tile(id, valid_rect);
1247         }
1248         if let Some(surface) = self.surfaces.get_mut(&id.surface_id) {
1249             if let Some(tile) = surface.tiles.iter_mut().find(|t| t.x == id.x && t.y == id.y) {
1250                 tile.invalid.set(true);
1251                 tile.valid_rect = valid_rect;
1252             }
1253         }
1254     }
1255 
bind(&mut self, id: NativeTileId, dirty_rect: DeviceIntRect, valid_rect: DeviceIntRect) -> NativeSurfaceInfo1256     fn bind(&mut self, id: NativeTileId, dirty_rect: DeviceIntRect, valid_rect: DeviceIntRect) -> NativeSurfaceInfo {
1257         let mut surface_info = NativeSurfaceInfo {
1258             origin: DeviceIntPoint::zero(),
1259             fbo_id: 0,
1260         };
1261 
1262         self.cur_tile = id;
1263 
1264         if let Some(surface) = self.surfaces.get_mut(&id.surface_id) {
1265             if let Some(tile) = surface.tiles.iter_mut().find(|t| t.x == id.x && t.y == id.y) {
1266                 assert_eq!(tile.valid_rect, valid_rect);
1267                 if valid_rect.is_empty() {
1268                     return surface_info;
1269                 }
1270 
1271                 let mut stride = 0;
1272                 let mut buf = ptr::null_mut();
1273                 if self.use_native_compositor {
1274                     if let Some(tile_info) = self.compositor.map_tile(id, dirty_rect, valid_rect) {
1275                         stride = tile_info.stride;
1276                         buf = tile_info.data;
1277                     }
1278                 }
1279                 self.gl.set_texture_buffer(
1280                     tile.color_id,
1281                     gl::RGBA8,
1282                     valid_rect.width(),
1283                     valid_rect.height(),
1284                     stride,
1285                     buf,
1286                     surface.tile_size.width,
1287                     surface.tile_size.height,
1288                 );
1289                 // Reallocate the shared depth buffer to fit the valid rect, but within
1290                 // a buffer sized to actually fit at least the maximum possible tile size.
1291                 // The maximum tile size is supplied to avoid reallocation by ensuring the
1292                 // allocated buffer is actually big enough to accommodate the largest tile
1293                 // size requested by any used surface, even though supplied valid rect may
1294                 // actually be much smaller than this. This will only force a texture
1295                 // reallocation inside SWGL if the maximum tile size has grown since the
1296                 // last time it was supplied, instead simply reusing the buffer if the max
1297                 // tile size is not bigger than what was previously allocated.
1298                 self.gl.set_texture_buffer(
1299                     self.depth_id,
1300                     gl::DEPTH_COMPONENT,
1301                     valid_rect.width(),
1302                     valid_rect.height(),
1303                     0,
1304                     ptr::null_mut(),
1305                     self.max_tile_size.width,
1306                     self.max_tile_size.height,
1307                 );
1308                 surface_info.fbo_id = tile.fbo_id;
1309                 surface_info.origin -= valid_rect.min.to_vector();
1310             }
1311         }
1312 
1313         surface_info
1314     }
1315 
unbind(&mut self)1316     fn unbind(&mut self) {
1317         let id = self.cur_tile;
1318         if let Some(surface) = self.surfaces.get(&id.surface_id) {
1319             if let Some(tile) = surface.tiles.iter().find(|t| t.x == id.x && t.y == id.y) {
1320                 if tile.valid_rect.is_empty() {
1321                     // If we didn't actually render anything, then just queue any
1322                     // dependencies.
1323                     self.flush_composites(&id, surface, tile);
1324                     return;
1325                 }
1326 
1327                 // Force any delayed clears to be resolved.
1328                 self.gl.resolve_framebuffer(tile.fbo_id);
1329 
1330                 if self.use_native_compositor {
1331                     self.compositor.unmap_tile();
1332                 } else {
1333                     // If we're not relying on a native compositor, then composite
1334                     // any tiles that are dependent on this tile being updated but
1335                     // are otherwise ready to composite.
1336                     self.flush_composites(&id, surface, tile);
1337                 }
1338             }
1339         }
1340     }
1341 
begin_frame(&mut self)1342     fn begin_frame(&mut self) {
1343         self.reset_overlaps();
1344 
1345         if self.use_native_compositor {
1346             self.compositor.begin_frame();
1347         }
1348     }
1349 
add_surface( &mut self, id: NativeSurfaceId, transform: CompositorSurfaceTransform, clip_rect: DeviceIntRect, filter: ImageRendering, )1350     fn add_surface(
1351         &mut self,
1352         id: NativeSurfaceId,
1353         transform: CompositorSurfaceTransform,
1354         clip_rect: DeviceIntRect,
1355         filter: ImageRendering,
1356     ) {
1357         if self.use_native_compositor {
1358             self.compositor.add_surface(id, transform, clip_rect, filter);
1359         }
1360 
1361         if self.composite_thread.is_some() {
1362             // If the surface has an attached external image, try to lock that now.
1363             self.try_lock_composite_surface(&id);
1364 
1365             // If we're already busy compositing, then add to the queue of late
1366             // surfaces instead of trying to sort into the main frame queue.
1367             // These late surfaces will not have any overlap tracking done for
1368             // them and must be processed synchronously at the end of the frame.
1369             if self.composite_thread.as_ref().unwrap().is_busy_compositing() {
1370                 self.late_surfaces.push((id, transform, clip_rect, filter));
1371                 return;
1372             }
1373         }
1374 
1375         self.frame_surfaces.push((id, transform, clip_rect, filter));
1376     }
1377 
1378     /// Now that all the dependency graph nodes have been built, start queuing
1379     /// composition jobs. Any surfaces that get added after this point in the
1380     /// frame will not have overlap dependencies assigned and so must instead
1381     /// be added to the late_surfaces queue to be processed at the end of the
1382     /// frame.
start_compositing(&mut self, clear_color: ColorF, dirty_rects: &[DeviceIntRect], _opaque_rects: &[DeviceIntRect])1383     fn start_compositing(&mut self, clear_color: ColorF, dirty_rects: &[DeviceIntRect], _opaque_rects: &[DeviceIntRect]) {
1384         // Opaque rects are currently only computed here, not by WR itself, so we
1385         // ignore the passed parameter and forward our own version onto the native
1386         // compositor.
1387         let mut opaque_rects: Vec<DeviceIntRect> = Vec::new();
1388         for &(ref id, ref transform, ref clip_rect, _filter) in &self.frame_surfaces {
1389             if let Some(surface) = self.surfaces.get(id) {
1390                 if !surface.is_opaque {
1391                     continue;
1392                 }
1393 
1394                 for tile in &surface.tiles {
1395                     if let Some(rect) = tile.overlap_rect(surface, transform, clip_rect) {
1396                         opaque_rects.push(rect);
1397                     }
1398                 }
1399             }
1400         }
1401 
1402         self.compositor.start_compositing(clear_color, dirty_rects, &opaque_rects);
1403 
1404         if let Some(dirty_rect) = dirty_rects
1405             .iter()
1406             .fold(DeviceIntRect::zero(), |acc, dirty_rect| acc.union(dirty_rect))
1407             .to_non_empty()
1408         {
1409             // Factor dirty rect into surface clip rects
1410             for &mut (_, _, ref mut clip_rect, _) in &mut self.frame_surfaces {
1411                 *clip_rect = clip_rect.intersection(&dirty_rect).unwrap_or_default();
1412             }
1413         }
1414 
1415         self.occlude_surfaces();
1416 
1417         // Discard surfaces that are entirely clipped out
1418         self.frame_surfaces
1419             .retain(|&(_, _, clip_rect, _)| !clip_rect.is_empty());
1420 
1421         if let Some(ref composite_thread) = self.composite_thread {
1422             // Compute overlap dependencies for surfaces.
1423             for &(ref id, ref transform, ref clip_rect, _filter) in &self.frame_surfaces {
1424                 if let Some(surface) = self.surfaces.get(id) {
1425                     for tile in &surface.tiles {
1426                         self.init_overlaps(id, surface, tile, transform, clip_rect);
1427                     }
1428                 }
1429             }
1430 
1431             self.locked_framebuffer = self.gl.lock_framebuffer(0);
1432 
1433             composite_thread.prepare_for_composites();
1434 
1435             // Issue any initial composite jobs for the SwComposite thread.
1436             let mut lock = composite_thread.lock();
1437             for &(ref id, ref transform, ref clip_rect, filter) in &self.frame_surfaces {
1438                 if let Some(surface) = self.surfaces.get(id) {
1439                     for tile in &surface.tiles {
1440                         if tile.overlaps.get() == 0 {
1441                             // Not dependent on any tiles, so go ahead and composite now.
1442                             self.queue_composite(surface, transform, clip_rect, filter, tile, &mut lock);
1443                         }
1444                     }
1445                 }
1446             }
1447         }
1448     }
1449 
end_frame(&mut self)1450     fn end_frame(&mut self) {
1451         if self.use_native_compositor {
1452             self.compositor.end_frame();
1453         } else if let Some(ref composite_thread) = self.composite_thread {
1454             // Need to wait for the SwComposite thread to finish any queued jobs.
1455             composite_thread.wait_for_composites(false);
1456 
1457             if !self.late_surfaces.is_empty() {
1458                 // All of the main frame surface have been processed by now. But if there
1459                 // are any late surfaces, we need to kick off a new synchronous composite
1460                 // phase. These late surfaces don't have any overlap/dependency tracking,
1461                 // so we just queue them directly and wait synchronously for the composite
1462                 // thread to process them in order.
1463                 composite_thread.prepare_for_composites();
1464                 {
1465                     let mut lock = composite_thread.lock();
1466                     for &(ref id, ref transform, ref clip_rect, filter) in &self.late_surfaces {
1467                         if let Some(surface) = self.surfaces.get(id) {
1468                             for tile in &surface.tiles {
1469                                 self.queue_composite(surface, transform, clip_rect, filter, tile, &mut lock);
1470                             }
1471                         }
1472                     }
1473                 }
1474                 composite_thread.wait_for_composites(true);
1475             }
1476 
1477             self.locked_framebuffer = None;
1478 
1479             self.unlock_composite_surfaces();
1480         }
1481 
1482         self.frame_surfaces.clear();
1483         self.late_surfaces.clear();
1484 
1485         self.reset_overlaps();
1486     }
1487 
enable_native_compositor(&mut self, enable: bool)1488     fn enable_native_compositor(&mut self, enable: bool) {
1489         // TODO: The SwComposite thread is not properly instantiated if this is
1490         // ever actually toggled.
1491         assert_eq!(self.use_native_compositor, enable);
1492         self.compositor.enable_native_compositor(enable);
1493         self.use_native_compositor = enable;
1494     }
1495 
get_capabilities(&self) -> CompositorCapabilities1496     fn get_capabilities(&self) -> CompositorCapabilities {
1497         self.compositor.get_capabilities()
1498     }
1499 }
1500