Bug 1656533 - Handle buffer ages other than 2 for EGL_EXT_buffer_age. r=sotaro,gw
Add a trait PartialPresentCompositor with a function get_buffer_age(), and allow gecko to pass an implementation to webrender during initialization. This allows webrender to query the age of the current backbuffer during compositing. Make webrender track the previous 2 frame's dirty rects, rather than just the previous 1 frame's, allowing it to calculate the total damage rect for buffer ages of up to 3. If the age is greater than 3, treat the entire buffer as invalid. Also handle special cases of ages 0 and 1, 0 meaning the entire buffer is invalid, and 1 meaning the entire buffer is valid. Make gecko stop requesting a full render for buffer ages other than 2, as webrender can now handle these cases itself. Differential Revision: https://phabricator.services.mozilla.com/D91202
This commit is contained in:
@@ -125,6 +125,12 @@ void wr_compositor_unmap_tile(void* aCompositor) {
|
||||
compositor->UnmapTile();
|
||||
}
|
||||
|
||||
size_t wr_partial_present_compositor_get_buffer_age(const void* aCompositor) {
|
||||
const RenderCompositor* compositor =
|
||||
static_cast<const RenderCompositor*>(aCompositor);
|
||||
return compositor->GetBufferAge();
|
||||
}
|
||||
|
||||
/* static */
|
||||
UniquePtr<RenderCompositor> RenderCompositor::Create(
|
||||
RefPtr<widget::CompositorWidget>&& aWidget, nsACString& aError) {
|
||||
|
||||
@@ -126,6 +126,11 @@ class RenderCompositor {
|
||||
virtual bool RequestFullRender() { return false; }
|
||||
virtual uint32_t GetMaxPartialPresentRects() { return 0; }
|
||||
virtual bool ShouldDrawPreviousPartialPresentRegions() { return false; }
|
||||
// Returns the age of the current backbuffer., This should be used, if
|
||||
// ShouldDrawPreviousPartialPresentRegions() returns true, to determine the
|
||||
// region which must be rendered in addition to the current frame's dirty
|
||||
// rect.
|
||||
virtual size_t GetBufferAge() const { return 0; }
|
||||
|
||||
// Whether the surface origin is top-left.
|
||||
virtual bool SurfaceOriginIsTopLeft() { return false; }
|
||||
|
||||
@@ -238,7 +238,7 @@ bool RenderCompositorEGL::UsePartialPresent() {
|
||||
return gfx::gfxVars::WebRenderMaxPartialPresentRects() > 0;
|
||||
}
|
||||
|
||||
bool RenderCompositorEGL::RequestFullRender() { return mBufferAge != 2; }
|
||||
bool RenderCompositorEGL::RequestFullRender() { return mBufferAge == 0; }
|
||||
|
||||
uint32_t RenderCompositorEGL::GetMaxPartialPresentRects() {
|
||||
return gfx::gfxVars::WebRenderMaxPartialPresentRects();
|
||||
@@ -248,4 +248,6 @@ bool RenderCompositorEGL::ShouldDrawPreviousPartialPresentRegions() {
|
||||
return gl::GLContextEGL::Cast(gl())->HasBufferAge();
|
||||
}
|
||||
|
||||
size_t RenderCompositorEGL::GetBufferAge() const { return mBufferAge; }
|
||||
|
||||
} // namespace mozilla::wr
|
||||
|
||||
@@ -42,6 +42,7 @@ class RenderCompositorEGL : public RenderCompositor {
|
||||
bool RequestFullRender() override;
|
||||
uint32_t GetMaxPartialPresentRects() override;
|
||||
bool ShouldDrawPreviousPartialPresentRegions() override;
|
||||
size_t GetBufferAge() const override;
|
||||
|
||||
ipc::FileDescriptor GetAndResetReleaseFence() override;
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ uint32_t RenderCompositorOGL::GetMaxPartialPresentRects() {
|
||||
}
|
||||
|
||||
bool RenderCompositorOGL::RequestFullRender() {
|
||||
return mIsEGL && (mBufferAge != 2);
|
||||
return mIsEGL && (mBufferAge == 0);
|
||||
}
|
||||
|
||||
bool RenderCompositorOGL::UsePartialPresent() {
|
||||
@@ -124,5 +124,12 @@ bool RenderCompositorOGL::ShouldDrawPreviousPartialPresentRegions() {
|
||||
return mIsEGL && gl::GLContextEGL::Cast(gl())->HasBufferAge();
|
||||
}
|
||||
|
||||
size_t RenderCompositorOGL::GetBufferAge() const {
|
||||
if (mIsEGL) {
|
||||
return mBufferAge;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace wr
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -39,6 +39,7 @@ class RenderCompositorOGL : public RenderCompositor {
|
||||
bool RequestFullRender() override;
|
||||
uint32_t GetMaxPartialPresentRects() override;
|
||||
bool ShouldDrawPreviousPartialPresentRegions() override;
|
||||
size_t GetBufferAge() const override;
|
||||
|
||||
protected:
|
||||
RefPtr<gl::GLContext> mGL;
|
||||
|
||||
@@ -139,6 +139,7 @@ class NewRenderer : public RendererEvent {
|
||||
compositor->ShouldUseNativeCompositor() ? compositor.get()
|
||||
: nullptr,
|
||||
compositor->GetMaxUpdateRects(),
|
||||
compositor->UsePartialPresent() ? compositor.get() : nullptr,
|
||||
compositor->GetMaxPartialPresentRects(),
|
||||
compositor->ShouldDrawPreviousPartialPresentRegions(), mDocHandle,
|
||||
&wrRenderer, mMaxTextureSize, &errorMessage,
|
||||
|
||||
@@ -36,9 +36,9 @@ use tracy_rs::register_thread_with_profiler;
|
||||
use webrender::{
|
||||
api::units::*, api::*, render_api::*, set_profiler_hooks, AsyncPropertySampler, AsyncScreenshotHandle, Compositor,
|
||||
CompositorCapabilities, CompositorConfig, CompositorSurfaceTransform, DebugFlags, Device, FastHashMap,
|
||||
NativeSurfaceId, NativeSurfaceInfo, NativeTileId, PipelineInfo, ProfilerHooks, RecordedFrameHandle, Renderer,
|
||||
RendererOptions, RendererStats, SceneBuilderHooks, ShaderPrecacheFlags, Shaders, ThreadListener, UploadMethod,
|
||||
WrShaders, ONE_TIME_USAGE_HINT,
|
||||
NativeSurfaceId, NativeSurfaceInfo, NativeTileId, PartialPresentCompositor, PipelineInfo, ProfilerHooks,
|
||||
RecordedFrameHandle, Renderer, RendererOptions, RendererStats, SceneBuilderHooks, ShaderPrecacheFlags, Shaders,
|
||||
ThreadListener, UploadMethod, WrShaders, ONE_TIME_USAGE_HINT,
|
||||
};
|
||||
use wr_malloc_size_of::MallocSizeOfOps;
|
||||
|
||||
@@ -1240,6 +1240,8 @@ extern "C" {
|
||||
stride: &mut i32,
|
||||
);
|
||||
fn wr_compositor_unmap_tile(compositor: *mut c_void);
|
||||
|
||||
fn wr_partial_present_compositor_get_buffer_age(compositor: *const c_void) -> usize;
|
||||
}
|
||||
|
||||
pub struct WrCompositor(*mut c_void);
|
||||
@@ -1354,6 +1356,14 @@ impl Compositor for WrCompositor {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WrPartialPresentCompositor(*mut c_void);
|
||||
|
||||
impl PartialPresentCompositor for WrPartialPresentCompositor {
|
||||
fn get_buffer_age(&self) -> usize {
|
||||
unsafe { wr_partial_present_compositor_get_buffer_age(self.0) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about the underlying data buffer of a mapped tile.
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
@@ -1430,6 +1440,7 @@ pub extern "C" fn wr_window_new(
|
||||
document_id: u32,
|
||||
compositor: *mut c_void,
|
||||
max_update_rects: usize,
|
||||
partial_present_compositor: *mut c_void,
|
||||
max_partial_present_rects: usize,
|
||||
draw_previous_partial_present_regions: bool,
|
||||
out_handle: &mut *mut DocumentHandle,
|
||||
@@ -1520,6 +1531,11 @@ pub extern "C" fn wr_window_new(
|
||||
CompositorConfig::Draw {
|
||||
max_partial_present_rects,
|
||||
draw_previous_partial_present_regions,
|
||||
partial_present: if partial_present_compositor != ptr::null_mut() {
|
||||
Some(Box::new(WrPartialPresentCompositor(partial_present_compositor)))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -200,10 +200,13 @@ pub enum CompositorConfig {
|
||||
/// then the operating system supports a form of 'partial present' where
|
||||
/// only dirty regions of the framebuffer need to be updated.
|
||||
max_partial_present_rects: usize,
|
||||
/// If this is true, WR would draw the previous frame's dirty region when
|
||||
/// If this is true, WR must draw the previous frames' dirty regions when
|
||||
/// doing a partial present. This is used for EGL which requires the front
|
||||
/// buffer to always be fully consistent.
|
||||
draw_previous_partial_present_regions: bool,
|
||||
/// A client provided interface to a compositor handling partial present.
|
||||
/// Required if webrender must query the backbuffer's age.
|
||||
partial_present: Option<Box<dyn PartialPresentCompositor>>,
|
||||
},
|
||||
/// Use a native OS compositor to draw tiles. This requires clients to implement
|
||||
/// the Compositor trait, but can be significantly more power efficient on operating
|
||||
@@ -229,6 +232,18 @@ impl CompositorConfig {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn partial_present(&mut self) -> Option<&mut Box<dyn PartialPresentCompositor>> {
|
||||
match self {
|
||||
CompositorConfig::Native { .. } => {
|
||||
None
|
||||
}
|
||||
CompositorConfig::Draw { ref mut partial_present, .. } => {
|
||||
partial_present.as_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Default for CompositorConfig {
|
||||
@@ -237,6 +252,7 @@ impl Default for CompositorConfig {
|
||||
CompositorConfig::Draw {
|
||||
max_partial_present_rects: 0,
|
||||
draw_previous_partial_present_regions: false,
|
||||
partial_present: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -999,6 +1015,16 @@ pub trait Compositor {
|
||||
fn get_capabilities(&self) -> CompositorCapabilities;
|
||||
}
|
||||
|
||||
/// Defines an interface to a non-native (application-level) Compositor which handles
|
||||
/// partial present. This is required if webrender must query the backbuffer's age.
|
||||
/// TODO: Use the Compositor trait for native and non-native compositors, and integrate
|
||||
/// this functionality there.
|
||||
pub trait PartialPresentCompositor {
|
||||
/// Returns the age of the current backbuffer. This should be used, if
|
||||
/// draw_previous_partial_present_regions is true, to determine the
|
||||
/// region which must be rendered in addition to the current frame's dirty rect.
|
||||
fn get_buffer_age(&self) -> usize;
|
||||
}
|
||||
|
||||
/// Information about an opaque surface used to occlude tiles.
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
|
||||
@@ -209,8 +209,8 @@ pub extern crate api;
|
||||
extern crate webrender_build;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use crate::composite::{CompositorConfig, Compositor, CompositorCapabilities};
|
||||
pub use crate::composite::{NativeSurfaceId, NativeTileId, NativeSurfaceInfo, CompositorSurfaceTransform};
|
||||
pub use crate::composite::{CompositorConfig, Compositor, CompositorCapabilities, CompositorSurfaceTransform};
|
||||
pub use crate::composite::{NativeSurfaceId, NativeTileId, NativeSurfaceInfo, PartialPresentCompositor};
|
||||
pub use crate::device::{UploadMethod, VertexUsageHint, get_gl_target, get_unoptimized_shader_source};
|
||||
pub use crate::device::{ProgramBinary, ProgramCache, ProgramCacheObserver, FormatDesc};
|
||||
pub use crate::device::Device;
|
||||
|
||||
@@ -2172,6 +2172,61 @@ impl VertexDataTextures {
|
||||
}
|
||||
}
|
||||
|
||||
/// Tracks buffer damage rects over a series of frames.
|
||||
#[derive(Debug)]
|
||||
struct BufferDamageTracker {
|
||||
damage_rects: [DeviceRect; 2],
|
||||
current_offset: usize,
|
||||
}
|
||||
|
||||
impl Default for BufferDamageTracker {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
damage_rects: [DeviceRect::default(); 2],
|
||||
current_offset: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BufferDamageTracker {
|
||||
/// Sets the damage rect for the current frame. Should only be called *after*
|
||||
/// get_damage_rect() has been called to get the current backbuffer's damage rect.
|
||||
fn push_dirty_rect(&mut self, rect: &DeviceRect) {
|
||||
self.damage_rects[self.current_offset] = rect.clone();
|
||||
self.current_offset = match self.current_offset {
|
||||
0 => self.damage_rects.len() - 1,
|
||||
n => n - 1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the damage rect for the current backbuffer, given the backbuffer's age.
|
||||
/// (The number of frames since it was previously the backbuffer.)
|
||||
/// Returns an empty rect if the buffer is valid, and None if the entire buffer is invalid.
|
||||
fn get_damage_rect(&self, buffer_age: usize) -> Option<DeviceRect> {
|
||||
match buffer_age {
|
||||
// 0 means this is a new buffer, so is completely invalid.
|
||||
0 => None,
|
||||
// 1 means this backbuffer was also the previous frame's backbuffer
|
||||
// (so must have been copied to the frontbuffer). It is therefore entirely valid.
|
||||
1 => Some(DeviceRect::zero()),
|
||||
// We must calculate the union of the damage rects since this buffer was previously
|
||||
// the backbuffer.
|
||||
n if n <= self.damage_rects.len() + 1 => {
|
||||
Some(
|
||||
self.damage_rects.iter()
|
||||
.cycle()
|
||||
.skip(self.current_offset + 1)
|
||||
.take(n - 1)
|
||||
.fold(DeviceRect::zero(), |acc, r| acc.union(r))
|
||||
)
|
||||
}
|
||||
// The backbuffer is older than the number of frames for which we track,
|
||||
// so we treat it as entirely invalid.
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The renderer is responsible for submitting to the GPU the work prepared by the
|
||||
/// RenderBackend.
|
||||
///
|
||||
@@ -2298,10 +2353,10 @@ pub struct Renderer {
|
||||
/// State related to the debug / profiling overlays
|
||||
debug_overlay_state: DebugOverlayState,
|
||||
|
||||
/// The dirty rectangle from the previous frame, used on platforms that
|
||||
/// require keeping the front buffer fully correct when doing
|
||||
/// Tracks the dirty rectangles from previous frames. Used on platforms
|
||||
/// that require keeping the front buffer fully correct when doing
|
||||
/// partial present (e.g. unix desktop with EGL_EXT_buffer_age).
|
||||
prev_dirty_rect: DeviceRect,
|
||||
buffer_damage_tracker: BufferDamageTracker,
|
||||
|
||||
max_primitive_instance_count: usize,
|
||||
}
|
||||
@@ -2592,7 +2647,7 @@ impl Renderer {
|
||||
};
|
||||
|
||||
let compositor_kind = match options.compositor_config {
|
||||
CompositorConfig::Draw { max_partial_present_rects, draw_previous_partial_present_regions } => {
|
||||
CompositorConfig::Draw { max_partial_present_rects, draw_previous_partial_present_regions, .. } => {
|
||||
CompositorKind::Draw { max_partial_present_rects, draw_previous_partial_present_regions }
|
||||
}
|
||||
CompositorConfig::Native { ref compositor, max_update_rects, .. } => {
|
||||
@@ -2876,7 +2931,7 @@ impl Renderer {
|
||||
current_compositor_kind: compositor_kind,
|
||||
allocated_native_surfaces: FastHashSet::default(),
|
||||
debug_overlay_state: DebugOverlayState::new(),
|
||||
prev_dirty_rect: DeviceRect::zero(),
|
||||
buffer_damage_tracker: BufferDamageTracker::default(),
|
||||
max_primitive_instance_count:
|
||||
RendererOptions::MAX_INSTANCE_BUFFER_SIZE / mem::size_of::<PrimitiveInstanceData>(),
|
||||
};
|
||||
@@ -5167,9 +5222,19 @@ impl Renderer {
|
||||
let mut partial_present_mode = None;
|
||||
|
||||
if max_partial_present_rects > 0 {
|
||||
let can_use_partial_present = composite_state.dirty_rects_are_valid &&
|
||||
!self.force_redraw &&
|
||||
!self.debug_overlay_state.is_enabled;
|
||||
let prev_frames_damage_rect = if let Some(partial_present) = self.compositor_config.partial_present() {
|
||||
self.buffer_damage_tracker
|
||||
.get_damage_rect(partial_present.get_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();
|
||||
@@ -5189,18 +5254,21 @@ impl Renderer {
|
||||
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,
|
||||
// combine the previous frame's damage for tile clipping.
|
||||
// combine the previous frames' damage rects for tile clipping.
|
||||
// (Not for the returned region though, that should be from this frame only)
|
||||
partial_present_mode = Some(PartialPresentMode::Single {
|
||||
dirty_rect: if draw_previous_partial_present_regions {
|
||||
combined_dirty_rect.union(&self.prev_dirty_rect)
|
||||
} else { combined_dirty_rect },
|
||||
combined_dirty_rect.union(&prev_frames_damage_rect.unwrap())
|
||||
} else {
|
||||
combined_dirty_rect
|
||||
},
|
||||
});
|
||||
|
||||
if draw_previous_partial_present_regions {
|
||||
self.prev_dirty_rect = combined_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.
|
||||
@@ -5211,7 +5279,7 @@ impl Renderer {
|
||||
results.dirty_rects.push(fb_rect);
|
||||
|
||||
if draw_previous_partial_present_regions {
|
||||
self.prev_dirty_rect = fb_rect.to_f32();
|
||||
self.buffer_damage_tracker.push_dirty_rect(&fb_rect.to_f32());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7863,3 +7931,36 @@ impl CompositeState {
|
||||
compositor.start_compositing();
|
||||
}
|
||||
}
|
||||
|
||||
mod tests {
|
||||
#[test]
|
||||
fn test_buffer_damage_tracker() {
|
||||
use super::BufferDamageTracker;
|
||||
use api::units::{DevicePoint, DeviceRect, DeviceSize};
|
||||
|
||||
let mut tracker = BufferDamageTracker::default();
|
||||
assert_eq!(tracker.get_damage_rect(0), None);
|
||||
assert_eq!(tracker.get_damage_rect(1), Some(DeviceRect::zero()));
|
||||
assert_eq!(tracker.get_damage_rect(2), Some(DeviceRect::zero()));
|
||||
assert_eq!(tracker.get_damage_rect(3), Some(DeviceRect::zero()));
|
||||
assert_eq!(tracker.get_damage_rect(4), None);
|
||||
|
||||
let damage1 = DeviceRect::new(DevicePoint::new(10, 10), DeviceSize::new(10, 10));
|
||||
let damage2 = DeviceRect::new(DevicePoint::new(20, 20), DeviceSize::new(10, 10));
|
||||
let combined = damage1.union(&damage2);
|
||||
|
||||
tracker.push_dirty_rect(&damage1);
|
||||
assert_eq!(tracker.get_damage_rect(0), None);
|
||||
assert_eq!(tracker.get_damage_rect(1), Some(DeviceRect::zero()));
|
||||
assert_eq!(tracker.get_damage_rect(2), Some(damage1));
|
||||
assert_eq!(tracker.get_damage_rect(3), Some(damage1));
|
||||
assert_eq!(tracker.get_damage_rect(4), None);
|
||||
|
||||
tracker.push_dirty_rect(&damage2);
|
||||
assert_eq!(tracker.get_damage_rect(0), None);
|
||||
assert_eq!(tracker.get_damage_rect(1), Some(DeviceRect::zero()));
|
||||
assert_eq!(tracker.get_damage_rect(2), Some(damage2));
|
||||
assert_eq!(tracker.get_damage_rect(3), Some(combined));
|
||||
assert_eq!(tracker.get_damage_rect(4), None);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user