Bug 597336, part 4: If UseClientSideRendering(), render to a gfxImageSurface backed by memory shared with X and then XShmPutImage to copy it to our window's drawable. r=karl a=blocking-fennec

This commit is contained in:
Chris Jones 2010-09-23 20:00:06 -05:00
Родитель 61c93dad95
Коммит 27603d255c
4 изменённых файлов: 204 добавлений и 6 удалений

Просмотреть файл

@ -53,6 +53,7 @@
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>

Просмотреть файл

@ -142,6 +142,8 @@ CPPSRCS += nsNativeThemeGTK.cpp
DEFINES += -DNATIVE_THEME_SUPPORT
endif
include $(topsrcdir)/config/config.mk
include $(topsrcdir)/ipc/chromium/chromium-config.mk
include $(topsrcdir)/config/rules.mk
CFLAGS += $(MOZ_GTK2_CFLAGS) $(MOZ_STARTUP_NOTIFICATION_CFLAGS)

Просмотреть файл

@ -43,6 +43,10 @@
#define MAEMO_CHANGES
#endif
#ifdef MOZ_IPC
# include "mozilla/ipc/SharedMemorySysV.h"
#endif
#include "prlink.h"
#include "nsWindow.h"
@ -64,6 +68,7 @@
#ifdef MOZ_X11
#include <gdk/gdkx.h>
#include <X11/Xatom.h>
#include <X11/extensions/XShm.h>
#ifdef AIX
#include <X11/keysym.h>
@ -149,6 +154,7 @@ D_DEBUG_DOMAIN( ns_Window, "nsWindow", "nsWindow" );
#define GDK_WINDOW_XWINDOW(_win) _win
#endif
using namespace mozilla;
using mozilla::gl::GLContext;
using mozilla::layers::LayerManagerOGL;
@ -285,6 +291,14 @@ UpdateLastInputEventTime()
}
}
// If XShm isn't available to our client, we'll try XShm once, fail,
// set this to false and then never try again.
static PRBool gShmAvailable = PR_TRUE;
static PRBool UseShm()
{
return gfxPlatformGtk::UseClientSideRendering() && gShmAvailable;
}
// this is the last window that had a drag event happen on it.
nsWindow *nsWindow::mLastDragMotionWindow = NULL;
PRBool nsWindow::sIsDraggingOutOf = PR_FALSE;
@ -378,6 +392,160 @@ protected:
pixman_region32& get() { return *this; }
};
#if defined(MOZ_X11) && defined(MOZ_HAVE_SHAREDMEMORYSYSV)
# define MOZ_HAVE_SHMIMAGE
using mozilla::ipc::SharedMemorySysV;
class nsShmImage {
NS_INLINE_DECL_REFCOUNTING(nsShmImage)
public:
typedef gfxASurface::gfxImageFormat Format;
static already_AddRefed<nsShmImage>
Create(const gfxIntSize& aSize, Visual* aVisual, unsigned int aDepth);
~nsShmImage() {
if (mImage) {
if (mXAttached) {
XShmDetach(gdk_x11_get_default_xdisplay(), &mInfo);
}
XDestroyImage(mImage);
}
}
already_AddRefed<gfxASurface> AsSurface();
void Put(GdkWindow* aWindow, GdkRectangle* aRects, GdkRectangle* aEnd);
gfxIntSize Size() const { return mSize; }
private:
nsShmImage()
: mImage(nsnull)
, mXAttached(PR_FALSE)
{ mInfo.shmid = SharedMemorySysV::NULLHandle(); }
nsRefPtr<SharedMemorySysV> mSegment;
XImage* mImage;
XShmSegmentInfo mInfo;
gfxIntSize mSize;
Format mFormat;
PRPackedBool mXAttached;
};
already_AddRefed<nsShmImage>
nsShmImage::Create(const gfxIntSize& aSize,
Visual* aVisual, unsigned int aDepth)
{
Display* dpy = gdk_x11_get_default_xdisplay();
nsRefPtr<nsShmImage> shm = new nsShmImage();
shm->mImage = XShmCreateImage(dpy, aVisual, aDepth,
ZPixmap, nsnull,
&(shm->mInfo),
aSize.width, aSize.height);
if (!shm->mImage) {
return nsnull;
}
size_t size = shm->mImage->bytes_per_line * shm->mImage->height;
shm->mSegment = new SharedMemorySysV();
if (!shm->mSegment->Create(size) || !shm->mSegment->Map(size)) {
return nsnull;
}
shm->mInfo.shmid = shm->mSegment->GetHandle();
shm->mInfo.shmaddr =
shm->mImage->data = static_cast<char*>(shm->mSegment->memory());
shm->mInfo.readOnly = False;
gdk_error_trap_push();
Status attachOk = XShmAttach(dpy, &shm->mInfo);
gint xerror = gdk_error_trap_pop();
if (!attachOk || xerror) {
// Assume XShm isn't available, and don't attempt to use it
// again.
gShmAvailable = PR_FALSE;
return nsnull;
}
shm->mXAttached = PR_TRUE;
shm->mSize = aSize;
switch (shm->mImage->depth) {
case 24:
shm->mFormat = gfxASurface::ImageFormatRGB24; break;
case 16:
shm->mFormat = gfxASurface::ImageFormatRGB16_565; break;
default:
NS_WARNING("Unsupported XShm Image depth!");
gShmAvailable = PR_FALSE;
return nsnull;
}
return shm.forget();
}
already_AddRefed<gfxASurface>
nsShmImage::AsSurface()
{
return nsRefPtr<gfxASurface>(
new gfxImageSurface(static_cast<unsigned char*>(mSegment->memory()),
mSize,
mImage->bytes_per_line,
mFormat)
).forget();
}
void
nsShmImage::Put(GdkWindow* aWindow, GdkRectangle* aRects, GdkRectangle* aEnd)
{
GdkDrawable* gd;
gint dx, dy;
gdk_window_get_internal_paint_info(aWindow, &gd, &dx, &dy);
Display* dpy = gdk_x11_get_default_xdisplay();
Drawable d = GDK_DRAWABLE_XID(gd);
GC gc = XCreateGC(dpy, d, 0, nsnull);
for (GdkRectangle* r = aRects; r < aEnd; r++) {
XShmPutImage(dpy, d, gc, mImage,
r->x, r->y,
r->x - dx, r->y - dy,
r->width, r->height,
False);
}
XFreeGC(dpy, gc);
// FIXME/bug 597336: we need to ensure that the shm image isn't
// scribbled over before all its pending XShmPutImage()s complete.
// However, XSync() is an unnecessarily heavyweight
// synchronization mechanism; other options are possible. If this
// XSync is shown to hurt responsiveness, we need to explore the
// other options.
XSync(dpy, False);
}
static already_AddRefed<gfxASurface>
EnsureShmImage(const gfxIntSize& aSize, Visual* aVisual, unsigned int aDepth,
nsRefPtr<nsShmImage>& aImage)
{
if (!aImage || aImage->Size() != aSize) {
// Because we XSync() after XShmAttach() to trap errors, we
// know that the X server has the old image's memory mapped
// into its address space, so it's OK to destroy the old image
// here even if there are outstanding Puts. The Detach is
// ordered after the Puts.
aImage = nsShmImage::Create(aSize, aVisual, aDepth);
}
return !aImage ? nsnull : aImage->AsSurface();
}
#endif // defined(MOZ_X11) && defined(MOZ_HAVE_SHAREDMEMORYSYSV)
nsWindow::nsWindow()
{
mIsTopLevel = PR_FALSE;
@ -2141,10 +2309,6 @@ nsWindow::OnExposeEvent(GtkWidget *aWidget, GdkEventExpose *aEvent)
}
nsRefPtr<gfxContext> ctx = new gfxContext(GetThebesSurface());
if (NS_UNLIKELY(!ctx)) {
g_free(rects);
return FALSE;
}
#ifdef MOZ_DFB
gfxPlatformGtk::SetGdkDrawable(ctx->OriginalSurface(),
@ -2187,6 +2351,11 @@ nsWindow::OnExposeEvent(GtkWidget *aWidget, GdkEventExpose *aEvent)
// channel is used on compositing window managers.)
layerBuffering = BasicLayerManager::BUFFER_NONE;
ctx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
#ifdef MOZ_HAVE_SHMIMAGE
} else if (UseShm()) {
// We're using an xshm mapping as a back buffer.
layerBuffering = BasicLayerManager::BUFFER_NONE;
#endif // MOZ_HAVE_SHMIMAGE
} else {
// Get the layer manager to do double buffering (if necessary).
layerBuffering = BasicLayerManager::BUFFER_BUFFERED;
@ -2241,6 +2410,11 @@ nsWindow::OnExposeEvent(GtkWidget *aWidget, GdkEventExpose *aEvent)
}
}
}
# ifdef MOZ_HAVE_SHMIMAGE
if (UseShm() && NS_LIKELY(!mIsDestroyed)) {
mShmImage->Put(mGdkWindow, rects, r_end);
}
# endif // MOZ_HAVE_SHMIMAGE
#endif // MOZ_X11
g_free(rects);
@ -6544,12 +6718,28 @@ nsWindow::GetThebesSurface()
// Owen Taylor says this is the right thing to do!
width = PR_MIN(32767, width);
height = PR_MIN(32767, height);
gfxIntSize size(width, height);
Visual* visual = GDK_VISUAL_XVISUAL(gdk_drawable_get_visual(d));
# ifdef MOZ_HAVE_SHMIMAGE
PRBool usingShm = PR_FALSE;
if (UseShm()) {
// EnsureShmImage() is a dangerous interface, but we guarantee
// that the thebes surface and the shmimage have the same
// lifetime
mThebesSurface = EnsureShmImage(size,
visual, gdk_drawable_get_depth(d),
mShmImage);
usingShm = mThebesSurface != nsnull;
}
if (!usingShm)
# endif // MOZ_HAVE_SHMIMAGE
mThebesSurface = new gfxXlibSurface
(GDK_WINDOW_XDISPLAY(d),
GDK_WINDOW_XWINDOW(d),
GDK_VISUAL_XVISUAL(gdk_drawable_get_visual(d)),
gfxIntSize(width, height));
visual,
size);
#endif
#ifdef MOZ_DFB
mThebesSurface = new gfxDirectFBSurface(gdk_directfb_surface_lookup(d));

Просмотреть файл

@ -97,6 +97,7 @@ extern PRLogModuleInfo *gWidgetDrawLog;
#endif /* MOZ_LOGGING */
class nsShmImage;
class nsWindow : public nsBaseWidget, public nsSupportsWeakReference
{
@ -402,6 +403,10 @@ private:
PRInt32 mTransparencyBitmapWidth;
PRInt32 mTransparencyBitmapHeight;
#ifdef MOZ_X11
// If we're using xshm rendering, mThebesSurface wraps mShmImage
nsRefPtr<nsShmImage> mShmImage;
#endif
nsRefPtr<gfxASurface> mThebesSurface;
#ifdef MOZ_DFB