Bug 774552 - Draw GraphicBuffer directly. r=roc,BenWa
authorKan-Ru Chen <kanru@kanru.info>
Tue, 17 Jul 2012 10:12:58 +0800
changeset 99509 2bf404e6e7e207a3632e21c8c3c47883dc356027
parent 99508 dd08c10193c62abf525b3be5a3ecffbc6012c152
child 99510 ba8463beab132c2c6be9a576d7d25d094671844a
push id23135
push useremorley@mozilla.com
push dateTue, 17 Jul 2012 09:04:49 +0000
treeherdermozilla-central@ba8463beab13 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc, BenWa
bugs774552
milestone17.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 774552 - Draw GraphicBuffer directly. r=roc,BenWa
gfx/gl/GLContext.h
gfx/gl/GLContextProviderEGL.cpp
gfx/layers/ImageLayers.cpp
gfx/layers/ImageLayers.h
gfx/layers/opengl/ImageLayerOGL.cpp
gfx/layers/opengl/LayerManagerOGLProgram.cpp
gfx/layers/opengl/LayerManagerOGLShaders.h
gfx/layers/opengl/LayerManagerOGLShaders.txt
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -50,16 +50,17 @@ class GLContext;
 typedef uintptr_t SharedTextureHandle;
 
 enum ShaderProgramType {
     RGBALayerProgramType,
     BGRALayerProgramType,
     RGBXLayerProgramType,
     BGRXLayerProgramType,
     RGBARectLayerProgramType,
+    RGBAExternalLayerProgramType,
     ColorLayerProgramType,
     YCbCrLayerProgramType,
     ComponentAlphaPass1ProgramType,
     ComponentAlphaPass2ProgramType,
     Copy2DProgramType,
     Copy2DRectProgramType,
     NumProgramTypes
 };
@@ -735,16 +736,17 @@ public:
      */
     virtual bool ReleaseTexImage() { return false; }
 
     /**
      * Applies aFilter to the texture currently bound to GL_TEXTURE_2D.
      */
     void ApplyFilterToBoundTexture(gfxPattern::GraphicsFilter aFilter);
 
