Backed out 4 changesets (bug 1879154) for causing bustage on nsUserCharacteristics...
[gecko.git] / gfx / webrender_bindings / RenderCompositorOGLSWGL.cpp
blobd2d0cce0167603c4483e4119e5fb8e09b01f49c8
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
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file,
7 * You can obtain one at http://mozilla.org/MPL/2.0/. */
9 #include "RenderCompositorOGLSWGL.h"
11 #include "GLContext.h"
12 #include "GLContextEGL.h"
13 #include "mozilla/layers/BuildConstants.h"
14 #include "mozilla/layers/CompositorOGL.h"
15 #include "mozilla/layers/Effects.h"
16 #include "mozilla/layers/TextureHostOGL.h"
17 #include "mozilla/widget/CompositorWidget.h"
18 #include "OGLShaderProgram.h"
20 #ifdef MOZ_WIDGET_ANDROID
21 # include "mozilla/java/GeckoSurfaceTextureWrappers.h"
22 # include "mozilla/layers/AndroidHardwareBuffer.h"
23 # include "mozilla/webrender/RenderAndroidHardwareBufferTextureHost.h"
24 # include "mozilla/webrender/RenderAndroidSurfaceTextureHost.h"
25 # include "mozilla/widget/AndroidCompositorWidget.h"
26 # include <android/native_window.h>
27 # include <android/native_window_jni.h>
28 #endif
30 #ifdef MOZ_WIDGET_GTK
31 # include "mozilla/widget/GtkCompositorWidget.h"
32 # include <gdk/gdk.h>
33 # ifdef MOZ_X11
34 # include <gdk/gdkx.h>
35 # endif
36 #endif
38 namespace mozilla {
39 using namespace layers;
40 using namespace gfx;
41 namespace wr {
43 extern LazyLogModule gRenderThreadLog;
44 #define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
46 UniquePtr<RenderCompositor> RenderCompositorOGLSWGL::Create(
47 const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError) {
48 if (!aWidget->GetCompositorOptions().AllowSoftwareWebRenderOGL()) {
49 return nullptr;
52 RefPtr<Compositor> compositor;
53 #ifdef MOZ_WIDGET_ANDROID
54 RefPtr<gl::GLContext> context =
55 RenderThread::Get()->SingletonGLForCompositorOGL();
56 if (!context) {
57 gfxCriticalNote << "SingletonGL does not exist for SWGL";
58 return nullptr;
60 auto programs = RenderThread::Get()->GetProgramsForCompositorOGL();
61 if (!programs) {
62 gfxCriticalNote << "Failed to get Programs for CompositorOGL for SWGL";
63 return nullptr;
66 nsCString log;
67 RefPtr<CompositorOGL> compositorOGL;
68 compositorOGL = new CompositorOGL(aWidget, /* aSurfaceWidth */ -1,
69 /* aSurfaceHeight */ -1,
70 /* aUseExternalSurfaceSize */ true);
71 if (!compositorOGL->Initialize(context, programs, &log)) {
72 gfxCriticalNote << "Failed to initialize CompositorOGL for SWGL: "
73 << log.get();
74 return nullptr;
76 compositor = compositorOGL;
77 #elif defined(MOZ_WIDGET_GTK)
78 nsCString log;
79 RefPtr<CompositorOGL> compositorOGL;
80 compositorOGL = new CompositorOGL(aWidget);
81 if (!compositorOGL->Initialize(&log)) {
82 gfxCriticalNote << "Failed to initialize CompositorOGL for SWGL: "
83 << log.get();
84 return nullptr;
86 compositor = compositorOGL;
87 #endif
89 if (!compositor) {
90 return nullptr;
93 void* ctx = wr_swgl_create_context();
94 if (!ctx) {
95 gfxCriticalNote << "Failed SWGL context creation for WebRender";
96 return nullptr;
99 return MakeUnique<RenderCompositorOGLSWGL>(compositor, aWidget, ctx);
102 RenderCompositorOGLSWGL::RenderCompositorOGLSWGL(
103 Compositor* aCompositor, const RefPtr<widget::CompositorWidget>& aWidget,
104 void* aContext)
105 : RenderCompositorLayersSWGL(aCompositor, aWidget, aContext) {
106 LOG("RenderCompositorOGLSWGL::RenderCompositorOGLSWGL()");
109 RenderCompositorOGLSWGL::~RenderCompositorOGLSWGL() {
110 LOG("RRenderCompositorOGLSWGL::~RenderCompositorOGLSWGL()");
111 #ifdef MOZ_WIDGET_ANDROID
112 java::GeckoSurfaceTexture::DestroyUnused((int64_t)GetGLContext());
113 DestroyEGLSurface();
114 #endif
117 gl::GLContext* RenderCompositorOGLSWGL::GetGLContext() {
118 return mCompositor->AsCompositorOGL()->gl();
121 bool RenderCompositorOGLSWGL::MakeCurrent() {
122 GetGLContext()->MakeCurrent();
123 #ifdef MOZ_WIDGET_ANDROID
124 if (GetGLContext()->GetContextType() == gl::GLContextType::EGL) {
125 gl::GLContextEGL::Cast(GetGLContext())->SetEGLSurfaceOverride(mEGLSurface);
127 #endif
128 RenderCompositorLayersSWGL::MakeCurrent();
129 return true;
132 EGLSurface RenderCompositorOGLSWGL::CreateEGLSurface() {
133 MOZ_ASSERT(GetGLContext()->GetContextType() == gl::GLContextType::EGL);
135 EGLSurface surface = EGL_NO_SURFACE;
136 surface = gl::GLContextEGL::CreateEGLSurfaceForCompositorWidget(
137 mWidget, gl::GLContextEGL::Cast(GetGLContext())->mSurfaceConfig);
138 if (surface == EGL_NO_SURFACE) {
139 const auto* renderThread = RenderThread::Get();
140 gfxCriticalNote << "Failed to create EGLSurface. "
141 << renderThread->RendererCount() << " renderers, "
142 << renderThread->ActiveRendererCount() << " active.";
145 // The subsequent render after creating a new surface must be a full render.
146 mFullRender = true;
148 return surface;
151 void RenderCompositorOGLSWGL::DestroyEGLSurface() {
152 MOZ_ASSERT(GetGLContext()->GetContextType() == gl::GLContextType::EGL);
154 const auto& gle = gl::GLContextEGL::Cast(GetGLContext());
155 const auto& egl = gle->mEgl;
157 // Release EGLSurface of back buffer before calling ResizeBuffers().
158 if (mEGLSurface) {
159 gle->SetEGLSurfaceOverride(EGL_NO_SURFACE);
160 gl::GLContextEGL::DestroySurface(*egl, mEGLSurface);
161 mEGLSurface = EGL_NO_SURFACE;
165 bool RenderCompositorOGLSWGL::BeginFrame() {
166 MOZ_ASSERT(!mInFrame);
167 RenderCompositorLayersSWGL::BeginFrame();
169 #ifdef MOZ_WIDGET_ANDROID
170 java::GeckoSurfaceTexture::DestroyUnused((int64_t)GetGLContext());
171 GetGLContext()
172 ->MakeCurrent(); // DestroyUnused can change the current context!
173 #endif
175 return true;
178 RenderedFrameId RenderCompositorOGLSWGL::EndFrame(
179 const nsTArray<DeviceIntRect>& aDirtyRects) {
180 mFullRender = false;
182 return RenderCompositorLayersSWGL::EndFrame(aDirtyRects);
185 void RenderCompositorOGLSWGL::HandleExternalImage(
186 RenderTextureHost* aExternalImage, FrameSurface& aFrameSurface) {
187 MOZ_ASSERT(aExternalImage);
189 #ifdef MOZ_WIDGET_ANDROID
190 GLenum target =
191 LOCAL_GL_TEXTURE_EXTERNAL; // This is required by SurfaceTexture
192 GLenum wrapMode = LOCAL_GL_CLAMP_TO_EDGE;
194 if (auto* host = aExternalImage->AsRenderAndroidSurfaceTextureHost()) {
195 host->UpdateTexImageIfNecessary();
197 // We need to hold the texture source separately from the effect,
198 // since the effect doesn't hold a strong reference.
199 RefPtr<SurfaceTextureSource> layer = new SurfaceTextureSource(
200 (TextureSourceProvider*)mCompositor, host->mSurfTex, host->mFormat,
201 target, wrapMode, host->mSize, host->mTransformOverride);
202 RefPtr<TexturedEffect> texturedEffect =
203 CreateTexturedEffect(host->mFormat, layer, aFrameSurface.mFilter,
204 /* isAlphaPremultiplied */ true);
206 gfx::Rect drawRect(0, 0, host->mSize.width, host->mSize.height);
208 EffectChain effect;
209 effect.mPrimaryEffect = texturedEffect;
210 mCompositor->DrawQuad(drawRect, aFrameSurface.mClipRect, effect, 1.0,
211 aFrameSurface.mTransform, drawRect);
212 } else if (auto* host =
213 aExternalImage->AsRenderAndroidHardwareBufferTextureHost()) {
214 // We need to hold the texture source separately from the effect,
215 // since the effect doesn't hold a strong reference.
216 RefPtr<AndroidHardwareBufferTextureSource> layer =
217 new AndroidHardwareBufferTextureSource(
218 (TextureSourceProvider*)mCompositor,
219 host->GetAndroidHardwareBuffer(),
220 host->GetAndroidHardwareBuffer()->mFormat, target, wrapMode,
221 host->GetSize());
222 RefPtr<TexturedEffect> texturedEffect = CreateTexturedEffect(
223 host->GetAndroidHardwareBuffer()->mFormat, layer, aFrameSurface.mFilter,
224 /* isAlphaPremultiplied */ true);
226 gfx::Rect drawRect(0, 0, host->GetSize().width, host->GetSize().height);
228 EffectChain effect;
229 effect.mPrimaryEffect = texturedEffect;
230 mCompositor->DrawQuad(drawRect, aFrameSurface.mClipRect, effect, 1.0,
231 aFrameSurface.mTransform, drawRect);
233 #endif
236 void RenderCompositorOGLSWGL::GetCompositorCapabilities(
237 CompositorCapabilities* aCaps) {
238 RenderCompositor::GetCompositorCapabilities(aCaps);
240 // max_update_rects are not yet handled properly
241 aCaps->max_update_rects = 0;
244 bool RenderCompositorOGLSWGL::RequestFullRender() { return mFullRender; }
246 void RenderCompositorOGLSWGL::Pause() {
247 #ifdef MOZ_WIDGET_ANDROID
248 DestroyEGLSurface();
249 #elif defined(MOZ_WIDGET_GTK)
250 mCompositor->Pause();
251 #endif
254 bool RenderCompositorOGLSWGL::Resume() {
255 #ifdef MOZ_WIDGET_ANDROID
256 // Destroy EGLSurface if it exists.
257 DestroyEGLSurface();
259 auto size = GetBufferSize();
260 GLint maxTextureSize = 0;
261 GetGLContext()->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE,
262 (GLint*)&maxTextureSize);
264 // When window size is too big, hardware buffer allocation could fail.
265 if (maxTextureSize < size.width || maxTextureSize < size.height) {
266 gfxCriticalNote << "Too big ANativeWindow size(" << size.width << ", "
267 << size.height << ") MaxTextureSize " << maxTextureSize;
268 return false;
271 mEGLSurface = CreateEGLSurface();
272 if (mEGLSurface == EGL_NO_SURFACE) {
273 // Often when we fail to create an EGL surface it is because the
274 // Java Surface we have been provided is invalid. Therefore the on
275 // the first occurence we don't raise a WebRenderError and instead
276 // just return failure. This allows the widget a chance to request
277 // a new Java Surface. On subsequent failures, raising the
278 // WebRenderError will result in the compositor being recreated,
279 // falling back through webrender configurations, and eventually
280 // crashing if we still do not succeed.
281 if (!mHandlingNewSurfaceError) {
282 mHandlingNewSurfaceError = true;
283 } else {
284 RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE);
286 return false;
288 mHandlingNewSurfaceError = false;
290 gl::GLContextEGL::Cast(GetGLContext())->SetEGLSurfaceOverride(mEGLSurface);
291 mCompositor->SetDestinationSurfaceSize(size.ToUnknownSize());
292 #elif defined(MOZ_WIDGET_GTK)
293 bool resumed = mCompositor->Resume();
294 if (!resumed) {
295 RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE);
296 return false;
298 #endif
299 return true;
302 bool RenderCompositorOGLSWGL::IsPaused() {
303 #ifdef MOZ_WIDGET_ANDROID
304 return mEGLSurface == EGL_NO_SURFACE;
305 #endif
306 return false;
309 LayoutDeviceIntSize RenderCompositorOGLSWGL::GetBufferSize() {
310 return mWidget->GetClientSize();
313 UniquePtr<RenderCompositorLayersSWGL::Tile>
314 RenderCompositorOGLSWGL::DoCreateTile(Surface* aSurface) {
315 auto source = MakeRefPtr<TextureImageTextureSourceOGL>(
316 mCompositor->AsCompositorOGL(), layers::TextureFlags::NO_FLAGS);
318 return MakeUnique<TileOGL>(std::move(source), aSurface->TileSize());
321 bool RenderCompositorOGLSWGL::MaybeReadback(
322 const gfx::IntSize& aReadbackSize, const wr::ImageFormat& aReadbackFormat,
323 const Range<uint8_t>& aReadbackBuffer, bool* aNeedsYFlip) {
324 #ifdef MOZ_WIDGET_ANDROID
325 MOZ_ASSERT(aReadbackFormat == wr::ImageFormat::RGBA8);
326 const GLenum format = LOCAL_GL_RGBA;
327 #else
328 MOZ_ASSERT(aReadbackFormat == wr::ImageFormat::BGRA8);
329 const GLenum format = LOCAL_GL_BGRA;
330 #endif
332 GetGLContext()->fReadPixels(0, 0, aReadbackSize.width, aReadbackSize.height,
333 format, LOCAL_GL_UNSIGNED_BYTE,
334 &aReadbackBuffer[0]);
336 if (aNeedsYFlip) {
337 *aNeedsYFlip = true;
340 return true;
343 // This is a DataSourceSurface that represents a 0-based PBO for GLTextureImage.
344 class PBOUnpackSurface : public gfx::DataSourceSurface {
345 public:
346 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PBOUnpackSurface, override)
348 explicit PBOUnpackSurface(const gfx::IntSize& aSize) : mSize(aSize) {}
350 uint8_t* GetData() override { return nullptr; }
351 int32_t Stride() override { return mSize.width * sizeof(uint32_t); }
352 gfx::SurfaceType GetType() const override {
353 return gfx::SurfaceType::DATA_ALIGNED;
355 gfx::IntSize GetSize() const override { return mSize; }
356 gfx::SurfaceFormat GetFormat() const override {
357 return gfx::SurfaceFormat::B8G8R8A8;
360 // PBO offsets need to start from a 0 address, but DataSourceSurface::Map
361 // checks for failure by comparing the address against nullptr. Override Map
362 // to work around this.
363 bool Map(MapType, MappedSurface* aMappedSurface) override {
364 aMappedSurface->mData = GetData();
365 aMappedSurface->mStride = Stride();
366 return true;
369 void Unmap() override {}
371 private:
372 gfx::IntSize mSize;
375 RenderCompositorOGLSWGL::TileOGL::TileOGL(
376 RefPtr<layers::TextureImageTextureSourceOGL>&& aTexture,
377 const gfx::IntSize& aSize)
378 : mTexture(aTexture) {
379 auto* gl = mTexture->gl();
380 if (gl && gl->HasPBOState() && gl->MakeCurrent()) {
381 mSurface = new PBOUnpackSurface(aSize);
382 // Create a PBO large enough to encompass any valid rects within the tile.
383 gl->fGenBuffers(1, &mPBO);
384 gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, mPBO);
385 gl->fBufferData(LOCAL_GL_PIXEL_UNPACK_BUFFER,
386 mSurface->Stride() * aSize.height, nullptr,
387 LOCAL_GL_DYNAMIC_DRAW);
388 gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0);
389 } else {
390 // Couldn't allocate a PBO, so just use a memory surface instead.
391 mSurface = gfx::Factory::CreateDataSourceSurface(
392 aSize, gfx::SurfaceFormat::B8G8R8A8);
396 RenderCompositorOGLSWGL::TileOGL::~TileOGL() {
397 if (mPBO) {
398 auto* gl = mTexture->gl();
399 if (gl && gl->MakeCurrent()) {
400 gl->fDeleteBuffers(1, &mPBO);
401 mPBO = 0;
406 layers::DataTextureSource*
407 RenderCompositorOGLSWGL::TileOGL::GetTextureSource() {
408 return mTexture.get();
411 bool RenderCompositorOGLSWGL::TileOGL::Map(wr::DeviceIntRect aDirtyRect,
412 wr::DeviceIntRect aValidRect,
413 void** aData, int32_t* aStride) {
414 if (mPBO) {
415 auto* gl = mTexture->gl();
416 if (!gl) {
417 return false;
419 // Map the PBO, but only within the range of the buffer that spans from the
420 // linear start offset to the linear end offset. Since we don't care about
421 // the previous contents of the buffer, we can just tell OpenGL to
422 // invalidate the entire buffer, even though we're only mapping a sub-range.
423 gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, mPBO);
424 size_t stride = mSurface->Stride();
425 size_t offset =
426 stride * aValidRect.min.y + aValidRect.min.x * sizeof(uint32_t);
427 size_t length = stride * (aValidRect.height() - 1) +
428 (aValidRect.width()) * sizeof(uint32_t);
429 void* data = gl->fMapBufferRange(
430 LOCAL_GL_PIXEL_UNPACK_BUFFER, offset, length,
431 LOCAL_GL_MAP_WRITE_BIT | LOCAL_GL_MAP_INVALIDATE_BUFFER_BIT);
432 gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0);
433 if (!data) {
434 return false;
436 *aData = data;
437 *aStride = stride;
438 } else {
439 // No PBO is available, so just directly write to the memory surface.
440 gfx::DataSourceSurface::MappedSurface map;
441 if (!mSurface->Map(gfx::DataSourceSurface::READ_WRITE, &map)) {
442 return false;
444 // Verify that we're not somehow using a PBOUnpackSurface.
445 MOZ_ASSERT(map.mData != nullptr);
446 // glTex(Sub)Image on ES doesn't support arbitrary strides without
447 // the EXT_unpack_subimage extension. To avoid needing to make a
448 // copy of the data we'll always draw it with stride = bpp*width
449 // unless we're uploading the entire texture.
450 if (!mTexture->IsValid()) {
451 // If we don't have a texture we need to position our
452 // data in the correct spot because we're going to upload
453 // the entire surface
454 *aData = map.mData + aValidRect.min.y * map.mStride +
455 aValidRect.min.x * sizeof(uint32_t);
457 *aStride = map.mStride;
458 mSubSurface = nullptr;
459 } else {
460 // Otherwise, we can just use the top left as a scratch space
461 *aData = map.mData;
462 *aStride = aDirtyRect.width() * BytesPerPixel(mSurface->GetFormat());
463 mSubSurface = Factory::CreateWrappingDataSourceSurface(
464 (uint8_t*)*aData, *aStride,
465 IntSize(aDirtyRect.width(), aDirtyRect.height()),
466 mSurface->GetFormat());
469 return true;
472 void RenderCompositorOGLSWGL::TileOGL::Unmap(const gfx::IntRect& aDirtyRect) {
473 nsIntRegion dirty(aDirtyRect);
474 if (mPBO) {
475 // If there is a PBO, it must be unmapped before it can be sourced from.
476 // Leave the PBO bound before the call to Update so that the texture uploads
477 // will source from it.
478 auto* gl = mTexture->gl();
479 if (!gl) {
480 return;
482 gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, mPBO);
483 gl->fUnmapBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER);
484 mTexture->Update(mSurface, &dirty);
485 gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0);
486 } else {
487 if (mSubSurface) {
488 mSurface->Unmap();
489 // Our subsurface has a stride = aDirtyRect.width
490 // We use a negative offset to move it to match
491 // the dirty rect's top-left. These two offsets
492 // will cancel each other out by the time we reach
493 // TexSubImage.
494 IntPoint srcOffset = {0, 0};
495 IntPoint dstOffset = aDirtyRect.TopLeft();
496 // adjust the dirty region to be relative to the dstOffset
497 dirty.MoveBy(-dstOffset);
498 mTexture->Update(mSubSurface, &dirty, &srcOffset, &dstOffset);
499 mSubSurface = nullptr;
500 } else {
501 mSurface->Unmap();
502 mTexture->Update(mSurface, &dirty);
507 } // namespace wr
508 } // namespace mozilla