tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

RenderCompositorEGL.cpp (11025B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "RenderCompositorEGL.h"
      8 
      9 #include "GLContext.h"
     10 #include "GLContextEGL.h"
     11 #include "GLContextProvider.h"
     12 #include "GLLibraryEGL.h"
     13 #include "mozilla/StaticPrefs_gfx.h"
     14 #include "mozilla/gfx/Logging.h"
     15 #include "mozilla/gfx/gfxVars.h"
     16 #include "mozilla/layers/BuildConstants.h"
     17 #include "mozilla/webrender/RenderThread.h"
     18 #include "mozilla/widget/CompositorWidget.h"
     19 
     20 #ifdef MOZ_WIDGET_GTK
     21 #  include "mozilla/WidgetUtilsGtk.h"
     22 #  include "mozilla/widget/GtkCompositorWidget.h"
     23 #endif
     24 
     25 #ifdef MOZ_WIDGET_ANDROID
     26 #  include "mozilla/java/GeckoSurfaceTextureWrappers.h"
     27 #  include "mozilla/layers/AndroidHardwareBuffer.h"
     28 #  include "mozilla/widget/AndroidCompositorWidget.h"
     29 #  include <android/native_window.h>
     30 #  include <android/native_window_jni.h>
     31 #endif
     32 
     33 namespace mozilla::wr {
     34 
     35 extern LazyLogModule gRenderThreadLog;
     36 #define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
     37 
     38 /* static */
     39 UniquePtr<RenderCompositor> RenderCompositorEGL::Create(
     40    const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError) {
     41  if (kIsLinux && !gfx::gfxVars::UseEGL()) {
     42    return nullptr;
     43  }
     44  RefPtr<gl::GLContext> gl = RenderThread::Get()->SingletonGL(aError);
     45  if (!gl) {
     46    if (aError.IsEmpty()) {
     47      aError.Assign("RcANGLE(no shared GL)"_ns);
     48    } else {
     49      aError.Append("(Create)"_ns);
     50    }
     51    return nullptr;
     52  }
     53  return MakeUnique<RenderCompositorEGL>(aWidget, std::move(gl));
     54 }
     55 
     56 EGLSurface RenderCompositorEGL::CreateEGLSurface() {
     57  EGLSurface surface = EGL_NO_SURFACE;
     58  surface = gl::GLContextEGL::CreateEGLSurfaceForCompositorWidget(
     59      mWidget, gl::GLContextEGL::Cast(gl())->mSurfaceConfig);
     60  if (surface == EGL_NO_SURFACE) {
     61    const auto* renderThread = RenderThread::Get();
     62    gfxCriticalNote << "Failed to create EGLSurface. "
     63                    << renderThread->RendererCount() << " renderers, "
     64                    << renderThread->ActiveRendererCount() << " active.";
     65  }
     66  return surface;
     67 }
     68 
     69 RenderCompositorEGL::RenderCompositorEGL(
     70    const RefPtr<widget::CompositorWidget>& aWidget,
     71    RefPtr<gl::GLContext>&& aGL)
     72    : RenderCompositor(aWidget), mGL(aGL), mEGLSurface(EGL_NO_SURFACE) {
     73  MOZ_ASSERT(mGL);
     74  LOG("RenderCompositorEGL::RenderCompositorEGL()");
     75 }
     76 
     77 RenderCompositorEGL::~RenderCompositorEGL() {
     78  LOG("RenderCompositorEGL::~RenderCompositorEGL()");
     79 #ifdef MOZ_WIDGET_ANDROID
     80  java::GeckoSurfaceTexture::DestroyUnused((int64_t)gl());
     81 #endif
     82  DestroyEGLSurface();
     83 }
     84 
     85 bool RenderCompositorEGL::BeginFrame() {
     86  if (kIsLinux && mEGLSurface == EGL_NO_SURFACE) {
     87    gfxCriticalNote
     88        << "We don't have EGLSurface to draw into. Called too early?";
     89    return false;
     90  }
     91 #ifdef MOZ_WAYLAND
     92  if (auto* gtkWidget = mWidget->AsGTK()) {
     93    gtkWidget->SetEGLNativeWindowSize(GetBufferSize());
     94  }
     95 #endif
     96  if (!MakeCurrent()) {
     97    gfxCriticalNote << "Failed to make render context current, can't draw.";
     98    return false;
     99  }
    100 
    101 #ifdef MOZ_WIDGET_ANDROID
    102  java::GeckoSurfaceTexture::DestroyUnused((int64_t)gl());
    103  gl()->MakeCurrent();  // DestroyUnused can change the current context!
    104 #endif
    105 
    106  return true;
    107 }
    108 
    109 RenderedFrameId RenderCompositorEGL::EndFrame(
    110    const nsTArray<DeviceIntRect>& aDirtyRects) {
    111 #ifdef MOZ_WIDGET_ANDROID
    112  const auto& gle = gl::GLContextEGL::Cast(gl());
    113  const auto& egl = gle->mEgl;
    114 
    115  EGLSync sync = nullptr;
    116  if (layers::AndroidHardwareBufferApi::Get()) {
    117    sync = egl->fCreateSync(LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
    118  }
    119  if (sync) {
    120    int fenceFd = egl->fDupNativeFenceFDANDROID(sync);
    121    if (fenceFd >= 0) {
    122      mReleaseFence = new layers::FenceFileHandle(UniqueFileHandle(fenceFd));
    123    }
    124    egl->fDestroySync(sync);
    125    sync = nullptr;
    126  }
    127 #endif
    128 
    129  RenderedFrameId frameId = GetNextRenderFrameId();
    130 #ifdef MOZ_WIDGET_GTK
    131  if (mWidget->IsHidden()) {
    132    return frameId;
    133  }
    134 #endif
    135  if (mEGLSurface != EGL_NO_SURFACE && aDirtyRects.Length() > 0) {
    136    gfx::IntRegion bufferInvalid;
    137    const auto bufferSize = GetBufferSize();
    138    for (const DeviceIntRect& rect : aDirtyRects) {
    139      const auto left = std::clamp(rect.min.x, 0, bufferSize.width);
    140      const auto top = std::clamp(rect.min.y, 0, bufferSize.height);
    141 
    142      const auto right = std::clamp(rect.max.x, 0, bufferSize.width);
    143      const auto bottom = std::clamp(rect.max.y, 0, bufferSize.height);
    144 
    145      const auto width = right - left;
    146      const auto height = bottom - top;
    147 
    148      bufferInvalid.OrWith(
    149          gfx::IntRect(left, (GetBufferSize().height - bottom), width, height));
    150    }
    151    gl()->SetDamage(bufferInvalid);
    152  }
    153 
    154 #ifdef MOZ_WIDGET_GTK
    155  // Rendering on Wayland has to be atomic (buffer attach + commit) and
    156  // wayland surface is also used by main thread so lock it before
    157  // we paint at SwapBuffers().
    158  UniquePtr<widget::WaylandSurfaceLock> lock;
    159  if (auto* gtkWidget = mWidget->AsGTK()) {
    160    lock = gtkWidget->LockSurface();
    161  }
    162 #endif
    163  gl()->SwapBuffers();
    164  return frameId;
    165 }
    166 
    167 void RenderCompositorEGL::Pause() { DestroyEGLSurface(); }
    168 
    169 bool RenderCompositorEGL::Resume() {
    170  if (kIsAndroid) {
    171    // Destroy EGLSurface if it exists.
    172    DestroyEGLSurface();
    173 
    174    auto size = GetBufferSize();
    175    GLint maxTextureSize = 0;
    176    gl()->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, (GLint*)&maxTextureSize);
    177 
    178    // When window size is too big, hardware buffer allocation could fail.
    179    if (maxTextureSize < size.width || maxTextureSize < size.height) {
    180      gfxCriticalNote << "Too big ANativeWindow size(" << size.width << ", "
    181                      << size.height << ") MaxTextureSize " << maxTextureSize;
    182      return false;
    183    }
    184 
    185    mEGLSurface = CreateEGLSurface();
    186    if (mEGLSurface == EGL_NO_SURFACE) {
    187      // Often when we fail to create an EGL surface it is because the Java
    188      // Surface we have been provided is invalid. Therefore the on the first
    189      // occurence we don't raise a WebRenderError and instead just return
    190      // failure. This allows the widget a chance to request a new Java
    191      // Surface. On subsequent failures, raising the WebRenderError will
    192      // result in the compositor being recreated, falling back through
    193      // webrender configurations, and eventually crashing if we still do not
    194      // succeed.
    195      if (!mHandlingNewSurfaceError) {
    196        mHandlingNewSurfaceError = true;
    197      } else {
    198        RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE);
    199      }
    200      return false;
    201    }
    202    mHandlingNewSurfaceError = false;
    203 
    204    gl::GLContextEGL::Cast(gl())->SetEGLSurfaceOverride(mEGLSurface);
    205  } else if (kIsLinux) {
    206    // Destroy EGLSurface if it exists and create a new one. We will set the
    207    // swap interval after MakeCurrent() has been called.
    208    DestroyEGLSurface();
    209    mEGLSurface = CreateEGLSurface();
    210    if (mEGLSurface != EGL_NO_SURFACE) {
    211      // We have a new EGL surface, which on wayland needs to be configured for
    212      // non-blocking buffer swaps. We need MakeCurrent() to set our current EGL
    213      // context before we call eglSwapInterval, which is why we do it here
    214      // rather than where the surface was created.
    215      const auto& gle = gl::GLContextEGL::Cast(gl());
    216      const auto& egl = gle->mEgl;
    217      MakeCurrent();
    218 
    219      const int interval = gfx::gfxVars::SwapIntervalEGL() ? 1 : 0;
    220      egl->fSwapInterval(interval);
    221    } else {
    222      RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE);
    223      return false;
    224    }
    225  }
    226  return true;
    227 }
    228 
    229 bool RenderCompositorEGL::IsPaused() { return mEGLSurface == EGL_NO_SURFACE; }
    230 
    231 bool RenderCompositorEGL::MakeCurrent() {
    232  const auto& gle = gl::GLContextEGL::Cast(gl());
    233 
    234  gle->SetEGLSurfaceOverride(mEGLSurface);
    235  bool ok = gl()->MakeCurrent();
    236  if (!gl()->IsGLES() && ok && mEGLSurface != EGL_NO_SURFACE) {
    237    // If we successfully made a surface current, set the draw buffer
    238    // appropriately. It's not well-defined by the EGL spec whether
    239    // eglMakeCurrent should do this automatically. See bug 1646135.
    240    gl()->fDrawBuffer(gl()->IsDoubleBuffered() ? LOCAL_GL_BACK
    241                                               : LOCAL_GL_FRONT);
    242  }
    243  return ok;
    244 }
    245 
    246 void RenderCompositorEGL::DestroyEGLSurface() {
    247  const auto& gle = gl::GLContextEGL::Cast(gl());
    248  const auto& egl = gle->mEgl;
    249 
    250  // Release EGLSurface of back buffer before calling ResizeBuffers().
    251  if (mEGLSurface) {
    252    gle->SetEGLSurfaceOverride(EGL_NO_SURFACE);
    253    gl::GLContextEGL::DestroySurface(*egl, mEGLSurface);
    254    mEGLSurface = nullptr;
    255  }
    256 }
    257 
    258 RefPtr<layers::Fence> RenderCompositorEGL::GetAndResetReleaseFence() {
    259 #ifdef MOZ_WIDGET_ANDROID
    260  MOZ_ASSERT(!layers::AndroidHardwareBufferApi::Get() || mReleaseFence);
    261  return mReleaseFence.forget();
    262 #else
    263  return nullptr;
    264 #endif
    265 }
    266 
    267 LayoutDeviceIntSize RenderCompositorEGL::GetBufferSize() {
    268  return mWidget->GetClientSize();
    269 }
    270 
    271 bool RenderCompositorEGL::UsePartialPresent() {
    272  return gfx::gfxVars::WebRenderMaxPartialPresentRects() > 0;
    273 }
    274 
    275 bool RenderCompositorEGL::RequestFullRender() { return false; }
    276 
    277 uint32_t RenderCompositorEGL::GetMaxPartialPresentRects() {
    278  return gfx::gfxVars::WebRenderMaxPartialPresentRects();
    279 }
    280 
    281 bool RenderCompositorEGL::ShouldDrawPreviousPartialPresentRegions() {
    282  return true;
    283 }
    284 
    285 size_t RenderCompositorEGL::GetBufferAge() const {
    286  if (!StaticPrefs::
    287          gfx_webrender_allow_partial_present_buffer_age_AtStartup()) {
    288    return 0;
    289  }
    290  return gl()->GetBufferAge();
    291 }
    292 
    293 void RenderCompositorEGL::SetBufferDamageRegion(const wr::DeviceIntRect* aRects,
    294                                                size_t aNumRects) {
    295  const auto& gle = gl::GLContextEGL::Cast(gl());
    296  const auto& egl = gle->mEgl;
    297  if (gle->HasKhrPartialUpdate() &&
    298      StaticPrefs::gfx_webrender_allow_partial_present_buffer_age_AtStartup()) {
    299    std::vector<EGLint> rects;
    300    rects.reserve(4 * aNumRects);
    301    const auto bufferSize = GetBufferSize();
    302    for (size_t i = 0; i < aNumRects; i++) {
    303      const auto left = std::clamp(aRects[i].min.x, 0, bufferSize.width);
    304      const auto top = std::clamp(aRects[i].min.y, 0, bufferSize.height);
    305 
    306      const auto right = std::clamp(aRects[i].max.x, 0, bufferSize.width);
    307      const auto bottom = std::clamp(aRects[i].max.y, 0, bufferSize.height);
    308 
    309      const auto width = right - left;
    310      const auto height = bottom - top;
    311 
    312      rects.push_back(left);
    313      rects.push_back(bufferSize.height - bottom);
    314      rects.push_back(width);
    315      rects.push_back(height);
    316    }
    317    const auto ret =
    318        egl->fSetDamageRegion(mEGLSurface, rects.data(), rects.size() / 4);
    319    if (ret == LOCAL_EGL_FALSE) {
    320      const auto err = egl->mLib->fGetError();
    321      gfxCriticalError() << "Error in eglSetDamageRegion: " << gfx::hexa(err);
    322    }
    323  }
    324 }
    325 
    326 }  // namespace mozilla::wr