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(
61 void* aObj
, wr::ExternalImageId aId
, uint8_t aChannelIndex
,
62 wr::ImageRendering aRendering
) {
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
, aRendering
);
73 } else if (auto* swgl
= renderer
->swgl()) {
74 return texture
->LockSWGL(aChannelIndex
, swgl
, renderer
->GetCompositor(),
78 << "No GL or SWGL context available to lock ExternalImage for extId:"
80 return InvalidToWrExternalImage();
84 void wr_renderer_unlock_external_image(void* aObj
, wr::ExternalImageId aId
,
85 uint8_t aChannelIndex
) {
86 RendererOGL
* renderer
= reinterpret_cast<RendererOGL
*>(aObj
);
87 RenderTextureHost
* texture
= renderer
->GetRenderTexture(aId
);
94 } else if (renderer
->swgl()) {
95 texture
->UnlockSWGL();
99 RendererOGL::RendererOGL(RefPtr
<RenderThread
>&& aThread
,
100 UniquePtr
<RenderCompositor
> aCompositor
,
101 wr::WindowId aWindowId
, wr::Renderer
* aRenderer
,
102 layers::CompositorBridgeParent
* aBridge
)
104 mCompositor(std::move(aCompositor
)),
105 mRenderer(aRenderer
),
107 mWindowId(aWindowId
),
108 mDisableNativeCompositor(false) {
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::Update() {
134 mCompositor
->Update();
135 if (mCompositor
->MakeCurrent()) {
136 wr_renderer_update(mRenderer
);
140 static void DoWebRenderDisableNativeCompositor(
141 layers::CompositorBridgeParent
* aBridge
) {
142 aBridge
->NotifyWebRenderDisableNativeCompositor();
145 RenderedFrameId
RendererOGL::UpdateAndRender(
146 const Maybe
<gfx::IntSize
>& aReadbackSize
,
147 const Maybe
<wr::ImageFormat
>& aReadbackFormat
,
148 const Maybe
<Range
<uint8_t>>& aReadbackBuffer
, bool* aNeedsYFlip
,
149 RendererStats
* aOutStats
) {
150 mozilla::widget::WidgetRenderingContext widgetContext
;
152 #if defined(XP_MACOSX)
153 widgetContext
.mGL
= mCompositor
->gl();
156 if (!mCompositor
->GetWidget()->PreRender(&widgetContext
)) {
157 // XXX This could cause oom in webrender since pending_texture_updates is
158 // not handled. It needs to be addressed.
159 return RenderedFrameId();
162 // XXX set clear color if MOZ_WIDGET_ANDROID is defined.
164 if (mThread
->IsHandlingDeviceReset() || !mCompositor
->BeginFrame()) {
165 CheckGraphicsResetStatus("BeginFrame", /* aForce */ true);
166 mCompositor
->GetWidget()->PostRender(&widgetContext
);
167 return RenderedFrameId();
170 auto size
= mCompositor
->GetBufferSize();
171 auto bufferAge
= mCompositor
->GetBufferAge();
173 wr_renderer_update(mRenderer
);
175 bool fullRender
= mCompositor
->RequestFullRender();
176 // When we're rendering to an external target, we want to render everything.
177 if (mCompositor
->UsePartialPresent() &&
178 (aReadbackBuffer
.isSome() || layers::ProfilerScreenshots::IsEnabled())) {
182 wr_renderer_force_redraw(mRenderer
);
185 nsTArray
<DeviceIntRect
> dirtyRects
;
186 if (!wr_renderer_render(mRenderer
, size
.width
, size
.height
, bufferAge
,
187 aOutStats
, &dirtyRects
)) {
188 mCompositor
->CancelFrame();
189 RenderThread::Get()->HandleWebRenderError(WebRenderError::RENDER
);
190 mCompositor
->GetWidget()->PostRender(&widgetContext
);
191 return RenderedFrameId();
194 if (aReadbackBuffer
.isSome()) {
195 MOZ_ASSERT(aReadbackSize
.isSome());
196 MOZ_ASSERT(aReadbackFormat
.isSome());
197 if (!mCompositor
->MaybeReadback(aReadbackSize
.ref(), aReadbackFormat
.ref(),
198 aReadbackBuffer
.ref(), aNeedsYFlip
)) {
199 wr_renderer_readback(mRenderer
, aReadbackSize
.ref().width
,
200 aReadbackSize
.ref().height
, aReadbackFormat
.ref(),
201 &aReadbackBuffer
.ref()[0],
202 aReadbackBuffer
.ref().length());
204 *aNeedsYFlip
= !mCompositor
->SurfaceOriginIsTopLeft();
209 if (size
.Width() != 0 && size
.Height() != 0) {
210 if (!mCompositor
->MaybeGrabScreenshot(size
.ToUnknownSize())) {
211 mScreenshotGrabber
.MaybeGrabScreenshot(this, size
.ToUnknownSize());
215 RenderedFrameId frameId
= mCompositor
->EndFrame(dirtyRects
);
217 mCompositor
->GetWidget()->PostRender(&widgetContext
);
219 #if defined(ENABLE_FRAME_LATENCY_LOG)
220 if (mFrameStartTime
) {
222 round((TimeStamp::Now() - mFrameStartTime
).ToMilliseconds());
223 printf_stderr("generate frame latencyMs latencyMs %d\n", latencyMs
);
225 // Clear frame start time
226 mFrameStartTime
= TimeStamp();
229 if (!mCompositor
->MaybeProcessScreenshotQueue()) {
230 mScreenshotGrabber
.MaybeProcessQueue(this);
233 // TODO: Flush pending actions such as texture deletions/unlocks and
234 // textureHosts recycling.
239 bool RendererOGL::EnsureAsyncScreenshot() {
240 if (mCompositor
->SupportAsyncScreenshot()) {
243 if (!mDisableNativeCompositor
) {
244 layers::CompositorThread()->Dispatch(
245 NewRunnableFunction("DoWebRenderDisableNativeCompositorRunnable",
246 &DoWebRenderDisableNativeCompositor
, mBridge
));
248 mDisableNativeCompositor
= true;
249 gfxCriticalNote
<< "Disable native compositor for async screenshot";
254 void RendererOGL::CheckGraphicsResetStatus(const char* aCaller
, bool aForce
) {
256 auto reason
= mCompositor
->IsContextLost(aForce
);
257 if (reason
!= LOCAL_GL_NO_ERROR
) {
258 RenderThread::Get()->HandleDeviceReset(aCaller
, reason
);
263 void RendererOGL::WaitForGPU() {
264 if (!mCompositor
->WaitForGPU()) {
265 CheckGraphicsResetStatus("WaitForGPU", /* aForce */ true);
269 ipc::FileDescriptor
RendererOGL::GetAndResetReleaseFence() {
270 return mCompositor
->GetAndResetReleaseFence();
273 RenderedFrameId
RendererOGL::GetLastCompletedFrameId() {
274 return mCompositor
->GetLastCompletedFrameId();
277 RenderedFrameId
RendererOGL::UpdateFrameId() {
278 return mCompositor
->UpdateFrameId();
281 void RendererOGL::Pause() { mCompositor
->Pause(); }
283 bool RendererOGL::Resume() { return mCompositor
->Resume(); }
285 bool RendererOGL::IsPaused() { return mCompositor
->IsPaused(); }
287 layers::SyncObjectHost
* RendererOGL::GetSyncObject() const {
288 return mCompositor
->GetSyncObject();
291 gl::GLContext
* RendererOGL::gl() const { return mCompositor
->gl(); }
293 void* RendererOGL::swgl() const { return mCompositor
->swgl(); }
295 void RendererOGL::SetFrameStartTime(const TimeStamp
& aTime
) {
296 if (mFrameStartTime
) {
297 // frame start time is already set. This could happen when multiple
298 // generate frame requests are merged by webrender.
301 mFrameStartTime
= aTime
;
304 void RendererOGL::BeginRecording(const TimeStamp
& aRecordingStart
,
305 wr::PipelineId aRootPipelineId
) {
306 MOZ_ASSERT(!mCompositionRecorder
);
308 mRootPipelineId
= aRootPipelineId
;
309 mCompositionRecorder
=
310 MakeUnique
<layers::CompositionRecorder
>(aRecordingStart
);
311 mCompositor
->MaybeRequestAllowFrameRecording(true);
314 void RendererOGL::MaybeRecordFrame(const WebRenderPipelineInfo
* aPipelineInfo
) {
315 if (!mCompositionRecorder
|| !EnsureAsyncScreenshot()) {
319 if (!mRenderer
|| !aPipelineInfo
|| !DidPaintContent(aPipelineInfo
)) {
323 if (mCompositor
->MaybeRecordFrame(*mCompositionRecorder
)) {
327 wr::RecordedFrameHandle handle
{0};
328 gfx::IntSize
size(0, 0);
330 if (wr_renderer_record_frame(mRenderer
, wr::ImageFormat::BGRA8
, &handle
,
331 &size
.width
, &size
.height
)) {
332 RefPtr
<layers::RecordedFrame
> frame
=
333 new RendererRecordedFrame(TimeStamp::Now(), mRenderer
, handle
, size
);
335 mCompositionRecorder
->RecordFrame(frame
);
339 bool RendererOGL::DidPaintContent(const WebRenderPipelineInfo
* aFrameEpochs
) {
340 const wr::WrPipelineInfo
& info
= aFrameEpochs
->Raw();
341 bool didPaintContent
= false;
343 // Check if a non-root pipeline has updated to a new epoch.
344 // We treat all non-root pipelines as "content" pipelines, even if they're
345 // not fed by content paints, such as videos (see bug 1665512).
346 for (const auto& epoch
: info
.epochs
) {
347 const wr::PipelineId pipelineId
= epoch
.pipeline_id
;
349 if (pipelineId
== mRootPipelineId
) {
353 const auto it
= mContentPipelineEpochs
.find(AsUint64(pipelineId
));
354 if (it
== mContentPipelineEpochs
.end() || it
->second
!= epoch
.epoch
) {
355 // This pipeline has updated since last render or has newly rendered.
356 didPaintContent
= true;
357 mContentPipelineEpochs
[AsUint64(pipelineId
)] = epoch
.epoch
;
361 for (const auto& removedPipeline
: info
.removed_pipelines
) {
362 const wr::PipelineId pipelineId
= removedPipeline
.pipeline_id
;
363 if (pipelineId
== mRootPipelineId
) {
366 mContentPipelineEpochs
.erase(AsUint64(pipelineId
));
369 return didPaintContent
;
371 void RendererOGL::WriteCollectedFrames() {
372 if (!mCompositionRecorder
) {
373 MOZ_DIAGNOSTIC_ASSERT(
375 "Attempted to write frames from a window that was not recording.");
379 mCompositionRecorder
->WriteCollectedFrames();
381 wr_renderer_release_composition_recorder_structures(mRenderer
);
383 mCompositor
->MaybeRequestAllowFrameRecording(false);
384 mCompositionRecorder
= nullptr;
387 Maybe
<layers::CollectedFrames
> RendererOGL::GetCollectedFrames() {
388 if (!mCompositionRecorder
) {
389 MOZ_DIAGNOSTIC_ASSERT(
390 false, "Attempted to get frames from a window that was not recording.");
394 layers::CollectedFrames frames
= mCompositionRecorder
->GetCollectedFrames();
396 wr_renderer_release_composition_recorder_structures(mRenderer
);
398 mCompositor
->MaybeRequestAllowFrameRecording(false);
399 mCompositionRecorder
= nullptr;
401 return Some(std::move(frames
));
404 RefPtr
<WebRenderPipelineInfo
> RendererOGL::FlushPipelineInfo() {
405 RefPtr
<WebRenderPipelineInfo
> info
= new WebRenderPipelineInfo();
406 wr_renderer_flush_pipeline_info(mRenderer
, &info
->Raw());
410 RenderTextureHost
* RendererOGL::GetRenderTexture(
411 wr::ExternalImageId aExternalImageId
) {
412 return mThread
->GetRenderTexture(aExternalImageId
);
415 void RendererOGL::AccumulateMemoryReport(MemoryReport
* aReport
) {
416 wr_renderer_accumulate_memory_report(GetRenderer(), aReport
);
418 LayoutDeviceIntSize size
= mCompositor
->GetBufferSize();
420 // Assume BGRA8 for the format since it's not exposed anywhere,
421 // and all compositor backends should be using that.
422 uintptr_t swapChainSize
= size
.width
* size
.height
*
423 BytesPerPixel(gfx::SurfaceFormat::B8G8R8A8
) *
424 (mCompositor
->UseTripleBuffering() ? 3 : 2);
425 aReport
->swap_chain
+= swapChainSize
;
428 void RendererOGL::SetProfilerUI(const nsCString
& aUI
) {
429 wr_renderer_set_profiler_ui(GetRenderer(), (const uint8_t*)aUI
.get(),
434 } // namespace mozilla