+    virtual bool BindExternalBuffer(GLuint texture, void* buffer) { return false; }
 
     /*
      * Offscreen support API
      */
 
     /*
      * Bind aOffscreen's color buffer as a texture to the TEXTURE_2D
      * target.  Returns TRUE on success, otherwise FALSE.  If
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -39,16 +39,17 @@
 # if defined(MOZ_WIDGET_GONK)
 #  include "cutils/properties.h"
 #  include <ui/GraphicBuffer.h>
 
 using namespace android;
 
 # define EGL_NATIVE_BUFFER_ANDROID 0x3140
 # define EGL_IMAGE_PRESERVED_KHR   0x30D2
+# define GL_TEXTURE_EXTERNAL_OES   0x8D65
 
 # endif
 
 #endif
 
 #define GLES2_LIB "libGLESv2.so"
 #define GLES2_LIB2 "libGLESv2.so.2"
 
@@ -398,16 +399,36 @@ public:
                                                LOCAL_EGL_BACK_BUFFER);
         if (success == LOCAL_EGL_FALSE)
             return false;
 
         mBound = false;
         return true;
     }
 
+    bool BindExternalBuffer(GLuint texture, void* buffer)
+    {
+#if defined(MOZ_WIDGET_GONK)
+        EGLint attrs[] = {
+            EGL_IMAGE_PRESERVED_KHR, LOCAL_EGL_TRUE,
+            LOCAL_EGL_NONE, LOCAL_EGL_NONE
+        };
+        EGLImage image = sEGLLibrary.fCreateImage(EGL_DISPLAY(),
+                                                  EGL_NO_CONTEXT,
+                                                  EGL_NATIVE_BUFFER_ANDROID,
+                                                  buffer, attrs);
+        sEGLLibrary.fImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image);
+        fBindTexture(GL_TEXTURE_EXTERNAL_OES, texture);
+        sEGLLibrary.fDestroyImage(EGL_DISPLAY(), image);
+        return true;
+#else
+        return false;
+#endif
+    }
+
     bool MakeCurrentImpl(bool aForce = false) {
         bool succeeded = true;
 
         // Assume that EGL has the same problem as WGL does,
         // where MakeCurrent with an already-current context is
         // still expensive.
 #ifndef MOZ_WIDGET_QT
         if (!mSurface) {
--- a/gfx/layers/ImageLayers.cpp
+++ b/gfx/layers/ImageLayers.cpp
@@ -42,16 +42,20 @@ ImageFactory::CreateImage(const Image::F
   if (FormatInList(aFormats, aNumFormats, Image::PLANAR_YCBCR)) {
     img = new PlanarYCbCrImage(aRecycleBin);
   } else if (FormatInList(aFormats, aNumFormats, Image::CAIRO_SURFACE)) {
     img = new CairoImage();
 #ifdef XP_MACOSX
   } else if (FormatInList(aFormats, aNumFormats, Image::MAC_IO_SURFACE)) {
     img = new MacIOSurfaceImage();
 #endif
+#ifdef MOZ_WIDGET_GONK
+  } else if (FormatInList(aFormats, aNumFormats, Image::GONK_IO_SURFACE)) {
+    img = new GonkIOSurfaceImage();
+#endif
   }
   return img.forget();
 }
 
 BufferRecycleBin::BufferRecycleBin()
   : mLock("mozilla.layers.BufferRecycleBin.mLock")
 {
 }
--- a/gfx/layers/ImageLayers.h
+++ b/gfx/layers/ImageLayers.h
@@ -22,16 +22,19 @@
 #endif
 #ifdef XP_WIN
 struct ID3D10Texture2D;
 struct ID3D10Device;
 struct ID3D10ShaderResourceView;
 
 typedef void* HANDLE;
 #endif
+#ifdef MOZ_WIDGET_GONK
+# include <ui/GraphicBuffer.h>
+#endif
 
 namespace mozilla {
 
 class CrossProcessMutex;
 namespace ipc {
 class Shmem;
 }
 
@@ -102,16 +105,23 @@ public:
     /**
      * The MAC_IO_SURFACE format creates a MacIOSurfaceImage.
      *
      * It wraps an IOSurface object and binds it directly to a GL texture.
      */
     MAC_IO_SURFACE,
 
     /**
+     * The GONK_IO_SURFACE format creates a GonkIOSurfaceImage.
+     *
+     * It wraps an GraphicBuffer object and binds it directly to a GL texture.
+     */
+    GONK_IO_SURFACE,
+
+    /**
      * An bitmap image that can be shared with a remote process.
      */
     REMOTE_IMAGE_BITMAP,
 
     /**
      * An DXGI shared surface handle that can be shared with a remote process.
      */
     REMOTE_IMAGE_DXGI_TEXTURE
@@ -919,16 +929,92 @@ private:
   gfxIntSize mSize;
   nsRefPtr<nsIOSurface> mIOSurface;
   void* mPluginInstanceOwner;
   UpdateSurfaceCallback mUpdateCallback;
   DestroyCallback mDestroyCallback;
 };
 #endif
 
