RenderCompositorEGL.cpp (11025B)
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/. */ 6 7 #include "RenderCompositorEGL.h" 8 9 #include "GLContext.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" 19 20 #ifdef MOZ_WIDGET_GTK 21 # include "mozilla/WidgetUtilsGtk.h" 22 # include "mozilla/widget/GtkCompositorWidget.h" 23 #endif 24 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> 31 #endif 32 33 namespace mozilla::wr { 34 35 extern LazyLogModule gRenderThreadLog; 36 #define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__)) 37 38 /* static */ 39 UniquePtr<RenderCompositor> RenderCompositorEGL::Create( 40 const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError) { 41 if (kIsLinux && !gfx::gfxVars::UseEGL()) { 42 return nullptr; 43 } 44 RefPtr<gl::GLContext> gl = RenderThread::Get()->SingletonGL(aError); 45 if (!gl) { 46 if (aError.IsEmpty()) { 47 aError.Assign("RcANGLE(no shared GL)"_ns); 48 } else { 49 aError.Append("(Create)"_ns); 50 } 51 return nullptr; 52 } 53 return MakeUnique<RenderCompositorEGL>(aWidget, std::move(gl)); 54 } 55 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."; 65 } 66 return surface; 67 } 68 69 RenderCompositorEGL::RenderCompositorEGL( 70 const RefPtr<widget::CompositorWidget>& aWidget, 71 RefPtr<gl::GLContext>&& aGL) 72 : RenderCompositor(aWidget), mGL(aGL), mEGLSurface(EGL_NO_SURFACE) { 73 MOZ_ASSERT(mGL); 74 LOG("RenderCompositorEGL::RenderCompositorEGL()"); 75 } 76 77 RenderCompositorEGL::~RenderCompositorEGL() { 78 LOG("RenderCompositorEGL::~RenderCompositorEGL()"); 79 #ifdef MOZ_WIDGET_ANDROID 80 java::GeckoSurfaceTexture::DestroyUnused((int64_t)gl()); 81 #endif 82 DestroyEGLSurface(); 83 } 84 85 bool RenderCompositorEGL::BeginFrame() { 86 if (kIsLinux && mEGLSurface == EGL_NO_SURFACE) { 87 gfxCriticalNote 88 << "We don't have EGLSurface to draw into. Called too early?"; 89 return false; 90 } 91 #ifdef MOZ_WAYLAND 92 if (auto* gtkWidget = mWidget->AsGTK()) { 93 gtkWidget->SetEGLNativeWindowSize(GetBufferSize()); 94 } 95 #endif 96 if (!MakeCurrent()) { 97 gfxCriticalNote << "Failed to make render context current, can't draw."; 98 return false; 99 } 100 101 #ifdef MOZ_WIDGET_ANDROID 102 java::GeckoSurfaceTexture::DestroyUnused((int64_t)gl()); 103 gl()->MakeCurrent(); // DestroyUnused can change the current context! 104 #endif 105 106 return true; 107 } 108 109 RenderedFrameId RenderCompositorEGL::EndFrame( 110 const nsTArray<DeviceIntRect>& aDirtyRects) { 111 #ifdef MOZ_WIDGET_ANDROID 112 const auto& gle = gl::GLContextEGL::Cast(gl()); 113 const auto& egl = gle->mEgl; 114 115 EGLSync sync = nullptr; 116 if (layers::AndroidHardwareBufferApi::Get()) { 117 sync = egl->fCreateSync(LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); 118 } 119 if (sync) { 120 int fenceFd = egl->fDupNativeFenceFDANDROID(sync); 121 if (fenceFd >= 0) { 122 mReleaseFence = new layers::FenceFileHandle(UniqueFileHandle(fenceFd)); 123 } 124 egl->fDestroySync(sync); 125 sync = nullptr; 126 } 127 #endif 128 129 RenderedFrameId frameId = GetNextRenderFrameId(); 130 #ifdef MOZ_WIDGET_GTK 131 if (mWidget->IsHidden()) { 132 return frameId; 133 } 134 #endif 135 if (mEGLSurface != EGL_NO_SURFACE && aDirtyRects.Length() > 0) { 136 gfx::IntRegion bufferInvalid; 137 const auto bufferSize = GetBufferSize(); 138 for (const DeviceIntRect& rect : aDirtyRects) { 139 const auto left = std::clamp(rect.min.x, 0, bufferSize.width); 140 const auto top = std::clamp(rect.min.y, 0, bufferSize.height); 141 142 const auto right = std::clamp(rect.max.x, 0, bufferSize.width); 143 const auto bottom = std::clamp(rect.max.y, 0, bufferSize.height); 144 145 const auto width = right - left; 146 const auto height = bottom - top; 147 148 bufferInvalid.OrWith( 149 gfx::IntRect(left, (GetBufferSize().height - bottom), width, height)); 150 } 151 gl()->SetDamage(bufferInvalid); 152 } 153 154 #ifdef MOZ_WIDGET_GTK 155 // Rendering on Wayland has to be atomic (buffer attach + commit) and 156 // wayland surface is also used by main thread so lock it before 157 // we paint at SwapBuffers(). 158 UniquePtr<widget::WaylandSurfaceLock> lock; 159 if (auto* gtkWidget = mWidget->AsGTK()) { 160 lock = gtkWidget->LockSurface(); 161 } 162 #endif 163 gl()->SwapBuffers(); 164 return frameId; 165 } 166 167 void RenderCompositorEGL::Pause() { DestroyEGLSurface(); } 168 169 bool RenderCompositorEGL::Resume() { 170 if (kIsAndroid) { 171 // Destroy EGLSurface if it exists. 172 DestroyEGLSurface(); 173 174 auto size = GetBufferSize(); 175 GLint maxTextureSize = 0; 176 gl()->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, (GLint*)&maxTextureSize); 177 178 // When window size is too big, hardware buffer allocation could fail. 179 if (maxTextureSize < size.width || maxTextureSize < size.height) { 180 gfxCriticalNote << "Too big ANativeWindow size(" << size.width << ", " 181 << size.height << ") MaxTextureSize " << maxTextureSize; 182 return false; 183 } 184 185 mEGLSurface = CreateEGLSurface(); 186 if (mEGLSurface == EGL_NO_SURFACE) { 187 // Often when we fail to create an EGL surface it is because the Java 188 // Surface we have been provided is invalid. Therefore the on the first 189 // occurence we don't raise a WebRenderError and instead just return 190 // failure. This allows the widget a chance to request a new Java 191 // Surface. On subsequent failures, raising the WebRenderError will 192 // result in the compositor being recreated, falling back through 193 // webrender configurations, and eventually crashing if we still do not 194 // succeed. 195 if (!mHandlingNewSurfaceError) { 196 mHandlingNewSurfaceError = true; 197 } else { 198 RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE); 199 } 200 return false; 201 } 202 mHandlingNewSurfaceError = false; 203 204 gl::GLContextEGL::Cast(gl())->SetEGLSurfaceOverride(mEGLSurface); 205 } else if (kIsLinux) { 206 // Destroy EGLSurface if it exists and create a new one. We will set the 207 // swap interval after MakeCurrent() has been called. 208 DestroyEGLSurface(); 209 mEGLSurface = CreateEGLSurface(); 210 if (mEGLSurface != EGL_NO_SURFACE) { 211 // We have a new EGL surface, which on wayland needs to be configured for 212 // non-blocking buffer swaps. We need MakeCurrent() to set our current EGL 213 // context before we call eglSwapInterval, which is why we do it here 214 // rather than where the surface was created. 215 const auto& gle = gl::GLContextEGL::Cast(gl()); 216 const auto& egl = gle->mEgl; 217 MakeCurrent(); 218 219 const int interval = gfx::gfxVars::SwapIntervalEGL() ? 1 : 0; 220 egl->fSwapInterval(interval); 221 } else { 222 RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE); 223 return false; 224 } 225 } 226 return true; 227 } 228 229 bool RenderCompositorEGL::IsPaused() { return mEGLSurface == EGL_NO_SURFACE; } 230 231 bool RenderCompositorEGL::MakeCurrent() { 232 const auto& gle = gl::GLContextEGL::Cast(gl()); 233 234 gle->SetEGLSurfaceOverride(mEGLSurface); 235 bool ok = gl()->MakeCurrent(); 236 if (!gl()->IsGLES() && ok && mEGLSurface != EGL_NO_SURFACE) { 237 // If we successfully made a surface current, set the draw buffer 238 // appropriately. It's not well-defined by the EGL spec whether 239 // eglMakeCurrent should do this automatically. See bug 1646135. 240 gl()->fDrawBuffer(gl()->IsDoubleBuffered() ? LOCAL_GL_BACK 241 : LOCAL_GL_FRONT); 242 } 243 return ok; 244 } 245 246 void RenderCompositorEGL::DestroyEGLSurface() { 247 const auto& gle = gl::GLContextEGL::Cast(gl()); 248 const auto& egl = gle->mEgl; 249 250 // Release EGLSurface of back buffer before calling ResizeBuffers(). 251 if (mEGLSurface) { 252 gle->SetEGLSurfaceOverride(EGL_NO_SURFACE); 253 gl::GLContextEGL::DestroySurface(*egl, mEGLSurface); 254 mEGLSurface = nullptr; 255 } 256 } 257 258 RefPtr<layers::Fence> RenderCompositorEGL::GetAndResetReleaseFence() { 259 #ifdef MOZ_WIDGET_ANDROID 260 MOZ_ASSERT(!layers::AndroidHardwareBufferApi::Get() || mReleaseFence); 261 return mReleaseFence.forget(); 262 #else 263 return nullptr; 264 #endif 265 } 266 267 LayoutDeviceIntSize RenderCompositorEGL::GetBufferSize() { 268 return mWidget->GetClientSize(); 269 } 270 271 bool RenderCompositorEGL::UsePartialPresent() { 272 return gfx::gfxVars::WebRenderMaxPartialPresentRects() > 0; 273 } 274 275 bool RenderCompositorEGL::RequestFullRender() { return false; } 276 277 uint32_t RenderCompositorEGL::GetMaxPartialPresentRects() { 278 return gfx::gfxVars::WebRenderMaxPartialPresentRects(); 279 } 280 281 bool RenderCompositorEGL::ShouldDrawPreviousPartialPresentRegions() { 282 return true; 283 } 284 285 size_t RenderCompositorEGL::GetBufferAge() const { 286 if (!StaticPrefs:: 287 gfx_webrender_allow_partial_present_buffer_age_AtStartup()) { 288 return 0; 289 } 290 return gl()->GetBufferAge(); 291 } 292 293 void RenderCompositorEGL::SetBufferDamageRegion(const wr::DeviceIntRect* aRects, 294 size_t aNumRects) { 295 const auto& gle = gl::GLContextEGL::Cast(gl()); 296 const auto& egl = gle->mEgl; 297 if (gle->HasKhrPartialUpdate() && 298 StaticPrefs::gfx_webrender_allow_partial_present_buffer_age_AtStartup()) { 299 std::vector<EGLint> rects; 300 rects.reserve(4 * aNumRects); 301 const auto bufferSize = GetBufferSize(); 302 for (size_t i = 0; i < aNumRects; i++) { 303 const auto left = std::clamp(aRects[i].min.x, 0, bufferSize.width); 304 const auto top = std::clamp(aRects[i].min.y, 0, bufferSize.height); 305 306 const auto right = std::clamp(aRects[i].max.x, 0, bufferSize.width); 307 const auto bottom = std::clamp(aRects[i].max.y, 0, bufferSize.height); 308 309 const auto width = right - left; 310 const auto height = bottom - top; 311 312 rects.push_back(left); 313 rects.push_back(bufferSize.height - bottom); 314 rects.push_back(width); 315 rects.push_back(height); 316 } 317 const auto ret = 318 egl->fSetDamageRegion(mEGLSurface, rects.data(), rects.size() / 4); 319 if (ret == LOCAL_EGL_FALSE) { 320 const auto err = egl->mLib->fGetError(); 321 gfxCriticalError() << "Error in eglSetDamageRegion: " << gfx::hexa(err); 322 } 323 } 324 } 325 326 } // namespace mozilla::wr