/* 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 "GLContextEGL.h" #include "GLSharedHandleHelpers.h" #ifdef MOZ_WIDGET_ANDROID #include "nsSurfaceTexture.h" #endif namespace mozilla { namespace gl { enum SharedHandleType { SharedHandleType_Image #ifdef MOZ_WIDGET_ANDROID , SharedHandleType_SurfaceTexture #endif }; class SharedTextureHandleWrapper { public: SharedTextureHandleWrapper(SharedHandleType aHandleType) : mHandleType(aHandleType) { } virtual ~SharedTextureHandleWrapper() { } SharedHandleType Type() { return mHandleType; } SharedHandleType mHandleType; }; #ifdef MOZ_WIDGET_ANDROID class SurfaceTextureWrapper: public SharedTextureHandleWrapper { public: SurfaceTextureWrapper(nsSurfaceTexture* aSurfaceTexture) : SharedTextureHandleWrapper(SharedHandleType_SurfaceTexture) , mSurfaceTexture(aSurfaceTexture) { } virtual ~SurfaceTextureWrapper() { mSurfaceTexture = nullptr; } nsSurfaceTexture* SurfaceTexture() { return mSurfaceTexture; } nsRefPtr mSurfaceTexture; }; #endif // MOZ_WIDGET_ANDROID class EGLTextureWrapper : public SharedTextureHandleWrapper { public: EGLTextureWrapper() : SharedTextureHandleWrapper(SharedHandleType_Image) , mEGLImage(nullptr) , mSyncObject(nullptr) { } // Args are the active GL context, and a texture in that GL // context for which to create an EGLImage. After the EGLImage // is created, the texture is unused by EGLTextureWrapper. bool CreateEGLImage(GLContext *ctx, uintptr_t texture) { MOZ_ASSERT(!mEGLImage && texture && sEGLLibrary.HasKHRImageBase()); static const EGLint eglAttributes[] = { LOCAL_EGL_NONE }; EGLContext eglContext = GLContextEGL::Cast(ctx)->GetEGLContext(); mEGLImage = sEGLLibrary.fCreateImage(EGL_DISPLAY(), eglContext, LOCAL_EGL_GL_TEXTURE_2D, (EGLClientBuffer)texture, eglAttributes); if (!mEGLImage) { #ifdef DEBUG printf_stderr("Could not create EGL images: ERROR (0x%04x)\n", sEGLLibrary.fGetError()); #endif return false; } return true; } virtual ~EGLTextureWrapper() { if (mEGLImage) { sEGLLibrary.fDestroyImage(EGL_DISPLAY(), mEGLImage); mEGLImage = nullptr; } } const EGLImage GetEGLImage() { return mEGLImage; } // Insert a sync point on the given context, which should be the current active // context. bool MakeSync(GLContext *ctx) { MOZ_ASSERT(mSyncObject == nullptr); if (sEGLLibrary.IsExtensionSupported(GLLibraryEGL::KHR_fence_sync)) { mSyncObject = sEGLLibrary.fCreateSync(EGL_DISPLAY(), LOCAL_EGL_SYNC_FENCE, nullptr); // We need to flush to make sure the sync object enters the command stream; // we can't use EGL_SYNC_FLUSH_COMMANDS_BIT at wait time, because the wait // happens on a different thread/context. ctx->fFlush(); } if (mSyncObject == EGL_NO_SYNC) { // we failed to create one, so just do a finish ctx->fFinish(); } return true; } bool WaitSync() { if (!mSyncObject) { // if we have no sync object, then we did a Finish() earlier return true; } // wait at most 1 second; this should really be never/rarely hit const uint64_t ns_per_ms = 1000 * 1000; EGLTime timeout = 1000 * ns_per_ms; EGLint result = sEGLLibrary.fClientWaitSync(EGL_DISPLAY(), mSyncObject, 0, timeout); sEGLLibrary.fDestroySync(EGL_DISPLAY(), mSyncObject); mSyncObject = nullptr; return result == LOCAL_EGL_CONDITION_SATISFIED; } private: EGLImage mEGLImage; EGLSync mSyncObject; }; static bool DoesEGLContextSupportSharingWithEGLImage(GLContext *gl) { return sEGLLibrary.HasKHRImageBase() && sEGLLibrary.HasKHRImageTexture2D() && gl->IsExtensionSupported(GLContext::OES_EGL_image); } SharedTextureHandle CreateSharedHandle(GLContext* gl, SharedTextureShareType shareType, void* buffer, SharedTextureBufferType bufferType) { // unimplemented outside of EGL if (gl->GetContextType() != ContextTypeEGL) return 0; // Both EGLImage and SurfaceTexture only support same-process currently, but // it's possible to make SurfaceTexture work across processes. We should do that. if (shareType != SameProcess) return 0; switch (bufferType) { #ifdef MOZ_WIDGET_ANDROID case SharedTextureBufferType::SurfaceTexture: if (!gl->IsExtensionSupported(GLContext::OES_EGL_image_external)) { NS_WARNING("Missing GL_OES_EGL_image_external"); return 0; } return (SharedTextureHandle) new SurfaceTextureWrapper(reinterpret_cast(buffer)); #endif case SharedTextureBufferType::TextureID: { if (!DoesEGLContextSupportSharingWithEGLImage(gl)) return 0; GLuint texture = (uintptr_t)buffer; EGLTextureWrapper* tex = new EGLTextureWrapper(); if (!tex->CreateEGLImage(gl, texture)) { NS_ERROR("EGLImage creation for EGLTextureWrapper failed"); delete tex; return 0; } return (SharedTextureHandle)tex; } default: NS_ERROR("Unknown shared texture buffer type"); return 0; } } void ReleaseSharedHandle(GLContext* gl, SharedTextureShareType shareType, SharedTextureHandle sharedHandle) { // unimplemented outside of EGL if (gl->GetContextType() != ContextTypeEGL) return; if (shareType != SameProcess) { NS_ERROR("Implementation not available for this sharing type"); return; } SharedTextureHandleWrapper* wrapper = reinterpret_cast(sharedHandle); switch (wrapper->Type()) { #ifdef MOZ_WIDGET_ANDROID case SharedHandleType_SurfaceTexture: delete wrapper; break; #endif case SharedHandleType_Image: { NS_ASSERTION(DoesEGLContextSupportSharingWithEGLImage(gl), "EGLImage not supported or disabled in runtime"); EGLTextureWrapper* wrap = (EGLTextureWrapper*)sharedHandle; delete wrap; break; } default: NS_ERROR("Unknown shared handle type"); return; } } bool GetSharedHandleDetails(GLContext* gl, SharedTextureShareType shareType, SharedTextureHandle sharedHandle, SharedHandleDetails& details) { // unimplemented outside of EGL if (gl->GetContextType() != ContextTypeEGL) return false; if (shareType != SameProcess) return false; SharedTextureHandleWrapper* wrapper = reinterpret_cast(sharedHandle); switch (wrapper->Type()) { #ifdef MOZ_WIDGET_ANDROID case SharedHandleType_SurfaceTexture: { SurfaceTextureWrapper* surfaceWrapper = reinterpret_cast(wrapper); details.mTarget = LOCAL_GL_TEXTURE_EXTERNAL; details.mTextureFormat = gfx::FORMAT_R8G8B8A8; surfaceWrapper->SurfaceTexture()->GetTransformMatrix(details.mTextureTransform); break; } #endif case SharedHandleType_Image: details.mTarget = LOCAL_GL_TEXTURE_2D; details.mTextureFormat = gfx::FORMAT_R8G8B8A8; break; default: NS_ERROR("Unknown shared handle type"); return false; } return true; } bool AttachSharedHandle(GLContext* gl, SharedTextureShareType shareType, SharedTextureHandle sharedHandle) { // unimplemented outside of EGL if (gl->GetContextType() != ContextTypeEGL) return false; if (shareType != SameProcess) return false; SharedTextureHandleWrapper* wrapper = reinterpret_cast(sharedHandle); switch (wrapper->Type()) { #ifdef MOZ_WIDGET_ANDROID case SharedHandleType_SurfaceTexture: { #ifndef DEBUG /* * NOTE: SurfaceTexture spams us if there are any existing GL errors, so we'll clear * them here in order to avoid that. */ gl->GetAndClearError(); #endif SurfaceTextureWrapper* surfaceTextureWrapper = reinterpret_cast(wrapper); // FIXME: SurfaceTexture provides a transform matrix which is supposed to // be applied to the texture coordinates. We should return that here // so we can render correctly. Bug 775083 surfaceTextureWrapper->SurfaceTexture()->UpdateTexImage(); break; } #endif // MOZ_WIDGET_ANDROID case SharedHandleType_Image: { NS_ASSERTION(DoesEGLContextSupportSharingWithEGLImage(gl), "EGLImage not supported or disabled in runtime"); EGLTextureWrapper* wrap = (EGLTextureWrapper*)sharedHandle; wrap->WaitSync(); gl->fEGLImageTargetTexture2D(LOCAL_GL_TEXTURE_2D, wrap->GetEGLImage()); break; } default: NS_ERROR("Unknown shared handle type"); return false; } return true; } /** * Detach Shared GL Handle from GL_TEXTURE_2D target */ void DetachSharedHandle(GLContext*, SharedTextureShareType, SharedTextureHandle) { // currently a no-operation } } }