tor-browser

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

RendererOGL.cpp (15746B)


      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 "RendererOGL.h"
      8 
      9 #include "base/task.h"
     10 #include "GLContext.h"
     11 #include "mozilla/gfx/Logging.h"
     12 #include "mozilla/gfx/gfxVars.h"
     13 #include "mozilla/gfx/Types.h"
     14 #include "mozilla/layers/CompositorBridgeParent.h"
     15 #include "mozilla/layers/CompositorThread.h"
     16 #include "mozilla/layers/Fence.h"
     17 #include "mozilla/layers/LayersTypes.h"
     18 #include "mozilla/layers/ProfilerScreenshots.h"
     19 #include "mozilla/webrender/RenderCompositor.h"
     20 #include "mozilla/webrender/RenderTextureHost.h"
     21 #include "mozilla/widget/CompositorWidget.h"
     22 
     23 namespace mozilla {
     24 namespace wr {
     25 
     26 class RendererRecordedFrame final : public layers::RecordedFrame {
     27 public:
     28  RendererRecordedFrame(const TimeStamp& aTimeStamp, wr::Renderer* aRenderer,
     29                        const wr::RecordedFrameHandle aHandle,
     30                        const gfx::IntSize& aSize)
     31      : RecordedFrame(aTimeStamp),
     32        mRenderer(aRenderer),
     33        mSize(aSize),
     34        mHandle(aHandle) {}
     35 
     36  already_AddRefed<gfx::DataSourceSurface> GetSourceSurface() override {
     37    if (!mSurface) {
     38      mSurface = gfx::Factory::CreateDataSourceSurface(
     39          mSize, gfx::SurfaceFormat::B8G8R8A8, /* aZero = */ false);
     40 
     41      gfx::DataSourceSurface::ScopedMap map(mSurface,
     42                                            gfx::DataSourceSurface::WRITE);
     43 
     44      if (!wr_renderer_map_recorded_frame(mRenderer, mHandle, map.GetData(),
     45                                          map.GetStride() * mSize.height,
     46                                          map.GetStride())) {
     47        return nullptr;
     48      }
     49    }
     50 
     51    return do_AddRef(mSurface);
     52  }
     53 
     54 private:
     55  wr::Renderer* mRenderer;
     56  RefPtr<gfx::DataSourceSurface> mSurface;
     57  gfx::IntSize mSize;
     58  wr::RecordedFrameHandle mHandle;
     59 };
     60 
     61 wr::WrExternalImage wr_renderer_lock_external_image(void* aObj,
     62                                                    wr::ExternalImageId aId,
     63                                                    uint8_t aChannelIndex,
     64                                                    bool aIsComposited) {
     65  RendererOGL* renderer = reinterpret_cast<RendererOGL*>(aObj);
     66  RenderTextureHost* texture = renderer->GetRenderTexture(aId);
     67  MOZ_ASSERT(texture);
     68  if (!texture) {
     69    gfxCriticalNoteOnce << "Failed to lock ExternalImage for extId:"
     70                        << AsUint64(aId);
     71    return InvalidToWrExternalImage();
     72  }
     73 
     74 #if defined(MOZ_WAYLAND)
     75  // Wayland native compositor doesn't use textures for direct compositing.
     76  if (aIsComposited && texture->AsRenderDMABUFTextureHost() &&
     77      renderer->GetCompositor()->CompositorType() ==
     78          layers::WebRenderCompositor::WAYLAND) {
     79    return texture->Lock(aChannelIndex, nullptr);
     80  }
     81 #endif
     82 
     83  if (auto* gl = renderer->gl()) {
     84    return texture->Lock(aChannelIndex, gl);
     85  } else if (auto* swgl = renderer->swgl()) {
     86    return texture->LockSWGL(aChannelIndex, swgl, renderer->GetCompositor());
     87  } else {
     88    gfxCriticalNoteOnce
     89        << "No GL or SWGL context available to lock ExternalImage for extId:"
     90        << AsUint64(aId);
     91    return InvalidToWrExternalImage();
     92  }
     93 }
     94 
     95 void wr_renderer_unlock_external_image(void* aObj, wr::ExternalImageId aId,
     96                                       uint8_t aChannelIndex) {
     97  RendererOGL* renderer = reinterpret_cast<RendererOGL*>(aObj);
     98  RenderTextureHost* texture = renderer->GetRenderTexture(aId);
     99  MOZ_ASSERT(texture);
    100  if (!texture) {
    101    return;
    102  }
    103  if (renderer->gl()) {
    104    texture->Unlock();
    105  } else if (renderer->swgl()) {
    106    texture->UnlockSWGL();
    107  }
    108 }
    109 
    110 RendererOGL::RendererOGL(RefPtr<RenderThread>&& aThread,
    111                         UniquePtr<RenderCompositor> aCompositor,
    112                         wr::WindowId aWindowId, wr::Renderer* aRenderer,
    113                         layers::CompositorBridgeParent* aBridge)
    114    : mThread(aThread),
    115      mCompositor(std::move(aCompositor)),
    116      mRenderer(aRenderer),
    117      mBridge(aBridge),
    118      mWindowId(aWindowId),
    119      mDisableNativeCompositor(false),
    120      mLastPipelineInfo(new WebRenderPipelineInfo) {
    121  MOZ_ASSERT(mThread);
    122  MOZ_ASSERT(mCompositor);
    123  MOZ_ASSERT(mRenderer);
    124  MOZ_ASSERT(mBridge);
    125  MOZ_COUNT_CTOR(RendererOGL);
    126 }
    127 
    128 RendererOGL::~RendererOGL() {
    129  MOZ_COUNT_DTOR(RendererOGL);
    130  if (!mCompositor->MakeCurrent()) {
    131    gfxCriticalNote
    132        << "Failed to make render context current during destroying.";
    133    // Leak resources!
    134  } else {
    135    wr_renderer_delete(mRenderer);
    136  }
    137 }
    138 
    139 wr::WrExternalImageHandler RendererOGL::GetExternalImageHandler() {
    140  return wr::WrExternalImageHandler{
    141      this,
    142  };
    143 }
    144 
    145 void RendererOGL::SetFramePublishId(FramePublishId aPublishId) {
    146  wr_renderer_set_target_frame_publish_id(mRenderer, aPublishId);
    147 }
    148 
    149 void RendererOGL::Update() {
    150  mCompositor->Update();
    151  if (mCompositor->MakeCurrent()) {
    152    wr_renderer_update(mRenderer);
    153    FlushPipelineInfo();
    154  }
    155 }
    156 
    157 static void DoWebRenderDisableNativeCompositor(
    158    layers::CompositorBridgeParent* aBridge) {
    159  aBridge->NotifyWebRenderDisableNativeCompositor();
    160 }
    161 
    162 RenderedFrameId RendererOGL::UpdateAndRender(
    163    const Maybe<gfx::IntSize>& aReadbackSize,
    164    const Maybe<wr::ImageFormat>& aReadbackFormat,
    165    const Maybe<Range<uint8_t>>& aReadbackBuffer, bool* aNeedsYFlip,
    166    const wr::FrameReadyParams& aFrameParams, RendererStats* aOutStats) {
    167  mozilla::widget::WidgetRenderingContext widgetContext;
    168 
    169 #if defined(XP_MACOSX)
    170  widgetContext.mGL = mCompositor->gl();
    171 #endif
    172 
    173  // If present is false, WebRender needs to render some offscreen content
    174  // but we don't want to touch the window, so we avoid most interactions
    175  // with mCompositor.
    176  bool present = aFrameParams.present;
    177 
    178  LayoutDeviceIntSize size(0, 0);
    179  auto bufferAge = 0;
    180  bool fullRender = false;
    181 
    182  bool needPostRenderCall = false;
    183  bool beginFrame = !mThread->IsHandlingDeviceReset();
    184 
    185  if (beginFrame && present) {
    186    if (!mCompositor->GetWidget()->PreRender(&widgetContext)) {
    187      // XXX This could cause oom in webrender since pending_texture_updates is
    188      // not handled. It needs to be addressed.
    189      return RenderedFrameId();
    190    }
    191    needPostRenderCall = true;
    192 
    193    // XXX set clear color if MOZ_WIDGET_ANDROID is defined.
    194 
    195    if (!mCompositor->BeginFrame()) {
    196      beginFrame = false;
    197    }
    198 
    199    size = mCompositor->GetBufferSize();
    200    bufferAge = mCompositor->GetBufferAge();
    201 
    202    fullRender = mCompositor->RequestFullRender();
    203    // When we're rendering to an external target, we want to render everything.
    204    if (mCompositor->UsePartialPresent() &&
    205        (aReadbackBuffer.isSome() ||
    206         layers::ProfilerScreenshots::IsEnabled())) {
    207      fullRender = true;
    208    }
    209  } else if (!mCompositor->MakeCurrent()) {
    210    // MakeCurrent is otherwise called by mCompositor->BeginFrame above.
    211    return RenderedFrameId();
    212  }
    213 
    214  if (!beginFrame) {
    215    CheckGraphicsResetStatus(gfx::DeviceResetDetectPlace::WR_BEGIN_FRAME,
    216                             /* aForce */ true);
    217    if (needPostRenderCall) {
    218      mCompositor->GetWidget()->PostRender(&widgetContext);
    219    }
    220    return RenderedFrameId();
    221  }
    222 
    223  wr_renderer_update(mRenderer);
    224 
    225  if (fullRender) {
    226    wr_renderer_force_redraw(mRenderer);
    227  }
    228 
    229  nsTArray<DeviceIntRect> dirtyRects;
    230  bool rendered = wr_renderer_render(mRenderer, size.width, size.height,
    231                                     bufferAge, aOutStats, &dirtyRects);
    232  FlushPipelineInfo();
    233  if (!rendered) {
    234    if (present) {
    235      mCompositor->CancelFrame();
    236    }
    237    if (needPostRenderCall) {
    238      mCompositor->GetWidget()->PostRender(&widgetContext);
    239    }
    240    RenderThread::Get()->HandleWebRenderError(WebRenderError::RENDER);
    241    return RenderedFrameId();
    242  }
    243 
    244  RenderedFrameId frameId;
    245 
    246  if (present) {
    247    if (aReadbackBuffer.isSome()) {
    248      MOZ_ASSERT(aReadbackSize.isSome());
    249      MOZ_ASSERT(aReadbackFormat.isSome());
    250      if (!mCompositor->MaybeReadback(aReadbackSize.ref(),
    251                                      aReadbackFormat.ref(),
    252                                      aReadbackBuffer.ref(), aNeedsYFlip)) {
    253        wr_renderer_readback(mRenderer, aReadbackSize.ref().width,
    254                             aReadbackSize.ref().height, aReadbackFormat.ref(),
    255                             &aReadbackBuffer.ref()[0],
    256                             aReadbackBuffer.ref().length());
    257        if (aNeedsYFlip != nullptr) {
    258          *aNeedsYFlip = !mCompositor->SurfaceOriginIsTopLeft();
    259        }
    260      }
    261    }
    262 
    263    if (size.Width() != 0 && size.Height() != 0) {
    264      if (!mCompositor->MaybeGrabScreenshot(size.ToUnknownSize())) {
    265        mScreenshotGrabber.MaybeGrabScreenshot(this, size.ToUnknownSize());
    266      }
    267    }
    268 
    269    // Frame recording must happen before EndFrame, as we must ensure we read
    270    // the contents of the back buffer before any calls to SwapBuffers which
    271    // might invalidate it.
    272    MaybeRecordFrame(mLastPipelineInfo);
    273    frameId = mCompositor->EndFrame(dirtyRects);
    274    MOZ_ASSERT(needPostRenderCall);
    275    mCompositor->GetWidget()->PostRender(&widgetContext);
    276  }
    277 
    278 #if defined(ENABLE_FRAME_LATENCY_LOG)
    279  if (mFrameStartTime) {
    280    uint32_t latencyMs =
    281        round((TimeStamp::Now() - mFrameStartTime).ToMilliseconds());
    282    printf_stderr("generate frame latencyMs latencyMs %d\n", latencyMs);
    283  }
    284  // Clear frame start time
    285  mFrameStartTime = TimeStamp();
    286 #endif
    287 
    288  if (present) {
    289    if (!mCompositor->MaybeProcessScreenshotQueue()) {
    290      mScreenshotGrabber.MaybeProcessQueue(this);
    291    }
    292  }
    293 
    294  // TODO: Flush pending actions such as texture deletions/unlocks and
    295  //       textureHosts recycling.
    296 
    297  return frameId;
    298 }
    299 
    300 bool RendererOGL::EnsureAsyncScreenshot() {
    301  if (mCompositor->UseLayerCompositor()) {
    302    return mCompositor->EnableAsyncScreenshot();
    303  }
    304  if (mCompositor->SupportAsyncScreenshot()) {
    305    return true;
    306  }
    307  if (!mDisableNativeCompositor) {
    308    layers::CompositorThread()->Dispatch(
    309        NewRunnableFunction("DoWebRenderDisableNativeCompositorRunnable",
    310                            &DoWebRenderDisableNativeCompositor, mBridge));
    311 
    312    mDisableNativeCompositor = true;
    313    gfxCriticalNote << "Disable native compositor for async screenshot";
    314  }
    315  return false;
    316 }
    317 
    318 void RendererOGL::CheckGraphicsResetStatus(gfx::DeviceResetDetectPlace aPlace,
    319                                           bool aForce) {
    320  if (mCompositor) {
    321    auto reason = mCompositor->IsContextLost(aForce);
    322    if (reason != gfx::DeviceResetReason::OK) {
    323      RenderThread::Get()->HandleDeviceReset(aPlace, reason);
    324    }
    325  }
    326 }
    327 
    328 void RendererOGL::WaitForGPU() {
    329  if (!mCompositor->WaitForGPU()) {
    330    CheckGraphicsResetStatus(gfx::DeviceResetDetectPlace::WR_WAIT_FOR_GPU,
    331                             /* aForce */ true);
    332  }
    333 }
    334 
    335 RefPtr<layers::Fence> RendererOGL::GetAndResetReleaseFence() {
    336  return mCompositor->GetAndResetReleaseFence();
    337 }
    338 
    339 RenderedFrameId RendererOGL::GetLastCompletedFrameId() {
    340  return mCompositor->GetLastCompletedFrameId();
    341 }
    342 
    343 RenderedFrameId RendererOGL::UpdateFrameId() {
    344  return mCompositor->UpdateFrameId();
    345 }
    346 
    347 void RendererOGL::Pause() { mCompositor->Pause(); }
    348 
    349 bool RendererOGL::Resume() { return mCompositor->Resume(); }
    350 
    351 bool RendererOGL::IsPaused() { return mCompositor->IsPaused(); }
    352 
    353 layers::SyncObjectHost* RendererOGL::GetSyncObject() const {
    354  return mCompositor->GetSyncObject();
    355 }
    356 
    357 gl::GLContext* RendererOGL::gl() const { return mCompositor->gl(); }
    358 
    359 void* RendererOGL::swgl() const { return mCompositor->swgl(); }
    360 
    361 void RendererOGL::SetFrameStartTime(const TimeStamp& aTime) {
    362  if (mFrameStartTime) {
    363    // frame start time is already set. This could happen when multiple
    364    // generate frame requests are merged by webrender.
    365    return;
    366  }
    367  mFrameStartTime = aTime;
    368 }
    369 
    370 void RendererOGL::BeginRecording(const TimeStamp& aRecordingStart,
    371                                 wr::PipelineId aRootPipelineId) {
    372  MOZ_ASSERT(!mCompositionRecorder);
    373 
    374  mRootPipelineId = aRootPipelineId;
    375  mCompositionRecorder =
    376      MakeUnique<layers::CompositionRecorder>(aRecordingStart);
    377  mCompositor->MaybeRequestAllowFrameRecording(true);
    378 }
    379 
    380 void RendererOGL::MaybeRecordFrame(const WebRenderPipelineInfo* aPipelineInfo) {
    381  if (!mCompositionRecorder || !EnsureAsyncScreenshot()) {
    382    return;
    383  }
    384 
    385  if (!mRenderer || !aPipelineInfo || !DidPaintContent(aPipelineInfo)) {
    386    return;
    387  }
    388 
    389  if (mCompositor->MaybeRecordFrame(*mCompositionRecorder)) {
    390    return;
    391  }
    392 
    393  wr::RecordedFrameHandle handle{0};
    394  gfx::IntSize size(0, 0);
    395 
    396  if (wr_renderer_record_frame(mRenderer, wr::ImageFormat::BGRA8, &handle,
    397                               &size.width, &size.height)) {
    398    RefPtr<layers::RecordedFrame> frame =
    399        new RendererRecordedFrame(TimeStamp::Now(), mRenderer, handle, size);
    400 
    401    mCompositionRecorder->RecordFrame(frame);
    402  }
    403 }
    404 
    405 bool RendererOGL::DidPaintContent(const WebRenderPipelineInfo* aFrameEpochs) {
    406  const wr::WrPipelineInfo& info = aFrameEpochs->Raw();
    407  bool didPaintContent = false;
    408 
    409  // Check if a non-root pipeline has updated to a new epoch.
    410  // We treat all non-root pipelines as "content" pipelines, even if they're
    411  // not fed by content paints, such as videos (see bug 1665512).
    412  for (const auto& epoch : info.epochs) {
    413    const wr::PipelineId pipelineId = epoch.pipeline_id;
    414 
    415    if (pipelineId == mRootPipelineId) {
    416      continue;
    417    }
    418 
    419    const auto it = mContentPipelineEpochs.find(AsUint64(pipelineId));
    420    if (it == mContentPipelineEpochs.end() || it->second != epoch.epoch) {
    421      // This pipeline has updated since last render or has newly rendered.
    422      didPaintContent = true;
    423      mContentPipelineEpochs[AsUint64(pipelineId)] = epoch.epoch;
    424    }
    425  }
    426 
    427  for (const auto& removedPipeline : info.removed_pipelines) {
    428    const wr::PipelineId pipelineId = removedPipeline.pipeline_id;
    429    if (pipelineId == mRootPipelineId) {
    430      continue;
    431    }
    432    mContentPipelineEpochs.erase(AsUint64(pipelineId));
    433  }
    434 
    435  return didPaintContent;
    436 }
    437 
    438 Maybe<layers::FrameRecording> RendererOGL::EndRecording() {
    439  if (!mCompositionRecorder) {
    440    MOZ_DIAGNOSTIC_ASSERT(
    441        false, "Attempted to get frames from a window that was not recording.");
    442    return Nothing();
    443  }
    444 
    445  auto maybeRecording = mCompositionRecorder->GetRecording();
    446 
    447  wr_renderer_release_composition_recorder_structures(mRenderer);
    448 
    449  mCompositor->MaybeRequestAllowFrameRecording(false);
    450  mCompositionRecorder = nullptr;
    451 
    452  return maybeRecording;
    453 }
    454 
    455 void RendererOGL::FlushPipelineInfo() {
    456  RefPtr<WebRenderPipelineInfo> info = new WebRenderPipelineInfo;
    457  wr_renderer_flush_pipeline_info(mRenderer, &info->Raw());
    458  mLastPipelineInfo = info;
    459 }
    460 
    461 RenderTextureHost* RendererOGL::GetRenderTexture(
    462    wr::ExternalImageId aExternalImageId) {
    463  return mThread->GetRenderTexture(aExternalImageId);
    464 }
    465 
    466 void RendererOGL::AccumulateMemoryReport(MemoryReport* aReport) {
    467  wr_renderer_accumulate_memory_report(GetRenderer(), aReport, swgl());
    468 
    469  LayoutDeviceIntSize size = mCompositor->GetBufferSize();
    470 
    471  // Assume BGRA8 for the format since it's not exposed anywhere,
    472  // and all compositor backends should be using that.
    473  uintptr_t swapChainSize = size.width * size.height *
    474                            BytesPerPixel(gfx::SurfaceFormat::B8G8R8A8) *
    475                            (mCompositor->UseTripleBuffering() ? 3 : 2);
    476  aReport->swap_chain += swapChainSize;
    477 }
    478 
    479 void RendererOGL::SetProfilerUI(const nsACString& aUI) {
    480  wr_renderer_set_profiler_ui(GetRenderer(), (const uint8_t*)aUI.BeginReading(),
    481                              aUI.Length());
    482 }
    483 
    484 }  // namespace wr
    485 }  // namespace mozilla