tor-browser

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

RenderCompositorOGLSWGL.cpp (16317B)


      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 *
      5 * License, v. 2.0. If a copy of the MPL was not distributed with this
      6 * file,
      7 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      8 
      9 #include "RenderCompositorOGLSWGL.h"
     10 
     11 #include "GLContext.h"
     12 #include "GLContextEGL.h"
     13 #include "mozilla/layers/BuildConstants.h"
     14 #include "mozilla/layers/CompositorOGL.h"
     15 #include "mozilla/layers/Effects.h"
     16 #include "mozilla/layers/TextureHostOGL.h"
     17 #include "mozilla/widget/CompositorWidget.h"
     18 #include "OGLShaderProgram.h"
     19 
     20 #ifdef MOZ_WIDGET_ANDROID
     21 #  include "mozilla/java/GeckoSurfaceTextureWrappers.h"
     22 #  include "mozilla/layers/AndroidHardwareBuffer.h"
     23 #  include "mozilla/webrender/RenderAndroidHardwareBufferTextureHost.h"
     24 #  include "mozilla/webrender/RenderAndroidSurfaceTextureHost.h"
     25 #  include "mozilla/widget/AndroidCompositorWidget.h"
     26 #  include <android/native_window.h>
     27 #  include <android/native_window_jni.h>
     28 #endif
     29 
     30 #ifdef MOZ_WIDGET_GTK
     31 #  include "mozilla/widget/GtkCompositorWidget.h"
     32 #  include <gdk/gdk.h>
     33 #  ifdef MOZ_X11
     34 #    include <gdk/gdkx.h>
     35 #  endif
     36 #endif
     37 
     38 namespace mozilla {
     39 using namespace layers;
     40 using namespace gfx;
     41 namespace wr {
     42 
     43 extern LazyLogModule gRenderThreadLog;
     44 #define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
     45 
     46 UniquePtr<RenderCompositor> RenderCompositorOGLSWGL::Create(
     47    const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError) {
     48  if (!aWidget->GetCompositorOptions().AllowSoftwareWebRenderOGL()) {
     49    return nullptr;
     50  }
     51 
     52  RefPtr<Compositor> compositor;
     53 #ifdef MOZ_WIDGET_ANDROID
     54  RefPtr<gl::GLContext> context =
     55      RenderThread::Get()->SingletonGLForCompositorOGL();
     56  if (!context) {
     57    gfxCriticalNote << "SingletonGL does not exist for SWGL";
     58    return nullptr;
     59  }
     60  auto programs = RenderThread::Get()->GetProgramsForCompositorOGL();
     61  if (!programs) {
     62    gfxCriticalNote << "Failed to get Programs for CompositorOGL for SWGL";
     63    return nullptr;
     64  }
     65 
     66  nsCString log;
     67  RefPtr<CompositorOGL> compositorOGL;
     68  compositorOGL = new CompositorOGL(aWidget, /* aSurfaceWidth */ -1,
     69                                    /* aSurfaceHeight */ -1,
     70                                    /* aUseExternalSurfaceSize */ true);
     71  if (!compositorOGL->Initialize(context, programs, &log)) {
     72    gfxCriticalNote << "Failed to initialize CompositorOGL for SWGL: "
     73                    << log.get();
     74    return nullptr;
     75  }
     76  compositor = compositorOGL;
     77 #elif defined(MOZ_WIDGET_GTK)
     78  nsCString log;
     79  RefPtr<CompositorOGL> compositorOGL;
     80  compositorOGL = new CompositorOGL(aWidget);
     81  if (!compositorOGL->Initialize(&log)) {
     82    gfxCriticalNote << "Failed to initialize CompositorOGL for SWGL: "
     83                    << log.get();
     84    return nullptr;
     85  }
     86  compositor = compositorOGL;
     87 #endif
     88 
     89  if (!compositor) {
     90    return nullptr;
     91  }
     92 
     93  void* ctx = wr_swgl_create_context();
     94  if (!ctx) {
     95    gfxCriticalNote << "Failed SWGL context creation for WebRender";
     96    return nullptr;
     97  }
     98 
     99  return MakeUnique<RenderCompositorOGLSWGL>(compositor, aWidget, ctx);
    100 }
    101 
    102 RenderCompositorOGLSWGL::RenderCompositorOGLSWGL(
    103    Compositor* aCompositor, const RefPtr<widget::CompositorWidget>& aWidget,
    104    void* aContext)
    105    : RenderCompositorLayersSWGL(aCompositor, aWidget, aContext) {
    106  LOG("RenderCompositorOGLSWGL::RenderCompositorOGLSWGL()");
    107 }
    108 
    109 RenderCompositorOGLSWGL::~RenderCompositorOGLSWGL() {
    110  LOG("RRenderCompositorOGLSWGL::~RenderCompositorOGLSWGL()");
    111 #ifdef MOZ_WIDGET_ANDROID
    112  java::GeckoSurfaceTexture::DestroyUnused((int64_t)GetGLContext());
    113  DestroyEGLSurface();
    114 #endif
    115 }
    116 
    117 gl::GLContext* RenderCompositorOGLSWGL::GetGLContext() {
    118  return mCompositor->AsCompositorOGL()->gl();
    119 }
    120 
    121 bool RenderCompositorOGLSWGL::MakeCurrent() {
    122  GetGLContext()->MakeCurrent();
    123 #ifdef MOZ_WIDGET_ANDROID
    124  if (GetGLContext()->GetContextType() == gl::GLContextType::EGL) {
    125    gl::GLContextEGL::Cast(GetGLContext())->SetEGLSurfaceOverride(mEGLSurface);
    126  }
    127 #endif
    128  RenderCompositorLayersSWGL::MakeCurrent();
    129  return true;
    130 }
    131 
    132 EGLSurface RenderCompositorOGLSWGL::CreateEGLSurface() {
    133  MOZ_ASSERT(GetGLContext()->GetContextType() == gl::GLContextType::EGL);
    134 
    135  EGLSurface surface = EGL_NO_SURFACE;
    136  surface = gl::GLContextEGL::CreateEGLSurfaceForCompositorWidget(
    137      mWidget, gl::GLContextEGL::Cast(GetGLContext())->mSurfaceConfig);
    138  if (surface == EGL_NO_SURFACE) {
    139    const auto* renderThread = RenderThread::Get();
    140    gfxCriticalNote << "Failed to create EGLSurface. "
    141                    << renderThread->RendererCount() << " renderers, "
    142                    << renderThread->ActiveRendererCount() << " active.";
    143  }
    144 
    145  // The subsequent render after creating a new surface must be a full render.
    146  mFullRender = true;
    147 
    148  return surface;
    149 }
    150 
    151 void RenderCompositorOGLSWGL::DestroyEGLSurface() {
    152  MOZ_ASSERT(GetGLContext()->GetContextType() == gl::GLContextType::EGL);
    153 
    154  const auto& gle = gl::GLContextEGL::Cast(GetGLContext());
    155  const auto& egl = gle->mEgl;
    156 
    157  // Release EGLSurface of back buffer before calling ResizeBuffers().
    158  if (mEGLSurface) {
    159    gle->SetEGLSurfaceOverride(EGL_NO_SURFACE);
    160    gl::GLContextEGL::DestroySurface(*egl, mEGLSurface);
    161    mEGLSurface = EGL_NO_SURFACE;
    162  }
    163 }
    164 
    165 bool RenderCompositorOGLSWGL::BeginFrame() {
    166  MOZ_ASSERT(!mInFrame);
    167  RenderCompositorLayersSWGL::BeginFrame();
    168 
    169 #ifdef MOZ_WIDGET_ANDROID
    170  java::GeckoSurfaceTexture::DestroyUnused((int64_t)GetGLContext());
    171  GetGLContext()
    172      ->MakeCurrent();  // DestroyUnused can change the current context!
    173 #endif
    174 
    175  return true;
    176 }
    177 
    178 RenderedFrameId RenderCompositorOGLSWGL::EndFrame(
    179    const nsTArray<DeviceIntRect>& aDirtyRects) {
    180  mFullRender = false;
    181 
    182  return RenderCompositorLayersSWGL::EndFrame(aDirtyRects);
    183 }
    184 
    185 void RenderCompositorOGLSWGL::HandleExternalImage(
    186    RenderTextureHost* aExternalImage, FrameSurface& aFrameSurface) {
    187  MOZ_ASSERT(aExternalImage);
    188 
    189  // We need to hold the texture source separately from the effect,
    190  // since the effect doesn't hold a strong reference.
    191  RefPtr<TextureSource> layer =
    192      aExternalImage->CreateTextureSource(mCompositor);
    193  if (layer) {
    194    RefPtr<TexturedEffect> texturedEffect = CreateTexturedEffect(
    195        aExternalImage->GetFormat(), layer, aFrameSurface.mFilter,
    196        /* isAlphaPremultiplied */ true);
    197 
    198    auto size = layer->GetSize();
    199    gfx::Rect drawRect(0.0, 0.0, float(size.width), float(size.height));
    200 
    201    EffectChain effect;
    202    effect.mPrimaryEffect = texturedEffect;
    203    mCompositor->DrawQuad(drawRect, aFrameSurface.mClipRect, effect, 1.0,
    204                          aFrameSurface.mTransform, drawRect);
    205  }
    206 }
    207 
    208 void RenderCompositorOGLSWGL::GetCompositorCapabilities(
    209    CompositorCapabilities* aCaps) {
    210  RenderCompositor::GetCompositorCapabilities(aCaps);
    211 
    212  // max_update_rects are not yet handled properly
    213  aCaps->max_update_rects = 0;
    214 }
    215 
    216 bool RenderCompositorOGLSWGL::RequestFullRender() { return mFullRender; }
    217 
    218 void RenderCompositorOGLSWGL::Pause() {
    219 #ifdef MOZ_WIDGET_ANDROID
    220  DestroyEGLSurface();
    221 #elif defined(MOZ_WIDGET_GTK)
    222  mCompositor->Pause();
    223 #endif
    224 }
    225 
    226 bool RenderCompositorOGLSWGL::Resume() {
    227 #ifdef MOZ_WIDGET_ANDROID
    228  // Destroy EGLSurface if it exists.
    229  DestroyEGLSurface();
    230 
    231  auto size = GetBufferSize();
    232  GLint maxTextureSize = 0;
    233  GetGLContext()->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE,
    234                               (GLint*)&maxTextureSize);
    235 
    236  // When window size is too big, hardware buffer allocation could fail.
    237  if (maxTextureSize < size.width || maxTextureSize < size.height) {
    238    gfxCriticalNote << "Too big ANativeWindow size(" << size.width << ", "
    239                    << size.height << ") MaxTextureSize " << maxTextureSize;
    240    return false;
    241  }
    242 
    243  mEGLSurface = CreateEGLSurface();
    244  if (mEGLSurface == EGL_NO_SURFACE) {
    245    // Often when we fail to create an EGL surface it is because the
    246    // Java Surface we have been provided is invalid. Therefore the on
    247    // the first occurence we don't raise a WebRenderError and instead
    248    // just return failure. This allows the widget a chance to request
    249    // a new Java Surface. On subsequent failures, raising the
    250    // WebRenderError will result in the compositor being recreated,
    251    // falling back through webrender configurations, and eventually
    252    // crashing if we still do not succeed.
    253    if (!mHandlingNewSurfaceError) {
    254      mHandlingNewSurfaceError = true;
    255    } else {
    256      RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE);
    257    }
    258    return false;
    259  }
    260  mHandlingNewSurfaceError = false;
    261 
    262  gl::GLContextEGL::Cast(GetGLContext())->SetEGLSurfaceOverride(mEGLSurface);
    263  mCompositor->SetDestinationSurfaceSize(size.ToUnknownSize());
    264 #elif defined(MOZ_WIDGET_GTK)
    265  bool resumed = mCompositor->Resume();
    266  if (!resumed) {
    267    RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE);
    268    return false;
    269  }
    270 #endif
    271  return true;
    272 }
    273 
    274 bool RenderCompositorOGLSWGL::IsPaused() {
    275 #ifdef MOZ_WIDGET_ANDROID
    276  return mEGLSurface == EGL_NO_SURFACE;
    277 #else
    278  return false;
    279 #endif
    280 }
    281 
    282 LayoutDeviceIntSize RenderCompositorOGLSWGL::GetBufferSize() {
    283  return mWidget->GetClientSize();
    284 }
    285 
    286 UniquePtr<RenderCompositorLayersSWGL::Tile>
    287 RenderCompositorOGLSWGL::DoCreateTile(Surface* aSurface) {
    288  auto source = MakeRefPtr<TextureImageTextureSourceOGL>(
    289      mCompositor->AsCompositorOGL(), layers::TextureFlags::NO_FLAGS);
    290 
    291  return MakeUnique<TileOGL>(std::move(source), aSurface->TileSize());
    292 }
    293 
    294 bool RenderCompositorOGLSWGL::MaybeReadback(
    295    const gfx::IntSize& aReadbackSize, const wr::ImageFormat& aReadbackFormat,
    296    const Range<uint8_t>& aReadbackBuffer, bool* aNeedsYFlip) {
    297 #ifdef MOZ_WIDGET_ANDROID
    298  MOZ_ASSERT(aReadbackFormat == wr::ImageFormat::RGBA8);
    299  const GLenum format = LOCAL_GL_RGBA;
    300 #else
    301  MOZ_ASSERT(aReadbackFormat == wr::ImageFormat::BGRA8);
    302  const GLenum format = LOCAL_GL_BGRA;
    303 #endif
    304 
    305  GetGLContext()->fReadPixels(0, 0, aReadbackSize.width, aReadbackSize.height,
    306                              format, LOCAL_GL_UNSIGNED_BYTE,
    307                              &aReadbackBuffer[0]);
    308 
    309  if (aNeedsYFlip) {
    310    *aNeedsYFlip = true;
    311  }
    312 
    313  return true;
    314 }
    315 
    316 // This is a DataSourceSurface that represents a 0-based PBO for GLTextureImage.
    317 class PBOUnpackSurface : public gfx::DataSourceSurface {
    318 public:
    319  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PBOUnpackSurface, override)
    320 
    321  explicit PBOUnpackSurface(const gfx::IntSize& aSize) : mSize(aSize) {}
    322 
    323  uint8_t* GetData() override { return nullptr; }
    324  int32_t Stride() override { return mSize.width * sizeof(uint32_t); }
    325  gfx::SurfaceType GetType() const override {
    326    return gfx::SurfaceType::DATA_ALIGNED;
    327  }
    328  gfx::IntSize GetSize() const override { return mSize; }
    329  gfx::SurfaceFormat GetFormat() const override {
    330    return gfx::SurfaceFormat::B8G8R8A8;
    331  }
    332 
    333  // PBO offsets need to start from a 0 address, but DataSourceSurface::Map
    334  // checks for failure by comparing the address against nullptr. Override Map
    335  // to work around this.
    336  bool Map(MapType, MappedSurface* aMappedSurface) override {
    337    aMappedSurface->mData = GetData();
    338    aMappedSurface->mStride = Stride();
    339    return true;
    340  }
    341 
    342  void Unmap() override {}
    343 
    344 private:
    345  gfx::IntSize mSize;
    346 };
    347 
    348 RenderCompositorOGLSWGL::TileOGL::TileOGL(
    349    RefPtr<layers::TextureImageTextureSourceOGL>&& aTexture,
    350    const gfx::IntSize& aSize)
    351    : mTexture(aTexture) {
    352  auto* gl = mTexture->gl();
    353  if (gl && gl->HasPBOState() && gl->MakeCurrent()) {
    354    mSurface = new PBOUnpackSurface(aSize);
    355    // Create a PBO large enough to encompass any valid rects within the tile.
    356    gl->fGenBuffers(1, &mPBO);
    357    gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, mPBO);
    358    gl->fBufferData(LOCAL_GL_PIXEL_UNPACK_BUFFER,
    359                    mSurface->Stride() * aSize.height, nullptr,
    360                    LOCAL_GL_DYNAMIC_DRAW);
    361    gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0);
    362  } else {
    363    // Couldn't allocate a PBO, so just use a memory surface instead.
    364    mSurface = gfx::Factory::CreateDataSourceSurface(
    365        aSize, gfx::SurfaceFormat::B8G8R8A8);
    366  }
    367 }
    368 
    369 RenderCompositorOGLSWGL::TileOGL::~TileOGL() {
    370  if (mPBO) {
    371    auto* gl = mTexture->gl();
    372    if (gl && gl->MakeCurrent()) {
    373      gl->fDeleteBuffers(1, &mPBO);
    374      mPBO = 0;
    375    }
    376  }
    377 }
    378 
    379 layers::DataTextureSource*
    380 RenderCompositorOGLSWGL::TileOGL::GetTextureSource() {
    381  return mTexture.get();
    382 }
    383 
    384 bool RenderCompositorOGLSWGL::TileOGL::Map(wr::DeviceIntRect aDirtyRect,
    385                                           wr::DeviceIntRect aValidRect,
    386                                           void** aData, int32_t* aStride) {
    387  if (mPBO) {
    388    auto* gl = mTexture->gl();
    389    if (!gl) {
    390      return false;
    391    }
    392    // Map the PBO, but only within the range of the buffer that spans from the
    393    // linear start offset to the linear end offset. Since we don't care about
    394    // the previous contents of the buffer, we can just tell OpenGL to
    395    // invalidate the entire buffer, even though we're only mapping a sub-range.
    396    gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, mPBO);
    397    size_t stride = mSurface->Stride();
    398    size_t offset =
    399        stride * aValidRect.min.y + aValidRect.min.x * sizeof(uint32_t);
    400    size_t length = stride * (aValidRect.height() - 1) +
    401                    (aValidRect.width()) * sizeof(uint32_t);
    402    void* data = gl->fMapBufferRange(
    403        LOCAL_GL_PIXEL_UNPACK_BUFFER, offset, length,
    404        LOCAL_GL_MAP_WRITE_BIT | LOCAL_GL_MAP_INVALIDATE_BUFFER_BIT);
    405    gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0);
    406    if (!data) {
    407      return false;
    408    }
    409    *aData = data;
    410    *aStride = stride;
    411  } else {
    412    // No PBO is available, so just directly write to the memory surface.
    413    gfx::DataSourceSurface::MappedSurface map;
    414    if (!mSurface->Map(gfx::DataSourceSurface::READ_WRITE, &map)) {
    415      return false;
    416    }
    417    // Verify that we're not somehow using a PBOUnpackSurface.
    418    MOZ_ASSERT(map.mData != nullptr);
    419    // glTex(Sub)Image on ES doesn't support arbitrary strides without
    420    // the EXT_unpack_subimage extension. To avoid needing to make a
    421    // copy of the data we'll always draw it with stride = bpp*width
    422    // unless we're uploading the entire texture.
    423    if (!mTexture->IsValid()) {
    424      // If we don't have a texture we need to position our
    425      // data in the correct spot because we're going to upload
    426      // the entire surface
    427      *aData = map.mData + aValidRect.min.y * map.mStride +
    428               aValidRect.min.x * sizeof(uint32_t);
    429 
    430      *aStride = map.mStride;
    431      mSubSurface = nullptr;
    432    } else {
    433      // Otherwise, we can just use the top left as a scratch space
    434      *aData = map.mData;
    435      *aStride = aDirtyRect.width() * BytesPerPixel(mSurface->GetFormat());
    436      mSubSurface = Factory::CreateWrappingDataSourceSurface(
    437          (uint8_t*)*aData, *aStride,
    438          IntSize(aDirtyRect.width(), aDirtyRect.height()),
    439          mSurface->GetFormat());
    440    }
    441  }
    442  return true;
    443 }
    444 
    445 void RenderCompositorOGLSWGL::TileOGL::Unmap(const gfx::IntRect& aDirtyRect) {
    446  nsIntRegion dirty(aDirtyRect);
    447  if (mPBO) {
    448    // If there is a PBO, it must be unmapped before it can be sourced from.
    449    // Leave the PBO bound before the call to Update so that the texture uploads
    450    // will source from it.
    451    auto* gl = mTexture->gl();
    452    if (!gl) {
    453      return;
    454    }
    455    gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, mPBO);
    456    gl->fUnmapBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER);
    457    mTexture->Update(mSurface, &dirty);
    458    gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0);
    459  } else {
    460    if (mSubSurface) {
    461      mSurface->Unmap();
    462      // Our subsurface has a stride = aDirtyRect.width
    463      // We use a negative offset to move it to match
    464      // the dirty rect's top-left. These two offsets
    465      // will cancel each other out by the time we reach
    466      // TexSubImage.
    467      IntPoint srcOffset = {0, 0};
    468      IntPoint dstOffset = aDirtyRect.TopLeft();
    469      // adjust the dirty region to be relative to the dstOffset
    470      dirty.MoveBy(-dstOffset);
    471      mTexture->Update(mSubSurface, &dirty, &srcOffset, &dstOffset);
    472      mSubSurface = nullptr;
    473    } else {
    474      mSurface->Unmap();
    475      mTexture->Update(mSurface, &dirty);
    476    }
    477  }
    478 }
    479 
    480 }  // namespace wr
    481 }  // namespace mozilla