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