diff options
Diffstat (limited to 'gfx/gl/GLContextProviderCGL.mm')
-rw-r--r-- | gfx/gl/GLContextProviderCGL.mm | 397 |
1 files changed, 397 insertions, 0 deletions
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 */ |