+#ifdef MOZ_WIDGET_GONK
+/**
+ * The gralloc buffer maintained by android GraphicBuffer can be
+ * shared between the compositor thread and the producer thread. The
+ * mGraphicBuffer is owned by the producer thread, but when it is
+ * wrapped by GraphicBufferLocked and passed to the compositor, the
+ * buffer content is guaranteed to not change until Unlock() is
+ * called. Each producer must maintain their own buffer queue and
+ * implement the GraphicBufferLocked::Unlock() interface.
+ */
+class GraphicBufferLocked {
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GraphicBufferLocked)
+
+public:
+  GraphicBufferLocked(android::GraphicBuffer* aGraphicBuffer)
+    : mGraphicBuffer(aGraphicBuffer)
+  {}
+
+  virtual ~GraphicBufferLocked() {}
+
+  virtual void Unlock() {}
+
+  virtual void* GetNativeBuffer()
+  {
+    return mGraphicBuffer->getNativeBuffer();
+  }   
+
+protected:
+  android::GraphicBuffer* mGraphicBuffer;
+};
+
+class THEBES_API GonkIOSurfaceImage : public Image {
+public:
+  struct Data {
+    nsRefPtr<GraphicBufferLocked> mGraphicBuffer;
+    gfxIntSize mPicSize;
+  };
+
+  GonkIOSurfaceImage()
+    : Image(NULL, GONK_IO_SURFACE)
+    , mSize(0, 0)
+    {}
+
+  virtual ~GonkIOSurfaceImage()
+  {
+    mGraphicBuffer->Unlock();
+  }
+
+  virtual void SetData(const Data& aData)
+  {
+    mGraphicBuffer = aData.mGraphicBuffer;
+    mSize = aData.mPicSize;
+  }
+
+  virtual gfxIntSize GetSize()
+  {
+    return mSize;
+  }
+
+  virtual already_AddRefed<gfxASurface> GetAsSurface()
+  {
+    // We need to fix this and return a ASurface at some point.
+    return nsnull;
+  }
+
+  void* GetNativeBuffer()
+  {
+    return mGraphicBuffer->GetNativeBuffer();
+  }
+
+private:
+  nsRefPtr<GraphicBufferLocked> mGraphicBuffer;
+  gfxIntSize mSize;
+};
+#endif
+
 class RemoteBitmapImage : public Image {
 public:
   RemoteBitmapImage() : Image(NULL, REMOTE_IMAGE_BITMAP) {}
 
   already_AddRefed<gfxASurface> GetAsSurface();
 
   gfxIntSize GetSize() { return mSize; }
 
--- a/gfx/layers/opengl/ImageLayerOGL.cpp
+++ b/gfx/layers/opengl/ImageLayerOGL.cpp
@@ -171,16 +171,33 @@ AllocateTextureIOSurface(MacIOSurfaceIma
                                      LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV, 0);
 
   aGL->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0);
 
   aIOImage->SetBackendData(LayerManager::LAYERS_OPENGL, backendData.forget());
 }
 #endif
 
