/* -*- 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 #endif #ifdef MOZ_WIDGET_ANDROID # include "GLLibraryEGL.h" # include "mozilla/webrender/RenderAndroidSurfaceTextureHost.h" #endif #ifdef MOZ_WIDGET_GTK # include "mozilla/WidgetUtilsGtk.h" # include "GLLibraryEGL.h" #endif using namespace mozilla; static already_AddRefed 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 sRenderThread; static mozilla::BackgroundHangMonitor* sBackgroundHangMonitor; #ifdef DEBUG static bool sRenderThreadEverStarted = false; #endif size_t RenderThread::sRendererCount = 0; size_t RenderThread::sActiveRendererCount = 0; RenderThread::RenderThread(RefPtr aThread) : mThread(std::move(aThread)), mThreadPool(false), mThreadPoolLP(true), mSingletonGLIsForHardwareWebRender(true), mBatteryInfo("RenderThread.mBatteryInfo"), 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 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 thread = NS_GetCurrentThread(); nsThread* nsthread = static_cast(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); CrashReporter::RegisterAnnotationUSize( CrashReporter::Annotation::GraphicsNumRenderers, &sRendererCount); CrashReporter::RegisterAnnotationUSize( CrashReporter::Annotation::GraphicsNumActiveRenderers, &sActiveRendererCount); #ifdef XP_WIN widget::WinCompositorWindowThread::Start(); #endif layers::SharedSurfacesParent::Initialize(); RefPtr runnable = WrapRunnable( RefPtr(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 = WrapRunnable( RefPtr(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 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 RenderThread::GetRenderThread() { nsCOMPtr thread; if (sRenderThread) { thread = sRenderThread->mThread; } return thread.forget(); } void RenderThread::DoAccumulateMemoryReport( MemoryReport aReport, const RefPtr& 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 RenderThread::AccumulateMemoryReport( MemoryReport aInitial) { RefPtr 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>( "wr::RenderThread::DoAccumulateMemoryReport", Get(), &RenderThread::DoAccumulateMemoryReport, aInitial, p)); return p; } void RenderThread::SetBatteryInfo(const hal::BatteryInformation& aBatteryInfo) { MOZ_ASSERT(XRE_IsGPUProcess()); auto batteryInfo = mBatteryInfo.Lock(); batteryInfo.ref() = Some(aBatteryInfo); } bool RenderThread::GetPowerIsCharging() { MOZ_ASSERT(XRE_IsGPUProcess()); auto batteryInfo = mBatteryInfo.Lock(); if (batteryInfo.ref().isSome()) { return batteryInfo.ref().ref().charging(); } gfxCriticalNoteOnce << "BatteryInfo is not set"; MOZ_ASSERT_UNREACHABLE("unexpected to be called"); return false; } void RenderThread::AddRenderer(wr::WindowId aWindowId, UniquePtr aRenderer) { MOZ_ASSERT(IsInRenderThread()); LOG("RenderThread::AddRenderer() aWindowId %" PRIx64 "", AsUint64(aWindowId)); if (mHasShutdown) { return; } mRenderers[aWindowId] = std::move(aRenderer); sRendererCount = mRenderers.size(); auto windows = mWindowInfos.Lock(); windows->emplace(AsUint64(aWindowId), new WindowInfo()); mWrNotifierEventsQueues.emplace(AsUint64(aWindowId), new std::queue); } void RenderThread::RemoveRenderer(wr::WindowId aWindowId) { MOZ_ASSERT(IsInRenderThread()); LOG("RenderThread::RemoveRenderer() aWindowId %" PRIx64 "", AsUint64(aWindowId)); if (mHasShutdown) { return; } mRenderers.erase(aWindowId); sRendererCount = 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 remove, RemoveRenderer() is called in // HandleWrNotifierEvents(). RefPtr 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(); } void RenderThread::UpdateActiveRendererCount() { MOZ_ASSERT(IsInRenderThread()); size_t num_active = 0; for (const auto& it : mRenderers) { if (!it.second->IsPaused()) { num_active++; } } sActiveRendererCount = 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 evt(reinterpret_cast(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 runnable = NewRunnableMethod( "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 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 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 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 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::glean::gfx::composite_time.AccumulateRawDuration(compositeDuration); PerfStats::RecordMeasurement(PerfStats::Metric::Compositing, compositeDuration); } void RenderThread::SetClearColor(wr::WindowId aWindowId, wr::ColorF aColor) { if (mHasShutdown) { return; } if (!IsInRenderThread()) { PostRunnable(NewRunnableMethod( "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::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 aEvent) { PostRunnable(NewRunnableMethod&&>( "wr::RenderThread::PostEvent", this, &RenderThread::RunEvent, aWindowId, std::move(aEvent))); } void RenderThread::RunEvent(wr::WindowId aWindowId, UniquePtr aEvent) { MOZ_ASSERT(IsInRenderThread()); aEvent->Run(*this, aWindowId); aEvent = nullptr; } static void NotifyDidRender(layers::CompositorBridgeParent* aBridge, const RefPtr& 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& aReadbackSize, const Maybe& aReadbackFormat, const Maybe>& 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 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 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(); UpdateActiveRendererCount(); } 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(); UpdateActiveRendererCount(); 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 aTexture) { MutexAutoLock lock(mRenderTextureMapLock); if (mHasShutdown) { return; } MOZ_ASSERT(mRenderTextures.find(aExternalImageId) == mRenderTextures.end()); RefPtr 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 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&& aIds) { if (!IsInRenderThread()) { layers::SynchronousTask task("Destroy external images"); RefPtr 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&& aIds) { MOZ_ASSERT(IsInRenderThread()); std::vector> 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 texture = it->second; mRenderTextureOps.emplace_back(aOp, std::move(texture)); if (mRenderTextureOpsRunnable) { // Runnable was already triggered return; } RefPtr runnable = NewRunnableMethod("RenderThread::HandleRenderTextureOps", this, &RenderThread::HandleRenderTextureOps); mRenderTextureOpsRunnable = runnable; PostRunnable(runnable.forget()); } void RenderThread::HandleRenderTextureOps() { MOZ_ASSERT(IsInRenderThread()); std::list>> 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); 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(ThreadPool().Raw()); } // Query the shared GL context to force the // lazy initialization to happen now. SingletonGL(); } void RenderThread::PostRunnable(already_AddRefed aRunnable) { nsCOMPtr 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) { MOZ_ASSERT(IsInRenderThread()); 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(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; } if (mShaders) { if (mSingletonGL) { mSingletonGL->MakeCurrent(); } mShaders = nullptr; } mSingletonGL = nullptr; } RefPtr RenderThread::GetProgramsForCompositorOGL() { if (!mSingletonGL) { return nullptr; } if (!mProgramsForCompositorOGL) { mProgramsForCompositorOGL = MakeAndAddRef(mSingletonGL); } return mProgramsForCompositorOGL; } RefPtr RenderThread::SharedSurfacePool() { #if defined(XP_DARWIN) || 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 CreateGLContextANGLE( nsACString& aError) { const RefPtr 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_WIDGET_GTK) static already_AddRefed CreateGLContextEGL() { // Create GLContext with dummy EGLSurface. bool forHardwareWebRender = true; // SW-WR uses CompositorOGL in native compositor. if (gfx::gfxVars::UseSoftwareWebRender()) { forHardwareWebRender = false; } RefPtr 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_DARWIN static already_AddRefed CreateGLContextCGL() { nsCString failureUnused; return gl::GLContextProvider::CreateHeadless( {gl::CreateContextFlags::ALLOW_OFFLINE_RENDERER | gl::CreateContextFlags::FORBID_SOFTWARE}, &failureUnused); } #endif static already_AddRefed CreateGLContext(nsACString& aError) { RefPtr gl; #ifdef XP_WIN if (gfx::gfxVars::UseWebRenderANGLE()) { gl = CreateGLContextANGLE(aError); } #elif defined(MOZ_WIDGET_ANDROID) gl = CreateGLContextEGL(); #elif defined(MOZ_WIDGET_GTK) if (gfx::gfxVars::UseEGL()) { gl = CreateGLContextEGL(); } #elif XP_DARWIN 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 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& aInfo) { RefPtr 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 info = new wr::WebRenderPipelineInfo(); info->Raw() = std::move(*aPipelineInfo); layers::CompositorThread()->Dispatch(NewRunnableFunction( "NotifyDidSceneBuild", &NotifyDidSceneBuild, aWindowId, info)); } } // extern C