Bug 860441 - Camera preview update is not smooth on gonk - r=nsilva
authorBenoit Jacob <bjacob@mozilla.com>
Wed, 17 Apr 2013 16:56:03 -0400
changeset 129475 0d85e11dffaf9f42e794cc5b23121c012a35f8cf
parent 129474 c73bc7ab0014a82907f8cafc8b47cf90240d65ea
child 129476 a5e3b5d175a391ce569a78e0eff814e2bd449f4a
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersnsilva
bugs860441
milestone23.0a1
Bug 860441 - Camera preview update is not smooth on gonk - r=nsilva
gfx/gl/GLContext.h
gfx/gl/GLContextProviderEGL.cpp
gfx/layers/client/ImageClient.cpp
gfx/layers/composite/ImageHost.cpp
gfx/layers/opengl/TextureHostOGL.cpp
gfx/layers/opengl/TextureHostOGL.h
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -354,16 +354,17 @@ public:
                                    gfxPattern::GraphicsFilter aFilter);
 
     virtual bool BindExternalBuffer(GLuint texture, void* buffer) { return false; }
     virtual bool UnbindExternalBuffer(GLuint texture) { return false; }
 
 #ifdef MOZ_WIDGET_GONK
     virtual EGLImage CreateEGLImageForNativeBuffer(void* buffer) = 0;
     virtual void DestroyEGLImage(EGLImage image) = 0;
+    virtual EGLImage GetNullEGLImage() = 0;
 #endif
 
     virtual already_AddRefed<TextureImage>
     CreateDirectTextureImage(android::GraphicBuffer* aBuffer, GLenum aWrapMode)
     { return nullptr; }
 
     // Before reads from offscreen texture
     void GuaranteeResolve();
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -436,16 +436,36 @@ public:
                                         LOCAL_EGL_NATIVE_BUFFER_ANDROID,
                                         buffer, attrs);
     }
 
     void DestroyEGLImage(EGLImage image) MOZ_OVERRIDE
     {
         sEGLLibrary.fDestroyImage(EGL_DISPLAY(), image);
     }
