1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/renderer_host/backing_store_gtk.h"
7 #include <cairo-xlib.h>
12 #include <X11/extensions/sync.h>
14 #if defined(OS_OPENBSD) || defined(OS_FREEBSD)
15 #include <sys/endian.h>
23 #include "base/compiler_specific.h"
24 #include "base/logging.h"
25 #include "base/memory/singleton.h"
26 #include "base/metrics/histogram.h"
27 #include "base/time.h"
28 #include "content/browser/renderer_host/render_process_host_impl.h"
29 #include "skia/ext/platform_canvas.h"
30 #include "third_party/skia/include/core/SkBitmap.h"
31 #include "ui/base/gtk/gtk_signal.h"
32 #include "ui/base/x/x11_util.h"
33 #include "ui/base/x/x11_util_internal.h"
34 #include "ui/gfx/rect.h"
35 #include "ui/gfx/rect_conversions.h"
36 #include "ui/surface/transport_dib.h"
41 // Assume that somewhere along the line, someone will do width * height * 4
42 // with signed numbers. If the maximum value is 2**31, then 2**31 / 4 =
43 // 2**29 and floor(sqrt(2**29)) = 23170.
45 // Max height and width for layers
46 static const int kMaxVideoLayerSize
= 23170;
51 // Unlike Windows, where the backing store is kept in heap memory, we keep our
52 // backing store in the X server, as a pixmap. Thus expose events just require
53 // instructing the X server to copy from the backing store to the window.
55 // The backing store is in the same format as the visual which our main window
56 // is using. Bitmaps from the renderer are uploaded to the X server, either via
57 // shared memory or over the wire, and XRENDER is used to convert them to the
58 // correct format for the backing store.
60 // Destroys the image and the associated shared memory structures. This is a
61 // helper function for code using shared memory.
62 void DestroySharedImage(Display
* display
,
64 XShmSegmentInfo
* shminfo
) {
65 XShmDetach(display
, shminfo
);
67 shmdt(shminfo
->shmaddr
);
70 // So we don't don't want to call XSync(), which can block the UI loop for
71 // ~100ms on first paint and is generally slow. We optionally use the
72 // XSyncExtension to push a callback into the X11 event queue and get a
73 // callback instead of blocking until the event queue is cleared.
75 // TODO(erg): If ui::GetXDisplay() ever gets fixed to handle multiple Displays,
76 // this must be modified to be per Display instead of a Singleton.
79 static XSyncHandler
* GetInstance() {
80 return Singleton
<XSyncHandler
>::get();
84 return loaded_extension_
;
87 void PushPaintCounter(TransportDIB
* dib
,
91 const base::Closure
& completion_callback
);
94 friend struct DefaultSingletonTraits
<XSyncHandler
>;
96 // A struct that has cleanup and callback tasks that were queued into the
97 // future and are run on |g_backing_store_sync_alarm| firing.
98 struct BackingStoreEvents
{
99 BackingStoreEvents(TransportDIB
* dib
, Display
* d
, Picture pic
, Pixmap pix
,
100 const base::Closure
& c
)
106 dib
->IncreaseInFlightCounter();
111 // The display we're running on.
118 // Callback once everything else is done.
119 base::Closure closure
;
125 // An event filter notified about all XEvents. We then filter out XSync
126 // events that are on counters that we made.
127 CHROMEG_CALLBACK_1(XSyncHandler
, GdkFilterReturn
, OnEvent
, GdkXEvent
*,
130 // Whether we successfully loaded XSyncExtension.
131 bool loaded_extension_
;
133 // The event ids returned to us by XSyncQueryExtension().
134 int xsync_event_base_
;
135 int xsync_error_base_
;
137 XSyncCounter backing_store_sync_counter_
;
138 XSyncAlarm backing_store_sync_alarm_
;
140 // A queue of pending paints that we clean up after as alarms fire.
141 std::queue
<BackingStoreEvents
*> backing_store_events_
;
144 void XSyncHandler::PushPaintCounter(TransportDIB
* dib
,
148 const base::Closure
& completion_callback
) {
149 backing_store_events_
.push(new BackingStoreEvents(
150 dib
, display
, picture
, pixmap
, completion_callback
));
152 // Push a change counter event into the X11 event queue that will trigger our
153 // alarm when it is processed.
155 XSyncIntToValue(&value
, 1);
156 XSyncChangeCounter(ui::GetXDisplay(),
157 backing_store_sync_counter_
,
161 XSyncHandler::XSyncHandler()
162 : loaded_extension_(false),
163 xsync_event_base_(0),
164 xsync_error_base_(0),
165 backing_store_sync_counter_(0),
166 backing_store_sync_alarm_(0) {
167 Display
* display
= ui::GetXDisplay();
168 if (XSyncQueryExtension(display
,
170 &xsync_error_base_
)) {
171 // Create our monotonically increasing counter.
173 XSyncIntToValue(&value
, 0);
174 backing_store_sync_counter_
= XSyncCreateCounter(display
, value
);
176 // Cerate our alarm that watches for changes to our counter.
177 XSyncAlarmAttributes attributes
;
178 attributes
.trigger
.counter
= backing_store_sync_counter_
;
179 backing_store_sync_alarm_
= XSyncCreateAlarm(display
,
183 // Add our filter to the message loop to handle alarm triggers.
184 gdk_window_add_filter(NULL
, &OnEventThunk
, this);
186 loaded_extension_
= true;
190 XSyncHandler::~XSyncHandler() {
191 if (loaded_extension_
)
192 gdk_window_remove_filter(NULL
, &OnEventThunk
, this);
194 XSync(ui::GetXDisplay(), False
);
195 while (!backing_store_events_
.empty()) {
196 // We delete the X11 resources we're holding onto. We don't run the
197 // callbacks because we are shutting down.
198 BackingStoreEvents
* data
= backing_store_events_
.front();
199 backing_store_events_
.pop();
200 XRenderFreePicture(data
->display
, data
->picture
);
201 XFreePixmap(data
->display
, data
->pixmap
);
202 data
->dib
->DecreaseInFlightCounter();
207 GdkFilterReturn
XSyncHandler::OnEvent(GdkXEvent
* gdkxevent
,
209 XEvent
* xevent
= reinterpret_cast<XEvent
*>(gdkxevent
);
210 if (xevent
->type
== xsync_event_base_
+ XSyncAlarmNotify
) {
211 XSyncAlarmNotifyEvent
* alarm_event
=
212 reinterpret_cast<XSyncAlarmNotifyEvent
*>(xevent
);
213 if (alarm_event
->alarm
== backing_store_sync_alarm_
) {
214 if (alarm_event
->counter_value
.hi
== 0 &&
215 alarm_event
->counter_value
.lo
== 0) {
216 // We receive an event about the initial state of the counter during
217 // alarm creation. We must ignore this event instead of responding to
219 return GDK_FILTER_REMOVE
;
222 DCHECK(!backing_store_events_
.empty());
223 BackingStoreEvents
* data
= backing_store_events_
.front();
224 backing_store_events_
.pop();
226 // We are responsible for deleting all the data in the struct now that
227 // we are finished with it.
228 XRenderFreePicture(data
->display
, data
->picture
);
229 XFreePixmap(data
->display
, data
->pixmap
);
231 // Dispatch the closure we were given.
234 data
->dib
->DecreaseInFlightCounter();
237 return GDK_FILTER_REMOVE
;
241 return GDK_FILTER_CONTINUE
;
246 BackingStoreGtk::BackingStoreGtk(RenderWidgetHost
* widget
,
247 const gfx::Size
& size
,
250 : BackingStore(widget
, size
),
251 display_(ui::GetXDisplay()),
252 shared_memory_support_(ui::QuerySharedMemorySupport(display_
)),
253 use_render_(ui::QueryRenderSupport(display_
)),
255 visual_depth_(depth
),
256 root_window_(ui::GetX11RootWindow()) {
257 #if defined(OS_OPENBSD) || defined(OS_FREEBSD)
258 COMPILE_ASSERT(_BYTE_ORDER
== _LITTLE_ENDIAN
, assumes_little_endian
);
260 COMPILE_ASSERT(__BYTE_ORDER
== __LITTLE_ENDIAN
, assumes_little_endian
);
263 pixmap_
= XCreatePixmap(display_
, root_window_
,
264 size
.width(), size
.height(), depth
);
267 picture_
= XRenderCreatePicture(
269 ui::GetRenderVisualFormat(display_
,
270 static_cast<Visual
*>(visual
)),
275 pixmap_bpp_
= ui::BitsPerPixelForPixmapDepth(display_
, depth
);
278 pixmap_gc_
= XCreateGC(display_
, pixmap_
, 0, NULL
);
281 BackingStoreGtk::BackingStoreGtk(RenderWidgetHost
* widget
,
282 const gfx::Size
& size
)
283 : BackingStore(widget
, size
),
285 shared_memory_support_(ui::SHARED_MEMORY_NONE
),
296 BackingStoreGtk::~BackingStoreGtk() {
297 // In unit tests, display_ may be NULL.
301 XRenderFreePicture(display_
, picture_
);
302 XFreePixmap(display_
, pixmap_
);
303 XFreeGC(display_
, static_cast<GC
>(pixmap_gc_
));
306 size_t BackingStoreGtk::MemorySize() {
308 return size().GetArea() * (pixmap_bpp_
/ 8);
310 return size().GetArea() * 4;
313 void BackingStoreGtk::PaintRectWithoutXrender(
314 TransportDIB
* bitmap
,
315 const gfx::Rect
& bitmap_rect
,
316 const std::vector
<gfx::Rect
>& copy_rects
) {
317 const int width
= bitmap_rect
.width();
318 const int height
= bitmap_rect
.height();
319 Pixmap pixmap
= XCreatePixmap(display_
, root_window_
, width
, height
,
322 // Draw ARGB transport DIB onto our pixmap.
323 ui::PutARGBImage(display_
, visual_
, visual_depth_
, pixmap
,
324 pixmap_gc_
, static_cast<uint8
*>(bitmap
->memory()),
327 for (size_t i
= 0; i
< copy_rects
.size(); i
++) {
328 const gfx::Rect
& copy_rect
= copy_rects
[i
];
332 static_cast<GC
>(pixmap_gc_
), // gc
333 copy_rect
.x() - bitmap_rect
.x(), // src_x
334 copy_rect
.y() - bitmap_rect
.y(), // src_y
335 copy_rect
.width(), // width
336 copy_rect
.height(), // height
337 copy_rect
.x(), // dest_x
338 copy_rect
.y()); // dest_y
341 XFreePixmap(display_
, pixmap
);
344 void BackingStoreGtk::PaintToBackingStore(
345 RenderProcessHost
* process
,
346 TransportDIB::Id bitmap
,
347 const gfx::Rect
& bitmap_rect
,
348 const std::vector
<gfx::Rect
>& copy_rects
,
350 const base::Closure
& completion_callback
,
351 bool* scheduled_completion_callback
) {
352 *scheduled_completion_callback
= false;
357 if (bitmap_rect
.IsEmpty())
360 gfx::Rect pixel_bitmap_rect
= gfx::ToEnclosedRect(
361 gfx::ScaleRect(bitmap_rect
, scale_factor
));
362 const int width
= pixel_bitmap_rect
.width();
363 const int height
= pixel_bitmap_rect
.height();
365 if (width
<= 0 || width
> kMaxVideoLayerSize
||
366 height
<= 0 || height
> kMaxVideoLayerSize
)
369 TransportDIB
* dib
= process
->GetTransportDIB(bitmap
);
374 return PaintRectWithoutXrender(dib
, bitmap_rect
, copy_rects
);
379 if (shared_memory_support_
== ui::SHARED_MEMORY_PIXMAP
) {
380 XShmSegmentInfo shminfo
= {0};
381 shminfo
.shmseg
= dib
->MapToX(display_
);
383 // The NULL in the following is the |data| pointer: this is an artifact of
384 // Xlib trying to be helpful, rather than just exposing the X protocol. It
385 // assumes that we have the shared memory segment mapped into our memory,
386 // which we don't, and it's trying to calculate an offset by taking the
387 // difference between the |data| pointer and the address of the mapping in
388 // |shminfo|. Since both are NULL, the offset will be calculated to be 0,
389 // which is correct for us.
390 pixmap
= XShmCreatePixmap(display_
, root_window_
, NULL
, &shminfo
,
393 // We don't have shared memory pixmaps. Fall back to creating a pixmap
394 // ourselves and putting an image on it.
395 pixmap
= XCreatePixmap(display_
, root_window_
, width
, height
, 32);
396 GC gc
= XCreateGC(display_
, pixmap
, 0, NULL
);
398 if (shared_memory_support_
== ui::SHARED_MEMORY_PUTIMAGE
) {
399 const XID shmseg
= dib
->MapToX(display_
);
401 XShmSegmentInfo shminfo
;
402 memset(&shminfo
, 0, sizeof(shminfo
));
403 shminfo
.shmseg
= shmseg
;
404 shminfo
.shmaddr
= static_cast<char*>(dib
->memory());
406 XImage
* image
= XShmCreateImage(display_
, static_cast<Visual
*>(visual_
),
408 shminfo
.shmaddr
, &shminfo
,
411 // This code path is important for performance and we have found that
412 // different techniques work better on different platforms. See
413 // http://code.google.com/p/chromium/issues/detail?id=44124.
415 // Checking for ARM is an approximation, but it seems to be a good one so
417 #if defined(ARCH_CPU_ARM_FAMILY)
418 for (size_t i
= 0; i
< copy_rects
.size(); i
++) {
419 const gfx::Rect
& copy_rect
= copy_rects
[i
];
420 gfx::Rect pixel_copy_rect
= gfx::ToEnclosedRect(
421 gfx::ScaleRect(copy_rect
, scale_factor
));
422 XShmPutImage(display_
, pixmap
, gc
, image
,
423 pixel_copy_rect
.x() - pixel_bitmap_rect
.x(), /* source x */
424 pixel_copy_rect
.y() - pixel_bitmap_rect
.y(), /* source y */
425 pixel_copy_rect
.x() - pixel_bitmap_rect
.x(), /* dest x */
426 pixel_copy_rect
.y() - pixel_bitmap_rect
.y(), /* dest y */
427 pixel_copy_rect
.width(), pixel_copy_rect
.height(),
428 False
/* send_event */);
431 XShmPutImage(display_
, pixmap
, gc
, image
,
432 0, 0 /* source x, y */, 0, 0 /* dest x, y */,
433 width
, height
, False
/* send_event */);
435 XDestroyImage(image
);
436 } else { // case SHARED_MEMORY_NONE
437 // No shared memory support, we have to copy the bitmap contents
438 // to the X server. Xlib wraps the underlying PutImage call
439 // behind several layers of functions which try to convert the
440 // image into the format which the X server expects. The
441 // following values hopefully disable all conversions.
443 memset(&image
, 0, sizeof(image
));
446 image
.height
= height
;
448 image
.bits_per_pixel
= 32;
449 image
.format
= ZPixmap
;
450 image
.byte_order
= LSBFirst
;
451 image
.bitmap_unit
= 8;
452 image
.bitmap_bit_order
= LSBFirst
;
453 image
.bytes_per_line
= width
* 4;
454 image
.red_mask
= 0xff;
455 image
.green_mask
= 0xff00;
456 image
.blue_mask
= 0xff0000;
457 image
.data
= static_cast<char*>(dib
->memory());
459 XPutImage(display_
, pixmap
, gc
, &image
,
460 0, 0 /* source x, y */, 0, 0 /* dest x, y */,
463 XFreeGC(display_
, gc
);
466 picture
= ui::CreatePictureFromSkiaPixmap(display_
, pixmap
);
468 if (scale_factor
!= 1.0) {
469 float up_scale
= 1.0 / scale_factor
;
470 XTransform scaling
= { {
471 { XDoubleToFixed(1), XDoubleToFixed(0), XDoubleToFixed(0) },
472 { XDoubleToFixed(0), XDoubleToFixed(1), XDoubleToFixed(0) },
473 { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(up_scale
) }
475 XRenderSetPictureTransform(display_
, picture
, &scaling
);
476 XRenderSetPictureFilter(display_
, picture
, FilterGood
, NULL
, 0);
478 for (size_t i
= 0; i
< copy_rects
.size(); i
++) {
479 const gfx::Rect
& copy_rect
= copy_rects
[i
];
480 XRenderComposite(display_
,
485 copy_rect
.x() - bitmap_rect
.x(), // src_x
486 copy_rect
.y() - bitmap_rect
.y(), // src_y
489 copy_rect
.x(), // dest_x
490 copy_rect
.y(), // dest_y
491 copy_rect
.width(), // width
492 copy_rect
.height()); // height
495 // In the case of shared memory, we wait for the composite to complete so that
496 // we are sure that the X server has finished reading from the shared memory
498 if (shared_memory_support_
!= ui::SHARED_MEMORY_NONE
) {
499 XSyncHandler
* handler
= XSyncHandler::GetInstance();
500 if (handler
->Enabled()) {
501 *scheduled_completion_callback
= true;
502 handler
->PushPaintCounter(
503 dib
, display_
, picture
, pixmap
, completion_callback
);
505 XSync(display_
, False
);
509 if (*scheduled_completion_callback
== false) {
510 // If we didn't schedule a callback, we need to delete our resources now.
511 XRenderFreePicture(display_
, picture
);
512 XFreePixmap(display_
, pixmap
);
516 bool BackingStoreGtk::CopyFromBackingStore(const gfx::Rect
& rect
,
517 skia::PlatformBitmap
* output
) {
518 base::TimeTicks begin_time
= base::TimeTicks::Now();
520 if (visual_depth_
< 24) {
521 // CopyFromBackingStore() copies pixels out of the XImage
522 // in a way that assumes that each component (red, green,
523 // blue) is a byte. This doesn't work on visuals which
524 // encode a pixel color with less than a byte per color.
528 const int width
= std::min(size().width(), rect
.width());
529 const int height
= std::min(size().height(), rect
.height());
532 XShmSegmentInfo shminfo
; // Used only when shared memory is enabled.
533 if (shared_memory_support_
!= ui::SHARED_MEMORY_NONE
) {
534 // Use shared memory for faster copies when it's available.
535 Visual
* visual
= static_cast<Visual
*>(visual_
);
536 memset(&shminfo
, 0, sizeof(shminfo
));
537 image
= XShmCreateImage(display_
, visual
, 32,
538 ZPixmap
, NULL
, &shminfo
, width
, height
);
542 // Create the shared memory segment for the image and map it.
543 if (image
->bytes_per_line
== 0 || image
->height
== 0 ||
544 static_cast<size_t>(image
->height
) >
545 (std::numeric_limits
<size_t>::max() / image
->bytes_per_line
)) {
546 XDestroyImage(image
);
549 shminfo
.shmid
= shmget(IPC_PRIVATE
, image
->bytes_per_line
* image
->height
,
551 if (shminfo
.shmid
== -1) {
552 XDestroyImage(image
);
553 LOG(WARNING
) << "Failed to get shared memory segment. "
554 "Performance may be degraded.";
557 VLOG(1) << "Got shared memory segment " << shminfo
.shmid
;
560 void* mapped_memory
= shmat(shminfo
.shmid
, NULL
, SHM_RDONLY
);
561 shmctl(shminfo
.shmid
, IPC_RMID
, 0);
562 if (mapped_memory
== (void*)-1) {
563 XDestroyImage(image
);
566 shminfo
.shmaddr
= image
->data
= static_cast<char*>(mapped_memory
);
568 if (!XShmAttach(display_
, &shminfo
) ||
569 !XShmGetImage(display_
, pixmap_
, image
, rect
.x(), rect
.y(),
571 DestroySharedImage(display_
, image
, &shminfo
);
572 LOG(WARNING
) << "X failed to get shared memory segment. "
573 "Performance may be degraded.";
577 VLOG(1) << "Using X shared memory segment " << shminfo
.shmid
;
579 LOG(WARNING
) << "Not using X shared memory.";
580 // Non-shared memory case just copy the image from the server.
581 image
= XGetImage(display_
, pixmap_
,
582 rect
.x(), rect
.y(), width
, height
,
586 // TODO(jhawkins): Need to convert the image data if the image bits per pixel
588 // Note that this also initializes the output bitmap as opaque.
589 if (!output
->Allocate(width
, height
, true) ||
590 image
->bits_per_pixel
!= 32) {
591 if (shared_memory_support_
!= ui::SHARED_MEMORY_NONE
)
592 DestroySharedImage(display_
, image
, &shminfo
);
594 XDestroyImage(image
);
598 // The X image might have a different row stride, so iterate through
599 // it and copy each row out, only up to the pixels we're actually
600 // using. This code assumes a visual mode where a pixel is
601 // represented using a 32-bit unsigned int, with a byte per component.
602 const SkBitmap
& bitmap
= output
->GetBitmap();
603 SkAutoLockPixels
alp(bitmap
);
605 for (int y
= 0; y
< height
; y
++) {
606 const uint32
* src_row
= reinterpret_cast<uint32
*>(
607 &image
->data
[image
->bytes_per_line
* y
]);
608 uint32
* dest_row
= bitmap
.getAddr32(0, y
);
609 for (int x
= 0; x
< width
; ++x
, ++dest_row
) {
610 // Force alpha to be 0xff, because otherwise it causes rendering problems.
611 *dest_row
= src_row
[x
] | 0xff000000;
615 if (shared_memory_support_
!= ui::SHARED_MEMORY_NONE
)
616 DestroySharedImage(display_
, image
, &shminfo
);
618 XDestroyImage(image
);
620 HISTOGRAM_TIMES("BackingStore.RetrievalFromX",
621 base::TimeTicks::Now() - begin_time
);
625 void BackingStoreGtk::ScrollBackingStore(const gfx::Vector2d
& delta
,
626 const gfx::Rect
& clip_rect
,
627 const gfx::Size
& view_size
) {
631 // We only support scrolling in one direction at a time.
632 DCHECK(delta
.x() == 0 || delta
.y() == 0);
635 // Positive values of |delta|.y() scroll up
636 if (abs(delta
.y()) < clip_rect
.height()) {
637 XCopyArea(display_
, pixmap_
, pixmap_
, static_cast<GC
>(pixmap_gc_
),
638 clip_rect
.x() /* source x */,
639 std::max(clip_rect
.y(), clip_rect
.y() - delta
.y()),
641 clip_rect
.height() - abs(delta
.y()),
642 clip_rect
.x() /* dest x */,
643 std::max(clip_rect
.y(), clip_rect
.y() + delta
.y()) /* dest y */
646 } else if (delta
.x()) {
647 // Positive values of |delta|.x() scroll right
648 if (abs(delta
.x()) < clip_rect
.width()) {
649 XCopyArea(display_
, pixmap_
, pixmap_
, static_cast<GC
>(pixmap_gc_
),
650 std::max(clip_rect
.x(), clip_rect
.x() - delta
.x()),
651 clip_rect
.y() /* source y */,
652 clip_rect
.width() - abs(delta
.x()),
654 std::max(clip_rect
.x(), clip_rect
.x() + delta
.x()) /* dest x */,
655 clip_rect
.y() /* dest x */);
660 void BackingStoreGtk::XShowRect(const gfx::Point
&origin
,
661 const gfx::Rect
& rect
, XID target
) {
662 XCopyArea(display_
, pixmap_
, target
, static_cast<GC
>(pixmap_gc_
),
663 rect
.x(), rect
.y(), rect
.width(), rect
.height(),
664 rect
.x() + origin
.x(), rect
.y() + origin
.y());
667 #if defined(TOOLKIT_GTK)
668 void BackingStoreGtk::PaintToRect(const gfx::Rect
& rect
, GdkDrawable
* target
) {
669 cairo_surface_t
* surface
= cairo_xlib_surface_create(
670 display_
, pixmap_
, static_cast<Visual
*>(visual_
),
671 size().width(), size().height());
672 cairo_t
* cr
= gdk_cairo_create(target
);
674 cairo_translate(cr
, rect
.x(), rect
.y());
675 double x_scale
= static_cast<double>(rect
.width()) / size().width();
676 double y_scale
= static_cast<double>(rect
.height()) / size().height();
677 cairo_scale(cr
, x_scale
, y_scale
);
679 cairo_pattern_t
* pattern
= cairo_pattern_create_for_surface(surface
);
680 cairo_pattern_set_filter(pattern
, CAIRO_FILTER_BEST
);
681 cairo_set_source(cr
, pattern
);
682 cairo_pattern_destroy(pattern
);
684 cairo_identity_matrix(cr
);
686 cairo_rectangle(cr
, rect
.x(), rect
.y(), rect
.width(), rect
.height());
692 } // namespace content