зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1575765 - Implement KHR_partial_update for webrender. r=sotaro,jgilbert
KHR_partial_update allows us to avoid rerendering the entire backbuffer every frame, and instead only render what has changed on the current frame, as well as the difference between the current backbuffer and the current frontbuffer. It works similarily to EXT_buffer_age, which we already support, with the additional requirement that we must call eglSetDamageRegion each frame before rendering to the backbuffer. Modify GLContextEGL::GetBufferAge() so that it queries the age if either EXT_buffer_age or KHR_partial_update are available. This will now automatically be queried by webrender through the PartialPresentCompositor trait. Add a new function to that trait, set_buffer_damage_region(), whose RenderCompositorEGL implementation calls eglSetDamageRegion(). Call this from composite_simple(), once the damage rect has been calculated but before rendering to the backbuffer. Additionally, change both RenderCompositorEGL and RenderCompositorOGL's implementations of ShouldDrawPreviousPartialPresentRegions() to unconditionally return true, rather than checking for the existence of EXT_buffer_age (or adding a new check for KHR_partial_update). The lack of these extensions does not mean that webrender is able to skip rendering previous frames' damage. Rather the opposite, it means we cannot render *only* the previous frames' damage, and must instead always render the entire buffer. Differential Revision: https://phabricator.services.mozilla.com/D91203
This commit is contained in:
Родитель
5d407f06e0
Коммит
7adec85b80
|
@ -98,7 +98,8 @@ class GLContextEGL final : public GLContext {
|
|||
|
||||
EGLSurface GetEGLSurface() const { return mSurface; }
|
||||
|
||||
bool HasBufferAge() const;
|
||||
bool HasExtBufferAge() const;
|
||||
bool HasKhrPartialUpdate() const;
|
||||
EGLint GetBufferAge() const;
|
||||
|
||||
bool BindTex2DOffscreen(GLContext* aOffscreen);
|
||||
|
|
|
@ -599,15 +599,19 @@ void GLContextEGL::GetWSIInfo(nsCString* const out) const {
|
|||
// for the lifetime of this context.
|
||||
void GLContextEGL::HoldSurface(gfxASurface* aSurf) { mThebesSurface = aSurf; }
|
||||
|
||||
bool GLContextEGL::HasBufferAge() const {
|
||||
bool GLContextEGL::HasExtBufferAge() const {
|
||||
return mEgl->IsExtensionSupported(EGLExtension::EXT_buffer_age);
|
||||
}
|
||||
|
||||
bool GLContextEGL::HasKhrPartialUpdate() const {
|
||||
return mEgl->IsExtensionSupported(EGLExtension::KHR_partial_update);
|
||||
}
|
||||
|
||||
EGLint GLContextEGL::GetBufferAge() const {
|
||||
EGLSurface surface =
|
||||
mSurfaceOverride != EGL_NO_SURFACE ? mSurfaceOverride : mSurface;
|
||||
|
||||
if (surface && HasBufferAge()) {
|
||||
if (surface && (HasExtBufferAge() || HasKhrPartialUpdate())) {
|
||||
EGLint result;
|
||||
mEgl->fQuerySurface(surface, LOCAL_EGL_BUFFER_AGE_EXT, &result);
|
||||
return result;
|
||||
|
|
|
@ -76,7 +76,8 @@ static const char* sEGLExtensionNames[] = {
|
|||
"EGL_MOZ_create_context_provoking_vertex_dont_care",
|
||||
"EGL_EXT_swap_buffers_with_damage",
|
||||
"EGL_KHR_swap_buffers_with_damage",
|
||||
"EGL_EXT_buffer_age"};
|
||||
"EGL_EXT_buffer_age",
|
||||
"EGL_KHR_partial_update"};
|
||||
|
||||
PRLibrary* LoadApitraceLibrary() {
|
||||
const char* path = nullptr;
|
||||
|
@ -599,6 +600,12 @@ bool GLLibraryEGL::Init(nsACString* const out_failureId) {
|
|||
END_OF_SYMBOLS};
|
||||
(void)fnLoadSymbols(symbols);
|
||||
}
|
||||
{
|
||||
const SymLoadStruct symbols[] = {
|
||||
{(PRFuncPtr*)&mSymbols.fSetDamageRegion, {{"eglSetDamageRegionKHR"}}},
|
||||
END_OF_SYMBOLS};
|
||||
(void)fnLoadSymbols(symbols);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -104,6 +104,7 @@ enum class EGLExtension {
|
|||
EXT_swap_buffers_with_damage,
|
||||
KHR_swap_buffers_with_damage,
|
||||
EXT_buffer_age,
|
||||
KHR_partial_update,
|
||||
Max
|
||||
};
|
||||
|
||||
|
@ -446,6 +447,12 @@ class GLLibraryEGL final {
|
|||
WRAP(fSwapBuffersWithDamage(dpy, surface, rects, n_rects));
|
||||
}
|
||||
|
||||
// EGL_KHR_partial_update
|
||||
EGLBoolean fSetDamageRegion(EGLDisplay dpy, EGLSurface surface,
|
||||
const EGLint* rects, EGLint n_rects) {
|
||||
WRAP(fSetDamageRegion(dpy, surface, rects, n_rects));
|
||||
}
|
||||
|
||||
#undef WRAP
|
||||
#undef PROFILE_CALL
|
||||
#undef BEFORE_CALL
|
||||
|
@ -571,6 +578,10 @@ class GLLibraryEGL final {
|
|||
EGLSurface surface,
|
||||
const EGLint* rects,
|
||||
EGLint n_rects);
|
||||
// EGL_KHR_partial_update
|
||||
EGLBoolean(GLAPIENTRY* fSetDamageRegion)(EGLDisplay dpy, EGLSurface surface,
|
||||
const EGLint* rects,
|
||||
EGLint n_rects);
|
||||
EGLClientBuffer(GLAPIENTRY* fGetNativeClientBufferANDROID)(
|
||||
const struct AHardwareBuffer* buffer);
|
||||
} mSymbols = {};
|
||||
|
@ -824,6 +835,13 @@ class EglDisplay final {
|
|||
IsExtensionSupported(EGLExtension::KHR_swap_buffers_with_damage));
|
||||
return mLib->fSwapBuffersWithDamage(mDisplay, surface, rects, n_rects);
|
||||
}
|
||||
|
||||
// EGL_KHR_partial_update
|
||||
EGLBoolean fSetDamageRegion(EGLSurface surface, const EGLint* rects,
|
||||
EGLint n_rects) {
|
||||
MOZ_ASSERT(IsExtensionSupported(EGLExtension::KHR_partial_update));
|
||||
return mLib->fSetDamageRegion(mDisplay, surface, rects, n_rects);
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace gl */
|
||||
|
|
|
@ -131,6 +131,12 @@ size_t wr_partial_present_compositor_get_buffer_age(const void* aCompositor) {
|
|||
return compositor->GetBufferAge();
|
||||
}
|
||||
|
||||
void wr_partial_present_compositor_set_buffer_damage_region(
|
||||
void* aCompositor, const wr::DeviceIntRect* aRects, size_t aNumRects) {
|
||||
RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor);
|
||||
compositor->SetBufferDamageRegion(aRects, aNumRects);
|
||||
}
|
||||
|
||||
/* static */
|
||||
UniquePtr<RenderCompositor> RenderCompositor::Create(
|
||||
RefPtr<widget::CompositorWidget>&& aWidget, nsACString& aError) {
|
||||
|
|
|
@ -131,6 +131,12 @@ class RenderCompositor {
|
|||
// region which must be rendered in addition to the current frame's dirty
|
||||
// rect.
|
||||
virtual size_t GetBufferAge() const { return 0; }
|
||||
// Allows webrender to specify the total region that will be rendered to this
|
||||
// frame, ie the frame's dirty region and some previous frames' dirty regions,
|
||||
// if applicable (calculated using the buffer age). Must be called before
|
||||
// anything has been rendered to the main framebuffer.
|
||||
virtual void SetBufferDamageRegion(const wr::DeviceIntRect* aRects,
|
||||
size_t aNumRects) {}
|
||||
|
||||
// Whether the surface origin is top-left.
|
||||
virtual bool SurfaceOriginIsTopLeft() { return false; }
|
||||
|
|
|
@ -245,9 +245,47 @@ uint32_t RenderCompositorEGL::GetMaxPartialPresentRects() {
|
|||
}
|
||||
|
||||
bool RenderCompositorEGL::ShouldDrawPreviousPartialPresentRegions() {
|
||||
return gl::GLContextEGL::Cast(gl())->HasBufferAge();
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t RenderCompositorEGL::GetBufferAge() const { return mBufferAge; }
|
||||
|
||||
void RenderCompositorEGL::SetBufferDamageRegion(const wr::DeviceIntRect* aRects,
|
||||
size_t aNumRects) {
|
||||
const auto& gle = gl::GLContextEGL::Cast(gl());
|
||||
const auto& egl = gle->mEgl;
|
||||
if (gle->HasKhrPartialUpdate()) {
|
||||
std::vector<EGLint> rects;
|
||||
rects.reserve(4 * aNumRects);
|
||||
const auto bufferSize = GetBufferSize();
|
||||
for (size_t i = 0; i < aNumRects; i++) {
|
||||
const auto left =
|
||||
std::max(0, std::min(bufferSize.width, aRects[i].origin.x));
|
||||
const auto top =
|
||||
std::max(0, std::min(bufferSize.height, aRects[i].origin.y));
|
||||
|
||||
const auto right =
|
||||
std::min(bufferSize.width,
|
||||
std::max(0, aRects[i].origin.x + aRects[i].size.width));
|
||||
const auto bottom =
|
||||
std::min(bufferSize.height,
|
||||
std::max(0, aRects[i].origin.y + aRects[i].size.height));
|
||||
|
||||
const auto width = right - left;
|
||||
const auto height = bottom - top;
|
||||
|
||||
rects.push_back(left);
|
||||
rects.push_back(bufferSize.height - bottom);
|
||||
rects.push_back(width);
|
||||
rects.push_back(height);
|
||||
}
|
||||
const auto ret =
|
||||
egl->fSetDamageRegion(mEGLSurface, rects.data(), rects.size() / 4);
|
||||
if (ret == LOCAL_EGL_FALSE) {
|
||||
const auto err = egl->mLib->fGetError();
|
||||
gfxCriticalError() << "Error in eglSetDamageRegion: " << gfx::hexa(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla::wr
|
||||
|
|
|
@ -43,6 +43,8 @@ class RenderCompositorEGL : public RenderCompositor {
|
|||
uint32_t GetMaxPartialPresentRects() override;
|
||||
bool ShouldDrawPreviousPartialPresentRegions() override;
|
||||
size_t GetBufferAge() const override;
|
||||
void SetBufferDamageRegion(const wr::DeviceIntRect* aRects,
|
||||
size_t aNumRects) override;
|
||||
|
||||
ipc::FileDescriptor GetAndResetReleaseFence() override;
|
||||
|
||||
|
|
|
@ -121,7 +121,7 @@ bool RenderCompositorOGL::UsePartialPresent() {
|
|||
}
|
||||
|
||||
bool RenderCompositorOGL::ShouldDrawPreviousPartialPresentRegions() {
|
||||
return mIsEGL && gl::GLContextEGL::Cast(gl())->HasBufferAge();
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t RenderCompositorOGL::GetBufferAge() const {
|
||||
|
|
|
@ -1242,6 +1242,11 @@ extern "C" {
|
|||
fn wr_compositor_unmap_tile(compositor: *mut c_void);
|
||||
|
||||
fn wr_partial_present_compositor_get_buffer_age(compositor: *const c_void) -> usize;
|
||||
fn wr_partial_present_compositor_set_buffer_damage_region(
|
||||
compositor: *mut c_void,
|
||||
rects: *const DeviceIntRect,
|
||||
n_rects: usize,
|
||||
);
|
||||
}
|
||||
|
||||
pub struct WrCompositor(*mut c_void);
|
||||
|
@ -1362,6 +1367,12 @@ impl PartialPresentCompositor for WrPartialPresentCompositor {
|
|||
fn get_buffer_age(&self) -> usize {
|
||||
unsafe { wr_partial_present_compositor_get_buffer_age(self.0) }
|
||||
}
|
||||
|
||||
fn set_buffer_damage_region(&mut self, rects: &[DeviceIntRect]) {
|
||||
unsafe {
|
||||
wr_partial_present_compositor_set_buffer_damage_region(self.0, rects.as_ptr(), rects.len());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about the underlying data buffer of a mapped tile.
|
||||
|
|
|
@ -1024,6 +1024,11 @@ pub trait PartialPresentCompositor {
|
|||
/// 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;
|
||||
/// Allows webrender to specify the total region that will be rendered to this frame,
|
||||
/// ie the frame's dirty region and some previous frames' dirty regions, if applicable
|
||||
/// (calculated using the buffer age). Must be called before anything has been rendered
|
||||
/// to the main framebuffer.
|
||||
fn set_buffer_damage_region(&mut self, rects: &[DeviceIntRect]);
|
||||
}
|
||||
|
||||
/// Information about an opaque surface used to occlude tiles.
|
||||
|
|
|
@ -5242,14 +5242,14 @@ impl Renderer {
|
|||
// Work out how many dirty rects WR produced, and if that's more than
|
||||
// what the device supports.
|
||||
for tile in composite_state.opaque_tiles.iter().chain(composite_state.alpha_tiles.iter()) {
|
||||
let dirty_rect = tile.dirty_rect.translate(tile.rect.origin.to_vector());
|
||||
combined_dirty_rect = combined_dirty_rect.union(&dirty_rect);
|
||||
let tile_dirty_rect = tile.dirty_rect.translate(tile.rect.origin.to_vector());
|
||||
combined_dirty_rect = combined_dirty_rect.union(&tile_dirty_rect);
|
||||
}
|
||||
|
||||
let combined_dirty_rect = combined_dirty_rect.round();
|
||||
let combined_dirty_rect_i32 = combined_dirty_rect.to_i32();
|
||||
// If nothing has changed, don't return any dirty rects at all (the client
|
||||
// can use this as a signal to skip present completely).
|
||||
// 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);
|
||||
}
|
||||
|
@ -5260,15 +5260,23 @@ impl Renderer {
|
|||
}
|
||||
|
||||
// If the implementation requires manually keeping the buffer consistent,
|
||||
// combine the previous frames' damage rects for tile clipping.
|
||||
// (Not for the returned region though, that should be from this frame only)
|
||||
// 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: if draw_previous_partial_present_regions {
|
||||
combined_dirty_rect.union(&prev_frames_damage_rect.unwrap())
|
||||
} else {
|
||||
combined_dirty_rect
|
||||
},
|
||||
dirty_rect: total_dirty_rect,
|
||||
});
|
||||
|
||||
if let Some(partial_present) = self.compositor_config.partial_present() {
|
||||
partial_present.set_buffer_damage_region(&[total_dirty_rect.to_i32()]);
|
||||
}
|
||||
} else {
|
||||
// If we don't have a valid partial present scenario, return a single
|
||||
// dirty rect to the client that covers the entire framebuffer.
|
||||
|
|
Загрузка…
Ссылка в новой задаче