+
+    EGLImage GetNullEGLImage() MOZ_OVERRIDE
+    {
+        if (!mNullGraphicBuffer.get()) {
+            mNullGraphicBuffer
+              = new android::GraphicBuffer(
+                  1, 1,
+                  PIXEL_FORMAT_RGB_565,
+                  GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_NEVER);
+            EGLint attrs[] = {
+                LOCAL_EGL_NONE, LOCAL_EGL_NONE
+            };
+            mNullEGLImage = sEGLLibrary.fCreateImage(EGL_DISPLAY(),
+                                                     EGL_NO_CONTEXT,
+                                                     LOCAL_EGL_NATIVE_BUFFER_ANDROID,
+                                                     mNullGraphicBuffer->getNativeBuffer(),
+                                                     attrs);
+        }
+        return mNullEGLImage;
+    }
 #endif
 
 
     bool BindExternalBuffer(GLuint texture, void* buffer) MOZ_OVERRIDE
     {
 #ifdef MOZ_WIDGET_GONK
         EGLImage image = CreateEGLImageForNativeBuffer(buffer);
         // FIXME [bjacob] There is a bug here: GL_TEXTURE_EXTERNAL here is incompatible
@@ -690,16 +710,18 @@ protected:
     bool mBound;
 
     bool mIsPBuffer;
     bool mIsDoubleBuffered;
     bool mCanBindToTexture;
     bool mShareWithEGLImage;
 #ifdef MOZ_WIDGET_GONK
     nsRefPtr<HwcComposer2D> mHwc;
+    EGLImage mNullEGLImage;
+    android::sp<android::GraphicBuffer> mNullGraphicBuffer;
 #endif
 
     // A dummy texture ID that can be used when we need a texture object whose
     // images we're going to define with EGLImageTargetTexture2D.
     GLuint mTemporaryEGLImageTexture;
 
     static EGLSurface CreatePBufferSurfaceTryingPowerOfTwo(EGLConfig config,
                                                            EGLenum bindToTextureFormat,
--- a/gfx/layers/client/ImageClient.cpp
+++ b/gfx/layers/client/ImageClient.cpp
@@ -154,16 +154,31 @@ ImageClientSingle::UpdateImage(ImageCont
 
     AutoLockTextureClient lock(mTextureClient);
 
     SurfaceDescriptor desc;
     if (!static_cast<SharedRGBImage*>(image)->ToSurfaceDescriptor(desc)) {
       return false;
     }
     mTextureClient->SetDescriptor(desc);
+  } else if (image->GetFormat() == GONK_IO_SURFACE) {
+    EnsureTextureClient(TEXTURE_SHARED_GL_EXTERNAL);
+
+    nsIntRect rect(0, 0,
+                   image->GetSize().width,
+                   image->GetSize().height);
+    UpdatePictureRect(rect);
+
+    AutoLockTextureClient lock(mTextureClient);
+
+    SurfaceDescriptor desc = static_cast<GonkIOSurfaceImage*>(image)->GetSurfaceDescriptor();
+    if (!IsSurfaceDescriptorValid(desc)) {
+      return false;
+    }
+    mTextureClient->SetDescriptor(desc);
   } else {
     nsRefPtr<gfxASurface> surface = image->GetAsSurface();
     MOZ_ASSERT(surface);
 
     EnsureTextureClient(TEXTURE_SHMEM);
 
     nsRefPtr<gfxPattern> pattern = new gfxPattern(surface);
     pattern->SetFilter(mFilter);
--- a/gfx/layers/composite/ImageHost.cpp
+++ b/gfx/layers/composite/ImageHost.cpp
@@ -156,16 +156,15 @@ ImageHostBuffered::EnsureTextureHost(Tex
                                      const TextureInfo& aTextureInfo)
 {
   bool result = ImageHostSingle::EnsureTextureHost(aTextureId,
                                                    aSurface,
                                                    aAllocator,
                                                    aTextureInfo);
   if (result) {
     mTextureHost->SetBuffer(new SurfaceDescriptor(null_t()), aAllocator);
-    mPictureRect = nsIntRect(0, 0, -1, -1);
   }
 
   return result;
 }
 
 }
 }
--- a/gfx/layers/opengl/TextureHostOGL.cpp
+++ b/gfx/layers/opengl/TextureHostOGL.cpp
@@ -588,19 +588,32 @@ SurfaceFormatForAndroidPixelFormat(andro
   case android::PIXEL_FORMAT_RGBA_8888:
     return FORMAT_B8G8R8A8;
   case android::PIXEL_FORMAT_RGBX_8888:
     return FORMAT_B8G8R8X8;
   case android::PIXEL_FORMAT_RGB_565:
     return FORMAT_R5G6B5;
   case android::PIXEL_FORMAT_A_8:
     return FORMAT_A8;
+  case 17: // NV21 YUV format, see http://developer.android.com/reference/android/graphics/ImageFormat.html#NV21
+    return FORMAT_B8G8R8A8; // yup, use FORMAT_B8G8R8A8 even though it's a YUV texture. This is an external texture.
   default:
     MOZ_NOT_REACHED("Unknown Android pixel format");
-    return FORMAT_B8G8R8A8;
+    return FORMAT_UNKNOWN;
+  }
+}
+
+static GLenum
+TextureTargetForAndroidPixelFormat(android::PixelFormat aFormat)
+{
+  switch (aFormat) {
+  case 17: // NV21 YUV format, see http://developer.android.com/reference/android/graphics/ImageFormat.html#NV21
+    return LOCAL_GL_TEXTURE_EXTERNAL;
+  default:
+    return LOCAL_GL_TEXTURE_2D;
   }
 }
 
 void GrallocTextureHostOGL::SetCompositor(Compositor* aCompositor)
 {
   CompositorOGL* glCompositor = static_cast<CompositorOGL*>(aCompositor);
   if (mGL && !glCompositor) {
     DeleteTextures();
@@ -610,17 +623,17 @@ void GrallocTextureHostOGL::SetComposito
 
 void
 GrallocTextureHostOGL::DeleteTextures()
 {
   if (mGLTexture || mEGLImage) {
     mGL->MakeCurrent();
     if (mGLTexture) {
       mGL->fDeleteTextures(1, &mGLTexture);
-      mGLTexture= 0;
+      mGLTexture = 0;
     }
     if (mEGLImage) {
       mGL->DestroyEGLImage(mEGLImage);
       mEGLImage = 0;
     }
   }
 }
 
@@ -636,27 +649,28 @@ GrallocTextureHostOGL::SwapTexturesImpl(
                                         nsIntRegion*)
 {
   android::sp<android::GraphicBuffer> buffer = GrallocBufferActor::GetFrom(aImage);
   MOZ_ASSERT(aImage.type() == SurfaceDescriptor::TSurfaceDescriptorGralloc);
 
   const SurfaceDescriptorGralloc& desc = aImage.get_SurfaceDescriptorGralloc();
   mGraphicBuffer = GrallocBufferActor::GetFrom(desc);
   mFormat = SurfaceFormatForAndroidPixelFormat(mGraphicBuffer->getPixelFormat());
+  mTextureTarget = TextureTargetForAndroidPixelFormat(mGraphicBuffer->getPixelFormat());
 
   DeleteTextures();
 }
 
 void GrallocTextureHostOGL::BindTexture(GLenum aTextureUnit)
 {
   MOZ_ASSERT(mGLTexture);
 
   mGL->MakeCurrent();
   mGL->fActiveTexture(aTextureUnit);
-  mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mGLTexture);
+  mGL->fBindTexture(mTextureTarget, mGLTexture);
   mGL->fActiveTexture(LOCAL_GL_TEXTURE0);
 }
 
 bool
 GrallocTextureHostOGL::IsValid() const
 {
   return !!mGraphicBuffer.get();
 }
@@ -683,49 +697,40 @@ GrallocTextureHostOGL::Lock()
   MOZ_ASSERT(mGraphicBuffer.get());
 
   mGL->MakeCurrent();
 
   if (!mGLTexture) {
     mGL->fGenTextures(1, &mGLTexture);
   }
   mGL->fActiveTexture(LOCAL_GL_TEXTURE0);
-  mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mGLTexture);
+  mGL->fBindTexture(mTextureTarget, mGLTexture);
   if (!mEGLImage) {
     mEGLImage = mGL->CreateEGLImageForNativeBuffer(mGraphicBuffer->getNativeBuffer());
   }
