Date: Thursday, September 12, 2019 @ 21:06:45 Author: heftig Revision: 362344
3.34.0-1 Modified: mutter/trunk/PKGBUILD Deleted: mutter/trunk/0001-Add-point-polygon-testing-API.patch mutter/trunk/0001-Remove-GLX-threaded-swap-wait.patch mutter/trunk/0002-Geometric-OpenGL-less-picking.patch ------------------------------------------+ 0001-Add-point-polygon-testing-API.patch | 282 ---- 0001-Remove-GLX-threaded-swap-wait.patch | 467 ------- 0002-Geometric-OpenGL-less-picking.patch | 1714 ----------------------------- PKGBUILD | 39 4 files changed, 6 insertions(+), 2496 deletions(-) Deleted: 0001-Add-point-polygon-testing-API.patch =================================================================== --- 0001-Add-point-polygon-testing-API.patch 2019-09-12 20:57:17 UTC (rev 362343) +++ 0001-Add-point-polygon-testing-API.patch 2019-09-12 21:06:45 UTC (rev 362344) @@ -1,282 +0,0 @@ -From 37be90384d807c811785f51b5566e8b3b4db3ed3 Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt <[email protected]> -Date: Thu, 18 Jul 2019 16:56:41 +0800 -Subject: [PATCH 1/2] clutter-types: Add ClutterPoint polygon testing API - -https://gitlab.gnome.org/GNOME/mutter/merge_requests/189 ---- - clutter/clutter/clutter-base-types.c | 113 +++++++++++++++++++++++++++ - clutter/clutter/clutter-types.h | 8 ++ - clutter/tests/conform/meson.build | 1 + - clutter/tests/conform/point.c | 104 ++++++++++++++++++++++++ - 4 files changed, 226 insertions(+) - create mode 100644 clutter/tests/conform/point.c - -diff --git a/clutter/clutter/clutter-base-types.c b/clutter/clutter/clutter-base-types.c -index aeb25c90e..0d98c4814 100644 ---- a/clutter/clutter/clutter-base-types.c -+++ b/clutter/clutter/clutter-base-types.c -@@ -570,6 +570,119 @@ G_DEFINE_BOXED_TYPE_WITH_CODE (ClutterPoint, clutter_point, - clutter_point_free, - CLUTTER_REGISTER_INTERVAL_PROGRESS (clutter_point_progress)) - -+static int -+clutter_point_compare_line (const ClutterPoint *p, -+ const ClutterPoint *a, -+ const ClutterPoint *b) -+{ -+ /* -+ * Given two vectors ab and ap: -+ * ab = (x1, y1, 0) -+ * ap = (x2, y2, 0) -+ * their cross product is a vector: -+ * ab x ap = (0, 0, x1 * y2 - y1 * x2) -+ */ -+ float x1 = b->x - a->x; -+ float y1 = b->y - a->y; -+ float x2 = p->x - a->x; -+ float y2 = p->y - a->y; -+ float cross_z = x1 * y2 - y1 * x2; -+ -+ /* -+ * The cross product is also proportional to the sine of the angle between -+ * the vectors, so its sign tells us the sign of the angle between the two -+ * vectors. That is whether p is left of or right of line ab. -+ */ -+ if (cross_z > 0.f) -+ return +1; -+ else if (cross_z < 0.f) -+ return -1; -+ else -+ return 0; -+} -+ -+static int -+clutter_point_compare_polygon (const ClutterPoint *point, -+ const ClutterPoint *vertices, -+ unsigned int nvertices) -+{ -+ unsigned int i; -+ int first_side = 0; -+ gboolean on_an_edge = FALSE; -+ -+ g_return_val_if_fail (nvertices >= 3, +1); -+ -+ for (i = 0; i < nvertices; i++) -+ { -+ int side = clutter_point_compare_line (point, -+ &vertices[i], -+ &vertices[(i + 1) % nvertices]); -+ -+ if (side) -+ { -+ if (!first_side) -+ first_side = side; -+ else if (side != first_side) -+ return +1; /* outside */ -+ } -+ else -+ { -+ on_an_edge = TRUE; -+ } -+ } -+ -+ if (first_side) -+ { -+ return on_an_edge ? 0 : -1; /* on an edge or completely inside */ -+ } -+ else -+ { -+ /* The point was neither inside nor outside any edges so we have an empty -+ * polygon. Therefore if the vertices match the point we'll at least call -+ * that touching. Otherwise the point is outside. -+ */ -+ return point->x == vertices[0].x && point->y == vertices[0].y ? 0 : +1; -+ } -+} -+ -+/** -+ * clutter_point_inside_polygon: -+ * @point: a #ClutterPoint to test -+ * @vertices: array of vertices of the polygon -+ * @nvertices: number of vertices in array @vertices -+ * -+ * Determines whether a point is inside the convex polygon provided, and not -+ * on any of its edges or vertices. -+ * -+ * Return value: true if @point is inside the polygon -+ */ -+gboolean -+clutter_point_inside_polygon (const ClutterPoint *point, -+ const ClutterPoint *vertices, -+ unsigned int nvertices) -+{ -+ return clutter_point_compare_polygon (point, vertices, nvertices) < 0; -+} -+ -+/** -+ * clutter_point_touches_polygon: -+ * @point: a #ClutterPoint to test -+ * @vertices: array of vertices of the polygon -+ * @nvertices: number of vertices in array @vertices -+ * -+ * Determines whether a point is on the convex polygon provided, including -+ * on any of its edges or vertices. -+ * -+ * Return value: true if @point is on the polygon -+ */ -+gboolean -+clutter_point_touches_polygon (const ClutterPoint *point, -+ const ClutterPoint *vertices, -+ unsigned int nvertices) -+{ -+ return clutter_point_compare_polygon (point, vertices, nvertices) <= 0; -+} -+ - - - /* -diff --git a/clutter/clutter/clutter-types.h b/clutter/clutter/clutter-types.h -index 0f0fb1c2a..e3683184b 100644 ---- a/clutter/clutter/clutter-types.h -+++ b/clutter/clutter/clutter-types.h -@@ -200,6 +200,14 @@ float clutter_point_distance (const ClutterPoint *a, - const ClutterPoint *b, - float *x_distance, - float *y_distance); -+CLUTTER_EXPORT -+gboolean clutter_point_inside_polygon (const ClutterPoint *point, -+ const ClutterPoint *vertices, -+ unsigned int nvertices); -+CLUTTER_EXPORT -+gboolean clutter_point_touches_polygon (const ClutterPoint *point, -+ const ClutterPoint *vertices, -+ unsigned int nvertices); - - /** - * ClutterSize: -diff --git a/clutter/tests/conform/meson.build b/clutter/tests/conform/meson.build -index a9f2d7e20..916f5c342 100644 ---- a/clutter/tests/conform/meson.build -+++ b/clutter/tests/conform/meson.build -@@ -36,6 +36,7 @@ clutter_conform_tests_general_tests = [ - 'interval', - 'script-parser', - 'units', -+ 'point', - ] - - clutter_conform_tests_deprecated_tests = [ -diff --git a/clutter/tests/conform/point.c b/clutter/tests/conform/point.c -new file mode 100644 -index 000000000..a95a3e440 ---- /dev/null -+++ b/clutter/tests/conform/point.c -@@ -0,0 +1,104 @@ -+#include <clutter/clutter.h> -+ -+static void -+point_on_nonempty_polygon (void) -+{ -+ int p; -+ static const ClutterPoint vertices[4] = -+ { -+ {1.f, 2.f}, -+ {6.f, 3.f}, -+ {7.f, 6.f}, -+ {0.f, 5.f} -+ }; -+ static const ClutterPoint points_inside[] = -+ { -+ {2.f, 3.f}, -+ {1.f, 4.f}, -+ {5.f, 5.f}, -+ {4.f, 3.f}, -+ }; -+ static const ClutterPoint points_outside[] = -+ { -+ {3.f, 1.f}, -+ {7.f, 4.f}, -+ {4.f, 6.f}, -+ {99.f, -77.f}, -+ {-1.f, 3.f}, -+ {-8.f, -8.f}, -+ {11.f, 4.f}, -+ {-7.f, 4.f}, -+ }; -+ static const ClutterPoint points_touching[] = -+ { -+ {1.f, 2.f}, -+ {3.5f, 2.5f}, -+ {6.f, 3.f}, -+ {6.5f, 4.5f}, -+ {7.f, 6.f}, -+ {3.5f, 5.5f}, -+ {0.f, 5.f} -+ }; -+ -+ for (p = 0; p < G_N_ELEMENTS (points_inside); p++) -+ { -+ const ClutterPoint *point = points_inside + p; -+ -+ g_assert_true (clutter_point_inside_polygon (point, vertices, 4)); -+ g_assert_true (clutter_point_touches_polygon (point, vertices, 4)); -+ } -+ -+ for (p = 0; p < G_N_ELEMENTS (points_outside); p++) -+ { -+ const ClutterPoint *point = points_outside + p; -+ -+ g_assert_false (clutter_point_inside_polygon (point, vertices, 4)); -+ g_assert_false (clutter_point_touches_polygon (point, vertices, 4)); -+ } -+ -+ for (p = 0; p < G_N_ELEMENTS (points_touching); p++) -+ { -+ const ClutterPoint *point = points_touching + p; -+ -+ g_assert_false (clutter_point_inside_polygon (point, vertices, 4)); -+ g_assert_true (clutter_point_touches_polygon (point, vertices, 4)); -+ } -+} -+ -+static void -+point_on_empty_polygon (void) -+{ -+ int p; -+ static const ClutterPoint vertices[4] = -+ { -+ {5.f, 6.f}, -+ {5.f, 6.f}, -+ {5.f, 6.f}, -+ {5.f, 6.f}, -+ }; -+ static const ClutterPoint points_outside[] = -+ { -+ {3.f, 1.f}, -+ {7.f, 4.f}, -+ {4.f, 6.f}, -+ {99.f, -77.f}, -+ {-1.f, 3.f}, -+ {-8.f, -8.f}, -+ }; -+ -+ for (p = 0; p < G_N_ELEMENTS (points_outside); p++) -+ { -+ const ClutterPoint *point = points_outside + p; -+ -+ g_assert_false (clutter_point_inside_polygon (point, vertices, 4)); -+ g_assert_false (clutter_point_touches_polygon (point, vertices, 4)); -+ } -+ -+ g_assert_false (clutter_point_inside_polygon (&vertices[0], vertices, 4)); -+ g_assert_true (clutter_point_touches_polygon (&vertices[0], vertices, 4)); -+} -+ -+CLUTTER_TEST_SUITE ( -+ CLUTTER_TEST_UNIT ("/point/on_nonempty_polygon", point_on_nonempty_polygon) -+ CLUTTER_TEST_UNIT ("/point/on_empty_polygon", point_on_empty_polygon) -+) --- -2.23.0 - Deleted: 0001-Remove-GLX-threaded-swap-wait.patch =================================================================== --- 0001-Remove-GLX-threaded-swap-wait.patch 2019-09-12 20:57:17 UTC (rev 362343) +++ 0001-Remove-GLX-threaded-swap-wait.patch 2019-09-12 21:06:45 UTC (rev 362344) @@ -1,467 +0,0 @@ -From 0ff4d5ed3423c3eeb7e7e597c571148fc98834cf Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt <[email protected]> -Date: Fri, 31 May 2019 14:59:22 +0800 -Subject: [PATCH] cogl: Remove GLX "threaded swap wait" used on Nvidia - -The single purpose of "threaded swap wait" was to provide the value: -`u.presentation_time = get_monotonic_time_ns ();` for use by -`clutter-stage-cogl`. - -Until recently (before !363), all backends were required to provide -a nonzero value for `presentation_time` or else suffer falling back -to poor-performing throttling methods in `master_clock_next_frame_delay`. -So we needed "threaded swap wait" to support the Nvidia driver. - -This is no longer true. The fallbacks don't exist any more and -`clutter_stage_cogl_schedule_update` now always succeeds even in the -absence of a `presentation_time` (since !363). - -The drawbacks to keeping "threaded swap wait" are: - - * `u.presentation_time = get_monotonic_time_ns ();` is a guess and not - an accurate hardware presentation time. - * It required blocking the main loop on every frame in - `_cogl_winsys_wait_for_gpu` due to `glFinish`. Any OpenGL programmer - will tell you calling `glFinish` is a bad idea because it kills CPU-GPU - parallelism. In my case, it was blocking the main loop for 1-3ms on - every mutter frame. It's easy to imagine slower (or higher resolution) - Nvidia systems would lose an even larger chunk of their frame interval - blocked in that function. This significantly crippled frame rates on - Nvidia systems. - -The benefit to keeping "threaded swap wait" is: - - * Its guess of `presentation_time` is likely a better guess by a few - milliseconds than the guess that `clutter_stage_cogl_schedule_update` - will make in its place. - -So "threaded swap wait" provided better sub-frame phase accuracy, but at -the expense of frame rates. And as soon as it starts causing frame drops, -that one and only benefit is lost. There is no reason to keep it. - -And in case you are wondering, the documentation for "threaded swap wait" -is now wrong (since !363): - - > The advantage of enabling this is that it will allow your main loop - > to do other work while waiting for the system to be ready to draw - > the next frame, instead of blocking in glXSwapBuffers()." - -At the time (before !363) it was true that "threaded swap wait" avoided -swap interval throttling that would occur as a result of -`master_clock_next_frame_delay` blindly returning zero and over-queuing -frames. That code no longer exists. And ironically the implementation of -"threaded swap wait" necessitates the same kind of blocking (to a lesser -extent) that it was designed to avoid. We can eliminate all blocking -however by deleting "threaded swap wait", which is now safe since !363. - -https://gitlab.gnome.org/GNOME/mutter/merge_requests/602 ---- - cogl/cogl/cogl-private.h | 3 - - cogl/cogl/cogl-renderer-private.h | 1 - - cogl/cogl/cogl-renderer.c | 11 -- - cogl/cogl/cogl-xlib-renderer.h | 30 ---- - cogl/cogl/winsys/cogl-winsys-glx.c | 240 +-------------------------- - src/backends/x11/meta-backend-x11.c | 6 - - src/backends/x11/meta-renderer-x11.c | 8 - - 7 files changed, 2 insertions(+), 297 deletions(-) - -diff --git a/cogl/cogl/cogl-private.h b/cogl/cogl/cogl-private.h -index 9f918b851..db0f08df0 100644 ---- a/cogl/cogl/cogl-private.h -+++ b/cogl/cogl/cogl-private.h -@@ -76,9 +76,6 @@ typedef enum - COGL_PRIVATE_FEATURE_GL_PROGRAMMABLE, - COGL_PRIVATE_FEATURE_GL_EMBEDDED, - COGL_PRIVATE_FEATURE_GL_WEB, -- /* This is currently only implemented for GLX, but isn't actually -- * that winsys dependent */ -- COGL_PRIVATE_FEATURE_THREADED_SWAP_WAIT, - - COGL_N_PRIVATE_FEATURES - } CoglPrivateFeature; -diff --git a/cogl/cogl/cogl-renderer-private.h b/cogl/cogl/cogl-renderer-private.h -index a308801a7..cc8f94bf1 100644 ---- a/cogl/cogl/cogl-renderer-private.h -+++ b/cogl/cogl/cogl-renderer-private.h -@@ -71,7 +71,6 @@ struct _CoglRenderer - Display *foreign_xdpy; - gboolean xlib_enable_event_retrieval; - gboolean xlib_want_reset_on_video_memory_purge; -- gboolean xlib_enable_threaded_swap_wait; - #endif - - CoglDriver driver; -diff --git a/cogl/cogl/cogl-renderer.c b/cogl/cogl/cogl-renderer.c -index 854aa1fdd..bdc9762e8 100644 ---- a/cogl/cogl/cogl-renderer.c -+++ b/cogl/cogl/cogl-renderer.c -@@ -270,17 +270,6 @@ cogl_xlib_renderer_request_reset_on_video_memory_purge (CoglRenderer *renderer, - - renderer->xlib_want_reset_on_video_memory_purge = enable; - } -- --void --cogl_xlib_renderer_set_threaded_swap_wait_enabled (CoglRenderer *renderer, -- gboolean enable) --{ -- _COGL_RETURN_IF_FAIL (cogl_is_renderer (renderer)); -- /* NB: Renderers are considered immutable once connected */ -- _COGL_RETURN_IF_FAIL (!renderer->connected); -- -- renderer->xlib_enable_threaded_swap_wait = enable; --} - #endif /* COGL_HAS_XLIB_SUPPORT */ - - gboolean -diff --git a/cogl/cogl/cogl-xlib-renderer.h b/cogl/cogl/cogl-xlib-renderer.h -index 3a3c08d64..e8fe43a51 100644 ---- a/cogl/cogl/cogl-xlib-renderer.h -+++ b/cogl/cogl/cogl-xlib-renderer.h -@@ -167,36 +167,6 @@ void - cogl_xlib_renderer_set_event_retrieval_enabled (CoglRenderer *renderer, - gboolean enable); - --/** -- * cogl_xlib_renderer_set_threaded_swap_wait_enabled: (skip) -- * @renderer: a #CoglRenderer -- * @enable: The new value -- * -- * Sets whether Cogl is allowed to use a separate threaded to wait for the -- * completion of glXSwapBuffers() and call the frame callback for the -- * corresponding #CoglOnscreen. This is a way of emulating the -- * INTEL_swap_event extension, and will only ever be used if -- * INTEL_swap_event is not present; it will also only be used for -- * specific white-listed drivers that are known to work correctly with -- * multiple contexts sharing state between threads. -- * -- * The advantage of enabling this is that it will allow your main loop -- * to do other work while waiting for the system to be ready to draw -- * the next frame, instead of blocking in glXSwapBuffers(). A disadvantage -- * is that the driver will be prevented from buffering up multiple frames -- * even if it thinks that it would be advantageous. In general, this -- * will work best for something like a system compositor that is doing -- * simple drawing but handling lots of other complex tasks. -- * -- * If you enable this, you must call XInitThreads() before any other -- * X11 calls in your program. (See the documentation for XInitThreads()) -- * -- * Stability: unstable -- */ --void --cogl_xlib_renderer_set_threaded_swap_wait_enabled (CoglRenderer *renderer, -- gboolean enable); -- - /** - * cogl_xlib_renderer_get_display: (skip) - */ -diff --git a/cogl/cogl/winsys/cogl-winsys-glx.c b/cogl/cogl/winsys/cogl-winsys-glx.c -index 235cfe81f..89e60a539 100644 ---- a/cogl/cogl/winsys/cogl-winsys-glx.c -+++ b/cogl/cogl/winsys/cogl-winsys-glx.c -@@ -895,29 +895,6 @@ update_winsys_features (CoglContext *context, CoglError **error) - COGL_FEATURE_ID_PRESENTATION_TIME, - TRUE); - } -- else -- { -- CoglGpuInfo *info = &context->gpu; -- if (glx_display->have_vblank_counter && -- context->display->renderer->xlib_enable_threaded_swap_wait && -- info->vendor == COGL_GPU_INFO_VENDOR_NVIDIA) -- { -- COGL_FLAGS_SET (context->winsys_features, -- COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT, TRUE); -- COGL_FLAGS_SET (context->winsys_features, -- COGL_WINSYS_FEATURE_SWAP_BUFFERS_EVENT, TRUE); -- /* TODO: remove this deprecated feature */ -- COGL_FLAGS_SET (context->features, -- COGL_FEATURE_ID_SWAP_BUFFERS_EVENT, -- TRUE); -- COGL_FLAGS_SET (context->features, -- COGL_FEATURE_ID_PRESENTATION_TIME, -- TRUE); -- COGL_FLAGS_SET (context->private_features, -- COGL_PRIVATE_FEATURE_THREADED_SWAP_WAIT, -- TRUE); -- } -- } - - /* We'll manually handle queueing dirty events in response to - * Expose events from X */ -@@ -1514,8 +1491,7 @@ _cogl_winsys_onscreen_init (CoglOnscreen *onscreen, - } - - #ifdef GLX_INTEL_swap_event -- if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT) && -- !_cogl_has_private_feature (context, COGL_PRIVATE_FEATURE_THREADED_SWAP_WAIT)) -+ if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT)) - { - GLXDrawable drawable = - glx_onscreen->glxwin ? glx_onscreen->glxwin : xlib_onscreen->xwin; -@@ -1810,199 +1786,6 @@ set_frame_info_output (CoglOnscreen *onscreen, - } - } - --static gpointer --threaded_swap_wait (gpointer data) --{ -- CoglOnscreen *onscreen = data; -- -- CoglOnscreenGLX *glx_onscreen = onscreen->winsys; -- -- CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); -- CoglContext *context = framebuffer->context; -- CoglDisplay *display = context->display; -- CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (display->renderer); -- CoglGLXDisplay *glx_display = display->winsys; -- CoglGLXRenderer *glx_renderer = display->renderer->winsys; -- GLXDrawable dummy_drawable; -- -- if (glx_display->dummy_glxwin) -- dummy_drawable = glx_display->dummy_glxwin; -- else -- dummy_drawable = glx_display->dummy_xwin; -- -- glx_renderer->glXMakeContextCurrent (xlib_renderer->xdpy, -- dummy_drawable, -- dummy_drawable, -- glx_onscreen->swap_wait_context); -- -- g_mutex_lock (&glx_onscreen->swap_wait_mutex); -- -- while (TRUE) -- { -- gpointer queue_element; -- uint32_t vblank_counter; -- -- while (!glx_onscreen->closing_down && glx_onscreen->swap_wait_queue->length == 0) -- g_cond_wait (&glx_onscreen->swap_wait_cond, &glx_onscreen->swap_wait_mutex); -- -- if (glx_onscreen->closing_down) -- break; -- -- queue_element = g_queue_pop_tail (glx_onscreen->swap_wait_queue); -- vblank_counter = GPOINTER_TO_UINT(queue_element); -- -- g_mutex_unlock (&glx_onscreen->swap_wait_mutex); -- glx_renderer->glXWaitVideoSync (2, -- (vblank_counter + 1) % 2, -- &vblank_counter); -- g_mutex_lock (&glx_onscreen->swap_wait_mutex); -- -- if (!glx_onscreen->closing_down) -- { -- int bytes_written = 0; -- -- union { -- char bytes[8]; -- int64_t presentation_time; -- } u; -- -- u.presentation_time = get_monotonic_time_ns (); -- -- while (bytes_written < 8) -- { -- int res = write (glx_onscreen->swap_wait_pipe[1], u.bytes + bytes_written, 8 - bytes_written); -- if (res == -1) -- { -- if (errno != EINTR) -- g_error ("Error writing to swap notification pipe: %s\n", -- g_strerror (errno)); -- } -- else -- { -- bytes_written += res; -- } -- } -- } -- } -- -- g_mutex_unlock (&glx_onscreen->swap_wait_mutex); -- -- glx_renderer->glXMakeContextCurrent (xlib_renderer->xdpy, -- None, -- None, -- NULL); -- -- return NULL; --} -- --static int64_t --threaded_swap_wait_pipe_prepare (void *user_data) --{ -- return -1; --} -- --static void --threaded_swap_wait_pipe_dispatch (void *user_data, int revents) --{ -- CoglOnscreen *onscreen = user_data; -- CoglOnscreenGLX *glx_onscreen = onscreen->winsys; -- -- CoglFrameInfo *info; -- -- if ((revents & COGL_POLL_FD_EVENT_IN)) -- { -- int bytes_read = 0; -- -- union { -- char bytes[8]; -- int64_t presentation_time; -- } u; -- -- while (bytes_read < 8) -- { -- int res = read (glx_onscreen->swap_wait_pipe[0], u.bytes + bytes_read, 8 - bytes_read); -- if (res == -1) -- { -- if (errno != EINTR) -- g_error ("Error reading from swap notification pipe: %s\n", -- g_strerror (errno)); -- } -- else -- { -- bytes_read += res; -- } -- } -- -- set_sync_pending (onscreen); -- set_complete_pending (onscreen); -- -- info = g_queue_peek_head (&onscreen->pending_frame_infos); -- info->presentation_time = u.presentation_time; -- } --} -- --static void --start_threaded_swap_wait (CoglOnscreen *onscreen, -- uint32_t vblank_counter) --{ -- CoglOnscreenGLX *glx_onscreen = onscreen->winsys; -- CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); -- CoglContext *context = framebuffer->context; -- -- if (glx_onscreen->swap_wait_thread == NULL) -- { -- CoglDisplay *display = context->display; -- CoglGLXRenderer *glx_renderer = display->renderer->winsys; -- CoglGLXDisplay *glx_display = display->winsys; -- CoglOnscreenXlib *xlib_onscreen = onscreen->winsys; -- CoglXlibRenderer *xlib_renderer = -- _cogl_xlib_renderer_get_data (display->renderer); -- -- GLXDrawable drawable = -- glx_onscreen->glxwin ? glx_onscreen->glxwin : xlib_onscreen->xwin; -- int i; -- -- ensure_ust_type (display->renderer, drawable); -- -- if ((pipe (glx_onscreen->swap_wait_pipe) == -1)) -- g_error ("Couldn't create pipe for swap notification: %s\n", -- g_strerror (errno)); -- -- for (i = 0; i < 2; i++) -- { -- if (fcntl(glx_onscreen->swap_wait_pipe[i], F_SETFD, -- fcntl(glx_onscreen->swap_wait_pipe[i], F_GETFD, 0) | FD_CLOEXEC) == -1) -- g_error ("Couldn't set swap notification pipe CLOEXEC: %s\n", -- g_strerror (errno)); -- } -- -- _cogl_poll_renderer_add_fd (display->renderer, -- glx_onscreen->swap_wait_pipe[0], -- COGL_POLL_FD_EVENT_IN, -- threaded_swap_wait_pipe_prepare, -- threaded_swap_wait_pipe_dispatch, -- onscreen); -- -- glx_onscreen->swap_wait_queue = g_queue_new (); -- g_mutex_init (&glx_onscreen->swap_wait_mutex); -- g_cond_init (&glx_onscreen->swap_wait_cond); -- glx_onscreen->swap_wait_context = -- glx_renderer->glXCreateNewContext (xlib_renderer->xdpy, -- glx_display->fbconfig, -- GLX_RGBA_TYPE, -- glx_display->glx_context, -- True); -- glx_onscreen->swap_wait_thread = g_thread_new ("cogl_glx_swap_wait", -- threaded_swap_wait, -- onscreen); -- } -- -- g_mutex_lock (&glx_onscreen->swap_wait_mutex); -- g_queue_push_head (glx_onscreen->swap_wait_queue, GUINT_TO_POINTER(vblank_counter)); -- g_cond_signal (&glx_onscreen->swap_wait_cond); -- g_mutex_unlock (&glx_onscreen->swap_wait_mutex); --} -- - static void - _cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen, - const int *user_rectangles, -@@ -2238,26 +2021,7 @@ _cogl_winsys_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen, - - have_counter = glx_display->have_vblank_counter; - -- if (glx_renderer->glXSwapInterval) -- { -- if (_cogl_has_private_feature (context, COGL_PRIVATE_FEATURE_THREADED_SWAP_WAIT)) -- { -- /* If we didn't wait for the GPU here, then it's easy to get the case -- * where there is a VBlank between the point where we get the vsync counter -- * and the point where the GPU is ready to actually perform the glXSwapBuffers(), -- * and the swap wait terminates at the first VBlank rather than the one -- * where the swap buffers happens. Calling glFinish() here makes this a -- * rare race since the GPU is already ready to swap when we call glXSwapBuffers(). -- * The glFinish() also prevents any serious damage if the rare race happens, -- * since it will wait for the preceding glXSwapBuffers() and prevent us from -- * getting premanently ahead. (For NVIDIA drivers, glFinish() after glXSwapBuffers() -- * waits for the buffer swap to happen.) -- */ -- _cogl_winsys_wait_for_gpu (onscreen); -- start_threaded_swap_wait (onscreen, _cogl_winsys_get_vsync_counter (context)); -- } -- } -- else -+ if (!glx_renderer->glXSwapInterval) - { - gboolean can_wait = have_counter || glx_display->can_vblank_wait; - -diff --git a/src/backends/x11/meta-backend-x11.c b/src/backends/x11/meta-backend-x11.c -index c10365f9d..7bade4844 100644 ---- a/src/backends/x11/meta-backend-x11.c -+++ b/src/backends/x11/meta-backend-x11.c -@@ -786,12 +786,6 @@ meta_backend_x11_class_init (MetaBackendX11Class *klass) - static void - meta_backend_x11_init (MetaBackendX11 *x11) - { -- /* XInitThreads() is needed to use the "threaded swap wait" functionality -- * in Cogl - see meta_renderer_x11_create_cogl_renderer(). We call it here -- * to hopefully call it before any other use of XLib. -- */ -- XInitThreads(); -- - /* We do X11 event retrieval ourselves */ - clutter_x11_disable_event_retrieval (); - } -diff --git a/src/backends/x11/meta-renderer-x11.c b/src/backends/x11/meta-renderer-x11.c -index a501416e7..96beb4eb8 100644 ---- a/src/backends/x11/meta-renderer-x11.c -+++ b/src/backends/x11/meta-renderer-x11.c -@@ -85,14 +85,6 @@ meta_renderer_x11_create_cogl_renderer (MetaRenderer *renderer) - cogl_xlib_renderer_set_foreign_display (cogl_renderer, xdisplay); - cogl_xlib_renderer_request_reset_on_video_memory_purge (cogl_renderer, TRUE); - -- /* Set up things so that if the INTEL_swap_event extension is not present, -- * but the driver is known to have good thread support, we use an extra -- * thread and call glXWaitVideoSync() in the thread. This allows idles -- * to work properly, even when Mutter is constantly redrawing new frames; -- * otherwise, without INTEL_swap_event, we'll just block in glXSwapBuffers(). -- */ -- cogl_xlib_renderer_set_threaded_swap_wait_enabled (cogl_renderer, TRUE); -- - return cogl_renderer; - } - --- -2.22.0 - Deleted: 0002-Geometric-OpenGL-less-picking.patch =================================================================== --- 0002-Geometric-OpenGL-less-picking.patch 2019-09-12 20:57:17 UTC (rev 362343) +++ 0002-Geometric-OpenGL-less-picking.patch 2019-09-12 21:06:45 UTC (rev 362344) @@ -1,1714 +0,0 @@ -From 6a18d760ed05b87af7aae43f1963eb32bc1d81e7 Mon Sep 17 00:00:00 2001 -From: Daniel van Vugt <[email protected]> -Date: Thu, 2 Aug 2018 19:03:30 +0800 -Subject: [PATCH 2/2] Geometric (OpenGL-less) picking - -Cherry picked from commit b9170a69314739638db7f471b87ba55a906bdf03. - -Redesign picking to avoid using OpenGL. Previously picking involved -several GL operations per mouse movement, but now the GPU/GL isn't -used at all. - -By avoiding OpenGL and the graphics driver we also reduce CPU usage. -Despite reimplementing the logic on the CPU, it still takes less CPU -time than going through GL did. - -This new approach also dramatically reduces the number of picking paint -cycles required for cursor movement since the pickability of the entire -screen is calculated and cached. The cache is only invalidated when the -screen contents change so for typical desktop usage where the screen is -mostly idle, cursor movement doesn't incur anywhere near as many paint -cycles as it used to. - -CPU usage on an Intel i7-7700, tested with two different GPUs/drivers: - - | | Intel | Nvidia | - | ------: | --------: | -----: | - | Moving the mouse: | - | Before | 10% | 10% | - | After | 6% | 6% | - | Moving a window: | - | Before | 23% | 81% | - | After | 19% | 40% | - -Closes: https://gitlab.gnome.org/GNOME/mutter/issues/154, - https://gitlab.gnome.org/GNOME/mutter/issues/691 - -Helps significantly with: https://gitlab.gnome.org/GNOME/mutter/issues/283, - https://gitlab.gnome.org/GNOME/mutter/issues/590, - https://gitlab.gnome.org/GNOME/mutter/issues/700 - -https://gitlab.gnome.org/GNOME/mutter/merge_requests/189 ---- - clutter/clutter/clutter-actor-private.h | 2 - - clutter/clutter/clutter-actor.c | 254 +++++++---- - clutter/clutter/clutter-actor.h | 4 + - clutter/clutter/clutter-debug.h | 3 +- - clutter/clutter/clutter-main.c | 120 ----- - clutter/clutter/clutter-private.h | 5 - - clutter/clutter/clutter-stage-private.h | 16 +- - clutter/clutter/clutter-stage-window.c | 18 - - clutter/clutter/clutter-stage-window.h | 8 - - clutter/clutter/clutter-stage.c | 433 +++++++++++-------- - clutter/clutter/cogl/clutter-stage-cogl.c | 50 --- - clutter/clutter/deprecated/clutter-texture.c | 93 +--- - clutter/tests/conform/actor-pick.c | 99 +---- - clutter/tests/conform/meson.build | 1 - - clutter/tests/conform/texture.c | 84 ---- - src/compositor/meta-surface-actor.c | 27 +- - 16 files changed, 434 insertions(+), 783 deletions(-) - delete mode 100644 clutter/tests/conform/texture.c - -diff --git a/clutter/clutter/clutter-actor-private.h b/clutter/clutter/clutter-actor-private.h -index c44f6342f..9bf1a3049 100644 ---- a/clutter/clutter/clutter-actor-private.h -+++ b/clutter/clutter/clutter-actor-private.h -@@ -297,8 +297,6 @@ const gchar * _clutter_actor_get_debug_name - void _clutter_actor_push_clone_paint (void); - void _clutter_actor_pop_clone_paint (void); - --guint32 _clutter_actor_get_pick_id (ClutterActor *self); -- - void _clutter_actor_shader_pre_paint (ClutterActor *actor, - gboolean repeat); - void _clutter_actor_shader_post_paint (ClutterActor *actor); -diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c -index a7fecb830..5feae6574 100644 ---- a/clutter/clutter/clutter-actor.c -+++ b/clutter/clutter/clutter-actor.c -@@ -729,8 +729,6 @@ struct _ClutterActorPrivate - - gchar *name; /* a non-unique name, used for debugging */ - -- gint32 pick_id; /* per-stage unique id, used for picking */ -- - /* a back-pointer to the Pango context that we can use - * to create pre-configured PangoLayout - */ -@@ -1282,6 +1280,129 @@ clutter_actor_verify_map_state (ClutterActor *self) - - #endif /* CLUTTER_ENABLE_DEBUG */ - -+static gboolean -+_clutter_actor_transform_local_box_to_stage (ClutterActor *self, -+ const ClutterActorBox *box, -+ ClutterPoint vertices[4]) -+{ -+ ClutterActor *stage = _clutter_actor_get_stage_internal (self); -+ CoglMatrix stage_transform, inv_stage_transform; -+ CoglMatrix modelview, transform_to_stage; -+ int v; -+ -+ g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); -+ g_return_val_if_fail (box != NULL, FALSE); -+ -+ g_return_val_if_fail (stage != NULL, FALSE); -+ g_return_val_if_fail (box->x1 <= box->x2, FALSE); -+ g_return_val_if_fail (box->y1 <= box->y2, FALSE); -+ -+ /* Below is generally equivalent to: -+ * -+ * _clutter_actor_get_relative_transformation_matrix (self, -+ * stage, -+ * &transform_to_stage); -+ * -+ * but we do it the hard way here instead so as to accurately include any -+ * cogl transformations that an actor's paint function might have added in. -+ * Those additional transformations are only known to cogl matrices and not -+ * known to the clutter getter like above. So this way we more accurately -+ * represent what's really getting painted. -+ */ -+ clutter_actor_get_transform (stage, &stage_transform); -+ if (!cogl_matrix_get_inverse (&stage_transform, &inv_stage_transform)) -+ return FALSE; -+ cogl_get_modelview_matrix (&modelview); -+ cogl_matrix_multiply (&transform_to_stage, &inv_stage_transform, &modelview); -+ -+ vertices[0].x = box->x1; -+ vertices[0].y = box->y1; -+ -+ vertices[1].x = box->x2; -+ vertices[1].y = box->y1; -+ -+ vertices[2].x = box->x2; -+ vertices[2].y = box->y2; -+ -+ vertices[3].x = box->x1; -+ vertices[3].y = box->y2; -+ -+ for (v = 0; v < 4; v++) -+ { -+ gfloat z = 0.f; -+ gfloat w = 1.f; -+ -+ cogl_matrix_transform_point (&transform_to_stage, -+ &vertices[v].x, -+ &vertices[v].y, -+ &z, -+ &w); -+ } -+ -+ return TRUE; -+} -+ -+/** -+ * clutter_actor_pick_box: -+ * @self: The #ClutterActor being "pick" painted. -+ * @box: A rectangle in the actor's own local coordinates. -+ * -+ * Logs (does a virtual paint of) a rectangle for picking. Note that @box is -+ * in the actor's own local coordinates, so is usually {0,0,width,height} -+ * to include the whole actor. That is unless the actor has a shaped input -+ * region in which case you may wish to log the (multiple) smaller rectangles -+ * that make up the input region. -+ */ -+void -+clutter_actor_pick_box (ClutterActor *self, -+ const ClutterActorBox *box) -+{ -+ ClutterActor *stage; -+ ClutterPoint vertices[4]; -+ -+ g_return_if_fail (CLUTTER_IS_ACTOR (self)); -+ g_return_if_fail (box != NULL); -+ -+ /* An empty box to a "pick" paint means to paint nothing at all. */ -+ if (box->x1 >= box->x2 || box->y1 >= box->y2) -+ return; -+ -+ stage = _clutter_actor_get_stage_internal (self); -+ -+ if (_clutter_actor_transform_local_box_to_stage (self, box, vertices)) -+ _clutter_stage_log_pick (CLUTTER_STAGE (stage), vertices, self); -+} -+ -+static gboolean -+_clutter_actor_push_pick_clip (ClutterActor *self, -+ const ClutterActorBox *clip) -+{ -+ ClutterActor *stage; -+ ClutterPoint vertices[4]; -+ -+ g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); -+ g_return_val_if_fail (clip != NULL, FALSE); -+ -+ stage = _clutter_actor_get_stage_internal (self); -+ -+ if (!_clutter_actor_transform_local_box_to_stage (self, clip, vertices)) -+ return FALSE; -+ -+ _clutter_stage_push_pick_clip (CLUTTER_STAGE (stage), vertices); -+ return TRUE; -+} -+ -+static void -+_clutter_actor_pop_pick_clip (ClutterActor *self) -+{ -+ ClutterActor *stage; -+ -+ g_return_if_fail (CLUTTER_IS_ACTOR (self)); -+ -+ stage = _clutter_actor_get_stage_internal (self); -+ _clutter_stage_pop_pick_clip (CLUTTER_STAGE (stage)); -+} -+ - static void - clutter_actor_set_mapped (ClutterActor *self, - gboolean mapped) -@@ -1510,8 +1631,7 @@ clutter_actor_update_map_state (ClutterActor *self, - static void - clutter_actor_real_map (ClutterActor *self) - { -- ClutterActorPrivate *priv = self->priv; -- ClutterActor *stage, *iter; -+ ClutterActor *iter; - - g_assert (!CLUTTER_ACTOR_IS_MAPPED (self)); - -@@ -1522,13 +1642,6 @@ clutter_actor_real_map (ClutterActor *self) - - self->priv->needs_paint_volume_update = TRUE; - -- stage = _clutter_actor_get_stage_internal (self); -- priv->pick_id = _clutter_stage_acquire_pick_id (CLUTTER_STAGE (stage), self); -- -- CLUTTER_NOTE (ACTOR, "Pick id '%d' for actor '%s'", -- priv->pick_id, -- _clutter_actor_get_debug_name (self)); -- - clutter_actor_ensure_resource_scale (self); - - /* notify on parent mapped before potentially mapping -@@ -1633,11 +1746,6 @@ clutter_actor_real_unmap (ClutterActor *self) - - stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self)); - -- if (stage != NULL) -- _clutter_stage_release_pick_id (stage, priv->pick_id); -- -- priv->pick_id = -1; -- - if (stage != NULL && - clutter_stage_get_key_focus (stage) == self) - { -@@ -2256,46 +2364,14 @@ static void - clutter_actor_real_pick (ClutterActor *self, - const ClutterColor *color) - { -- CoglFramebuffer *framebuffer = cogl_get_draw_framebuffer (); -- -- /* the default implementation is just to paint a rectangle -- * with the same size of the actor using the passed color -- */ - if (clutter_actor_should_pick_paint (self)) - { -- static CoglPipeline *default_pick_pipeline = NULL; -- ClutterActorBox box = { 0, }; -- CoglPipeline *pick_pipeline; -- float width, height; -- -- if (G_UNLIKELY (default_pick_pipeline == NULL)) -- { -- CoglContext *ctx = -- clutter_backend_get_cogl_context (clutter_get_default_backend ()); -- -- default_pick_pipeline = cogl_pipeline_new (ctx); -- } -- -- g_assert (default_pick_pipeline != NULL); -- pick_pipeline = cogl_pipeline_copy (default_pick_pipeline); -- -- clutter_actor_get_allocation_box (self, &box); -- -- width = box.x2 - box.x1; -- height = box.y2 - box.y1; -- -- cogl_pipeline_set_color4ub (pick_pipeline, -- color->red, -- color->green, -- color->blue, -- color->alpha); -+ ClutterActorBox box = {0, -+ 0, -+ clutter_actor_get_width (self), -+ clutter_actor_get_height (self)}; - -- cogl_framebuffer_draw_rectangle (framebuffer, -- pick_pipeline, -- 0, 0, -- width, height); -- -- cogl_object_unref (pick_pipeline); -+ clutter_actor_pick_box (self, &box); - } - - /* XXX - this thoroughly sucks, but we need to maintain compatibility -@@ -3586,15 +3662,6 @@ _clutter_actor_update_last_paint_volume (ClutterActor *self) - priv->last_paint_volume_valid = TRUE; - } - --guint32 --_clutter_actor_get_pick_id (ClutterActor *self) --{ -- if (self->priv->pick_id < 0) -- return 0; -- -- return self->priv->pick_id; --} -- - /* This is the same as clutter_actor_add_effect except that it doesn't - queue a redraw and it doesn't notify on the effect property */ - static void -@@ -3826,6 +3893,7 @@ clutter_actor_paint (ClutterActor *self) - { - ClutterActorPrivate *priv; - ClutterPickMode pick_mode; -+ ClutterActorBox clip; - gboolean clip_set = FALSE; - ClutterStage *stage; - -@@ -3919,24 +3987,38 @@ clutter_actor_paint (ClutterActor *self) - - if (priv->has_clip) - { -- CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage); -- cogl_framebuffer_push_rectangle_clip (fb, -- priv->clip.origin.x, -- priv->clip.origin.y, -- priv->clip.origin.x + priv->clip.size.width, -- priv->clip.origin.y + priv->clip.size.height); -+ clip.x1 = priv->clip.origin.x; -+ clip.y1 = priv->clip.origin.y; -+ clip.x2 = priv->clip.origin.x + priv->clip.size.width; -+ clip.y2 = priv->clip.origin.y + priv->clip.size.height; - clip_set = TRUE; - } - else if (priv->clip_to_allocation) - { -- CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage); -- gfloat width, height; -+ clip.x1 = 0.f; -+ clip.y1 = 0.f; -+ clip.x2 = priv->allocation.x2 - priv->allocation.x1; -+ clip.y2 = priv->allocation.y2 - priv->allocation.y1; -+ clip_set = TRUE; -+ } - -- width = priv->allocation.x2 - priv->allocation.x1; -- height = priv->allocation.y2 - priv->allocation.y1; -+ if (clip_set) -+ { -+ if (pick_mode == CLUTTER_PICK_NONE) -+ { -+ CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage); - -- cogl_framebuffer_push_rectangle_clip (fb, 0, 0, width, height); -- clip_set = TRUE; -+ cogl_framebuffer_push_rectangle_clip (fb, -+ clip.x1, -+ clip.y1, -+ clip.x2, -+ clip.y2); -+ } -+ else -+ { -+ if (!_clutter_actor_push_pick_clip (self, &clip)) -+ clip_set = FALSE; -+ } - } - - if (pick_mode == CLUTTER_PICK_NONE) -@@ -4017,9 +4099,16 @@ clutter_actor_paint (ClutterActor *self) - done: - if (clip_set) - { -- CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage); -+ if (pick_mode == CLUTTER_PICK_NONE) -+ { -+ CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage); - -- cogl_framebuffer_pop_clip (fb); -+ cogl_framebuffer_pop_clip (fb); -+ } -+ else -+ { -+ _clutter_actor_pop_pick_clip (self); -+ } - } - - cogl_pop_matrix (); -@@ -4090,11 +4179,12 @@ clutter_actor_continue_paint (ClutterActor *self) - { - ClutterColor col = { 0, }; - -- _clutter_id_to_color (_clutter_actor_get_pick_id (self), &col); -- -- /* Actor will then paint silhouette of itself in supplied -- * color. See clutter_stage_get_actor_at_pos() for where -- * picking is enabled. -+ /* The actor will log a silhouette of itself to the stage pick log. -+ * Note that the picking color is no longer used as the "log" instead -+ * keeps a weak pointer to the actor itself. But we keep the color -+ * parameter for now so as to maintain ABI compatibility. The color -+ * parameter can be removed when someone feels like breaking the ABI -+ * along with gnome-shell. - * - * XXX:2.0 - Call the pick() virtual directly - */ -@@ -8602,8 +8692,6 @@ clutter_actor_init (ClutterActor *self) - - self->priv = priv = clutter_actor_get_instance_private (self); - -- priv->pick_id = -1; -- - priv->opacity = 0xff; - priv->show_on_set_parent = TRUE; - priv->resource_scale = -1.0f; -diff --git a/clutter/clutter/clutter-actor.h b/clutter/clutter/clutter-actor.h -index 7d2168af1..b1abff86d 100644 ---- a/clutter/clutter/clutter-actor.h -+++ b/clutter/clutter/clutter-actor.h -@@ -902,6 +902,10 @@ void clutter_actor_bind_model_with_properties - const char *first_model_property, - ...); - -+CLUTTER_EXPORT -+void clutter_actor_pick_box (ClutterActor *self, -+ const ClutterActorBox *box); -+ - G_END_DECLS - - #endif /* __CLUTTER_ACTOR_H__ */ -diff --git a/clutter/clutter/clutter-debug.h b/clutter/clutter/clutter-debug.h -index 2462385f6..3744c648f 100644 ---- a/clutter/clutter/clutter-debug.h -+++ b/clutter/clutter/clutter-debug.h -@@ -29,8 +29,7 @@ typedef enum - - typedef enum - { -- CLUTTER_DEBUG_NOP_PICKING = 1 << 0, -- CLUTTER_DEBUG_DUMP_PICK_BUFFERS = 1 << 1 -+ CLUTTER_DEBUG_NOP_PICKING = 1 << 0 - } ClutterPickDebugFlag; - - typedef enum -diff --git a/clutter/clutter/clutter-main.c b/clutter/clutter/clutter-main.c -index 71ec0d80c..c2ec82697 100644 ---- a/clutter/clutter/clutter-main.c -+++ b/clutter/clutter/clutter-main.c -@@ -130,7 +130,6 @@ static const GDebugKey clutter_debug_keys[] = { - - static const GDebugKey clutter_pick_debug_keys[] = { - { "nop-picking", CLUTTER_DEBUG_NOP_PICKING }, -- { "dump-pick-buffers", CLUTTER_DEBUG_DUMP_PICK_BUFFERS }, - }; - - static const GDebugKey clutter_paint_debug_keys[] = { -@@ -532,125 +531,6 @@ clutter_get_motion_events_enabled (void) - return _clutter_context_get_motion_events_enabled (); - } - --void --_clutter_id_to_color (guint id_, -- ClutterColor *col) --{ -- ClutterMainContext *ctx; -- gint red, green, blue; -- -- ctx = _clutter_context_get_default (); -- -- if (ctx->fb_g_mask == 0) -- { -- /* Figure out framebuffer masks used for pick */ -- cogl_get_bitmasks (&ctx->fb_r_mask, -- &ctx->fb_g_mask, -- &ctx->fb_b_mask, NULL); -- -- ctx->fb_r_mask_used = ctx->fb_r_mask; -- ctx->fb_g_mask_used = ctx->fb_g_mask; -- ctx->fb_b_mask_used = ctx->fb_b_mask; -- -- /* XXX - describe what "fuzzy picking" is */ -- if (clutter_use_fuzzy_picking) -- { -- ctx->fb_r_mask_used--; -- ctx->fb_g_mask_used--; -- ctx->fb_b_mask_used--; -- } -- } -- -- /* compute the numbers we'll store in the components */ -- red = (id_ >> (ctx->fb_g_mask_used+ctx->fb_b_mask_used)) -- & (0xff >> (8-ctx->fb_r_mask_used)); -- green = (id_ >> ctx->fb_b_mask_used) -- & (0xff >> (8-ctx->fb_g_mask_used)); -- blue = (id_) -- & (0xff >> (8-ctx->fb_b_mask_used)); -- -- /* shift left bits a bit and add one, this circumvents -- * at least some potential rounding errors in GL/GLES -- * driver / hw implementation. -- */ -- if (ctx->fb_r_mask_used != ctx->fb_r_mask) -- red = red * 2; -- if (ctx->fb_g_mask_used != ctx->fb_g_mask) -- green = green * 2; -- if (ctx->fb_b_mask_used != ctx->fb_b_mask) -- blue = blue * 2; -- -- /* shift up to be full 8bit values */ -- red = (red << (8 - ctx->fb_r_mask)) | (0x7f >> (ctx->fb_r_mask_used)); -- green = (green << (8 - ctx->fb_g_mask)) | (0x7f >> (ctx->fb_g_mask_used)); -- blue = (blue << (8 - ctx->fb_b_mask)) | (0x7f >> (ctx->fb_b_mask_used)); -- -- col->red = red; -- col->green = green; -- col->blue = blue; -- col->alpha = 0xff; -- -- /* XXX: We rotate the nibbles of the colors here so that there is a -- * visible variation between colors of sequential actor identifiers; -- * otherwise pick buffers dumped to an image will pretty much just look -- * black. -- */ -- if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)) -- { -- col->red = (col->red << 4) | (col->red >> 4); -- col->green = (col->green << 4) | (col->green >> 4); -- col->blue = (col->blue << 4) | (col->blue >> 4); -- } --} -- --guint --_clutter_pixel_to_id (guchar pixel[4]) --{ -- ClutterMainContext *ctx; -- gint red, green, blue; -- guint retval; -- -- ctx = _clutter_context_get_default (); -- -- /* reduce the pixel components to the number of bits actually used of the -- * 8bits. -- */ -- if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)) -- { -- guchar tmp; -- -- /* XXX: In _clutter_id_to_color we rotated the nibbles of the colors so -- * that there is a visible variation between colors of sequential actor -- * identifiers (otherwise pick buffers dumped to an image will pretty -- * much just look black.) Here we reverse that rotation. -- */ -- tmp = ((pixel[0] << 4) | (pixel[0] >> 4)); -- red = tmp >> (8 - ctx->fb_r_mask); -- tmp = ((pixel[1] << 4) | (pixel[1] >> 4)); -- green = tmp >> (8 - ctx->fb_g_mask); -- tmp = ((pixel[2] << 4) | (pixel[2] >> 4)); -- blue = tmp >> (8 - ctx->fb_b_mask); -- } -- else -- { -- red = pixel[0] >> (8 - ctx->fb_r_mask); -- green = pixel[1] >> (8 - ctx->fb_g_mask); -- blue = pixel[2] >> (8 - ctx->fb_b_mask); -- } -- -- /* divide potentially by two if 'fuzzy' */ -- red = red >> (ctx->fb_r_mask - ctx->fb_r_mask_used); -- green = green >> (ctx->fb_g_mask - ctx->fb_g_mask_used); -- blue = blue >> (ctx->fb_b_mask - ctx->fb_b_mask_used); -- -- /* combine the correct per component values into the final id */ -- retval = blue -- + (green << ctx->fb_b_mask_used) -- + (red << (ctx->fb_b_mask_used + ctx->fb_g_mask_used)); -- -- return retval; --} -- - static CoglPangoFontMap * - clutter_context_get_pango_fontmap (void) - { -diff --git a/clutter/clutter/clutter-private.h b/clutter/clutter/clutter-private.h -index a5cd1fa19..47bef4ac2 100644 ---- a/clutter/clutter/clutter-private.h -+++ b/clutter/clutter/clutter-private.h -@@ -210,11 +210,6 @@ gboolean _clutter_feature_init (GError **error); - gboolean _clutter_diagnostic_enabled (void); - void _clutter_diagnostic_message (const char *fmt, ...) G_GNUC_PRINTF (1, 2); - --/* Picking code */ --guint _clutter_pixel_to_id (guchar pixel[4]); --void _clutter_id_to_color (guint id, -- ClutterColor *col); -- - void _clutter_set_sync_to_vblank (gboolean sync_to_vblank); - - /* use this function as the accumulator if you have a signal with -diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h -index 4799c29e1..f8591ddcb 100644 ---- a/clutter/clutter/clutter-stage-private.h -+++ b/clutter/clutter/clutter-stage-private.h -@@ -74,6 +74,15 @@ gint64 _clutter_stage_get_update_time (ClutterStage *stage); - void _clutter_stage_clear_update_time (ClutterStage *stage); - gboolean _clutter_stage_has_full_redraw_queued (ClutterStage *stage); - -+void _clutter_stage_log_pick (ClutterStage *stage, -+ const ClutterPoint vertices[4], -+ ClutterActor *actor); -+ -+void _clutter_stage_push_pick_clip (ClutterStage *stage, -+ const ClutterPoint vertices[4]); -+ -+void _clutter_stage_pop_pick_clip (ClutterStage *stage); -+ - ClutterActor *_clutter_stage_do_pick (ClutterStage *stage, - gint x, - gint y, -@@ -92,13 +101,6 @@ void _clutter_stage_queue_redraw_entry_invalidate (Clut - - CoglFramebuffer *_clutter_stage_get_active_framebuffer (ClutterStage *stage); - --gint32 _clutter_stage_acquire_pick_id (ClutterStage *stage, -- ClutterActor *actor); --void _clutter_stage_release_pick_id (ClutterStage *stage, -- gint32 pick_id); --ClutterActor * _clutter_stage_get_actor_by_pick_id (ClutterStage *stage, -- gint32 pick_id); -- - void _clutter_stage_add_pointer_drag_actor (ClutterStage *stage, - ClutterInputDevice *device, - ClutterActor *actor); -diff --git a/clutter/clutter/clutter-stage-window.c b/clutter/clutter/clutter-stage-window.c -index e8fa976a7..4c4ef9d64 100644 ---- a/clutter/clutter/clutter-stage-window.c -+++ b/clutter/clutter/clutter-stage-window.c -@@ -293,24 +293,6 @@ _clutter_stage_window_redraw (ClutterStageWindow *window) - iface->redraw (window); - } - -- --void --_clutter_stage_window_get_dirty_pixel (ClutterStageWindow *window, -- ClutterStageView *view, -- int *x, int *y) --{ -- ClutterStageWindowInterface *iface; -- -- *x = 0; -- *y = 0; -- -- g_return_if_fail (CLUTTER_IS_STAGE_WINDOW (window)); -- -- iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window); -- if (iface->get_dirty_pixel) -- iface->get_dirty_pixel (window, view, x, y); --} -- - gboolean - _clutter_stage_window_can_clip_redraws (ClutterStageWindow *window) - { -diff --git a/clutter/clutter/clutter-stage-window.h b/clutter/clutter/clutter-stage-window.h -index 6c3601745..aa0c5f71c 100644 ---- a/clutter/clutter/clutter-stage-window.h -+++ b/clutter/clutter/clutter-stage-window.h -@@ -68,10 +68,6 @@ struct _ClutterStageWindowInterface - - void (* redraw) (ClutterStageWindow *stage_window); - -- void (* get_dirty_pixel) (ClutterStageWindow *stage_window, -- ClutterStageView *view, -- int *x, int *y); -- - gboolean (* can_clip_redraws) (ClutterStageWindow *stage_window); - - GList *(* get_views) (ClutterStageWindow *stage_window); -@@ -119,10 +115,6 @@ void _clutter_stage_window_set_accept_focus (ClutterStageWin - - void _clutter_stage_window_redraw (ClutterStageWindow *window); - --void _clutter_stage_window_get_dirty_pixel (ClutterStageWindow *window, -- ClutterStageView *view, -- int *x, int *y); -- - gboolean _clutter_stage_window_can_clip_redraws (ClutterStageWindow *window); - - GList * _clutter_stage_window_get_views (ClutterStageWindow *window); -diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c -index 1eea5b305..bd0ac9ed2 100644 ---- a/clutter/clutter/clutter-stage.c -+++ b/clutter/clutter/clutter-stage.c -@@ -103,6 +103,23 @@ struct _ClutterStageQueueRedrawEntry - ClutterPaintVolume clip; - }; - -+struct _PickRecord -+{ -+ ClutterPoint vertex[4]; -+ ClutterActor *actor; -+ int clip_stack_top; -+}; -+ -+typedef struct _PickRecord PickRecord; -+ -+struct _PickClipRecord -+{ -+ int prev; -+ ClutterPoint vertex[4]; -+}; -+ -+typedef struct _PickClipRecord PickClipRecord; -+ - struct _ClutterStagePrivate - { - /* the stage implementation */ -@@ -136,7 +153,11 @@ struct _ClutterStagePrivate - GTimer *fps_timer; - gint32 timer_n_frames; - -- ClutterIDPool *pick_id_pool; -+ GArray *pick_stack; -+ GArray *pick_clip_stack; -+ int pick_clip_stack_top; -+ gboolean pick_stack_frozen; -+ ClutterPickMode cached_pick_mode; - - #ifdef CLUTTER_ENABLE_DEBUG - gulong redraw_count; -@@ -322,6 +343,177 @@ clutter_stage_get_preferred_height (ClutterActor *self, - *natural_height_p = geom.height; - } - -+/* In order to keep weak pointers valid between frames we need them to not -+ * move in memory, so the stack is marked as "frozen". -+ */ -+static void -+_clutter_stage_freeze_pick_stack (ClutterStage *stage) -+{ -+ ClutterStagePrivate *priv = stage->priv; -+ int i; -+ -+ if (priv->pick_stack_frozen) -+ return; -+ -+ for (i = 0; i < priv->pick_stack->len; i++) -+ { -+ PickRecord *rec = &g_array_index (priv->pick_stack, PickRecord, i); -+ -+ if (rec->actor) -+ g_object_add_weak_pointer (G_OBJECT (rec->actor), -+ (gpointer) &rec->actor); -+ } -+ -+ priv->pick_stack_frozen = TRUE; -+} -+ -+static void -+_clutter_stage_thaw_pick_stack (ClutterStage *stage) -+{ -+ ClutterStagePrivate *priv = stage->priv; -+ int i; -+ -+ if (!priv->pick_stack_frozen) -+ return; -+ -+ for (i = 0; i < priv->pick_stack->len; i++) -+ { -+ PickRecord *rec = &g_array_index (priv->pick_stack, PickRecord, i); -+ -+ if (rec->actor) -+ g_object_remove_weak_pointer (G_OBJECT (rec->actor), -+ (gpointer) &rec->actor); -+ } -+ -+ priv->pick_stack_frozen = FALSE; -+} -+ -+static void -+_clutter_stage_clear_pick_stack (ClutterStage *stage) -+{ -+ ClutterStagePrivate *priv = stage->priv; -+ -+ _clutter_stage_thaw_pick_stack (stage); -+ g_array_set_size (priv->pick_stack, 0); -+ g_array_set_size (priv->pick_clip_stack, 0); -+ priv->pick_clip_stack_top = -1; -+ priv->cached_pick_mode = CLUTTER_PICK_NONE; -+} -+ -+void -+_clutter_stage_log_pick (ClutterStage *stage, -+ const ClutterPoint vertices[4], -+ ClutterActor *actor) -+{ -+ ClutterStagePrivate *priv; -+ PickRecord rec; -+ -+ g_return_if_fail (CLUTTER_IS_STAGE (stage)); -+ g_return_if_fail (actor != NULL); -+ -+ priv = stage->priv; -+ -+ g_assert (!priv->pick_stack_frozen); -+ -+ memcpy (rec.vertex, vertices, 4 * sizeof (ClutterPoint)); -+ rec.actor = actor; -+ rec.clip_stack_top = priv->pick_clip_stack_top; -+ -+ g_array_append_val (priv->pick_stack, rec); -+} -+ -+void -+_clutter_stage_push_pick_clip (ClutterStage *stage, -+ const ClutterPoint vertices[4]) -+{ -+ ClutterStagePrivate *priv; -+ PickClipRecord clip; -+ -+ g_return_if_fail (CLUTTER_IS_STAGE (stage)); -+ -+ priv = stage->priv; -+ -+ g_assert (!priv->pick_stack_frozen); -+ -+ clip.prev = priv->pick_clip_stack_top; -+ memcpy (clip.vertex, vertices, 4 * sizeof (ClutterPoint)); -+ -+ g_array_append_val (priv->pick_clip_stack, clip); -+ priv->pick_clip_stack_top = priv->pick_clip_stack->len - 1; -+} -+ -+void -+_clutter_stage_pop_pick_clip (ClutterStage *stage) -+{ -+ ClutterStagePrivate *priv; -+ const PickClipRecord *top; -+ -+ g_return_if_fail (CLUTTER_IS_STAGE (stage)); -+ -+ priv = stage->priv; -+ -+ g_assert (!priv->pick_stack_frozen); -+ g_assert (priv->pick_clip_stack_top >= 0); -+ -+ /* Individual elements of pick_clip_stack are not freed. This is so they -+ * can be shared as part of a tree of different stacks used by different -+ * actors in the pick_stack. The whole pick_clip_stack does however get -+ * freed later in _clutter_stage_clear_pick_stack. -+ */ -+ -+ top = &g_array_index (priv->pick_clip_stack, -+ PickClipRecord, -+ priv->pick_clip_stack_top); -+ -+ priv->pick_clip_stack_top = top->prev; -+} -+ -+static gboolean -+pick_record_contains_pixel (ClutterStage *stage, -+ const PickRecord *rec, -+ int x, -+ int y) -+{ -+ ClutterStagePrivate *priv; -+ int clip_index; -+ -+ /* We test the centre point of the pixel so that we can use -+ * clutter_point_inside_polygon and avoid any ambiguity about the boundaries -+ * of pixel-aligned actors. If we were to use point (x,y) instead (which is -+ * the top-left corner of the pixel) then we'd need to use -+ * clutter_point_touches_polygon. -+ */ -+ const ClutterPoint point = { x + 0.5f, y + 0.5f }; -+ -+ if (!clutter_point_inside_polygon (&point, rec->vertex, 4)) -+ return FALSE; -+ -+ priv = stage->priv; -+ -+ /* pick_clip_stack is actually a graph containing one or more trees. And the -+ * clip_stack_top for a given PickRecord is the path from a leaf of such a -+ * tree to its root. It's still a stack though, from the point of view of the -+ * code that built it and the code that traverses it here. -+ * -+ * Allowing multiple stacks to overlap and merge into trees saves us time and -+ * memory. -+ */ -+ clip_index = rec->clip_stack_top; -+ while (clip_index >= 0) -+ { -+ const PickClipRecord *clip = &g_array_index (priv->pick_clip_stack, -+ PickClipRecord, -+ clip_index); -+ -+ if (!clutter_point_inside_polygon (&point, clip->vertex, 4)) -+ return FALSE; -+ -+ clip_index = clip->prev; -+ } -+ -+ return TRUE; -+} -+ - static inline void - queue_full_redraw (ClutterStage *stage) - { -@@ -632,6 +824,12 @@ clutter_stage_do_paint_view (ClutterStage *stage, - float viewport[4]; - cairo_rectangle_int_t geom; - -+ /* Any mode of painting/picking invalidates the pick cache, unless we're -+ * in the middle of building it. So we reset the cached flag but don't -+ * completely clear the pick stack... -+ */ -+ priv->cached_pick_mode = CLUTTER_PICK_NONE; -+ - _clutter_stage_window_get_geometry (priv->impl, &geom); - - viewport[0] = priv->viewport[0]; -@@ -1397,40 +1595,6 @@ clutter_stage_get_redraw_clip_bounds (ClutterStage *stage, - } - } - --static void --read_pixels_to_file (CoglFramebuffer *fb, -- char *filename_stem, -- int x, -- int y, -- int width, -- int height) --{ -- guint8 *data; -- cairo_surface_t *surface; -- static int read_count = 0; -- char *filename = g_strdup_printf ("%s-%05d.png", -- filename_stem, -- read_count); -- -- data = g_malloc (4 * width * height); -- cogl_framebuffer_read_pixels (fb, -- x, y, width, height, -- CLUTTER_CAIRO_FORMAT_ARGB32, -- data); -- -- surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_RGB24, -- width, height, -- width * 4); -- -- cairo_surface_write_to_png (surface, filename); -- cairo_surface_destroy (surface); -- -- g_free (data); -- g_free (filename); -- -- read_count++; --} -- - static ClutterActor * - _clutter_stage_do_pick_on_view (ClutterStage *stage, - gint x, -@@ -1438,140 +1602,47 @@ _clutter_stage_do_pick_on_view (ClutterStage *stage, - ClutterPickMode mode, - ClutterStageView *view) - { -- ClutterActor *actor = CLUTTER_ACTOR (stage); -+ ClutterMainContext *context = _clutter_context_get_default (); -+ int i; - ClutterStagePrivate *priv = stage->priv; - CoglFramebuffer *fb = clutter_stage_view_get_framebuffer (view); -- cairo_rectangle_int_t view_layout; -- ClutterMainContext *context; -- guchar pixel[4] = { 0xff, 0xff, 0xff, 0xff }; -- CoglColor stage_pick_id; -- gboolean dither_enabled_save; -- ClutterActor *retval; -- gint dirty_x; -- gint dirty_y; -- gint read_x; -- gint read_y; -- float fb_width, fb_height; -- float fb_scale; -- float viewport_offset_x; -- float viewport_offset_y; -- -- priv = stage->priv; -- -- context = _clutter_context_get_default (); -- fb_scale = clutter_stage_view_get_scale (view); -- clutter_stage_view_get_layout (view, &view_layout); -- -- fb_width = view_layout.width * fb_scale; -- fb_height = view_layout.height * fb_scale; -- cogl_push_framebuffer (fb); - -- /* needed for when a context switch happens */ -- _clutter_stage_maybe_setup_viewport (stage, view); -- -- /* FIXME: For some reason leaving the cogl clip stack empty causes the -- * picking to not work at all, so setting it the whole framebuffer content -- * for now. */ -- cogl_framebuffer_push_scissor_clip (fb, 0, 0, -- view_layout.width * fb_scale, -- view_layout.height * fb_scale); -- -- _clutter_stage_window_get_dirty_pixel (priv->impl, view, &dirty_x, &dirty_y); -- -- if (G_LIKELY (!(clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))) -- { -- CLUTTER_NOTE (PICK, "Pushing pick scissor clip x: %d, y: %d, 1x1", -- (int) (dirty_x * fb_scale), -- (int) (dirty_y * fb_scale)); -- cogl_framebuffer_push_scissor_clip (fb, dirty_x * fb_scale, dirty_y * fb_scale, 1, 1); -- } -+ /* We are not reentrant right now. If this proves to be a problem then we -+ * would need to keep a stack of pick_stack's, or something... :( -+ */ -+ g_assert (context->pick_mode == CLUTTER_PICK_NONE); - -- viewport_offset_x = x * fb_scale - dirty_x * fb_scale; -- viewport_offset_y = y * fb_scale - dirty_y * fb_scale; -- CLUTTER_NOTE (PICK, "Setting viewport to %f, %f, %f, %f", -- priv->viewport[0] * fb_scale - viewport_offset_x, -- priv->viewport[1] * fb_scale - viewport_offset_y, -- priv->viewport[2] * fb_scale, -- priv->viewport[3] * fb_scale); -- cogl_framebuffer_set_viewport (fb, -- priv->viewport[0] * fb_scale - viewport_offset_x, -- priv->viewport[1] * fb_scale - viewport_offset_y, -- priv->viewport[2] * fb_scale, -- priv->viewport[3] * fb_scale); -- -- read_x = dirty_x * fb_scale; -- read_y = dirty_y * fb_scale; -- -- CLUTTER_NOTE (PICK, "Performing pick at %i,%i on view %dx%d+%d+%d s: %f", -- x, y, -- view_layout.width, view_layout.height, -- view_layout.x, view_layout.y, fb_scale); -- -- cogl_color_init_from_4ub (&stage_pick_id, 255, 255, 255, 255); -- cogl_framebuffer_clear (fb, COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_DEPTH, &stage_pick_id); -- -- /* Disable dithering (if any) when doing the painting in pick mode */ -- dither_enabled_save = cogl_framebuffer_get_dither_enabled (fb); -- cogl_framebuffer_set_dither_enabled (fb, FALSE); -- -- /* Render the entire scence in pick mode - just single colored silhouette's -- * are drawn offscreen (as we never swap buffers) -- */ -- context->pick_mode = mode; -- -- clutter_stage_do_paint_view (stage, view, NULL); -- context->pick_mode = CLUTTER_PICK_NONE; -- -- /* Read the color of the screen co-ords pixel. RGBA_8888_PRE is used -- even though we don't care about the alpha component because under -- GLES this is the only format that is guaranteed to work so Cogl -- will end up having to do a conversion if any other format is -- used. The format is requested as pre-multiplied because Cogl -- assumes that all pixels in the framebuffer are premultiplied so -- it avoids a conversion. */ -- cogl_framebuffer_read_pixels (fb, -- read_x, read_y, 1, 1, -- COGL_PIXEL_FORMAT_RGBA_8888_PRE, -- pixel); -- -- if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)) -+ if (mode != priv->cached_pick_mode) - { -- char *file_name = -- g_strdup_printf ("pick-buffer-%s-view-x-%d", -- _clutter_actor_get_debug_name (actor), -- view_layout.x); -- -- read_pixels_to_file (fb, file_name, 0, 0, fb_width, fb_height); -+ _clutter_stage_clear_pick_stack (stage); - -- g_free (file_name); -+ /* We don't render to the fb, but have to set one to stop the cogl matrix -+ * operations from crashing in paint functions. Because the matrices are -+ * stored relative to the current fb. -+ */ -+ cogl_push_framebuffer (fb); -+ context->pick_mode = mode; -+ clutter_stage_do_paint_view (stage, view, NULL); -+ context->pick_mode = CLUTTER_PICK_NONE; -+ priv->cached_pick_mode = mode; -+ cogl_pop_framebuffer (); -+ -+ _clutter_stage_freeze_pick_stack (stage); - } - -- /* Restore whether GL_DITHER was enabled */ -- cogl_framebuffer_set_dither_enabled (fb, dither_enabled_save); -- -- if (G_LIKELY (!(clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))) -- cogl_framebuffer_pop_clip (fb); -- -- cogl_framebuffer_pop_clip (fb); -- -- _clutter_stage_dirty_viewport (stage); -- -- if (pixel[0] == 0xff && pixel[1] == 0xff && pixel[2] == 0xff) -- retval = actor; -- else -+ /* Search all "painted" pickable actors from front to back. A linear search -+ * is required, and also performs fine since there is typically only -+ * on the order of dozens of actors in the list (on screen) at a time. -+ */ -+ for (i = priv->pick_stack->len - 1; i >= 0; i--) - { -- guint32 id_ = _clutter_pixel_to_id (pixel); -+ const PickRecord *rec = &g_array_index (priv->pick_stack, PickRecord, i); - -- retval = _clutter_stage_get_actor_by_pick_id (stage, id_); -- CLUTTER_NOTE (PICK, "Picking actor %s with id %u (pixel: 0x%x%x%x%x", -- G_OBJECT_TYPE_NAME (retval), -- id_, -- pixel[0], pixel[1], pixel[2], pixel[3]); -+ if (rec->actor && pick_record_contains_pixel (stage, rec, x, y)) -+ return rec->actor; - } - -- cogl_pop_framebuffer (); -- -- return retval; -+ return CLUTTER_ACTOR (stage); - } - - static ClutterStageView * -@@ -1884,7 +1955,9 @@ clutter_stage_finalize (GObject *object) - - g_array_free (priv->paint_volume_stack, TRUE); - -- _clutter_id_pool_free (priv->pick_id_pool); -+ _clutter_stage_clear_pick_stack (stage); -+ g_array_free (priv->pick_clip_stack, TRUE); -+ g_array_free (priv->pick_stack, TRUE); - - if (priv->fps_timer != NULL) - g_timer_destroy (priv->fps_timer); -@@ -2384,7 +2457,10 @@ clutter_stage_init (ClutterStage *self) - priv->paint_volume_stack = - g_array_new (FALSE, FALSE, sizeof (ClutterPaintVolume)); - -- priv->pick_id_pool = _clutter_id_pool_new (256); -+ priv->pick_stack = g_array_new (FALSE, FALSE, sizeof (PickRecord)); -+ priv->pick_clip_stack = g_array_new (FALSE, FALSE, sizeof (PickClipRecord)); -+ priv->pick_clip_stack_top = -1; -+ priv->cached_pick_mode = CLUTTER_PICK_NONE; - } - - /** -@@ -4208,6 +4284,12 @@ _clutter_stage_queue_actor_redraw (ClutterStage *stage, - CLUTTER_NOTE (CLIPPING, "stage_queue_actor_redraw (actor=%s, clip=%p): ", - _clutter_actor_get_debug_name (actor), clip); - -+ /* Queuing a redraw or clip change invalidates the pick cache, unless we're -+ * in the middle of building it. So we reset the cached flag but don't -+ * completely clear the pick stack... -+ */ -+ priv->cached_pick_mode = CLUTTER_PICK_NONE; -+ - if (!priv->redraw_pending) - { - ClutterMasterClock *master_clock; -@@ -4468,39 +4550,6 @@ _clutter_stage_get_active_framebuffer (ClutterStage *stage) - return stage->priv->active_framebuffer; - } - --gint32 --_clutter_stage_acquire_pick_id (ClutterStage *stage, -- ClutterActor *actor) --{ -- ClutterStagePrivate *priv = stage->priv; -- -- g_assert (priv->pick_id_pool != NULL); -- -- return _clutter_id_pool_add (priv->pick_id_pool, actor); --} -- --void --_clutter_stage_release_pick_id (ClutterStage *stage, -- gint32 pick_id) --{ -- ClutterStagePrivate *priv = stage->priv; -- -- g_assert (priv->pick_id_pool != NULL); -- -- _clutter_id_pool_remove (priv->pick_id_pool, pick_id); --} -- --ClutterActor * --_clutter_stage_get_actor_by_pick_id (ClutterStage *stage, -- gint32 pick_id) --{ -- ClutterStagePrivate *priv = stage->priv; -- -- g_assert (priv->pick_id_pool != NULL); -- -- return _clutter_id_pool_lookup (priv->pick_id_pool, pick_id); --} -- - void - _clutter_stage_add_pointer_drag_actor (ClutterStage *stage, - ClutterInputDevice *device, -diff --git a/clutter/clutter/cogl/clutter-stage-cogl.c b/clutter/clutter/cogl/clutter-stage-cogl.c -index 4f8d6a650..f957d9840 100644 ---- a/clutter/clutter/cogl/clutter-stage-cogl.c -+++ b/clutter/clutter/cogl/clutter-stage-cogl.c -@@ -983,55 +983,6 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window) - stage_cogl->frame_count++; - } - --static void --clutter_stage_cogl_get_dirty_pixel (ClutterStageWindow *stage_window, -- ClutterStageView *view, -- int *x, -- int *y) --{ -- CoglFramebuffer *framebuffer = clutter_stage_view_get_framebuffer (view); -- gboolean has_buffer_age = -- cogl_is_onscreen (framebuffer) && -- is_buffer_age_enabled (); -- float fb_scale; -- gboolean scale_is_fractional; -- -- fb_scale = clutter_stage_view_get_scale (view); -- if (fb_scale != floorf (fb_scale)) -- scale_is_fractional = TRUE; -- else -- scale_is_fractional = FALSE; -- -- /* -- * Buffer damage is tracked in the framebuffer coordinate space -- * using the damage history. When fractional scaling is used, a -- * coordinate on the stage might not correspond to the exact position of any -- * physical pixel, which causes issues when painting using the pick mode. -- * -- * For now, always use the (0, 0) pixel for picking when using fractional -- * framebuffer scaling. -- */ -- if (!has_buffer_age || scale_is_fractional) -- { -- *x = 0; -- *y = 0; -- } -- else -- { -- ClutterStageViewCogl *view_cogl = CLUTTER_STAGE_VIEW_COGL (view); -- ClutterStageViewCoglPrivate *view_priv = -- clutter_stage_view_cogl_get_instance_private (view_cogl); -- cairo_rectangle_int_t view_layout; -- cairo_rectangle_int_t *fb_damage; -- -- clutter_stage_view_get_layout (view, &view_layout); -- -- fb_damage = &view_priv->damage_history[DAMAGE_HISTORY (view_priv->damage_index - 1)]; -- *x = fb_damage->x / fb_scale; -- *y = fb_damage->y / fb_scale; -- } --} -- - static void - clutter_stage_window_iface_init (ClutterStageWindowInterface *iface) - { -@@ -1049,7 +1000,6 @@ clutter_stage_window_iface_init (ClutterStageWindowInterface *iface) - iface->ignoring_redraw_clips = clutter_stage_cogl_ignoring_redraw_clips; - iface->get_redraw_clip_bounds = clutter_stage_cogl_get_redraw_clip_bounds; - iface->redraw = clutter_stage_cogl_redraw; -- iface->get_dirty_pixel = clutter_stage_cogl_get_dirty_pixel; - } - - static void -diff --git a/clutter/clutter/deprecated/clutter-texture.c b/clutter/clutter/deprecated/clutter-texture.c -index bea239f45..2c677b8a4 100644 ---- a/clutter/clutter/deprecated/clutter-texture.c -+++ b/clutter/clutter/deprecated/clutter-texture.c -@@ -572,83 +572,6 @@ gen_texcoords_and_draw_cogl_rectangle (ClutterActor *self, - 0, 0, t_w, t_h); - } - --static CoglPipeline * --create_pick_pipeline (ClutterActor *self) --{ -- ClutterTexture *texture = CLUTTER_TEXTURE (self); -- ClutterTexturePrivate *priv = texture->priv; -- CoglPipeline *pick_pipeline = cogl_pipeline_copy (texture_template_pipeline); -- GError *error = NULL; -- -- if (!cogl_pipeline_set_layer_combine (pick_pipeline, 0, -- "RGBA = " -- " MODULATE (CONSTANT, TEXTURE[A])", -- &error)) -- { -- if (!priv->seen_create_pick_pipeline_warning) -- g_warning ("Error setting up texture combine for shaped " -- "texture picking: %s", error->message); -- priv->seen_create_pick_pipeline_warning = TRUE; -- g_error_free (error); -- cogl_object_unref (pick_pipeline); -- return NULL; -- } -- -- cogl_pipeline_set_blend (pick_pipeline, -- "RGBA = ADD (SRC_COLOR[RGBA], 0)", -- NULL); -- -- cogl_pipeline_set_alpha_test_function (pick_pipeline, -- COGL_PIPELINE_ALPHA_FUNC_EQUAL, -- 1.0); -- -- return pick_pipeline; --} -- --static void --clutter_texture_pick (ClutterActor *self, -- const ClutterColor *color) --{ -- ClutterTexture *texture = CLUTTER_TEXTURE (self); -- ClutterTexturePrivate *priv = texture->priv; -- CoglFramebuffer *framebuffer = cogl_get_draw_framebuffer (); -- -- if (!clutter_actor_should_pick_paint (self)) -- return; -- -- if (G_LIKELY (priv->pick_with_alpha_supported) && priv->pick_with_alpha) -- { -- CoglColor pick_color; -- -- if (priv->pick_pipeline == NULL) -- priv->pick_pipeline = create_pick_pipeline (self); -- -- if (priv->pick_pipeline == NULL) -- { -- priv->pick_with_alpha_supported = FALSE; -- CLUTTER_ACTOR_CLASS (clutter_texture_parent_class)->pick (self, -- color); -- return; -- } -- -- if (priv->fbo_handle != NULL) -- update_fbo (self); -- -- cogl_color_init_from_4ub (&pick_color, -- color->red, -- color->green, -- color->blue, -- 0xff); -- cogl_pipeline_set_layer_combine_constant (priv->pick_pipeline, -- 0, &pick_color); -- cogl_pipeline_set_layer_texture (priv->pick_pipeline, 0, -- clutter_texture_get_cogl_texture (texture)); -- gen_texcoords_and_draw_cogl_rectangle (self, priv->pick_pipeline, framebuffer); -- } -- else -- CLUTTER_ACTOR_CLASS (clutter_texture_parent_class)->pick (self, color); --} -- - static void - clutter_texture_paint (ClutterActor *self) - { -@@ -767,12 +690,6 @@ clutter_texture_dispose (GObject *object) - priv->pipeline = NULL; - } - -- if (priv->pick_pipeline != NULL) -- { -- cogl_object_unref (priv->pick_pipeline); -- priv->pick_pipeline = NULL; -- } -- - G_OBJECT_CLASS (clutter_texture_parent_class)->dispose (object); - } - -@@ -944,7 +861,6 @@ clutter_texture_class_init (ClutterTextureClass *klass) - GParamSpec *pspec; - - actor_class->paint = clutter_texture_paint; -- actor_class->pick = clutter_texture_pick; - actor_class->get_paint_volume = clutter_texture_get_paint_volume; - actor_class->realize = clutter_texture_realize; - actor_class->unrealize = clutter_texture_unrealize; -@@ -1263,11 +1179,9 @@ clutter_texture_init (ClutterTexture *self) - priv->repeat_y = FALSE; - priv->sync_actor_size = TRUE; - priv->fbo_handle = NULL; -- priv->pick_pipeline = NULL; - priv->keep_aspect_ratio = FALSE; - priv->pick_with_alpha = FALSE; - priv->pick_with_alpha_supported = TRUE; -- priv->seen_create_pick_pipeline_warning = FALSE; - - if (G_UNLIKELY (texture_template_pipeline == NULL)) - { -@@ -3052,13 +2966,8 @@ clutter_texture_set_pick_with_alpha (ClutterTexture *texture, - if (priv->pick_with_alpha == pick_with_alpha) - return; - -- if (!pick_with_alpha && priv->pick_pipeline != NULL) -- { -- cogl_object_unref (priv->pick_pipeline); -- priv->pick_pipeline = NULL; -- } -+ g_assert (!pick_with_alpha); /* No longer supported */ - -- /* NB: the pick pipeline is created lazily when we first pick */ - priv->pick_with_alpha = pick_with_alpha; - - /* NB: actors are expected to call clutter_actor_queue_redraw when -diff --git a/clutter/tests/conform/actor-pick.c b/clutter/tests/conform/actor-pick.c -index 969b4920a..2bf5954c7 100644 ---- a/clutter/tests/conform/actor-pick.c -+++ b/clutter/tests/conform/actor-pick.c -@@ -5,7 +5,6 @@ - #define STAGE_HEIGHT 480 - #define ACTORS_X 12 - #define ACTORS_Y 16 --#define SHIFT_STEP STAGE_WIDTH / ACTORS_X - - typedef struct _State State; - -@@ -20,84 +19,11 @@ struct _State - gboolean pass; - }; - --struct _ShiftEffect --{ -- ClutterShaderEffect parent_instance; --}; -- --struct _ShiftEffectClass --{ -- ClutterShaderEffectClass parent_class; --}; -- --typedef struct _ShiftEffect ShiftEffect; --typedef struct _ShiftEffectClass ShiftEffectClass; -- --#define TYPE_SHIFT_EFFECT (shift_effect_get_type ()) -- --GType shift_effect_get_type (void); -- --G_DEFINE_TYPE (ShiftEffect, -- shift_effect, -- CLUTTER_TYPE_SHADER_EFFECT); -- --static void --shader_paint (ClutterEffect *effect, -- ClutterEffectPaintFlags flags) --{ -- ClutterShaderEffect *shader = CLUTTER_SHADER_EFFECT (effect); -- float tex_width; -- ClutterActor *actor = -- clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect)); -- -- if (g_test_verbose ()) -- g_debug ("shader_paint"); -- -- clutter_shader_effect_set_shader_source (shader, -- "uniform sampler2D tex;\n" -- "uniform float step;\n" -- "void main (void)\n" -- "{\n" -- " cogl_color_out = texture2D(tex, vec2 (cogl_tex_coord_in[0].s + step,\n" -- " cogl_tex_coord_in[0].t));\n" -- "}\n"); -- -- tex_width = clutter_actor_get_width (actor); -- -- clutter_shader_effect_set_uniform (shader, "tex", G_TYPE_INT, 1, 0); -- clutter_shader_effect_set_uniform (shader, "step", G_TYPE_FLOAT, 1, -- SHIFT_STEP / tex_width); -- -- CLUTTER_EFFECT_CLASS (shift_effect_parent_class)->paint (effect, flags); --} -- --static void --shader_pick (ClutterEffect *effect, -- ClutterEffectPaintFlags flags) --{ -- shader_paint (effect, flags); --} -- --static void --shift_effect_class_init (ShiftEffectClass *klass) --{ -- ClutterEffectClass *shader_class = CLUTTER_EFFECT_CLASS (klass); -- -- shader_class->paint = shader_paint; -- shader_class->pick = shader_pick; --} -- --static void --shift_effect_init (ShiftEffect *self) --{ --} -- - static const char *test_passes[] = { - "No covering actor", - "Invisible covering actor", - "Clipped covering actor", - "Blur effect", -- "Shift effect", - }; - - static gboolean -@@ -165,30 +91,10 @@ on_timeout (gpointer data) - if (g_test_verbose ()) - g_print ("With blur effect:\n"); - } -- else if (test_num == 4) -- { -- if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL)) -- continue; -- -- clutter_actor_hide (over_actor); -- clutter_actor_remove_effect_by_name (CLUTTER_ACTOR (state->stage), -- "blur"); -- -- clutter_actor_add_effect_with_name (CLUTTER_ACTOR (state->stage), -- "shift", -- g_object_new (TYPE_SHIFT_EFFECT, -- NULL)); -- -- if (g_test_verbose ()) -- g_print ("With shift effect:\n"); -- } - - for (y = 0; y < ACTORS_Y; y++) - { -- if (test_num == 4) -- x = 1; -- else -- x = 0; -+ x = 0; - - for (; x < ACTORS_X; x++) - { -@@ -198,9 +104,6 @@ on_timeout (gpointer data) - - pick_x = x * state->actor_width + state->actor_width / 2; - -- if (test_num == 4) -- pick_x -= SHIFT_STEP; -- - actor = - clutter_stage_get_actor_at_pos (CLUTTER_STAGE (state->stage), - CLUTTER_PICK_ALL, -diff --git a/clutter/tests/conform/meson.build b/clutter/tests/conform/meson.build -index 916f5c342..e747c9b79 100644 ---- a/clutter/tests/conform/meson.build -+++ b/clutter/tests/conform/meson.build -@@ -43,7 +43,6 @@ clutter_conform_tests_deprecated_tests = [ - 'behaviours', - 'group', - 'rectangle', -- 'texture', - ] - - clutter_conform_tests = [] -diff --git a/clutter/tests/conform/texture.c b/clutter/tests/conform/texture.c -deleted file mode 100644 -index 392fd5c47..000000000 ---- a/clutter/tests/conform/texture.c -+++ /dev/null -@@ -1,84 +0,0 @@ --#define CLUTTER_DISABLE_DEPRECATION_WARNINGS --#include <clutter/clutter.h> --#include <string.h> -- --static CoglHandle --make_texture (void) --{ -- guint32 *data = g_malloc (100 * 100 * 4); -- int x; -- int y; -- -- for (y = 0; y < 100; y ++) -- for (x = 0; x < 100; x++) -- { -- if (x < 50 && y < 50) -- data[y * 100 + x] = 0xff00ff00; -- else -- data[y * 100 + x] = 0xff00ffff; -- } -- return cogl_texture_new_from_data (100, -- 100, -- COGL_TEXTURE_NONE, -- COGL_PIXEL_FORMAT_ARGB_8888, -- COGL_PIXEL_FORMAT_ARGB_8888, -- 400, -- (guchar *)data); --} -- --static void --texture_pick_with_alpha (void) --{ -- ClutterTexture *tex = CLUTTER_TEXTURE (clutter_texture_new ()); -- ClutterStage *stage = CLUTTER_STAGE (clutter_test_get_stage ()); -- ClutterActor *actor; -- -- clutter_texture_set_cogl_texture (tex, make_texture ()); -- -- clutter_actor_add_child (CLUTTER_ACTOR (stage), CLUTTER_ACTOR (tex)); -- -- clutter_actor_show (CLUTTER_ACTOR (stage)); -- -- if (g_test_verbose ()) -- { -- g_print ("\nstage = %p\n", stage); -- g_print ("texture = %p\n\n", tex); -- } -- -- clutter_texture_set_pick_with_alpha (tex, TRUE); -- if (g_test_verbose ()) -- g_print ("Testing with pick-with-alpha enabled:\n"); -- -- /* This should fall through and hit the stage: */ -- actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 10, 10); -- if (g_test_verbose ()) -- g_print ("actor @ (10, 10) = %p\n", actor); -- g_assert (actor == CLUTTER_ACTOR (stage)); -- -- /* The rest should hit the texture */ -- actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 90, 10); -- if (g_test_verbose ()) -- g_print ("actor @ (90, 10) = %p\n", actor); -- g_assert (actor == CLUTTER_ACTOR (tex)); -- actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 90, 90); -- if (g_test_verbose ()) -- g_print ("actor @ (90, 90) = %p\n", actor); -- g_assert (actor == CLUTTER_ACTOR (tex)); -- actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 10, 90); -- if (g_test_verbose ()) -- g_print ("actor @ (10, 90) = %p\n", actor); -- g_assert (actor == CLUTTER_ACTOR (tex)); -- -- clutter_texture_set_pick_with_alpha (tex, FALSE); -- if (g_test_verbose ()) -- g_print ("Testing with pick-with-alpha disabled:\n"); -- -- actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 10, 10); -- if (g_test_verbose ()) -- g_print ("actor @ (10, 10) = %p\n", actor); -- g_assert (actor == CLUTTER_ACTOR (tex)); --} -- --CLUTTER_TEST_SUITE ( -- CLUTTER_TEST_UNIT ("/texture/pick-with-alpha", texture_pick_with_alpha) --) -diff --git a/src/compositor/meta-surface-actor.c b/src/compositor/meta-surface-actor.c -index ca4ca19a9..814199145 100644 ---- a/src/compositor/meta-surface-actor.c -+++ b/src/compositor/meta-surface-actor.c -@@ -70,38 +70,23 @@ meta_surface_actor_pick (ClutterActor *actor, - else - { - int n_rects; -- float *rectangles; - int i; -- CoglPipeline *pipeline; -- CoglContext *ctx; -- CoglFramebuffer *fb; -- CoglColor cogl_color; - - n_rects = cairo_region_num_rectangles (priv->input_region); -- rectangles = g_alloca (sizeof (float) * 4 * n_rects); - - for (i = 0; i < n_rects; i++) - { - cairo_rectangle_int_t rect; -- int pos = i * 4; -+ ClutterActorBox box; - - cairo_region_get_rectangle (priv->input_region, i, &rect); - -- rectangles[pos + 0] = rect.x; -- rectangles[pos + 1] = rect.y; -- rectangles[pos + 2] = rect.x + rect.width; -- rectangles[pos + 3] = rect.y + rect.height; -+ box.x1 = rect.x; -+ box.y1 = rect.y; -+ box.x2 = rect.x + rect.width; -+ box.y2 = rect.y + rect.height; -+ clutter_actor_pick_box (actor, &box); - } -- -- ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); -- fb = cogl_get_draw_framebuffer (); -- -- cogl_color_init_from_4ub (&cogl_color, color->red, color->green, color->blue, color->alpha); -- -- pipeline = cogl_pipeline_new (ctx); -- cogl_pipeline_set_color (pipeline, &cogl_color); -- cogl_framebuffer_draw_rectangles (fb, pipeline, rectangles, n_rects); -- cogl_object_unref (pipeline); - } - - clutter_actor_iter_init (&iter, actor); --- -2.23.0 - Modified: PKGBUILD =================================================================== --- PKGBUILD 2019-09-12 20:57:17 UTC (rev 362343) +++ PKGBUILD 2019-09-12 21:06:45 UTC (rev 362344) @@ -3,7 +3,7 @@ # Contributor: Michael Kanis <mkanis_at_gmx_dot_de> pkgname=mutter -pkgver=3.32.2+43+gb7f158811 +pkgver=3.34.0 pkgrel=1 pkgdesc="A window manager for GNOME" url="https://gitlab.gnome.org/GNOME/mutter" @@ -12,19 +12,13 @@ depends=(dconf gobject-introspection-runtime gsettings-desktop-schemas libcanberra startup-notification zenity libsm gnome-desktop upower libxkbcommon-x11 gnome-settings-daemon libgudev libinput pipewire xorg-server-xwayland) -makedepends=(gobject-introspection git egl-wayland meson xorg-server) +makedepends=(gobject-introspection git egl-wayland meson xorg-server sysprof) checkdepends=(xorg-server-xvfb) groups=(gnome) install=mutter.install -_commit=b7f158811934d8e4d9dd0be28ad8e1746ceac46c # gnome-3-32 -source=("git+https://gitlab.gnome.org/GNOME/mutter.git#commit=$_commit" - 0001-Remove-GLX-threaded-swap-wait.patch - 0001-Add-point-polygon-testing-API.patch - 0002-Geometric-OpenGL-less-picking.patch) -sha256sums=('SKIP' - '92c0dd3a1df455722c7bfb205eab7c72ee21055d64f397bea5a8332431f3fee7' - '12c69bdc0836b47d5d6eba0f5b0118035aa08330388a05fcee367025adb31f21' - 'a6643593071ab171866dd6fdb30e65826a753a134832a8e1ec05b2b515d599e5') +_commit=8cdcf529e9acd95e903f699acfbde7f75213ebec # tags/3.34.0^0 +source=("git+https://gitlab.gnome.org/GNOME/mutter.git#commit=$_commit") +sha256sums=('SKIP') pkgver() { cd $pkgname @@ -34,29 +28,8 @@ prepare() { cd $pkgname - # rt-scheduler feature (needs CAP_SYS_NICE) - git cherry-pick -n dae2c1d420ed272710ac55b7a00f6787e5c0e762 # !460 - - # high-priority EGL contexts (needs CAP_SYS_NICE) - git cherry-pick -n 3f29b47809a038b45528dfccd4089721c97965df # !454 - git cherry-pick -n 7df86fb24646f8a19a47e54b86424048ec08e715 # !454 - - # reduce lag - git cherry-pick -n 45244852acc214a7a9d01dc96896ad0c079b437c # !520 - git cherry-pick -n 4faeb12731b81ce617042973155cc4b5737e1133 # !281 - # reduce overhead moving cursor or windows - git cherry-pick -n 01e20a6ba9e0cfa22e864c01b3395ba9568b061a # !568 - git cherry-pick -n a20a0d7a4563366d2cd29c32a1b95a59121e7bf5 # !283 - git apply -3 ../0001-Remove-GLX-threaded-swap-wait.patch # !602 - git apply -3 ../0001-Add-point-polygon-testing-API.patch # !189 - git apply -3 ../0002-Geometric-OpenGL-less-picking.patch # !189 - - # fix background corruption on nvidia - git cherry-pick -n a5265365dd268e15a461a58000a10b122d0bccba # !600 - - # required to build gala - git cherry-pick -n bd7704f9e17e9554ad663386ef4fce1e16a56f08 # !640 + #git cherry-pick -n a20a0d7a4563366d2cd29c32a1b95a59121e7bf5 # !283 } build() {
