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/. */
7 #include "RendererOGL.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/LayersTypes.h"
17 #include "mozilla/layers/ProfilerScreenshots.h"
18 #include "mozilla/webrender/RenderCompositor.h"
19 #include "mozilla/webrender/RenderTextureHost.h"
20 #include "mozilla/widget/CompositorWidget.h"
25 class RendererRecordedFrame final
: public layers::RecordedFrame
{
27 RendererRecordedFrame(const TimeStamp
& aTimeStamp
, wr::Renderer
* aRenderer
,
28 const wr::RecordedFrameHandle aHandle
,
29 const gfx::IntSize
& aSize
)
30 : RecordedFrame(aTimeStamp
),
35 already_AddRefed
<gfx::DataSourceSurface
> GetSourceSurface() override
{
37 mSurface
= gfx::Factory::CreateDataSourceSurface(
38 mSize
, gfx::SurfaceFormat::B8G8R8A8
, /* aZero = */ false);
40 gfx::DataSourceSurface::ScopedMap
map(mSurface
,
41 gfx::DataSourceSurface::WRITE
);
43 if (!wr_renderer_map_recorded_frame(mRenderer
, mHandle
, map
.GetData(),
44 map
.GetStride() * mSize
.height
,
50 return do_AddRef(mSurface
);
54 wr::Renderer
* mRenderer
;
55 RefPtr
<gfx::DataSourceSurface
> mSurface
;
57 wr::RecordedFrameHandle mHandle
;
60 wr::WrExternalImage
wr_renderer_lock_external_image(void* aObj
,
61 wr::ExternalImageId aId
,
62 uint8_t aChannelIndex
) {
63 RendererOGL
* renderer
= reinterpret_cast<RendererOGL
*>(aObj
);
64 RenderTextureHost
* texture
= renderer
->GetRenderTexture(aId
);
67 gfxCriticalNoteOnce
<< "Failed to lock ExternalImage for extId:"
69 return InvalidToWrExternalImage();
71 if (auto* gl
= renderer
->gl()) {
72 return texture
->Lock(aChannelIndex
, gl
);
73 } else if (auto* swgl
= renderer
->swgl()) {
74 return texture
->LockSWGL(aChannelIndex
, swgl
, renderer
->GetCompositor());
77 << "No GL or SWGL context available to lock ExternalImage for extId:"
79 return InvalidToWrExternalImage();
83 void wr_renderer_unlock_external_image(void* aObj
, wr::ExternalImageId aId
,
84 uint8_t aChannelIndex
) {
85 RendererOGL
* renderer
= reinterpret_cast<RendererOGL
*>(aObj
);
86 RenderTextureHost
* texture
= renderer
->GetRenderTexture(aId
);
93 } else if (renderer
->swgl()) {
94 texture
->UnlockSWGL();
98 RendererOGL::RendererOGL(RefPtr
<RenderThread
>&& aThread
,
99 UniquePtr
<RenderCompositor
> aCompositor
,
100 wr::WindowId aWindowId
, wr::Renderer
* aRenderer
,
101 layers::CompositorBridgeParent
* aBridge
)
103 mCompositor(std::move(aCompositor
)),
104 mRenderer(aRenderer
),
106 mWindowId(aWindowId
),
107 mDisableNativeCompositor(false),
108 mLastPipelineInfo(new WebRenderPipelineInfo
) {
110 MOZ_ASSERT(mCompositor
);
111 MOZ_ASSERT(mRenderer
);
113 MOZ_COUNT_CTOR(RendererOGL
);
116 RendererOGL::~RendererOGL() {
117 MOZ_COUNT_DTOR(RendererOGL
);
118 if (!mCompositor
->MakeCurrent()) {
120 << "Failed to make render context current during destroying.";
123 wr_renderer_delete(mRenderer
);
127 wr::WrExternalImageHandler
RendererOGL::GetExternalImageHandler() {
128 return wr::WrExternalImageHandler
{
133 void RendererOGL::SetFramePublishId(FramePublishId aPublishId
) {
134 wr_renderer_set_target_frame_publish_id(mRenderer
, aPublishId
);
137 void RendererOGL::Update() {
138 mCompositor
->Update();
139 if (mCompositor
->MakeCurrent()) {
140 wr_renderer_update(mRenderer
);
145 static void DoWebRenderDisableNativeCompositor(
146 layers::CompositorBridgeParent
* aBridge
) {
147 aBridge
->NotifyWebRenderDisableNativeCompositor();
150 RenderedFrameId
RendererOGL::UpdateAndRender(
151 const Maybe
<gfx::IntSize
>& aReadbackSize
,
152 const Maybe
<wr::ImageFormat
>& aReadbackFormat
,
153 const Maybe
<Range
<uint8_t>>& aReadbackBuffer
, bool* aNeedsYFlip
,
154 RendererStats
* aOutStats
) {
155 mozilla::widget::WidgetRenderingContext widgetContext
;
157 #if defined(XP_MACOSX)
158 widgetContext
.mGL
= mCompositor
->gl();
161 if (!mCompositor
->GetWidget()->PreRender(&widgetContext
)) {
162 // XXX This could cause oom in webrender since pending_texture_updates is
163 // not handled. It needs to be addressed.
164 return RenderedFrameId();
166 // XXX set clear color if MOZ_WIDGET_ANDROID is defined.
168 if (mThread
->IsHandlingDeviceReset() || !mCompositor
->BeginFrame()) {
169 CheckGraphicsResetStatus(gfx::DeviceResetDetectPlace::WR_BEGIN_FRAME
,
171 mCompositor
->GetWidget()->PostRender(&widgetContext
);
172 return RenderedFrameId();
175 auto size
= mCompositor
->GetBufferSize();
176 auto bufferAge
= mCompositor
->GetBufferAge();
178 wr_renderer_update(mRenderer
);
180 bool fullRender
= mCompositor
->RequestFullRender();
181 // When we're rendering to an external target, we want to render everything.
182 if (mCompositor
->UsePartialPresent() &&
183 (aReadbackBuffer
.isSome() || layers::ProfilerScreenshots::IsEnabled())) {
187 wr_renderer_force_redraw(mRenderer
);
190 nsTArray
<DeviceIntRect
> dirtyRects
;
191 bool rendered
= wr_renderer_render(mRenderer
, size
.width
, size
.height
,
192 bufferAge
, aOutStats
, &dirtyRects
);
195 mCompositor
->CancelFrame();
196 RenderThread::Get()->HandleWebRenderError(WebRenderError::RENDER
);
197 mCompositor
->GetWidget()->PostRender(&widgetContext
);
198 return RenderedFrameId();
201 if (aReadbackBuffer
.isSome()) {
202 MOZ_ASSERT(aReadbackSize
.isSome());
203 MOZ_ASSERT(aReadbackFormat
.isSome());
204 if (!mCompositor
->MaybeReadback(aReadbackSize
.ref(), aReadbackFormat
.ref(),
205 aReadbackBuffer
.ref(), aNeedsYFlip
)) {
206 wr_renderer_readback(mRenderer
, aReadbackSize
.ref().width
,
207 aReadbackSize
.ref().height
, aReadbackFormat
.ref(),
208 &aReadbackBuffer
.ref()[0],
209 aReadbackBuffer
.ref().length());
211 *aNeedsYFlip
= !mCompositor
->SurfaceOriginIsTopLeft();
216 if (size
.Width() != 0 && size
.Height() != 0) {
217 if (!mCompositor
->MaybeGrabScreenshot(size
.ToUnknownSize())) {
218 mScreenshotGrabber
.MaybeGrabScreenshot(this, size
.ToUnknownSize());
222 // Frame recording must happen before EndFrame, as we must ensure we read the
223 // contents of the back buffer before any calls to SwapBuffers which might
225 MaybeRecordFrame(mLastPipelineInfo
);
227 RenderedFrameId frameId
= mCompositor
->EndFrame(dirtyRects
);
229 mCompositor
->GetWidget()->PostRender(&widgetContext
);
231 #if defined(ENABLE_FRAME_LATENCY_LOG)
232 if (mFrameStartTime
) {
234 round((TimeStamp::Now() - mFrameStartTime
).ToMilliseconds());
235 printf_stderr("generate frame latencyMs latencyMs %d\n", latencyMs
);
237 // Clear frame start time
238 mFrameStartTime
= TimeStamp();
241 if (!mCompositor
->MaybeProcessScreenshotQueue()) {
242 mScreenshotGrabber
.MaybeProcessQueue(this);
245 // TODO: Flush pending actions such as texture deletions/unlocks and
246 // textureHosts recycling.
251 bool RendererOGL::EnsureAsyncScreenshot() {
252 if (mCompositor
->SupportAsyncScreenshot()) {
255 if (!mDisableNativeCompositor
) {
256 layers::CompositorThread()->Dispatch(
257 NewRunnableFunction("DoWebRenderDisableNativeCompositorRunnable",
258 &DoWebRenderDisableNativeCompositor
, mBridge
));
260 mDisableNativeCompositor
= true;
261 gfxCriticalNote
<< "Disable native compositor for async screenshot";
266 void RendererOGL::CheckGraphicsResetStatus(gfx::DeviceResetDetectPlace aPlace
,
269 auto reason
= mCompositor
->IsContextLost(aForce
);
270 if (reason
!= gfx::DeviceResetReason::OK
) {
271 RenderThread::Get()->HandleDeviceReset(aPlace
, reason
);
276 void RendererOGL::WaitForGPU() {
277 if (!mCompositor
->WaitForGPU()) {
278 CheckGraphicsResetStatus(gfx::DeviceResetDetectPlace::WR_WAIT_FOR_GPU
,
283 ipc::FileDescriptor
RendererOGL::GetAndResetReleaseFence() {
284 return mCompositor
->GetAndResetReleaseFence();
287 RenderedFrameId
RendererOGL::GetLastCompletedFrameId() {
288 return mCompositor
->GetLastCompletedFrameId();
291 RenderedFrameId
RendererOGL::UpdateFrameId() {
292 return mCompositor
->UpdateFrameId();
295 void RendererOGL::Pause() { mCompositor
->Pause(); }
297 bool RendererOGL::Resume() { return mCompositor
->Resume(); }
299 bool RendererOGL::IsPaused() { return mCompositor
->IsPaused(); }
301 layers::SyncObjectHost
* RendererOGL::GetSyncObject() const {
302 return mCompositor
->GetSyncObject();
305 gl::GLContext
* RendererOGL::gl() const { return mCompositor
->gl(); }
307 void* RendererOGL::swgl() const { return mCompositor
->swgl(); }
309 void RendererOGL::SetFrameStartTime(const TimeStamp
& aTime
) {
310 if (mFrameStartTime
) {
311 // frame start time is already set. This could happen when multiple
312 // generate frame requests are merged by webrender.
315 mFrameStartTime
= aTime
;
318 void RendererOGL::BeginRecording(const TimeStamp
& aRecordingStart
,
319 wr::PipelineId aRootPipelineId
) {
320 MOZ_ASSERT(!mCompositionRecorder
);
322 mRootPipelineId
= aRootPipelineId
;
323 mCompositionRecorder
=
324 MakeUnique
<layers::CompositionRecorder
>(aRecordingStart
);
325 mCompositor
->MaybeRequestAllowFrameRecording(true);
328 void RendererOGL::MaybeRecordFrame(const WebRenderPipelineInfo
* aPipelineInfo
) {
329 if (!mCompositionRecorder
|| !EnsureAsyncScreenshot()) {
333 if (!mRenderer
|| !aPipelineInfo
|| !DidPaintContent(aPipelineInfo
)) {
337 if (mCompositor
->MaybeRecordFrame(*mCompositionRecorder
)) {
341 wr::RecordedFrameHandle handle
{0};
342 gfx::IntSize
size(0, 0);
344 if (wr_renderer_record_frame(mRenderer
, wr::ImageFormat::BGRA8
, &handle
,
345 &size
.width
, &size
.height
)) {
346 RefPtr
<layers::RecordedFrame
> frame
=
347 new RendererRecordedFrame(TimeStamp::Now(), mRenderer
, handle
, size
);
349 mCompositionRecorder
->RecordFrame(frame
);
353 bool RendererOGL::DidPaintContent(const WebRenderPipelineInfo
* aFrameEpochs
) {
354 const wr::WrPipelineInfo
& info
= aFrameEpochs
->Raw();
355 bool didPaintContent
= false;
357 // Check if a non-root pipeline has updated to a new epoch.
358 // We treat all non-root pipelines as "content" pipelines, even if they're
359 // not fed by content paints, such as videos (see bug 1665512).
360 for (const auto& epoch
: info
.epochs
) {
361 const wr::PipelineId pipelineId
= epoch
.pipeline_id
;
363 if (pipelineId
== mRootPipelineId
) {
367 const auto it
= mContentPipelineEpochs
.find(AsUint64(pipelineId
));
368 if (it
== mContentPipelineEpochs
.end() || it
->second
!= epoch
.epoch
) {
369 // This pipeline has updated since last render or has newly rendered.
370 didPaintContent
= true;
371 mContentPipelineEpochs
[AsUint64(pipelineId
)] = epoch
.epoch
;
375 for (const auto& removedPipeline
: info
.removed_pipelines
) {
376 const wr::PipelineId pipelineId
= removedPipeline
.pipeline_id
;
377 if (pipelineId
== mRootPipelineId
) {
380 mContentPipelineEpochs
.erase(AsUint64(pipelineId
));
383 return didPaintContent
;
386 Maybe
<layers::FrameRecording
> RendererOGL::EndRecording() {
387 if (!mCompositionRecorder
) {
388 MOZ_DIAGNOSTIC_ASSERT(
389 false, "Attempted to get frames from a window that was not recording.");
393 auto maybeRecording
= mCompositionRecorder
->GetRecording();
395 wr_renderer_release_composition_recorder_structures(mRenderer
);
397 mCompositor
->MaybeRequestAllowFrameRecording(false);
398 mCompositionRecorder
= nullptr;
400 return maybeRecording
;
403 void RendererOGL::FlushPipelineInfo() {
404 RefPtr
<WebRenderPipelineInfo
> info
= new WebRenderPipelineInfo
;
405 wr_renderer_flush_pipeline_info(mRenderer
, &info
->Raw());
406 mLastPipelineInfo
= info
;
409 RenderTextureHost
* RendererOGL::GetRenderTexture(
410 wr::ExternalImageId aExternalImageId
) {
411 return mThread
->GetRenderTexture(aExternalImageId
);
414 void RendererOGL::AccumulateMemoryReport(MemoryReport
* aReport
) {
415 wr_renderer_accumulate_memory_report(GetRenderer(), aReport
, swgl());
417 LayoutDeviceIntSize size
= mCompositor
->GetBufferSize();
419 // Assume BGRA8 for the format since it's not exposed anywhere,
420 // and all compositor backends should be using that.
421 uintptr_t swapChainSize
= size
.width
* size
.height
*
422 BytesPerPixel(gfx::SurfaceFormat::B8G8R8A8
) *
423 (mCompositor
->UseTripleBuffering() ? 3 : 2);
424 aReport
->swap_chain
+= swapChainSize
;
427 void RendererOGL::SetProfilerUI(const nsACString
& aUI
) {
428 wr_renderer_set_profiler_ui(GetRenderer(), (const uint8_t*)aUI
.BeginReading(),
433 } // namespace mozilla