diff options
Diffstat (limited to 'gfx/gl/AndroidSurfaceTexture.cpp')
-rw-r--r-- | gfx/gl/AndroidSurfaceTexture.cpp | 260 |
1 files changed, 260 insertions, 0 deletions
diff --git a/gfx/gl/AndroidSurfaceTexture.cpp b/gfx/gl/AndroidSurfaceTexture.cpp new file mode 100644 index 000000000..3c8c81974 --- /dev/null +++ b/gfx/gl/AndroidSurfaceTexture.cpp @@ -0,0 +1,260 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +// vim:set ts=2 sts=2 sw=2 et cin: +/* 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/. */ + +#ifdef MOZ_WIDGET_ANDROID + +#include <map> +#include <android/native_window_jni.h> +#include <android/log.h> +#include "AndroidSurfaceTexture.h" +#include "gfxImageSurface.h" +#include "gfxPrefs.h" +#include "AndroidBridge.h" +#include "nsThreadUtils.h" +#include "mozilla/gfx/Matrix.h" +#include "GeneratedJNINatives.h" +#include "GLContext.h" + +using namespace mozilla; + +namespace mozilla { +namespace gl { + +class AndroidSurfaceTexture::Listener + : public java::SurfaceTextureListener::Natives<Listener> +{ + using Base = java::SurfaceTextureListener::Natives<Listener>; + + const nsCOMPtr<nsIRunnable> mCallback; + +public: + using Base::AttachNative; + using Base::DisposeNative; + + Listener(nsIRunnable* aCallback) : mCallback(aCallback) {} + + void OnFrameAvailable() + { + if (NS_IsMainThread()) { + mCallback->Run(); + return; + } + NS_DispatchToMainThread(mCallback); + } +}; + +already_AddRefed<AndroidSurfaceTexture> +AndroidSurfaceTexture::Create() +{ + return Create(nullptr, 0); +} + +already_AddRefed<AndroidSurfaceTexture> +AndroidSurfaceTexture::Create(GLContext* aContext, GLuint aTexture) +{ + RefPtr<AndroidSurfaceTexture> st = new AndroidSurfaceTexture(); + if (!st->Init(aContext, aTexture)) { + printf_stderr("Failed to initialize AndroidSurfaceTexture"); + st = nullptr; + } + + return st.forget(); +} + +nsresult +AndroidSurfaceTexture::Attach(GLContext* aContext, PRIntervalTime aTimeout) +{ + MonitorAutoLock lock(mMonitor); + + if (mAttachedContext == aContext) { + NS_WARNING("Tried to attach same GLContext to AndroidSurfaceTexture"); + return NS_OK; + } + + if (!CanDetach()) { + return NS_ERROR_NOT_AVAILABLE; + } + + while (mAttachedContext) { + // Wait until it's detached (or we time out) + if (NS_FAILED(lock.Wait(aTimeout))) { + return NS_ERROR_NOT_AVAILABLE; + } + } + + MOZ_ASSERT(aContext->IsOwningThreadCurrent(), "Trying to attach GLContext from different thread"); + + aContext->fGenTextures(1, &mTexture); + + if (NS_FAILED(mSurfaceTexture->AttachToGLContext(mTexture))) { + return NS_ERROR_NOT_AVAILABLE; + } + mAttachedContext = aContext; + mAttachedContext->MakeCurrent(); + + return NS_OK; +} + +nsresult +AndroidSurfaceTexture::Detach() +{ + MonitorAutoLock lock(mMonitor); + + if (!CanDetach() || + !mAttachedContext || + !mAttachedContext->IsOwningThreadCurrent()) + { + return NS_ERROR_FAILURE; + } + + mAttachedContext->MakeCurrent(); + + mSurfaceTexture->DetachFromGLContext(); + + mTexture = 0; + mAttachedContext = nullptr; + lock.NotifyAll(); + return NS_OK; +} + +bool +AndroidSurfaceTexture::CanDetach() const +{ + // The API for attach/detach only exists on 16+, and PowerVR has some sort of + // fencing issue. Additionally, attach/detach seems to be busted on at least + // some Mali adapters (400MP2 for sure, bug 1131793) + return AndroidBridge::Bridge()->GetAPIVersion() >= 16 && + (!mAttachedContext || mAttachedContext->Vendor() != GLVendor::Imagination) && + (!mAttachedContext || mAttachedContext->Vendor() != GLVendor::ARM /* Mali */) && + gfxPrefs::SurfaceTextureDetachEnabled(); +} + +bool +AndroidSurfaceTexture::Init(GLContext* aContext, GLuint aTexture) +{ + + if (!aTexture && !CanDetach()) { + // We have no texture and cannot initialize detached, bail out + return false; + } + + if (NS_WARN_IF(NS_FAILED( + java::sdk::SurfaceTexture::New(aTexture, ReturnTo(&mSurfaceTexture))))) { + return false; + } + + if (!aTexture) { + mSurfaceTexture->DetachFromGLContext(); + } + + mAttachedContext = aContext; + + if (NS_WARN_IF(NS_FAILED( + java::sdk::Surface::New(mSurfaceTexture, ReturnTo(&mSurface))))) { + return false; + } + + mNativeWindow = ANativeWindow_fromSurface(jni::GetEnvForThread(), + mSurface.Get()); + MOZ_ASSERT(mNativeWindow, "Failed to create native window from surface"); + + return true; +} + +AndroidSurfaceTexture::AndroidSurfaceTexture() + : mTexture(0) + , mSurfaceTexture() + , mSurface() + , mAttachedContext(nullptr) + , mMonitor("AndroidSurfaceTexture") +{ +} + +AndroidSurfaceTexture::~AndroidSurfaceTexture() +{ + if (mSurfaceTexture) { + SetFrameAvailableCallback(nullptr); + mSurfaceTexture = nullptr; + } + + if (mNativeWindow) { + ANativeWindow_release(mNativeWindow); + mNativeWindow = nullptr; + } +} + +void +AndroidSurfaceTexture::UpdateTexImage() +{ + mSurfaceTexture->UpdateTexImage(); +} + +void +AndroidSurfaceTexture::GetTransformMatrix(gfx::Matrix4x4& aMatrix) const +{ + JNIEnv* const env = jni::GetEnvForThread(); + + auto jarray = jni::FloatArray::LocalRef::Adopt(env, env->NewFloatArray(16)); + mSurfaceTexture->GetTransformMatrix(jarray); + + jfloat* array = env->GetFloatArrayElements(jarray.Get(), nullptr); + + aMatrix._11 = array[0]; + aMatrix._12 = array[1]; + aMatrix._13 = array[2]; + aMatrix._14 = array[3]; + + aMatrix._21 = array[4]; + aMatrix._22 = array[5]; + aMatrix._23 = array[6]; + aMatrix._24 = array[7]; + + aMatrix._31 = array[8]; + aMatrix._32 = array[9]; + aMatrix._33 = array[10]; + aMatrix._34 = array[11]; + + aMatrix._41 = array[12]; + aMatrix._42 = array[13]; + aMatrix._43 = array[14]; + aMatrix._44 = array[15]; + + env->ReleaseFloatArrayElements(jarray.Get(), array, 0); +} + +void +AndroidSurfaceTexture::SetFrameAvailableCallback(nsIRunnable* aRunnable) +{ + java::SurfaceTextureListener::LocalRef newListener; + + if (aRunnable) { + newListener = java::SurfaceTextureListener::New(); + Listener::AttachNative(newListener, MakeUnique<Listener>(aRunnable)); + } + + if (aRunnable || mListener) { + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + mSurfaceTexture->SetOnFrameAvailableListener(newListener))); + } + + if (mListener) { + Listener::DisposeNative(java::SurfaceTextureListener::LocalRef( + newListener.Env(), mListener)); + } + + mListener = newListener; +} + +void +AndroidSurfaceTexture::SetDefaultSize(mozilla::gfx::IntSize size) +{ + mSurfaceTexture->SetDefaultBufferSize(size.width, size.height); +} + +} // gl +} // mozilla + +#endif // MOZ_WIDGET_ANDROID |