diff options
author | Brian Smith <brian@dbsoft.org> | 2022-04-26 09:38:50 -0500 |
---|---|---|
committer | Brian Smith <brian@dbsoft.org> | 2022-04-26 10:19:02 -0500 |
commit | adf51bc0f5eb9b924e52c11bb184f6a4d2c5534a (patch) | |
tree | 797a081cc0bb717a8a83a7c508b9265dec244217 /gfx/gl | |
parent | 378738aaa9924d0b95e2c57f27cbad2b2e644282 (diff) | |
download | uxp-adf51bc0f5eb9b924e52c11bb184f6a4d2c5534a.tar.gz |
Issue #1829 - Revert "Issue #1751 -- Remove cocoa and uikit gfx and hal support code"
This reverts commit 1ee35eafa043142293f3d42317c1eee490d00375.
Diffstat (limited to 'gfx/gl')
-rw-r--r-- | gfx/gl/ForceDiscreteGPUHelperCGL.h | 36 | ||||
-rw-r--r-- | gfx/gl/GLContextCGL.h | 67 | ||||
-rw-r--r-- | gfx/gl/GLContextEAGL.h | 80 | ||||
-rw-r--r-- | gfx/gl/GLContextProviderCGL.mm | 397 | ||||
-rw-r--r-- | gfx/gl/GLContextProviderEAGL.mm | 275 | ||||
-rw-r--r-- | gfx/gl/SharedSurfaceIO.cpp | 248 | ||||
-rw-r--r-- | gfx/gl/SharedSurfaceIO.h | 100 | ||||
-rw-r--r-- | gfx/gl/moz.build | 32 |
8 files changed, 1234 insertions, 1 deletions
diff --git a/gfx/gl/ForceDiscreteGPUHelperCGL.h b/gfx/gl/ForceDiscreteGPUHelperCGL.h new file mode 100644 index 0000000000..3b4cb07efa --- /dev/null +++ b/gfx/gl/ForceDiscreteGPUHelperCGL.h @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * 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/. */ + +#ifndef ForceDiscreteGPUHelperCGL_h_ +#define ForceDiscreteGPUHelperCGL_h_ + +#include <OpenGL/OpenGL.h> + +/** This RAII helper guarantees that we're on the discrete GPU during its lifetime. + * + * As long as any ForceDiscreteGPUHelperCGL object is alive, we're on the discrete GPU. + */ +class ForceDiscreteGPUHelperCGL +{ + CGLPixelFormatObj mPixelFormatObj; + +public: + ForceDiscreteGPUHelperCGL() + { + // the code in this function is taken from Chromium, src/ui/gfx/gl/gl_context_cgl.cc, r122013 + // BSD-style license, (c) The Chromium Authors + CGLPixelFormatAttribute attribs[1]; + attribs[0] = static_cast<CGLPixelFormatAttribute>(0); + GLint num_pixel_formats = 0; + CGLChoosePixelFormat(attribs, &mPixelFormatObj, &num_pixel_formats); + } + + ~ForceDiscreteGPUHelperCGL() + { + CGLReleasePixelFormat(mPixelFormatObj); + } +}; + +#endif // ForceDiscreteGPUHelperCGL_h_ diff --git a/gfx/gl/GLContextCGL.h b/gfx/gl/GLContextCGL.h new file mode 100644 index 0000000000..29d5d982d7 --- /dev/null +++ b/gfx/gl/GLContextCGL.h @@ -0,0 +1,67 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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/. */ + +#ifndef GLCONTEXTCGL_H_ +#define GLCONTEXTCGL_H_ + +#include "GLContext.h" + +#include "OpenGL/OpenGL.h" + +#ifdef __OBJC__ +#include <AppKit/NSOpenGL.h> +#else +typedef void NSOpenGLContext; +#endif + +namespace mozilla { +namespace gl { + +class GLContextCGL : public GLContext +{ + friend class GLContextProviderCGL; + + NSOpenGLContext* const mContext; + +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GLContextCGL, override) + GLContextCGL(CreateContextFlags flags, const SurfaceCaps& caps, + NSOpenGLContext* context, bool isOffscreen, ContextProfile profile); + + ~GLContextCGL(); + + virtual GLContextType GetContextType() const override { return GLContextType::CGL; } + + static GLContextCGL* Cast(GLContext* gl) { + MOZ_ASSERT(gl->GetContextType() == GLContextType::CGL); + return static_cast<GLContextCGL*>(gl); + } + + bool Init() override; + + NSOpenGLContext* GetNSOpenGLContext() const { return mContext; } + CGLContextObj GetCGLContext() const; + + virtual bool MakeCurrentImpl(bool aForce) override; + + virtual bool IsCurrent() override; + + virtual GLenum GetPreferredARGB32Format() const override; + + virtual bool SetupLookupFunction() override; + + virtual bool IsDoubleBuffered() const override; + + virtual bool SupportsRobustness() const override { return false; } + + virtual bool SwapBuffers() override; + + virtual void GetWSIInfo(nsCString* const out) const override; +}; + +} // namespace gl +} // namespace mozilla + +#endif // GLCONTEXTCGL_H_ diff --git a/gfx/gl/GLContextEAGL.h b/gfx/gl/GLContextEAGL.h new file mode 100644 index 0000000000..f677d23d57 --- /dev/null +++ b/gfx/gl/GLContextEAGL.h @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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/. */ + +#ifndef GLCONTEXTEAGL_H_ +#define GLCONTEXTEAGL_H_ + +#include "GLContext.h" + +#include <CoreGraphics/CoreGraphics.h> +#include <OpenGLES/EAGL.h> + +namespace mozilla { +namespace gl { + +class GLContextEAGL : public GLContext +{ + friend class GLContextProviderEAGL; + + EAGLContext* const mContext; + +public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GLContextEAGL, override) + GLContextEAGL(CreateContextFlags flags, const SurfaceCaps& caps, EAGLContext* context, + GLContext* sharedContext, bool isOffscreen, ContextProfile profile); + + ~GLContextEAGL(); + + virtual GLContextType GetContextType() const override { + return GLContextType::EAGL; + } + + static GLContextEAGL* Cast(GLContext* gl) { + MOZ_ASSERT(gl->GetContextType() == GLContextType::EAGL); + return static_cast<GLContextEAGL*>(gl); + } + + bool Init() override; + + bool AttachToWindow(nsIWidget* aWidget); + + EAGLContext* GetEAGLContext() const { return mContext; } + + virtual bool MakeCurrentImpl(bool aForce) override; + + virtual bool IsCurrent() override; + + virtual bool SetupLookupFunction() override; + + virtual bool IsDoubleBuffered() const override; + + virtual bool SupportsRobustness() const override { return false; } + + virtual bool SwapBuffers() override; + + virtual void GetWSIInfo(nsCString* const out) const override; + + virtual GLuint GetDefaultFramebuffer() override { + return mBackbufferFB; + } + + virtual bool RenewSurface(nsIWidget* aWidget) override { + // FIXME: should use the passed widget instead of the existing one. + return RecreateRB(); + } + +private: + GLuint mBackbufferRB; + GLuint mBackbufferFB; + + void* mLayer; + + bool RecreateRB(); +}; + +} // namespace gl +} // namespace mozilla + +#endif // GLCONTEXTEAGL_H_ diff --git a/gfx/gl/GLContextProviderCGL.mm b/gfx/gl/GLContextProviderCGL.mm new file mode 100644 index 0000000000..7cc89eac9f --- /dev/null +++ b/gfx/gl/GLContextProviderCGL.mm @@ -0,0 +1,397 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * 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 "GLContextProvider.h" +#include "GLContextCGL.h" +#include "nsDebug.h" +#include "nsIWidget.h" +#include <OpenGL/gl.h> +#include "gfxFailure.h" +#include "gfxPrefs.h" +#include "prenv.h" +#include "GeckoProfiler.h" +#include "mozilla/gfx/MacIOSurface.h" +#include "mozilla/widget/CompositorWidget.h" + +#include <OpenGL/OpenGL.h> + +// When running inside a VM, creating an accelerated OpenGL context usually +// fails. Uncomment this line to emulate that behavior. +// #define EMULATE_VM + +namespace mozilla { +namespace gl { + +using namespace mozilla::gfx; +using namespace mozilla::widget; + +class CGLLibrary +{ +public: + CGLLibrary() + : mInitialized(false) + , mUseDoubleBufferedWindows(true) + , mOGLLibrary(nullptr) + {} + + bool EnsureInitialized() + { + if (mInitialized) { + return true; + } + if (!mOGLLibrary) { + mOGLLibrary = PR_LoadLibrary("/System/Library/Frameworks/OpenGL.framework/OpenGL"); + if (!mOGLLibrary) { + NS_WARNING("Couldn't load OpenGL Framework."); + return false; + } + } + + const char* db = PR_GetEnv("MOZ_CGL_DB"); + if (db) { + mUseDoubleBufferedWindows = *db != '0'; + } + + mInitialized = true; + return true; + } + + bool UseDoubleBufferedWindows() const { + MOZ_ASSERT(mInitialized); + return mUseDoubleBufferedWindows; + } + +private: + bool mInitialized; + bool mUseDoubleBufferedWindows; + PRLibrary* mOGLLibrary; +}; + +CGLLibrary sCGLLibrary; + +GLContextCGL::GLContextCGL(CreateContextFlags flags, const SurfaceCaps& caps, + NSOpenGLContext* context, bool isOffscreen, + ContextProfile profile) + : GLContext(flags, caps, nullptr, isOffscreen) + , mContext(context) +{ + SetProfileVersion(profile, 210); +} + +GLContextCGL::~GLContextCGL() +{ + MarkDestroyed(); + [mContext release]; +} + +bool +GLContextCGL::Init() +{ + return InitWithPrefix("gl", true); +} + +CGLContextObj +GLContextCGL::GetCGLContext() const +{ + return static_cast<CGLContextObj>([mContext CGLContextObj]); +} + +bool +GLContextCGL::MakeCurrentImpl(bool aForce) +{ + if (IsDestroyed()) { + [NSOpenGLContext clearCurrentContext]; + return false; + } + + if (!aForce && [NSOpenGLContext currentContext] == mContext) { + return true; + } + + if (mContext) { + [mContext makeCurrentContext]; + MOZ_ASSERT(IsCurrent()); + // Use non-blocking swap in "ASAP mode". + // ASAP mode means that rendering is iterated as fast as possible. + // ASAP mode is entered when layout.frame_rate=0 (requires restart). + // If swapInt is 1, then glSwapBuffers will block and wait for a vblank signal. + // When we're iterating as fast as possible, however, we want a non-blocking + // glSwapBuffers, which will happen when swapInt==0. + GLint swapInt = gfxPrefs::LayoutFrameRate() == 0 ? 0 : 1; + [mContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; + } + return true; +} + +bool +GLContextCGL::IsCurrent() { + return [NSOpenGLContext currentContext] == mContext; +} + +GLenum +GLContextCGL::GetPreferredARGB32Format() const +{ + return LOCAL_GL_BGRA; +} + +bool +GLContextCGL::SetupLookupFunction() +{ + return false; +} + +bool +GLContextCGL::IsDoubleBuffered() const +{ + return sCGLLibrary.UseDoubleBufferedWindows(); +} + +bool +GLContextCGL::SwapBuffers() +{ + PROFILER_LABEL("GLContextCGL", "SwapBuffers", + js::ProfileEntry::Category::GRAPHICS); + + [mContext flushBuffer]; + return true; +} + +void +GLContextCGL::GetWSIInfo(nsCString* const out) const +{ + out->AppendLiteral("CGL"); +} + +already_AddRefed<GLContext> +GLContextProviderCGL::CreateWrappingExisting(void*, void*) +{ + return nullptr; +} + +static const NSOpenGLPixelFormatAttribute kAttribs_singleBuffered[] = { + NSOpenGLPFAAllowOfflineRenderers, + 0 +}; + +static const NSOpenGLPixelFormatAttribute kAttribs_singleBuffered_accel[] = { + NSOpenGLPFAAccelerated, + NSOpenGLPFAAllowOfflineRenderers, + 0 +}; + +static const NSOpenGLPixelFormatAttribute kAttribs_doubleBuffered[] = { + NSOpenGLPFAAllowOfflineRenderers, + NSOpenGLPFADoubleBuffer, + 0 +}; + +static const NSOpenGLPixelFormatAttribute kAttribs_doubleBuffered_accel[] = { + NSOpenGLPFAAccelerated, + NSOpenGLPFAAllowOfflineRenderers, + NSOpenGLPFADoubleBuffer, + 0 +}; + +static const NSOpenGLPixelFormatAttribute kAttribs_offscreen[] = { + 0 +}; + +static const NSOpenGLPixelFormatAttribute kAttribs_offscreen_allow_offline[] = { + NSOpenGLPFAAllowOfflineRenderers, + 0 +}; + +static const NSOpenGLPixelFormatAttribute kAttribs_offscreen_accel[] = { + NSOpenGLPFAAccelerated, + 0 +}; + +static const NSOpenGLPixelFormatAttribute kAttribs_offscreen_coreProfile[] = { + NSOpenGLPFAAccelerated, + NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, + 0 +}; + +static NSOpenGLContext* +CreateWithFormat(const NSOpenGLPixelFormatAttribute* attribs) +{ + NSOpenGLPixelFormat* format = [[NSOpenGLPixelFormat alloc] + initWithAttributes:attribs]; + if (!format) { + NS_WARNING("Failed to create NSOpenGLPixelFormat."); + return nullptr; + } + + NSOpenGLContext* context = [[NSOpenGLContext alloc] initWithFormat:format + shareContext:nullptr]; + + [format release]; + + return context; +} + +already_AddRefed<GLContext> +GLContextProviderCGL::CreateForCompositorWidget(CompositorWidget* aCompositorWidget, bool aForceAccelerated) +{ + return CreateForWindow(aCompositorWidget->RealWidget(), aForceAccelerated); +} + +already_AddRefed<GLContext> +GLContextProviderCGL::CreateForWindow(nsIWidget* aWidget, bool aForceAccelerated) +{ + if (!sCGLLibrary.EnsureInitialized()) { + return nullptr; + } + +#ifdef EMULATE_VM + if (aForceAccelerated) { + return nullptr; + } +#endif + + const NSOpenGLPixelFormatAttribute* attribs; + if (sCGLLibrary.UseDoubleBufferedWindows()) { + attribs = aForceAccelerated ? kAttribs_doubleBuffered_accel : kAttribs_doubleBuffered; + } else { + attribs = aForceAccelerated ? kAttribs_singleBuffered_accel : kAttribs_singleBuffered; + } + NSOpenGLContext* context = CreateWithFormat(attribs); + if (!context) { + return nullptr; + } + + // make the context transparent + GLint opaque = 0; + [context setValues:&opaque forParameter:NSOpenGLCPSurfaceOpacity]; + + SurfaceCaps caps = SurfaceCaps::ForRGBA(); + ContextProfile profile = ContextProfile::OpenGLCompatibility; + RefPtr<GLContextCGL> glContext = new GLContextCGL(CreateContextFlags::NONE, caps, + context, false, profile); + + if (!glContext->Init()) { + glContext = nullptr; + [context release]; + return nullptr; + } + + return glContext.forget(); +} + +static already_AddRefed<GLContextCGL> +CreateOffscreenFBOContext(CreateContextFlags flags) +{ + if (!sCGLLibrary.EnsureInitialized()) { + return nullptr; + } + + ContextProfile profile; + NSOpenGLContext* context = nullptr; + + if (!(flags & CreateContextFlags::REQUIRE_COMPAT_PROFILE)) { + profile = ContextProfile::OpenGLCore; + context = CreateWithFormat(kAttribs_offscreen_coreProfile); + } + if (!context) { + profile = ContextProfile::OpenGLCompatibility; + + if (flags & CreateContextFlags::ALLOW_OFFLINE_RENDERER) { + if (gfxPrefs::RequireHardwareGL()) + context = CreateWithFormat(kAttribs_singleBuffered); + else + context = CreateWithFormat(kAttribs_offscreen_allow_offline); + + } else { + if (gfxPrefs::RequireHardwareGL()) + context = CreateWithFormat(kAttribs_offscreen_accel); + else + context = CreateWithFormat(kAttribs_offscreen); + } + } + if (!context) { + NS_WARNING("Failed to create NSOpenGLContext."); + return nullptr; + } + + SurfaceCaps dummyCaps = SurfaceCaps::Any(); + RefPtr<GLContextCGL> glContext = new GLContextCGL(flags, dummyCaps, context, true, + profile); + + if (gfxPrefs::GLMultithreaded()) { + CGLEnable(glContext->GetCGLContext(), kCGLCEMPEngine); + } + return glContext.forget(); +} + +already_AddRefed<GLContext> +GLContextProviderCGL::CreateHeadless(CreateContextFlags flags, + nsACString* const out_failureId) +{ + RefPtr<GLContextCGL> gl; + gl = CreateOffscreenFBOContext(flags); + if (!gl) { + *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_CGL_FBO"); + return nullptr; + } + + if (!gl->Init()) { + *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_CGL_INIT"); + NS_WARNING("Failed during Init."); + return nullptr; + } + + return gl.forget(); +} + +already_AddRefed<GLContext> +GLContextProviderCGL::CreateOffscreen(const IntSize& size, + const SurfaceCaps& minCaps, + CreateContextFlags flags, + nsACString* const out_failureId) +{ + RefPtr<GLContext> gl = CreateHeadless(flags, out_failureId); + if (!gl) { + return nullptr; + } + + if (!gl->InitOffscreen(size, minCaps)) { + *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_CGL_INIT"); + return nullptr; + } + + return gl.forget(); +} + +static RefPtr<GLContext> gGlobalContext; + +GLContext* +GLContextProviderCGL::GetGlobalContext() +{ + static bool triedToCreateContext = false; + if (!triedToCreateContext) { + triedToCreateContext = true; + + MOZ_RELEASE_ASSERT(!gGlobalContext); + nsCString discardFailureId; + RefPtr<GLContext> temp = CreateHeadless(CreateContextFlags::NONE, + &discardFailureId); + gGlobalContext = temp; + + if (!gGlobalContext) { + NS_WARNING("Couldn't init gGlobalContext."); + } + } + + return gGlobalContext; +} + +void +GLContextProviderCGL::Shutdown() +{ + gGlobalContext = nullptr; +} + +} /* namespace gl */ +} /* namespace mozilla */ diff --git a/gfx/gl/GLContextProviderEAGL.mm b/gfx/gl/GLContextProviderEAGL.mm new file mode 100644 index 0000000000..11c7cce776 --- /dev/null +++ b/gfx/gl/GLContextProviderEAGL.mm @@ -0,0 +1,275 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * 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 "GLContextProvider.h" +#include "GLContextEAGL.h" +#include "nsDebug.h" +#include "nsIWidget.h" +#include "gfxPrefs.h" +#include "gfxFailure.h" +#include "prenv.h" +#include "mozilla/Preferences.h" +#include "mozilla/widget/CompositorWidget.h" +#include "GeckoProfiler.h" + +#import <UIKit/UIKit.h> + +namespace mozilla { +namespace gl { + +using namespace mozilla::widget; + +GLContextEAGL::GLContextEAGL(CreateContextFlags flags, const SurfaceCaps& caps, + EAGLContext* context, GLContext* sharedContext, + bool isOffscreen, ContextProfile profile) + : GLContext(flags, caps, sharedContext, isOffscreen) + , mContext(context) + , mBackbufferRB(0) + , mBackbufferFB(0) + , mLayer(nil) +{ + SetProfileVersion(ContextProfile::OpenGLES, + [context API] == kEAGLRenderingAPIOpenGLES3 ? 300 : 200); +} + +GLContextEAGL::~GLContextEAGL() +{ + if (MakeCurrent()) { + if (mBackbufferFB) { + fDeleteFramebuffers(1, &mBackbufferFB); + } + if (mBackbufferRB) { + fDeleteRenderbuffers(1, &mBackbufferRB); + } + } + + mLayer = nil; + MarkDestroyed(); + [mContext release]; +} + +bool +GLContextEAGL::Init() +{ + return InitWithPrefix("gl", true); +} + +bool +GLContextEAGL::AttachToWindow(nsIWidget* aWidget) +{ + // This should only be called once + MOZ_ASSERT(!mBackbufferFB && !mBackbufferRB); + + UIView* view = + reinterpret_cast<UIView*>(aWidget->GetNativeData(NS_NATIVE_WIDGET)); + + if (!view) { + MOZ_CRASH("no view!"); + } + + mLayer = [view layer]; + + fGenFramebuffers(1, &mBackbufferFB); + return RecreateRB(); +} + +bool +GLContextEAGL::RecreateRB() +{ + MakeCurrent(); + + CAEAGLLayer* layer = (CAEAGLLayer*)mLayer; + + if (mBackbufferRB) { + // It doesn't seem to be enough to just call renderbufferStorage: below, + // we apparently have to recreate the RB. + fDeleteRenderbuffers(1, &mBackbufferRB); + mBackbufferRB = 0; + } + + fGenRenderbuffers(1, &mBackbufferRB); + fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mBackbufferRB); + + [mContext renderbufferStorage:LOCAL_GL_RENDERBUFFER + fromDrawable:layer]; + + fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mBackbufferFB); + fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, + LOCAL_GL_RENDERBUFFER, mBackbufferRB); + + return LOCAL_GL_FRAMEBUFFER_COMPLETE == fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER); +} + +bool +GLContextEAGL::MakeCurrentImpl(bool aForce) +{ + if (!aForce && [EAGLContext currentContext] == mContext) { + return true; + } + + if (IsDestroyed()) { + [EAGLContext setCurrentContext:nil]; + return false; + } + + return [EAGLContext setCurrentContext:mContext]; +} + +bool +GLContextEAGL::IsCurrent() { + return [EAGLContext currentContext] == mContext; +} + +bool +GLContextEAGL::SetupLookupFunction() +{ + return false; +} + +bool +GLContextEAGL::IsDoubleBuffered() const +{ + return true; +} + +bool +GLContextEAGL::SwapBuffers() +{ + PROFILER_LABEL("GLContextEAGL", "SwapBuffers", + js::ProfileEntry::Category::GRAPHICS); + + [mContext presentRenderbuffer:LOCAL_GL_RENDERBUFFER]; + return true; +} + +void +GLContextEAGL::GetWSIInfo(nsCString* const out) const +{ + out->AppendLiteral("EAGL"); +} + +already_AddRefed<GLContext> +GLContextProviderEAGL::CreateWrappingExisting(void*, void*) +{ + return nullptr; +} + +static GLContextEAGL* +GetGlobalContextEAGL() +{ + return static_cast<GLContextEAGL*>(GLContextProviderEAGL::GetGlobalContext()); +} + +static already_AddRefed<GLContext> +CreateEAGLContext(CreateContextFlags flags, bool aOffscreen, GLContextEAGL* sharedContext) +{ + EAGLRenderingAPI apis[] = { kEAGLRenderingAPIOpenGLES3, kEAGLRenderingAPIOpenGLES2 }; + + // Try to create a GLES3 context if we can, otherwise fall back to GLES2 + EAGLContext* context = nullptr; + for (EAGLRenderingAPI api : apis) { + if (sharedContext) { + context = [[EAGLContext alloc] initWithAPI:api + sharegroup:sharedContext->GetEAGLContext().sharegroup]; + } else { + context = [[EAGLContext alloc] initWithAPI:api]; + } + + if (context) { + break; + } + } + + if (!context) { + return nullptr; + } + + SurfaceCaps caps = SurfaceCaps::ForRGBA(); + ContextProfile profile = ContextProfile::OpenGLES; + RefPtr<GLContextEAGL> glContext = new GLContextEAGL(flags, caps, context, + sharedContext, + aOffscreen, + profile); + + if (!glContext->Init()) { + glContext = nullptr; + return nullptr; + } + + return glContext.forget(); +} + +already_AddRefed<GLContext> +GLContextProviderEAGL::CreateForCompositorWidget(CompositorWidget* aCompositorWidget, bool aForceAccelerated) +{ + return CreateForWindow(aCompositorWidget->RealWidget(), aForceAccelerated); +} + +already_AddRefed<GLContext> +GLContextProviderEAGL::CreateForWindow(nsIWidget* aWidget, bool aForceAccelerated) +{ + RefPtr<GLContext> glContext = CreateEAGLContext(CreateContextFlags::NONE, false, + GetGlobalContextEAGL()); + if (!glContext) { + return nullptr; + } + + if (!GLContextEAGL::Cast(glContext)->AttachToWindow(aWidget)) { + return nullptr; + } + + return glContext.forget(); +} + +already_AddRefed<GLContext> +GLContextProviderEAGL::CreateHeadless(CreateContextFlags flags, + nsACString* const out_failureId) +{ + return CreateEAGLContext(flags, true, GetGlobalContextEAGL()); +} + +already_AddRefed<GLContext> +GLContextProviderEAGL::CreateOffscreen(const mozilla::gfx::IntSize& size, + const SurfaceCaps& caps, + CreateContextFlags flags, + nsACString* const out_failureId) +{ + RefPtr<GLContext> glContext = CreateHeadless(flags, out_failureId); + if (!glContext->InitOffscreen(size, caps)) { + return nullptr; + } + + return glContext.forget(); +} + +static RefPtr<GLContext> gGlobalContext; + +GLContext* +GLContextProviderEAGL::GetGlobalContext() +{ + static bool triedToCreateContext = false; + if (!triedToCreateContext) { + triedToCreateContext = true; + + MOZ_RELEASE_ASSERT(!gGlobalContext, "GFX: Global GL context already initialized."); + RefPtr<GLContext> temp = CreateHeadless(CreateContextFlags::NONE); + gGlobalContext = temp; + + if (!gGlobalContext) { + MOZ_CRASH("Failed to create global context"); + } + } + + return gGlobalContext; +} + +void +GLContextProviderEAGL::Shutdown() +{ + gGlobalContext = nullptr; +} + +} /* namespace gl */ +} /* namespace mozilla */ diff --git a/gfx/gl/SharedSurfaceIO.cpp b/gfx/gl/SharedSurfaceIO.cpp new file mode 100644 index 0000000000..50262d6cce --- /dev/null +++ b/gfx/gl/SharedSurfaceIO.cpp @@ -0,0 +1,248 @@ +/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */ +/* 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 "SharedSurfaceIO.h" + +#include "GLContextCGL.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/gfx/MacIOSurface.h" +#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc +#include "ScopedGLHelpers.h" + +namespace mozilla { +namespace gl { + +/*static*/ UniquePtr<SharedSurface_IOSurface> +SharedSurface_IOSurface::Create(const RefPtr<MacIOSurface>& ioSurf, + GLContext* gl, + bool hasAlpha) +{ + MOZ_ASSERT(ioSurf); + MOZ_ASSERT(gl); + + auto size = gfx::IntSize::Truncate(ioSurf->GetWidth(), ioSurf->GetHeight()); + + typedef SharedSurface_IOSurface ptrT; + UniquePtr<ptrT> ret( new ptrT(ioSurf, gl, size, hasAlpha) ); + return Move(ret); +} + +void +SharedSurface_IOSurface::ProducerReleaseImpl() +{ + mGL->MakeCurrent(); + mGL->fFlush(); +} + +bool +SharedSurface_IOSurface::CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, + GLint x, GLint y, GLsizei width, GLsizei height, + GLint border) +{ + /* Bug 896693 - OpenGL framebuffers that are backed by IOSurface on OSX expose a bug + * in glCopyTexImage2D --- internalformats GL_ALPHA, GL_LUMINANCE, GL_LUMINANCE_ALPHA + * return the wrong results. To work around, copy framebuffer to a temporary texture + * using GL_RGBA (which works), attach as read framebuffer and glCopyTexImage2D + * instead. + */ + + // https://www.opengl.org/sdk/docs/man3/xhtml/glCopyTexImage2D.xml says that width or + // height set to 0 results in a NULL texture. Lets not do any work and punt to + // original glCopyTexImage2D, since the FBO below will fail when trying to attach a + // texture of 0 width or height. + if (width == 0 || height == 0) + return false; + + switch (internalformat) { + case LOCAL_GL_ALPHA: + case LOCAL_GL_LUMINANCE: + case LOCAL_GL_LUMINANCE_ALPHA: + break; + + default: + return false; + } + + MOZ_ASSERT(mGL->IsCurrent()); + + ScopedTexture destTex(mGL); + { + ScopedBindTexture bindTex(mGL, destTex.Texture()); + mGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, + LOCAL_GL_NEAREST); + mGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, + LOCAL_GL_NEAREST); + mGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, + LOCAL_GL_CLAMP_TO_EDGE); + mGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, + LOCAL_GL_CLAMP_TO_EDGE); + mGL->raw_fCopyTexImage2D(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA, x, y, width, + height, 0); + } + + ScopedFramebufferForTexture tmpFB(mGL, destTex.Texture(), LOCAL_GL_TEXTURE_2D); + ScopedBindFramebuffer bindFB(mGL, tmpFB.FB()); + mGL->raw_fCopyTexImage2D(target, level, internalformat, x, y, width, height, border); + + return true; +} + +bool +SharedSurface_IOSurface::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, + GLenum format, GLenum type, GLvoid* pixels) +{ + // Calling glReadPixels when an IOSurface is bound to the current framebuffer + // can cause corruption in following glReadPixel calls (even if they aren't + // reading from an IOSurface). + // We workaround this by copying to a temporary texture, and doing the readback + // from that. + MOZ_ASSERT(mGL->IsCurrent()); + + ScopedTexture destTex(mGL); + { + ScopedFramebufferForTexture srcFB(mGL, ProdTexture(), ProdTextureTarget()); + + ScopedBindFramebuffer bindFB(mGL, srcFB.FB()); + ScopedBindTexture bindTex(mGL, destTex.Texture()); + mGL->raw_fCopyTexImage2D(LOCAL_GL_TEXTURE_2D, 0, + mHasAlpha ? LOCAL_GL_RGBA : LOCAL_GL_RGB, + x, y, + width, height, 0); + } + + ScopedFramebufferForTexture destFB(mGL, destTex.Texture()); + + ScopedBindFramebuffer bindFB(mGL, destFB.FB()); + mGL->raw_fReadPixels(0, 0, width, height, format, type, pixels); + return true; +} + +static void +BackTextureWithIOSurf(GLContext* gl, GLuint tex, MacIOSurface* ioSurf) +{ + MOZ_ASSERT(gl->IsCurrent()); + + ScopedBindTexture texture(gl, tex, LOCAL_GL_TEXTURE_RECTANGLE_ARB); + + gl->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, + LOCAL_GL_TEXTURE_MIN_FILTER, + LOCAL_GL_LINEAR); + gl->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, + LOCAL_GL_TEXTURE_MAG_FILTER, + LOCAL_GL_LINEAR); + gl->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, + LOCAL_GL_TEXTURE_WRAP_S, + LOCAL_GL_CLAMP_TO_EDGE); + gl->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, + LOCAL_GL_TEXTURE_WRAP_T, + LOCAL_GL_CLAMP_TO_EDGE); + + CGLContextObj cgl = GLContextCGL::Cast(gl)->GetCGLContext(); + MOZ_ASSERT(cgl); + + ioSurf->CGLTexImageIOSurface2D(cgl); +} + +SharedSurface_IOSurface::SharedSurface_IOSurface(const RefPtr<MacIOSurface>& ioSurf, + GLContext* gl, + const gfx::IntSize& size, + bool hasAlpha) + : SharedSurface(SharedSurfaceType::IOSurface, + AttachmentType::GLTexture, + gl, + size, + hasAlpha, + true) + , mIOSurf(ioSurf) +{ + gl->MakeCurrent(); + mProdTex = 0; + gl->fGenTextures(1, &mProdTex); + BackTextureWithIOSurf(gl, mProdTex, mIOSurf); +} + +SharedSurface_IOSurface::~SharedSurface_IOSurface() +{ + if (!mGL || !mGL->MakeCurrent()) + return; + + mGL->fDeleteTextures(1, &mProdTex); +} + +bool +SharedSurface_IOSurface::ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) +{ + bool isOpaque = !mHasAlpha; + *out_descriptor = layers::SurfaceDescriptorMacIOSurface(mIOSurf->GetIOSurfaceID(), + mIOSurf->GetContentsScaleFactor(), + isOpaque); + return true; +} + +bool +SharedSurface_IOSurface::ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) +{ + MOZ_ASSERT(out_surface); + mIOSurf->Lock(); + size_t bytesPerRow = mIOSurf->GetBytesPerRow(); + size_t ioWidth = mIOSurf->GetDevicePixelWidth(); + size_t ioHeight = mIOSurf->GetDevicePixelHeight(); + + const unsigned char* ioData = (unsigned char*)mIOSurf->GetBaseAddress(); + gfx::DataSourceSurface::ScopedMap map(out_surface, gfx::DataSourceSurface::WRITE); + if (!map.IsMapped()) { + mIOSurf->Unlock(); + return false; + } + + for (size_t i = 0; i < ioHeight; i++) { + memcpy(map.GetData() + i * map.GetStride(), + ioData + i * bytesPerRow, ioWidth * 4); + } + + mIOSurf->Unlock(); + return true; +} + +//////////////////////////////////////////////////////////////////////// +// SurfaceFactory_IOSurface + +/*static*/ UniquePtr<SurfaceFactory_IOSurface> +SurfaceFactory_IOSurface::Create(GLContext* gl, const SurfaceCaps& caps, + const RefPtr<layers::LayersIPCChannel>& allocator, + const layers::TextureFlags& flags) +{ + auto maxDims = gfx::IntSize::Truncate(MacIOSurface::GetMaxWidth(), + MacIOSurface::GetMaxHeight()); + + typedef SurfaceFactory_IOSurface ptrT; + UniquePtr<ptrT> ret( new ptrT(gl, caps, allocator, flags, maxDims) ); + return Move(ret); +} + +UniquePtr<SharedSurface> +SurfaceFactory_IOSurface::CreateShared(const gfx::IntSize& size) +{ + if (size.width > mMaxDims.width || + size.height > mMaxDims.height) + { + return nullptr; + } + + bool hasAlpha = mReadCaps.alpha; + RefPtr<MacIOSurface> ioSurf; + ioSurf = MacIOSurface::CreateIOSurface(size.width, size.height, 1.0, + hasAlpha); + + if (!ioSurf) { + NS_WARNING("Failed to create MacIOSurface."); + return nullptr; + } + + return SharedSurface_IOSurface::Create(ioSurf, mGL, hasAlpha); +} + +} // namespace gl +} // namespace mozilla diff --git a/gfx/gl/SharedSurfaceIO.h b/gfx/gl/SharedSurfaceIO.h new file mode 100644 index 0000000000..16e0cbbb7f --- /dev/null +++ b/gfx/gl/SharedSurfaceIO.h @@ -0,0 +1,100 @@ +/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */ +/* 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/. */ + +#ifndef SHARED_SURFACEIO_H_ +#define SHARED_SURFACEIO_H_ + +#include "mozilla/RefPtr.h" +#include "SharedSurface.h" + +class MacIOSurface; + +namespace mozilla { +namespace gl { + +class SharedSurface_IOSurface : public SharedSurface +{ +private: + const RefPtr<MacIOSurface> mIOSurf; + GLuint mProdTex; + +public: + static UniquePtr<SharedSurface_IOSurface> Create(const RefPtr<MacIOSurface>& ioSurf, + GLContext* gl, + bool hasAlpha); + +private: + SharedSurface_IOSurface(const RefPtr<MacIOSurface>& ioSurf, + GLContext* gl, const gfx::IntSize& size, + bool hasAlpha); + +public: + ~SharedSurface_IOSurface(); + + virtual void LockProdImpl() override { } + virtual void UnlockProdImpl() override { } + + virtual void ProducerAcquireImpl() override {} + virtual void ProducerReleaseImpl() override; + + virtual bool CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, + GLint x, GLint y, GLsizei width, GLsizei height, + GLint border) override; + virtual bool ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, + GLenum format, GLenum type, GLvoid* pixels) override; + + virtual GLuint ProdTexture() override { + return mProdTex; + } + + virtual GLenum ProdTextureTarget() const override { + return LOCAL_GL_TEXTURE_RECTANGLE_ARB; + } + + static SharedSurface_IOSurface* Cast(SharedSurface* surf) { + MOZ_ASSERT(surf->mType == SharedSurfaceType::IOSurface); + return static_cast<SharedSurface_IOSurface*>(surf); + } + + MacIOSurface* GetIOSurface() const { + return mIOSurf; + } + + virtual bool NeedsIndirectReads() const override { + return true; + } + + virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override; + + virtual bool ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) override; +}; + +class SurfaceFactory_IOSurface : public SurfaceFactory +{ +public: + // Infallible. + static UniquePtr<SurfaceFactory_IOSurface> Create(GLContext* gl, + const SurfaceCaps& caps, + const RefPtr<layers::LayersIPCChannel>& allocator, + const layers::TextureFlags& flags); +protected: + const gfx::IntSize mMaxDims; + + SurfaceFactory_IOSurface(GLContext* gl, const SurfaceCaps& caps, + const RefPtr<layers::LayersIPCChannel>& allocator, + const layers::TextureFlags& flags, + const gfx::IntSize& maxDims) + : SurfaceFactory(SharedSurfaceType::IOSurface, gl, caps, allocator, flags) + , mMaxDims(maxDims) + { } + + virtual UniquePtr<SharedSurface> CreateShared(const gfx::IntSize& size) override; +}; + +} // namespace gl + +} /* namespace mozilla */ + +#endif /* SHARED_SURFACEIO_H_ */ diff --git a/gfx/gl/moz.build b/gfx/gl/moz.build index 7bf5a4ab04..c490400cbf 100644 --- a/gfx/gl/moz.build +++ b/gfx/gl/moz.build @@ -7,6 +7,10 @@ gl_provider = 'Null' if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': gl_provider = 'WGL' +elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa': + gl_provider = 'CGL' +elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'uikit': + gl_provider = 'EAGL' elif 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']: if CONFIG['MOZ_EGL_XRENDER_COMPOSITE']: gl_provider = 'EGL' @@ -19,6 +23,7 @@ if CONFIG['MOZ_GL_PROVIDER']: EXPORTS += [ 'DecomposeIntoNoRepeatTriangles.h', 'EGLUtils.h', + 'ForceDiscreteGPUHelperCGL.h', 'GfxTexturesReporter.h', 'GLBlitHelper.h', 'GLConsts.h', @@ -73,7 +78,32 @@ if CONFIG['MOZ_ENABLE_SKIA_GPU']: # Suppress warnings from Skia header files. SOURCES['SkiaGLGlue.cpp'].flags += ['-Wno-implicit-fallthrough'] -if gl_provider == 'GLX': +if gl_provider == 'CGL': + # These files include Mac headers that are unfriendly to unified builds + SOURCES += [ + "GLContextProviderCGL.mm", + ] + EXPORTS += [ + 'GLContextCGL.h', + 'SharedSurfaceIO.h', + ] + # SharedSurfaceIO.cpp includes MacIOSurface.h which include Mac headers + # which define Size and Point types in root namespace with often conflict with + # our own types. While I haven't actually hit this issue in the present case, + # it's been an issue in gfx/layers so let's not risk it. + SOURCES += [ + 'SharedSurfaceIO.cpp', + ] +elif gl_provider == 'EAGL': + # These files include ObjC headers that are unfriendly to unified builds + SOURCES += [ + 'GLContextProviderEAGL.mm', + ] + EXPORTS += [ + 'GLContextEAGL.h', + ] + +elif gl_provider == 'GLX': # GLContextProviderGLX.cpp needs to be kept out of UNIFIED_SOURCES # as it includes X11 headers which cause conflicts. SOURCES += [ |