summaryrefslogtreecommitdiffstats
path: root/gfx/webrender_bindings/RenderThread.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/webrender_bindings/RenderThread.cpp')
-rw-r--r--gfx/webrender_bindings/RenderThread.cpp1648
1 files changed, 1648 insertions, 0 deletions
diff --git a/gfx/webrender_bindings/RenderThread.cpp b/gfx/webrender_bindings/RenderThread.cpp
new file mode 100644
index 0000000000..ec47c9a213
--- /dev/null
+++ b/gfx/webrender_bindings/RenderThread.cpp
@@ -0,0 +1,1648 @@
+/* -*- 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 "base/task.h"
+#include "GeckoProfiler.h"
+#include "gfxPlatform.h"
+#include "GLContext.h"
+#include "RenderThread.h"
+#include "nsThread.h"
+#include "nsThreadUtils.h"
+#include "transport/runnable_utils.h"
+#include "mozilla/BackgroundHangMonitor.h"
+#include "mozilla/layers/AsyncImagePipelineManager.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/GPUParent.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "mozilla/glean/GleanMetrics.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/CompositorManagerParent.h"
+#include "mozilla/layers/WebRenderBridgeParent.h"
+#include "mozilla/layers/SharedSurfacesParent.h"
+#include "mozilla/layers/SurfacePool.h"
+#include "mozilla/layers/SynchronousTask.h"
+#include "mozilla/PerfStats.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/webrender/RendererOGL.h"
+#include "mozilla/webrender/RenderTextureHost.h"
+#include "mozilla/widget/CompositorWidget.h"
+#include "OGLShaderProgram.h"
+
+#ifdef XP_WIN
+# include "GLContextEGL.h"
+# include "GLLibraryEGL.h"
+# include "mozilla/widget/WinCompositorWindowThread.h"
+# include "mozilla/gfx/DeviceManagerDx.h"
+# include "mozilla/webrender/DCLayerTree.h"
+// # include "nsWindowsHelpers.h"
+// # include <d3d11.h>
+#endif
+
+#ifdef MOZ_WIDGET_ANDROID
+# include "GLLibraryEGL.h"
+# include "mozilla/webrender/RenderAndroidSurfaceTextureHost.h"
+#endif
+
+#ifdef MOZ_WIDGET_GTK
+# include "mozilla/WidgetUtilsGtk.h"
+#endif
+
+#ifdef MOZ_WAYLAND
+# include "GLLibraryEGL.h"
+#endif
+
+using namespace mozilla;
+
+static already_AddRefed<gl::GLContext> CreateGLContext(nsACString& aError);
+
+MOZ_DEFINE_MALLOC_SIZE_OF(WebRenderRendererMallocSizeOf)
+
+namespace mozilla::wr {
+
+LazyLogModule gRenderThreadLog("RenderThread");
+// Should be called only on RenderThread, since LazyLogModule is not thread safe
+#define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
+
+static StaticRefPtr<RenderThread> sRenderThread;
+static mozilla::BackgroundHangMonitor* sBackgroundHangMonitor;
+#ifdef DEBUG
+static bool sRenderThreadEverStarted = false;
+#endif
+
+RenderThread::RenderThread(RefPtr<nsIThread> aThread)
+ : mThread(std::move(aThread)),
+ mThreadPool(false),
+ mThreadPoolLP(true),
+ mSingletonGLIsForHardwareWebRender(true),
+ mWindowInfos("RenderThread.mWindowInfos"),
+ mRenderTextureMapLock("RenderThread.mRenderTextureMapLock"),
+ mHasShutdown(false),
+ mHandlingDeviceReset(false),
+ mHandlingWebRenderError(false) {}
+
+RenderThread::~RenderThread() { MOZ_ASSERT(mRenderTexturesDeferred.empty()); }
+
+// static
+RenderThread* RenderThread::Get() { return sRenderThread; }
+
+// static
+void RenderThread::Start(uint32_t aNamespace) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!sRenderThread);
+
+#ifdef DEBUG
+ // Check to ensure nobody will try to ever start us more than once during
+ // the process' lifetime (in particular after ShutDown).
+ MOZ_ASSERT(!sRenderThreadEverStarted);
+ sRenderThreadEverStarted = true;
+#endif
+
+ // When the CanvasRenderer thread is disabled, WebGL may be handled on this
+ // thread, requiring a bigger stack size. See: CanvasManagerParent::Init
+ //
+ // This is 4M, which is higher than the default 256K.
+ // Increased with bug 1753349 to accommodate the `chromium/5359` branch of
+ // ANGLE, which has large peak stack usage for some pathological shader
+ // compilations.
+ //
+ // Previously increased to 512K to accommodate Mesa in bug 1753340.
+ //
+ // Previously increased to 320K to avoid a stack overflow in the
+ // Intel Vulkan driver initialization in bug 1716120.
+ //
+ // Note: we only override it if it's limited already.
+ uint32_t stackSize = nsIThreadManager::DEFAULT_STACK_SIZE;
+ if (stackSize && !gfx::gfxVars::SupportsThreadsafeGL()) {
+ stackSize = std::max(stackSize, 4096U << 10);
+ }
+
+ RefPtr<nsIThread> thread;
+ nsresult rv = NS_NewNamedThread(
+ "Renderer", getter_AddRefs(thread),
+ NS_NewRunnableFunction(
+ "Renderer::BackgroundHanSetup",
+ []() {
+ sBackgroundHangMonitor = new mozilla::BackgroundHangMonitor(
+ "Render",
+ /* Timeout values are powers-of-two to enable us get better
+ data. 128ms is chosen for transient hangs because 8Hz should
+ be the minimally acceptable goal for Render
+ responsiveness (normal goal is 60Hz). */
+ 128,
+ /* 2048ms is chosen for permanent hangs because it's longer than
+ * most Render hangs seen in the wild, but is short enough
+ * to not miss getting native hang stacks. */
+ 2048);
+ nsCOMPtr<nsIThread> thread = NS_GetCurrentThread();
+ nsThread* nsthread = static_cast<nsThread*>(thread.get());
+ nsthread->SetUseHangMonitor(true);
+ nsthread->SetPriority(nsISupportsPriority::PRIORITY_HIGH);
+ }),
+ {.stackSize = stackSize});
+
+ if (NS_FAILED(rv)) {
+ gfxCriticalNote << "Failed to create Renderer thread: "
+ << gfx::hexa((uint32_t)rv);
+ return;
+ }
+
+ sRenderThread = new RenderThread(thread);
+#ifdef XP_WIN
+ widget::WinCompositorWindowThread::Start();
+#endif
+ layers::SharedSurfacesParent::Initialize();
+
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<RenderThread>(sRenderThread.get()), &RenderThread::InitDeviceTask);
+ sRenderThread->PostRunnable(runnable.forget());
+}
+
+// static
+void RenderThread::ShutDown() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(sRenderThread);
+
+ {
+ MutexAutoLock lock(sRenderThread->mRenderTextureMapLock);
+ sRenderThread->mHasShutdown = true;
+ }
+
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<RenderThread>(sRenderThread.get()), &RenderThread::ShutDownTask);
+ sRenderThread->PostRunnable(runnable.forget());
+
+ // This will empty the thread queue and thus run the above runnable while
+ // spinning the MT event loop.
+ nsCOMPtr<nsIThread> oldThread = sRenderThread->GetRenderThread();
+ oldThread->Shutdown();
+
+ layers::SharedSurfacesParent::Shutdown();
+
+#ifdef XP_WIN
+ if (widget::WinCompositorWindowThread::Get()) {
+ widget::WinCompositorWindowThread::ShutDown();
+ }
+#endif
+
+ // We null this out only after we finished shutdown to give everbody the
+ // chance to check for sRenderThread->mHasShutdown. Hopefully everybody
+ // checks this before using us!
+ sRenderThread = nullptr;
+}
+
+extern void ClearAllBlobImageResources();
+
+void RenderThread::ShutDownTask() {
+ MOZ_ASSERT(IsInRenderThread());
+ LOG("RenderThread::ShutDownTask()");
+
+ {
+ // Clear RenderTextureHosts
+ MutexAutoLock lock(mRenderTextureMapLock);
+ mRenderTexturesDeferred.clear();
+ mRenderTextures.clear();
+ mSyncObjectNeededRenderTextures.clear();
+ mRenderTextureOps.clear();
+ }
+
+ // Let go of our handle to the (internally ref-counted) thread pool.
+ mThreadPool.Release();
+ mThreadPoolLP.Release();
+
+ // Releasing on the render thread will allow us to avoid dispatching to remove
+ // remaining textures from the texture map.
+ layers::SharedSurfacesParent::ShutdownRenderThread();
+
+#ifdef XP_WIN
+ DCLayerTree::Shutdown();
+#endif
+
+ ClearAllBlobImageResources();
+ ClearSingletonGL();
+ ClearSharedSurfacePool();
+}
+
+// static
+bool RenderThread::IsInRenderThread() {
+ return sRenderThread && sRenderThread->mThread == NS_GetCurrentThread();
+}
+
+// static
+already_AddRefed<nsIThread> RenderThread::GetRenderThread() {
+ nsCOMPtr<nsIThread> thread;
+ if (sRenderThread) {
+ thread = sRenderThread->mThread;
+ }
+ return thread.forget();
+}
+
+void RenderThread::DoAccumulateMemoryReport(
+ MemoryReport aReport,
+ const RefPtr<MemoryReportPromise::Private>& aPromise) {
+ MOZ_ASSERT(IsInRenderThread());
+
+ for (auto& r : mRenderers) {
+ r.second->AccumulateMemoryReport(&aReport);
+ }
+
+ // Note memory used by the shader cache, which is shared across all WR
+ // instances.
+ MOZ_ASSERT(aReport.shader_cache == 0);
+ if (mProgramCache) {
+ aReport.shader_cache = wr_program_cache_report_memory(
+ mProgramCache->Raw(), &WebRenderRendererMallocSizeOf);
+ }
+
+ size_t renderTextureMemory = 0;
+ {
+ MutexAutoLock lock(mRenderTextureMapLock);
+ for (const auto& entry : mRenderTextures) {
+ renderTextureMemory += entry.second->Bytes();
+ }
+ }
+ aReport.render_texture_hosts = renderTextureMemory;
+
+ aPromise->Resolve(aReport, __func__);
+}
+
+// static
+RefPtr<MemoryReportPromise> RenderThread::AccumulateMemoryReport(
+ MemoryReport aInitial) {
+ RefPtr<MemoryReportPromise::Private> p =
+ new MemoryReportPromise::Private(__func__);
+ MOZ_ASSERT(!IsInRenderThread());
+ if (!Get()) {
+ // This happens when the GPU process fails to start and we fall back to the
+ // basic compositor in the parent process. We could assert against this if
+ // we made the webrender detection code in gfxPlatform.cpp smarter. See bug
+ // 1494430 comment 12.
+ NS_WARNING("No render thread, returning empty memory report");
+ p->Resolve(aInitial, __func__);
+ return p;
+ }
+
+ Get()->PostRunnable(
+ NewRunnableMethod<MemoryReport, RefPtr<MemoryReportPromise::Private>>(
+ "wr::RenderThread::DoAccumulateMemoryReport", Get(),
+ &RenderThread::DoAccumulateMemoryReport, aInitial, p));
+
+ return p;
+}
+
+void RenderThread::AddRenderer(wr::WindowId aWindowId,
+ UniquePtr<RendererOGL> aRenderer) {
+ MOZ_ASSERT(IsInRenderThread());
+ LOG("RenderThread::AddRenderer() aWindowId %" PRIx64 "", AsUint64(aWindowId));
+
+ if (mHasShutdown) {
+ return;
+ }
+
+ mRenderers[aWindowId] = std::move(aRenderer);
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::GraphicsNumRenderers,
+ (unsigned int)mRenderers.size());
+
+ auto windows = mWindowInfos.Lock();
+ windows->emplace(AsUint64(aWindowId), new WindowInfo());
+ mWrNotifierEventsQueues.emplace(AsUint64(aWindowId),
+ new std::queue<WrNotifierEvent>);
+}
+
+void RenderThread::RemoveRenderer(wr::WindowId aWindowId) {
+ MOZ_ASSERT(IsInRenderThread());
+ LOG("RenderThread::RemoveRenderer() aWindowId %" PRIx64 "",
+ AsUint64(aWindowId));
+
+ if (mHasShutdown) {
+ return;
+ }
+
+ mRenderers.erase(aWindowId);
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::GraphicsNumRenderers,
+ (unsigned int)mRenderers.size());
+
+ if (mRenderers.empty()) {
+ if (mHandlingDeviceReset) {
+ ClearSingletonGL();
+ }
+ mHandlingDeviceReset = false;
+ mHandlingWebRenderError = false;
+ }
+
+ auto windows = mWindowInfos.Lock();
+ auto it = windows->find(AsUint64(aWindowId));
+ MOZ_ASSERT(it != windows->end());
+ windows->erase(it);
+
+ // Defer std::deque<WrNotifierEvent> remove, RemoveRenderer() is called in
+ // HandleWrNotifierEvents().
+ RefPtr<Runnable> runnable =
+ NS_NewRunnableFunction("RenderThread::RemoveRenderer", [aWindowId]() {
+ auto* self = RenderThread::Get();
+ auto it = self->mWrNotifierEventsQueues.find(AsUint64(aWindowId));
+ if (it == self->mWrNotifierEventsQueues.end()) {
+ return;
+ }
+ self->mWrNotifierEventsQueues.erase(it);
+ });
+ RenderThread::Get()->PostRunnable(runnable.forget());
+}
+
+RendererOGL* RenderThread::GetRenderer(wr::WindowId aWindowId) {
+ MOZ_ASSERT(IsInRenderThread());
+
+ auto it = mRenderers.find(aWindowId);
+ MOZ_ASSERT(it != mRenderers.end());
+
+ if (it == mRenderers.end()) {
+ return nullptr;
+ }
+
+ return it->second.get();
+}
+
+size_t RenderThread::RendererCount() const {
+ MOZ_ASSERT(IsInRenderThread());
+ return mRenderers.size();
+}
+
+size_t RenderThread::ActiveRendererCount() const {
+ MOZ_ASSERT(IsInRenderThread());
+ size_t num_active = 0;
+ for (const auto& it : mRenderers) {
+ if (!it.second->IsPaused()) {
+ num_active++;
+ }
+ }
+ return num_active;
+}
+
+void RenderThread::WrNotifierEvent_WakeUp(WrWindowId aWindowId,
+ bool aCompositeNeeded) {
+ auto windows = mWindowInfos.Lock();
+ auto it = windows->find(AsUint64(aWindowId));
+ if (it == windows->end()) {
+ MOZ_ASSERT(false);
+ return;
+ }
+
+ WindowInfo* info = it->second.get();
+
+ info->mPendingWrNotifierEvents.emplace(
+ WrNotifierEvent::WakeUp(aCompositeNeeded));
+ PostWrNotifierEvents(aWindowId, info);
+}
+
+void RenderThread::WrNotifierEvent_NewFrameReady(WrWindowId aWindowId,
+ bool aCompositeNeeded,
+ FramePublishId aPublishId) {
+ auto windows = mWindowInfos.Lock();
+ auto it = windows->find(AsUint64(aWindowId));
+ if (it == windows->end()) {
+ MOZ_ASSERT(false);
+ return;
+ }
+ WindowInfo* info = it->second.get();
+
+ info->mPendingWrNotifierEvents.emplace(
+ WrNotifierEvent::NewFrameReady(aCompositeNeeded, aPublishId));
+ PostWrNotifierEvents(aWindowId, info);
+}
+
+void RenderThread::WrNotifierEvent_ExternalEvent(WrWindowId aWindowId,
+ size_t aRawEvent) {
+ UniquePtr<RendererEvent> evt(reinterpret_cast<RendererEvent*>(aRawEvent));
+ {
+ auto windows = mWindowInfos.Lock();
+ auto it = windows->find(AsUint64(aWindowId));
+ if (it == windows->end()) {
+ MOZ_ASSERT(false);
+ return;
+ }
+ WindowInfo* info = it->second.get();
+
+ info->mPendingWrNotifierEvents.emplace(
+ WrNotifierEvent::ExternalEvent(std::move(evt)));
+ PostWrNotifierEvents(aWindowId, info);
+ }
+}
+
+void RenderThread::PostWrNotifierEvents(WrWindowId aWindowId) {
+ {
+ auto windows = mWindowInfos.Lock();
+ auto it = windows->find(AsUint64(aWindowId));
+ if (it == windows->end()) {
+ MOZ_ASSERT(false);
+ return;
+ }
+ WindowInfo* info = it->second.get();
+ PostWrNotifierEvents(aWindowId, info);
+ }
+}
+
+void RenderThread::PostWrNotifierEvents(WrWindowId aWindowId,
+ WindowInfo* aInfo) {
+ // Runnable has already been triggered.
+ if (aInfo->mWrNotifierEventsRunnable) {
+ return;
+ }
+
+ // Runnable has not been triggered yet.
+ RefPtr<nsIRunnable> runnable = NewRunnableMethod<WrWindowId>(
+ "RenderThread::HandleWrNotifierEvents", this,
+ &RenderThread::HandleWrNotifierEvents, aWindowId);
+ aInfo->mWrNotifierEventsRunnable = runnable;
+ PostRunnable(runnable.forget());
+}
+
+void RenderThread::HandleWrNotifierEvents(WrWindowId aWindowId) {
+ MOZ_ASSERT(IsInRenderThread());
+
+ auto eventsIt = mWrNotifierEventsQueues.find(AsUint64(aWindowId));
+ if (eventsIt == mWrNotifierEventsQueues.end()) {
+ return;
+ }
+ auto* events = eventsIt->second.get();
+
+ {
+ auto windows = mWindowInfos.Lock();
+ auto infoIt = windows->find(AsUint64(aWindowId));
+ if (infoIt == windows->end()) {
+ MOZ_ASSERT(false);
+ return;
+ }
+ WindowInfo* info = infoIt->second.get();
+ info->mWrNotifierEventsRunnable = nullptr;
+
+ if (events->empty() && !info->mPendingWrNotifierEvents.empty()) {
+ events->swap(info->mPendingWrNotifierEvents);
+ }
+ }
+
+ bool handleNext = true;
+
+ while (!events->empty() && handleNext) {
+ auto& front = events->front();
+ switch (front.mTag) {
+ case WrNotifierEvent::Tag::WakeUp:
+ WrNotifierEvent_HandleWakeUp(aWindowId, front.CompositeNeeded());
+ handleNext = false;
+ break;
+ case WrNotifierEvent::Tag::NewFrameReady:
+ WrNotifierEvent_HandleNewFrameReady(aWindowId, front.CompositeNeeded(),
+ front.PublishId());
+ handleNext = false;
+ break;
+ case WrNotifierEvent::Tag::ExternalEvent:
+ WrNotifierEvent_HandleExternalEvent(aWindowId, front.ExternalEvent());
+ break;
+ }
+ events->pop();
+ }
+
+ {
+ auto windows = mWindowInfos.Lock();
+ auto it = windows->find(AsUint64(aWindowId));
+ if (it == windows->end()) {
+ return;
+ }
+ WindowInfo* info = it->second.get();
+
+ if (!events->empty() || !info->mPendingWrNotifierEvents.empty()) {
+ PostWrNotifierEvents(aWindowId, info);
+ }
+ }
+}
+
+void RenderThread::WrNotifierEvent_HandleWakeUp(wr::WindowId aWindowId,
+ bool aCompositeNeeded) {
+ MOZ_ASSERT(IsInRenderThread());
+
+ bool isTrackedFrame = false;
+ HandleFrameOneDoc(aWindowId, aCompositeNeeded, isTrackedFrame, Nothing());
+}
+
+void RenderThread::WrNotifierEvent_HandleNewFrameReady(
+ wr::WindowId aWindowId, bool aCompositeNeeded, FramePublishId aPublishId) {
+ MOZ_ASSERT(IsInRenderThread());
+
+ bool isTrackedFrame = true;
+ HandleFrameOneDoc(aWindowId, aCompositeNeeded, isTrackedFrame,
+ Some(aPublishId));
+}
+
+void RenderThread::WrNotifierEvent_HandleExternalEvent(
+ wr::WindowId aWindowId, UniquePtr<RendererEvent> aRendererEvent) {
+ MOZ_ASSERT(IsInRenderThread());
+
+ RunEvent(aWindowId, std::move(aRendererEvent));
+}
+
+void RenderThread::BeginRecordingForWindow(wr::WindowId aWindowId,
+ const TimeStamp& aRecordingStart,
+ wr::PipelineId aRootPipelineId) {
+ MOZ_ASSERT(IsInRenderThread());
+ RendererOGL* renderer = GetRenderer(aWindowId);
+ MOZ_ASSERT(renderer);
+
+ renderer->BeginRecording(aRecordingStart, aRootPipelineId);
+}
+
+Maybe<layers::FrameRecording> RenderThread::EndRecordingForWindow(
+ wr::WindowId aWindowId) {
+ MOZ_ASSERT(IsInRenderThread());
+
+ RendererOGL* renderer = GetRenderer(aWindowId);
+ MOZ_ASSERT(renderer);
+ return renderer->EndRecording();
+}
+
+void RenderThread::HandleFrameOneDoc(wr::WindowId aWindowId, bool aRender,
+ bool aTrackedFrame,
+ Maybe<FramePublishId> aPublishId) {
+ MOZ_ASSERT(IsInRenderThread());
+
+ if (mHasShutdown) {
+ return;
+ }
+
+ HandleFrameOneDocInner(aWindowId, aRender, aTrackedFrame, aPublishId);
+
+ if (aTrackedFrame) {
+ DecPendingFrameCount(aWindowId);
+ }
+}
+
+void RenderThread::HandleFrameOneDocInner(wr::WindowId aWindowId, bool aRender,
+ bool aTrackedFrame,
+ Maybe<FramePublishId> aPublishId) {
+ if (IsDestroyed(aWindowId)) {
+ return;
+ }
+
+ if (mHandlingDeviceReset) {
+ return;
+ }
+
+ bool render = aRender;
+ PendingFrameInfo frame;
+ if (aTrackedFrame) {
+ // scope lock
+ auto windows = mWindowInfos.Lock();
+ auto it = windows->find(AsUint64(aWindowId));
+ if (it == windows->end()) {
+ MOZ_ASSERT(false);
+ return;
+ }
+
+ WindowInfo* info = it->second.get();
+ PendingFrameInfo& frameInfo = info->mPendingFrames.front();
+
+ frame = frameInfo;
+ } else {
+ // Just give the frame info default values.
+ frame = {TimeStamp::Now(), VsyncId()};
+ }
+
+ // Sadly this doesn't include the lock, since we don't have the frame there
+ // yet.
+ glean::wr::time_to_render_start.AccumulateRawDuration(TimeStamp::Now() -
+ frame.mStartTime);
+
+ // It is for ensuring that PrepareForUse() is called before
+ // RenderTextureHost::Lock().
+ HandleRenderTextureOps();
+
+ if (aPublishId.isSome()) {
+ SetFramePublishId(aWindowId, aPublishId.ref());
+ }
+
+ UpdateAndRender(aWindowId, frame.mStartId, frame.mStartTime, render,
+ /* aReadbackSize */ Nothing(),
+ /* aReadbackFormat */ Nothing(),
+ /* aReadbackBuffer */ Nothing());
+
+ // The start time is from WebRenderBridgeParent::CompositeToTarget. From that
+ // point until now (when the frame is finally pushed to the screen) is
+ // equivalent to the COMPOSITE_TIME metric in the non-WR codepath.
+ TimeDuration compositeDuration = TimeStamp::Now() - frame.mStartTime;
+ mozilla::Telemetry::Accumulate(mozilla::Telemetry::COMPOSITE_TIME,
+ uint32_t(compositeDuration.ToMilliseconds()));
+ PerfStats::RecordMeasurement(PerfStats::Metric::Compositing,
+ compositeDuration);
+}
+
+void RenderThread::SetClearColor(wr::WindowId aWindowId, wr::ColorF aColor) {
+ if (mHasShutdown) {
+ return;
+ }
+
+ if (!IsInRenderThread()) {
+ PostRunnable(NewRunnableMethod<wr::WindowId, wr::ColorF>(
+ "wr::RenderThread::SetClearColor", this, &RenderThread::SetClearColor,
+ aWindowId, aColor));
+ return;
+ }
+
+ if (IsDestroyed(aWindowId)) {
+ return;
+ }
+
+ auto it = mRenderers.find(aWindowId);
+ MOZ_ASSERT(it != mRenderers.end());
+ if (it != mRenderers.end()) {
+ wr_renderer_set_clear_color(it->second->GetRenderer(), aColor);
+ }
+}
+
+void RenderThread::SetProfilerUI(wr::WindowId aWindowId,
+ const nsACString& aUI) {
+ if (mHasShutdown) {
+ return;
+ }
+
+ if (!IsInRenderThread()) {
+ PostRunnable(NewRunnableMethod<wr::WindowId, nsCString>(
+ "wr::RenderThread::SetProfilerUI", this, &RenderThread::SetProfilerUI,
+ aWindowId, nsCString(aUI)));
+ return;
+ }
+
+ auto it = mRenderers.find(aWindowId);
+ if (it != mRenderers.end()) {
+ it->second->SetProfilerUI(aUI);
+ }
+}
+
+void RenderThread::PostEvent(wr::WindowId aWindowId,
+ UniquePtr<RendererEvent> aEvent) {
+ PostRunnable(NewRunnableMethod<wr::WindowId, UniquePtr<RendererEvent>&&>(
+ "wr::RenderThread::PostEvent", this, &RenderThread::RunEvent, aWindowId,
+ std::move(aEvent)));
+}
+
+void RenderThread::RunEvent(wr::WindowId aWindowId,
+ UniquePtr<RendererEvent> aEvent) {
+ MOZ_ASSERT(IsInRenderThread());
+
+ aEvent->Run(*this, aWindowId);
+ aEvent = nullptr;
+}
+
+static void NotifyDidRender(layers::CompositorBridgeParent* aBridge,
+ const RefPtr<const WebRenderPipelineInfo>& aInfo,
+ VsyncId aCompositeStartId,
+ TimeStamp aCompositeStart, TimeStamp aRenderStart,
+ TimeStamp aEnd, bool aRender,
+ RendererStats aStats) {
+ if (aRender && aBridge->GetWrBridge()) {
+ // We call this here to mimic the behavior in LayerManagerComposite, as to
+ // not change what Talos measures. That is, we do not record an empty frame
+ // as a frame.
+ aBridge->GetWrBridge()->RecordFrame();
+ }
+
+ aBridge->NotifyDidRender(aCompositeStartId, aCompositeStart, aRenderStart,
+ aEnd, &aStats);
+
+ for (const auto& epoch : aInfo->Raw().epochs) {
+ aBridge->NotifyPipelineRendered(epoch.pipeline_id, epoch.epoch,
+ aCompositeStartId, aCompositeStart,
+ aRenderStart, aEnd, &aStats);
+ }
+
+ if (aBridge->GetWrBridge()) {
+ aBridge->GetWrBridge()->RetrySkippedComposite();
+ }
+}
+
+static void NotifyDidStartRender(layers::CompositorBridgeParent* aBridge) {
+ if (aBridge->GetWrBridge()) {
+ aBridge->GetWrBridge()->RetrySkippedComposite();
+ }
+}
+
+void RenderThread::SetFramePublishId(wr::WindowId aWindowId,
+ FramePublishId aPublishId) {
+ MOZ_ASSERT(IsInRenderThread());
+
+ auto it = mRenderers.find(aWindowId);
+ MOZ_ASSERT(it != mRenderers.end());
+ if (it == mRenderers.end()) {
+ return;
+ }
+ auto& renderer = it->second;
+
+ renderer->SetFramePublishId(aPublishId);
+}
+
+void RenderThread::UpdateAndRender(
+ wr::WindowId aWindowId, const VsyncId& aStartId,
+ const TimeStamp& aStartTime, bool aRender,
+ const Maybe<gfx::IntSize>& aReadbackSize,
+ const Maybe<wr::ImageFormat>& aReadbackFormat,
+ const Maybe<Range<uint8_t>>& aReadbackBuffer, bool* aNeedsYFlip) {
+ AUTO_PROFILER_LABEL("RenderThread::UpdateAndRender", GRAPHICS);
+ MOZ_ASSERT(IsInRenderThread());
+ MOZ_ASSERT(aRender || aReadbackBuffer.isNothing());
+
+ auto it = mRenderers.find(aWindowId);
+ MOZ_ASSERT(it != mRenderers.end());
+ if (it == mRenderers.end()) {
+ return;
+ }
+
+ TimeStamp start = TimeStamp::Now();
+
+ auto& renderer = it->second;
+
+ std::string markerName = "Composite #" + std::to_string(AsUint64(aWindowId));
+ AutoProfilerTracing tracingCompositeMarker(
+ "Paint", markerName.c_str(), geckoprofiler::category::GRAPHICS,
+ Some(renderer->GetCompositorBridge()->GetInnerWindowId()));
+
+ if (renderer->IsPaused()) {
+ aRender = false;
+ }
+ LOG("RenderThread::UpdateAndRender() aWindowId %" PRIx64 " aRender %d",
+ AsUint64(aWindowId), aRender);
+
+ layers::CompositorThread()->Dispatch(
+ NewRunnableFunction("NotifyDidStartRenderRunnable", &NotifyDidStartRender,
+ renderer->GetCompositorBridge()));
+
+ wr::RenderedFrameId latestFrameId;
+ RendererStats stats = {0};
+ if (aRender) {
+ latestFrameId = renderer->UpdateAndRender(
+ aReadbackSize, aReadbackFormat, aReadbackBuffer, aNeedsYFlip, &stats);
+ } else {
+ renderer->Update();
+ }
+ // Check graphics reset status even when rendering is skipped.
+ renderer->CheckGraphicsResetStatus("PostUpdate", /* aForce */ false);
+
+ TimeStamp end = TimeStamp::Now();
+ RefPtr<const WebRenderPipelineInfo> info = renderer->GetLastPipelineInfo();
+
+ layers::CompositorThread()->Dispatch(
+ NewRunnableFunction("NotifyDidRenderRunnable", &NotifyDidRender,
+ renderer->GetCompositorBridge(), info, aStartId,
+ aStartTime, start, end, aRender, stats));
+
+ ipc::FileDescriptor fenceFd;
+
+ if (latestFrameId.IsValid()) {
+ fenceFd = renderer->GetAndResetReleaseFence();
+
+ // Wait for GPU after posting NotifyDidRender, since the wait is not
+ // necessary for the NotifyDidRender.
+ // The wait is necessary for Textures recycling of AsyncImagePipelineManager
+ // and for avoiding GPU queue is filled with too much tasks.
+ // WaitForGPU's implementation is different for each platform.
+ auto timerId = glean::wr::gpu_wait_time.Start();
+ renderer->WaitForGPU();
+ glean::wr::gpu_wait_time.StopAndAccumulate(std::move(timerId));
+ } else {
+ // Update frame id for NotifyPipelinesUpdated() when rendering does not
+ // happen, either because rendering was not requested or the frame was
+ // canceled. Rendering can sometimes be canceled if UpdateAndRender is
+ // called when the window is not yet ready (not mapped or 0 size).
+ latestFrameId = renderer->UpdateFrameId();
+ }
+
+ RenderedFrameId lastCompletedFrameId = renderer->GetLastCompletedFrameId();
+
+ RefPtr<layers::AsyncImagePipelineManager> pipelineMgr =
+ renderer->GetCompositorBridge()->GetAsyncImagePipelineManager();
+ // pipelineMgr should always be non-null here because it is only nulled out
+ // after the WebRenderAPI instance for the CompositorBridgeParent is
+ // destroyed, and that destruction blocks until the renderer thread has
+ // removed the relevant renderer. And after that happens we should never reach
+ // this code at all; it would bail out at the mRenderers.find check above.
+ MOZ_ASSERT(pipelineMgr);
+ pipelineMgr->NotifyPipelinesUpdated(info, latestFrameId, lastCompletedFrameId,
+ std::move(fenceFd));
+}
+
+void RenderThread::Pause(wr::WindowId aWindowId) {
+ MOZ_ASSERT(IsInRenderThread());
+ LOG("RenderThread::Pause() aWindowId %" PRIx64 "", AsUint64(aWindowId));
+
+ auto it = mRenderers.find(aWindowId);
+ MOZ_ASSERT(it != mRenderers.end());
+ if (it == mRenderers.end()) {
+ gfxCriticalNote << "RenderThread cannot find renderer for window "
+ << gfx::hexa(aWindowId) << " to pause.";
+ return;
+ }
+ auto& renderer = it->second;
+ renderer->Pause();
+
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::GraphicsNumActiveRenderers,
+ (unsigned int)ActiveRendererCount());
+}
+
+bool RenderThread::Resume(wr::WindowId aWindowId) {
+ MOZ_ASSERT(IsInRenderThread());
+ LOG("enderThread::Resume() aWindowId %" PRIx64 "", AsUint64(aWindowId));
+
+ auto it = mRenderers.find(aWindowId);
+ MOZ_ASSERT(it != mRenderers.end());
+ if (it == mRenderers.end()) {
+ gfxCriticalNote << "RenderThread cannot find renderer for window "
+ << gfx::hexa(aWindowId) << " to resume.";
+ return false;
+ }
+ auto& renderer = it->second;
+ bool resumed = renderer->Resume();
+
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::GraphicsNumActiveRenderers,
+ (unsigned int)ActiveRendererCount());
+
+ return resumed;
+}
+
+bool RenderThread::TooManyPendingFrames(wr::WindowId aWindowId) {
+ const int64_t maxFrameCount = 1;
+
+ // Too many pending frames if pending frames exit more than maxFrameCount
+ // or if RenderBackend is still processing a frame.
+
+ auto windows = mWindowInfos.Lock();
+ auto it = windows->find(AsUint64(aWindowId));
+ if (it == windows->end()) {
+ MOZ_ASSERT(false);
+ return true;
+ }
+ WindowInfo* info = it->second.get();
+
+ if (info->PendingCount() > maxFrameCount) {
+ return true;
+ }
+ // If there is no ongoing frame build, we accept a new frame.
+ return info->mPendingFrameBuild > 0;
+}
+
+bool RenderThread::IsDestroyed(wr::WindowId aWindowId) {
+ auto windows = mWindowInfos.Lock();
+ auto it = windows->find(AsUint64(aWindowId));
+ if (it == windows->end()) {
+ return true;
+ }
+
+ return it->second->mIsDestroyed;
+}
+
+void RenderThread::SetDestroyed(wr::WindowId aWindowId) {
+ auto windows = mWindowInfos.Lock();
+ auto it = windows->find(AsUint64(aWindowId));
+ if (it == windows->end()) {
+ MOZ_ASSERT(false);
+ return;
+ }
+ it->second->mIsDestroyed = true;
+}
+
+void RenderThread::IncPendingFrameCount(wr::WindowId aWindowId,
+ const VsyncId& aStartId,
+ const TimeStamp& aStartTime) {
+ auto windows = mWindowInfos.Lock();
+ auto it = windows->find(AsUint64(aWindowId));
+ if (it == windows->end()) {
+ MOZ_ASSERT(false);
+ return;
+ }
+ it->second->mPendingFrameBuild++;
+ it->second->mPendingFrames.push(PendingFrameInfo{aStartTime, aStartId});
+}
+
+void RenderThread::DecPendingFrameBuildCount(wr::WindowId aWindowId) {
+ auto windows = mWindowInfos.Lock();
+ auto it = windows->find(AsUint64(aWindowId));
+ if (it == windows->end()) {
+ MOZ_ASSERT(false);
+ return;
+ }
+ WindowInfo* info = it->second.get();
+ MOZ_RELEASE_ASSERT(info->mPendingFrameBuild >= 1);
+ info->mPendingFrameBuild--;
+}
+
+void RenderThread::DecPendingFrameCount(wr::WindowId aWindowId) {
+ auto windows = mWindowInfos.Lock();
+ auto it = windows->find(AsUint64(aWindowId));
+ if (it == windows->end()) {
+ MOZ_ASSERT(false);
+ return;
+ }
+ WindowInfo* info = it->second.get();
+ info->mPendingFrames.pop();
+}
+
+void RenderThread::RegisterExternalImage(
+ const wr::ExternalImageId& aExternalImageId,
+ already_AddRefed<RenderTextureHost> aTexture) {
+ MutexAutoLock lock(mRenderTextureMapLock);
+
+ if (mHasShutdown) {
+ return;
+ }
+ MOZ_ASSERT(mRenderTextures.find(aExternalImageId) == mRenderTextures.end());
+ RefPtr<RenderTextureHost> texture = aTexture;
+ if (texture->SyncObjectNeeded()) {
+ mSyncObjectNeededRenderTextures.emplace(aExternalImageId, texture);
+ }
+ mRenderTextures.emplace(aExternalImageId, texture);
+}
+
+void RenderThread::UnregisterExternalImage(
+ const wr::ExternalImageId& aExternalImageId) {
+ MutexAutoLock lock(mRenderTextureMapLock);
+ if (mHasShutdown) {
+ return;
+ }
+ auto it = mRenderTextures.find(aExternalImageId);
+ if (it == mRenderTextures.end()) {
+ return;
+ }
+
+ auto& texture = it->second;
+ if (texture->SyncObjectNeeded()) {
+ MOZ_RELEASE_ASSERT(
+ mSyncObjectNeededRenderTextures.erase(aExternalImageId) == 1);
+ }
+
+ if (!IsInRenderThread()) {
+ // The RenderTextureHost should be released in render thread. So, post the
+ // deletion task here.
+ // The shmem and raw buffer are owned by compositor ipc channel. It's
+ // possible that RenderTextureHost is still exist after the shmem/raw buffer
+ // deletion. Then the buffer in RenderTextureHost becomes invalid. It's fine
+ // for this situation. Gecko will only release the buffer if WR doesn't need
+ // it. So, no one will access the invalid buffer in RenderTextureHost.
+ RefPtr<RenderTextureHost> texture = it->second;
+ mRenderTextures.erase(it);
+ mRenderTexturesDeferred.emplace_back(std::move(texture));
+ PostRunnable(NewRunnableMethod(
+ "RenderThread::DeferredRenderTextureHostDestroy", this,
+ &RenderThread::DeferredRenderTextureHostDestroy));
+ } else {
+ mRenderTextures.erase(it);
+ }
+}
+
+void RenderThread::DestroyExternalImagesSyncWait(
+ const std::vector<wr::ExternalImageId>&& aIds) {
+ if (!IsInRenderThread()) {
+ layers::SynchronousTask task("Destroy external images");
+
+ RefPtr<Runnable> runnable = NS_NewRunnableFunction(
+ "RenderThread::DestroyExternalImagesSyncWait::Runnable",
+ [&task, ids = std::move(aIds)]() {
+ layers::AutoCompleteTask complete(&task);
+ RenderThread::Get()->DestroyExternalImages(std::move(ids));
+ });
+
+ PostRunnable(runnable.forget());
+ task.Wait();
+ return;
+ }
+ DestroyExternalImages(std::move(aIds));
+}
+
+void RenderThread::DestroyExternalImages(
+ const std::vector<wr::ExternalImageId>&& aIds) {
+ MOZ_ASSERT(IsInRenderThread());
+
+ std::vector<RefPtr<RenderTextureHost>> hosts;
+ {
+ MutexAutoLock lock(mRenderTextureMapLock);
+ if (mHasShutdown) {
+ return;
+ }
+
+ for (auto& id : aIds) {
+ auto it = mRenderTextures.find(id);
+ if (it == mRenderTextures.end()) {
+ continue;
+ }
+ hosts.emplace_back(it->second);
+ }
+ }
+
+ for (auto& host : hosts) {
+ host->Destroy();
+ }
+}
+
+void RenderThread::PrepareForUse(const wr::ExternalImageId& aExternalImageId) {
+ AddRenderTextureOp(RenderTextureOp::PrepareForUse, aExternalImageId);
+}
+
+void RenderThread::NotifyNotUsed(const wr::ExternalImageId& aExternalImageId) {
+ AddRenderTextureOp(RenderTextureOp::NotifyNotUsed, aExternalImageId);
+}
+
+void RenderThread::NotifyForUse(const wr::ExternalImageId& aExternalImageId) {
+ AddRenderTextureOp(RenderTextureOp::NotifyForUse, aExternalImageId);
+}
+
+void RenderThread::AddRenderTextureOp(
+ RenderTextureOp aOp, const wr::ExternalImageId& aExternalImageId) {
+ MOZ_ASSERT(!IsInRenderThread());
+
+ MutexAutoLock lock(mRenderTextureMapLock);
+
+ auto it = mRenderTextures.find(aExternalImageId);
+ MOZ_ASSERT(it != mRenderTextures.end());
+ if (it == mRenderTextures.end()) {
+ return;
+ }
+
+ RefPtr<RenderTextureHost> texture = it->second;
+ mRenderTextureOps.emplace_back(aOp, std::move(texture));
+
+ if (mRenderTextureOpsRunnable) {
+ // Runnable was already triggered
+ return;
+ }
+
+ RefPtr<nsIRunnable> runnable =
+ NewRunnableMethod("RenderThread::HandleRenderTextureOps", this,
+ &RenderThread::HandleRenderTextureOps);
+ mRenderTextureOpsRunnable = runnable;
+ PostRunnable(runnable.forget());
+}
+
+void RenderThread::HandleRenderTextureOps() {
+ MOZ_ASSERT(IsInRenderThread());
+
+ std::list<std::pair<RenderTextureOp, RefPtr<RenderTextureHost>>>
+ renderTextureOps;
+ {
+ MutexAutoLock lock(mRenderTextureMapLock);
+ mRenderTextureOps.swap(renderTextureOps);
+ mRenderTextureOpsRunnable = nullptr;
+ }
+
+ for (auto& it : renderTextureOps) {
+ switch (it.first) {
+ case RenderTextureOp::PrepareForUse:
+ it.second->PrepareForUse();
+ break;
+ case RenderTextureOp::NotifyForUse:
+ it.second->NotifyForUse();
+ break;
+ case RenderTextureOp::NotifyNotUsed:
+ it.second->NotifyNotUsed();
+ break;
+ }
+ }
+}
+
+void RenderThread::UnregisterExternalImageDuringShutdown(
+ const wr::ExternalImageId& aExternalImageId) {
+ MOZ_ASSERT(IsInRenderThread());
+ MutexAutoLock lock(mRenderTextureMapLock);
+ MOZ_ASSERT(mHasShutdown);
+ MOZ_ASSERT(mRenderTextures.find(aExternalImageId) != mRenderTextures.end());
+ mRenderTextures.erase(aExternalImageId);
+}
+
+bool RenderThread::SyncObjectNeeded() {
+ MOZ_ASSERT(IsInRenderThread());
+ MutexAutoLock lock(mRenderTextureMapLock);
+ return !mSyncObjectNeededRenderTextures.empty();
+}
+
+void RenderThread::DeferredRenderTextureHostDestroy() {
+ MutexAutoLock lock(mRenderTextureMapLock);
+ mRenderTexturesDeferred.clear();
+}
+
+RenderTextureHost* RenderThread::GetRenderTexture(
+ const wr::ExternalImageId& aExternalImageId) {
+ MutexAutoLock lock(mRenderTextureMapLock);
+ auto it = mRenderTextures.find(aExternalImageId);
+ MOZ_ASSERT(it != mRenderTextures.end());
+ if (it == mRenderTextures.end()) {
+ return nullptr;
+ }
+ return it->second;
+}
+
+void RenderThread::InitDeviceTask() {
+ MOZ_ASSERT(IsInRenderThread());
+ MOZ_ASSERT(!mSingletonGL);
+ LOG("RenderThread::InitDeviceTask()");
+
+ if (gfx::gfxVars::UseSoftwareWebRender()) {
+ // Ensure we don't instantiate any shared GL context when SW-WR is used.
+ return;
+ }
+
+ nsAutoCString err;
+ CreateSingletonGL(err);
+ if (gfx::gfxVars::UseWebRenderProgramBinaryDisk()) {
+ mProgramCache = MakeUnique<WebRenderProgramCache>(ThreadPool().Raw());
+ }
+ // Query the shared GL context to force the
+ // lazy initialization to happen now.
+ SingletonGL();
+}
+
+void RenderThread::PostRunnable(already_AddRefed<nsIRunnable> aRunnable) {
+ nsCOMPtr<nsIRunnable> runnable = aRunnable;
+ mThread->Dispatch(runnable.forget());
+}
+
+#ifndef XP_WIN
+static DeviceResetReason GLenumToResetReason(GLenum aReason) {
+ switch (aReason) {
+ case LOCAL_GL_NO_ERROR:
+ return DeviceResetReason::FORCED_RESET;
+ case LOCAL_GL_INNOCENT_CONTEXT_RESET_ARB:
+ return DeviceResetReason::DRIVER_ERROR;
+ case LOCAL_GL_PURGED_CONTEXT_RESET_NV:
+ return DeviceResetReason::NVIDIA_VIDEO;
+ case LOCAL_GL_GUILTY_CONTEXT_RESET_ARB:
+ return DeviceResetReason::RESET;
+ case LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB:
+ return DeviceResetReason::UNKNOWN;
+ case LOCAL_GL_OUT_OF_MEMORY:
+ return DeviceResetReason::OUT_OF_MEMORY;
+ default:
+ return DeviceResetReason::OTHER;
+ }
+}
+#endif
+
+void RenderThread::HandleDeviceReset(const char* aWhere, GLenum aReason) {
+ MOZ_ASSERT(IsInRenderThread());
+
+ // This happens only on simulate device reset.
+ if (aReason == LOCAL_GL_NO_ERROR) {
+ if (!mHandlingDeviceReset) {
+ mHandlingDeviceReset = true;
+
+ MutexAutoLock lock(mRenderTextureMapLock);
+ mRenderTexturesDeferred.clear();
+ for (const auto& entry : mRenderTextures) {
+ entry.second->ClearCachedResources();
+ }
+
+ // All RenderCompositors will be destroyed by the GPUProcessManager in
+ // either OnRemoteProcessDeviceReset via the GPUChild, or
+ // OnInProcessDeviceReset here directly.
+ if (XRE_IsGPUProcess()) {
+ gfx::GPUParent::GetSingleton()->NotifyDeviceReset();
+ } else {
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "gfx::GPUProcessManager::OnInProcessDeviceReset", []() -> void {
+ gfx::GPUProcessManager::Get()->OnInProcessDeviceReset(
+ /* aTrackThreshold */ false);
+ }));
+ }
+ }
+ return;
+ }
+
+ if (mHandlingDeviceReset) {
+ return;
+ }
+
+ mHandlingDeviceReset = true;
+
+#ifndef XP_WIN
+ // On Windows, see DeviceManagerDx::MaybeResetAndReacquireDevices.
+ gfx::GPUProcessManager::RecordDeviceReset(GLenumToResetReason(aReason));
+#endif
+
+ {
+ MutexAutoLock lock(mRenderTextureMapLock);
+ mRenderTexturesDeferred.clear();
+ for (const auto& entry : mRenderTextures) {
+ entry.second->ClearCachedResources();
+ }
+ }
+
+ // All RenderCompositors will be destroyed by the GPUProcessManager in
+ // either OnRemoteProcessDeviceReset via the GPUChild, or
+ // OnInProcessDeviceReset here directly.
+ // On Windows, device will be re-created before sessions re-creation.
+ gfxCriticalNote << "GFX: RenderThread detected a device reset in " << aWhere;
+ if (XRE_IsGPUProcess()) {
+ gfx::GPUParent::GetSingleton()->NotifyDeviceReset();
+ } else {
+#ifndef XP_WIN
+ // FIXME(aosmond): Do we need to do this on Windows? nsWindow::OnPaint
+ // seems to do its own detection for the parent process.
+ bool guilty = aReason == LOCAL_GL_GUILTY_CONTEXT_RESET_ARB;
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "gfx::GPUProcessManager::OnInProcessDeviceReset", [guilty]() -> void {
+ gfx::GPUProcessManager::Get()->OnInProcessDeviceReset(guilty);
+ }));
+#endif
+ }
+}
+
+bool RenderThread::IsHandlingDeviceReset() {
+ MOZ_ASSERT(IsInRenderThread());
+ return mHandlingDeviceReset;
+}
+
+void RenderThread::SimulateDeviceReset() {
+ if (!IsInRenderThread()) {
+ PostRunnable(NewRunnableMethod("RenderThread::SimulateDeviceReset", this,
+ &RenderThread::SimulateDeviceReset));
+ } else {
+ // When this function is called GPUProcessManager::SimulateDeviceReset()
+ // already triggers destroying all CompositorSessions before re-creating
+ // them.
+ HandleDeviceReset("SimulateDeviceReset", LOCAL_GL_NO_ERROR);
+ }
+}
+
+static void DoNotifyWebRenderError(WebRenderError aError) {
+ layers::CompositorManagerParent::NotifyWebRenderError(aError);
+}
+
+void RenderThread::NotifyWebRenderError(WebRenderError aError) {
+ MOZ_ASSERT(IsInRenderThread());
+
+ layers::CompositorThread()->Dispatch(NewRunnableFunction(
+ "DoNotifyWebRenderErrorRunnable", &DoNotifyWebRenderError, aError));
+}
+
+void RenderThread::HandleWebRenderError(WebRenderError aError) {
+ if (mHandlingWebRenderError) {
+ return;
+ }
+
+ NotifyWebRenderError(aError);
+
+ {
+ MutexAutoLock lock(mRenderTextureMapLock);
+ mRenderTexturesDeferred.clear();
+ for (const auto& entry : mRenderTextures) {
+ entry.second->ClearCachedResources();
+ }
+ }
+ mHandlingWebRenderError = true;
+ // WebRender is going to be disabled by
+ // GPUProcessManager::NotifyWebRenderError()
+}
+
+bool RenderThread::IsHandlingWebRenderError() {
+ MOZ_ASSERT(IsInRenderThread());
+ return mHandlingWebRenderError;
+}
+
+gl::GLContext* RenderThread::SingletonGL() {
+ nsAutoCString err;
+ auto* gl = SingletonGL(err);
+ if (!err.IsEmpty()) {
+ gfxCriticalNote << err.get();
+ }
+ return gl;
+}
+
+void RenderThread::CreateSingletonGL(nsACString& aError) {
+ MOZ_ASSERT(IsInRenderThread());
+ LOG("RenderThread::CreateSingletonGL()");
+
+ mSingletonGL = CreateGLContext(aError);
+ mSingletonGLIsForHardwareWebRender = !gfx::gfxVars::UseSoftwareWebRender();
+}
+
+gl::GLContext* RenderThread::SingletonGL(nsACString& aError) {
+ MOZ_ASSERT(IsInRenderThread());
+ if (!mSingletonGL) {
+ CreateSingletonGL(aError);
+ mShaders = nullptr;
+ }
+ if (mSingletonGL && mSingletonGLIsForHardwareWebRender && !mShaders) {
+ mShaders = MakeUnique<WebRenderShaders>(mSingletonGL, mProgramCache.get());
+ }
+
+ return mSingletonGL.get();
+}
+
+gl::GLContext* RenderThread::SingletonGLForCompositorOGL() {
+ MOZ_RELEASE_ASSERT(gfx::gfxVars::UseSoftwareWebRender());
+
+ if (mSingletonGLIsForHardwareWebRender) {
+ // Clear singleton GL, since GLContext is for hardware WebRender.
+ ClearSingletonGL();
+ }
+ return SingletonGL();
+}
+
+void RenderThread::ClearSingletonGL() {
+ MOZ_ASSERT(IsInRenderThread());
+ LOG("RenderThread::ClearSingletonGL()");
+
+ if (mSurfacePool) {
+ mSurfacePool->DestroyGLResourcesForContext(mSingletonGL);
+ }
+ if (mProgramsForCompositorOGL) {
+ mProgramsForCompositorOGL->Clear();
+ mProgramsForCompositorOGL = nullptr;
+ }
+ mShaders = nullptr;
+ mSingletonGL = nullptr;
+}
+
+RefPtr<layers::ShaderProgramOGLsHolder>
+RenderThread::GetProgramsForCompositorOGL() {
+ if (!mSingletonGL) {
+ return nullptr;
+ }
+
+ if (!mProgramsForCompositorOGL) {
+ mProgramsForCompositorOGL =
+ MakeAndAddRef<layers::ShaderProgramOGLsHolder>(mSingletonGL);
+ }
+ return mProgramsForCompositorOGL;
+}
+
+RefPtr<layers::SurfacePool> RenderThread::SharedSurfacePool() {
+#if defined(XP_MACOSX) || defined(MOZ_WAYLAND)
+ if (!mSurfacePool) {
+ size_t poolSizeLimit =
+ StaticPrefs::gfx_webrender_compositor_surface_pool_size_AtStartup();
+ mSurfacePool = layers::SurfacePool::Create(poolSizeLimit);
+ }
+#endif
+ return mSurfacePool;
+}
+
+void RenderThread::ClearSharedSurfacePool() { mSurfacePool = nullptr; }
+
+static void GLAPIENTRY DebugMessageCallback(GLenum aSource, GLenum aType,
+ GLuint aId, GLenum aSeverity,
+ GLsizei aLength,
+ const GLchar* aMessage,
+ const GLvoid* aUserParam) {
+ constexpr const char* kContextLost = "Context has been lost.";
+
+ if (StaticPrefs::gfx_webrender_gl_debug_message_critical_note_AtStartup() &&
+ aSeverity == LOCAL_GL_DEBUG_SEVERITY_HIGH) {
+ auto message = std::string(aMessage, aLength);
+ // When content lost happned, error messages are flooded by its message.
+ if (message != kContextLost) {
+ gfxCriticalNote << message;
+ } else {
+ gfxCriticalNoteOnce << message;
+ }
+ }
+
+ if (StaticPrefs::gfx_webrender_gl_debug_message_print_AtStartup()) {
+ gl::GLContext* gl = (gl::GLContext*)aUserParam;
+ gl->DebugCallback(aSource, aType, aId, aSeverity, aLength, aMessage);
+ }
+}
+
+// static
+void RenderThread::MaybeEnableGLDebugMessage(gl::GLContext* aGLContext) {
+ if (!aGLContext) {
+ return;
+ }
+
+ bool enableDebugMessage =
+ StaticPrefs::gfx_webrender_gl_debug_message_critical_note_AtStartup() ||
+ StaticPrefs::gfx_webrender_gl_debug_message_print_AtStartup();
+
+ if (enableDebugMessage &&
+ aGLContext->IsExtensionSupported(gl::GLContext::KHR_debug)) {
+ aGLContext->fEnable(LOCAL_GL_DEBUG_OUTPUT);
+ aGLContext->fDisable(LOCAL_GL_DEBUG_OUTPUT_SYNCHRONOUS);
+ aGLContext->fDebugMessageCallback(&DebugMessageCallback, (void*)aGLContext);
+ aGLContext->fDebugMessageControl(LOCAL_GL_DONT_CARE, LOCAL_GL_DONT_CARE,
+ LOCAL_GL_DONT_CARE, 0, nullptr, true);
+ }
+}
+
+WebRenderShaders::WebRenderShaders(gl::GLContext* gl,
+ WebRenderProgramCache* programCache) {
+ mGL = gl;
+ mShaders =
+ wr_shaders_new(gl, programCache ? programCache->Raw() : nullptr,
+ StaticPrefs::gfx_webrender_precache_shaders_AtStartup());
+}
+
+WebRenderShaders::~WebRenderShaders() {
+ wr_shaders_delete(mShaders, mGL.get());
+}
+
+WebRenderThreadPool::WebRenderThreadPool(bool low_priority) {
+ mThreadPool = wr_thread_pool_new(low_priority);
+}
+
+WebRenderThreadPool::~WebRenderThreadPool() { Release(); }
+
+void WebRenderThreadPool::Release() {
+ if (mThreadPool) {
+ wr_thread_pool_delete(mThreadPool);
+ mThreadPool = nullptr;
+ }
+}
+
+WebRenderProgramCache::WebRenderProgramCache(wr::WrThreadPool* aThreadPool) {
+ MOZ_ASSERT(aThreadPool);
+
+ nsAutoString path;
+ if (gfx::gfxVars::UseWebRenderProgramBinaryDisk()) {
+ path.Append(gfx::gfxVars::ProfDirectory());
+ }
+ mProgramCache = wr_program_cache_new(&path, aThreadPool);
+ if (gfx::gfxVars::UseWebRenderProgramBinaryDisk()) {
+ wr_try_load_startup_shaders_from_disk(mProgramCache);
+ }
+}
+
+WebRenderProgramCache::~WebRenderProgramCache() {
+ wr_program_cache_delete(mProgramCache);
+}
+
+} // namespace mozilla::wr
+
+#ifdef XP_WIN
+static already_AddRefed<gl::GLContext> CreateGLContextANGLE(
+ nsACString& aError) {
+ const RefPtr<ID3D11Device> d3d11Device =
+ gfx::DeviceManagerDx::Get()->GetCompositorDevice();
+ if (!d3d11Device) {
+ aError.Assign("RcANGLE(no compositor device for EGLDisplay)"_ns);
+ return nullptr;
+ }
+
+ nsCString failureId;
+ const auto lib = gl::GLLibraryEGL::Get(&failureId);
+ if (!lib) {
+ aError.Assign(
+ nsPrintfCString("RcANGLE(load EGL lib failed: %s)", failureId.get()));
+ return nullptr;
+ }
+
+ const auto egl = lib->CreateDisplay(d3d11Device.get());
+ if (!egl) {
+ aError.Assign(nsPrintfCString("RcANGLE(create EGLDisplay failed: %s)",
+ failureId.get()));
+ return nullptr;
+ }
+
+ gl::CreateContextFlags flags = gl::CreateContextFlags::PREFER_ES3;
+
+ if (StaticPrefs::gfx_webrender_prefer_robustness_AtStartup()) {
+ flags |= gl::CreateContextFlags::PREFER_ROBUSTNESS;
+ }
+
+ if (egl->IsExtensionSupported(
+ gl::EGLExtension::MOZ_create_context_provoking_vertex_dont_care)) {
+ flags |= gl::CreateContextFlags::PROVOKING_VERTEX_DONT_CARE;
+ }
+
+ // Create GLContext with dummy EGLSurface, the EGLSurface is not used.
+ // Instread we override it with EGLSurface of SwapChain's back buffer.
+
+ auto gl = gl::GLContextEGL::CreateWithoutSurface(egl, {flags}, &failureId);
+ if (!gl || !gl->IsANGLE()) {
+ aError.Assign(nsPrintfCString("RcANGLE(create GL context failed: %p, %s)",
+ gl.get(), failureId.get()));
+ return nullptr;
+ }
+
+ if (!gl->MakeCurrent()) {
+ aError.Assign(
+ nsPrintfCString("RcANGLE(make current GL context failed: %p, %x)",
+ gl.get(), gl->mEgl->mLib->fGetError()));
+ return nullptr;
+ }
+
+ return gl.forget();
+}
+#endif
+
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WAYLAND) || defined(MOZ_X11)
+static already_AddRefed<gl::GLContext> CreateGLContextEGL() {
+ // Create GLContext with dummy EGLSurface.
+ bool forHardwareWebRender = true;
+ // SW-WR uses CompositorOGL in native compositor.
+ if (gfx::gfxVars::UseSoftwareWebRender()) {
+ forHardwareWebRender = false;
+ }
+ RefPtr<gl::GLContext> gl =
+ gl::GLContextProviderEGL::CreateForCompositorWidget(
+ nullptr, forHardwareWebRender, /* aForceAccelerated */ true);
+ if (!gl || !gl->MakeCurrent()) {
+ gfxCriticalNote << "Failed GL context creation for hardware WebRender: "
+ << forHardwareWebRender;
+ return nullptr;
+ }
+ return gl.forget();
+}
+#endif
+
+#ifdef XP_MACOSX
+static already_AddRefed<gl::GLContext> CreateGLContextCGL() {
+ nsCString failureUnused;
+ return gl::GLContextProvider::CreateHeadless(
+ {gl::CreateContextFlags::ALLOW_OFFLINE_RENDERER |
+ gl::CreateContextFlags::FORBID_SOFTWARE},
+ &failureUnused);
+}
+#endif
+
+static already_AddRefed<gl::GLContext> CreateGLContext(nsACString& aError) {
+ RefPtr<gl::GLContext> gl;
+
+#ifdef XP_WIN
+ if (gfx::gfxVars::UseWebRenderANGLE()) {
+ gl = CreateGLContextANGLE(aError);
+ }
+#elif defined(MOZ_WIDGET_ANDROID)
+ gl = CreateGLContextEGL();
+#elif defined(MOZ_WAYLAND) || defined(MOZ_X11)
+ if (gfx::gfxVars::UseEGL()) {
+ gl = CreateGLContextEGL();
+ }
+#elif XP_MACOSX
+ gl = CreateGLContextCGL();
+#endif
+
+ wr::RenderThread::MaybeEnableGLDebugMessage(gl);
+
+ return gl.forget();
+}
+
+extern "C" {
+
+void wr_notifier_wake_up(mozilla::wr::WrWindowId aWindowId,
+ bool aCompositeNeeded) {
+ // wake_up is used for things like propagating debug options or memory
+ // pressure events, so we are not tracking pending frame counts.
+ mozilla::wr::RenderThread::Get()->WrNotifierEvent_WakeUp(aWindowId,
+ aCompositeNeeded);
+}
+
+void wr_notifier_new_frame_ready(mozilla::wr::WrWindowId aWindowId,
+ bool aCompositeNeeded,
+ mozilla::wr::FramePublishId aPublishId) {
+ auto* renderThread = mozilla::wr::RenderThread::Get();
+ renderThread->DecPendingFrameBuildCount(aWindowId);
+
+ renderThread->WrNotifierEvent_NewFrameReady(aWindowId, aCompositeNeeded,
+ aPublishId);
+}
+
+void wr_notifier_external_event(mozilla::wr::WrWindowId aWindowId,
+ size_t aRawEvent) {
+ mozilla::wr::RenderThread::Get()->WrNotifierEvent_ExternalEvent(
+ mozilla::wr::WindowId(aWindowId), aRawEvent);
+}
+
+static void NotifyScheduleRender(mozilla::wr::WrWindowId aWindowId,
+ wr::RenderReasons aReasons) {
+ RefPtr<mozilla::layers::CompositorBridgeParent> cbp = mozilla::layers::
+ CompositorBridgeParent::GetCompositorBridgeParentFromWindowId(aWindowId);
+ if (cbp) {
+ cbp->ScheduleComposition(aReasons);
+ }
+}
+
+void wr_schedule_render(mozilla::wr::WrWindowId aWindowId,
+ wr::RenderReasons aReasons) {
+ layers::CompositorThread()->Dispatch(NewRunnableFunction(
+ "NotifyScheduleRender", &NotifyScheduleRender, aWindowId, aReasons));
+}
+
+static void NotifyDidSceneBuild(
+ mozilla::wr::WrWindowId aWindowId,
+ const RefPtr<const wr::WebRenderPipelineInfo>& aInfo) {
+ RefPtr<mozilla::layers::CompositorBridgeParent> cbp = mozilla::layers::
+ CompositorBridgeParent::GetCompositorBridgeParentFromWindowId(aWindowId);
+ if (cbp) {
+ cbp->NotifyDidSceneBuild(aInfo);
+ }
+}
+
+void wr_finished_scene_build(mozilla::wr::WrWindowId aWindowId,
+ mozilla::wr::WrPipelineInfo* aPipelineInfo) {
+ RefPtr<wr::WebRenderPipelineInfo> info = new wr::WebRenderPipelineInfo();
+ info->Raw() = std::move(*aPipelineInfo);
+ layers::CompositorThread()->Dispatch(NewRunnableFunction(
+ "NotifyDidSceneBuild", &NotifyDidSceneBuild, aWindowId, info));
+}
+
+} // extern C