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("BeginFrame", /* aForce */ true);
170 mCompositor
->GetWidget()->PostRender(&widgetContext
);
171 return RenderedFrameId();
174 auto size
= mCompositor
->GetBufferSize();
175 auto bufferAge
= mCompositor
->GetBufferAge();
177 wr_renderer_update(mRenderer
);
179 bool fullRender
= mCompositor
->RequestFullRender();
180 // When we're rendering to an external target, we want to render everything.
181 if (mCompositor
->UsePartialPresent() &&
182 (aReadbackBuffer
.isSome() || layers::ProfilerScreenshots::IsEnabled())) {
186 wr_renderer_force_redraw(mRenderer
);
189 nsTArray
<DeviceIntRect
> dirtyRects
;
190 bool rendered
= wr_renderer_render(mRenderer
, size
.width
, size
.height
,
191 bufferAge
, aOutStats
, &dirtyRects
);
194 mCompositor
->CancelFrame();
195 RenderThread::Get()->HandleWebRenderError(WebRenderError::RENDER
);
196 mCompositor
->GetWidget()->PostRender(&widgetContext
);
197 return RenderedFrameId();
200 if (aReadbackBuffer
.isSome()) {
201 MOZ_ASSERT(aReadbackSize
.isSome());
202 MOZ_ASSERT(aReadbackFormat
.isSome());
203 if (!mCompositor
->MaybeReadback(aReadbackSize
.ref(), aReadbackFormat
.ref(),
204 aReadbackBuffer
.ref(), aNeedsYFlip
)) {
205 wr_renderer_readback(mRenderer
, aReadbackSize
.ref().width
,
206 aReadbackSize
.ref().height
, aReadbackFormat
.ref(),
207 &aReadbackBuffer
.ref()[0],
208 aReadbackBuffer
.ref().length());
210 *aNeedsYFlip
= !mCompositor
->SurfaceOriginIsTopLeft();
215 if (size
.Width() != 0 && size
.Height() != 0) {
216 if (!mCompositor
->MaybeGrabScreenshot(size
.ToUnknownSize())) {
217 mScreenshotGrabber
.MaybeGrabScreenshot(this, size
.ToUnknownSize());
221 // Frame recording must happen before EndFrame, as we must ensure we read the
222 // contents of the back buffer before any calls to SwapBuffers which might
224 MaybeRecordFrame(mLastPipelineInfo
);
226 RenderedFrameId frameId
= mCompositor
->EndFrame(dirtyRects
);
228 mCompositor
->GetWidget()->PostRender(&widgetContext
);
230 #if defined(ENABLE_FRAME_LATENCY_LOG)
231 if (mFrameStartTime
) {
233 round((TimeStamp::Now() - mFrameStartTime
).ToMilliseconds());
234 printf_stderr("generate frame latencyMs latencyMs %d\n", latencyMs
);
236 // Clear frame start time
237 mFrameStartTime
= TimeStamp();
240 if (!mCompositor
->MaybeProcessScreenshotQueue()) {
241 mScreenshotGrabber
.MaybeProcessQueue(this);
244 // TODO: Flush pending actions such as texture deletions/unlocks and
245 // textureHosts recycling.
250 bool RendererOGL::EnsureAsyncScreenshot() {
251 if (mCompositor
->SupportAsyncScreenshot()) {
254 if (!mDisableNativeCompositor
) {
255 layers::CompositorThread()->Dispatch(
256 NewRunnableFunction("DoWebRenderDisableNativeCompositorRunnable",
257 &DoWebRenderDisableNativeCompositor
, mBridge
));
259 mDisableNativeCompositor
= true;
260 gfxCriticalNote
<< "Disable native compositor for async screenshot";
265 void RendererOGL::CheckGraphicsResetStatus(const char* aCaller
, bool aForce
) {
267 auto reason
= mCompositor
->IsContextLost(aForce
);
268 if (reason
!= LOCAL_GL_NO_ERROR
) {
269 RenderThread::Get()->HandleDeviceReset(aCaller
, reason
);
274 void RendererOGL::WaitForGPU() {
275 if (!mCompositor
->WaitForGPU()) {
276 CheckGraphicsResetStatus("WaitForGPU", /* aForce */ true);
280 ipc::FileDescriptor
RendererOGL::GetAndResetReleaseFence() {
281 return mCompositor
->GetAndResetReleaseFence();
284 RenderedFrameId
RendererOGL::GetLastCompletedFrameId() {
285 return mCompositor
->GetLastCompletedFrameId();
288 RenderedFrameId
RendererOGL::UpdateFrameId() {
289 return mCompositor
->UpdateFrameId();
292 void RendererOGL::Pause() { mCompositor
->Pause(); }
294 bool RendererOGL::Resume() { return mCompositor
->Resume(); }
296 bool RendererOGL::IsPaused() { return mCompositor
->IsPaused(); }
298 layers::SyncObjectHost
* RendererOGL::GetSyncObject() const {
299 return mCompositor
->GetSyncObject();
302 gl::GLContext
* RendererOGL::gl() const { return mCompositor
->gl(); }
304 void* RendererOGL::swgl() const { return mCompositor
->swgl(); }
306 void RendererOGL::SetFrameStartTime(const TimeStamp
& aTime
) {
307 if (mFrameStartTime
) {
308 // frame start time is already set. This could happen when multiple
309 // generate frame requests are merged by webrender.
312 mFrameStartTime
= aTime
;
315 void RendererOGL::BeginRecording(const TimeStamp
& aRecordingStart
,
316 wr::PipelineId aRootPipelineId
) {
317 MOZ_ASSERT(!mCompositionRecorder
);
319 mRootPipelineId
= aRootPipelineId
;
320 mCompositionRecorder
=
321 MakeUnique
<layers::CompositionRecorder
>(aRecordingStart
);
322 mCompositor
->MaybeRequestAllowFrameRecording(true);
325 void RendererOGL::MaybeRecordFrame(const WebRenderPipelineInfo
* aPipelineInfo
) {
326 if (!mCompositionRecorder
|| !EnsureAsyncScreenshot()) {
330 if (!mRenderer
|| !aPipelineInfo
|| !DidPaintContent(aPipelineInfo
)) {
334 if (mCompositor
->MaybeRecordFrame(*mCompositionRecorder
)) {
338 wr::RecordedFrameHandle handle
{0};
339 gfx::IntSize
size(0, 0);
341 if (wr_renderer_record_frame(mRenderer
, wr::ImageFormat::BGRA8
, &handle
,
342 &size
.width
, &size
.height
)) {
343 RefPtr
<layers::RecordedFrame
> frame
=
344 new RendererRecordedFrame(TimeStamp::Now(), mRenderer
, handle
, size
);
346 mCompositionRecorder
->RecordFrame(frame
);
350 bool RendererOGL::DidPaintContent(const WebRenderPipelineInfo
* aFrameEpochs
) {
351 const wr::WrPipelineInfo
& info
= aFrameEpochs
->Raw();
352 bool didPaintContent
= false;
354 // Check if a non-root pipeline has updated to a new epoch.
355 // We treat all non-root pipelines as "content" pipelines, even if they're
356 // not fed by content paints, such as videos (see bug 1665512).
357 for (const auto& epoch
: info
.epochs
) {
358 const wr::PipelineId pipelineId
= epoch
.pipeline_id
;
360 if (pipelineId
== mRootPipelineId
) {
364 const auto it
= mContentPipelineEpochs
.find(AsUint64(pipelineId
));
365 if (it
== mContentPipelineEpochs
.end() || it
->second
!= epoch
.epoch
) {
366 // This pipeline has updated since last render or has newly rendered.
367 didPaintContent
= true;
368 mContentPipelineEpochs
[AsUint64(pipelineId
)] = epoch
.epoch
;
372 for (const auto& removedPipeline
: info
.removed_pipelines
) {
373 const wr::PipelineId pipelineId
= removedPipeline
.pipeline_id
;
374 if (pipelineId
== mRootPipelineId
) {
377 mContentPipelineEpochs
.erase(AsUint64(pipelineId
));
380 return didPaintContent
;
383 Maybe
<layers::FrameRecording
> RendererOGL::EndRecording() {
384 if (!mCompositionRecorder
) {
385 MOZ_DIAGNOSTIC_ASSERT(
386 false, "Attempted to get frames from a window that was not recording.");
390 auto maybeRecording
= mCompositionRecorder
->GetRecording();
392 wr_renderer_release_composition_recorder_structures(mRenderer
);
394 mCompositor
->MaybeRequestAllowFrameRecording(false);
395 mCompositionRecorder
= nullptr;
397 return maybeRecording
;
400 void RendererOGL::FlushPipelineInfo() {
401 RefPtr
<WebRenderPipelineInfo
> info
= new WebRenderPipelineInfo
;
402 wr_renderer_flush_pipeline_info(mRenderer
, &info
->Raw());
403 mLastPipelineInfo
= info
;
406 RenderTextureHost
* RendererOGL::GetRenderTexture(
407 wr::ExternalImageId aExternalImageId
) {
408 return mThread
->GetRenderTexture(aExternalImageId
);
411 void RendererOGL::AccumulateMemoryReport(MemoryReport
* aReport
) {
412 wr_renderer_accumulate_memory_report(GetRenderer(), aReport
, swgl());
414 LayoutDeviceIntSize size
= mCompositor
->GetBufferSize();
416 // Assume BGRA8 for the format since it's not exposed anywhere,
417 // and all compositor backends should be using that.
418 uintptr_t swapChainSize
= size
.width
* size
.height
*
419 BytesPerPixel(gfx::SurfaceFormat::B8G8R8A8
) *
420 (mCompositor
->UseTripleBuffering() ? 3 : 2);
421 aReport
->swap_chain
+= swapChainSize
;
424 void RendererOGL::SetProfilerUI(const nsACString
& aUI
) {
425 wr_renderer_set_profiler_ui(GetRenderer(), (const uint8_t*)aUI
.BeginReading(),
430 } // namespace mozilla