gfx/webrender_bindings/RendererOGL.cpp
author Mike Hommey <mh+mozilla@glandium.org>
Thu, 28 Sep 2017 08:46:43 +0900
changeset 384593 d6dc8ba8b0762681e7c0ed4005ca5a194d48b325
parent 379016 5ca9c4d3658cda11a748e7e8722f3fc51fc80b5d
child 389027 5f74d262924171a8fa0b4483a64f4cac34c4c9b8
child 389104 40ec5550739449f1cb8ad7d030a40cc10f9653bc
permissions -rw-r--r--
Bug 1403366 - Don't use nsDirectoryService::Create in nsDirectoryService::GetCurrentProcessDirectory. r=froydnj Back in the day, there was no global with an already initialized DirectoryService. But now there is, and, in fact, GetCurrentProcessDirectory already errors out if that global is not set by the time it's called. All calling nsDirectoryService::Create achieves is doing the check again and calling QueryInterface, which we don't need to do anyways.

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "RendererOGL.h"
#include "GLContext.h"
#include "GLContextProvider.h"
#include "mozilla/gfx/Logging.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/layers/CompositorBridgeParent.h"
#include "mozilla/layers/CompositorThread.h"
#include "mozilla/layers/LayersTypes.h"
#include "mozilla/webrender/RenderBufferTextureHost.h"
#include "mozilla/webrender/RenderTextureHostOGL.h"
#include "mozilla/widget/CompositorWidget.h"

