cairo: add some patches posted upstream for improving the drawing performance (#3675)

See https://lists.cairographics.org/archives/cairo/2018-April/028606.html

This should help gtk3 apps without CSD.
This commit is contained in:
Christoph Reiter
2018-05-02 19:40:02 +02:00
committed by Алексей
parent f83bcac7c8
commit f1eba2b29f
4 changed files with 346 additions and 3 deletions

View File

@@ -0,0 +1,104 @@
From ba41e2203ea32e459cd26dfb20d748cead547d82 Mon Sep 17 00:00:00 2001
From: Vasily Galkin <galkin-vv@yandex.ru>
Date: Sat, 28 Apr 2018 21:10:54 +0300
Subject: [PATCH 1/3] win32: introduce new flag to mark surfaces supporting
solid brush drawing
This is beginning of a patch series that speedups the CAIRO_OPERATOR_SOURCE
when used to copy data
to a argb32 cairo surface corresponding to a win32 dc
from a "backbuffer" - DibSection-based cairo surface
created with cairo_surface_create_similar().
This initial patch presents only private header changes
without changing any implementation logic.
The big problem with argb32 surfaces and gdi is the gdi's inability to
correctly set alpha channel on all operations except BitBlt and AlphaBlend
So CAIRO_WIN32_SURFACE_CAN_RGB_BRUSH flag introduced in this commit
will be used as a mark that surfaces that correctly such handle brushes -
essentially all surface types except argb32.
_cairo_win32_flags_for_dc receives new argument
that would be used in flag calculation.
---
src/win32/cairo-win32-device.c | 2 +-
src/win32/cairo-win32-display-surface.c | 4 ++--
src/win32/cairo-win32-printing-surface.c | 2 +-
src/win32/cairo-win32-private.h | 5 ++++-
4 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/src/win32/cairo-win32-device.c b/src/win32/cairo-win32-device.c
index c60c4948d..309f16c56 100644
--- a/src/win32/cairo-win32-device.c
+++ b/src/win32/cairo-win32-device.c
@@ -153,7 +153,7 @@ _cairo_win32_device_get (void)
}
unsigned
-_cairo_win32_flags_for_dc (HDC dc)
+_cairo_win32_flags_for_dc (HDC dc, cairo_format_t format)
{
uint32_t flags = 0;
int cap;
diff --git a/src/win32/cairo-win32-display-surface.c b/src/win32/cairo-win32-display-surface.c
index 92d1f6ce4..0d737f542 100644
--- a/src/win32/cairo-win32-display-surface.c
+++ b/src/win32/cairo-win32-display-surface.c
@@ -258,7 +258,7 @@ _create_dc_and_bitmap (cairo_win32_display_surface_t *surface,
}
}
- surface->win32.flags = _cairo_win32_flags_for_dc (surface->win32.dc);
+ surface->win32.flags = _cairo_win32_flags_for_dc (surface->win32.dc, format);
return CAIRO_STATUS_SUCCESS;
@@ -973,7 +973,7 @@ cairo_win32_surface_create_with_format (HDC hdc, cairo_format_t format)
surface->is_dib = FALSE;
surface->saved_dc_bitmap = NULL;
- surface->win32.flags = _cairo_win32_flags_for_dc (surface->win32.dc);
+ surface->win32.flags = _cairo_win32_flags_for_dc (surface->win32.dc, format);
device = _cairo_win32_device_get ();
diff --git a/src/win32/cairo-win32-printing-surface.c b/src/win32/cairo-win32-printing-surface.c
index 7f374a076..84960772a 100644
--- a/src/win32/cairo-win32-printing-surface.c
+++ b/src/win32/cairo-win32-printing-surface.c
@@ -2159,7 +2159,7 @@ cairo_win32_printing_surface_create (HDC hdc)
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
}
- surface->win32.flags = _cairo_win32_flags_for_dc (surface->win32.dc);
+ surface->win32.flags = _cairo_win32_flags_for_dc (surface->win32.dc, CAIRO_FORMAT_RGB24);
surface->win32.flags |= CAIRO_WIN32_SURFACE_FOR_PRINTING;
_cairo_win32_printing_surface_init_ps_mode (surface);
diff --git a/src/win32/cairo-win32-private.h b/src/win32/cairo-win32-private.h
index 6fdf96f87..79e1e0f77 100644
--- a/src/win32/cairo-win32-private.h
+++ b/src/win32/cairo-win32-private.h
@@ -81,6 +81,9 @@ enum {
/* Whether we can use the CHECKJPEGFORMAT escape function */
CAIRO_WIN32_SURFACE_CAN_CHECK_PNG = (1<<8),
+
+ /* Whether we can use gdi drawing with solid rgb brush with this surface */
+ CAIRO_WIN32_SURFACE_CAN_RGB_BRUSH = (1<<9),
};
typedef struct _cairo_win32_surface {
@@ -186,7 +189,7 @@ _cairo_win32_surface_get_extents (void *abstract_surface,
cairo_rectangle_int_t *rectangle);
uint32_t
-_cairo_win32_flags_for_dc (HDC dc);
+_cairo_win32_flags_for_dc (HDC dc, cairo_format_t format);
cairo_int_status_t
_cairo_win32_surface_emit_glyphs (cairo_win32_surface_t *dst,
--
2.17.0

View File

@@ -0,0 +1,140 @@
From 4534b8cbbd032451da6f56f012cdf7b034b86159 Mon Sep 17 00:00:00 2001
From: Vasily Galkin <galkin-vv@yandex.ru>
Date: Sat, 28 Apr 2018 21:36:21 +0300
Subject: [PATCH 2/3] win32: CAIRO_WIN32_SURFACE_CAN_RGB_BRUSH and other argb32
flags set+check
This belongs to a patch series that speedups the CAIRO_OPERATOR_SOURCE
when used to copy data
to a argb32 cairo surface corresponding to a win32 dc
from a "backbuffer" - DibSection-based cairo surface
created with cairo_surface_create_similar().
This patch introduces checks that ensure that no solid brush gdi operations
are done with argb32 surfaces.
This is needed for enabling gdi compositor usage for win32 argb32 surfaces.
To make this checks working the calculatinig function correctly fills
argb32 flags disabling STRETCHBLT, STRETCHDIB and RGB_BRUSH.
_cairo_win32_flags_for_dc refactored to make rgb24 vs argb32 distinction
more readable. All logic&flags for rgb24 surfaces are kept,
except addition of CAIRO_WIN32_SURFACE_CAN_RGB_BRUSH.
The logic of forbidding AlphaBlend on display surfaces
kept as is without investigation.
---
src/win32/cairo-win32-device.c | 36 ++++++++++++++++----------
src/win32/cairo-win32-gdi-compositor.c | 18 ++++++++++++-
2 files changed, 40 insertions(+), 14 deletions(-)
diff --git a/src/win32/cairo-win32-device.c b/src/win32/cairo-win32-device.c
index 309f16c56..a2c95087c 100644
--- a/src/win32/cairo-win32-device.c
+++ b/src/win32/cairo-win32-device.c
@@ -156,17 +156,31 @@ unsigned
_cairo_win32_flags_for_dc (HDC dc, cairo_format_t format)
{
uint32_t flags = 0;
- int cap;
+ cairo_bool_t is_display = GetDeviceCaps(dc, TECHNOLOGY) == DT_RASDISPLAY;
+
+ if (format == CAIRO_FORMAT_RGB24 || format == CAIRO_FORMAT_ARGB32)
+ {
+ int cap = GetDeviceCaps(dc, RASTERCAPS);
+ if (cap & RC_BITBLT)
+ flags |= CAIRO_WIN32_SURFACE_CAN_BITBLT;
+ if (!is_display && GetDeviceCaps(dc, SHADEBLENDCAPS) != SB_NONE)
+ flags |= CAIRO_WIN32_SURFACE_CAN_ALPHABLEND;
- cap = GetDeviceCaps(dc, RASTERCAPS);
- if (cap & RC_BITBLT)
- flags |= CAIRO_WIN32_SURFACE_CAN_BITBLT;
- if (cap & RC_STRETCHBLT)
- flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHBLT;
- if (cap & RC_STRETCHDIB)
- flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHDIB;
+ /* ARGB32 available operations is a strict subset of RGB24 available
+ * operations. It's because the same gdi functions can be used but most
+ * of them always reset alpha channel to 0 which is bad for ARGB32.
+ */
+ if (format == CAIRO_FORMAT_RGB24)
+ {
+ flags |= CAIRO_WIN32_SURFACE_CAN_RGB_BRUSH;
+ if (cap & RC_STRETCHBLT)
+ flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHBLT;
+ if (cap & RC_STRETCHDIB)
+ flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHDIB;
+ }
+ }
- if (GetDeviceCaps(dc, TECHNOLOGY) == DT_RASDISPLAY) {
+ if (is_display) {
flags |= CAIRO_WIN32_SURFACE_IS_DISPLAY;
/* These will always be possible, but the actual GetDeviceCaps
@@ -181,10 +195,6 @@ _cairo_win32_flags_for_dc (HDC dc, cairo_format_t format)
flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHBLT;
flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHDIB;
#endif
- } else {
- cap = GetDeviceCaps(dc, SHADEBLENDCAPS);
- if (cap != SB_NONE)
- flags |= CAIRO_WIN32_SURFACE_CAN_ALPHABLEND;
}
return flags;
diff --git a/src/win32/cairo-win32-gdi-compositor.c b/src/win32/cairo-win32-gdi-compositor.c
index 2858affcb..0873391eb 100644
--- a/src/win32/cairo-win32-gdi-compositor.c
+++ b/src/win32/cairo-win32-gdi-compositor.c
@@ -179,6 +179,9 @@ fill_boxes (cairo_win32_display_surface_t *dst,
TRACE ((stderr, "%s\n", __FUNCTION__));
+ if ((dst->win32.flags & CAIRO_WIN32_SURFACE_CAN_RGB_BRUSH) == 0)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
fb.dc = dst->win32.dc;
fb.brush = CreateSolidBrush (color_to_rgb(color));
if (!fb.brush)
@@ -213,6 +216,7 @@ copy_boxes (cairo_win32_display_surface_t *dst,
struct copy_box cb;
cairo_surface_t *surface;
cairo_status_t status;
+ cairo_win32_surface_t *src;
TRACE ((stderr, "%s\n", __FUNCTION__));
@@ -230,8 +234,16 @@ copy_boxes (cairo_win32_display_surface_t *dst,
&cb.tx, &cb.ty))
return CAIRO_INT_STATUS_UNSUPPORTED;
+ src = to_win32_surface(surface);
+
+ if (src->format != dst->win32.format &&
+ !(src->format == CAIRO_FORMAT_ARGB32 && dst->win32.format == CAIRO_FORMAT_RGB24))
+ {
+ /* forbid copy different surfaces unless it is from argb32 to rgb (alpha-drop) */
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
cb.dst = dst->win32.dc;
- cb.src = to_win32_surface(surface)->dc;
+ cb.src = src->dc;
/* First check that the data is entirely within the image */
if (! _cairo_boxes_for_each_box (boxes, source_contains_box, &cb))
@@ -614,6 +626,10 @@ _cairo_win32_gdi_compositor_glyphs (const cairo_compositor_t *compositor,
cairo_win32_display_surface_t *dst = to_win32_display_surface (composite->surface);
TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ if ((dst->win32.flags & CAIRO_WIN32_SURFACE_CAN_RGB_BRUSH) == 0)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
status = _cairo_win32_display_surface_set_clip(dst, composite->clip);
if (status)
return status;
--
2.17.0

View File

@@ -0,0 +1,88 @@
From dfc2a62106416cf57fadf8a94108f1e454ddebd3 Mon Sep 17 00:00:00 2001
From: Vasily Galkin <galkin-vv@yandex.ru>
Date: Sat, 28 Apr 2018 22:06:48 +0300
Subject: [PATCH 3/3] win32: Allow gdi operations for argb32 surfaces (allowed
by surface flags)
This ends the patch series that speedups the CAIRO_OPERATOR_SOURCE
when used to copy data
to a argb32 cairo surface corresponding to a win32 dc
from a "backbuffer" - DibSection-based cairo surface
created with cairo_surface_create_similar().
This final patch allows gdi compositor to be used on argb32 surfaces.
Actually for display surfaces only copying is allowed with gdi (by BitBlt),
since other operations are filtered by flags in implementations.
But since copying pixels is the only used operation in common scenario
"prepare offscreen image and put it to screen" - this is important for
presenting argb32 windows with cairo directly
or with gtk+gdk (which nowdays always create argb32 windows)
Before this patch pixel copy worked by:
1. mapping image to memory (by copying data from window dc to system memory
which is very slow on windows maybe due to gpu or interprocess access)
2. copying new data over that image.
3. copying updated image from system memory back to window dc.
After this patch there is only one step:
2+3. Copying new data over window dc.
Completely eliminating step 1 gives a very huge speedup and allows
argb32 cairo drawing be as fats as typical dibsection-buffered gdi drawing.
There is quick&dirty cairo-vs-gdi perf test made for this patch set:
https://gitlab.gnome.org/galkinvv/cairo/snippets/109
The results show multiple times improvement:
Before speedup
Painting 5000 32bits-per-pixel single-color frames of size 1056x1056 for profiling
GDI entire pipeline : 4.123983 GB/s, 5408.053900 ms, 924.546998 FPS
GDI entire drawing : 4.156272 GB/s, 5366.039400 ms
cairo entire pipeline : 0.835951 GB/s, 26679.463300 ms, 187.410067 FPS
cairo entire drawing : 0.838992 GB/s, 26582.750800 ms
cairo fill inmem : 16.130683 GB/s, 1382.627100 ms
cairo to window : 1.102623 GB/s, 20226.963700 ms
After speedup (running several times shows that there is 5-10% inaccuracy, so this results sgouldn't be used as a source for comparing raw gdi vs cairo)
Painting 5000 32bits-per-pixel single-color frames of size 1056x1056 for profiling
GDI entire pipeline : 4.139421 GB/s, 5387.883400 ms, 928.008204 FPS
GDI entire drawing : 4.165124 GB/s, 5354.635400 ms
cairo entire pipeline : 4.029344 GB/s, 5535.075100 ms, 903.330110 FPS
cairo entire drawing : 4.063073 GB/s, 5489.126000 ms
cairo fill inmem : 22.665569 GB/s, 983.991200 ms
cairo to window : 5.049950 GB/s, 4416.423700 ms
End-user visible speedup does present too - it relates to the following bug
https://gitlab.gnome.org/GNOME/meld/issues/133
Cairo speedup allow more simultaneous meld windows
without eating 100% of cpu core time on spinner rendering.
gtk's speedup is near 1.7x, not such huge as pure cairo ~7-8x on results above
It looks that gtk has some problems in caching cairo surfaces
and recreates them every frame with initial black fill.
---
src/win32/cairo-win32-gdi-compositor.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/win32/cairo-win32-gdi-compositor.c b/src/win32/cairo-win32-gdi-compositor.c
index 0873391eb..4a09a70a9 100644
--- a/src/win32/cairo-win32-gdi-compositor.c
+++ b/src/win32/cairo-win32-gdi-compositor.c
@@ -488,7 +488,8 @@ static cairo_bool_t check_blit (cairo_composite_rectangles_t *composite)
if (dst->fallback)
return FALSE;
- if (dst->win32.format != CAIRO_FORMAT_RGB24)
+ if (dst->win32.format != CAIRO_FORMAT_RGB24
+ && dst->win32.format != CAIRO_FORMAT_ARGB32)
return FALSE;
if (dst->win32.flags & CAIRO_WIN32_SURFACE_CAN_BITBLT)
--
2.17.0

View File

@@ -5,7 +5,7 @@ _realname=cairo
pkgbase=mingw-w64-${_realname}
pkgname="${MINGW_PACKAGE_PREFIX}-${_realname}"
pkgver=1.15.12
pkgrel=1
pkgrel=2
pkgdesc="Cairo vector graphics library (mingw-w64)"
arch=('any')
url="https://cairographics.org/"
@@ -32,11 +32,17 @@ source=(#"https://cairographics.org/releases/cairo-${pkgver}.tar.xz"
https://cairographics.org/snapshots/cairo-${pkgver}.tar.xz
0009-standalone-headers.mingw.patch
0026-create-argb-fonts.all.patch
0027-win32-print-fix-unbounded-surface-assertion.patch)
0027-win32-print-fix-unbounded-surface-assertion.patch
0001-win32-introduce-new-flag-to-mark-surfaces-supporting.patch
0002-win32-CAIRO_WIN32_SURFACE_CAN_RGB_BRUSH-and-other-ar.patch
0003-win32-Allow-gdi-operations-for-argb32-surfaces-allow.patch)
sha256sums=('7623081b94548a47ee6839a7312af34e9322997806948b6eec421a8c6d0594c9'
'234de8c5d4c28b03c19e638a353e8defb2de0367a634c002b0ea7d2877bd0756'
'6db6c44fbdb4926d09afa978fe80430186c4b7b7d255059602b1f94c6a079975'
'7e244c20eec8c7b287dbee1d34de178d9b0c419dc4c2b11c90eaf626c92bf781')
'7e244c20eec8c7b287dbee1d34de178d9b0c419dc4c2b11c90eaf626c92bf781'
'f4fc46accb941c08fa017efe05ee871dd23ab630adc261f0ddf55d2d1e6e6722'
'f9deb2b1d74e8d2b94e4fe742bd81329e76e52383ba53446eba9ab3a2fd180e9'
'ea3eff7cf71ae074b846a28987e0d8f4dcf836e34bdbb04f38e7344398f43126')
prepare() {
cd "${srcdir}"/${_realname}-${pkgver}
@@ -44,6 +50,11 @@ prepare() {
patch -p1 -i ${srcdir}/0026-create-argb-fonts.all.patch
patch -p1 -i ${srcdir}/0027-win32-print-fix-unbounded-surface-assertion.patch
# https://lists.cairographics.org/archives/cairo/2018-April/028606.html
patch -p1 -i ${srcdir}/0001-win32-introduce-new-flag-to-mark-surfaces-supporting.patch
patch -p1 -i ${srcdir}/0002-win32-CAIRO_WIN32_SURFACE_CAN_RGB_BRUSH-and-other-ar.patch
patch -p1 -i ${srcdir}/0003-win32-Allow-gdi-operations-for-argb32-surfaces-allow.patch
autoreconf -fi
}