diff options
Diffstat (limited to 'gfx/gl')
-rw-r--r-- | gfx/gl/ForceDiscreteGPUHelperCGL.h | 36 | ||||
-rw-r--r-- | gfx/gl/GLBlitHelper.cpp | 111 | ||||
-rw-r--r-- | gfx/gl/GLBlitHelper.h | 3 | ||||
-rw-r--r-- | gfx/gl/GLContext.cpp | 78 | ||||
-rw-r--r-- | gfx/gl/GLContextCGL.h | 67 | ||||
-rw-r--r-- | gfx/gl/GLContextEAGL.h | 80 | ||||
-rw-r--r-- | gfx/gl/GLContextProvider.h | 7 | ||||
-rw-r--r-- | gfx/gl/GLContextProviderCGL.mm | 397 | ||||
-rw-r--r-- | gfx/gl/GLContextProviderEAGL.mm | 275 | ||||
-rwxr-xr-x | gfx/gl/GLScreenBuffer.cpp | 8 | ||||
-rw-r--r-- | gfx/gl/SharedSurfaceIO.cpp | 248 | ||||
-rw-r--r-- | gfx/gl/SharedSurfaceIO.h | 100 | ||||
-rw-r--r-- | gfx/gl/moz.build | 32 |
13 files changed, 1440 insertions, 2 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/GLBlitHelper.cpp b/gfx/gl/GLBlitHelper.cpp index 12e30c8804..da7f5b462d 100644 --- a/gfx/gl/GLBlitHelper.cpp +++ b/gfx/gl/GLBlitHelper.cpp @@ -14,6 +14,11 @@ #include "mozilla/gfx/Matrix.h" #include "mozilla/UniquePtr.h" +#ifdef XP_MACOSX +#include "MacIOSurfaceImage.h" +#include "GLContextCGL.h" +#endif + using mozilla::layers::PlanarYCbCrImage; using mozilla::layers::PlanarYCbCrData; @@ -181,6 +186,34 @@ GLBlitHelper::InitTexQuadProgram(BlitType target) } \n\ "; +#ifdef XP_MACOSX + const char kTexNV12PlanarBlit_FragShaderSource[] = "\ + #version 100 \n\ + #extension GL_ARB_texture_rectangle : require \n\ + #ifdef GL_ES \n\ + precision mediump float \n\ + #endif \n\ + varying vec2 vTexCoord; \n\ + uniform sampler2DRect uYTexture; \n\ + uniform sampler2DRect uCbCrTexture; \n\ + uniform vec2 uYTexScale; \n\ + uniform vec2 uCbCrTexScale; \n\ + void main() \n\ + { \n\ + float y = texture2DRect(uYTexture, vTexCoord * uYTexScale).r; \n\ + float cb = texture2DRect(uCbCrTexture, vTexCoord * uCbCrTexScale).r; \n\ + float cr = texture2DRect(uCbCrTexture, vTexCoord * uCbCrTexScale).a; \n\ + y = (y - 0.06275) * 1.16438; \n\ + cb = cb - 0.50196; \n\ + cr = cr - 0.50196; \n\ + gl_FragColor.r = y + cr * 1.59603; \n\ + gl_FragColor.g = y - 0.81297 * cr - 0.39176 * cb; \n\ + gl_FragColor.b = y + cb * 2.01723; \n\ + gl_FragColor.a = 1.0; \n\ + } \n\ + "; +#endif + bool success = false; GLuint* programPtr; @@ -203,6 +236,13 @@ GLBlitHelper::InitTexQuadProgram(BlitType target) fragShaderPtr = &mTexYUVPlanarBlit_FragShader; fragShaderSource = kTexYUVPlanarBlit_FragShaderSource; break; +#ifdef XP_MACOSX + case ConvertMacIOSurfaceImage: + programPtr = &mTexNV12PlanarBlit_Program; + fragShaderPtr = &mTexNV12PlanarBlit_FragShader; + fragShaderSource = kTexNV12PlanarBlit_FragShaderSource; + break; +#endif default: return false; } @@ -353,6 +393,24 @@ GLBlitHelper::InitTexQuadProgram(BlitType target) mGL->fUniform1i(texCr, Channel_Cr); break; } + case ConvertMacIOSurfaceImage: { +#ifdef XP_MACOSX + GLint texY = mGL->fGetUniformLocation(program, "uYTexture"); + GLint texCbCr = mGL->fGetUniformLocation(program, "uCbCrTexture"); + mYTexScaleLoc = mGL->fGetUniformLocation(program, "uYTexScale"); + mCbCrTexScaleLoc= mGL->fGetUniformLocation(program, "uCbCrTexScale"); + + DebugOnly<bool> hasUniformLocations = texY != -1 && + texCbCr != -1 && + mYTexScaleLoc != -1 && + mCbCrTexScaleLoc != -1; + MOZ_ASSERT(hasUniformLocations, "uniforms not found"); + + mGL->fUniform1i(texY, Channel_Y); + mGL->fUniform1i(texCbCr, Channel_Cb); +#endif + break; + } default: return false; } @@ -620,6 +678,47 @@ GLBlitHelper::BlitPlanarYCbCrImage(layers::PlanarYCbCrImage* yuvImage) return true; } +#ifdef XP_MACOSX +bool +GLBlitHelper::BlitMacIOSurfaceImage(layers::MacIOSurfaceImage* ioImage) +{ + ScopedBindTextureUnit boundTU(mGL, LOCAL_GL_TEXTURE0); + MacIOSurface* surf = ioImage->GetSurface(); + + GLint oldTex[2]; + for (int i = 0; i < 2; i++) { + mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + i); + mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, &oldTex[i]); + } + + GLuint textures[2]; + mGL->fGenTextures(2, textures); + + mGL->fActiveTexture(LOCAL_GL_TEXTURE0); + mGL->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, textures[0]); + mGL->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE); + mGL->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE); + surf->CGLTexImageIOSurface2D(gl::GLContextCGL::Cast(mGL)->GetCGLContext(), 0); + mGL->fUniform2f(mYTexScaleLoc, surf->GetWidth(0), surf->GetHeight(0)); + + mGL->fActiveTexture(LOCAL_GL_TEXTURE1); + mGL->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, textures[1]); + mGL->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE); + mGL->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE); + surf->CGLTexImageIOSurface2D(gl::GLContextCGL::Cast(mGL)->GetCGLContext(), 1); + mGL->fUniform2f(mCbCrTexScaleLoc, surf->GetWidth(1), surf->GetHeight(1)); + + mGL->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4); + for (int i = 0; i < 2; i++) { + mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + i); + mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, oldTex[i]); + } + + mGL->fDeleteTextures(2, textures); + return true; +} +#endif + bool GLBlitHelper::BlitImageToFramebuffer(layers::Image* srcImage, const gfx::IntSize& destSize, @@ -637,6 +736,13 @@ GLBlitHelper::BlitImageToFramebuffer(layers::Image* srcImage, srcOrigin = OriginPos::BottomLeft; break; +#ifdef XP_MACOSX + case ImageFormat::MAC_IOSURFACE: + type = ConvertMacIOSurfaceImage; + srcOrigin = OriginPos::TopLeft; + break; +#endif + default: return false; } @@ -662,6 +768,11 @@ GLBlitHelper::BlitImageToFramebuffer(layers::Image* srcImage, return ret; } +#ifdef XP_MACOSX + case ConvertMacIOSurfaceImage: + return BlitMacIOSurfaceImage(srcImage->AsMacIOSurfaceImage()); +#endif + default: return false; } diff --git a/gfx/gl/GLBlitHelper.h b/gfx/gl/GLBlitHelper.h index 9dd8cd8870..c2858f5913 100644 --- a/gfx/gl/GLBlitHelper.h +++ b/gfx/gl/GLBlitHelper.h @@ -107,6 +107,9 @@ class GLBlitHelper final void BindAndUploadEGLImage(EGLImage image, GLuint target); bool BlitPlanarYCbCrImage(layers::PlanarYCbCrImage* yuvImage); +#ifdef XP_MACOSX + bool BlitMacIOSurfaceImage(layers::MacIOSurfaceImage* ioImage); +#endif explicit GLBlitHelper(GLContext* gl); diff --git a/gfx/gl/GLContext.cpp b/gfx/gl/GLContext.cpp index 0106f4fd09..72a7bea58b 100644 --- a/gfx/gl/GLContext.cpp +++ b/gfx/gl/GLContext.cpp @@ -36,6 +36,10 @@ #include "mozilla/DebugOnly.h" +#ifdef XP_MACOSX +#include <CoreServices/CoreServices.h> +#endif + #if defined(MOZ_WIDGET_COCOA) #include "nsCocoaFeatures.h" #endif @@ -873,6 +877,18 @@ GLContext::InitWithPrefixImpl(const char* prefix, bool trygl) // multisampling hardcodes blending with the default blendfunc, which breaks WebGL. MarkUnsupported(GLFeature::framebuffer_multisample); } + +#ifdef XP_MACOSX + // The Mac Nvidia driver, for versions up to and including 10.8, + // don't seem to properly support this. See 814839 + // this has been fixed in Mac OS X 10.9. See 907946 + // and it also works in 10.8.3 and higher. See 1094338. + if (Vendor() == gl::GLVendor::NVIDIA && + !nsCocoaFeatures::IsAtLeastVersion(10,8,3)) + { + MarkUnsupported(GLFeature::depth_texture); + } +#endif } if (IsExtensionSupported(GLContext::ARB_pixel_buffer_object)) { @@ -993,6 +1009,25 @@ GLContext::InitWithPrefixImpl(const char* prefix, bool trygl) raw_fGetIntegerv(LOCAL_GL_MAX_RENDERBUFFER_SIZE, &mMaxRenderbufferSize); raw_fGetIntegerv(LOCAL_GL_MAX_VIEWPORT_DIMS, mMaxViewportDims); +#ifdef XP_MACOSX + if (mWorkAroundDriverBugs) { + if (mVendor == GLVendor::Intel) { + // see bug 737182 for 2D textures, bug 684882 for cube map textures. + mMaxTextureSize = std::min(mMaxTextureSize, 4096); + mMaxCubeMapTextureSize = std::min(mMaxCubeMapTextureSize, 512); + // for good measure, we align renderbuffers on what we do for 2D textures + mMaxRenderbufferSize = std::min(mMaxRenderbufferSize, 4096); + mNeedsTextureSizeChecks = true; + } else if (mVendor == GLVendor::NVIDIA) { + // See bug 879656. 8192 fails, 8191 works. + mMaxTextureSize = std::min(mMaxTextureSize, 8191); + mMaxRenderbufferSize = std::min(mMaxRenderbufferSize, 8191); + + // Part of the bug 879656, but it also doesn't hurt the 877949 + mNeedsTextureSizeChecks = true; + } + } +#endif #ifdef MOZ_X11 if (mWorkAroundDriverBugs) { if (mVendor == GLVendor::Nouveau) { @@ -1765,6 +1800,20 @@ GLContext::InitExtensions() MarkExtensionUnsupported(ANGLE_texture_compression_dxt3); MarkExtensionUnsupported(ANGLE_texture_compression_dxt5); } + +#ifdef XP_MACOSX + // Bug 1009642: On OSX Mavericks (10.9), the driver for Intel HD + // 3000 appears to be buggy WRT updating sub-images of S3TC + // textures with glCompressedTexSubImage2D. Works on Intel HD 4000 + // and Intel HD 5000/Iris that I tested. + // Bug 1124996: Appears to be the same on OSX Yosemite (10.10) + if (nsCocoaFeatures::macOSVersionMajor() == 10 && + nsCocoaFeatures::macOSVersionMinor() >= 9 && + Renderer() == GLRenderer::IntelHD3000) + { + MarkExtensionUnsupported(EXT_texture_compression_s3tc); + } +#endif } if (shouldDumpExts) { @@ -2779,6 +2828,35 @@ GLContext::fReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum f } AfterGLReadCall(); + + // Check if GL is giving back 1.0 alpha for + // RGBA reads to RGBA images from no-alpha buffers. +#ifdef XP_MACOSX + if (WorkAroundDriverBugs() && + Vendor() == gl::GLVendor::NVIDIA && + format == LOCAL_GL_RGBA && + type == LOCAL_GL_UNSIGNED_BYTE && + !IsCoreProfile() && + width && height) + { + GLint alphaBits = 0; + fGetIntegerv(LOCAL_GL_ALPHA_BITS, &alphaBits); + if (!alphaBits) { + const uint32_t alphaMask = 0xff000000; + + uint32_t* itr = (uint32_t*)pixels; + uint32_t testPixel = *itr; + if ((testPixel & alphaMask) != alphaMask) { + // We need to set the alpha channel to 1.0 manually. + uint32_t* itrEnd = itr + width*height; // Stride is guaranteed to be width*4. + + for (; itr != itrEnd; itr++) { + *itr |= alphaMask; + } + } + } + } +#endif } void 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/GLContextProvider.h b/gfx/gl/GLContextProvider.h index d9b3f04196..6e096c1f7e 100644 --- a/gfx/gl/GLContextProvider.h +++ b/gfx/gl/GLContextProvider.h @@ -34,6 +34,13 @@ namespace gl { #define DEFAULT_IMPL WGL #endif +#ifdef XP_MACOSX + #define GL_CONTEXT_PROVIDER_NAME GLContextProviderCGL + #include "GLContextProviderImpl.h" + #undef GL_CONTEXT_PROVIDER_NAME + #define GL_CONTEXT_PROVIDER_DEFAULT GLContextProviderCGL +#endif + #if defined(MOZ_X11) #define GL_CONTEXT_PROVIDER_NAME GLContextProviderGLX #include "GLContextProviderImpl.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/GLScreenBuffer.cpp b/gfx/gl/GLScreenBuffer.cpp index 0ac0322be4..5d95eb9285 100755 --- a/gfx/gl/GLScreenBuffer.cpp +++ b/gfx/gl/GLScreenBuffer.cpp @@ -24,6 +24,10 @@ #include "mozilla/gfx/DeviceManagerDx.h" #endif +#ifdef XP_MACOSX +#include "SharedSurfaceIO.h" +#endif + #ifdef GL_PROVIDER_GLX #include "GLXLibrary.h" #include "SharedSurfaceGLX.h" @@ -80,7 +84,9 @@ GLScreenBuffer::CreateFactory(GLContext* gl, if (!gfxPrefs::WebGLForceLayersReadback()) { switch (backend) { case mozilla::layers::LayersBackend::LAYERS_OPENGL: { -#if defined(GL_PROVIDER_GLX) +#if defined(XP_MACOSX) + factory = SurfaceFactory_IOSurface::Create(gl, caps, ipcChannel, flags); +#elif defined(GL_PROVIDER_GLX) if (sGLXLibrary.UseTextureFromPixmap()) factory = SurfaceFactory_GLXDrawable::Create(gl, caps, ipcChannel, flags); #elif defined(MOZ_WIDGET_UIKIT) 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 += [ |