From cd154b137b5afc50131258f6451bfda10f368a0a Mon Sep 17 00:00:00 2001 From: David Anderson Date: Wed, 2 Dec 2015 11:31:17 -0800 Subject: [PATCH] Implement the direct bitmap drawing model for plugins. (bug 1217665 part 6, r=mattwoodrow) --- dom/plugins/ipc/PPluginInstance.ipdl | 13 ++ dom/plugins/ipc/PluginInstanceChild.cpp | 153 +++++++++++++++++++++-- dom/plugins/ipc/PluginInstanceChild.h | 26 +++- dom/plugins/ipc/PluginInstanceParent.cpp | 119 +++++++++++++++++- dom/plugins/ipc/PluginInstanceParent.h | 20 ++- dom/plugins/ipc/PluginModuleParent.cpp | 10 ++ dom/plugins/ipc/PluginModuleParent.h | 8 ++ gfx/thebes/gfxPlatformGtk.h | 4 + gfx/thebes/gfxUtils.h | 14 +++ gfx/thebes/gfxWindowsPlatform.h | 3 + 10 files changed, 357 insertions(+), 13 deletions(-) diff --git a/dom/plugins/ipc/PPluginInstance.ipdl b/dom/plugins/ipc/PPluginInstance.ipdl index b0694450d3f..b3f19d5105c 100644 --- a/dom/plugins/ipc/PPluginInstance.ipdl +++ b/dom/plugins/ipc/PPluginInstance.ipdl @@ -23,10 +23,12 @@ using NPNVariable from "npapi.h"; using mozilla::plugins::NativeWindowHandle from "mozilla/plugins/PluginMessageUtils.h"; using gfxSurfaceType from "gfxTypes.h"; using mozilla::gfx::IntSize from "mozilla/gfx/2D.h"; +using mozilla::gfx::IntRect from "mozilla/gfx/2D.h"; using struct mozilla::null_t from "ipc/IPCMessageUtils.h"; using mozilla::plugins::WindowsSharedMemoryHandle from "mozilla/plugins/PluginMessageUtils.h"; using mozilla::layers::SurfaceDescriptorX11 from "gfxipc/ShadowLayerUtils.h"; using nsIntRect from "nsRect.h"; +using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h"; namespace mozilla { namespace plugins { @@ -180,6 +182,17 @@ parent: async NPN_InvalidateRect(NPRect rect); + // Clear the current plugin image. + sync RevokeCurrentDirectSurface(); + + // Set the current plugin image to the bitmap in the given shmem buffer. The + // format must be B8G8R8A8 or B8G8R8X8. + sync ShowDirectBitmap(Shmem buffer, + SurfaceFormat format, + uint32_t stride, + IntSize size, + IntRect dirty); + // Give |newSurface|, containing this instance's updated pixels, to // the browser for compositing. When this method returns, any surface // previously passed to Show may be destroyed. diff --git a/dom/plugins/ipc/PluginInstanceChild.cpp b/dom/plugins/ipc/PluginInstanceChild.cpp index 4d00403ecd8..59116ad6a2d 100644 --- a/dom/plugins/ipc/PluginInstanceChild.cpp +++ b/dom/plugins/ipc/PluginInstanceChild.cpp @@ -131,6 +131,7 @@ PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface, , mContentsScaleFactor(1.0) #endif , mDrawingModel(kDefaultDrawingModel) + , mCurrentDirectSurface(nullptr) , mAsyncInvalidateMutex("PluginInstanceChild::mAsyncInvalidateMutex") , mAsyncInvalidateTask(0) , mCachedWindowActor(nullptr) @@ -2553,20 +2554,108 @@ PluginInstanceChild::IsUsingDirectDrawing() return IsDrawingModelDirect(mDrawingModel); } +PluginInstanceChild::DirectBitmap::DirectBitmap(PluginInstanceChild* aOwner, const Shmem& shmem, + const IntSize& size, uint32_t stride, SurfaceFormat format) + : mOwner(aOwner), + mShmem(shmem), + mFormat(format), + mSize(size), + mStride(stride) +{ +} + +PluginInstanceChild::DirectBitmap::~DirectBitmap() +{ + mOwner->DeallocShmem(mShmem); +} + +static inline SurfaceFormat +NPImageFormatToSurfaceFormat(NPImageFormat aFormat) +{ + switch (aFormat) { + case NPImageFormatBGRA32: + return SurfaceFormat::B8G8R8A8; + case NPImageFormatBGRX32: + return SurfaceFormat::B8G8R8X8; + default: + MOZ_ASSERT_UNREACHABLE("unknown NPImageFormat"); + return SurfaceFormat::UNKNOWN; + } +} + NPError PluginInstanceChild::NPN_InitAsyncSurface(NPSize *size, NPImageFormat format, void *initData, NPAsyncSurface *surface) { AssertPluginThread(); - surface->bitmap.data = NULL; - if (!IsUsingDirectDrawing()) { - return NPERR_GENERIC_ERROR; + return NPERR_INVALID_PARAM; + } + if (format != NPImageFormatBGRA32 && format != NPImageFormatBGRX32) { + return NPERR_INVALID_PARAM; } - MOZ_CRASH("NYI"); - return NPERR_GENERIC_ERROR; + PodZero(surface); + + // NPAPI guarantees that the SetCurrentAsyncSurface call will release the + // previous surface if it was different. However, no functionality exists + // within content to synchronize a non-shadow-layers transaction with the + // compositor. + // + // To get around this, we allocate two surfaces: a child copy, which we + // hand off to the plugin, and a parent copy, which we will hand off to + // the compositor. Each call to SetCurrentAsyncSurface will copy the + // invalid region from the child surface to its parent. + switch (mDrawingModel) { + case NPDrawingModelAsyncBitmapSurface: { + // Validate that the caller does not expect initial data to be set. + if (initData) { + return NPERR_INVALID_PARAM; + } + + // Validate that we're not double-allocating a surface. + RefPtr holder; + if (mDirectBitmaps.Get(surface, getter_AddRefs(holder))) { + return NPERR_INVALID_PARAM; + } + + SurfaceFormat mozformat = NPImageFormatToSurfaceFormat(format); + int32_t bytesPerPixel = BytesPerPixel(mozformat); + + if (size->width <= 0 || size->height <= 0) { + return NPERR_INVALID_PARAM; + } + + CheckedInt nbytes = SafeBytesForBitmap(size->width, size->height, bytesPerPixel); + if (!nbytes.isValid()) { + return NPERR_INVALID_PARAM; + } + + Shmem shmem; + if (!AllocUnsafeShmem(nbytes.value(), SharedMemory::TYPE_BASIC, &shmem)) { + return NPERR_OUT_OF_MEMORY_ERROR; + } + MOZ_ASSERT(shmem.Size() == nbytes.value()); + + surface->version = 0; + surface->size = *size; + surface->format = format; + surface->bitmap.data = shmem.get(); + surface->bitmap.stride = size->width * bytesPerPixel; + + // Hold the shmem alive until Finalize() is called or this actor dies. + holder = new DirectBitmap(this, shmem, + IntSize(size->width, size->height), + surface->bitmap.stride, mozformat); + mDirectBitmaps.Put(surface, holder); + return NPERR_NO_ERROR; + } + default: + MOZ_ASSERT_UNREACHABLE("unknown drawing model"); + } + + return NPERR_INVALID_PARAM; } NPError @@ -2578,17 +2667,64 @@ PluginInstanceChild::NPN_FinalizeAsyncSurface(NPAsyncSurface *surface) return NPERR_GENERIC_ERROR; } - MOZ_CRASH("NYI"); - return NPERR_GENERIC_ERROR; + // The API forbids this. If it becomes a problem we can revoke the current + // surface instead. + MOZ_ASSERT(!surface || mCurrentDirectSurface != surface); + + switch (mDrawingModel) { + case NPDrawingModelAsyncBitmapSurface: { + RefPtr bitmap; + if (!mDirectBitmaps.Get(surface, getter_AddRefs(bitmap))) { + return NPERR_INVALID_PARAM; + } + + PodZero(surface); + mDirectBitmaps.Remove(surface); + return NPERR_NO_ERROR; + } + default: + MOZ_ASSERT_UNREACHABLE("unknown drawing model"); + } + + return NPERR_INVALID_PARAM; } void PluginInstanceChild::NPN_SetCurrentAsyncSurface(NPAsyncSurface *surface, NPRect *changed) { + AssertPluginThread(); + if (!IsUsingDirectDrawing()) { return; } - MOZ_CRASH("NYI"); + + mCurrentDirectSurface = surface; + + if (!surface) { + SendRevokeCurrentDirectSurface(); + return; + } + + switch (mDrawingModel) { + case NPDrawingModelAsyncBitmapSurface: { + RefPtr bitmap; + if (!mDirectBitmaps.Get(surface, getter_AddRefs(bitmap))) { + return; + } + + IntRect dirty = changed + ? IntRect(changed->left, changed->top, + changed->right - changed->left, changed->bottom - changed->top) + : IntRect(IntPoint(0, 0), bitmap->mSize); + + // Need a holder since IPDL zaps the object for mysterious reasons. + Shmem shmemHolder = bitmap->mShmem; + SendShowDirectBitmap(shmemHolder, bitmap->mFormat, bitmap->mStride, bitmap->mSize, dirty); + break; + } + default: + MOZ_ASSERT_UNREACHABLE("unknown drawing model"); + } } void @@ -3907,6 +4043,7 @@ PluginInstanceChild::Destroy() } ClearAllSurfaces(); + mDirectBitmaps.Clear(); mDeletingHash = new nsTHashtable; PluginScriptableObjectChild::NotifyOfInstanceShutdown(this); diff --git a/dom/plugins/ipc/PluginInstanceChild.h b/dom/plugins/ipc/PluginInstanceChild.h index d06f4d4a836..7e64e46975b 100644 --- a/dom/plugins/ipc/PluginInstanceChild.h +++ b/dom/plugins/ipc/PluginInstanceChild.h @@ -12,7 +12,7 @@ #include "mozilla/plugins/StreamNotifyChild.h" #include "mozilla/plugins/PPluginSurfaceChild.h" #include "mozilla/ipc/CrossProcessMutex.h" -#include "nsClassHashtable.h" +#include "nsRefPtrHashtable.h" #if defined(OS_WIN) #include "mozilla/gfx/SharedDIBWin.h" #elif defined(MOZ_WIDGET_COCOA) @@ -30,6 +30,7 @@ #include "nsRect.h" #include "nsTHashtable.h" #include "mozilla/PaintTracker.h" +#include "mozilla/gfx/Types.h" #include @@ -394,6 +395,29 @@ private: #endif int16_t mDrawingModel; + NPAsyncSurface* mCurrentDirectSurface; + + // The surface hashtables below serve a few purposes. They let us verify + // and retain extra information about plugin surfaces, and they let us + // free shared memory that the plugin might forget to release. + struct DirectBitmap { + DirectBitmap(PluginInstanceChild* aOwner, const Shmem& shmem, + const gfx::IntSize& size, uint32_t stride, SurfaceFormat format); + + private: + ~DirectBitmap(); + + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DirectBitmap); + + PluginInstanceChild* mOwner; + Shmem mShmem; + gfx::SurfaceFormat mFormat; + gfx::IntSize mSize; + uint32_t mStride; + }; + nsRefPtrHashtable, DirectBitmap> mDirectBitmaps; + mozilla::Mutex mAsyncInvalidateMutex; CancelableTask *mAsyncInvalidateTask; diff --git a/dom/plugins/ipc/PluginInstanceParent.cpp b/dom/plugins/ipc/PluginInstanceParent.cpp index 4320069aa44..688f5eff273 100644 --- a/dom/plugins/ipc/PluginInstanceParent.cpp +++ b/dom/plugins/ipc/PluginInstanceParent.cpp @@ -38,6 +38,10 @@ #include "GLContext.h" #include "GLContextProvider.h" #include "gfxPrefs.h" +#include "LayersLogging.h" +#include "mozilla/layers/TextureWrapperImage.h" +#include "mozilla/layers/TextureClientRecycleAllocator.h" +#include "mozilla/layers/ImageBridgeChild.h" #ifdef XP_MACOSX #include "MacIOSurfaceImage.h" @@ -117,6 +121,7 @@ PluginInstanceParent::PluginInstanceParent(PluginModuleParent* parent, , mIsWhitelistedForShumway(false) , mWindowType(NPWindowTypeWindow) , mDrawingModel(kDefaultDrawingModel) + , mFrameID(0) #if defined(OS_WIN) , mPluginHWND(nullptr) , mChildPluginHWND(nullptr) @@ -190,6 +195,9 @@ PluginInstanceParent::ActorDestroy(ActorDestroyReason why) FinishX(DefaultXDisplay()); #endif } + if (IsUsingDirectDrawing() && mImageContainer) { + mImageContainer->ClearAllImages(); + } } NPError @@ -588,6 +596,111 @@ PluginInstanceParent::RecvNPN_InvalidateRect(const NPRect& rect) return true; } +static inline NPRect +IntRectToNPRect(const gfx::IntRect& rect) +{ + NPRect r; + r.left = rect.x; + r.top = rect.y; + r.right = rect.x + rect.width; + r.bottom = rect.y + rect.height; + return r; +} + +bool +PluginInstanceParent::RecvRevokeCurrentDirectSurface() +{ + ImageContainer *container = GetImageContainer(); + if (!container) { + return true; + } + + container->ClearAllImages(); + + PLUGIN_LOG_DEBUG((" (RecvRevokeCurrentDirectSurface)")); + return true; +} + +bool +PluginInstanceParent::RecvShowDirectBitmap(Shmem&& buffer, + const SurfaceFormat& format, + const uint32_t& stride, + const IntSize& size, + const IntRect& dirty) +{ + // Validate format. + if (format != SurfaceFormat::B8G8R8A8 && format != SurfaceFormat::B8G8R8X8) { + MOZ_ASSERT_UNREACHABLE("bad format type"); + return false; + } + if (size.width <= 0 || size.height <= 0) { + MOZ_ASSERT_UNREACHABLE("bad image size"); + return false; + } + if (mDrawingModel != NPDrawingModelAsyncBitmapSurface) { + MOZ_ASSERT_UNREACHABLE("plugin did not set a bitmap drawing model"); + return false; + } + + // Validate buffer and size. + CheckedInt nbytes = CheckedInt(uint32_t(size.height)) * stride; + if (!nbytes.isValid() || nbytes.value() != buffer.Size()) { + MOZ_ASSERT_UNREACHABLE("bad shmem size"); + return false; + } + + ImageContainer* container = GetImageContainer(); + if (!container) { + return false; + } + + RefPtr source = + gfx::Factory::CreateWrappingDataSourceSurface(buffer.get(), stride, size, format); + if (!source) { + return false; + } + + // Allocate a texture for the compositor. + RefPtr allocator = mParent->EnsureTextureAllocator(); + RefPtr texture = allocator->CreateOrRecycle( + format, size, BackendSelector::Content, + TextureFlags::NO_FLAGS, + ALLOC_FOR_OUT_OF_BAND_CONTENT); + if (!texture) { + return false; + } + + // Upload the plugin buffer. + { + TextureClientAutoLock autoLock(texture, OpenMode::OPEN_WRITE_ONLY); + if (!autoLock.Succeeded()) { + return false; + } + texture->UpdateFromSurface(source); + } + + // Wrap the texture in an image and ship it off. + RefPtr image = + new TextureWrapperImage(texture, gfx::IntRect(gfx::IntPoint(0, 0), size)); + SetCurrentImage(image); + + PLUGIN_LOG_DEBUG((" (RecvShowDirectBitmap received shmem=%p stride=%d size=%s dirty=%s)", + buffer.get(), stride, Stringify(size).c_str(), Stringify(dirty).c_str())); + return true; +} + +void +PluginInstanceParent::SetCurrentImage(Image* aImage) +{ + MOZ_ASSERT(IsUsingDirectDrawing()); + ImageContainer::NonOwningImage holder(aImage); + holder.mFrameID = ++mFrameID; + + nsAutoTArray imageList; + imageList.AppendElement(holder); + mImageContainer->SetCurrentImages(imageList); +} + bool PluginInstanceParent::RecvShow(const NPRect& updatedRect, const SurfaceDescriptor& newSurface, @@ -999,7 +1112,11 @@ PluginInstanceParent::GetImageContainer() return mImageContainer; } - mImageContainer = LayerManager::CreateImageContainer(); + if (IsUsingDirectDrawing()) { + mImageContainer = LayerManager::CreateImageContainer(ImageContainer::ASYNCHRONOUS); + } else { + mImageContainer = LayerManager::CreateImageContainer(); + } return mImageContainer; } diff --git a/dom/plugins/ipc/PluginInstanceParent.h b/dom/plugins/ipc/PluginInstanceParent.h index e91d642f81a..1b6e7edc67d 100644 --- a/dom/plugins/ipc/PluginInstanceParent.h +++ b/dom/plugins/ipc/PluginInstanceParent.h @@ -32,7 +32,9 @@ class nsPluginInstanceOwner; namespace mozilla { namespace layers { +class Image; class ImageContainer; +class TextureClientRecycleAllocator; } // namespace layers namespace plugins { @@ -167,6 +169,16 @@ public: virtual bool RecvNPN_InvalidateRect(const NPRect& rect) override; + virtual bool + RecvRevokeCurrentDirectSurface() override; + + virtual bool + RecvShowDirectBitmap(Shmem&& buffer, + const gfx::SurfaceFormat& format, + const uint32_t& stride, + const gfx::IntSize& size, + const gfx::IntRect& dirty) override; + // Async rendering virtual bool RecvShow(const NPRect& updatedRect, @@ -346,6 +358,8 @@ private: nsPluginInstanceOwner* GetOwner(); + void SetCurrentImage(layers::Image* aImage); + private: PluginModuleParent* mParent; RefPtr mSurrogate; @@ -359,6 +373,9 @@ private: nsDataHashtable, PluginScriptableObjectParent*> mScriptableObjects; + // This is used to tell the compositor that it should invalidate the ImageLayer. + uint32_t mFrameID; + #if defined(OS_WIN) private: // Used in handling parent/child forwarding of events. @@ -380,9 +397,6 @@ private: HWND mChildPluginsParentHWND; WNDPROC mPluginWndProc; bool mNestedEventState; - - // This will automatically release the textures when this object goes away. - nsRefPtrHashtable, ID3D10Texture2D> mTextureMap; #endif // defined(XP_WIN) #if defined(MOZ_WIDGET_COCOA) private: diff --git a/dom/plugins/ipc/PluginModuleParent.cpp b/dom/plugins/ipc/PluginModuleParent.cpp index 7effd804873..c4374febbb6 100755 --- a/dom/plugins/ipc/PluginModuleParent.cpp +++ b/dom/plugins/ipc/PluginModuleParent.cpp @@ -40,6 +40,7 @@ #include "GeckoProfiler.h" #include "nsPluginTags.h" #include "nsUnicharUtils.h" +#include "mozilla/layers/TextureClientRecycleAllocator.h" #ifdef XP_WIN #include "mozilla/plugins/PluginSurfaceParent.h" @@ -3024,6 +3025,15 @@ PluginModuleParent::RecvReturnSitesWithData(nsTArray&& aSites, return true; } +layers::TextureClientRecycleAllocator* +PluginModuleParent::EnsureTextureAllocator() +{ + if (!mTextureAllocator) { + mTextureAllocator = new TextureClientRecycleAllocator(ImageBridgeChild::GetSingleton()); + } + return mTextureAllocator; +} + #ifdef MOZ_CRASHREPORTER_INJECTOR // We only add the crash reporter to subprocess which have the filename diff --git a/dom/plugins/ipc/PluginModuleParent.h b/dom/plugins/ipc/PluginModuleParent.h index 610c29cfa43..c3c0f9ed057 100644 --- a/dom/plugins/ipc/PluginModuleParent.h +++ b/dom/plugins/ipc/PluginModuleParent.h @@ -43,6 +43,10 @@ class PCrashReporterParent; class CrashReporterParent; } // namespace dom +namespace layers { +class TextureClientRecycleAllocator; +} // namespace layers + namespace plugins { //----------------------------------------------------------------------------- @@ -294,6 +298,8 @@ public: void InitAsyncSurrogates(); + layers::TextureClientRecycleAllocator* EnsureTextureAllocator(); + protected: void NotifyFlashHang(); void NotifyPluginCrashed(); @@ -340,6 +346,8 @@ protected: nsTArray> mSurrogateInstances; nsresult mAsyncNewRv; uint32_t mRunID; + + RefPtr mTextureAllocator; }; class PluginModuleContentParent : public PluginModuleParent diff --git a/gfx/thebes/gfxPlatformGtk.h b/gfx/thebes/gfxPlatformGtk.h index 5e8130e706a..9de7ea99b8a 100644 --- a/gfx/thebes/gfxPlatformGtk.h +++ b/gfx/thebes/gfxPlatformGtk.h @@ -132,6 +132,10 @@ public: // maximum number of fonts to substitute for a generic uint32_t MaxGenericSubstitions(); + bool SupportsPluginDirectBitmapDrawing() override { + return true; + } + protected: static gfxFontconfigUtils *sFontconfigUtils; diff --git a/gfx/thebes/gfxUtils.h b/gfx/thebes/gfxUtils.h index a90987445b1..595678e637a 100644 --- a/gfx/thebes/gfxUtils.h +++ b/gfx/thebes/gfxUtils.h @@ -15,6 +15,7 @@ #include "nsPrintfCString.h" #include "nsRegionFwd.h" #include "mozilla/gfx/Rect.h" +#include "mozilla/CheckedInt.h" class gfxASurface; class gfxDrawable; @@ -354,6 +355,19 @@ NextPowerOfTwo(int aNumber) #endif } +/** + * Performs a checked multiply of the given width, height, and bytes-per-pixel + * values. + */ +static inline CheckedInt +SafeBytesForBitmap(uint32_t aWidth, uint32_t aHeight, unsigned aBytesPerPixel) +{ + MOZ_ASSERT(aBytesPerPixel > 0); + CheckedInt width = uint32_t(aWidth); + CheckedInt height = uint32_t(aHeight); + return width * height * aBytesPerPixel; +} + } // namespace gfx } // namespace mozilla diff --git a/gfx/thebes/gfxWindowsPlatform.h b/gfx/thebes/gfxWindowsPlatform.h index 680d0fb62fb..6460e3bde72 100644 --- a/gfx/thebes/gfxWindowsPlatform.h +++ b/gfx/thebes/gfxWindowsPlatform.h @@ -295,6 +295,9 @@ public: void GetDeviceInitData(mozilla::gfx::DeviceInitData* aOut) override; + bool SupportsPluginDirectBitmapDrawing() override { + return true; + } bool SupportsPluginDirectDXGIDrawing(); protected: