Bug 803394 - Add image type for RGB images in shared memory r=nical
authorEdwin Flores <eflores@mozilla.com>
Thu, 13 Dec 2012 11:47:28 +1300
changeset 115866 f7830f9b52d8d4c804554ba854a1dfd88237c444
parent 115865 22c6700c87944a5ade3e3b1bb1051067da00a026
child 115867 5444501188685ed5b216f7b4b6926d3d0411346e
push id24028
push useremorley@mozilla.com
push dateThu, 13 Dec 2012 15:56:02 +0000
treeherdermozilla-central@9db79b97abbb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnical
bugs803394
milestone20.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 803394 - Add image type for RGB images in shared memory r=nical
gfx/layers/ImageTypes.h
gfx/layers/Makefile.in
gfx/layers/ipc/ImageContainerChild.cpp
gfx/layers/ipc/LayersSurfaces.ipdlh
gfx/layers/ipc/SharedRGBImage.cpp
gfx/layers/ipc/SharedRGBImage.h
gfx/layers/opengl/ImageLayerOGL.cpp
gfx/layers/opengl/ImageLayerOGL.h
gfx/thebes/gfxASurface.cpp
gfx/thebes/gfxASurface.h
--- a/gfx/layers/ImageTypes.h
+++ b/gfx/layers/ImageTypes.h
@@ -19,16 +19,23 @@ enum ImageFormat {
   /**
    * The GRALLOC_PLANAR_YCBCR format creates a GrallocPlanarYCbCrImage, a
    * subtype of PlanarYCbCrImage. It takes a PlanarYCbCrImage data and can be
    * used as a texture by Gonk backend directly.
    */
   GRALLOC_PLANAR_YCBCR,
 
   /**
+   * The SHARED_RGB format creates a SharedRGBImage, which stores RGB data in
+   * shared memory. Some Android hardware video decoders require this format.
+   * Currently only used on Android.
+   */
+  SHARED_RGB,
+
+  /**
    * The CAIRO_SURFACE format creates a CairoImage. All backends should
    * support this format, because video rendering sometimes requires it.
    * 
    * This format is useful even though a ThebesLayer could be used.
    * It makes it easy to render a cairo surface when another Image format
    * could be used. It can also avoid copying the surface data in some
    * cases.
    * 
--- a/gfx/layers/Makefile.in
+++ b/gfx/layers/Makefile.in
@@ -136,16 +136,17 @@ EXPORTS_mozilla/layers =\
         ImageContainerChild.h \
         ImageContainerParent.h \
         ShadowLayers.h \
         ShadowLayersChild.h \
         ShadowLayersParent.h \
         ShadowLayersManager.h \
         RenderTrace.h \
         SharedImageUtils.h \
+        SharedRGBImage.h \
         ShmemYCbCrImage.h \
         TaskThrottler.h \
         $(NULL)
 
 CPPSRCS += \
         AsyncPanZoomController.cpp \
         Axis.cpp \
         CompositorCocoaWidgetHelper.cpp \
@@ -156,16 +157,17 @@ CPPSRCS += \
         ImageBridgeParent.cpp \
         ImageContainerChild.cpp \
         ImageContainerParent.cpp \
         ShadowLayers.cpp \
         ShadowLayerChild.cpp \
         ShadowLayersChild.cpp \
         ShadowLayerParent.cpp \
         ShadowLayersParent.cpp \
+        SharedRGBImage.cpp \
         ShmemYCbCrImage.cpp \
         TaskThrottler.cpp \
         $(NULL)
 
 ifdef MOZ_X11 #{
 EXPORTS_mozilla/layers += ShadowLayerUtilsX11.h
 CPPSRCS += ShadowLayerUtilsX11.cpp
 endif #}
--- a/gfx/layers/ipc/ImageContainerChild.cpp
+++ b/gfx/layers/ipc/ImageContainerChild.cpp
@@ -7,16 +7,17 @@
 #include "ImageContainerChild.h"
 #include "gfxSharedImageSurface.h"
 #include "ShadowLayers.h"
 #include "mozilla/layers/PLayers.h"
 #include "mozilla/layers/SharedImageUtils.h"
 #include "ImageContainer.h"
 #include "GonkIOSurfaceImage.h"
 #include "GrallocImages.h"
+#include "SharedRGBImage.h"
 #include "mozilla/layers/ShmemYCbCrImage.h"
 #include "mozilla/ReentrantMonitor.h"
 
 using namespace mozilla::ipc;
 
 namespace mozilla {
 namespace layers {
 
@@ -591,35 +592,36 @@ private:
   nsRefPtr<ImageContainerChild> mImageContainerChild;
   bool mAllocated;
 };
 
 already_AddRefed<Image> ImageContainerChild::CreateImage(const uint32_t *aFormats,
                                                          uint32_t aNumFormats)
 {
   nsRefPtr<Image> img;
-#ifdef MOZ_WIDGET_GONK
   for (uint32_t i = 0; i < aNumFormats; i++) {
     switch (aFormats[i]) {
       case PLANAR_YCBCR:
-#endif
         img = new SharedPlanarYCbCrImage(this);
         return img.forget();
+      case SHARED_RGB:
+        img = new SharedRGBImage(this);
+        return img.forget();
 #ifdef MOZ_WIDGET_GONK
       case GONK_IO_SURFACE:
         img = new GonkIOSurfaceImage();
         return img.forget();
       case GRALLOC_PLANAR_YCBCR:
         img = new GrallocPlanarYCbCrImage();
         return img.forget();
+#endif
     }
   }
 
   return nullptr;
-#endif
 }
 
 SharedImage* ImageContainerChild::AsSharedImage(Image* aImage)
 {
 #ifdef MOZ_WIDGET_GONK
   if (aImage->GetFormat() == GONK_IO_SURFACE) {
     GonkIOSurfaceImage* gonkImage = static_cast<GonkIOSurfaceImage*>(aImage);
     SharedImage* result = new SharedImage(gonkImage->GetSurfaceDescriptor());
@@ -631,15 +633,20 @@ SharedImage* ImageContainerChild::AsShar
   }
 #endif
   if (aImage->GetFormat() == PLANAR_YCBCR) {
     SharedPlanarYCbCrImage* sharedYCbCr
       = static_cast<PlanarYCbCrImage*>(aImage)->AsSharedPlanarYCbCrImage();
     if (sharedYCbCr) {
       return sharedYCbCr->ToSharedImage();
     }
+  } else if (aImage->GetFormat() == SHARED_RGB) {
+    SharedRGBImage *rgbImage = static_cast<SharedRGBImage*>(aImage);
+    if (rgbImage) {
+      return rgbImage->ToSharedImage();
+    }
   }
   return nullptr;
 }
 
 } // namespace
 } // namespace
 
--- a/gfx/layers/ipc/LayersSurfaces.ipdlh
+++ b/gfx/layers/ipc/LayersSurfaces.ipdlh
@@ -69,26 +69,33 @@ union SurfaceDescriptor {
 };
 
 struct YCbCrImage {
   Shmem data;
   size_t offset;
   nsIntRect picture;
 };
 
+struct RGBImage {
+  Shmem data;
+  nsIntRect picture;
+  uint32_t rgbFormat;
+};
+
 // Will be removed in the near future
 struct YUVImage {
   Shmem Ydata;
   Shmem Udata;
   Shmem Vdata;
   nsIntRect picture;
 };
 
 union SharedImage {
   SurfaceDescriptor;
+  RGBImage;
   YCbCrImage;
   YUVImage;
   SharedImageID;
   null_t;
 };
 
 } // namespace
 } // namespace
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/SharedRGBImage.cpp
@@ -0,0 +1,125 @@
+/* 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 "ImageContainerChild.h"
+#include "ShadowLayers.h"
+#include "SharedRGBImage.h"
+#include "Shmem.h"
+
+// Just big enough for a 1080p RGBA32 frame
+#define MAX_FRAME_SIZE (16 * 1024 * 1024)
+
+namespace mozilla {
+namespace layers {
+
+SharedRGBImage::SharedRGBImage(ImageContainerChild *aImageContainerChild) :
+  Image(nullptr, SHARED_RGB),
+  mSize(0, 0),
+  mImageContainerChild(aImageContainerChild),
+  mAllocated(false),
+  mShmem(new ipc::Shmem())
+{
+  mImageContainerChild->AddRef();
+}
+
+SharedRGBImage::~SharedRGBImage()
+{
+  mImageContainerChild->DeallocShmemAsync(*mShmem);
+  mImageContainerChild->Release();
+  delete mShmem;
+}
+
+already_AddRefed<SharedRGBImage>
+SharedRGBImage::Create(ImageContainer *aImageContainer,
+                       nsIntSize aSize,
+                       gfxImageFormat aImageFormat)
+{
+  NS_ASSERTION(aImageFormat == gfxASurface::ImageFormatARGB32 ||
+               aImageFormat == gfxASurface::ImageFormatRGB24 ||
+               aImageFormat == gfxASurface::ImageFormatRGB16_565,
+               "RGB formats supported only");
+
+  if (!aImageContainer) {
+    NS_WARNING("No ImageContainer to allocate SharedRGBImage");
+    return nullptr;
+  }
+
+  ImageFormat format = SHARED_RGB;
+  nsRefPtr<Image> image = aImageContainer->CreateImage(&format, 1);
+
+  if (!image) {
+    NS_WARNING("Failed to create SharedRGBImage");
+    return nullptr;
+  }
+
+  nsRefPtr<SharedRGBImage> rgbImage = static_cast<SharedRGBImage*>(image.get());
+  rgbImage->mSize = gfxIntSize(aSize.width, aSize.height);
+  rgbImage->mImageFormat = aImageFormat;
+
+  if (!rgbImage->AllocateBuffer(aSize, aImageFormat)) {
+    NS_WARNING("Failed to allocate shared memory for SharedRGBImage");
+    return nullptr;
+  }
+
+  return rgbImage.forget();
+}
+
+uint8_t *
+SharedRGBImage::GetBuffer()
+{
+  return mShmem->get<uint8_t>();
+}
+
+size_t
+SharedRGBImage::GetBufferSize()
+{
+  return mSize.width * mSize.height * gfxASurface::BytesPerPixel(mImageFormat);
+}
+
+gfxIntSize
+SharedRGBImage::GetSize()
+{
+  return mSize;
+}
+
+bool
+SharedRGBImage::AllocateBuffer(nsIntSize aSize, gfxImageFormat aImageFormat)
+{
+  if (mAllocated) {
+    NS_WARNING("Already allocated shmem");
+    return false;
+  }
+
+  size_t size = GetBufferSize();
+
+  if (size == 0 || size > MAX_FRAME_SIZE) {
+    NS_WARNING("Invalid frame size");
+  }
+
+  if (mImageContainerChild->AllocUnsafeShmemSync(size, OptimalShmemType(), mShmem)) {
+    mAllocated = true;
+  }
+
+  return mAllocated;
+}
+
+already_AddRefed<gfxASurface>
+SharedRGBImage::GetAsSurface()
+{
+  return nullptr;
+}
+
+SharedImage *
+SharedRGBImage::ToSharedImage()
+{
+  if (!mAllocated) {
+    return nullptr;
+  }
+  return new SharedImage(RGBImage(*mShmem,
+                                  nsIntRect(0, 0, mSize.width, mSize.height),
+                                  mImageFormat));
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/SharedRGBImage.h
@@ -0,0 +1,61 @@
+/* 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 SHAREDRGBIMAGE_H_
+#define SHAREDRGBIMAGE_H_
+
+#include "ImageContainer.h"
+
+namespace mozilla {
+namespace ipc {
+class Shmem;
+}
+namespace layers {
+
+class ImageContainerChild;
+class SharedImage;
+
+/**
+ * Stores RGB data in shared memory
+ * It is assumed that the image width and stride are equal
+ */
+class SharedRGBImage : public Image
+{
+  typedef gfxASurface::gfxImageFormat gfxImageFormat;
+public:
+  struct Header {
+    gfxImageFormat mImageFormat;
+  };
+
+  SharedRGBImage(ImageContainerChild *aImageContainerChild);
+  ~SharedRGBImage();
+
+  static already_AddRefed<SharedRGBImage> Create(ImageContainer *aImageContainer,
+                                                 nsIntSize aSize,
+                                                 gfxImageFormat aImageFormat);
+  uint8_t *GetBuffer();
+
+  gfxIntSize GetSize();
+  size_t GetBufferSize();
+
+  static uint8_t BytesPerPixel(gfxImageFormat aImageFormat);
+  already_AddRefed<gfxASurface> GetAsSurface();
+
+  SharedImage *ToSharedImage();
+
+private:
+  bool AllocateBuffer(nsIntSize aSize, gfxImageFormat aImageFormat);
+
+  gfxIntSize mSize;
+  gfxImageFormat mImageFormat;
+  ImageContainerChild *mImageContainerChild;
+
+  bool mAllocated;
+  ipc::Shmem *mShmem;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
--- a/gfx/layers/opengl/ImageLayerOGL.cpp
+++ b/gfx/layers/opengl/ImageLayerOGL.cpp
@@ -921,16 +921,48 @@ void ShadowImageLayerOGL::UploadSharedYC
   data.mCbChannel = aImage.GetCbData();
   data.mCrChannel = aImage.GetCrData();
   data.mCbCrStride = aImage.GetCbCrStride();
   data.mCbCrSize = aImage.GetCbCrSize();
 
   UploadYUVToTexture(gl(), data, &mYUVTexture[0], &mYUVTexture[1], &mYUVTexture[2]);
 }
 
+void ShadowImageLayerOGL::UploadSharedRGBToTexture(ipc::Shmem *aShmem,
+                                                   nsIntRect aPictureRect,
+                                                   uint32_t aRgbFormat)
+{
+  mPictureRect = aPictureRect;
+
+  if (aPictureRect.width != mSize.width ||
+      aPictureRect.height != mSize.height ||
+      !mRGBTexture.IsAllocated()) {
+    mSize = gfxIntSize(aPictureRect.width, aPictureRect.height);
+
+    if (!mRGBTexture.IsAllocated()) {
+      mRGBTexture.Allocate(gl());
+    }
+
+    gl()->MakeCurrent();
+    SetClamping(gl(), mRGBTexture.GetTextureID());
+  }
+
+  gfxASurface::gfxImageFormat rgbFormat = (gfxASurface::gfxImageFormat)aRgbFormat;
+  GLuint texture = mRGBTexture.GetTextureID();
+  uint32_t stride = gfxASurface::BytesPerPixel(rgbFormat) * aPictureRect.width;
+  nsRefPtr<gfxASurface> surface = new gfxImageSurface(aShmem->get<uint8_t>(),
+                                                      mSize,
+                                                      stride,
+                                                      rgbFormat);
+
+  gl()->UploadSurfaceToTexture(surface,
+                               nsIntRect(0, 0, mSize.width, mSize.height),
+                               texture, true);
+}
+
 
 void
 ShadowImageLayerOGL::RenderLayer(int aPreviousFrameBuffer,
                                  const nsIntPoint& aOffset)
 {
   if (mOGLManager->CompositingDisabled()) {
     return;
   }
@@ -946,16 +978,21 @@ ShadowImageLayerOGL::RenderLayer(int aPr
   
         mImageVersion = imgVersion;
       } else if (img && (img->type() == SharedImage::TYCbCrImage)) {
         ShmemYCbCrImage shmemImage(img->get_YCbCrImage().data(),
                                    img->get_YCbCrImage().offset());
         UploadSharedYCbCrToTexture(shmemImage, img->get_YCbCrImage().picture());
 
         mImageVersion = imgVersion;
+      } else if (img && (img->type() == SharedImage::TRGBImage)) {
+        UploadSharedRGBToTexture(&img->get_RGBImage().data(),
+                                 img->get_RGBImage().picture(),
+                                 img->get_RGBImage().rgbFormat());
+        mImageVersion = imgVersion;
 #ifdef MOZ_WIDGET_GONK
       } else if (img
                  && (img->type() == SharedImage::TSurfaceDescriptor)
                  && (img->get_SurfaceDescriptor().type() == SurfaceDescriptor::TSurfaceDescriptorGralloc)) {
         const SurfaceDescriptorGralloc& desc = img->get_SurfaceDescriptor().get_SurfaceDescriptorGralloc();
         sp<GraphicBuffer> graphicBuffer = GrallocBufferActor::GetFrom(desc);
         mSize = gfxIntSize(graphicBuffer->getWidth(), graphicBuffer->getHeight());
         if (!mExternalBufferTexture.IsAllocated()) {
@@ -1062,16 +1099,36 @@ ShadowImageLayerOGL::RenderLayer(int aPr
 
     gl()->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA,
                              LOCAL_GL_ONE, LOCAL_GL_ONE);
     gl()->ApplyFilterToBoundTexture(mFilter);
     program->SetLayerQuadRect(nsIntRect(nsIntPoint(0, 0), mSize));
     mOGLManager->BindAndDrawQuad(program, mInverted);
     gl()->fBindTexture(handleDetails.mTarget, 0);
     gl()->DetachSharedHandle(mShareType, mSharedHandle);
+  } else if (mRGBTexture.IsAllocated()) {
+    gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
+    gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, mRGBTexture.GetTextureID());
+    gl()->ApplyFilterToBoundTexture(mFilter);
+
+    ShaderProgramOGL *shader = mOGLManager->GetProgram(RGBALayerProgramType, GetMaskLayer());
+    shader->Activate();
+
+    shader->SetLayerQuadRect(nsIntRect(0, 0,
+                                           mPictureRect.width,
+                                           mPictureRect.height));
+    shader->SetTextureUnit(0);
+    shader->SetLayerTransform(GetEffectiveTransform());
+    shader->SetLayerOpacity(GetEffectiveOpacity());
+    shader->SetRenderOffset(aOffset);
+    shader->LoadMask(GetMaskLayer());
+
+    mOGLManager->BindAndDrawQuadWithTextureRect(shader,
+                                                mPictureRect,
+                                                nsIntSize(mSize.width, mSize.height));
   } else {
     gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
     gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, mYUVTexture[0].GetTextureID());
     gl()->ApplyFilterToBoundTexture(mFilter);
     gl()->fActiveTexture(LOCAL_GL_TEXTURE1);
     gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, mYUVTexture[1].GetTextureID());
     gl()->ApplyFilterToBoundTexture(mFilter);
     gl()->fActiveTexture(LOCAL_GL_TEXTURE2);
@@ -1122,13 +1179,14 @@ ShadowImageLayerOGL::CleanupResources()
     gl()->ReleaseSharedHandle(mShareType, mSharedHandle);
     mSharedHandle = 0;
   }
 
   mExternalBufferTexture.Release();
   mYUVTexture[0].Release();
   mYUVTexture[1].Release();
   mYUVTexture[2].Release();
