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
{
36 UniquePtr
<RenderCompositor
> RenderCompositorEGL::Create(
37 RefPtr
<widget::CompositorWidget
> aWidget
, nsACString
& aError
) {
39 if (!mozilla::widget::GdkIsWaylandDisplay()) {
43 if (!RenderThread::Get()->SingletonGL()) {
44 gfxCriticalNote
<< "Failed to get shared GL context";
47 return MakeUnique
<RenderCompositorEGL
>(aWidget
);
50 EGLSurface
RenderCompositorEGL::CreateEGLSurface() {
51 EGLSurface surface
= EGL_NO_SURFACE
;
52 surface
= gl::GLContextEGL::CreateEGLSurfaceForCompositorWidget(
53 mWidget
, gl::GLContextEGL::Cast(gl())->mConfig
);
54 if (surface
== EGL_NO_SURFACE
) {
55 gfxCriticalNote
<< "Failed to create EGLSurface";
60 RenderCompositorEGL::RenderCompositorEGL(
61 RefPtr
<widget::CompositorWidget
> aWidget
)
62 : RenderCompositor(std::move(aWidget
)), mEGLSurface(EGL_NO_SURFACE
) {}
64 RenderCompositorEGL::~RenderCompositorEGL() {
65 #ifdef MOZ_WIDGET_ANDROID
66 java::GeckoSurfaceTexture::DestroyUnused((int64_t)gl());
71 bool RenderCompositorEGL::BeginFrame() {
73 if (mEGLSurface
== EGL_NO_SURFACE
) {
75 << "We don't have EGLSurface to draw into. Called too early?";
78 if (mWidget
->AsX11()) {
79 mWidget
->AsX11()->SetEGLNativeWindowSize(GetBufferSize());
83 gfxCriticalNote
<< "Failed to make render context current, can't draw.";
87 #ifdef MOZ_WIDGET_ANDROID
88 java::GeckoSurfaceTexture::DestroyUnused((int64_t)gl());
89 gl()->MakeCurrent(); // DestroyUnused can change the current context!
95 RenderedFrameId
RenderCompositorEGL::EndFrame(
96 const nsTArray
<DeviceIntRect
>& aDirtyRects
) {
97 #ifdef MOZ_WIDGET_ANDROID
98 const auto& gle
= gl::GLContextEGL::Cast(gl());
99 const auto& egl
= gle
->mEgl
;
101 EGLSync sync
= nullptr;
102 if (layers::AndroidHardwareBufferApi::Get()) {
103 sync
= egl
->fCreateSync(LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID
, nullptr);
106 int fenceFd
= egl
->fDupNativeFenceFDANDROID(sync
);
108 mReleaseFenceFd
= ipc::FileDescriptor(UniqueFileHandle(fenceFd
));
110 egl
->fDestroySync(sync
);
115 RenderedFrameId frameId
= GetNextRenderFrameId();
116 if (mEGLSurface
!= EGL_NO_SURFACE
&& aDirtyRects
.Length() > 0) {
117 gfx::IntRegion bufferInvalid
;
118 const auto bufferSize
= GetBufferSize();
119 for (const DeviceIntRect
& rect
: aDirtyRects
) {
120 const auto left
= std::max(0, std::min(bufferSize
.width
, rect
.origin
.x
));
121 const auto top
= std::max(0, std::min(bufferSize
.height
, rect
.origin
.y
));
123 const auto right
= std::min(bufferSize
.width
,
124 std::max(0, rect
.origin
.x
+ rect
.size
.width
));
125 const auto bottom
= std::min(
126 bufferSize
.height
, std::max(0, rect
.origin
.y
+ rect
.size
.height
));
128 const auto width
= right
- left
;
129 const auto height
= bottom
- top
;
131 bufferInvalid
.OrWith(
132 gfx::IntRect(left
, (GetBufferSize().height
- bottom
), width
, height
));
134 gl()->SetDamage(bufferInvalid
);
140 void RenderCompositorEGL::Pause() { DestroyEGLSurface(); }
142 bool RenderCompositorEGL::Resume() {
144 // Destroy EGLSurface if it exists.
147 #ifdef MOZ_WIDGET_ANDROID
148 // Query the new surface size as this may have changed. We cannot use
149 // mWidget->GetClientSize() due to a race condition between
150 // nsWindow::Resize() being called and the frame being rendered after the
151 // surface is resized.
152 EGLNativeWindowType window
= mWidget
->AsAndroid()->GetEGLNativeWindow();
153 JNIEnv
* const env
= jni::GetEnvForThread();
154 ANativeWindow
* const nativeWindow
=
155 ANativeWindow_fromSurface(env
, reinterpret_cast<jobject
>(window
));
156 const int32_t width
= ANativeWindow_getWidth(nativeWindow
);
157 const int32_t height
= ANativeWindow_getHeight(nativeWindow
);
159 GLint maxTextureSize
= 0;
160 gl()->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE
, (GLint
*)&maxTextureSize
);
162 // When window size is too big, hardware buffer allocation could fail.
163 if (maxTextureSize
< width
|| maxTextureSize
< height
) {
164 gfxCriticalNote
<< "Too big ANativeWindow size(" << width
<< ", "
165 << height
<< ") MaxTextureSize " << maxTextureSize
;
169 mEGLSurface
= CreateEGLSurface();
170 if (mEGLSurface
== EGL_NO_SURFACE
) {
171 RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE
);
174 gl::GLContextEGL::Cast(gl())->SetEGLSurfaceOverride(mEGLSurface
);
176 mEGLSurfaceSize
= LayoutDeviceIntSize(width
, height
);
177 ANativeWindow_release(nativeWindow
);
178 #endif // MOZ_WIDGET_ANDROID
179 } else if (kIsWayland
) {
180 // Destroy EGLSurface if it exists and create a new one. We will set the
181 // swap interval after MakeCurrent() has been called.
183 mEGLSurface
= CreateEGLSurface();
184 if (mEGLSurface
!= EGL_NO_SURFACE
) {
185 // We have a new EGL surface, which on wayland needs to be configured for
186 // non-blocking buffer swaps. We need MakeCurrent() to set our current EGL
187 // context before we call eglSwapInterval, which is why we do it here
188 // rather than where the surface was created.
189 const auto& gle
= gl::GLContextEGL::Cast(gl());
190 const auto& egl
= gle
->mEgl
;
192 // Make eglSwapBuffers() non-blocking on wayland.
193 egl
->fSwapInterval(0);
195 RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE
);
202 bool RenderCompositorEGL::IsPaused() { return mEGLSurface
== EGL_NO_SURFACE
; }
204 gl::GLContext
* RenderCompositorEGL::gl() const {
205 return RenderThread::Get()->SingletonGL();
208 bool RenderCompositorEGL::MakeCurrent() {
209 gl::GLContextEGL::Cast(gl())->SetEGLSurfaceOverride(mEGLSurface
);
210 return gl()->MakeCurrent();
213 void RenderCompositorEGL::DestroyEGLSurface() {
214 const auto& gle
= gl::GLContextEGL::Cast(gl());
215 const auto& egl
= gle
->mEgl
;
217 // Release EGLSurface of back buffer before calling ResizeBuffers().
219 gle
->SetEGLSurfaceOverride(EGL_NO_SURFACE
);
220 egl
->fDestroySurface(mEGLSurface
);
221 mEGLSurface
= nullptr;
225 ipc::FileDescriptor
RenderCompositorEGL::GetAndResetReleaseFence() {
226 #ifdef MOZ_WIDGET_ANDROID
227 MOZ_ASSERT(!layers::AndroidHardwareBufferApi::Get() ||
228 mReleaseFenceFd
.IsValid());
229 return std::move(mReleaseFenceFd
);
231 return ipc::FileDescriptor();
235 LayoutDeviceIntSize
RenderCompositorEGL::GetBufferSize() {
236 #ifdef MOZ_WIDGET_ANDROID
237 return mEGLSurfaceSize
;
239 return mWidget
->GetClientSize();
243 bool RenderCompositorEGL::UsePartialPresent() {
244 return gfx::gfxVars::WebRenderMaxPartialPresentRects() > 0;
247 bool RenderCompositorEGL::RequestFullRender() { return false; }
249 uint32_t RenderCompositorEGL::GetMaxPartialPresentRects() {
250 return gfx::gfxVars::WebRenderMaxPartialPresentRects();
253 bool RenderCompositorEGL::ShouldDrawPreviousPartialPresentRegions() {
257 size_t RenderCompositorEGL::GetBufferAge() const {
259 gfx_webrender_allow_partial_present_buffer_age_AtStartup()) {
262 return gl()->GetBufferAge();
265 void RenderCompositorEGL::SetBufferDamageRegion(const wr::DeviceIntRect
* aRects
,
267 const auto& gle
= gl::GLContextEGL::Cast(gl());
268 const auto& egl
= gle
->mEgl
;
269 if (gle
->HasKhrPartialUpdate() &&
270 StaticPrefs::gfx_webrender_allow_partial_present_buffer_age_AtStartup()) {
271 std::vector
<EGLint
> rects
;
272 rects
.reserve(4 * aNumRects
);
273 const auto bufferSize
= GetBufferSize();
274 for (size_t i
= 0; i
< aNumRects
; i
++) {
276 std::max(0, std::min(bufferSize
.width
, aRects
[i
].origin
.x
));
278 std::max(0, std::min(bufferSize
.height
, aRects
[i
].origin
.y
));
281 std::min(bufferSize
.width
,
282 std::max(0, aRects
[i
].origin
.x
+ aRects
[i
].size
.width
));
284 std::min(bufferSize
.height
,
285 std::max(0, aRects
[i
].origin
.y
+ aRects
[i
].size
.height
));
287 const auto width
= right
- left
;
288 const auto height
= bottom
- top
;
290 rects
.push_back(left
);
291 rects
.push_back(bufferSize
.height
- bottom
);
292 rects
.push_back(width
);
293 rects
.push_back(height
);
296 egl
->fSetDamageRegion(mEGLSurface
, rects
.data(), rects
.size() / 4);
297 if (ret
== LOCAL_EGL_FALSE
) {
298 const auto err
= egl
->mLib
->fGetError();
299 gfxCriticalError() << "Error in eglSetDamageRegion: " << gfx::hexa(err
);
304 } // namespace mozilla::wr