commit a25f1d8c2a59aa7d0200a52c8c3763ac6c09fde8
parent a0b5057dab54973a089db7b096bea12f6a252661
Author: Cristian Tuns <ctuns@mozilla.com>
Date: Mon, 5 Jan 2026 05:58:02 -0500
Revert "Bug 2006108 - Extract most of the compositing code out of renderer/mod.rs. r=gfx-reviewers,gw" for causing build bustages with ExternalImageSource
This reverts commit 34d11e544e548c8bc9972b2a304bcee4ea2140c2.
Revert "Bug 2006108 - Extract external image code from renderer/mod.rs. r=gfx-reviewers,gw" for causing build bustages with ExternalImageSource
This reverts commit 024c331d79ccdbd74fa177b1c0be6f710b5e658f.
Diffstat:
3 files changed, 1487 insertions(+), 1578 deletions(-)
diff --git a/gfx/wr/webrender/src/renderer/composite.rs b/gfx/wr/webrender/src/renderer/composite.rs
@@ -1,1454 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-use api::units::*;
-use api::{ColorF, ImageBufferKind, ImageRendering, PremultipliedColorF};
-use crate::batch::BatchTextures;
-use crate::composite::{
- CompositeState, CompositeSurfaceFormat, CompositeTileSurface, CompositorConfig,
- CompositorInputLayer, CompositorSurfaceUsage, CompositorSurfaceTransform,
- CompositeRoundedCorner, NativeTileId, ResolvedExternalSurface,
- ResolvedExternalSurfaceColorData, ClipRadius, CompositeFeatures, CompositorKind, TileKind,
-};
-use crate::frame_builder::Frame;
-use crate::{CompositorInputConfig, PictureCacheDebugInfo, debug_colors};
-use api::{ClipMode, DebugFlags};
-use std::collections::HashSet;
-use std::mem;
-use crate::debug_item::DebugItem;
-use crate::segment::EdgeAaSegmentMask;
-use crate::device::DrawTarget;
-use crate::gpu_types::{CompositeInstance, ZBufferId};
-use crate::internal_types::{FastHashMap, TextureSource};
-use crate::picture::ResolvedSurfaceTexture;
-use super::RenderResults;
-use crate::profiler::{self};
-use crate::rectangle_occlusion as occlusion;
-use crate::renderer::{
- GPU_SAMPLER_TAG_OPAQUE, GPU_SAMPLER_TAG_TRANSPARENT, GPU_TAG_COMPOSITE, PartialPresentMode,
-};
-use crate::renderer::{FramebufferKind, Renderer, RendererStats, VertexArrayKind};
-use crate::segment::SegmentBuilder;
-use crate::tile_cache::TileId;
-use euclid::{Scale, Transform3D, default};
-
-#[derive(Debug, Copy, Clone)]
-pub(super) struct OcclusionItemKey {
- pub tile_index: usize,
- pub needs_mask: bool,
-}
-
-pub(super) struct SwapChainLayer {
- pub occlusion: occlusion::FrontToBackBuilder<OcclusionItemKey>,
-}
-
-pub(super) struct CompositeTileState {
- pub local_rect: PictureRect,
- pub local_valid_rect: PictureRect,
- pub device_clip_rect: DeviceRect,
- pub z_id: ZBufferId,
- pub device_tile_box: DeviceRect,
- pub visible_rects: Vec<DeviceRect>,
-}
-
-impl CompositeTileState {
- pub fn same_state(&self, other: &CompositeTileState) -> bool {
- self.local_rect == other.local_rect
- && self.local_valid_rect == other.local_valid_rect
- && self.device_clip_rect == other.device_clip_rect
- && self.z_id == other.z_id
- && self.device_tile_box == other.device_tile_box
- }
-}
-
-pub(super) struct LayerCompositorFrameState {
- pub tile_states: FastHashMap<TileId, CompositeTileState>,
- pub rects_without_id: Vec<DeviceRect>,
-}
-
-impl Renderer {
- pub(super) fn update_external_native_surfaces(
- &mut self,
- external_surfaces: &[ResolvedExternalSurface],
- results: &mut RenderResults,
- ) {
- if external_surfaces.is_empty() {
- return;
- }
-
- let opaque_sampler = self.gpu_profiler.start_sampler(GPU_SAMPLER_TAG_OPAQUE);
-
- self.device.disable_depth();
- self.set_blend(false, FramebufferKind::Main);
-
- for surface in external_surfaces {
- let (native_surface_id, surface_size) = match surface.update_params {
- Some(params) => params,
- None => continue,
- };
-
- let surface_rect = surface_size.into();
-
- let surface_info = self.compositor_config.compositor().unwrap().bind(
- &mut self.device,
- NativeTileId {
- surface_id: native_surface_id,
- x: 0,
- y: 0,
- },
- surface_rect,
- surface_rect,
- );
-
- let draw_target = DrawTarget::NativeSurface {
- offset: surface_info.origin,
- external_fbo_id: surface_info.fbo_id,
- dimensions: surface_size,
- };
- self.device.bind_draw_target(draw_target);
-
- let projection = Transform3D::ortho(
- 0.0,
- surface_size.width as f32,
- 0.0,
- surface_size.height as f32,
- self.device.ortho_near_plane(),
- self.device.ortho_far_plane(),
- );
-
- let (textures, instance) = match surface.color_data {
- ResolvedExternalSurfaceColorData::Yuv {
- ref planes,
- color_space,
- format,
- channel_bit_depth,
- ..
- } => {
- let textures = BatchTextures::composite_yuv(
- planes[0].texture,
- planes[1].texture,
- planes[2].texture,
- );
-
- let uv_rects = [
- self.texture_resolver
- .get_uv_rect(&textures.input.colors[0], planes[0].uv_rect),
- self.texture_resolver
- .get_uv_rect(&textures.input.colors[1], planes[1].uv_rect),
- self.texture_resolver
- .get_uv_rect(&textures.input.colors[2], planes[2].uv_rect),
- ];
-
- let instance = CompositeInstance::new_yuv(
- surface_rect.to_f32(),
- surface_rect.to_f32(),
- color_space,
- format,
- channel_bit_depth,
- uv_rects,
- (false, false),
- None,
- );
-
- self.shaders
- .borrow_mut()
- .get_composite_shader(
- CompositeSurfaceFormat::Yuv,
- surface.image_buffer_kind,
- instance.get_yuv_features(),
- )
- .bind(
- &mut self.device,
- &projection,
- None,
- &mut self.renderer_errors,
- &mut self.profile,
- &mut self.command_log,
- );
-
- (textures, instance)
- }
- ResolvedExternalSurfaceColorData::Rgb { ref plane, .. } => {
- let textures = BatchTextures::composite_rgb(plane.texture);
- let uv_rect = self
- .texture_resolver
- .get_uv_rect(&textures.input.colors[0], plane.uv_rect);
- let instance = CompositeInstance::new_rgb(
- surface_rect.to_f32(),
- surface_rect.to_f32(),
- PremultipliedColorF::WHITE,
- uv_rect,
- plane.texture.uses_normalized_uvs(),
- (false, false),
- None,
- );
- let features = instance.get_rgb_features();
-
- self.shaders
- .borrow_mut()
- .get_composite_shader(
- CompositeSurfaceFormat::Rgba,
- surface.image_buffer_kind,
- features,
- )
- .bind(
- &mut self.device,
- &projection,
- None,
- &mut self.renderer_errors,
- &mut self.profile,
- &mut self.command_log,
- );
-
- (textures, instance)
- }
- };
-
- self.draw_instanced_batch(
- &[instance],
- VertexArrayKind::Composite,
- &textures,
- &mut results.stats,
- );
-
- self.compositor_config
- .compositor()
- .unwrap()
- .unbind(&mut self.device);
- }
-
- self.gpu_profiler.finish_sampler(opaque_sampler);
- }
-
- #[inline]
- pub(super) fn draw_tile_list<'a, I: Iterator<Item = &'a occlusion::Item<OcclusionItemKey>>>(
- &mut self,
- tiles_iter: I,
- composite_state: &CompositeState,
- external_surfaces: &[ResolvedExternalSurface],
- projection: &default::Transform3D<f32>,
- stats: &mut RendererStats,
- ) {
- let mut current_shader_params = (
- CompositeSurfaceFormat::Rgba,
- ImageBufferKind::Texture2D,
- CompositeFeatures::empty(),
- None,
- );
- let mut current_textures = BatchTextures::empty();
- let mut instances = Vec::new();
-
- self.shaders
- .borrow_mut()
- .get_composite_shader(
- current_shader_params.0,
- current_shader_params.1,
- current_shader_params.2,
- )
- .bind(
- &mut self.device,
- projection,
- None,
- &mut self.renderer_errors,
- &mut self.profile,
- &mut self.command_log,
- );
-
- for item in tiles_iter {
- let tile = &composite_state.tiles[item.key.tile_index];
-
- let clip_rect = item.rectangle;
- let tile_rect = composite_state.get_device_rect(&tile.local_rect, tile.transform_index);
- let transform = composite_state.get_device_transform(tile.transform_index);
- let flip = (transform.scale.x < 0.0, transform.scale.y < 0.0);
-
- let clip = if item.key.needs_mask {
- tile.clip_index
- .map(|index| composite_state.get_compositor_clip(index))
- } else {
- None
- };
-
- let (instance, textures, shader_params) = match tile.surface {
- CompositeTileSurface::Color { color } => {
- let dummy = TextureSource::Dummy;
- let image_buffer_kind = dummy.image_buffer_kind();
- let instance = CompositeInstance::new(
- tile_rect,
- clip_rect,
- color.premultiplied(),
- flip,
- clip,
- );
- let features = instance.get_rgb_features();
- (
- instance,
- BatchTextures::composite_rgb(dummy),
- (
- CompositeSurfaceFormat::Rgba,
- image_buffer_kind,
- features,
- None,
- ),
- )
- }
- CompositeTileSurface::Texture {
- surface: ResolvedSurfaceTexture::TextureCache { texture },
- } => {
- let instance = CompositeInstance::new(
- tile_rect,
- clip_rect,
- PremultipliedColorF::WHITE,
- flip,
- clip,
- );
- let features = instance.get_rgb_features();
- (
- instance,
- BatchTextures::composite_rgb(texture),
- (
- CompositeSurfaceFormat::Rgba,
- ImageBufferKind::Texture2D,
- features,
- None,
- ),
- )
- }
- CompositeTileSurface::ExternalSurface {
- external_surface_index,
- } => {
- let surface = &external_surfaces[external_surface_index.0];
-
- match surface.color_data {
- ResolvedExternalSurfaceColorData::Yuv {
- ref planes,
- color_space,
- format,
- channel_bit_depth,
- ..
- } => {
- let textures = BatchTextures::composite_yuv(
- planes[0].texture,
- planes[1].texture,
- planes[2].texture,
- );
-
- let uv_rects = [
- self.texture_resolver
- .get_uv_rect(&textures.input.colors[0], planes[0].uv_rect),
- self.texture_resolver
- .get_uv_rect(&textures.input.colors[1], planes[1].uv_rect),
- self.texture_resolver
- .get_uv_rect(&textures.input.colors[2], planes[2].uv_rect),
- ];
-
- let instance = CompositeInstance::new_yuv(
- tile_rect,
- clip_rect,
- color_space,
- format,
- channel_bit_depth,
- uv_rects,
- flip,
- clip,
- );
- let features = instance.get_yuv_features();
-
- (
- instance,
- textures,
- (
- CompositeSurfaceFormat::Yuv,
- surface.image_buffer_kind,
- features,
- None,
- ),
- )
- }
- ResolvedExternalSurfaceColorData::Rgb { ref plane, .. } => {
- let uv_rect = self
- .texture_resolver
- .get_uv_rect(&plane.texture, plane.uv_rect);
- let instance = CompositeInstance::new_rgb(
- tile_rect,
- clip_rect,
- PremultipliedColorF::WHITE,
- uv_rect,
- plane.texture.uses_normalized_uvs(),
- flip,
- clip,
- );
- let features = instance.get_rgb_features();
- (
- instance,
- BatchTextures::composite_rgb(plane.texture),
- (
- CompositeSurfaceFormat::Rgba,
- surface.image_buffer_kind,
- features,
- Some(
- self.texture_resolver
- .get_texture_size(&plane.texture)
- .to_f32(),
- ),
- ),
- )
- }
- }
- }
- CompositeTileSurface::Texture {
- surface: ResolvedSurfaceTexture::Native { .. },
- } => {
- unreachable!("bug: found native surface in simple composite path");
- }
- };
-
- let flush_batch = !current_textures.is_compatible_with(&textures)
- || shader_params != current_shader_params;
-
- if flush_batch {
- if !instances.is_empty() {
- self.draw_instanced_batch(
- &instances,
- VertexArrayKind::Composite,
- ¤t_textures,
- stats,
- );
- instances.clear();
- }
- }
-
- if shader_params != current_shader_params {
- self.shaders
- .borrow_mut()
- .get_composite_shader(shader_params.0, shader_params.1, shader_params.2)
- .bind(
- &mut self.device,
- projection,
- shader_params.3,
- &mut self.renderer_errors,
- &mut self.profile,
- &mut self.command_log,
- );
-
- current_shader_params = shader_params;
- }
-
- current_textures = textures;
-
- instances.push(instance);
- }
-
- if !instances.is_empty() {
- self.draw_instanced_batch(
- &instances,
- VertexArrayKind::Composite,
- ¤t_textures,
- stats,
- );
- }
- }
-
- // Composite tiles in a swapchain. When using LayerCompositor, we may
- // split the compositing in to multiple swapchains.
- pub(super) fn composite_pass(
- &mut self,
- composite_state: &CompositeState,
- draw_target: DrawTarget,
- clear_color: ColorF,
- projection: &default::Transform3D<f32>,
- results: &mut RenderResults,
- partial_present_mode: Option<super::PartialPresentMode>,
- layer: &SwapChainLayer,
- ) {
- self.device.bind_draw_target(draw_target);
- self.device.disable_depth_write();
- self.device.disable_depth();
-
- // If using KHR_partial_update, call eglSetDamageRegion.
- // This must be called exactly once per frame, and prior to any rendering to the main
- // framebuffer. Additionally, on Mali-G77 we encountered rendering issues when calling
- // this earlier in the frame, during offscreen render passes. So call it now, immediately
- // before rendering to the main framebuffer. See bug 1685276 for details.
- if let Some(partial_present) = self.compositor_config.partial_present() {
- if let Some(super::PartialPresentMode::Single { dirty_rect }) = partial_present_mode {
- // We have a single dirty rect, so clear only that
- partial_present.set_buffer_damage_region(&[dirty_rect.to_i32()]);
- }
- }
-
- let clear_color = Some(clear_color.to_array());
-
- match partial_present_mode {
- Some(super::PartialPresentMode::Single { dirty_rect }) => {
- // There is no need to clear if the dirty rect is occluded. Additionally,
- // on Mali-G77 we have observed artefacts when calling glClear (even with
- // the empty scissor rect set) after calling eglSetDamageRegion with an
- // empty damage region. So avoid clearing in that case. See bug 1709548.
- if !dirty_rect.is_empty() && layer.occlusion.test(&dirty_rect) {
- self.device.clear_target(
- clear_color,
- None,
- Some(draw_target.to_framebuffer_rect(dirty_rect.to_i32())),
- );
- }
- }
- None => {
- // Partial present is disabled, so clear the entire framebuffer
- self.device.clear_target(clear_color, None, None);
- }
- }
-
- // Draw opaque tiles
- let opaque_items = layer.occlusion.opaque_items();
- if !opaque_items.is_empty() {
- let opaque_sampler = self.gpu_profiler.start_sampler(GPU_SAMPLER_TAG_OPAQUE);
- self.set_blend(false, FramebufferKind::Main);
- self.draw_tile_list(
- opaque_items.iter(),
- &composite_state,
- &composite_state.external_surfaces,
- projection,
- &mut results.stats,
- );
- self.gpu_profiler.finish_sampler(opaque_sampler);
- }
-
- // Draw alpha tiles
- let alpha_items = layer.occlusion.alpha_items();
- if !alpha_items.is_empty() {
- let transparent_sampler = self.gpu_profiler.start_sampler(GPU_SAMPLER_TAG_TRANSPARENT);
- self.set_blend(true, FramebufferKind::Main);
- self.set_blend_mode_premultiplied_alpha(FramebufferKind::Main);
- self.draw_tile_list(
- alpha_items.iter().rev(),
- &composite_state,
- &composite_state.external_surfaces,
- projection,
- &mut results.stats,
- );
- self.gpu_profiler.finish_sampler(transparent_sampler);
- }
- }
-
- /// Composite picture cache tiles into the framebuffer. This is currently
- /// the only way that picture cache tiles get drawn. In future, the tiles
- /// will often be handed to the OS compositor, and this method will be
- /// rarely used.
- pub(super) fn composite_simple(
- &mut self,
- composite_state: &CompositeState,
- frame_device_size: DeviceIntSize,
- fb_draw_target: DrawTarget,
- projection: &default::Transform3D<f32>,
- results: &mut RenderResults,
- partial_present_mode: Option<super::PartialPresentMode>,
- device_size: DeviceIntSize,
- ) {
- let _gm = self.gpu_profiler.start_marker("framebuffer");
- let _timer = self.gpu_profiler.start_timer(GPU_TAG_COMPOSITE);
-
- let num_tiles = composite_state.tiles.len();
- self.profile.set(profiler::PICTURE_TILES, num_tiles);
-
- let (window_is_opaque, enable_screenshot) = match self.compositor_config.layer_compositor()
- {
- Some(ref compositor) => {
- let props = compositor.get_window_properties();
- (props.is_opaque, props.enable_screenshot)
- }
- None => (true, true),
- };
-
- let mut input_layers: Vec<CompositorInputLayer> = Vec::new();
- let mut swapchain_layers = Vec::new();
- let cap = composite_state.tiles.len();
- let mut segment_builder = SegmentBuilder::new();
- let mut tile_index_to_layer_index = vec![None; composite_state.tiles.len()];
- let mut full_render_occlusion = occlusion::FrontToBackBuilder::with_capacity(cap, cap);
- let mut layer_compositor_frame_state = LayerCompositorFrameState {
- tile_states: FastHashMap::default(),
- rects_without_id: Vec::new(),
- };
-
- // Calculate layers with full device rect
-
- // Add a debug overlay request if enabled
- if self.debug_overlay_state.is_enabled {
- self.debug_overlay_state.layer_index = input_layers.len();
-
- input_layers.push(CompositorInputLayer {
- usage: CompositorSurfaceUsage::DebugOverlay,
- is_opaque: false,
- offset: DeviceIntPoint::zero(),
- clip_rect: device_size.into(),
- rounded_clip_rect: device_size.into(),
- rounded_clip_radii: ClipRadius::EMPTY,
- });
-
- swapchain_layers.push(SwapChainLayer {
- occlusion: occlusion::FrontToBackBuilder::with_capacity(cap, cap),
- });
- }
-
- // NOTE: Tiles here are being iterated in front-to-back order by
- // z-id, due to the sort in composite_state.end_frame()
- for (idx, tile) in composite_state.tiles.iter().enumerate() {
- let device_tile_box =
- composite_state.get_device_rect(&tile.local_rect, tile.transform_index);
-
- if let Some(ref _compositor) = self.compositor_config.layer_compositor() {
- match tile.tile_id {
- Some(tile_id) => {
- layer_compositor_frame_state.tile_states.insert(
- tile_id,
- CompositeTileState {
- local_rect: tile.local_rect,
- local_valid_rect: tile.local_valid_rect,
- device_clip_rect: tile.device_clip_rect,
- z_id: tile.z_id,
- device_tile_box: device_tile_box,
- visible_rects: Vec::new(),
- },
- );
- }
- None => {}
- }
- }
-
- // Simple compositor needs the valid rect in device space to match clip rect
- let device_valid_rect =
- composite_state.get_device_rect(&tile.local_valid_rect, tile.transform_index);
-
- let rect = device_tile_box
- .intersection_unchecked(&tile.device_clip_rect)
- .intersection_unchecked(&device_valid_rect);
-
- if rect.is_empty() {
- continue;
- }
-
- // Determine if the tile is an external surface or content
- let usage = match tile.surface {
- CompositeTileSurface::Texture { .. } | CompositeTileSurface::Color { .. } => {
- CompositorSurfaceUsage::Content
- }
- CompositeTileSurface::ExternalSurface {
- external_surface_index,
- } => {
- match (self.current_compositor_kind, enable_screenshot) {
- (CompositorKind::Native { .. }, _) | (CompositorKind::Draw { .. }, _) => {
- CompositorSurfaceUsage::Content
- }
- (CompositorKind::Layer { .. }, true) => CompositorSurfaceUsage::Content,
- (CompositorKind::Layer { .. }, false) => {
- let surface =
- &composite_state.external_surfaces[external_surface_index.0];
-
- // TODO(gwc): For now, we only select a hardware overlay swapchain if we
- // have an external image, but it may make sense to do for compositor
- // surfaces without in future.
- match surface.external_image_id {
- Some(external_image_id) => {
- let image_key = match surface.color_data {
- ResolvedExternalSurfaceColorData::Rgb {
- image_dependency,
- ..
- } => image_dependency.key,
- ResolvedExternalSurfaceColorData::Yuv {
- image_dependencies,
- ..
- } => image_dependencies[0].key,
- };
-
- CompositorSurfaceUsage::External {
- image_key,
- external_image_id,
- transform_index: tile.transform_index,
- }
- }
- None => CompositorSurfaceUsage::Content,
- }
- }
- }
- }
- };
-
- if let Some(ref _compositor) = self.compositor_config.layer_compositor() {
- if let CompositeTileSurface::ExternalSurface { .. } = tile.surface {
- assert!(tile.tile_id.is_none());
- // ExternalSurface is not promoted to external composite.
- if let CompositorSurfaceUsage::Content = usage {
- layer_compositor_frame_state.rects_without_id.push(rect);
- }
- } else {
- assert!(tile.tile_id.is_some());
- }
- }
-
- // Determine whether we need a new layer, and if so, what kind
- let new_layer_kind = match input_layers.last() {
- Some(curr_layer) => {
- match (curr_layer.usage, usage) {
- // Content -> content, composite in to same layer
- (CompositorSurfaceUsage::Content, CompositorSurfaceUsage::Content) => None,
- (
- CompositorSurfaceUsage::External { .. },
- CompositorSurfaceUsage::Content,
- ) => Some(usage),
-
- // Switch of layer type, or video -> video, need new swapchain
- (
- CompositorSurfaceUsage::Content,
- CompositorSurfaceUsage::External { .. },
- )
- | (
- CompositorSurfaceUsage::External { .. },
- CompositorSurfaceUsage::External { .. },
- ) => {
- // Only create a new layer if we're using LayerCompositor
- match self.compositor_config {
- CompositorConfig::Draw { .. } | CompositorConfig::Native { .. } => {
- None
- }
- CompositorConfig::Layer { .. } => Some(usage),
- }
- }
- (CompositorSurfaceUsage::DebugOverlay, _) => Some(usage),
- // Should not encounter debug layers as new layer
- (_, CompositorSurfaceUsage::DebugOverlay) => {
- unreachable!();
- }
- }
- }
- None => {
- // No layers yet, so we need a new one
- Some(usage)
- }
- };
-
- if let Some(new_layer_kind) = new_layer_kind {
- let (offset, clip_rect, is_opaque, rounded_clip_rect, rounded_clip_radii) =
- match usage {
- CompositorSurfaceUsage::Content => {
- (
- DeviceIntPoint::zero(),
- device_size.into(),
- false, // Assume not opaque, we'll calculate this later
- device_size.into(),
- ClipRadius::EMPTY,
- )
- }
- CompositorSurfaceUsage::External { .. } => {
- let rect = composite_state
- .get_device_rect(&tile.local_rect, tile.transform_index);
-
- let clip_rect = tile.device_clip_rect.to_i32();
- let is_opaque = tile.kind != TileKind::Alpha;
-
- if self
- .debug_flags
- .contains(DebugFlags::EXTERNAL_COMPOSITE_BORDERS)
- {
- self.external_composite_debug_items.push(DebugItem::Rect {
- outer_color: debug_colors::ORANGERED,
- inner_color: ColorF {
- r: 0.0,
- g: 0.0,
- b: 0.0,
- a: 0.0,
- },
- rect: tile.device_clip_rect,
- thickness: 10,
- });
- }
-
- let (rounded_clip_rect, rounded_clip_radii) = match tile.clip_index {
- Some(clip_index) => {
- let clip = composite_state.get_compositor_clip(clip_index);
- let radius = ClipRadius {
- top_left: clip.radius.top_left.width.round() as i32,
- top_right: clip.radius.top_right.width.round() as i32,
- bottom_left: clip.radius.bottom_left.width.round() as i32,
- bottom_right: clip.radius.bottom_right.width.round() as i32,
- };
- (clip.rect.to_i32(), radius)
- }
- None => (clip_rect, ClipRadius::EMPTY),
- };
-
- (
- rect.min.to_i32(),
- clip_rect,
- is_opaque,
- rounded_clip_rect,
- rounded_clip_radii,
- )
- }
- CompositorSurfaceUsage::DebugOverlay => unreachable!(),
- };
-
- input_layers.push(CompositorInputLayer {
- usage: new_layer_kind,
- is_opaque,
- offset,
- clip_rect,
- rounded_clip_rect,
- rounded_clip_radii,
- });
-
- swapchain_layers.push(SwapChainLayer {
- occlusion: occlusion::FrontToBackBuilder::with_capacity(cap, cap),
- })
- }
- tile_index_to_layer_index[idx] = Some(input_layers.len() - 1);
-
- // Caluclate actual visible tile's rects
-
- let is_opaque = tile.kind == TileKind::Opaque;
-
- match tile.clip_index {
- Some(clip_index) => {
- let clip = composite_state.get_compositor_clip(clip_index);
-
- // TODO(gw): Make segment builder generic on unit to avoid casts below.
- segment_builder.initialize(rect.cast_unit(), None, rect.cast_unit());
- segment_builder.push_clip_rect(
- clip.rect.cast_unit(),
- Some(clip.radius),
- ClipMode::Clip,
- );
- segment_builder.build(|segment| {
- let key = OcclusionItemKey {
- tile_index: idx,
- needs_mask: segment.has_mask,
- };
-
- full_render_occlusion.add(
- &segment.rect.cast_unit(),
- is_opaque && !segment.has_mask,
- key,
- );
- });
- }
- None => {
- full_render_occlusion.add(
- &rect,
- is_opaque,
- OcclusionItemKey {
- tile_index: idx,
- needs_mask: false,
- },
- );
- }
- }
- }
-
- assert_eq!(swapchain_layers.len(), input_layers.len());
-
- if window_is_opaque {
- match input_layers.last_mut() {
- Some(_layer) => {
- // If the window is opaque, and the last(back) layer is
- // a content layer then mark that as opaque.
- // TODO: This causes talos performance regressions.
- // if let CompositorSurfaceUsage::Content = layer.usage {
- // layer.is_opaque = true;
- // }
- }
- None => {
- // If no tiles were present, and we expect an opaque window,
- // add an empty layer to force a composite that clears the screen,
- // to match existing semantics.
- input_layers.push(CompositorInputLayer {
- usage: CompositorSurfaceUsage::Content,
- is_opaque: true,
- offset: DeviceIntPoint::zero(),
- clip_rect: device_size.into(),
- rounded_clip_rect: device_size.into(),
- rounded_clip_radii: ClipRadius::EMPTY,
- });
-
- swapchain_layers.push(SwapChainLayer {
- occlusion: occlusion::FrontToBackBuilder::with_capacity(cap, cap),
- });
- }
- }
- }
-
- let mut full_render = self.debug_overlay_state.is_enabled;
-
- // Start compositing if using OS compositor
- if let Some(ref mut compositor) = self.compositor_config.layer_compositor() {
- let input = CompositorInputConfig {
- enable_screenshot,
- layers: &input_layers,
- };
- full_render |= compositor.begin_frame(&input);
- }
-
- // Full render is requested when layer tree is updated.
- let mut partial_present_mode = if full_render {
- None
- } else {
- partial_present_mode
- };
-
- assert_eq!(swapchain_layers.len(), input_layers.len());
-
- // Recalculate dirty rect for layer compositor
- if let Some(ref _compositor) = self.compositor_config.layer_compositor() {
- // Set visible rests of current frame to each tile's CompositeTileState.
- for item in full_render_occlusion
- .opaque_items()
- .iter()
- .chain(full_render_occlusion.alpha_items().iter())
- {
- let tile = &composite_state.tiles[item.key.tile_index];
- match tile.tile_id {
- Some(tile_id) => {
- if let Some(tile_state) =
- layer_compositor_frame_state.tile_states.get_mut(&tile_id)
- {
- tile_state.visible_rects.push(item.rectangle);
- } else {
- unreachable!();
- }
- }
- None => {}
- }
- }
-
- let can_use_partial_present = !self.force_redraw
- && !full_render
- && self.layer_compositor_frame_state_in_prev_frame.is_some();
-
- if can_use_partial_present {
- let mut combined_dirty_rect = DeviceRect::zero();
-
- for tile in composite_state.tiles.iter() {
- if tile.tile_id.is_none() {
- match tile.surface {
- CompositeTileSurface::ExternalSurface { .. } => {}
- CompositeTileSurface::Texture { .. }
- | CompositeTileSurface::Color { .. } => {
- unreachable!();
- }
- }
- continue;
- }
-
- assert!(tile.tile_id.is_some());
-
- let tiles_exists_in_prev_frame = self
- .layer_compositor_frame_state_in_prev_frame
- .as_ref()
- .unwrap()
- .tile_states
- .contains_key(&tile.tile_id.unwrap());
- let tile_id = tile.tile_id.unwrap();
- let tile_state = layer_compositor_frame_state
- .tile_states
- .get(&tile_id)
- .unwrap();
-
- if tiles_exists_in_prev_frame {
- let prev_tile_state = self
- .layer_compositor_frame_state_in_prev_frame
- .as_ref()
- .unwrap()
- .tile_states
- .get(&tile_id)
- .unwrap();
-
- if tile_state.same_state(prev_tile_state) {
- // Case that tile is same state in previous frame and current frame.
- // Intersection of tile's dirty rect and tile's visible rects are actual dirty rects.
- let dirty_rect = composite_state
- .get_device_rect(&tile.local_dirty_rect, tile.transform_index);
- for rect in tile_state.visible_rects.iter() {
- let visible_dirty_rect = rect.intersection(&dirty_rect);
- if visible_dirty_rect.is_some() {
- combined_dirty_rect =
- combined_dirty_rect.union(&visible_dirty_rect.unwrap());
- }
- }
- } else {
- // If tile is rendered in previous frame, but its state is different,
- // both visible rects in previous frame and current frame are dirty rects.
- for rect in tile_state
- .visible_rects
- .iter()
- .chain(prev_tile_state.visible_rects.iter())
- {
- combined_dirty_rect = combined_dirty_rect.union(&rect);
- }
- }
- } else {
- // If tile is not rendered in previous frame, its all visible rects are dirty rects.
- for rect in &tile_state.visible_rects {
- combined_dirty_rect = combined_dirty_rect.union(&rect);
- }
- }
- }
-
- // Case that tile is rendered in pervious frame, but not in current frame.
- for (tile_id, tile_state) in self
- .layer_compositor_frame_state_in_prev_frame
- .as_ref()
- .unwrap()
- .tile_states
- .iter()
- {
- if !layer_compositor_frame_state
- .tile_states
- .contains_key(&tile_id)
- {
- for rect in tile_state.visible_rects.iter() {
- combined_dirty_rect = combined_dirty_rect.union(&rect);
- }
- }
- }
-
- // Case that ExternalSurface is not promoted to external composite.
- for rect in layer_compositor_frame_state.rects_without_id.iter().chain(
- self.layer_compositor_frame_state_in_prev_frame
- .as_ref()
- .unwrap()
- .rects_without_id
- .iter(),
- ) {
- combined_dirty_rect = combined_dirty_rect.union(&rect);
- }
-
- partial_present_mode = Some(super::PartialPresentMode::Single {
- dirty_rect: combined_dirty_rect,
- });
- } else {
- partial_present_mode = None;
- }
-
- self.layer_compositor_frame_state_in_prev_frame = Some(layer_compositor_frame_state);
- }
-
- // Check tiles handling with partial_present_mode
-
- let mut opaque_rounded_corners: HashSet<CompositeRoundedCorner> = HashSet::new();
-
- // NOTE: Tiles here are being iterated in front-to-back order by
- // z-id, due to the sort in composite_state.end_frame()
- for (idx, tile) in composite_state.tiles.iter().enumerate() {
- let device_tile_box =
- composite_state.get_device_rect(&tile.local_rect, tile.transform_index);
-
- // Determine a clip rect to apply to this tile, depending on what
- // the partial present mode is.
- let partial_clip_rect = match partial_present_mode {
- Some(super::PartialPresentMode::Single { dirty_rect }) => dirty_rect,
- None => device_tile_box,
- };
-
- // Simple compositor needs the valid rect in device space to match clip rect
- let device_valid_rect =
- composite_state.get_device_rect(&tile.local_valid_rect, tile.transform_index);
-
- let rect = device_tile_box
- .intersection_unchecked(&tile.device_clip_rect)
- .intersection_unchecked(&partial_clip_rect)
- .intersection_unchecked(&device_valid_rect);
-
- if rect.is_empty() {
- continue;
- }
-
- let layer_index = match tile_index_to_layer_index[idx] {
- None => {
- // The rect of partial present should be subset of the rect of full render.
- error!("rect {:?} should have valid layer index", rect);
- continue;
- }
- Some(layer_index) => layer_index,
- };
-
- // For normal tiles, add to occlusion tracker
- let layer = &mut swapchain_layers[layer_index];
-
- let is_opaque = tile.kind == TileKind::Opaque;
-
- match tile.clip_index {
- Some(clip_index) => {
- let clip = composite_state.get_compositor_clip(clip_index);
-
- // TODO(gw): Make segment builder generic on unit to avoid casts below.
- segment_builder.initialize(rect.cast_unit(), None, rect.cast_unit());
- segment_builder.push_clip_rect(
- clip.rect.cast_unit(),
- Some(clip.radius),
- ClipMode::Clip,
- );
- segment_builder.build(|segment| {
- let key = OcclusionItemKey {
- tile_index: idx,
- needs_mask: segment.has_mask,
- };
-
- let radius = if segment.edge_flags
- == EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::LEFT
- && !clip.radius.top_left.is_empty()
- {
- Some(clip.radius.top_left)
- } else if segment.edge_flags
- == EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::RIGHT
- && !clip.radius.top_right.is_empty()
- {
- Some(clip.radius.top_right)
- } else if segment.edge_flags
- == EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::LEFT
- && !clip.radius.bottom_left.is_empty()
- {
- Some(clip.radius.bottom_left)
- } else if segment.edge_flags
- == EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::RIGHT
- && !clip.radius.bottom_right.is_empty()
- {
- Some(clip.radius.bottom_right)
- } else {
- None
- };
-
- if let Some(radius) = radius {
- let rounded_corner = CompositeRoundedCorner {
- rect: segment.rect.cast_unit(),
- radius: radius,
- edge_flags: segment.edge_flags,
- };
-
- // Drop overdraw rounded rect
- if opaque_rounded_corners.contains(&rounded_corner) {
- return;
- }
-
- if is_opaque {
- opaque_rounded_corners.insert(rounded_corner);
- }
- }
-
- layer.occlusion.add(
- &segment.rect.cast_unit(),
- is_opaque && !segment.has_mask,
- key,
- );
- });
- }
- None => {
- layer.occlusion.add(
- &rect,
- is_opaque,
- OcclusionItemKey {
- tile_index: idx,
- needs_mask: false,
- },
- );
- }
- }
- }
-
- assert_eq!(swapchain_layers.len(), input_layers.len());
- let mut content_clear_color = Some(self.clear_color);
-
- for (layer_index, (layer, swapchain_layer)) in
- input_layers.iter().zip(swapchain_layers.iter()).enumerate()
- {
- self.device.reset_state();
-
- // Skip compositing external images or debug layers here
- match layer.usage {
- CompositorSurfaceUsage::Content => {}
- CompositorSurfaceUsage::External { .. } | CompositorSurfaceUsage::DebugOverlay => {
- continue;
- }
- }
-
- // Only use supplied clear color for first content layer we encounter
- let clear_color = content_clear_color.take().unwrap_or(ColorF::TRANSPARENT);
-
- if let Some(ref mut _compositor) = self.compositor_config.layer_compositor() {
- if let Some(super::PartialPresentMode::Single { dirty_rect }) = partial_present_mode
- {
- if dirty_rect.is_empty() {
- continue;
- }
- }
- }
-
- let draw_target = match self.compositor_config {
- CompositorConfig::Layer { ref mut compositor } => {
- match partial_present_mode {
- Some(super::PartialPresentMode::Single { dirty_rect }) => {
- compositor.bind_layer(layer_index, &[dirty_rect.to_i32()]);
- }
- None => {
- compositor.bind_layer(layer_index, &[]);
- }
- };
-
- DrawTarget::NativeSurface {
- offset: -layer.offset,
- external_fbo_id: 0,
- dimensions: frame_device_size,
- }
- }
- // Native can be hit when switching compositors (disable when using Layer)
- CompositorConfig::Draw { .. } | CompositorConfig::Native { .. } => fb_draw_target,
- };
-
- // TODO(gwc): When supporting external attached swapchains, need to skip the composite pass here
-
- // Draw each compositing pass in to a swap chain
- self.composite_pass(
- composite_state,
- draw_target,
- clear_color,
- projection,
- results,
- partial_present_mode,
- swapchain_layer,
- );
-
- if let Some(ref mut compositor) = self.compositor_config.layer_compositor() {
- match partial_present_mode {
- Some(super::PartialPresentMode::Single { dirty_rect }) => {
- compositor.present_layer(layer_index, &[dirty_rect.to_i32()]);
- }
- None => {
- compositor.present_layer(layer_index, &[]);
- }
- };
- }
- }
-
- if let Some(ref mut compositor) = self.compositor_config.layer_compositor() {
- for (layer_index, layer) in input_layers.iter().enumerate() {
- // External surfaces need transform applied, but content
- // surfaces are always at identity
- let transform = match layer.usage {
- CompositorSurfaceUsage::Content => CompositorSurfaceTransform::identity(),
- CompositorSurfaceUsage::External {
- transform_index, ..
- } => composite_state.get_compositor_transform(transform_index),
- CompositorSurfaceUsage::DebugOverlay => CompositorSurfaceTransform::identity(),
- };
-
- compositor.add_surface(
- layer_index,
- transform,
- layer.clip_rect,
- ImageRendering::Auto,
- layer.rounded_clip_rect,
- layer.rounded_clip_radii,
- );
- }
- }
- }
-
- pub(super) fn composite_frame(
- &mut self,
- frame: &mut Frame,
- device_size: Option<DeviceIntSize>,
- results: &mut RenderResults,
- present_mode: Option<PartialPresentMode>,
- ) {
- profile_scope!("main target");
- if let Some(device_size) = device_size {
- if let Some(history) = &mut self.command_log {
- history.begin_render_target("Window", device_size);
- }
-
- results.stats.color_target_count += 1;
- results.picture_cache_debug = mem::replace(
- &mut frame.composite_state.picture_cache_debug,
- PictureCacheDebugInfo::new(),
- );
-
- let size = frame.device_rect.size().to_f32();
- let surface_origin_is_top_left = self.device.surface_origin_is_top_left();
- let (bottom, top) = if surface_origin_is_top_left {
- (0.0, size.height)
- } else {
- (size.height, 0.0)
- };
-
- let projection = Transform3D::ortho(
- 0.0,
- size.width,
- bottom,
- top,
- self.device.ortho_near_plane(),
- self.device.ortho_far_plane(),
- );
-
- let fb_scale = Scale::<_, _, FramebufferPixel>::new(1i32);
- let mut fb_rect = frame.device_rect * fb_scale;
-
- if !surface_origin_is_top_left {
- let h = fb_rect.height();
- fb_rect.min.y = device_size.height - fb_rect.max.y;
- fb_rect.max.y = fb_rect.min.y + h;
- }
-
- let draw_target = DrawTarget::Default {
- rect: fb_rect,
- total_size: device_size * fb_scale,
- surface_origin_is_top_left,
- };
-
- // If we have a native OS compositor, then make use of that interface
- // to specify how to composite each of the picture cache surfaces.
- match self.current_compositor_kind {
- CompositorKind::Native { .. } => {
- // We have already queued surfaces for early native composition by this point.
- // All that is left is to finally update any external native surfaces that were
- // invalidated so that composition can complete.
- self.update_external_native_surfaces(
- &frame.composite_state.external_surfaces,
- results,
- );
- }
- CompositorKind::Draw { .. } | CompositorKind::Layer { .. } => {
- self.composite_simple(
- &frame.composite_state,
- frame.device_rect.size(),
- draw_target,
- &projection,
- results,
- present_mode,
- device_size,
- );
- }
- }
- // Reset force_redraw. It was used in composite_simple() with layer compositor.
- self.force_redraw = false;
- } else {
- // Rendering a frame without presenting it will confuse the partial
- // present logic, so force a full present for the next frame.
- self.force_redraw = true;
- }
- }
-
- /// Update the dirty rects based on current compositing mode and config
- // TODO(gw): This can be tidied up significantly once the Draw compositor
- // is implemented in terms of the compositor trait.
- pub(super) fn calculate_dirty_rects(
- &mut self,
- buffer_age: usize,
- composite_state: &CompositeState,
- draw_target_dimensions: DeviceIntSize,
- results: &mut RenderResults,
- ) -> Option<PartialPresentMode> {
- if let Some(ref _compositor) = self.compositor_config.layer_compositor() {
- // Calculate dirty rects of layer compositor in composite_simple()
- return None;
- }
-
- let mut partial_present_mode = None;
-
- let (max_partial_present_rects, draw_previous_partial_present_regions) =
- match self.current_compositor_kind {
- CompositorKind::Native { .. } => {
- // Assume that we can return a single dirty rect for native
- // compositor for now, and that there is no buffer-age functionality.
- // These params can be exposed by the compositor capabilities struct
- // as the Draw compositor is ported to use it.
- (1, false)
- }
- CompositorKind::Draw {
- draw_previous_partial_present_regions,
- max_partial_present_rects,
- } => (
- max_partial_present_rects,
- draw_previous_partial_present_regions,
- ),
- CompositorKind::Layer { .. } => {
- unreachable!();
- }
- };
-
- if max_partial_present_rects > 0 {
- let prev_frames_damage_rect = if let Some(..) = self.compositor_config.partial_present()
- {
- self.buffer_damage_tracker
- .get_damage_rect(buffer_age)
- .or_else(|| Some(DeviceRect::from_size(draw_target_dimensions.to_f32())))
- } else {
- None
- };
-
- let can_use_partial_present = composite_state.dirty_rects_are_valid
- && !self.force_redraw
- && !(prev_frames_damage_rect.is_none() && draw_previous_partial_present_regions)
- && !self.debug_overlay_state.is_enabled;
-
- if can_use_partial_present {
- let mut combined_dirty_rect = DeviceRect::zero();
- let fb_rect = DeviceRect::from_size(draw_target_dimensions.to_f32());
-
- // Work out how many dirty rects WR produced, and if that's more than
- // what the device supports.
- for tile in &composite_state.tiles {
- let dirty_rect = composite_state
- .get_device_rect(&tile.local_dirty_rect, tile.transform_index);
-
- // In pathological cases where a tile is extremely zoomed, it
- // may end up with device coords outside the range of an i32,
- // so clamp it to the frame buffer rect here, before it gets
- // casted to an i32 rect below.
- if let Some(dirty_rect) = dirty_rect.intersection(&fb_rect) {
- combined_dirty_rect = combined_dirty_rect.union(&dirty_rect);
- }
- }
-
- let combined_dirty_rect = combined_dirty_rect.round();
- let combined_dirty_rect_i32 = combined_dirty_rect.to_i32();
- // Return this frame's dirty region. If nothing has changed, don't return any dirty
- // rects at all (the client can use this as a signal to skip present completely).
- if !combined_dirty_rect.is_empty() {
- results.dirty_rects.push(combined_dirty_rect_i32);
- }
-
- // Track this frame's dirty region, for calculating subsequent frames' damage.
- if draw_previous_partial_present_regions {
- self.buffer_damage_tracker
- .push_dirty_rect(&combined_dirty_rect);
- }
-
- // If the implementation requires manually keeping the buffer consistent,
- // then we must combine this frame's dirty region with that of previous frames
- // to determine the total_dirty_rect. The is used to determine what region we
- // render to, and is what we send to the compositor as the buffer damage region
- // (eg for KHR_partial_update).
- let total_dirty_rect = if draw_previous_partial_present_regions {
- combined_dirty_rect.union(&prev_frames_damage_rect.unwrap())
- } else {
- combined_dirty_rect
- };
-
- partial_present_mode = Some(PartialPresentMode::Single {
- dirty_rect: total_dirty_rect,
- });
- } else {
- // If we don't have a valid partial present scenario, return a single
- // dirty rect to the client that covers the entire framebuffer.
- let fb_rect = DeviceIntRect::from_size(draw_target_dimensions);
- results.dirty_rects.push(fb_rect);
-
- if draw_previous_partial_present_regions {
- self.buffer_damage_tracker
- .push_dirty_rect(&fb_rect.to_f32());
- }
- }
- }
-
- partial_present_mode
- }
-}
diff --git a/gfx/wr/webrender/src/renderer/external_image.rs b/gfx/wr/webrender/src/renderer/external_image.rs
@@ -1,97 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-use api::{ExternalImageHandler, ExternalImageSource, ExternalImageType};
-use crate::device::{Device, ExternalTexture};
-use crate::internal_types::{DeferredResolveIndex, FastHashMap};
-use crate::prim_store::DeferredResolve;
-use crate::device::query::GpuProfiler;
-use super::gpu_buffer::GpuBufferF;
-
-#[inline]
-pub(super) fn update_deferred_resolves(
- external_image_handler: Option<&mut Box<dyn ExternalImageHandler>>,
- external_images: &mut FastHashMap<DeferredResolveIndex, ExternalTexture>,
- gpu_profiler: &mut GpuProfiler,
- device: &mut Device,
- deferred_resolves: &[DeferredResolve],
- gpu_buffer: &mut GpuBufferF,
-) {
- if deferred_resolves.is_empty() {
- return;
- }
-
- let handler = external_image_handler.expect("Found external image, but no handler set!");
-
- for (i, deferred_resolve) in deferred_resolves.iter().enumerate() {
- gpu_profiler.place_marker("deferred resolve");
- let props = &deferred_resolve.image_properties;
- let ext_image = props
- .external_image
- .expect("BUG: Deferred resolves must be external images!");
-
- let image = handler.lock(ext_image.id, ext_image.channel_index, deferred_resolve.is_composited);
- let texture_target = match ext_image.image_type {
- ExternalImageType::TextureHandle(target) => target,
- ExternalImageType::Buffer => {
- panic!("not a suitable image type in update_deferred_resolves()");
- }
- };
-
- device.reset_state();
-
- let texture = match image.source {
- ExternalImageSource::NativeTexture(texture_id) => {
- ExternalTexture::new(
- texture_id,
- texture_target,
- image.uv,
- deferred_resolve.rendering,
- )
- }
- ExternalImageSource::Invalid => {
- warn!("Invalid ext-image");
- debug!(
- "For ext_id:{:?}, channel:{}.",
- ext_image.id,
- ext_image.channel_index
- );
- ExternalTexture::new(
- 0,
- texture_target,
- image.uv,
- deferred_resolve.rendering,
- )
- }
- ExternalImageSource::RawData(_) => {
- panic!("Raw external data is not expected for deferred resolves!");
- }
- };
-
- external_images.insert(DeferredResolveIndex(i as u32), texture);
-
- let addr = gpu_buffer.resolve_handle(deferred_resolve.handle);
- let index = addr.as_u32() as usize;
- gpu_buffer.data[index] = image.uv.to_array().into();
- gpu_buffer.data[index + 1] = [0f32; 4].into();
- }
-}
-
-#[inline]
-pub(super) fn unlock_external_images(
- external_image_handler: Option<&mut Box<dyn ExternalImageHandler>>,
- external_images: &mut FastHashMap<DeferredResolveIndex, ExternalTexture>,
- deferred_resolves: &[DeferredResolve],
-) {
- if !external_images.is_empty() {
- let handler = external_image_handler.expect("Found external image, but no handler set!");
- for (index, _) in external_images.drain() {
- let props = &deferred_resolves[index.0 as usize].image_properties;
- let ext_image = props
- .external_image
- .expect("BUG: Deferred resolves must be external images!");
- handler.unlock(ext_image.id, ext_image.channel_index);
- }
- }
-}
diff --git a/gfx/wr/webrender/src/renderer/mod.rs b/gfx/wr/webrender/src/renderer/mod.rs
@@ -34,11 +34,11 @@
//! up the scissor, are accepting already transformed coordinates, which we can get by
//! calling `DrawTarget::to_framebuffer_rect`
-use api::{ColorF, ColorU, MixBlendMode, TextureCacheCategory};
+use api::{ClipMode, ColorF, ColorU, MixBlendMode, TextureCacheCategory};
use api::{DocumentId, Epoch, ExternalImageHandler, RenderReasons};
#[cfg(feature = "replay")]
use api::ExternalImageId;
-use api::ImageFormat;
+use api::{ExternalImageSource, ExternalImageType, ImageFormat, PremultipliedColorF};
use api::{PipelineId, ImageRendering, Checkpoint, NotificationRequest, ImageBufferKind};
#[cfg(feature = "replay")]
use api::ExternalImage;
@@ -54,12 +54,14 @@ use crate::batch::{AlphaBatchContainer, BatchKind, BatchFeatures, BatchTextures,
use crate::batch::ClipMaskInstanceList;
#[cfg(any(feature = "capture", feature = "replay"))]
use crate::capture::{CaptureConfig, ExternalCaptureImage, PlainExternalImage};
-use crate::composite::{CompositeState, CompositeTileSurface, CompositorSurfaceTransform};
-use crate::composite::{CompositorKind, Compositor, NativeTileId};
+use crate::composite::{CompositeState, CompositeTileSurface, CompositorInputLayer, CompositorSurfaceTransform, ResolvedExternalSurface};
+use crate::composite::{CompositorKind, Compositor, NativeTileId, CompositeFeatures, CompositeSurfaceFormat, ResolvedExternalSurfaceColorData};
use crate::composite::{CompositorConfig, NativeSurfaceOperationDetails, NativeSurfaceId, NativeSurfaceOperation, ClipRadius};
+use crate::composite::{CompositeRoundedCorner, TileKind};
#[cfg(feature = "debugger")]
use api::debugger::{CompositorDebugInfo, DebuggerTextureContent};
-use crate::debug_colors;
+use crate::segment::{EdgeAaSegmentMask, SegmentBuilder};
+use crate::{debug_colors, CompositorInputConfig, CompositorSurfaceUsage};
use crate::device::{DepthFunction, Device, DrawTarget, ExternalTexture, GpuFrameId, UploadPBOPool};
use crate::device::{ReadTarget, ShaderError, Texture, TextureFilter, TextureFlags, TextureSlot, Texel};
use crate::device::query::{GpuSampler, GpuTimer};
@@ -69,7 +71,7 @@ use crate::debug_item::DebugItem;
use crate::frame_builder::Frame;
use glyph_rasterizer::GlyphFormat;
use crate::gpu_types::{ScalingInstance, SVGFEFilterInstance, CopyInstance, PrimitiveInstanceData};
-use crate::gpu_types::{BlurInstance, ClearInstance};
+use crate::gpu_types::{BlurInstance, ClearInstance, CompositeInstance, ZBufferId};
use crate::internal_types::{TextureSource, TextureSourceExternal, FrameVec};
#[cfg(any(feature = "capture", feature = "replay"))]
use crate::internal_types::DebugOutput;
@@ -77,6 +79,8 @@ use crate::internal_types::{CacheTextureId, FastHashMap, FastHashSet, RenderedDo
use crate::internal_types::{TextureCacheAllocInfo, TextureCacheAllocationKind, TextureUpdateList};
use crate::internal_types::{RenderTargetInfo, Swizzle, DeferredResolveIndex};
use crate::picture::ResolvedSurfaceTexture;
+use crate::tile_cache::TileId;
+use crate::prim_store::DeferredResolve;
use crate::profiler::{self, RenderCommandLog, GpuProfileTag, TransactionProfile};
use crate::profiler::{Profiler, add_event_marker, add_text_marker, thread_is_being_profiled};
use crate::device::query::GpuProfiler;
@@ -89,6 +93,7 @@ use crate::render_target::{RenderTargetKind, BlitJob};
use crate::telemetry::Telemetry;
use crate::tile_cache::PictureCacheDebugInfo;
use crate::util::drain_filter;
+use crate::rectangle_occlusion as occlusion;
#[cfg(feature = "debugger")]
use crate::debugger::{Debugger, DebugQueryKind};
use upload::{upload_to_texture_cache, UploadTexturePool};
@@ -103,6 +108,7 @@ use std::sync::Arc;
use std::{
cell::RefCell,
+ collections::HashSet,
collections::VecDeque,
f32,
ffi::c_void,
@@ -114,17 +120,13 @@ use std::{
#[cfg(any(feature = "capture", feature = "replay"))]
use std::collections::hash_map::Entry;
-mod composite;
mod debug;
-mod external_image;
mod gpu_buffer;
mod shade;
mod vertex;
mod upload;
pub(crate) mod init;
-use composite::LayerCompositorFrameState;
-
pub use debug::DebugRenderer;
pub use shade::{PendingShadersToPrecache, Shaders, SharedShaders};
pub use vertex::{desc, VertexArrayKind, MAX_VERTEX_TEXTURE_WIDTH};
@@ -251,11 +253,11 @@ const GPU_SAMPLER_TAG_ALPHA: GpuProfileTag = GpuProfileTag {
label: "Alpha targets",
color: debug_colors::BLACK,
};
-pub const GPU_SAMPLER_TAG_OPAQUE: GpuProfileTag = GpuProfileTag {
+const GPU_SAMPLER_TAG_OPAQUE: GpuProfileTag = GpuProfileTag {
label: "Opaque pass",
color: debug_colors::BLACK,
};
-pub const GPU_SAMPLER_TAG_TRANSPARENT: GpuProfileTag = GpuProfileTag {
+const GPU_SAMPLER_TAG_TRANSPARENT: GpuProfileTag = GpuProfileTag {
label: "Transparent pass",
color: debug_colors::BLACK,
};
@@ -263,11 +265,52 @@ const GPU_TAG_SVG_FILTER_NODES: GpuProfileTag = GpuProfileTag {
label: "SvgFilterNodes",
color: debug_colors::LEMONCHIFFON,
};
-pub const GPU_TAG_COMPOSITE: GpuProfileTag = GpuProfileTag {
+const GPU_TAG_COMPOSITE: GpuProfileTag = GpuProfileTag {
label: "Composite",
color: debug_colors::TOMATO,
};
+// Key used when adding compositing tiles to the occlusion tracker.
+// Since an entire tile may have a mask, but we may segment that in
+// to masked and non-masked regions, we need to track which of the
+// occlusion tracker outputs need a mask
+#[derive(Debug, Copy, Clone)]
+struct OcclusionItemKey {
+ tile_index: usize,
+ needs_mask: bool,
+}
+
+// Defines the content that we will draw to a given swapchain / layer, calculated
+// after occlusion culling.
+struct SwapChainLayer {
+ occlusion: occlusion::FrontToBackBuilder<OcclusionItemKey>,
+}
+
+// Store rects state of tile used for compositing with layer compositor
+struct CompositeTileState {
+ pub local_rect: PictureRect,
+ pub local_valid_rect: PictureRect,
+ pub device_clip_rect: DeviceRect,
+ pub z_id: ZBufferId,
+ pub device_tile_box: DeviceRect,
+ pub visible_rects: Vec<DeviceRect>,
+}
+
+impl CompositeTileState {
+ pub fn same_state(&self, other: &CompositeTileState) -> bool {
+ self.local_rect == other.local_rect &&
+ self.local_valid_rect == other.local_valid_rect &&
+ self.device_clip_rect == other.device_clip_rect &&
+ self.z_id == other.z_id &&
+ self.device_tile_box == other.device_tile_box
+ }
+}
+
+/// The list of tiles and rects used for compositing to a frame with layer compositor
+struct LayerCompositorFrameState {
+ tile_states: FastHashMap<TileId, CompositeTileState>,
+ pub rects_without_id: Vec<DeviceRect>,
+}
/// The clear color used for the texture cache when the debug display is enabled.
/// We use a shade of blue so that we can still identify completely blue items in
@@ -440,7 +483,7 @@ impl CpuProfile {
/// The selected partial present mode for a given frame.
#[derive(Debug, Copy, Clone)]
-pub(super) enum PartialPresentMode {
+enum PartialPresentMode {
/// The device supports fewer dirty rects than the number of dirty rects
/// that WR produced. In this case, the WR dirty rects are union'ed into
/// a single dirty rect, that is provided to the caller.
@@ -1678,14 +1721,7 @@ impl Renderer {
"Cleared texture cache without sending new document frame.");
}
- external_image::update_deferred_resolves(
- self.external_image_handler.as_mut(),
- &mut self.texture_resolver.external_images,
- &mut self.gpu_profiler,
- &mut self.device,
- &frame.deferred_resolves,
- &mut frame.gpu_buffer_f,
- );
+ self.update_deferred_resolves(&frame.deferred_resolves, &mut frame.gpu_buffer_f);
self.draw_frame(
frame,
@@ -1710,11 +1746,7 @@ impl Renderer {
self.profile.merge(profile);
- external_image::unlock_external_images(
- self.external_image_handler.as_mut(),
- &mut self.texture_resolver.external_images,
- &frame.deferred_resolves,
- );
+ self.unlock_external_images(&frame.deferred_resolves);
let _gm = self.gpu_profiler.start_marker("end frame");
self.gpu_profiler.end_frame();
@@ -3127,6 +3159,1141 @@ impl Renderer {
}
}
+ /// Rasterize any external compositor surfaces that require updating
+ fn update_external_native_surfaces(
+ &mut self,
+ external_surfaces: &[ResolvedExternalSurface],
+ results: &mut RenderResults,
+ ) {
+ if external_surfaces.is_empty() {
+ return;
+ }
+
+ let opaque_sampler = self.gpu_profiler.start_sampler(GPU_SAMPLER_TAG_OPAQUE);
+
+ self.device.disable_depth();
+ self.set_blend(false, FramebufferKind::Main);
+
+ for surface in external_surfaces {
+ // See if this surface needs to be updated
+ let (native_surface_id, surface_size) = match surface.update_params {
+ Some(params) => params,
+ None => continue,
+ };
+
+ // When updating an external surface, the entire surface rect is used
+ // for all of the draw, dirty, valid and clip rect parameters.
+ let surface_rect = surface_size.into();
+
+ // Bind the native compositor surface to update
+ let surface_info = self.compositor_config
+ .compositor()
+ .unwrap()
+ .bind(
+ &mut self.device,
+ NativeTileId {
+ surface_id: native_surface_id,
+ x: 0,
+ y: 0,
+ },
+ surface_rect,
+ surface_rect,
+ );
+
+ // Bind the native surface to current FBO target
+ let draw_target = DrawTarget::NativeSurface {
+ offset: surface_info.origin,
+ external_fbo_id: surface_info.fbo_id,
+ dimensions: surface_size,
+ };
+ self.device.bind_draw_target(draw_target);
+
+ let projection = Transform3D::ortho(
+ 0.0,
+ surface_size.width as f32,
+ 0.0,
+ surface_size.height as f32,
+ self.device.ortho_near_plane(),
+ self.device.ortho_far_plane(),
+ );
+
+ let ( textures, instance ) = match surface.color_data {
+ ResolvedExternalSurfaceColorData::Yuv{
+ ref planes, color_space, format, channel_bit_depth, .. } => {
+
+ let textures = BatchTextures::composite_yuv(
+ planes[0].texture,
+ planes[1].texture,
+ planes[2].texture,
+ );
+
+ // When the texture is an external texture, the UV rect is not known when
+ // the external surface descriptor is created, because external textures
+ // are not resolved until the lock() callback is invoked at the start of
+ // the frame render. To handle this, query the texture resolver for the
+ // UV rect if it's an external texture, otherwise use the default UV rect.
+ let uv_rects = [
+ self.texture_resolver.get_uv_rect(&textures.input.colors[0], planes[0].uv_rect),
+ self.texture_resolver.get_uv_rect(&textures.input.colors[1], planes[1].uv_rect),
+ self.texture_resolver.get_uv_rect(&textures.input.colors[2], planes[2].uv_rect),
+ ];
+
+ let instance = CompositeInstance::new_yuv(
+ surface_rect.to_f32(),
+ surface_rect.to_f32(),
+ // z-id is not relevant when updating a native compositor surface.
+ // TODO(gw): Support compositor surfaces without z-buffer, for memory / perf win here.
+ color_space,
+ format,
+ channel_bit_depth,
+ uv_rects,
+ (false, false),
+ None,
+ );
+
+ // Bind an appropriate YUV shader for the texture format kind
+ self.shaders
+ .borrow_mut()
+ .get_composite_shader(
+ CompositeSurfaceFormat::Yuv,
+ surface.image_buffer_kind,
+ instance.get_yuv_features(),
+ ).bind(
+ &mut self.device,
+ &projection,
+ None,
+ &mut self.renderer_errors,
+ &mut self.profile,
+ &mut self.command_log,
+ );
+
+ ( textures, instance )
+ },
+ ResolvedExternalSurfaceColorData::Rgb{ ref plane, .. } => {
+ let textures = BatchTextures::composite_rgb(plane.texture);
+ let uv_rect = self.texture_resolver.get_uv_rect(&textures.input.colors[0], plane.uv_rect);
+ let instance = CompositeInstance::new_rgb(
+ surface_rect.to_f32(),
+ surface_rect.to_f32(),
+ PremultipliedColorF::WHITE,
+ uv_rect,
+ plane.texture.uses_normalized_uvs(),
+ (false, false),
+ None,
+ );
+ let features = instance.get_rgb_features();
+
+ self.shaders
+ .borrow_mut()
+ .get_composite_shader(
+ CompositeSurfaceFormat::Rgba,
+ surface.image_buffer_kind,
+ features,
+ ).bind(
+ &mut self.device,
+ &projection,
+ None,
+ &mut self.renderer_errors,
+ &mut self.profile,
+ &mut self.command_log,
+ );
+
+ ( textures, instance )
+ },
+ };
+
+ self.draw_instanced_batch(
+ &[instance],
+ VertexArrayKind::Composite,
+ &textures,
+ &mut results.stats,
+ );
+
+ self.compositor_config
+ .compositor()
+ .unwrap()
+ .unbind(&mut self.device);
+ }
+
+ self.gpu_profiler.finish_sampler(opaque_sampler);
+ }
+
+ /// Draw a list of tiles to the framebuffer
+ fn draw_tile_list<'a, I: Iterator<Item = &'a occlusion::Item<OcclusionItemKey>>>(
+ &mut self,
+ tiles_iter: I,
+ composite_state: &CompositeState,
+ external_surfaces: &[ResolvedExternalSurface],
+ projection: &default::Transform3D<f32>,
+ stats: &mut RendererStats,
+ ) {
+ let mut current_shader_params = (
+ CompositeSurfaceFormat::Rgba,
+ ImageBufferKind::Texture2D,
+ CompositeFeatures::empty(),
+ None,
+ );
+ let mut current_textures = BatchTextures::empty();
+ let mut instances = Vec::new();
+
+ self.shaders
+ .borrow_mut()
+ .get_composite_shader(
+ current_shader_params.0,
+ current_shader_params.1,
+ current_shader_params.2,
+ ).bind(
+ &mut self.device,
+ projection,
+ None,
+ &mut self.renderer_errors,
+ &mut self.profile,
+ &mut self.command_log,
+ );
+
+ for item in tiles_iter {
+ let tile = &composite_state.tiles[item.key.tile_index];
+
+ let clip_rect = item.rectangle;
+ let tile_rect = composite_state.get_device_rect(&tile.local_rect, tile.transform_index);
+ let transform = composite_state.get_device_transform(tile.transform_index);
+ let flip = (transform.scale.x < 0.0, transform.scale.y < 0.0);
+
+ let clip = if item.key.needs_mask {
+ tile.clip_index.map(|index| {
+ composite_state.get_compositor_clip(index)
+ })
+ } else {
+ None
+ };
+
+ // Work out the draw params based on the tile surface
+ let (instance, textures, shader_params) = match tile.surface {
+ CompositeTileSurface::Color { color } => {
+ let dummy = TextureSource::Dummy;
+ let image_buffer_kind = dummy.image_buffer_kind();
+ let instance = CompositeInstance::new(
+ tile_rect,
+ clip_rect,
+ color.premultiplied(),
+ flip,
+ clip,
+ );
+ let features = instance.get_rgb_features();
+ (
+ instance,
+ BatchTextures::composite_rgb(dummy),
+ (CompositeSurfaceFormat::Rgba, image_buffer_kind, features, None),
+ )
+ }
+ CompositeTileSurface::Texture { surface: ResolvedSurfaceTexture::TextureCache { texture } } => {
+ let instance = CompositeInstance::new(
+ tile_rect,
+ clip_rect,
+ PremultipliedColorF::WHITE,
+ flip,
+ clip,
+ );
+ let features = instance.get_rgb_features();
+ (
+ instance,
+ BatchTextures::composite_rgb(texture),
+ (
+ CompositeSurfaceFormat::Rgba,
+ ImageBufferKind::Texture2D,
+ features,
+ None,
+ ),
+ )
+ }
+ CompositeTileSurface::ExternalSurface { external_surface_index } => {
+ let surface = &external_surfaces[external_surface_index.0];
+
+ match surface.color_data {
+ ResolvedExternalSurfaceColorData::Yuv{ ref planes, color_space, format, channel_bit_depth, .. } => {
+ let textures = BatchTextures::composite_yuv(
+ planes[0].texture,
+ planes[1].texture,
+ planes[2].texture,
+ );
+
+ // When the texture is an external texture, the UV rect is not known when
+ // the external surface descriptor is created, because external textures
+ // are not resolved until the lock() callback is invoked at the start of
+ // the frame render. To handle this, query the texture resolver for the
+ // UV rect if it's an external texture, otherwise use the default UV rect.
+ let uv_rects = [
+ self.texture_resolver.get_uv_rect(&textures.input.colors[0], planes[0].uv_rect),
+ self.texture_resolver.get_uv_rect(&textures.input.colors[1], planes[1].uv_rect),
+ self.texture_resolver.get_uv_rect(&textures.input.colors[2], planes[2].uv_rect),
+ ];
+
+ let instance = CompositeInstance::new_yuv(
+ tile_rect,
+ clip_rect,
+ color_space,
+ format,
+ channel_bit_depth,
+ uv_rects,
+ flip,
+ clip,
+ );
+ let features = instance.get_yuv_features();
+
+ (
+ instance,
+ textures,
+ (
+ CompositeSurfaceFormat::Yuv,
+ surface.image_buffer_kind,
+ features,
+ None
+ ),
+ )
+ },
+ ResolvedExternalSurfaceColorData::Rgb { ref plane, .. } => {
+ let uv_rect = self.texture_resolver.get_uv_rect(&plane.texture, plane.uv_rect);
+ let instance = CompositeInstance::new_rgb(
+ tile_rect,
+ clip_rect,
+ PremultipliedColorF::WHITE,
+ uv_rect,
+ plane.texture.uses_normalized_uvs(),
+ flip,
+ clip,
+ );
+ let features = instance.get_rgb_features();
+ (
+ instance,
+ BatchTextures::composite_rgb(plane.texture),
+ (
+ CompositeSurfaceFormat::Rgba,
+ surface.image_buffer_kind,
+ features,
+ Some(self.texture_resolver.get_texture_size(&plane.texture).to_f32()),
+ ),
+ )
+ },
+ }
+ }
+ CompositeTileSurface::Texture { surface: ResolvedSurfaceTexture::Native { .. } } => {
+ unreachable!("bug: found native surface in simple composite path");
+ }
+ };
+
+ // Flush batch if shader params or textures changed
+ let flush_batch = !current_textures.is_compatible_with(&textures) ||
+ shader_params != current_shader_params;
+
+ if flush_batch {
+ if !instances.is_empty() {
+ self.draw_instanced_batch(
+ &instances,
+ VertexArrayKind::Composite,
+ ¤t_textures,
+ stats,
+ );
+ instances.clear();
+ }
+ }
+
+ if shader_params != current_shader_params {
+ self.shaders
+ .borrow_mut()
+ .get_composite_shader(shader_params.0, shader_params.1, shader_params.2)
+ .bind(
+ &mut self.device,
+ projection,
+ shader_params.3,
+ &mut self.renderer_errors,
+ &mut self.profile,
+ &mut self.command_log,
+ );
+
+ current_shader_params = shader_params;
+ }
+
+ current_textures = textures;
+
+ // Add instance to current batch
+ instances.push(instance);
+ }
+
+ // Flush the last batch
+ if !instances.is_empty() {
+ self.draw_instanced_batch(
+ &instances,
+ VertexArrayKind::Composite,
+ ¤t_textures,
+ stats,
+ );
+ }
+ }
+
+ // Composite tiles in a swapchain. When using LayerCompositor, we may
+ // split the compositing in to multiple swapchains.
+ fn composite_pass(
+ &mut self,
+ composite_state: &CompositeState,
+ draw_target: DrawTarget,
+ clear_color: ColorF,
+ projection: &default::Transform3D<f32>,
+ results: &mut RenderResults,
+ partial_present_mode: Option<PartialPresentMode>,
+ layer: &SwapChainLayer,
+ ) {
+ self.device.bind_draw_target(draw_target);
+ self.device.disable_depth_write();
+ self.device.disable_depth();
+
+ // If using KHR_partial_update, call eglSetDamageRegion.
+ // This must be called exactly once per frame, and prior to any rendering to the main
+ // framebuffer. Additionally, on Mali-G77 we encountered rendering issues when calling
+ // this earlier in the frame, during offscreen render passes. So call it now, immediately
+ // before rendering to the main framebuffer. See bug 1685276 for details.
+ if let Some(partial_present) = self.compositor_config.partial_present() {
+ if let Some(PartialPresentMode::Single { dirty_rect }) = partial_present_mode {
+ partial_present.set_buffer_damage_region(&[dirty_rect.to_i32()]);
+ }
+ }
+
+ // Clear the framebuffer
+ let clear_color = Some(clear_color.to_array());
+
+ match partial_present_mode {
+ Some(PartialPresentMode::Single { dirty_rect }) => {
+ // There is no need to clear if the dirty rect is occluded. Additionally,
+ // on Mali-G77 we have observed artefacts when calling glClear (even with
+ // the empty scissor rect set) after calling eglSetDamageRegion with an
+ // empty damage region. So avoid clearing in that case. See bug 1709548.
+ if !dirty_rect.is_empty() && layer.occlusion.test(&dirty_rect) {
+ // We have a single dirty rect, so clear only that
+ self.device.clear_target(clear_color,
+ None,
+ Some(draw_target.to_framebuffer_rect(dirty_rect.to_i32())));
+ }
+ }
+ None => {
+ // Partial present is disabled, so clear the entire framebuffer
+ self.device.clear_target(clear_color,
+ None,
+ None);
+ }
+ }
+
+ // Draw opaque tiles
+ let opaque_items = layer.occlusion.opaque_items();
+ if !opaque_items.is_empty() {
+ let opaque_sampler = self.gpu_profiler.start_sampler(GPU_SAMPLER_TAG_OPAQUE);
+ self.set_blend(false, FramebufferKind::Main);
+ self.draw_tile_list(
+ opaque_items.iter(),
+ &composite_state,
+ &composite_state.external_surfaces,
+ projection,
+ &mut results.stats,
+ );
+ self.gpu_profiler.finish_sampler(opaque_sampler);
+ }
+
+ // Draw alpha tiles
+ let alpha_items = layer.occlusion.alpha_items();
+ if !alpha_items.is_empty() {
+ let transparent_sampler = self.gpu_profiler.start_sampler(GPU_SAMPLER_TAG_TRANSPARENT);
+ self.set_blend(true, FramebufferKind::Main);
+ self.set_blend_mode_premultiplied_alpha(FramebufferKind::Main);
+ self.draw_tile_list(
+ alpha_items.iter().rev(),
+ &composite_state,
+ &composite_state.external_surfaces,
+ projection,
+ &mut results.stats,
+ );
+ self.gpu_profiler.finish_sampler(transparent_sampler);
+ }
+ }
+
+ /// Composite picture cache tiles into the framebuffer. This is currently
+ /// the only way that picture cache tiles get drawn. In future, the tiles
+ /// will often be handed to the OS compositor, and this method will be
+ /// rarely used.
+ fn composite_simple(
+ &mut self,
+ composite_state: &CompositeState,
+ frame_device_size: DeviceIntSize,
+ fb_draw_target: DrawTarget,
+ projection: &default::Transform3D<f32>,
+ results: &mut RenderResults,
+ partial_present_mode: Option<PartialPresentMode>,
+ device_size: DeviceIntSize,
+ ) {
+ let _gm = self.gpu_profiler.start_marker("framebuffer");
+ let _timer = self.gpu_profiler.start_timer(GPU_TAG_COMPOSITE);
+
+ let num_tiles = composite_state.tiles.len();
+ self.profile.set(profiler::PICTURE_TILES, num_tiles);
+
+ let (window_is_opaque, enable_screenshot) = match self.compositor_config.layer_compositor() {
+ Some(ref compositor) => {
+ let props = compositor.get_window_properties();
+ (props.is_opaque, props.enable_screenshot)
+ }
+ None => (true, true)
+ };
+
+ let mut input_layers: Vec<CompositorInputLayer> = Vec::new();
+ let mut swapchain_layers = Vec::new();
+ let cap = composite_state.tiles.len();
+ let mut segment_builder = SegmentBuilder::new();
+ let mut tile_index_to_layer_index = vec![None; composite_state.tiles.len()];
+ let mut full_render_occlusion = occlusion::FrontToBackBuilder::with_capacity(cap, cap);
+ let mut layer_compositor_frame_state = LayerCompositorFrameState{
+ tile_states: FastHashMap::default(),
+ rects_without_id: Vec::new(),
+ };
+
+ // Calculate layers with full device rect
+
+ // Add a debug overlay request if enabled
+ if self.debug_overlay_state.is_enabled {
+ self.debug_overlay_state.layer_index = input_layers.len();
+
+ input_layers.push(CompositorInputLayer {
+ usage: CompositorSurfaceUsage::DebugOverlay,
+ is_opaque: false,
+ offset: DeviceIntPoint::zero(),
+ clip_rect: device_size.into(),
+ rounded_clip_rect: device_size.into(),
+ rounded_clip_radii: ClipRadius::EMPTY,
+ });
+
+ swapchain_layers.push(SwapChainLayer {
+ occlusion: occlusion::FrontToBackBuilder::with_capacity(cap, cap),
+ });
+ }
+
+ // NOTE: Tiles here are being iterated in front-to-back order by
+ // z-id, due to the sort in composite_state.end_frame()
+ for (idx, tile) in composite_state.tiles.iter().enumerate() {
+ let device_tile_box = composite_state.get_device_rect(
+ &tile.local_rect,
+ tile.transform_index
+ );
+
+ if let Some(ref _compositor) = self.compositor_config.layer_compositor() {
+ match tile.tile_id {
+ Some(tile_id) => {
+ layer_compositor_frame_state.
+ tile_states
+ .insert(
+ tile_id,
+ CompositeTileState {
+ local_rect: tile.local_rect,
+ local_valid_rect: tile.local_valid_rect,
+ device_clip_rect: tile.device_clip_rect,
+ z_id: tile.z_id,
+ device_tile_box: device_tile_box,
+ visible_rects: Vec::new(),
+ },
+ );
+ }
+ None => {}
+ }
+ }
+
+ // Simple compositor needs the valid rect in device space to match clip rect
+ let device_valid_rect = composite_state
+ .get_device_rect(&tile.local_valid_rect, tile.transform_index);
+
+ let rect = device_tile_box
+ .intersection_unchecked(&tile.device_clip_rect)
+ .intersection_unchecked(&device_valid_rect);
+
+ if rect.is_empty() {
+ continue;
+ }
+
+ // Determine if the tile is an external surface or content
+ let usage = match tile.surface {
+ CompositeTileSurface::Texture { .. } |
+ CompositeTileSurface::Color { .. } => {
+ CompositorSurfaceUsage::Content
+ }
+ CompositeTileSurface::ExternalSurface { external_surface_index } => {
+ match (self.current_compositor_kind, enable_screenshot) {
+ (CompositorKind::Native { .. }, _) | (CompositorKind::Draw { .. }, _) => {
+ CompositorSurfaceUsage::Content
+ }
+ (CompositorKind::Layer { .. }, true) => {
+ CompositorSurfaceUsage::Content
+ }
+ (CompositorKind::Layer { .. }, false) => {
+ let surface = &composite_state.external_surfaces[external_surface_index.0];
+
+ // TODO(gwc): For now, we only select a hardware overlay swapchain if we
+ // have an external image, but it may make sense to do for compositor
+ // surfaces without in future.
+ match surface.external_image_id {
+ Some(external_image_id) => {
+ let image_key = match surface.color_data {
+ ResolvedExternalSurfaceColorData::Rgb { image_dependency, .. } => image_dependency.key,
+ ResolvedExternalSurfaceColorData::Yuv { image_dependencies, .. } => image_dependencies[0].key,
+ };
+
+ CompositorSurfaceUsage::External {
+ image_key,
+ external_image_id,
+ transform_index: tile.transform_index,
+ }
+ }
+ None => {
+ CompositorSurfaceUsage::Content
+ }
+ }
+ }
+ }
+ }
+ };
+
+ if let Some(ref _compositor) = self.compositor_config.layer_compositor() {
+ if let CompositeTileSurface::ExternalSurface { .. } = tile.surface {
+ assert!(tile.tile_id.is_none());
+ // ExternalSurface is not promoted to external composite.
+ if let CompositorSurfaceUsage::Content = usage {
+ layer_compositor_frame_state.rects_without_id.push(rect);
+ }
+ } else {
+ assert!(tile.tile_id.is_some());
+ }
+ }
+
+ // Determine whether we need a new layer, and if so, what kind
+ let new_layer_kind = match input_layers.last() {
+ Some(curr_layer) => {
+ match (curr_layer.usage, usage) {
+ // Content -> content, composite in to same layer
+ (CompositorSurfaceUsage::Content, CompositorSurfaceUsage::Content) => None,
+ (CompositorSurfaceUsage::External { .. }, CompositorSurfaceUsage::Content) => Some(usage),
+
+ // Switch of layer type, or video -> video, need new swapchain
+ (CompositorSurfaceUsage::Content, CompositorSurfaceUsage::External { .. }) |
+ (CompositorSurfaceUsage::External { .. }, CompositorSurfaceUsage::External { .. }) => {
+ // Only create a new layer if we're using LayerCompositor
+ match self.compositor_config {
+ CompositorConfig::Draw { .. } | CompositorConfig::Native { .. } => None,
+ CompositorConfig::Layer { .. } => {
+ Some(usage)
+ }
+ }
+ }
+ (CompositorSurfaceUsage::DebugOverlay, _) => {
+ Some(usage)
+ }
+ // Should not encounter debug layers as new layer
+ (_, CompositorSurfaceUsage::DebugOverlay) => {
+ unreachable!();
+ }
+ }
+ }
+ None => {
+ // No layers yet, so we need a new one
+ Some(usage)
+ }
+ };
+
+ if let Some(new_layer_kind) = new_layer_kind {
+ let (offset, clip_rect, is_opaque, rounded_clip_rect, rounded_clip_radii) = match usage {
+ CompositorSurfaceUsage::Content => {
+ (
+ DeviceIntPoint::zero(),
+ device_size.into(),
+ false, // Assume not opaque, we'll calculate this later
+ device_size.into(),
+ ClipRadius::EMPTY,
+ )
+ }
+ CompositorSurfaceUsage::External { .. } => {
+ let rect = composite_state.get_device_rect(
+ &tile.local_rect,
+ tile.transform_index
+ );
+
+ let clip_rect = tile.device_clip_rect.to_i32();
+ let is_opaque = tile.kind != TileKind::Alpha;
+
+ if self.debug_flags.contains(DebugFlags::EXTERNAL_COMPOSITE_BORDERS) {
+ self.external_composite_debug_items.push(DebugItem::Rect {
+ outer_color: debug_colors::ORANGERED,
+ inner_color: ColorF { r: 0.0, g: 0.0, b: 0.0, a: 0.0 },
+ rect: tile.device_clip_rect,
+ thickness: 10,
+ });
+ }
+
+ let (rounded_clip_rect, rounded_clip_radii) = match tile.clip_index {
+ Some(clip_index) => {
+ let clip = composite_state.get_compositor_clip(clip_index);
+ let radius = ClipRadius {
+ top_left: clip.radius.top_left.width.round() as i32,
+ top_right: clip.radius.top_right.width.round() as i32,
+ bottom_left: clip.radius.bottom_left.width.round() as i32,
+ bottom_right: clip.radius.bottom_right.width.round() as i32,
+ };
+ (clip.rect.to_i32(), radius)
+ }
+ None => {
+ (clip_rect, ClipRadius::EMPTY)
+ }
+ };
+
+ (
+ rect.min.to_i32(),
+ clip_rect,
+ is_opaque,
+ rounded_clip_rect,
+ rounded_clip_radii,
+ )
+ }
+ CompositorSurfaceUsage::DebugOverlay => unreachable!(),
+ };
+
+ input_layers.push(CompositorInputLayer {
+ usage: new_layer_kind,
+ is_opaque,
+ offset,
+ clip_rect,
+ rounded_clip_rect,
+ rounded_clip_radii,
+ });
+
+ swapchain_layers.push(SwapChainLayer {
+ occlusion: occlusion::FrontToBackBuilder::with_capacity(cap, cap),
+ })
+ }
+ tile_index_to_layer_index[idx] = Some(input_layers.len() - 1);
+
+ // Caluclate actual visible tile's rects
+
+ let is_opaque = tile.kind == TileKind::Opaque;
+
+ match tile.clip_index {
+ Some(clip_index) => {
+ let clip = composite_state.get_compositor_clip(clip_index);
+
+ // TODO(gw): Make segment builder generic on unit to avoid casts below.
+ segment_builder.initialize(
+ rect.cast_unit(),
+ None,
+ rect.cast_unit(),
+ );
+ segment_builder.push_clip_rect(
+ clip.rect.cast_unit(),
+ Some(clip.radius),
+ ClipMode::Clip,
+ );
+ segment_builder.build(|segment| {
+ let key = OcclusionItemKey { tile_index: idx, needs_mask: segment.has_mask };
+
+ full_render_occlusion.add(
+ &segment.rect.cast_unit(),
+ is_opaque && !segment.has_mask,
+ key,
+ );
+ });
+ }
+ None => {
+ full_render_occlusion.add(&rect, is_opaque, OcclusionItemKey {
+ tile_index: idx,
+ needs_mask: false,
+ });
+ }
+ }
+ }
+
+ assert_eq!(swapchain_layers.len(), input_layers.len());
+
+ if window_is_opaque {
+ match input_layers.last_mut() {
+ Some(_layer) => {
+ // If the window is opaque, and the last(back) layer is
+ // a content layer then mark that as opaque.
+ // TODO: This causes talos performance regressions.
+ // if let CompositorSurfaceUsage::Content = layer.usage {
+ // layer.is_opaque = true;
+ // }
+ }
+ None => {
+ // If no tiles were present, and we expect an opaque window,
+ // add an empty layer to force a composite that clears the screen,
+ // to match existing semantics.
+ input_layers.push(CompositorInputLayer {
+ usage: CompositorSurfaceUsage::Content,
+ is_opaque: true,
+ offset: DeviceIntPoint::zero(),
+ clip_rect: device_size.into(),
+ rounded_clip_rect: device_size.into(),
+ rounded_clip_radii: ClipRadius::EMPTY,
+ });
+
+ swapchain_layers.push(SwapChainLayer {
+ occlusion: occlusion::FrontToBackBuilder::with_capacity(cap, cap),
+ });
+ }
+ }
+ }
+
+ let mut full_render = self.debug_overlay_state.is_enabled;
+
+ // Start compositing if using OS compositor
+ if let Some(ref mut compositor) = self.compositor_config.layer_compositor() {
+ let input = CompositorInputConfig {
+ enable_screenshot,
+ layers: &input_layers,
+ };
+ full_render |= compositor.begin_frame(&input);
+ }
+
+ // Full render is requested when layer tree is updated.
+ let mut partial_present_mode = if full_render {
+ None
+ } else {
+ partial_present_mode
+ };
+
+ assert_eq!(swapchain_layers.len(), input_layers.len());
+
+ // Recalculate dirty rect for layer compositor
+ if let Some(ref _compositor) = self.compositor_config.layer_compositor() {
+ // Set visible rests of current frame to each tile's CompositeTileState.
+ for item in full_render_occlusion
+ .opaque_items()
+ .iter()
+ .chain(full_render_occlusion.alpha_items().iter()) {
+ let tile = &composite_state.tiles[item.key.tile_index];
+ match tile.tile_id {
+ Some(tile_id) => {
+ if let Some(tile_state) = layer_compositor_frame_state.tile_states.get_mut(&tile_id) {
+ tile_state.visible_rects.push(item.rectangle);
+ } else {
+ unreachable!();
+ }
+ }
+ None => {}
+ }
+ }
+
+ let can_use_partial_present =
+ !self.force_redraw && !full_render &&
+ self.layer_compositor_frame_state_in_prev_frame.is_some();
+
+ if can_use_partial_present {
+ let mut combined_dirty_rect = DeviceRect::zero();
+
+ for tile in composite_state.tiles.iter() {
+ if tile.tile_id.is_none() {
+ match tile.surface {
+ CompositeTileSurface::ExternalSurface { .. } => {}
+ CompositeTileSurface::Texture { .. } |
+ CompositeTileSurface::Color { .. } => {
+ unreachable!();
+ },
+ }
+ continue;
+ }
+
+ assert!(tile.tile_id.is_some());
+
+ let tiles_exists_in_prev_frame =
+ self.layer_compositor_frame_state_in_prev_frame
+ .as_ref()
+ .unwrap()
+ .tile_states
+ .contains_key(&tile.tile_id.unwrap());
+ let tile_id = tile.tile_id.unwrap();
+ let tile_state = layer_compositor_frame_state.tile_states.get(&tile_id).unwrap();
+
+ if tiles_exists_in_prev_frame {
+ let prev_tile_state = self.layer_compositor_frame_state_in_prev_frame
+ .as_ref()
+ .unwrap()
+ .tile_states
+ .get(&tile_id)
+ .unwrap();
+
+ if tile_state.same_state(prev_tile_state) {
+ // Case that tile is same state in previous frame and current frame.
+ // Intersection of tile's dirty rect and tile's visible rects are actual dirty rects.
+ let dirty_rect = composite_state.get_device_rect(
+ &tile.local_dirty_rect,
+ tile.transform_index,
+ );
+ for rect in tile_state.visible_rects.iter() {
+ let visible_dirty_rect = rect.intersection(&dirty_rect);
+ if visible_dirty_rect.is_some() {
+ combined_dirty_rect = combined_dirty_rect.union(&visible_dirty_rect.unwrap());
+ }
+ }
+ } else {
+ // If tile is rendered in previous frame, but its state is different,
+ // both visible rects in previous frame and current frame are dirty rects.
+ for rect in tile_state.visible_rects
+ .iter()
+ .chain(prev_tile_state.visible_rects.iter()) {
+ combined_dirty_rect = combined_dirty_rect.union(&rect);
+ }
+ }
+ } else {
+ // If tile is not rendered in previous frame, its all visible rects are dirty rects.
+ for rect in &tile_state.visible_rects {
+ combined_dirty_rect = combined_dirty_rect.union(&rect);
+ }
+ }
+ }
+
+ // Case that tile is rendered in pervious frame, but not in current frame.
+ for (tile_id, tile_state) in self.layer_compositor_frame_state_in_prev_frame
+ .as_ref()
+ .unwrap()
+ .tile_states
+ .iter() {
+ if !layer_compositor_frame_state.tile_states.contains_key(&tile_id) {
+ for rect in tile_state.visible_rects.iter() {
+ combined_dirty_rect = combined_dirty_rect.union(&rect);
+ }
+ }
+ }
+
+ // Case that ExternalSurface is not promoted to external composite.
+ for rect in layer_compositor_frame_state
+ .rects_without_id
+ .iter()
+ .chain(self.layer_compositor_frame_state_in_prev_frame.as_ref().unwrap().rects_without_id.iter()) {
+ combined_dirty_rect = combined_dirty_rect.union(&rect);
+ }
+
+ partial_present_mode = Some(PartialPresentMode::Single {
+ dirty_rect: combined_dirty_rect,
+ });
+ } else {
+ partial_present_mode = None;
+ }
+
+ self.layer_compositor_frame_state_in_prev_frame = Some(layer_compositor_frame_state);
+ }
+
+ // Check tiles handling with partial_present_mode
+
+ let mut opaque_rounded_corners: HashSet<CompositeRoundedCorner> = HashSet::new();
+
+ // NOTE: Tiles here are being iterated in front-to-back order by
+ // z-id, due to the sort in composite_state.end_frame()
+ for (idx, tile) in composite_state.tiles.iter().enumerate() {
+ let device_tile_box = composite_state.get_device_rect(
+ &tile.local_rect,
+ tile.transform_index
+ );
+
+ // Determine a clip rect to apply to this tile, depending on what
+ // the partial present mode is.
+ let partial_clip_rect = match partial_present_mode {
+ Some(PartialPresentMode::Single { dirty_rect }) => dirty_rect,
+ None => device_tile_box,
+ };
+
+ // Simple compositor needs the valid rect in device space to match clip rect
+ let device_valid_rect = composite_state
+ .get_device_rect(&tile.local_valid_rect, tile.transform_index);
+
+ let rect = device_tile_box
+ .intersection_unchecked(&tile.device_clip_rect)
+ .intersection_unchecked(&partial_clip_rect)
+ .intersection_unchecked(&device_valid_rect);
+
+ if rect.is_empty() {
+ continue;
+ }
+
+ let layer_index = match tile_index_to_layer_index[idx] {
+ None => {
+ // The rect of partial present should be subset of the rect of full render.
+ error!("rect {:?} should have valid layer index", rect);
+ continue;
+ }
+ Some(layer_index) => layer_index,
+ };
+
+ // For normal tiles, add to occlusion tracker
+ let layer = &mut swapchain_layers[layer_index];
+
+ let is_opaque = tile.kind == TileKind::Opaque;
+
+ match tile.clip_index {
+ Some(clip_index) => {
+ let clip = composite_state.get_compositor_clip(clip_index);
+
+ // TODO(gw): Make segment builder generic on unit to avoid casts below.
+ segment_builder.initialize(
+ rect.cast_unit(),
+ None,
+ rect.cast_unit(),
+ );
+ segment_builder.push_clip_rect(
+ clip.rect.cast_unit(),
+ Some(clip.radius),
+ ClipMode::Clip,
+ );
+ segment_builder.build(|segment| {
+ let key = OcclusionItemKey { tile_index: idx, needs_mask: segment.has_mask };
+
+ let radius = if segment.edge_flags ==
+ EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::LEFT &&
+ !clip.radius.top_left.is_empty() {
+ Some(clip.radius.top_left)
+ } else if segment.edge_flags ==
+ EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::RIGHT &&
+ !clip.radius.top_right.is_empty() {
+ Some(clip.radius.top_right)
+ } else if segment.edge_flags ==
+ EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::LEFT &&
+ !clip.radius.bottom_left.is_empty() {
+ Some(clip.radius.bottom_left)
+ } else if segment.edge_flags ==
+ EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::RIGHT &&
+ !clip.radius.bottom_right.is_empty() {
+ Some(clip.radius.bottom_right)
+ } else {
+ None
+ };
+
+ if let Some(radius) = radius {
+ let rounded_corner = CompositeRoundedCorner {
+ rect: segment.rect.cast_unit(),
+ radius: radius,
+ edge_flags: segment.edge_flags,
+ };
+
+ // Drop overdraw rounded rect
+ if opaque_rounded_corners.contains(&rounded_corner) {
+ return;
+ }
+
+ if is_opaque {
+ opaque_rounded_corners.insert(rounded_corner);
+ }
+ }
+
+ layer.occlusion.add(
+ &segment.rect.cast_unit(),
+ is_opaque && !segment.has_mask,
+ key,
+ );
+ });
+ }
+ None => {
+ layer.occlusion.add(&rect, is_opaque, OcclusionItemKey {
+ tile_index: idx,
+ needs_mask: false,
+ });
+ }
+ }
+ }
+
+ assert_eq!(swapchain_layers.len(), input_layers.len());
+ let mut content_clear_color = Some(self.clear_color);
+
+ for (layer_index, (layer, swapchain_layer)) in input_layers.iter().zip(swapchain_layers.iter()).enumerate() {
+ self.device.reset_state();
+
+ // Skip compositing external images or debug layers here
+ match layer.usage {
+ CompositorSurfaceUsage::Content => {}
+ CompositorSurfaceUsage::External { .. } | CompositorSurfaceUsage::DebugOverlay => {
+ continue;
+ }
+ }
+
+ // Only use supplied clear color for first content layer we encounter
+ let clear_color = content_clear_color.take().unwrap_or(ColorF::TRANSPARENT);
+
+ if let Some(ref mut _compositor) = self.compositor_config.layer_compositor() {
+ if let Some(PartialPresentMode::Single { dirty_rect }) = partial_present_mode {
+ if dirty_rect.is_empty() {
+ continue;
+ }
+ }
+ }
+
+ let draw_target = match self.compositor_config {
+ CompositorConfig::Layer { ref mut compositor } => {
+ match partial_present_mode {
+ Some(PartialPresentMode::Single { dirty_rect }) => {
+ compositor.bind_layer(layer_index, &[dirty_rect.to_i32()]);
+ }
+ None => {
+ compositor.bind_layer(layer_index, &[]);
+ }
+ };
+
+ DrawTarget::NativeSurface {
+ offset: -layer.offset,
+ external_fbo_id: 0,
+ dimensions: frame_device_size,
+ }
+ }
+ // Native can be hit when switching compositors (disable when using Layer)
+ CompositorConfig::Draw { .. } | CompositorConfig::Native { .. } => {
+ fb_draw_target
+ }
+ };
+
+ // TODO(gwc): When supporting external attached swapchains, need to skip the composite pass here
+
+ // Draw each compositing pass in to a swap chain
+ self.composite_pass(
+ composite_state,
+ draw_target,
+ clear_color,
+ projection,
+ results,
+ partial_present_mode,
+ swapchain_layer,
+ );
+
+ if let Some(ref mut compositor) = self.compositor_config.layer_compositor() {
+ match partial_present_mode {
+ Some(PartialPresentMode::Single { dirty_rect }) => {
+ compositor.present_layer(layer_index, &[dirty_rect.to_i32()]);
+ }
+ None => {
+ compositor.present_layer(layer_index, &[]);
+ }
+ };
+ }
+ }
+
+ // End frame notify for experimental compositor
+ if let Some(ref mut compositor) = self.compositor_config.layer_compositor() {
+ for (layer_index, layer) in input_layers.iter().enumerate() {
+ // External surfaces need transform applied, but content
+ // surfaces are always at identity
+ let transform = match layer.usage {
+ CompositorSurfaceUsage::Content => CompositorSurfaceTransform::identity(),
+ CompositorSurfaceUsage::External { transform_index, .. } => composite_state.get_compositor_transform(transform_index),
+ CompositorSurfaceUsage::DebugOverlay => CompositorSurfaceTransform::identity(),
+ };
+
+ compositor.add_surface(
+ layer_index,
+ transform,
+ layer.clip_rect,
+ ImageRendering::Auto,
+ layer.rounded_clip_rect,
+ layer.rounded_clip_radii,
+ );
+ }
+ }
+ }
+
fn clear_render_target(
&mut self,
target: &RenderTarget,
@@ -3766,6 +4933,215 @@ impl Renderer {
}
}
+ fn update_deferred_resolves(
+ &mut self,
+ deferred_resolves: &[DeferredResolve],
+ gpu_buffer: &mut GpuBufferF,
+ ) {
+ // The first thing we do is run through any pending deferred
+ // resolves, and use a callback to get the UV rect for this
+ // custom item. Then we patch the resource_rects structure
+ // here before it's uploaded to the GPU.
+ if deferred_resolves.is_empty() {
+ return;
+ }
+
+ let handler = self.external_image_handler
+ .as_mut()
+ .expect("Found external image, but no handler set!");
+
+ for (i, deferred_resolve) in deferred_resolves.iter().enumerate() {
+ self.gpu_profiler.place_marker("deferred resolve");
+ let props = &deferred_resolve.image_properties;
+ let ext_image = props
+ .external_image
+ .expect("BUG: Deferred resolves must be external images!");
+ // Provide rendering information for NativeTexture external images.
+ let image = handler.lock(ext_image.id, ext_image.channel_index, deferred_resolve.is_composited);
+ let texture_target = match ext_image.image_type {
+ ExternalImageType::TextureHandle(target) => target,
+ ExternalImageType::Buffer => {
+ panic!("not a suitable image type in update_deferred_resolves()");
+ }
+ };
+
+ // In order to produce the handle, the external image handler may call into
+ // the GL context and change some states.
+ self.device.reset_state();
+
+ let texture = match image.source {
+ ExternalImageSource::NativeTexture(texture_id) => {
+ ExternalTexture::new(
+ texture_id,
+ texture_target,
+ image.uv,
+ deferred_resolve.rendering,
+ )
+ }
+ ExternalImageSource::Invalid => {
+ warn!("Invalid ext-image");
+ debug!(
+ "For ext_id:{:?}, channel:{}.",
+ ext_image.id,
+ ext_image.channel_index
+ );
+ // Just use 0 as the gl handle for this failed case.
+ ExternalTexture::new(
+ 0,
+ texture_target,
+ image.uv,
+ deferred_resolve.rendering,
+ )
+ }
+ ExternalImageSource::RawData(_) => {
+ panic!("Raw external data is not expected for deferred resolves!");
+ }
+ };
+
+ self.texture_resolver
+ .external_images
+ .insert(DeferredResolveIndex(i as u32), texture);
+
+ let addr = gpu_buffer.resolve_handle(deferred_resolve.handle);
+ let index = addr.as_u32() as usize;
+ gpu_buffer.data[index] = image.uv.to_array().into();
+ gpu_buffer.data[index + 1] = [0f32; 4].into();
+ }
+ }
+
+ fn unlock_external_images(
+ &mut self,
+ deferred_resolves: &[DeferredResolve],
+ ) {
+ if !self.texture_resolver.external_images.is_empty() {
+ let handler = self.external_image_handler
+ .as_mut()
+ .expect("Found external image, but no handler set!");
+
+ for (index, _) in self.texture_resolver.external_images.drain() {
+ let props = &deferred_resolves[index.0 as usize].image_properties;
+ let ext_image = props
+ .external_image
+ .expect("BUG: Deferred resolves must be external images!");
+ handler.unlock(ext_image.id, ext_image.channel_index);
+ }
+ }
+ }
+
+ /// Update the dirty rects based on current compositing mode and config
+ // TODO(gw): This can be tidied up significantly once the Draw compositor
+ // is implemented in terms of the compositor trait.
+ fn calculate_dirty_rects(
+ &mut self,
+ buffer_age: usize,
+ composite_state: &CompositeState,
+ draw_target_dimensions: DeviceIntSize,
+ results: &mut RenderResults,
+ ) -> Option<PartialPresentMode> {
+
+ if let Some(ref _compositor) = self.compositor_config.layer_compositor() {
+ // Calculate dirty rects of layer compositor in composite_simple()
+ return None;
+ }
+
+ let mut partial_present_mode = None;
+
+ let (max_partial_present_rects, draw_previous_partial_present_regions) = match self.current_compositor_kind {
+ CompositorKind::Native { .. } => {
+ // Assume that we can return a single dirty rect for native
+ // compositor for now, and that there is no buffer-age functionality.
+ // These params can be exposed by the compositor capabilities struct
+ // as the Draw compositor is ported to use it.
+ (1, false)
+ }
+ CompositorKind::Draw { draw_previous_partial_present_regions, max_partial_present_rects } => {
+ (max_partial_present_rects, draw_previous_partial_present_regions)
+ }
+ CompositorKind::Layer { .. } => {
+ unreachable!();
+ }
+ };
+
+ if max_partial_present_rects > 0 {
+ let prev_frames_damage_rect = if let Some(..) = self.compositor_config.partial_present() {
+ self.buffer_damage_tracker
+ .get_damage_rect(buffer_age)
+ .or_else(|| Some(DeviceRect::from_size(draw_target_dimensions.to_f32())))
+ } else {
+ None
+ };
+
+ let can_use_partial_present =
+ composite_state.dirty_rects_are_valid &&
+ !self.force_redraw &&
+ !(prev_frames_damage_rect.is_none() && draw_previous_partial_present_regions) &&
+ !self.debug_overlay_state.is_enabled;
+
+ if can_use_partial_present {
+ let mut combined_dirty_rect = DeviceRect::zero();
+ let fb_rect = DeviceRect::from_size(draw_target_dimensions.to_f32());
+
+ // Work out how many dirty rects WR produced, and if that's more than
+ // what the device supports.
+ for tile in &composite_state.tiles {
+ let dirty_rect = composite_state.get_device_rect(
+ &tile.local_dirty_rect,
+ tile.transform_index,
+ );
+
+ // In pathological cases where a tile is extremely zoomed, it
+ // may end up with device coords outside the range of an i32,
+ // so clamp it to the frame buffer rect here, before it gets
+ // casted to an i32 rect below.
+ if let Some(dirty_rect) = dirty_rect.intersection(&fb_rect) {
+ combined_dirty_rect = combined_dirty_rect.union(&dirty_rect);
+ }
+ }
+
+ let combined_dirty_rect = combined_dirty_rect.round();
+ let combined_dirty_rect_i32 = combined_dirty_rect.to_i32();
+ // Return this frame's dirty region. If nothing has changed, don't return any dirty
+ // rects at all (the client can use this as a signal to skip present completely).
+ if !combined_dirty_rect.is_empty() {
+ results.dirty_rects.push(combined_dirty_rect_i32);
+ }
+
+ // Track this frame's dirty region, for calculating subsequent frames' damage.
+ if draw_previous_partial_present_regions {
+ self.buffer_damage_tracker.push_dirty_rect(&combined_dirty_rect);
+ }
+
+ // If the implementation requires manually keeping the buffer consistent,
+ // then we must combine this frame's dirty region with that of previous frames
+ // to determine the total_dirty_rect. The is used to determine what region we
+ // render to, and is what we send to the compositor as the buffer damage region
+ // (eg for KHR_partial_update).
+ let total_dirty_rect = if draw_previous_partial_present_regions {
+ combined_dirty_rect.union(&prev_frames_damage_rect.unwrap())
+ } else {
+ combined_dirty_rect
+ };
+
+ partial_present_mode = Some(PartialPresentMode::Single {
+ dirty_rect: total_dirty_rect,
+ });
+ } else {
+ // If we don't have a valid partial present scenario, return a single
+ // dirty rect to the client that covers the entire framebuffer.
+ let fb_rect = DeviceIntRect::from_size(
+ draw_target_dimensions,
+ );
+ results.dirty_rects.push(fb_rect);
+
+ if draw_previous_partial_present_regions {
+ self.buffer_damage_tracker.push_dirty_rect(&fb_rect.to_f32());
+ }
+ }
+ }
+
+ partial_present_mode
+ }
+
fn bind_frame_data(&mut self, frame: &mut Frame) {
profile_scope!("bind_frame_data");
@@ -4189,6 +5565,90 @@ impl Renderer {
);
}
+ fn composite_frame(
+ &mut self,
+ frame: &mut Frame,
+ device_size: Option<DeviceIntSize>,
+ results: &mut RenderResults,
+ present_mode: Option<PartialPresentMode>,
+ ) {
+ profile_scope!("main target");
+ if let Some(device_size) = device_size {
+ if let Some(history) = &mut self.command_log {
+ history.begin_render_target("Window", device_size);
+ }
+
+ results.stats.color_target_count += 1;
+ results.picture_cache_debug = mem::replace(
+ &mut frame.composite_state.picture_cache_debug,
+ PictureCacheDebugInfo::new(),
+ );
+
+ let size = frame.device_rect.size().to_f32();
+ let surface_origin_is_top_left = self.device.surface_origin_is_top_left();
+ let (bottom, top) = if surface_origin_is_top_left {
+ (0.0, size.height)
+ } else {
+ (size.height, 0.0)
+ };
+
+ let projection = Transform3D::ortho(
+ 0.0,
+ size.width,
+ bottom,
+ top,
+ self.device.ortho_near_plane(),
+ self.device.ortho_far_plane(),
+ );
+
+ let fb_scale = Scale::<_, _, FramebufferPixel>::new(1i32);
+ let mut fb_rect = frame.device_rect * fb_scale;
+
+ if !surface_origin_is_top_left {
+ let h = fb_rect.height();
+ fb_rect.min.y = device_size.height - fb_rect.max.y;
+ fb_rect.max.y = fb_rect.min.y + h;
+ }
+
+ let draw_target = DrawTarget::Default {
+ rect: fb_rect,
+ total_size: device_size * fb_scale,
+ surface_origin_is_top_left,
+ };
+
+ // If we have a native OS compositor, then make use of that interface
+ // to specify how to composite each of the picture cache surfaces.
+ match self.current_compositor_kind {
+ CompositorKind::Native { .. } => {
+ // We have already queued surfaces for early native composition by this point.
+ // All that is left is to finally update any external native surfaces that were
+ // invalidated so that composition can complete.
+ self.update_external_native_surfaces(
+ &frame.composite_state.external_surfaces,
+ results,
+ );
+ }
+ CompositorKind::Draw { .. } | CompositorKind::Layer { .. } => {
+ self.composite_simple(
+ &frame.composite_state,
+ frame.device_rect.size(),
+ draw_target,
+ &projection,
+ results,
+ present_mode,
+ device_size,
+ );
+ }
+ }
+ // Reset force_redraw. It was used in composite_simple() with layer compositor.
+ self.force_redraw = false;
+ } else {
+ // Rendering a frame without presenting it will confuse the partial
+ // present logic, so force a full present for the next frame.
+ self.force_redraw = true;
+ }
+ }
+
pub fn debug_renderer(&mut self) -> Option<&mut DebugRenderer> {
self.debug.get_mut(&mut self.device)
}