+  mRGBTexture.Release();
   mTexImage = nullptr;
 }
 
 } /* layers */
 } /* mozilla */
--- a/gfx/layers/opengl/ImageLayerOGL.h
+++ b/gfx/layers/opengl/ImageLayerOGL.h
@@ -11,16 +11,19 @@
 
 #include "LayerManagerOGL.h"
 #include "ImageLayers.h"
 #include "ImageContainer.h"
 #include "yuv_convert.h"
 #include "mozilla/Mutex.h"
 
 namespace mozilla {
+namespace ipc {
+class Shmem;
+}
 namespace layers {
 
 class CairoImage;
 class PlanarYCbCrImage;
 class ShmemYCbCrImage;
 
 /**
  * This class wraps a GL texture. It includes a GLContext reference
@@ -184,31 +187,36 @@ private:
   bool Init(const SharedImage& aFront);
   // Will be replaced by UploadSharedYCbCrToTexture after the layers 
   // refactoring. 
   void UploadSharedYUVToTexture(const YUVImage& yuv);
 
   void UploadSharedYCbCrToTexture(ShmemYCbCrImage& aImage,
                                   nsIntRect aPictureRect);
 
+  void UploadSharedRGBToTexture(ipc::Shmem *aShmem,
+                                nsIntRect aPictureRect,
+                                uint32_t aRgbFormat);
+
 
   nsRefPtr<TextureImage> mTexImage;
 
   // For SharedTextureHandle
   gl::SharedTextureHandle mSharedHandle;
   gl::GLContext::SharedTextureShareType mShareType;
   bool mInverted;
   GLuint mTexture;
 
   // For direct texturing with OES_EGL_image_external extension. This
   // texture is allocated when the image supports binding with
   // BindExternalBuffer.
   GLTexture mExternalBufferTexture;
 
   GLTexture mYUVTexture[3];
+  GLTexture mRGBTexture;
   gfxIntSize mSize;
   gfxIntSize mCbCrSize;
   nsIntRect mPictureRect;
 };
 
 } /* layers */
 } /* mozilla */
 #endif /* GFX_IMAGELAYEROGL_H */
--- a/gfx/thebes/gfxASurface.cpp
+++ b/gfx/thebes/gfxASurface.cpp
@@ -656,16 +656,37 @@ gfxASurface::SizeOfExcludingThis(nsMallo
 }
 
 size_t
 gfxASurface::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const
 {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
 }
 
+/* static */ uint8_t
+gfxASurface::BytesPerPixel(gfxImageFormat aImageFormat)
+{
+  switch (aImageFormat) {
+    case ImageFormatARGB32:
+      return 4;
+    case ImageFormatRGB24:
+      return 4;
+    case ImageFormatRGB16_565:
+      return 2;
+    case ImageFormatA8:
+      return 1;
+    case ImageFormatA1:
+      return 1; // Close enough
+    case ImageFormatUnknown:
+    default:
+      NS_NOTREACHED("Not really sure what you want me to say here");
+      return 0;
+  }
+}
+
 #ifdef MOZ_DUMP_IMAGES
 void
 gfxASurface::WriteAsPNG(const char* aFile)
 {
     FILE *file = fopen(aFile, "wb");
     if (file) {
       WriteAsPNG_internal(file, true);
       fclose(file);
--- a/gfx/thebes/gfxASurface.h
+++ b/gfx/thebes/gfxASurface.h
@@ -279,16 +279,18 @@ public:
                             const nsIntPoint& aDestTopLeft);
 
     /**
      * Mark the surface as being allowed/not allowed to be used as a source.
      */
     void SetAllowUseAsSource(bool aAllow) { mAllowUseAsSource = aAllow; }
     bool GetAllowUseAsSource() { return mAllowUseAsSource; }
 
+    static uint8_t BytesPerPixel(gfxImageFormat aImageFormat);
+
 protected:
     gfxASurface() : mSurface(nullptr), mFloatingRefs(0), mBytesRecorded(0),
                     mSurfaceValid(false), mAllowUseAsSource(true)
     {
         MOZ_COUNT_CTOR(gfxASurface);
     }
 
     static gfxASurface* GetSurfaceWrapper(cairo_surface_t *csurf);