-  mGL->fEGLImageTargetTexture2D(LOCAL_GL_TEXTURE_2D, mEGLImage);
+  mGL->fEGLImageTargetTexture2D(mTextureTarget, mEGLImage);
   return true;
 }
 
 void
 GrallocTextureHostOGL::Unlock()
 {
   /*
    * The job of this function is to ensure that we release any read lock placed on
    * our android::GraphicBuffer by any drawing code that sourced it via this TextureHost.
    *
    * Indeed, as soon as we draw with a texture that's tied to a android::GraphicBuffer,
    * the GL may place read locks on it. We must ensure that we release them early enough,
    * i.e. before the next time that we will try to acquire a write lock on the same buffer,
    * because read and write locks on gralloc buffers are mutually exclusive.
-   *
-   * Unfortunately there does not seem to exist an EGL function to dissociate a gralloc
-   * buffer from a texture that it was tied to. Failing that, we achieve the same result
-   * by uploading a 1x1 dummy texture image to the same texture, replacing the existing
-   * gralloc buffer attachment.
    */
   mGL->MakeCurrent();
   mGL->fActiveTexture(LOCAL_GL_TEXTURE0);
-  mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mGLTexture);
-  mGL->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0,
-                   LOCAL_GL_RGBA,
-                   1, 1, 0,
-                   LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE,
-                   nullptr);
+  mGL->fBindTexture(mTextureTarget, mGLTexture);
+  mGL->fEGLImageTargetTexture2D(mTextureTarget, mGL->GetNullEGLImage());
 }
 
 gfx::SurfaceFormat
 GrallocTextureHostOGL::GetFormat() const
 {
   return mFormat;
 }
 
--- a/gfx/layers/opengl/TextureHostOGL.h
+++ b/gfx/layers/opengl/TextureHostOGL.h
@@ -542,16 +542,17 @@ private:
 // saving the cost of a texture upload.
 class GrallocTextureHostOGL
   : public TextureHost
   , public TextureSourceOGL
 {
 public:
   GrallocTextureHostOGL()
     : mGL(nullptr)
+    , mTextureTarget(0)
     , mGLTexture(0)
     , mEGLImage(0)
   {
   }
 
   ~GrallocTextureHostOGL();
 
   virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE;
@@ -570,16 +571,20 @@ public:
 
   virtual gfx::IntSize GetSize() const MOZ_OVERRIDE
   {
     return mGraphicBuffer.get() ? gfx::IntSize(mGraphicBuffer->getWidth(), mGraphicBuffer->getHeight()) : gfx::IntSize(0, 0);
   }
 
   gl::ShaderProgramType GetShaderProgram() const MOZ_OVERRIDE
   {
+    if (mTextureTarget == LOCAL_GL_TEXTURE_EXTERNAL) {
+      return gl::RGBAExternalLayerProgramType;
+    }
+    MOZ_ASSERT(mTextureTarget == LOCAL_GL_TEXTURE_2D);
     return mFormat == gfx::FORMAT_B8G8R8A8 || mFormat == gfx::FORMAT_B8G8R8X8
            ? gl::BGRALayerProgramType
            : gl::RGBALayerProgramType;
   }
 
   GLenum GetWrapMode() const MOZ_OVERRIDE
   {
     return LOCAL_GL_CLAMP_TO_EDGE;
@@ -600,16 +605,17 @@ public:
     return this;
   }
 
 private:
   void DeleteTextures();
 
   RefPtr<gl::GLContext> mGL;
   android::sp<android::GraphicBuffer> mGraphicBuffer;
+  GLenum mTextureTarget;
   GLuint mGLTexture;
   EGLImage mEGLImage;
 };
 #endif
 
 } // namespace
 } // namespace