Implement the direct bitmap drawing model for plugins. (bug 1217665 part 6, r=mattwoodrow)

This commit is contained in:
David Anderson 2015-12-02 11:31:17 -08:00
parent 1d51e8777e
commit e6efc8ad07
10 changed files with 357 additions and 13 deletions

View File

@ -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.

View File

@ -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<DirectBitmap> 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<uint32_t> 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<uint8_t>() == nbytes.value());
surface->version = 0;
surface->size = *size;
surface->format = format;
surface->bitmap.data = shmem.get<unsigned char>();
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<DirectBitmap> 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<DirectBitmap> 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<DeletingObjectEntry>;
PluginScriptableObjectChild::NotifyOfInstanceShutdown(this);

View File

@ -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 <map>
@ -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<nsPtrHashKey<NPAsyncSurface>, DirectBitmap> mDirectBitmaps;
mozilla::Mutex mAsyncInvalidateMutex;
CancelableTask *mAsyncInvalidateTask;

View File

@ -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<uint32_t> nbytes = CheckedInt<uint32_t>(uint32_t(size.height)) * stride;
if (!nbytes.isValid() || nbytes.value() != buffer.Size<uint8_t>()) {
MOZ_ASSERT_UNREACHABLE("bad shmem size");
return false;
}
ImageContainer* container = GetImageContainer();
if (!container) {
return false;
}
RefPtr<gfx::DataSourceSurface> source =
gfx::Factory::CreateWrappingDataSourceSurface(buffer.get<uint8_t>(), stride, size, format);
if (!source) {
return false;
}
// Allocate a texture for the compositor.
RefPtr<TextureClientRecycleAllocator> allocator = mParent->EnsureTextureAllocator();
RefPtr<TextureClient> 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<TextureWrapperImage> 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<unsigned char>(), 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<ImageContainer::NonOwningImage,1> 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;
}

View File

@ -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<PluginAsyncSurrogate> mSurrogate;
@ -359,6 +373,9 @@ private:
nsDataHashtable<nsPtrHashKey<NPObject>, 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<nsPtrHashKey<void>, ID3D10Texture2D> mTextureMap;
#endif // defined(XP_WIN)
#if defined(MOZ_WIDGET_COCOA)
private:

View File

@ -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<nsCString>&& 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

View File

@ -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<RefPtr<PluginAsyncSurrogate>> mSurrogateInstances;
nsresult mAsyncNewRv;
uint32_t mRunID;
RefPtr<layers::TextureClientRecycleAllocator> mTextureAllocator;
};
class PluginModuleContentParent : public PluginModuleParent

View File

@ -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;

View File

@ -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<uint32_t>
SafeBytesForBitmap(uint32_t aWidth, uint32_t aHeight, unsigned aBytesPerPixel)
{
MOZ_ASSERT(aBytesPerPixel > 0);
CheckedInt<uint32_t> width = uint32_t(aWidth);
CheckedInt<uint32_t> height = uint32_t(aHeight);
return width * height * aBytesPerPixel;
}
} // namespace gfx
} // namespace mozilla

View File

@ -295,6 +295,9 @@ public:
void GetDeviceInitData(mozilla::gfx::DeviceInitData* aOut) override;
bool SupportsPluginDirectBitmapDrawing() override {
return true;
}
bool SupportsPluginDirectDXGIDrawing();
protected: