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
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>
31 # include "mozilla/widget/GtkCompositorWidget.h"
34 # include <gdk/gdkx.h>
39 using namespace layers
;
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()) {
52 RefPtr
<Compositor
> compositor
;
53 #ifdef MOZ_WIDGET_ANDROID
54 RefPtr
<gl::GLContext
> context
=
55 RenderThread::Get()->SingletonGLForCompositorOGL();
57 gfxCriticalNote
<< "SingletonGL does not exist for SWGL";
60 auto programs
= RenderThread::Get()->GetProgramsForCompositorOGL();
62 gfxCriticalNote
<< "Failed to get Programs for CompositorOGL for SWGL";
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: "
76 compositor
= compositorOGL
;
77 #elif defined(MOZ_WIDGET_GTK)
79 RefPtr
<CompositorOGL
> compositorOGL
;
80 compositorOGL
= new CompositorOGL(aWidget
);
81 if (!compositorOGL
->Initialize(&log
)) {
82 gfxCriticalNote
<< "Failed to initialize CompositorOGL for SWGL: "
86 compositor
= compositorOGL
;
93 void* ctx
= wr_swgl_create_context();
95 gfxCriticalNote
<< "Failed SWGL context creation for WebRender";
99 return MakeUnique
<RenderCompositorOGLSWGL
>(compositor
, aWidget
, ctx
);
102 RenderCompositorOGLSWGL::RenderCompositorOGLSWGL(
103 Compositor
* aCompositor
, const RefPtr
<widget::CompositorWidget
>& aWidget
,
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());
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
);
128 RenderCompositorLayersSWGL::MakeCurrent();
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.
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().
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());
172 ->MakeCurrent(); // DestroyUnused can change the current context!
178 RenderedFrameId
RenderCompositorOGLSWGL::EndFrame(
179 const nsTArray
<DeviceIntRect
>& aDirtyRects
) {
182 return RenderCompositorLayersSWGL::EndFrame(aDirtyRects
);
185 void RenderCompositorOGLSWGL::HandleExternalImage(
186 RenderTextureHost
* aExternalImage
, FrameSurface
& aFrameSurface
) {
187 MOZ_ASSERT(aExternalImage
);
189 #ifdef MOZ_WIDGET_ANDROID
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
);
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
,
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
);
229 effect
.mPrimaryEffect
= texturedEffect
;
230 mCompositor
->DrawQuad(drawRect
, aFrameSurface
.mClipRect
, effect
, 1.0,
231 aFrameSurface
.mTransform
, drawRect
);
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
249 #elif defined(MOZ_WIDGET_GTK)
250 mCompositor
->Pause();
254 bool RenderCompositorOGLSWGL::Resume() {
255 #ifdef MOZ_WIDGET_ANDROID
256 // Destroy EGLSurface if it exists.
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
;
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;
284 RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE
);
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();
295 RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE
);
302 bool RenderCompositorOGLSWGL::IsPaused() {
303 #ifdef MOZ_WIDGET_ANDROID
304 return mEGLSurface
== EGL_NO_SURFACE
;
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
;
328 MOZ_ASSERT(aReadbackFormat
== wr::ImageFormat::BGRA8
);
329 const GLenum format
= LOCAL_GL_BGRA
;
332 GetGLContext()->fReadPixels(0, 0, aReadbackSize
.width
, aReadbackSize
.height
,
333 format
, LOCAL_GL_UNSIGNED_BYTE
,
334 &aReadbackBuffer
[0]);
343 // This is a DataSourceSurface that represents a 0-based PBO for GLTextureImage.
344 class PBOUnpackSurface
: public gfx::DataSourceSurface
{
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();
369 void Unmap() override
{}
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);
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() {
398 auto* gl
= mTexture
->gl();
399 if (gl
&& gl
->MakeCurrent()) {
400 gl
->fDeleteBuffers(1, &mPBO
);
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
) {
415 auto* gl
= mTexture
->gl();
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();
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);
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
)) {
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;
460 // Otherwise, we can just use the top left as a scratch space
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());
472 void RenderCompositorOGLSWGL::TileOGL::Unmap(const gfx::IntRect
& aDirtyRect
) {
473 nsIntRegion
dirty(aDirtyRect
);
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();
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);
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
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;
502 mTexture
->Update(mSurface
, &dirty
);
508 } // namespace mozilla