michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "GLContextProvider.h" michael@0: #include "GLContextCGL.h" michael@0: #include "TextureImageCGL.h" michael@0: #include "nsDebug.h" michael@0: #include "nsIWidget.h" michael@0: #include michael@0: #include "gfxPrefs.h" michael@0: #include "gfxFailure.h" michael@0: #include "prenv.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "GeckoProfiler.h" michael@0: #include "mozilla/gfx/MacIOSurface.h" michael@0: michael@0: using namespace mozilla::gfx; michael@0: michael@0: namespace mozilla { michael@0: namespace gl { michael@0: michael@0: static bool gUseDoubleBufferedWindows = true; michael@0: michael@0: class CGLLibrary michael@0: { michael@0: public: michael@0: CGLLibrary() michael@0: : mInitialized(false), michael@0: mOGLLibrary(nullptr), michael@0: mPixelFormat(nullptr) michael@0: { } michael@0: michael@0: bool EnsureInitialized() michael@0: { michael@0: if (mInitialized) { michael@0: return true; michael@0: } michael@0: if (!mOGLLibrary) { michael@0: mOGLLibrary = PR_LoadLibrary("/System/Library/Frameworks/OpenGL.framework/OpenGL"); michael@0: if (!mOGLLibrary) { michael@0: NS_WARNING("Couldn't load OpenGL Framework."); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: const char* db = PR_GetEnv("MOZ_CGL_DB"); michael@0: gUseDoubleBufferedWindows = (!db || *db != '0'); michael@0: michael@0: mInitialized = true; michael@0: return true; michael@0: } michael@0: michael@0: NSOpenGLPixelFormat *PixelFormat() michael@0: { michael@0: if (mPixelFormat == nullptr) { michael@0: NSOpenGLPixelFormatAttribute attribs[] = { michael@0: NSOpenGLPFAAccelerated, michael@0: NSOpenGLPFAAllowOfflineRenderers, michael@0: NSOpenGLPFADoubleBuffer, michael@0: 0 michael@0: }; michael@0: michael@0: if (!gUseDoubleBufferedWindows) { michael@0: attribs[2] = 0; michael@0: } michael@0: michael@0: mPixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs]; michael@0: } michael@0: michael@0: return mPixelFormat; michael@0: } michael@0: private: michael@0: bool mInitialized; michael@0: PRLibrary *mOGLLibrary; michael@0: NSOpenGLPixelFormat *mPixelFormat; michael@0: }; michael@0: michael@0: CGLLibrary sCGLLibrary; michael@0: michael@0: GLContextCGL::GLContextCGL( michael@0: const SurfaceCaps& caps, michael@0: GLContext *shareContext, michael@0: NSOpenGLContext *context, michael@0: bool isOffscreen) michael@0: : GLContext(caps, shareContext, isOffscreen), michael@0: mContext(context) michael@0: { michael@0: SetProfileVersion(ContextProfile::OpenGLCompatibility, 210); michael@0: } michael@0: michael@0: GLContextCGL::~GLContextCGL() michael@0: { michael@0: MarkDestroyed(); michael@0: michael@0: if (mContext) { michael@0: if ([NSOpenGLContext currentContext] == mContext) { michael@0: // Clear the current context before releasing. If we don't do michael@0: // this, the next time we call [NSOpenGLContext currentContext], michael@0: // "invalid context" will be printed to the console. michael@0: [NSOpenGLContext clearCurrentContext]; michael@0: } michael@0: [mContext release]; michael@0: } michael@0: michael@0: } michael@0: michael@0: bool michael@0: GLContextCGL::Init() michael@0: { michael@0: if (!InitWithPrefix("gl", true)) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: CGLContextObj michael@0: GLContextCGL::GetCGLContext() const michael@0: { michael@0: return static_cast([mContext CGLContextObj]); michael@0: } michael@0: michael@0: bool michael@0: GLContextCGL::MakeCurrentImpl(bool aForce) michael@0: { michael@0: if (!aForce && [NSOpenGLContext currentContext] == mContext) { michael@0: return true; michael@0: } michael@0: michael@0: if (mContext) { michael@0: [mContext makeCurrentContext]; michael@0: // Use non-blocking swap in "ASAP mode". michael@0: // ASAP mode means that rendering is iterated as fast as possible. michael@0: // ASAP mode is entered when layout.frame_rate=0 (requires restart). michael@0: // If swapInt is 1, then glSwapBuffers will block and wait for a vblank signal. michael@0: // When we're iterating as fast as possible, however, we want a non-blocking michael@0: // glSwapBuffers, which will happen when swapInt==0. michael@0: GLint swapInt = gfxPrefs::LayoutFrameRate() == 0 ? 0 : 1; michael@0: [mContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: GLContextCGL::IsCurrent() { michael@0: return [NSOpenGLContext currentContext] == mContext; michael@0: } michael@0: michael@0: GLenum michael@0: GLContextCGL::GetPreferredARGB32Format() const michael@0: { michael@0: return LOCAL_GL_BGRA; michael@0: } michael@0: michael@0: bool michael@0: GLContextCGL::SetupLookupFunction() michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: GLContextCGL::IsDoubleBuffered() const michael@0: { michael@0: return gUseDoubleBufferedWindows; michael@0: } michael@0: michael@0: bool michael@0: GLContextCGL::SupportsRobustness() const michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: GLContextCGL::SwapBuffers() michael@0: { michael@0: PROFILER_LABEL("GLContext", "SwapBuffers"); michael@0: [mContext flushBuffer]; michael@0: return true; michael@0: } michael@0: michael@0: michael@0: static GLContextCGL * michael@0: GetGlobalContextCGL() michael@0: { michael@0: return static_cast(GLContextProviderCGL::GetGlobalContext()); michael@0: } michael@0: michael@0: already_AddRefed michael@0: GLContextProviderCGL::CreateWrappingExisting(void*, void*) michael@0: { michael@0: return nullptr; michael@0: } michael@0: michael@0: already_AddRefed michael@0: GLContextProviderCGL::CreateForWindow(nsIWidget *aWidget) michael@0: { michael@0: GLContextCGL *shareContext = GetGlobalContextCGL(); michael@0: michael@0: NSOpenGLContext *context = [[NSOpenGLContext alloc] michael@0: initWithFormat:sCGLLibrary.PixelFormat() michael@0: shareContext:(shareContext ? shareContext->mContext : NULL)]; michael@0: if (!context) { michael@0: return nullptr; michael@0: } michael@0: michael@0: // make the context transparent michael@0: GLint opaque = 0; michael@0: [context setValues:&opaque forParameter:NSOpenGLCPSurfaceOpacity]; michael@0: michael@0: SurfaceCaps caps = SurfaceCaps::ForRGBA(); michael@0: nsRefPtr glContext = new GLContextCGL(caps, michael@0: shareContext, michael@0: context); michael@0: if (!glContext->Init()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return glContext.forget(); michael@0: } michael@0: michael@0: static already_AddRefed michael@0: CreateOffscreenFBOContext(bool aShare = true) michael@0: { michael@0: if (!sCGLLibrary.EnsureInitialized()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: GLContextCGL *shareContext = aShare ? GetGlobalContextCGL() : nullptr; michael@0: if (aShare && !shareContext) { michael@0: // if there is no share context, then we can't use FBOs. michael@0: return nullptr; michael@0: } michael@0: michael@0: NSOpenGLContext *context = [[NSOpenGLContext alloc] michael@0: initWithFormat:sCGLLibrary.PixelFormat() michael@0: shareContext:shareContext ? shareContext->GetNSOpenGLContext() : NULL]; michael@0: if (!context) { michael@0: return nullptr; michael@0: } michael@0: michael@0: SurfaceCaps dummyCaps = SurfaceCaps::Any(); michael@0: nsRefPtr glContext = new GLContextCGL(dummyCaps, shareContext, context, true); michael@0: michael@0: return glContext.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: GLContextProviderCGL::CreateOffscreen(const gfxIntSize& size, michael@0: const SurfaceCaps& caps) michael@0: { michael@0: nsRefPtr glContext = CreateOffscreenFBOContext(); michael@0: if (glContext && michael@0: glContext->Init() && michael@0: glContext->InitOffscreen(ToIntSize(size), caps)) michael@0: { michael@0: return glContext.forget(); michael@0: } michael@0: michael@0: // everything failed michael@0: return nullptr; michael@0: } michael@0: michael@0: static nsRefPtr gGlobalContext; michael@0: michael@0: GLContext * michael@0: GLContextProviderCGL::GetGlobalContext() michael@0: { michael@0: if (!sCGLLibrary.EnsureInitialized()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!gGlobalContext) { michael@0: // There are bugs in some older drivers with pbuffers less michael@0: // than 16x16 in size; also 16x16 is POT so that we can do michael@0: // a FBO with it on older video cards. A FBO context for michael@0: // sharing is preferred since it has no associated target. michael@0: gGlobalContext = CreateOffscreenFBOContext(false); michael@0: if (!gGlobalContext || !static_cast(gGlobalContext.get())->Init()) { michael@0: NS_WARNING("Couldn't init gGlobalContext."); michael@0: gGlobalContext = nullptr; michael@0: return nullptr; michael@0: } michael@0: } michael@0: michael@0: return gGlobalContext; michael@0: } michael@0: michael@0: void michael@0: GLContextProviderCGL::Shutdown() michael@0: { michael@0: gGlobalContext = nullptr; michael@0: } michael@0: michael@0: } /* namespace gl */ michael@0: } /* namespace mozilla */