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 "RenderCompositorEGL.h"
10 #include "GLContextEGL.h"
11 #include "GLContextProvider.h"
12 #include "GLLibraryEGL.h"
13 #include "mozilla/StaticPrefs_gfx.h"
14 #include "mozilla/gfx/Logging.h"
15 #include "mozilla/gfx/gfxVars.h"
16 #include "mozilla/layers/BuildConstants.h"
17 #include "mozilla/webrender/RenderThread.h"
18 #include "mozilla/widget/CompositorWidget.h"
21 # include "mozilla/WidgetUtilsGtk.h"
22 # include "mozilla/widget/GtkCompositorWidget.h"
25 #ifdef MOZ_WIDGET_ANDROID
26 # include "mozilla/java/GeckoSurfaceTextureWrappers.h"
27 # include "mozilla/layers/AndroidHardwareBuffer.h"
28 # include "mozilla/widget/AndroidCompositorWidget.h"
29 # include <android/native_window.h>
30 # include <android/native_window_jni.h>
33 namespace mozilla::wr
{
35 extern LazyLogModule gRenderThreadLog
;
36 #define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
39 UniquePtr
<RenderCompositor
> RenderCompositorEGL::Create(
40 const RefPtr
<widget::CompositorWidget
>& aWidget
, nsACString
& aError
) {
41 if (kIsLinux
&& !gfx::gfxVars::UseEGL()) {
44 RefPtr
<gl::GLContext
> gl
= RenderThread::Get()->SingletonGL(aError
);
46 if (aError
.IsEmpty()) {
47 aError
.Assign("RcANGLE(no shared GL)"_ns
);
49 aError
.Append("(Create)"_ns
);
53 return MakeUnique
<RenderCompositorEGL
>(aWidget
, std::move(gl
));
56 EGLSurface
RenderCompositorEGL::CreateEGLSurface() {
57 EGLSurface surface
= EGL_NO_SURFACE
;
58 surface
= gl::GLContextEGL::CreateEGLSurfaceForCompositorWidget(
59 mWidget
, gl::GLContextEGL::Cast(gl())->mSurfaceConfig
);
60 if (surface
== EGL_NO_SURFACE
) {
61 const auto* renderThread
= RenderThread::Get();
62 gfxCriticalNote
<< "Failed to create EGLSurface. "
63 << renderThread
->RendererCount() << " renderers, "
64 << renderThread
->ActiveRendererCount() << " active.";
69 RenderCompositorEGL::RenderCompositorEGL(
70 const RefPtr
<widget::CompositorWidget
>& aWidget
,
71 RefPtr
<gl::GLContext
>&& aGL
)
72 : RenderCompositor(aWidget
), mGL(aGL
), mEGLSurface(EGL_NO_SURFACE
) {
74 LOG("RenderCompositorEGL::RenderCompositorEGL()");
77 RenderCompositorEGL::~RenderCompositorEGL() {
78 LOG("RenderCompositorEGL::~RenderCompositorEGL()");
79 #ifdef MOZ_WIDGET_ANDROID
80 java::GeckoSurfaceTexture::DestroyUnused((int64_t)gl());
85 bool RenderCompositorEGL::BeginFrame() {
86 if (kIsLinux
&& mEGLSurface
== EGL_NO_SURFACE
) {
88 << "We don't have EGLSurface to draw into. Called too early?";
92 if (mWidget
->AsGTK()) {
93 if (!mWidget
->AsGTK()->SetEGLNativeWindowSize(GetBufferSize())) {
95 << "We don't have GTK/EGLWindow with correct size, can't draw.";
100 if (!MakeCurrent()) {
101 gfxCriticalNote
<< "Failed to make render context current, can't draw.";
105 #ifdef MOZ_WIDGET_ANDROID
106 java::GeckoSurfaceTexture::DestroyUnused((int64_t)gl());
107 gl()->MakeCurrent(); // DestroyUnused can change the current context!
113 RenderedFrameId
RenderCompositorEGL::EndFrame(
114 const nsTArray
<DeviceIntRect
>& aDirtyRects
) {
115 #ifdef MOZ_WIDGET_ANDROID
116 const auto& gle
= gl::GLContextEGL::Cast(gl());
117 const auto& egl
= gle
->mEgl
;
119 EGLSync sync
= nullptr;
120 if (layers::AndroidHardwareBufferApi::Get()) {
121 sync
= egl
->fCreateSync(LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID
, nullptr);
124 int fenceFd
= egl
->fDupNativeFenceFDANDROID(sync
);
126 mReleaseFenceFd
= ipc::FileDescriptor(UniqueFileHandle(fenceFd
));
128 egl
->fDestroySync(sync
);
133 RenderedFrameId frameId
= GetNextRenderFrameId();
134 #ifdef MOZ_WIDGET_GTK
135 if (mWidget
->IsHidden()) {
139 if (mEGLSurface
!= EGL_NO_SURFACE
&& aDirtyRects
.Length() > 0) {
140 gfx::IntRegion bufferInvalid
;
141 const auto bufferSize
= GetBufferSize();
142 for (const DeviceIntRect
& rect
: aDirtyRects
) {
143 const auto left
= std::max(0, std::min(bufferSize
.width
, rect
.min
.x
));
144 const auto top
= std::max(0, std::min(bufferSize
.height
, rect
.min
.y
));
146 const auto right
= std::min(bufferSize
.width
, std::max(0, rect
.max
.x
));
147 const auto bottom
= std::min(bufferSize
.height
, std::max(0, rect
.max
.y
));
149 const auto width
= right
- left
;
150 const auto height
= bottom
- top
;
152 bufferInvalid
.OrWith(
153 gfx::IntRect(left
, (GetBufferSize().height
- bottom
), width
, height
));
155 gl()->SetDamage(bufferInvalid
);
161 void RenderCompositorEGL::Pause() { DestroyEGLSurface(); }
163 bool RenderCompositorEGL::Resume() {
165 // Destroy EGLSurface if it exists.
168 auto size
= GetBufferSize();
169 GLint maxTextureSize
= 0;
170 gl()->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE
, (GLint
*)&maxTextureSize
);
172 // When window size is too big, hardware buffer allocation could fail.
173 if (maxTextureSize
< size
.width
|| maxTextureSize
< size
.height
) {
174 gfxCriticalNote
<< "Too big ANativeWindow size(" << size
.width
<< ", "
175 << size
.height
<< ") MaxTextureSize " << maxTextureSize
;
179 mEGLSurface
= CreateEGLSurface();
180 if (mEGLSurface
== EGL_NO_SURFACE
) {
181 // Often when we fail to create an EGL surface it is because the Java
182 // Surface we have been provided is invalid. Therefore the on the first
183 // occurence we don't raise a WebRenderError and instead just return
184 // failure. This allows the widget a chance to request a new Java
185 // Surface. On subsequent failures, raising the WebRenderError will
186 // result in the compositor being recreated, falling back through
187 // webrender configurations, and eventually crashing if we still do not
189 if (!mHandlingNewSurfaceError
) {
190 mHandlingNewSurfaceError
= true;
192 RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE
);
196 mHandlingNewSurfaceError
= false;
198 gl::GLContextEGL::Cast(gl())->SetEGLSurfaceOverride(mEGLSurface
);
199 } else if (kIsLinux
) {
200 // Destroy EGLSurface if it exists and create a new one. We will set the
201 // swap interval after MakeCurrent() has been called.
203 mEGLSurface
= CreateEGLSurface();
204 if (mEGLSurface
!= EGL_NO_SURFACE
) {
205 // We have a new EGL surface, which on wayland needs to be configured for
206 // non-blocking buffer swaps. We need MakeCurrent() to set our current EGL
207 // context before we call eglSwapInterval, which is why we do it here
208 // rather than where the surface was created.
209 const auto& gle
= gl::GLContextEGL::Cast(gl());
210 const auto& egl
= gle
->mEgl
;
213 const int interval
= gfx::gfxVars::SwapIntervalEGL() ? 1 : 0;
214 egl
->fSwapInterval(interval
);
216 RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE
);
223 bool RenderCompositorEGL::IsPaused() { return mEGLSurface
== EGL_NO_SURFACE
; }
225 bool RenderCompositorEGL::MakeCurrent() {
226 const auto& gle
= gl::GLContextEGL::Cast(gl());
228 gle
->SetEGLSurfaceOverride(mEGLSurface
);
229 bool ok
= gl()->MakeCurrent();
230 if (!gl()->IsGLES() && ok
&& mEGLSurface
!= EGL_NO_SURFACE
) {
231 // If we successfully made a surface current, set the draw buffer
232 // appropriately. It's not well-defined by the EGL spec whether
233 // eglMakeCurrent should do this automatically. See bug 1646135.
234 gl()->fDrawBuffer(gl()->IsDoubleBuffered() ? LOCAL_GL_BACK
240 void RenderCompositorEGL::DestroyEGLSurface() {
241 const auto& gle
= gl::GLContextEGL::Cast(gl());
242 const auto& egl
= gle
->mEgl
;
244 // Release EGLSurface of back buffer before calling ResizeBuffers().
246 gle
->SetEGLSurfaceOverride(EGL_NO_SURFACE
);
247 gl::GLContextEGL::DestroySurface(*egl
, mEGLSurface
);
248 mEGLSurface
= nullptr;
252 ipc::FileDescriptor
RenderCompositorEGL::GetAndResetReleaseFence() {
253 #ifdef MOZ_WIDGET_ANDROID
254 MOZ_ASSERT(!layers::AndroidHardwareBufferApi::Get() ||
255 mReleaseFenceFd
.IsValid());
256 return std::move(mReleaseFenceFd
);
258 return ipc::FileDescriptor();
262 LayoutDeviceIntSize
RenderCompositorEGL::GetBufferSize() {
263 return mWidget
->GetClientSize();
266 bool RenderCompositorEGL::UsePartialPresent() {
267 return gfx::gfxVars::WebRenderMaxPartialPresentRects() > 0;
270 bool RenderCompositorEGL::RequestFullRender() { return false; }
272 uint32_t RenderCompositorEGL::GetMaxPartialPresentRects() {
273 return gfx::gfxVars::WebRenderMaxPartialPresentRects();
276 bool RenderCompositorEGL::ShouldDrawPreviousPartialPresentRegions() {
280 size_t RenderCompositorEGL::GetBufferAge() const {
282 gfx_webrender_allow_partial_present_buffer_age_AtStartup()) {
285 return gl()->GetBufferAge();
288 void RenderCompositorEGL::SetBufferDamageRegion(const wr::DeviceIntRect
* aRects
,
290 const auto& gle
= gl::GLContextEGL::Cast(gl());
291 const auto& egl
= gle
->mEgl
;
292 if (gle
->HasKhrPartialUpdate() &&
293 StaticPrefs::gfx_webrender_allow_partial_present_buffer_age_AtStartup()) {
294 std::vector
<EGLint
> rects
;
295 rects
.reserve(4 * aNumRects
);
296 const auto bufferSize
= GetBufferSize();
297 for (size_t i
= 0; i
< aNumRects
; i
++) {
299 std::max(0, std::min(bufferSize
.width
, aRects
[i
].min
.x
));
301 std::max(0, std::min(bufferSize
.height
, aRects
[i
].min
.y
));
304 std::min(bufferSize
.width
, std::max(0, aRects
[i
].max
.x
));
306 std::min(bufferSize
.height
, std::max(0, aRects
[i
].max
.y
));
308 const auto width
= right
- left
;
309 const auto height
= bottom
- top
;
311 rects
.push_back(left
);
312 rects
.push_back(bufferSize
.height
- bottom
);
313 rects
.push_back(width
);
314 rects
.push_back(height
);
317 egl
->fSetDamageRegion(mEGLSurface
, rects
.data(), rects
.size() / 4);
318 if (ret
== LOCAL_EGL_FALSE
) {
319 const auto err
= egl
->mLib
->fGetError();
320 gfxCriticalError() << "Error in eglSetDamageRegion: " << gfx::hexa(err
);
325 } // namespace mozilla::wr