+#ifdef MOZ_WIDGET_GONK
+struct THEBES_API GonkIOSurfaceImageOGLBackendData : public ImageBackendData
+{
+  GLTexture mTexture;
+};
+
+void
+AllocateTextureIOSurface(GonkIOSurfaceImage *aIOImage, mozilla::gl::GLContext* aGL)
+{
+  nsAutoPtr<GonkIOSurfaceImageOGLBackendData> backendData(
+    new GonkIOSurfaceImageOGLBackendData);
+
+  backendData->mTexture.Allocate(aGL);
+  aIOImage->SetBackendData(LayerManager::LAYERS_OPENGL, backendData.forget());
+}
+#endif
+
 Layer*
 ImageLayerOGL::GetLayer()
 {
   return this;
 }
 
 void
 ImageLayerOGL::RenderLayer(int,
@@ -292,18 +309,16 @@ ImageLayerOGL::RenderLayer(int,
     }
 
     if (!data || data->mTexture.GetGLContext() != gl()) {
       // XXX - Can this ever happen? If so I need to fix this!
       return;
     }
 
     gl()->MakeCurrent();
-    unsigned int iwidth  = cairoImage->mSize.width;
-    unsigned int iheight = cairoImage->mSize.height;
 
     gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
     gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, data->mTexture.GetTextureID());
 
 #if defined(MOZ_WIDGET_GTK2) && !defined(MOZ_PLATFORM_MAEMO)
     GLXPixmap pixmap;
 
     if (cairoImage->mSurface) {
@@ -316,61 +331,31 @@ ImageLayerOGL::RenderLayer(int,
 #endif
 
     ShaderProgramOGL *program = 
       mOGLManager->GetProgram(data->mLayerProgram, GetMaskLayer());
 
     gl()->ApplyFilterToBoundTexture(mFilter);
 
     program->Activate();
-    // The following uniform controls the scaling of the vertex coords.
-    // Instead of setting the scale here and using coords in the range [0,1], we
-    // set an identity transform and use pixel coordinates below
-    program->SetLayerQuadRect(nsIntRect(0, 0, 1, 1));
+    program->SetLayerQuadRect(nsIntRect(0, 0, 
+                                        cairoImage->GetSize().width, 
+                                        cairoImage->GetSize().height));
     program->SetLayerTransform(GetEffectiveTransform());
     program->SetLayerOpacity(GetEffectiveOpacity());
     program->SetRenderOffset(aOffset);
     program->SetTextureUnit(0);
     program->LoadMask(GetMaskLayer());
 
-    nsIntRect rect = GetVisibleRegion().GetBounds();
-
-    GLContext::RectTriangles triangleBuffer;
-
-    float tex_offset_u = float(rect.x % iwidth) / iwidth;
-    float tex_offset_v = float(rect.y % iheight) / iheight;
-    triangleBuffer.addRect(rect.x, rect.y,
-                           rect.x + rect.width, rect.y + rect.height,
-                           tex_offset_u, tex_offset_v,
-                           tex_offset_u + float(rect.width) / float(iwidth),
-                           tex_offset_v + float(rect.height) / float(iheight));
-
-    GLuint vertAttribIndex =
-        program->AttribLocation(ShaderProgramOGL::VertexCoordAttrib);
-    GLuint texCoordAttribIndex =
-        program->AttribLocation(ShaderProgramOGL::TexCoordAttrib);
-    NS_ASSERTION(texCoordAttribIndex != GLuint(-1), "no texture coords?");
+    mOGLManager->BindAndDrawQuadWithTextureRect(program,
+                                                GetVisibleRegion().GetBounds(),
+                                                nsIntSize(cairoImage->GetSize().width,
+                                                          cairoImage->GetSize().height));
 
-    gl()->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
-    gl()->fVertexAttribPointer(vertAttribIndex, 2,
-                               LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0,
-                               triangleBuffer.vertexPointer());
 
-    gl()->fVertexAttribPointer(texCoordAttribIndex, 2,
-                               LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0,
-                               triangleBuffer.texCoordPointer());
-    {
-        gl()->fEnableVertexAttribArray(texCoordAttribIndex);
-        {
-            gl()->fEnableVertexAttribArray(vertAttribIndex);
-            gl()->fDrawArrays(LOCAL_GL_TRIANGLES, 0, triangleBuffer.elements());
-            gl()->fDisableVertexAttribArray(vertAttribIndex);
-        }
-        gl()->fDisableVertexAttribArray(texCoordAttribIndex);
-    }
 #if defined(MOZ_WIDGET_GTK2) && !defined(MOZ_PLATFORM_MAEMO)
     if (cairoImage->mSurface && pixmap) {
         sGLXLibrary.ReleaseTexImage(pixmap);
         sGLXLibrary.DestroyPixmap(pixmap);
     }
 #endif
 #ifdef XP_MACOSX
   } else if (image->GetFormat() == Image::MAC_IO_SURFACE) {
@@ -423,16 +408,55 @@ ImageLayerOGL::RenderLayer(int,
      program->SetLayerOpacity(GetEffectiveOpacity());
      program->SetRenderOffset(aOffset);
      program->SetTextureUnit(0);
      program->LoadMask(GetMaskLayer());
     
      mOGLManager->BindAndDrawQuad(program);
      gl()->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0);
 #endif
+#ifdef MOZ_WIDGET_GONK
+  } else if (image->GetFormat() == Image::GONK_IO_SURFACE) {
+
+    GonkIOSurfaceImage *ioImage = static_cast<GonkIOSurfaceImage*>(image);
+    if (!ioImage) {
+      return;
+    }
+
+    gl()->MakeCurrent();
+    gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
+
+    if (!ioImage->GetBackendData(LayerManager::LAYERS_OPENGL)) {
+      AllocateTextureIOSurface(ioImage, gl());
+    }
+    GonkIOSurfaceImageOGLBackendData *data =
+      static_cast<GonkIOSurfaceImageOGLBackendData*>(ioImage->GetBackendData(LayerManager::LAYERS_OPENGL));
+
+    gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
+    gl()->BindExternalBuffer(data->mTexture.GetTextureID(), ioImage->GetNativeBuffer());
+
+    ShaderProgramOGL *program = mOGLManager->GetProgram(RGBAExternalLayerProgramType, GetMaskLayer());
+
+    gl()->ApplyFilterToBoundTexture(mFilter);
+
+    program->Activate();
+    program->SetLayerQuadRect(nsIntRect(0, 0, 
+                                        ioImage->GetSize().width, 
+                                        ioImage->GetSize().height));
+    program->SetLayerTransform(GetEffectiveTransform());
+    program->SetLayerOpacity(GetEffectiveOpacity());
+    program->SetRenderOffset(aOffset);
+    program->SetTextureUnit(0);
+    program->LoadMask(GetMaskLayer());
+
+    mOGLManager->BindAndDrawQuadWithTextureRect(program,
+                                                GetVisibleRegion().GetBounds(),
+                                                nsIntSize(ioImage->GetSize().width,
+                                                          ioImage->GetSize().height));
+#endif
   }
   GetContainer()->NotifyPaintedImage(image);
 }
 
 static void
 SetClamping(GLContext* aGL, GLuint aTexture)
 {
   aGL->fBindTexture(LOCAL_GL_TEXTURE_2D, aTexture);
--- a/gfx/layers/opengl/LayerManagerOGLProgram.cpp
+++ b/gfx/layers/opengl/LayerManagerOGLProgram.cpp
@@ -99,16 +99,31 @@ ProgramProfileOGL::GetProfileFor(gl::Sha
     } else {
       result.mVertexShaderString = sLayerVS;
       result.mFragmentShaderString = sRGBARectTextureLayerFS;
     }
     AddCommonArgs(result);
     AddCommonTextureArgs(result);
     result.mTextureCount = 1;
     break;
+  case gl::RGBAExternalLayerProgramType:
+    if (aMask == Mask3d) {
+      result.mVertexShaderString = sLayerMask3DVS;
+      result.mFragmentShaderString = sRGBAExternalTextureLayerMask3DFS;
+    } else if (aMask == Mask2d) {
+      result.mVertexShaderString = sLayerMaskVS;
+      result.mFragmentShaderString = sRGBAExternalTextureLayerMaskFS;
+    } else {
+      result.mVertexShaderString = sLayerVS;
+      result.mFragmentShaderString = sRGBAExternalTextureLayerFS;
+    }
+    AddCommonArgs(result);
+    AddCommonTextureArgs(result);
+    result.mTextureCount = 1;
+    break;
   case gl::ColorLayerProgramType:
     if (aMask == Mask2d) {
       result.mVertexShaderString = sLayerMaskVS;
       result.mFragmentShaderString = sSolidColorLayerMaskFS;
     } else {
       result.mVertexShaderString = sLayerVS;
       result.mFragmentShaderString = sSolidColorLayerFS;
     }
@@ -322,17 +337,17 @@ ShaderProgramOGL::CreateProgram(const ch
   if (!success) {
     mGL->fDeleteProgram(result);
     return false;
   }
 
   mProgram = result;
   return true;
 }
-
+
 bool
 ShaderProgramOGL::LoadMask(Layer* aMaskLayer)
 {
   if (!aMaskLayer) {
     return false;
   }
 
   gfxIntSize size;
--- a/gfx/layers/opengl/LayerManagerOGLShaders.h
+++ b/gfx/layers/opengl/LayerManagerOGLShaders.h
@@ -336,16 +336,98 @@ gl_FragColor = texture2DRect(uTexture, v
 #else\n\
 void main()\n\
 {\n\
 gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n\
 }\n\
 #endif\n\
 ";
 
+static const char sRGBAExternalTextureLayerFS[] = "/* sRGBAExternalTextureLayerFS */\n\
+#extension GL_OES_EGL_image_external : require\n\
+/* Fragment Shader */\n\
+#ifdef GL_ES\n\
+precision lowp float;\n\
+#endif\n\
+\n\
+#ifndef NO_LAYER_OPACITY\n\
+uniform float uLayerOpacity;\n\
+#endif\n\
+#ifdef GL_ES // for tiling, texcoord can be greater than the lowfp range\n\
+varying mediump vec2 vTexCoord;\n\
+#else\n\
+varying vec2 vTexCoord;\n\
+#endif\n\
+\n\
+uniform samplerExternalOES uTexture;\n\
+void main()\n\
+{\n\
+float mask = 1.0;\n\
+\n\
+gl_FragColor = texture2D(uTexture, vTexCoord) * uLayerOpacity * mask;\n\
+}\n\
+";
+
+static const char sRGBAExternalTextureLayerMaskFS[] = "/* sRGBAExternalTextureLayerMaskFS */\n\
+#extension GL_OES_EGL_image_external : require\n\
+/* Fragment Shader */\n\
+#ifdef GL_ES\n\
+precision lowp float;\n\
+#endif\n\
+\n\
+#ifndef NO_LAYER_OPACITY\n\
+uniform float uLayerOpacity;\n\
+#endif\n\
+#ifdef GL_ES // for tiling, texcoord can be greater than the lowfp range\n\
+varying mediump vec2 vTexCoord;\n\
+#else\n\
+varying vec2 vTexCoord;\n\
+#endif\n\
+\n\
+varying vec2 vMaskCoord;\n\
+uniform sampler2D uMaskTexture;\n\
+\n\
+uniform samplerExternalOES uTexture;\n\
+void main()\n\
+{\n\
+float mask = texture2D(uMaskTexture, vMaskCoord).r;\n\
+\n\
+gl_FragColor = texture2D(uTexture, vTexCoord) * uLayerOpacity * mask;\n\
+}\n\
+";
+
+static const char sRGBAExternalTextureLayerMask3DFS[] = "/* sRGBAExternalTextureLayerMask3DFS */\n\
+#extension GL_OES_EGL_image_external : require\n\
+/* Fragment Shader */\n\
+#ifdef GL_ES\n\
+precision lowp float;\n\
+#endif\n\
+\n\
+#ifndef NO_LAYER_OPACITY\n\
+uniform float uLayerOpacity;\n\
+#endif\n\
+#ifdef GL_ES // for tiling, texcoord can be greater than the lowfp range\n\
+varying mediump vec2 vTexCoord;\n\
+#else\n\
+varying vec2 vTexCoord;\n\
+#endif\n\
+\n\
+varying vec3 vMaskCoord;\n\
+uniform sampler2D uMaskTexture;\n\
+\n\
+uniform samplerExternalOES uTexture;\n\
+void main()\n\
+{\n\
+vec2 maskCoords = vMaskCoord.xy / vMaskCoord.z;\n\
+float mask = texture2D(uMaskTexture, maskCoords).r;\n\
+\n\
+gl_FragColor = texture2D(uTexture, vTexCoord) * uLayerOpacity * mask;\n\
+}\n\
+";
+
 static const char sBGRATextureLayerFS[] = "/* sBGRATextureLayerFS */\n\
 /* Fragment Shader */\n\
 #ifdef GL_ES\n\
 precision lowp float;\n\
 #endif\n\
 \n\
 #ifndef NO_LAYER_OPACITY\n\
 uniform float uLayerOpacity;\n\
--- a/gfx/layers/opengl/LayerManagerOGLShaders.txt
+++ b/gfx/layers/opengl/LayerManagerOGLShaders.txt
@@ -231,16 +231,35 @@ void main()
 #else
 void main()
 {
   gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
 }
 #endif
 @end
 
+// Single texture in RGBA format, but uses external image.  External
+// image is an EGLImage which have internal formats not otherwise
+// supported by OpenGL ES. It is up to the implementation exactly what
+// formats are accepted. It is specified in the OES_EGL_image_external
+// extension.
+@shader sRGBAExternalTextureLayer<mask:,Mask,Mask3D>FS
+#extension GL_OES_EGL_image_external : require
+
+$LAYER_FRAGMENT<mask>$
+uniform samplerExternalOES uTexture;
+
+void main()
+{
+$FRAGMENT_CALC_MASK<mask>$
+  gl_FragColor = texture2D(uTexture, vTexCoord) * uLayerOpacity * mask;
+}
+@end
+
+
 // Single texture in BGRA format (via swizzle)
 @shader sBGRATextureLayer<mask:,Mask>FS
 $LAYER_FRAGMENT<mask>$
 uniform sampler2D uTexture;
 
 void main()
 {
 $FRAGMENT_CALC_MASK<mask>$