namespace mozilla {
namespace wr {

wr::WrExternalImage LockExternalImage(void* aObj, wr::WrExternalImageId aId, uint8_t aChannelIndex)
{
  RendererOGL* renderer = reinterpret_cast<RendererOGL*>(aObj);
  RenderTextureHost* texture = renderer->GetRenderTexture(aId);

  if (texture->AsBufferTextureHost()) {
    RenderBufferTextureHost* bufferTexture = texture->AsBufferTextureHost();
    MOZ_ASSERT(bufferTexture);

    if (bufferTexture->Lock()) {
      RenderBufferTextureHost::RenderBufferData data =
          bufferTexture->GetBufferDataForRender(aChannelIndex);

      return RawDataToWrExternalImage(data.mData, data.mBufferSize);
    } else {
      return RawDataToWrExternalImage(nullptr, 0);
    }
  } else {
    // texture handle case
    RenderTextureHostOGL* textureOGL = texture->AsTextureHostOGL();
    MOZ_ASSERT(textureOGL);

    textureOGL->SetGLContext(renderer->mGL);
    gfx::IntSize size = textureOGL->GetSize(aChannelIndex);
    if (textureOGL->Lock()) {
      return NativeTextureToWrExternalImage(textureOGL->GetGLHandle(aChannelIndex),
                                            0, 0,
                                            size.width, size.height);
    } else {
      // Just use 0 for the gl handle if the lock() was failed.
      return NativeTextureToWrExternalImage(0,
                                            0, 0,
                                            size.width, size.height);
    }
  }
}

void UnlockExternalImage(void* aObj, wr::WrExternalImageId aId, uint8_t aChannelIndex)
{
  RendererOGL* renderer = reinterpret_cast<RendererOGL*>(aObj);
  RenderTextureHost* texture = renderer->GetRenderTexture(aId);
  MOZ_ASSERT(texture);
  texture->Unlock();
}

RendererOGL::RendererOGL(RefPtr<RenderThread>&& aThread,
                         RefPtr<gl::GLContext>&& aGL,
                         RefPtr<widget::CompositorWidget>&& aWidget,
                         wr::WindowId aWindowId,
                         wr::Renderer* aRenderer,
                         layers::CompositorBridgeParentBase* aBridge)
  : mThread(aThread)
  , mGL(aGL)
  , mWidget(aWidget)
  , mRenderer(aRenderer)
  , mBridge(aBridge)
  , mWindowId(aWindowId)
  , mDebugFlags({ 0 })
{
  MOZ_ASSERT(mThread);
  MOZ_ASSERT(mGL);
  MOZ_ASSERT(mWidget);
  MOZ_ASSERT(mRenderer);
  MOZ_ASSERT(mBridge);
  MOZ_COUNT_CTOR(RendererOGL);

#ifdef XP_WIN
  if (aGL->IsANGLE()) {
    gl::GLLibraryEGL* egl = &gl::sEGLLibrary;

    // Fetch the D3D11 device.
    EGLDeviceEXT eglDevice = nullptr;
    egl->fQueryDisplayAttribEXT(egl->Display(), LOCAL_EGL_DEVICE_EXT, (EGLAttrib*)&eglDevice);
    MOZ_ASSERT(eglDevice);
    ID3D11Device* device = nullptr;
    egl->fQueryDeviceAttribEXT(eglDevice, LOCAL_EGL_D3D11_DEVICE_ANGLE, (EGLAttrib*)&device);
    MOZ_ASSERT(device);

    mSyncObject = layers::SyncObjectHost::CreateSyncObjectHost(device);
    if (mSyncObject) {
      if (!mSyncObject->Init()) {
        // Some errors occur. Clear the mSyncObject here.
        // Then, there will be no texture synchronization.
        mSyncObject = nullptr;
      }
    }
  }
#endif
}

RendererOGL::~RendererOGL()
{
  MOZ_COUNT_DTOR(RendererOGL);
  if (!mGL->MakeCurrent()) {
    gfxCriticalNote << "Failed to make render context current during destroying.";
    // Leak resources!
    return;
  }
  wr_renderer_delete(mRenderer);
}

wr::WrExternalImageHandler
RendererOGL::GetExternalImageHandler()
{
  return wr::WrExternalImageHandler {
    this,
    LockExternalImage,
    UnlockExternalImage,
  };
}

void
RendererOGL::Update()
{
  wr_renderer_update(mRenderer);
}

bool
RendererOGL::Render()
{
  uint32_t flags = gfx::gfxVars::WebRenderDebugFlags();

  if (mDebugFlags.mBits != flags) {
    mDebugFlags.mBits = flags;
    wr_renderer_set_debug_flags(mRenderer, mDebugFlags);
  }

  if (!mGL->MakeCurrent()) {
    gfxCriticalNote << "Failed to make render context current, can't draw.";
    NotifyWebRenderError(WebRenderError::MAKE_CURRENT);
    return false;
  }

  mozilla::widget::WidgetRenderingContext widgetContext;

#if defined(XP_MACOSX)
  widgetContext.mGL = mGL;
// TODO: we don't have a notion of compositor here.
//#elif defined(MOZ_WIDGET_ANDROID)
//  widgetContext.mCompositor = mCompositor;
#endif

  if (!mWidget->PreRender(&widgetContext)) {
    // XXX This could cause oom in webrender since pending_texture_updates is not handled.
    // It needs to be addressed.
    return false;
  }
  // XXX set clear color if MOZ_WIDGET_ANDROID is defined.

  auto size = mWidget->GetClientSize();

  if (mSyncObject) {
    // XXX: if the synchronization is failed, we should handle the device reset.
    mSyncObject->Synchronize();
  }

  if (!wr_renderer_render(mRenderer, size.width, size.height)) {
    NotifyWebRenderError(WebRenderError::RENDER);
  }

  mGL->SwapBuffers();
  mWidget->PostRender(&widgetContext);

#if defined(ENABLE_FRAME_LATENCY_LOG)
  if (mFrameStartTime) {
    uint32_t latencyMs = round((TimeStamp::Now() - mFrameStartTime).ToMilliseconds());
    printf_stderr("generate frame latencyMs latencyMs %d\n", latencyMs);
  }
  // Clear frame start time
  mFrameStartTime = TimeStamp();
#endif

  // TODO: Flush pending actions such as texture deletions/unlocks and
  //       textureHosts recycling.

  return true;
}

void
RendererOGL::Pause()
{
#ifdef MOZ_WIDGET_ANDROID
  if (!mGL || mGL->IsDestroyed()) {
    return;
  }
  // ReleaseSurface internally calls MakeCurrent.
  mGL->ReleaseSurface();
#endif
}

bool
RendererOGL::Resume()
{
#ifdef MOZ_WIDGET_ANDROID
  if (!mGL || mGL->IsDestroyed()) {
    return false;
  }
  // RenewSurface internally calls MakeCurrent.
  return mGL->RenewSurface(mWidget);
#else
  return true;
#endif
}

void
RendererOGL::SetFrameStartTime(const TimeStamp& aTime)
{
  if (mFrameStartTime) {
    // frame start time is already set. This could happen when multiple
    // generate frame requests are merged by webrender.
    return;
  }
  mFrameStartTime = aTime;
}

wr::WrRenderedEpochs*
RendererOGL::FlushRenderedEpochs()
{
  return wr_renderer_flush_rendered_epochs(mRenderer);
}

RenderTextureHost*
RendererOGL::GetRenderTexture(wr::WrExternalImageId aExternalImageId)
{
  return mThread->GetRenderTexture(aExternalImageId);
}

static void
DoNotifyWebRenderError(layers::CompositorBridgeParentBase* aBridge, WebRenderError aError)
{
  aBridge->NotifyWebRenderError(aError);
}

void
RendererOGL::NotifyWebRenderError(WebRenderError aError)
{
  layers::CompositorThreadHolder::Loop()->PostTask(NewRunnableFunction(
    &DoNotifyWebRenderError,
    mBridge,
    aError
  ));
}

} // namespace wr
} // namespace mozilla