Bug 1620076 - Partial compositing (damage) with EGL_EXT_buffer_age in WebRender r=jgilbert

EGL with buffer-age requires the application to keep the front buffer
fully consistent. This means we have to draw the previous frame's
damage as well. (But we don't need to include it in the hint we're
sending to the system compositor via SwapBuffersWithDamage.)

Differential Revision: https://phabricator.services.mozilla.com/D61062
This commit is contained in:
Greg V 2020-05-22 18:15:13 +00:00
Родитель 24b7b9ddd6
Коммит 22c3a0e8d8
12 изменённых файлов: 106 добавлений и 13 удалений

Просмотреть файл

@ -81,6 +81,9 @@ class GLContextEGL : public GLContext {
EGLSurface GetEGLSurface() const { return mSurface; }
bool HasBufferAge() const;
EGLint GetBufferAge() const;
bool BindTex2DOffscreen(GLContext* aOffscreen);
void UnbindTex2DOffscreen(GLContext* aOffscreen);
void BindOffscreenFramebuffer();

Просмотреть файл

@ -569,6 +569,24 @@ void GLContextEGL::GetWSIInfo(nsCString* const out) const {
// for the lifetime of this context.
void GLContextEGL::HoldSurface(gfxASurface* aSurf) { mThebesSurface = aSurf; }
bool GLContextEGL::HasBufferAge() const {
return mEgl->IsExtensionSupported(GLLibraryEGL::EXT_buffer_age);
}
EGLint GLContextEGL::GetBufferAge() const {
EGLSurface surface =
mSurfaceOverride != EGL_NO_SURFACE ? mSurfaceOverride : mSurface;
if (surface && HasBufferAge()) {
EGLint result;
mEgl->fQuerySurface(mEgl->Display(), surface, LOCAL_EGL_BUFFER_AGE_EXT,
&result);
return result;
}
return 0;
}
#define LOCAL_EGL_CONTEXT_PROVOKING_VERTEX_DONT_CARE_MOZ 0x6000
already_AddRefed<GLContextEGL> GLContextEGL::CreateGLContext(

Просмотреть файл

@ -76,7 +76,8 @@ static const char* sEGLExtensionNames[] = {
"EGL_KHR_create_context_no_error",
"EGL_MOZ_create_context_provoking_vertex_dont_care",
"EGL_EXT_swap_buffers_with_damage",
"EGL_KHR_swap_buffers_with_damage"};
"EGL_KHR_swap_buffers_with_damage",
"EGL_EXT_buffer_age"};
PRLibrary* LoadApitraceLibrary() {
const char* path = nullptr;

Просмотреть файл

@ -91,6 +91,7 @@ class GLLibraryEGL final {
MOZ_create_context_provoking_vertex_dont_care,
EXT_swap_buffers_with_damage,
KHR_swap_buffers_with_damage,
EXT_buffer_age,
Extensions_Max
};

Просмотреть файл

@ -107,6 +107,7 @@ class RenderCompositor {
virtual bool UsePartialPresent() { return false; }
virtual bool RequestFullRender() { return false; }
virtual uint32_t GetMaxPartialPresentRects() { return 0; }
virtual bool ShouldDrawPreviousPartialPresentRegions() { return false; }
// Whether the surface origin is top-left.
virtual bool SurfaceOriginIsTopLeft() { return false; }

Просмотреть файл

@ -10,6 +10,7 @@
#include "GLContextEGL.h"
#include "GLContextProvider.h"
#include "GLLibraryEGL.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/webrender/RenderThread.h"
#include "mozilla/widget/CompositorWidget.h"
@ -86,21 +87,34 @@ bool RenderCompositorEGL::BeginFrame() {
gl()->MakeCurrent(); // DestroyUnused can change the current context!
#endif
// sets 0 if buffer_age is not supported
mBufferAge = gl::GLContextEGL::Cast(gl())->GetBufferAge();
return true;
}
RenderedFrameId RenderCompositorEGL::EndFrame(
const nsTArray<DeviceIntRect>& aDirtyRects) {
RenderedFrameId frameId = GetNextRenderFrameId();
if (mEGLSurface != EGL_NO_SURFACE) {
gl()->SwapBuffers();
if (mEGLSurface != EGL_NO_SURFACE && aDirtyRects.Length() > 0) {
gfx::IntRegion bufferInvalid;
for (const DeviceIntRect& rect : aDirtyRects) {
const auto width = std::min(rect.size.width, GetBufferSize().width);
const auto height = std::min(rect.size.height, GetBufferSize().height);
const auto left =
std::max(0, std::min(rect.origin.x, GetBufferSize().width));
const auto bottom =
std::max(0, std::min(rect.origin.y + height, GetBufferSize().height));
bufferInvalid.OrWith(
gfx::IntRect(left, (GetBufferSize().height - bottom), width, height));
}
gl()->SetDamage(bufferInvalid);
}
gl()->SwapBuffers();
return frameId;
}
void RenderCompositorEGL::Pause() {
DestroyEGLSurface();
}
void RenderCompositorEGL::Pause() { DestroyEGLSurface(); }
bool RenderCompositorEGL::Resume() {
#ifdef MOZ_WIDGET_ANDROID
@ -179,4 +193,18 @@ CompositorCapabilities RenderCompositorEGL::GetCompositorCapabilities() {
return caps;
}
bool RenderCompositorEGL::UsePartialPresent() {
return gfx::gfxVars::WebRenderMaxPartialPresentRects() > 0;
}
bool RenderCompositorEGL::RequestFullRender() { return mBufferAge != 2; }
uint32_t RenderCompositorEGL::GetMaxPartialPresentRects() {
return gfx::gfxVars::WebRenderMaxPartialPresentRects();
}
bool RenderCompositorEGL::ShouldDrawPreviousPartialPresentRegions() {
return gl::GLContextEGL::Cast(gl())->HasBufferAge();
}
} // namespace mozilla::wr

Просмотреть файл

@ -37,6 +37,12 @@ class RenderCompositorEGL : public RenderCompositor {
CompositorCapabilities GetCompositorCapabilities() override;
// Interface for partial present
bool UsePartialPresent() override;
bool RequestFullRender() override;
uint32_t GetMaxPartialPresentRects() override;
bool ShouldDrawPreviousPartialPresentRegions() override;
protected:
EGLSurface CreateEGLSurface();
@ -47,6 +53,8 @@ class RenderCompositorEGL : public RenderCompositor {
// On android we must track our own surface size.
LayoutDeviceIntSize mEGLSurfaceSize;
#endif
EGLint mBufferAge;
};
} // namespace wr

Просмотреть файл

@ -116,8 +116,9 @@ class NewRenderer : public RendererEvent {
compositor->ShouldUseNativeCompositor() ? compositor.get()
: nullptr,
compositor->GetMaxUpdateRects(),
compositor->GetMaxPartialPresentRects(), mDocHandle, &wrRenderer,
mMaxTextureSize,
compositor->GetMaxPartialPresentRects(),
compositor->ShouldDrawPreviousPartialPresentRegions(), mDocHandle,
&wrRenderer, mMaxTextureSize,
StaticPrefs::gfx_webrender_enable_gpu_markers_AtStartup(),
panic_on_gl_error)) {
// wr_window_new puts a message into gfxCriticalNote if it returns false

Просмотреть файл

@ -1318,6 +1318,7 @@ pub extern "C" fn wr_window_new(
compositor: *mut c_void,
max_update_rects: usize,
max_partial_present_rects: usize,
draw_previous_partial_present_regions: bool,
out_handle: &mut *mut DocumentHandle,
out_renderer: &mut *mut Renderer,
out_max_texture_size: *mut i32,
@ -1396,6 +1397,7 @@ pub extern "C" fn wr_window_new(
} else {
CompositorConfig::Draw {
max_partial_present_rects,
draw_previous_partial_present_regions,
}
};

Просмотреть файл

@ -186,6 +186,10 @@ 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
/// 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,
},
/// 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
@ -218,6 +222,7 @@ impl Default for CompositorConfig {
fn default() -> Self {
CompositorConfig::Draw {
max_partial_present_rects: 0,
draw_previous_partial_present_regions: false,
}
}
}
@ -233,6 +238,8 @@ pub enum CompositorKind {
Draw {
/// Partial present support.
max_partial_present_rects: usize,
/// Draw previous regions when doing partial present.
draw_previous_partial_present_regions: bool,
},
/// Native OS compositor.
Native {
@ -248,6 +255,7 @@ impl Default for CompositorKind {
fn default() -> Self {
CompositorKind::Draw {
max_partial_present_rects: 0,
draw_previous_partial_present_regions: false,
}
}
}

Просмотреть файл

@ -1488,7 +1488,8 @@ impl RenderBackend {
// external image with NativeTexture or when platform requested to composite frame.
if invalidate_rendered_frame {
doc.rendered_frame_is_valid = false;
if let CompositorKind::Draw { max_partial_present_rects } = doc.scene.config.compositor_kind {
if let CompositorKind::Draw { max_partial_present_rects, .. } = doc.scene.config.compositor_kind {
// When partial present is enabled, we need to force redraw.
if max_partial_present_rects > 0 {
let msg = ResultMsg::ForceRedraw;

Просмотреть файл

@ -2114,6 +2114,11 @@ 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
/// partial present (e.g. unix desktop with EGL_EXT_buffer_age).
prev_dirty_rect: DeviceRect,
}
#[derive(Debug)]
@ -2391,8 +2396,8 @@ impl Renderer {
};
let compositor_kind = match options.compositor_config {
CompositorConfig::Draw { max_partial_present_rects } => {
CompositorKind::Draw { max_partial_present_rects }
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, .. } => {
let capabilities = compositor.get_capabilities();
@ -2668,6 +2673,7 @@ impl Renderer {
current_compositor_kind: compositor_kind,
allocated_native_surfaces: FastHashSet::default(),
debug_overlay_state: DebugOverlayState::new(),
prev_dirty_rect: DeviceRect::zero(),
};
// We initially set the flags to default and then now call set_debug_flags
@ -4876,6 +4882,7 @@ impl Renderer {
projection: &default::Transform3D<f32>,
results: &mut RenderResults,
max_partial_present_rects: usize,
draw_previous_partial_present_regions: bool,
) {
let _gm = self.gpu_profile.start_marker("framebuffer");
let _timer = self.gpu_profile.start_timer(GPU_TAG_COMPOSITE);
@ -4909,9 +4916,18 @@ impl Renderer {
results.dirty_rects.push(combined_dirty_rect_i32);
}
// If the implementation requires manually keeping the buffer consistent,
// combine the previous frame's damage for tile clipping.
// (Not for the returned region though, that should be from this frame only)
partial_present_mode = Some(PartialPresentMode::Single {
dirty_rect: combined_dirty_rect,
dirty_rect: if draw_previous_partial_present_regions {
combined_dirty_rect.union(&self.prev_dirty_rect)
} 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.
@ -4920,6 +4936,10 @@ impl Renderer {
draw_target.dimensions(),
);
results.dirty_rects.push(fb_rect);
if draw_previous_partial_present_regions {
self.prev_dirty_rect = fb_rect.to_f32();
}
}
self.force_redraw = false;
@ -5875,7 +5895,7 @@ impl Renderer {
let compositor = self.compositor_config.compositor().unwrap();
frame.composite_state.composite_native(&mut **compositor);
}
CompositorKind::Draw { max_partial_present_rects, .. } => {
CompositorKind::Draw { max_partial_present_rects, draw_previous_partial_present_regions, .. } => {
self.composite_simple(
&frame.composite_state,
clear_framebuffer,
@ -5883,6 +5903,7 @@ impl Renderer {
&projection,
results,
max_partial_present_rects,
draw_previous_partial_present_regions,
);
}
}