gecko-dev/gfx/webrender_bindings/RenderCompositorEGL.cpp
Greg V 22c3a0e8d8 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
2020-05-22 18:15:13 +00:00

211 lines
6.7 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#include "RenderCompositorEGL.h"
#include "GLContext.h"
#include "GLContextEGL.h"
#include "GLContextProvider.h"
#include "GLLibraryEGL.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/webrender/RenderThread.h"
#include "mozilla/widget/CompositorWidget.h"
#ifdef MOZ_WAYLAND
# include "mozilla/widget/GtkCompositorWidget.h"
# include <gdk/gdk.h>
# include <gdk/gdkx.h>
#endif
#ifdef MOZ_WIDGET_ANDROID
# include "mozilla/java/GeckoSurfaceTextureWrappers.h"
# include "mozilla/widget/AndroidCompositorWidget.h"
# include <android/native_window.h>
# include <android/native_window_jni.h>
#endif
namespace mozilla::wr {
/* static */
UniquePtr<RenderCompositor> RenderCompositorEGL::Create(
RefPtr<widget::CompositorWidget> aWidget) {
#ifdef MOZ_WAYLAND
if (!gdk_display_get_default() ||
GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
return nullptr;
}
#endif
if (!RenderThread::Get()->SharedGL()) {
gfxCriticalNote << "Failed to get shared GL context";
return nullptr;
}
return MakeUnique<RenderCompositorEGL>(aWidget);
}
EGLSurface RenderCompositorEGL::CreateEGLSurface() {
EGLSurface surface = EGL_NO_SURFACE;
surface = gl::GLContextEGL::CreateEGLSurfaceForCompositorWidget(
mWidget, gl::GLContextEGL::Cast(gl())->mConfig);
if (surface == EGL_NO_SURFACE) {
gfxCriticalNote << "Failed to create EGLSurface";
}
return surface;
}
RenderCompositorEGL::RenderCompositorEGL(
RefPtr<widget::CompositorWidget> aWidget)
: RenderCompositor(std::move(aWidget)), mEGLSurface(EGL_NO_SURFACE) {}
RenderCompositorEGL::~RenderCompositorEGL() {
#ifdef MOZ_WIDGET_ANDROID
java::GeckoSurfaceTexture::DestroyUnused((int64_t)gl());
#endif
DestroyEGLSurface();
}
bool RenderCompositorEGL::BeginFrame() {
#ifdef MOZ_WAYLAND
if (mEGLSurface == EGL_NO_SURFACE) {
gfxCriticalNote
<< "We don't have EGLSurface to draw into. Called too early?";
return false;
}
if (mWidget->AsX11()) {
mWidget->AsX11()->SetEGLNativeWindowSize(GetBufferSize());
}
#endif
if (!MakeCurrent()) {
gfxCriticalNote << "Failed to make render context current, can't draw.";
return false;
}
#ifdef MOZ_WIDGET_ANDROID
java::GeckoSurfaceTexture::DestroyUnused((int64_t)gl());
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 && 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(); }
bool RenderCompositorEGL::Resume() {
#ifdef MOZ_WIDGET_ANDROID
// Destroy EGLSurface if it exists.
DestroyEGLSurface();
mEGLSurface = CreateEGLSurface();
gl::GLContextEGL::Cast(gl())->SetEGLSurfaceOverride(mEGLSurface);
// Query the new surface size as this may have changed. We cannot use
// mWidget->GetClientSize() due to a race condition between nsWindow::Resize()
// being called and the frame being rendered after the surface is resized.
EGLNativeWindowType window = mWidget->AsAndroid()->GetEGLNativeWindow();
JNIEnv* const env = jni::GetEnvForThread();
ANativeWindow* const nativeWindow =
ANativeWindow_fromSurface(env, reinterpret_cast<jobject>(window));
const int32_t width = ANativeWindow_getWidth(nativeWindow);
const int32_t height = ANativeWindow_getHeight(nativeWindow);
mEGLSurfaceSize = LayoutDeviceIntSize(width, height);
ANativeWindow_release(nativeWindow);
#elif defined(MOZ_WAYLAND)
// Destroy EGLSurface if it exists and create a new one. We will set the
// swap interval after MakeCurrent() has been called.
DestroyEGLSurface();
mEGLSurface = CreateEGLSurface();
if (mEGLSurface != EGL_NO_SURFACE) {
// We have a new EGL surface, which on wayland needs to be configured for
// non-blocking buffer swaps. We need MakeCurrent() to set our current EGL
// context before we call eglSwapInterval, which is why we do it here rather
// than where the surface was created.
const auto& gle = gl::GLContextEGL::Cast(gl());
const auto& egl = gle->mEgl;
MakeCurrent();
// Make eglSwapBuffers() non-blocking on wayland.
egl->fSwapInterval(egl->Display(), 0);
} else {
RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE);
}
#endif
return true;
}
gl::GLContext* RenderCompositorEGL::gl() const {
return RenderThread::Get()->SharedGL();
}
bool RenderCompositorEGL::MakeCurrent() {
gl::GLContextEGL::Cast(gl())->SetEGLSurfaceOverride(mEGLSurface);
return gl()->MakeCurrent();
}
void RenderCompositorEGL::DestroyEGLSurface() {
const auto& gle = gl::GLContextEGL::Cast(gl());
const auto& egl = gle->mEgl;
// Release EGLSurface of back buffer before calling ResizeBuffers().
if (mEGLSurface) {
gle->SetEGLSurfaceOverride(EGL_NO_SURFACE);
egl->fDestroySurface(egl->Display(), mEGLSurface);
mEGLSurface = nullptr;
}
}
LayoutDeviceIntSize RenderCompositorEGL::GetBufferSize() {
#ifdef MOZ_WIDGET_ANDROID
return mEGLSurfaceSize;
#else
return mWidget->GetClientSize();
#endif
}
CompositorCapabilities RenderCompositorEGL::GetCompositorCapabilities() {
CompositorCapabilities caps;
caps.virtual_surface_size = 0;
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