Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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 "RenderCompositorLayerNative.h"
#include "GLContext.h"
#include "GLContextProvider.h"
#include "mozilla/ProfilerLabels.h"
#include "mozilla/ProfilerMarkers.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/gfx/Logging.h"
#include "mozilla/layers/CompositionRecorder.h"
#include "mozilla/layers/GpuFence.h"
#include "mozilla/layers/NativeLayer.h"
#include "mozilla/layers/ProfilerScreenshots.h"
#include "mozilla/layers/SurfacePool.h"
#include "mozilla/StaticPrefs_gfx.h"
#include "mozilla/webrender/RenderTextureHost.h"
#include "mozilla/webrender/RenderThread.h"
#include "mozilla/widget/CompositorWidget.h"
#include "RenderCompositorRecordedFrame.h"
namespace mozilla::wr {
extern LazyLogModule gRenderThreadLog;
#define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
RenderCompositorLayerNative::RenderCompositorLayerNative(
const RefPtr<widget::CompositorWidget>& aWidget, gl::GLContext* aGL)
: RenderCompositor(aWidget),
mNativeLayerRoot(GetWidget()->GetNativeLayerRoot()) {
LOG("RenderCompositorLayerNative::RenderCompositorLayerNative()");
MOZ_ASSERT(mNativeLayerRoot);
#if defined(XP_DARWIN) || defined(MOZ_WAYLAND)
auto pool = RenderThread::Get()->SharedSurfacePool();
if (pool) {
mSurfacePoolHandle = pool->GetHandleForGL(aGL);
}
#endif
MOZ_RELEASE_ASSERT(mSurfacePoolHandle);
}
RenderCompositorLayerNative::~RenderCompositorLayerNative() {
LOG("RRenderCompositorLayerNative::~RenderCompositorLayerNative()");
mProfilerScreenshotGrabber.Destroy();
mNativeLayerRoot->SetLayers({});
mNativeLayerRootSnapshotter = nullptr;
mNativeLayerRoot = nullptr;
}
bool RenderCompositorLayerNative::BeginFrame() {
if (!MakeCurrent()) {
gfxCriticalNote << "Failed to make render context current, can't draw.";
return false;
}
if (!InitDefaultFramebuffer()) {
return false;
}
return true;
}
RenderedFrameId RenderCompositorLayerNative::EndFrame(
const nsTArray<DeviceIntRect>& aDirtyRects) {
RenderedFrameId frameId = GetNextRenderFrameId();
DoSwap();
MOZ_ASSERT(mPendingGpuFeces.empty());
return frameId;
}
void RenderCompositorLayerNative::Pause() {}
bool RenderCompositorLayerNative::Resume() { return true; }
inline layers::WebRenderCompositor RenderCompositorLayerNative::CompositorType()
const {
#if defined(XP_DARWIN)
return layers::WebRenderCompositor::CORE_ANIMATION;
#elif defined(MOZ_WAYLAND)
return layers::WebRenderCompositor::WAYLAND;
#else
MOZ_ASSERT_UNREACHABLE("Unexpected to be called!");
return layers::WebRenderCompositor::Unknown;
#endif
}
LayoutDeviceIntSize RenderCompositorLayerNative::GetBufferSize() {
return mWidget->GetClientSize();
}
bool RenderCompositorLayerNative::ShouldUseNativeCompositor() { return false; }
bool RenderCompositorLayerNative::ShouldUseLayerCompositor() const {
return UseLayerCompositor();
}
bool RenderCompositorLayerNative::UseLayerCompositor() const { return true; }
bool RenderCompositorLayerNative::EnableAsyncScreenshot() {
#if defined(XP_DARWIN)
// On macOS, NativeLayerRootSnapshotterCA supports to take snapshot with
// multiple layers.
return true;
#else
// Request WebRender to use only one layer for content rendering during taking
// snapshot. In addition to the content layer, one debug layer could exist.
mAsyncScreenshotLastFrameUsed = mCurrentFrame;
if (!mEnableAsyncScreenshot) {
mEnableAsyncScreenshotInNextFrame = true;
return false;
}
return true;
#endif
}
void RenderCompositorLayerNative::GetCompositorCapabilities(
CompositorCapabilities* aCaps) {
RenderCompositor::GetCompositorCapabilities(aCaps);
}
void RenderCompositorLayerNative::GetWindowProperties(
WindowProperties* aProperties) {
// XXX
aProperties->is_opaque = false;
aProperties->enable_screenshot = mEnableAsyncScreenshot;
}
RenderCompositorLayerNative::Surface::~Surface() = default;
RenderCompositorLayerNative::Surface::Surface(wr::DeviceIntSize aSize,
bool aIsOpaque)
: mSize(aSize), mIsOpaque(aIsOpaque) {}
bool RenderCompositorLayerNative::MaybeReadback(
const gfx::IntSize& aReadbackSize, const wr::ImageFormat& aReadbackFormat,
const Range<uint8_t>& aReadbackBuffer, bool* aNeedsYFlip) {
MOZ_RELEASE_ASSERT(aReadbackFormat == wr::ImageFormat::BGRA8);
if (!mNativeLayerRootSnapshotter) {
mNativeLayerRootSnapshotter = mNativeLayerRoot->CreateSnapshotter();
if (!mNativeLayerRootSnapshotter) {
return false;
}
}
bool success = mNativeLayerRootSnapshotter->ReadbackPixels(
aReadbackSize, gfx::SurfaceFormat::B8G8R8A8, aReadbackBuffer);
// ReadbackPixels might have changed the current context. Make sure GL is
// current again.
MakeCurrent();
if (aNeedsYFlip) {
*aNeedsYFlip = true;
}
return success;
}
bool RenderCompositorLayerNative::MaybeRecordFrame(
layers::CompositionRecorder& aRecorder) {
if (!mNativeLayerRootSnapshotter) {
mNativeLayerRootSnapshotter = mNativeLayerRoot->CreateSnapshotter();
}
if (!mNativeLayerRootSnapshotter) {
return true;
}
gfx::IntSize size = GetBufferSize().ToUnknownSize();
RefPtr<layers::profiler_screenshots::RenderSource> snapshot =
mNativeLayerRootSnapshotter->GetWindowContents(size);
if (!snapshot) {
return true;
}
RefPtr<layers::profiler_screenshots::AsyncReadbackBuffer> buffer =
mNativeLayerRootSnapshotter->CreateAsyncReadbackBuffer(size);
buffer->CopyFrom(snapshot);
RefPtr<layers::RecordedFrame> frame =
new RenderCompositorRecordedFrame(TimeStamp::Now(), std::move(buffer));
aRecorder.RecordFrame(frame);
// GetWindowContents might have changed the current context. Make sure our
// context is current again.
MakeCurrent();
return true;
}
bool RenderCompositorLayerNative::MaybeGrabScreenshot(
const gfx::IntSize& aWindowSize) {
if (!mozilla::layers::ProfilerScreenshots::IsEnabled()) {
return false;
}
if (!mNativeLayerRootSnapshotter) {
mNativeLayerRootSnapshotter = mNativeLayerRoot->CreateSnapshotter();
}
if (mNativeLayerRootSnapshotter) {
mProfilerScreenshotGrabber.MaybeGrabScreenshot(*mNativeLayerRootSnapshotter,
aWindowSize);
// MaybeGrabScreenshot might have changed the current context. Make sure our
// context is current again.
MakeCurrent();
}
return true;
}
bool RenderCompositorLayerNative::MaybeProcessScreenshotQueue() {
mProfilerScreenshotGrabber.MaybeProcessQueue();
// MaybeProcessQueue might have changed the current context. Make sure our
// context is current again.
MakeCurrent();
return true;
}
void RenderCompositorLayerNative::WaitUntilPresentationFlushed() {
mNativeLayerRoot->WaitUntilCommitToScreenHasBeenProcessed();
}
void RenderCompositorLayerNative::CompositorBeginFrame() {
mAddedLayers.Clear();
mBeginFrameTimeStamp = TimeStamp::Now();
mSurfacePoolHandle->OnBeginFrame();
mNativeLayerRoot->PrepareForCommit();
mCurrentFrame++;
if (mEnableAsyncScreenshotInNextFrame) {
mEnableAsyncScreenshot = true;
mEnableAsyncScreenshotInNextFrame = false;
}
}
void RenderCompositorLayerNative::CompositorEndFrame() {
#if defined(XP_DARWIN)
// MacOS fails rendering without the flush here.
DoFlush();
#endif
mNativeLayerRoot->SetLayers(mAddedLayers);
mNativeLayerRoot->CommitToScreen();
mSurfacePoolHandle->OnEndFrame();
if (mEnableAsyncScreenshot &&
(mCurrentFrame - mAsyncScreenshotLastFrameUsed) > 1) {
mEnableAsyncScreenshot = false;
}
}
void RenderCompositorLayerNative::BindNativeLayer(wr::NativeSurfaceId aId) {
MOZ_RELEASE_ASSERT(!mCurrentlyBoundNativeLayer);
auto surfaceCursor = mSurfaces.find(aId);
MOZ_RELEASE_ASSERT(surfaceCursor != mSurfaces.end());
Surface& surface = surfaceCursor->second;
mCurrentlyBoundNativeLayer = surface.mNativeLayer;
}
void RenderCompositorLayerNative::UnbindNativeLayer() {
MOZ_RELEASE_ASSERT(mCurrentlyBoundNativeLayer);
mCurrentlyBoundNativeLayer->NotifySurfaceReady();
mCurrentlyBoundNativeLayer = nullptr;
}
void RenderCompositorLayerNative::CreateSurface(
wr::NativeSurfaceId aId, wr::DeviceIntPoint aVirtualOffset,
wr::DeviceIntSize aTileSize, bool aIsOpaque) {
MOZ_ASSERT_UNREACHABLE("Unexpected to be called!");
}
void RenderCompositorLayerNative::CreateExternalSurface(wr::NativeSurfaceId aId,
bool aIsOpaque) {
MOZ_RELEASE_ASSERT(mSurfaces.find(aId) == mSurfaces.end());
RefPtr<layers::NativeLayer> layer =
mNativeLayerRoot->CreateLayerForExternalTexture(aIsOpaque);
Surface surface{DeviceIntSize{}, aIsOpaque};
surface.mIsExternal = true;
surface.mNativeLayer = layer;
mSurfaces.insert({aId, std::move(surface)});
}
void RenderCompositorLayerNative::CreateSwapChainSurface(
wr::NativeSurfaceId aId, wr::DeviceIntSize aSize, bool aIsOpaque,
bool aNeedsSyncDcompCommit) {
MOZ_RELEASE_ASSERT(mSurfaces.find(aId) == mSurfaces.end());
Surface surface{aSize, aIsOpaque};
surface.mNativeLayer = mNativeLayerRoot->CreateLayer(
surface.Size(), aIsOpaque, mSurfacePoolHandle);
mSurfaces.insert({aId, std::move(surface)});
}
void RenderCompositorLayerNative::ResizeSwapChainSurface(
wr::NativeSurfaceId aId, wr::DeviceIntSize aSize) {
auto surfaceCursor = mSurfaces.find(aId);
MOZ_RELEASE_ASSERT(surfaceCursor != mSurfaces.end());
Surface& surface = surfaceCursor->second;
MOZ_ASSERT(!surface.mIsExternal);
if (aSize == surface.mSize) {
return;
}
surface.mSize = aSize;
surface.mNativeLayer = mNativeLayerRoot->CreateLayer(
surface.Size(), surface.mIsOpaque, mSurfacePoolHandle);
}
void RenderCompositorLayerNative::AttachExternalImage(
wr::NativeSurfaceId aId, wr::ExternalImageId aExternalImage) {
RenderTextureHost* image =
RenderThread::Get()->GetRenderTexture(aExternalImage);
MOZ_RELEASE_ASSERT(image);
auto surfaceCursor = mSurfaces.find(aId);
MOZ_RELEASE_ASSERT(surfaceCursor != mSurfaces.end());
Surface& surface = surfaceCursor->second;
MOZ_RELEASE_ASSERT(surface.mNativeLayer);
MOZ_RELEASE_ASSERT(surface.mIsExternal);
surface.mNativeLayer->AttachExternalImage(image);
}
void RenderCompositorLayerNativeOGL::AttachExternalImage(
wr::NativeSurfaceId aId, wr::ExternalImageId aExternalImage) {
RenderTextureHost* image =
RenderThread::Get()->GetRenderTexture(aExternalImage);
// image->Lock only uses the channel index to populate the returned
// `WrExternalImage`. Since we don't use that, it doesn't matter
// what channel index we pass.
image->Lock(0, mGL);
RenderCompositorLayerNative::AttachExternalImage(aId, aExternalImage);
}
void RenderCompositorLayerNative::DestroySurface(NativeSurfaceId aId) {
auto surfaceCursor = mSurfaces.find(aId);
MOZ_RELEASE_ASSERT(surfaceCursor != mSurfaces.end());
mSurfaces.erase(surfaceCursor);
}
void RenderCompositorLayerNative::CreateTile(wr::NativeSurfaceId aId, int aX,
int aY) {
MOZ_ASSERT_UNREACHABLE("Unexpected to be called!");
}
void RenderCompositorLayerNative::DestroyTile(wr::NativeSurfaceId aId, int aX,
int aY) {
MOZ_ASSERT_UNREACHABLE("Unexpected to be called!");
}
/* static */
gfx::SamplingFilter RenderCompositorLayerNative::ToSamplingFilter(
wr::ImageRendering aImageRendering) {
if (aImageRendering == wr::ImageRendering::Auto) {
return gfx::SamplingFilter::LINEAR;
}
return gfx::SamplingFilter::POINT;
}
void RenderCompositorLayerNative::AddSurface(
wr::NativeSurfaceId aId, const wr::CompositorSurfaceTransform& aTransform,
wr::DeviceIntRect aClipRect, wr::ImageRendering aImageRendering,
wr::DeviceIntRect aRoundedClipRect, wr::ClipRadius aClipRadius) {
MOZ_RELEASE_ASSERT(!mCurrentlyBoundNativeLayer);
auto surfaceCursor = mSurfaces.find(aId);
MOZ_RELEASE_ASSERT(surfaceCursor != mSurfaces.end());
const Surface& surface = surfaceCursor->second;
float sx = aTransform.scale.x;
float sy = aTransform.scale.y;
float tx = aTransform.offset.x;
float ty = aTransform.offset.y;
gfx::Matrix4x4 transform(sx, 0.0, 0.0, 0.0, 0.0, sy, 0.0, 0.0, 0.0, 0.0, 1.0,
0.0, tx, ty, 0.0, 1.0);
RefPtr<layers::NativeLayer> layer = surface.mNativeLayer;
gfx::IntPoint layerPosition(0, 0);
layer->SetPosition(layerPosition);
gfx::IntRect clipRect(aClipRect.min.x, aClipRect.min.y, aClipRect.width(),
aClipRect.height());
layer->SetClipRect(Some(clipRect));
gfx::Rect roundedClipRect(aRoundedClipRect.min.x, aRoundedClipRect.min.y,
aRoundedClipRect.width(),
aRoundedClipRect.height());
gfx::RectCornerRadii clipRadius(aClipRadius.top_left, aClipRadius.top_right,
aClipRadius.bottom_right,
aClipRadius.bottom_left);
gfx::RoundedRect roundedClip(roundedClipRect, clipRadius);
layer->SetRoundedClipRect(Some(roundedClip));
layer->SetTransform(transform);
layer->SetSamplingFilter(ToSamplingFilter(aImageRendering));
mAddedLayers.AppendElement(layer);
if (surface.mIsExternal) {
RefPtr<layers::GpuFence> fence = layer->GetGpuFence();
if (fence && BackendType() == layers::WebRenderBackend::HARDWARE) {
mPendingGpuFeces.emplace_back(fence);
}
}
}
/* static */
UniquePtr<RenderCompositor> RenderCompositorLayerNativeOGL::Create(
const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError) {
RefPtr<gl::GLContext> gl = RenderThread::Get()->SingletonGL();
if (!gl) {
gl = gl::GLContextProvider::CreateForCompositorWidget(
aWidget, /* aHardwareWebRender */ true, /* aForceAccelerated */ true);
RenderThread::MaybeEnableGLDebugMessage(gl);
}
if (!gl || !gl->MakeCurrent()) {
gfxCriticalNote << "Failed GL context creation for WebRender: "
<< gfx::hexa(gl.get());
return nullptr;
}
return MakeUnique<RenderCompositorLayerNativeOGL>(aWidget, std::move(gl));
}
RenderCompositorLayerNativeOGL::RenderCompositorLayerNativeOGL(
const RefPtr<widget::CompositorWidget>& aWidget,
RefPtr<gl::GLContext>&& aGL)
: RenderCompositorLayerNative(aWidget, aGL), mGL(aGL) {
MOZ_ASSERT(mGL);
}
RenderCompositorLayerNativeOGL::~RenderCompositorLayerNativeOGL() {
if (!mGL->MakeCurrent()) {
gfxCriticalNote
<< "Failed to make render context current during destroying.";
// Leak resources!
mPreviousFrameDoneFences = nullptr;
mThisFrameDoneFences = nullptr;
return;
}
if (mPreviousFrameDoneFences && mPreviousFrameDoneFences->mSync) {
mGL->fDeleteSync(mPreviousFrameDoneFences->mSync);
}
if (mThisFrameDoneFences && mThisFrameDoneFences->mSync) {
mGL->fDeleteSync(mThisFrameDoneFences->mSync);
}
}
bool RenderCompositorLayerNativeOGL::InitDefaultFramebuffer() {
mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mGL->GetDefaultFramebuffer());
return true;
}
void RenderCompositorLayerNativeOGL::DoSwap() { InsertFrameDoneSync(); }
void RenderCompositorLayerNativeOGL::DoFlush() { mGL->fFlush(); }
void RenderCompositorLayerNativeOGL::InsertFrameDoneSync() {
#ifdef XP_DARWIN
// Only do this on macOS.
// On other platforms, SwapBuffers automatically applies back-pressure.
if (mThisFrameDoneFences && mThisFrameDoneFences->mSync) {
mGL->fDeleteSync(mThisFrameDoneFences->mSync);
}
mThisFrameDoneFences =
MakeUnique<BackPressureFences>(std::move(mPendingGpuFeces));
mThisFrameDoneFences->mSync =
mGL->fFenceSync(LOCAL_GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
#endif
}
bool RenderCompositorLayerNativeOGL::WaitForGPU() {
if (mPreviousFrameDoneFences) {
bool complete = false;
while (!complete) {
complete = true;
for (const auto& fence : mPreviousFrameDoneFences->mGpuFeces) {
if (!fence->HasCompleted()) {
complete = false;
break;
}
}
if (!complete) {
PR_Sleep(PR_MillisecondsToInterval(1));
}
}
if (mPreviousFrameDoneFences->mSync) {
AUTO_PROFILER_LABEL("Waiting for GPU to finish previous frame", GRAPHICS);
mGL->fClientWaitSync(mPreviousFrameDoneFences->mSync,
LOCAL_GL_SYNC_FLUSH_COMMANDS_BIT,
LOCAL_GL_TIMEOUT_IGNORED);
mGL->fDeleteSync(mPreviousFrameDoneFences->mSync);
}
}
mPreviousFrameDoneFences = std::move(mThisFrameDoneFences);
MOZ_ASSERT(!mThisFrameDoneFences);
return true;
}
void RenderCompositorLayerNativeOGL::BindSwapChain(
wr::NativeSurfaceId aId, const wr::DeviceIntRect* aDirtyRects,
size_t aNumDirtyRects) {
BindNativeLayer(aId);
MOZ_ASSERT(mCurrentlyBoundNativeLayer);
gfx::IntSize size = mCurrentlyBoundNativeLayer->GetSize();
gfx::IntRect validRect = mCurrentlyBoundNativeLayer->GetRect();
const auto dirtyRect = [&]() {
if (aNumDirtyRects > 0) {
MOZ_RELEASE_ASSERT(aNumDirtyRects == 1);
const auto& rect = aDirtyRects[0];
// Clip rect to bufferSize
int left = std::clamp((int)rect.min.x, 0, size.width);
int top = std::clamp((int)rect.min.y, 0, size.height);
int right = std::clamp((int)rect.max.x, 0, size.width);
int bottom = std::clamp((int)rect.max.y, 0, size.height);
int width = right - left;
int height = bottom - top;
MOZ_RELEASE_ASSERT(width >= 0);
MOZ_RELEASE_ASSERT(height >= 0);
return gfx::IntRect(left, top, width, height);
}
return gfx::IntRect(0, 0, size.width, size.height);
}();
Maybe<GLuint> fbo = mCurrentlyBoundNativeLayer->NextSurfaceAsFramebuffer(
validRect, dirtyRect, true);
if (!fbo) {
// XXX
return;
}
mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, *fbo);
}
void RenderCompositorLayerNativeOGL::PresentSwapChain(
wr::NativeSurfaceId aId, const wr::DeviceIntRect* aDirtyRects,
size_t aNumDirtyRects) {
mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
UnbindNativeLayer();
}
} // namespace mozilla::wr