diff options
Diffstat (limited to 'gfx/webrender_bindings/RenderCompositorOGLSWGL.cpp')
-rw-r--r-- | gfx/webrender_bindings/RenderCompositorOGLSWGL.cpp | 508 |
1 files changed, 508 insertions, 0 deletions
diff --git a/gfx/webrender_bindings/RenderCompositorOGLSWGL.cpp b/gfx/webrender_bindings/RenderCompositorOGLSWGL.cpp new file mode 100644 index 0000000000..d2d0cce016 --- /dev/null +++ b/gfx/webrender_bindings/RenderCompositorOGLSWGL.cpp @@ -0,0 +1,508 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "RenderCompositorOGLSWGL.h" + +#include "GLContext.h" +#include "GLContextEGL.h" +#include "mozilla/layers/BuildConstants.h" +#include "mozilla/layers/CompositorOGL.h" +#include "mozilla/layers/Effects.h" +#include "mozilla/layers/TextureHostOGL.h" +#include "mozilla/widget/CompositorWidget.h" +#include "OGLShaderProgram.h" + +#ifdef MOZ_WIDGET_ANDROID +# include "mozilla/java/GeckoSurfaceTextureWrappers.h" +# include "mozilla/layers/AndroidHardwareBuffer.h" +# include "mozilla/webrender/RenderAndroidHardwareBufferTextureHost.h" +# include "mozilla/webrender/RenderAndroidSurfaceTextureHost.h" +# include "mozilla/widget/AndroidCompositorWidget.h" +# include <android/native_window.h> +# include <android/native_window_jni.h> +#endif + +#ifdef MOZ_WIDGET_GTK +# include "mozilla/widget/GtkCompositorWidget.h" +# include <gdk/gdk.h> +# ifdef MOZ_X11 +# include <gdk/gdkx.h> +# endif +#endif + +namespace mozilla { +using namespace layers; +using namespace gfx; +namespace wr { + +extern LazyLogModule gRenderThreadLog; +#define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__)) + +UniquePtr<RenderCompositor> RenderCompositorOGLSWGL::Create( + const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError) { + if (!aWidget->GetCompositorOptions().AllowSoftwareWebRenderOGL()) { + return nullptr; + } + + RefPtr<Compositor> compositor; +#ifdef MOZ_WIDGET_ANDROID + RefPtr<gl::GLContext> context = + RenderThread::Get()->SingletonGLForCompositorOGL(); + if (!context) { + gfxCriticalNote << "SingletonGL does not exist for SWGL"; + return nullptr; + } + auto programs = RenderThread::Get()->GetProgramsForCompositorOGL(); + if (!programs) { + gfxCriticalNote << "Failed to get Programs for CompositorOGL for SWGL"; + return nullptr; + } + + nsCString log; + RefPtr<CompositorOGL> compositorOGL; + compositorOGL = new CompositorOGL(aWidget, /* aSurfaceWidth */ -1, + /* aSurfaceHeight */ -1, + /* aUseExternalSurfaceSize */ true); + if (!compositorOGL->Initialize(context, programs, &log)) { + gfxCriticalNote << "Failed to initialize CompositorOGL for SWGL: " + << log.get(); + return nullptr; + } + compositor = compositorOGL; +#elif defined(MOZ_WIDGET_GTK) + nsCString log; + RefPtr<CompositorOGL> compositorOGL; + compositorOGL = new CompositorOGL(aWidget); + if (!compositorOGL->Initialize(&log)) { + gfxCriticalNote << "Failed to initialize CompositorOGL for SWGL: " + << log.get(); + return nullptr; + } + compositor = compositorOGL; +#endif + + if (!compositor) { + return nullptr; + } + + void* ctx = wr_swgl_create_context(); + if (!ctx) { + gfxCriticalNote << "Failed SWGL context creation for WebRender"; + return nullptr; + } + + return MakeUnique<RenderCompositorOGLSWGL>(compositor, aWidget, ctx); +} + +RenderCompositorOGLSWGL::RenderCompositorOGLSWGL( + Compositor* aCompositor, const RefPtr<widget::CompositorWidget>& aWidget, + void* aContext) + : RenderCompositorLayersSWGL(aCompositor, aWidget, aContext) { + LOG("RenderCompositorOGLSWGL::RenderCompositorOGLSWGL()"); +} + +RenderCompositorOGLSWGL::~RenderCompositorOGLSWGL() { + LOG("RRenderCompositorOGLSWGL::~RenderCompositorOGLSWGL()"); +#ifdef MOZ_WIDGET_ANDROID + java::GeckoSurfaceTexture::DestroyUnused((int64_t)GetGLContext()); + DestroyEGLSurface(); +#endif +} + +gl::GLContext* RenderCompositorOGLSWGL::GetGLContext() { + return mCompositor->AsCompositorOGL()->gl(); +} + +bool RenderCompositorOGLSWGL::MakeCurrent() { + GetGLContext()->MakeCurrent(); +#ifdef MOZ_WIDGET_ANDROID + if (GetGLContext()->GetContextType() == gl::GLContextType::EGL) { + gl::GLContextEGL::Cast(GetGLContext())->SetEGLSurfaceOverride(mEGLSurface); + } +#endif + RenderCompositorLayersSWGL::MakeCurrent(); + return true; +} + +EGLSurface RenderCompositorOGLSWGL::CreateEGLSurface() { + MOZ_ASSERT(GetGLContext()->GetContextType() == gl::GLContextType::EGL); + + EGLSurface surface = EGL_NO_SURFACE; + surface = gl::GLContextEGL::CreateEGLSurfaceForCompositorWidget( + mWidget, gl::GLContextEGL::Cast(GetGLContext())->mSurfaceConfig); + if (surface == EGL_NO_SURFACE) { + const auto* renderThread = RenderThread::Get(); + gfxCriticalNote << "Failed to create EGLSurface. " + << renderThread->RendererCount() << " renderers, " + << renderThread->ActiveRendererCount() << " active."; + } + + // The subsequent render after creating a new surface must be a full render. + mFullRender = true; + + return surface; +} + +void RenderCompositorOGLSWGL::DestroyEGLSurface() { + MOZ_ASSERT(GetGLContext()->GetContextType() == gl::GLContextType::EGL); + + const auto& gle = gl::GLContextEGL::Cast(GetGLContext()); + const auto& egl = gle->mEgl; + + // Release EGLSurface of back buffer before calling ResizeBuffers(). + if (mEGLSurface) { + gle->SetEGLSurfaceOverride(EGL_NO_SURFACE); + gl::GLContextEGL::DestroySurface(*egl, mEGLSurface); + mEGLSurface = EGL_NO_SURFACE; + } +} + +bool RenderCompositorOGLSWGL::BeginFrame() { + MOZ_ASSERT(!mInFrame); + RenderCompositorLayersSWGL::BeginFrame(); + +#ifdef MOZ_WIDGET_ANDROID + java::GeckoSurfaceTexture::DestroyUnused((int64_t)GetGLContext()); + GetGLContext() + ->MakeCurrent(); // DestroyUnused can change the current context! +#endif + + return true; +} + +RenderedFrameId RenderCompositorOGLSWGL::EndFrame( + const nsTArray<DeviceIntRect>& aDirtyRects) { + mFullRender = false; + + return RenderCompositorLayersSWGL::EndFrame(aDirtyRects); +} + +void RenderCompositorOGLSWGL::HandleExternalImage( + RenderTextureHost* aExternalImage, FrameSurface& aFrameSurface) { + MOZ_ASSERT(aExternalImage); + +#ifdef MOZ_WIDGET_ANDROID + GLenum target = + LOCAL_GL_TEXTURE_EXTERNAL; // This is required by SurfaceTexture + GLenum wrapMode = LOCAL_GL_CLAMP_TO_EDGE; + + if (auto* host = aExternalImage->AsRenderAndroidSurfaceTextureHost()) { + host->UpdateTexImageIfNecessary(); + + // We need to hold the texture source separately from the effect, + // since the effect doesn't hold a strong reference. + RefPtr<SurfaceTextureSource> layer = new SurfaceTextureSource( + (TextureSourceProvider*)mCompositor, host->mSurfTex, host->mFormat, + target, wrapMode, host->mSize, host->mTransformOverride); + RefPtr<TexturedEffect> texturedEffect = + CreateTexturedEffect(host->mFormat, layer, aFrameSurface.mFilter, + /* isAlphaPremultiplied */ true); + + gfx::Rect drawRect(0, 0, host->mSize.width, host->mSize.height); + + EffectChain effect; + effect.mPrimaryEffect = texturedEffect; + mCompositor->DrawQuad(drawRect, aFrameSurface.mClipRect, effect, 1.0, + aFrameSurface.mTransform, drawRect); + } else if (auto* host = + aExternalImage->AsRenderAndroidHardwareBufferTextureHost()) { + // We need to hold the texture source separately from the effect, + // since the effect doesn't hold a strong reference. + RefPtr<AndroidHardwareBufferTextureSource> layer = + new AndroidHardwareBufferTextureSource( + (TextureSourceProvider*)mCompositor, + host->GetAndroidHardwareBuffer(), + host->GetAndroidHardwareBuffer()->mFormat, target, wrapMode, + host->GetSize()); + RefPtr<TexturedEffect> texturedEffect = CreateTexturedEffect( + host->GetAndroidHardwareBuffer()->mFormat, layer, aFrameSurface.mFilter, + /* isAlphaPremultiplied */ true); + + gfx::Rect drawRect(0, 0, host->GetSize().width, host->GetSize().height); + + EffectChain effect; + effect.mPrimaryEffect = texturedEffect; + mCompositor->DrawQuad(drawRect, aFrameSurface.mClipRect, effect, 1.0, + aFrameSurface.mTransform, drawRect); + } +#endif +} + +void RenderCompositorOGLSWGL::GetCompositorCapabilities( + CompositorCapabilities* aCaps) { + RenderCompositor::GetCompositorCapabilities(aCaps); + + // max_update_rects are not yet handled properly + aCaps->max_update_rects = 0; +} + +bool RenderCompositorOGLSWGL::RequestFullRender() { return mFullRender; } + +void RenderCompositorOGLSWGL::Pause() { +#ifdef MOZ_WIDGET_ANDROID + DestroyEGLSurface(); +#elif defined(MOZ_WIDGET_GTK) + mCompositor->Pause(); +#endif +} + +bool RenderCompositorOGLSWGL::Resume() { +#ifdef MOZ_WIDGET_ANDROID + // Destroy EGLSurface if it exists. + DestroyEGLSurface(); + + auto size = GetBufferSize(); + GLint maxTextureSize = 0; + GetGLContext()->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, + (GLint*)&maxTextureSize); + + // When window size is too big, hardware buffer allocation could fail. + if (maxTextureSize < size.width || maxTextureSize < size.height) { + gfxCriticalNote << "Too big ANativeWindow size(" << size.width << ", " + << size.height << ") MaxTextureSize " << maxTextureSize; + return false; + } + + mEGLSurface = CreateEGLSurface(); + if (mEGLSurface == EGL_NO_SURFACE) { + // Often when we fail to create an EGL surface it is because the + // Java Surface we have been provided is invalid. Therefore the on + // the first occurence we don't raise a WebRenderError and instead + // just return failure. This allows the widget a chance to request + // a new Java Surface. On subsequent failures, raising the + // WebRenderError will result in the compositor being recreated, + // falling back through webrender configurations, and eventually + // crashing if we still do not succeed. + if (!mHandlingNewSurfaceError) { + mHandlingNewSurfaceError = true; + } else { + RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE); + } + return false; + } + mHandlingNewSurfaceError = false; + + gl::GLContextEGL::Cast(GetGLContext())->SetEGLSurfaceOverride(mEGLSurface); + mCompositor->SetDestinationSurfaceSize(size.ToUnknownSize()); +#elif defined(MOZ_WIDGET_GTK) + bool resumed = mCompositor->Resume(); + if (!resumed) { + RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE); + return false; + } +#endif + return true; +} + +bool RenderCompositorOGLSWGL::IsPaused() { +#ifdef MOZ_WIDGET_ANDROID + return mEGLSurface == EGL_NO_SURFACE; +#endif + return false; +} + +LayoutDeviceIntSize RenderCompositorOGLSWGL::GetBufferSize() { + return mWidget->GetClientSize(); +} + +UniquePtr<RenderCompositorLayersSWGL::Tile> +RenderCompositorOGLSWGL::DoCreateTile(Surface* aSurface) { + auto source = MakeRefPtr<TextureImageTextureSourceOGL>( + mCompositor->AsCompositorOGL(), layers::TextureFlags::NO_FLAGS); + + return MakeUnique<TileOGL>(std::move(source), aSurface->TileSize()); +} + +bool RenderCompositorOGLSWGL::MaybeReadback( + const gfx::IntSize& aReadbackSize, const wr::ImageFormat& aReadbackFormat, + const Range<uint8_t>& aReadbackBuffer, bool* aNeedsYFlip) { +#ifdef MOZ_WIDGET_ANDROID + MOZ_ASSERT(aReadbackFormat == wr::ImageFormat::RGBA8); + const GLenum format = LOCAL_GL_RGBA; +#else + MOZ_ASSERT(aReadbackFormat == wr::ImageFormat::BGRA8); + const GLenum format = LOCAL_GL_BGRA; +#endif + + GetGLContext()->fReadPixels(0, 0, aReadbackSize.width, aReadbackSize.height, + format, LOCAL_GL_UNSIGNED_BYTE, + &aReadbackBuffer[0]); + + if (aNeedsYFlip) { + *aNeedsYFlip = true; + } + + return true; +} + +// This is a DataSourceSurface that represents a 0-based PBO for GLTextureImage. +class PBOUnpackSurface : public gfx::DataSourceSurface { + public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PBOUnpackSurface, override) + + explicit PBOUnpackSurface(const gfx::IntSize& aSize) : mSize(aSize) {} + + uint8_t* GetData() override { return nullptr; } + int32_t Stride() override { return mSize.width * sizeof(uint32_t); } + gfx::SurfaceType GetType() const override { + return gfx::SurfaceType::DATA_ALIGNED; + } + gfx::IntSize GetSize() const override { return mSize; } + gfx::SurfaceFormat GetFormat() const override { + return gfx::SurfaceFormat::B8G8R8A8; + } + + // PBO offsets need to start from a 0 address, but DataSourceSurface::Map + // checks for failure by comparing the address against nullptr. Override Map + // to work around this. + bool Map(MapType, MappedSurface* aMappedSurface) override { + aMappedSurface->mData = GetData(); + aMappedSurface->mStride = Stride(); + return true; + } + + void Unmap() override {} + + private: + gfx::IntSize mSize; +}; + +RenderCompositorOGLSWGL::TileOGL::TileOGL( + RefPtr<layers::TextureImageTextureSourceOGL>&& aTexture, + const gfx::IntSize& aSize) + : mTexture(aTexture) { + auto* gl = mTexture->gl(); + if (gl && gl->HasPBOState() && gl->MakeCurrent()) { + mSurface = new PBOUnpackSurface(aSize); + // Create a PBO large enough to encompass any valid rects within the tile. + gl->fGenBuffers(1, &mPBO); + gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, mPBO); + gl->fBufferData(LOCAL_GL_PIXEL_UNPACK_BUFFER, + mSurface->Stride() * aSize.height, nullptr, + LOCAL_GL_DYNAMIC_DRAW); + gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0); + } else { + // Couldn't allocate a PBO, so just use a memory surface instead. + mSurface = gfx::Factory::CreateDataSourceSurface( + aSize, gfx::SurfaceFormat::B8G8R8A8); + } +} + +RenderCompositorOGLSWGL::TileOGL::~TileOGL() { + if (mPBO) { + auto* gl = mTexture->gl(); + if (gl && gl->MakeCurrent()) { + gl->fDeleteBuffers(1, &mPBO); + mPBO = 0; + } + } +} + +layers::DataTextureSource* +RenderCompositorOGLSWGL::TileOGL::GetTextureSource() { + return mTexture.get(); +} + +bool RenderCompositorOGLSWGL::TileOGL::Map(wr::DeviceIntRect aDirtyRect, + wr::DeviceIntRect aValidRect, + void** aData, int32_t* aStride) { + if (mPBO) { + auto* gl = mTexture->gl(); + if (!gl) { + return false; + } + // Map the PBO, but only within the range of the buffer that spans from the + // linear start offset to the linear end offset. Since we don't care about + // the previous contents of the buffer, we can just tell OpenGL to + // invalidate the entire buffer, even though we're only mapping a sub-range. + gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, mPBO); + size_t stride = mSurface->Stride(); + size_t offset = + stride * aValidRect.min.y + aValidRect.min.x * sizeof(uint32_t); + size_t length = stride * (aValidRect.height() - 1) + + (aValidRect.width()) * sizeof(uint32_t); + void* data = gl->fMapBufferRange( + LOCAL_GL_PIXEL_UNPACK_BUFFER, offset, length, + LOCAL_GL_MAP_WRITE_BIT | LOCAL_GL_MAP_INVALIDATE_BUFFER_BIT); + gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0); + if (!data) { + return false; + } + *aData = data; + *aStride = stride; + } else { + // No PBO is available, so just directly write to the memory surface. + gfx::DataSourceSurface::MappedSurface map; + if (!mSurface->Map(gfx::DataSourceSurface::READ_WRITE, &map)) { + return false; + } + // Verify that we're not somehow using a PBOUnpackSurface. + MOZ_ASSERT(map.mData != nullptr); + // glTex(Sub)Image on ES doesn't support arbitrary strides without + // the EXT_unpack_subimage extension. To avoid needing to make a + // copy of the data we'll always draw it with stride = bpp*width + // unless we're uploading the entire texture. + if (!mTexture->IsValid()) { + // If we don't have a texture we need to position our + // data in the correct spot because we're going to upload + // the entire surface + *aData = map.mData + aValidRect.min.y * map.mStride + + aValidRect.min.x * sizeof(uint32_t); + + *aStride = map.mStride; + mSubSurface = nullptr; + } else { + // Otherwise, we can just use the top left as a scratch space + *aData = map.mData; + *aStride = aDirtyRect.width() * BytesPerPixel(mSurface->GetFormat()); + mSubSurface = Factory::CreateWrappingDataSourceSurface( + (uint8_t*)*aData, *aStride, + IntSize(aDirtyRect.width(), aDirtyRect.height()), + mSurface->GetFormat()); + } + } + return true; +} + +void RenderCompositorOGLSWGL::TileOGL::Unmap(const gfx::IntRect& aDirtyRect) { + nsIntRegion dirty(aDirtyRect); + if (mPBO) { + // If there is a PBO, it must be unmapped before it can be sourced from. + // Leave the PBO bound before the call to Update so that the texture uploads + // will source from it. + auto* gl = mTexture->gl(); + if (!gl) { + return; + } + gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, mPBO); + gl->fUnmapBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER); + mTexture->Update(mSurface, &dirty); + gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0); + } else { + if (mSubSurface) { + mSurface->Unmap(); + // Our subsurface has a stride = aDirtyRect.width + // We use a negative offset to move it to match + // the dirty rect's top-left. These two offsets + // will cancel each other out by the time we reach + // TexSubImage. + IntPoint srcOffset = {0, 0}; + IntPoint dstOffset = aDirtyRect.TopLeft(); + // adjust the dirty region to be relative to the dstOffset + dirty.MoveBy(-dstOffset); + mTexture->Update(mSubSurface, &dirty, &srcOffset, &dstOffset); + mSubSurface = nullptr; + } else { + mSurface->Unmap(); + mTexture->Update(mSurface, &dirty); + } + } +} + +} // namespace wr +} // namespace mozilla |