From c61eca6002854b81ad8a418ae82a074423cea80b Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 6 Jan 2012 14:10:06 +0200 Subject: [PATCH 01/30] compositor: implement a stack of surface transformations Having at most one transformation object attached to a surface is not enough anymore. If we have a surface that needs to be scaled to fullscreen, and then we have the zoom animation, we already need two transformations combined. Implement support for multiple transformations by adding a transformation list. The final transformation is the ordered composite of those in the list. To avoid traversing the list every single time, add a dirty flag, and cache the final transformation. The existing transformation users (only zoom) are converted. Note: surface drawing should honour all kinds of transformations, but not damage region code nor input event translating code take transformations into account, AFAICT. Therefore anything but translation will probably behave badly until they are fixed. Cc: Juan Zhao Signed-off-by: Pekka Paalanen --- src/compositor.c | 44 ++++++++++++++++++++++++++++++++++++++------ src/compositor.h | 12 +++++++++++- src/util.c | 9 ++++++--- 3 files changed, 55 insertions(+), 10 deletions(-) diff --git a/src/compositor.c b/src/compositor.c index 9eb643c4..0a7dea3e 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -205,7 +205,8 @@ weston_surface_create(struct weston_compositor *compositor, surface->buffer_destroy_listener.func = surface_handle_buffer_destroy; - surface->transform = NULL; + wl_list_init(&surface->transform.list); + surface->transform.dirty = 1; return surface; } @@ -504,7 +505,9 @@ transform_vertex(struct weston_surface *surface, t.f[2] = 0.0; t.f[3] = 1.0; - weston_matrix_transform(&surface->transform->matrix, &t); + weston_matrix_transform(&surface->transform.cached.matrix, &t); + + /* XXX: assumes last row of matrix is [0 0 * 1] */ r[ 0] = t.f[0]; r[ 1] = t.f[1]; @@ -538,6 +541,34 @@ texture_transformed_surface(struct weston_surface *es) return 1; } +static void +weston_surface_update_transform(struct weston_surface *surface) +{ + struct weston_matrix *matrix = &surface->transform.cached.matrix; + struct weston_matrix *inverse = &surface->transform.cached.inverse; + struct weston_transform *tform; + + if (!surface->transform.dirty) + return; + + surface->transform.dirty = 0; + + if (wl_list_empty(&surface->transform.list)) { + surface->transform.enabled = 0; + return; + } + + surface->transform.enabled = 1; + + weston_matrix_init(matrix); + wl_list_for_each(tform, &surface->transform.list, link) + weston_matrix_multiply(matrix, &tform->matrix); + + weston_matrix_init(inverse); + wl_list_for_each_reverse(tform, &surface->transform.list, link) + weston_matrix_multiply(inverse, &tform->inverse); +} + WL_EXPORT void weston_surface_draw(struct weston_surface *es, struct weston_output *output) { @@ -584,12 +615,13 @@ weston_surface_draw(struct weston_surface *es, struct weston_output *output) ec->current_alpha = es->alpha; } - if (es->transform == NULL) { - filter = GL_NEAREST; - n = texture_region(es, &repaint); - } else { + weston_surface_update_transform(es); + if (es->transform.enabled) { filter = GL_LINEAR; n = texture_transformed_surface(es); + } else { + filter = GL_NEAREST; + n = texture_region(es, &repaint); } glBindTexture(GL_TEXTURE_2D, es->texture); diff --git a/src/compositor.h b/src/compositor.h index fe7176a1..3ac65584 100644 --- a/src/compositor.h +++ b/src/compositor.h @@ -43,6 +43,8 @@ struct weston_vector { void weston_matrix_init(struct weston_matrix *matrix); void +weston_matrix_multiply(struct weston_matrix *m, const struct weston_matrix *n); +void weston_matrix_scale(struct weston_matrix *matrix, GLfloat x, GLfloat y, GLfloat z); void weston_matrix_translate(struct weston_matrix *matrix, @@ -53,6 +55,7 @@ weston_matrix_transform(struct weston_matrix *matrix, struct weston_vector *v); struct weston_transform { struct weston_matrix matrix; struct weston_matrix inverse; + struct wl_list link; }; struct weston_surface; @@ -234,13 +237,20 @@ struct weston_surface { int32_t pitch; struct wl_list link; struct wl_list buffer_link; - struct weston_transform *transform; struct weston_shader *shader; GLfloat color[4]; uint32_t alpha; uint32_t visual; int overlapped; + struct { + struct wl_list list; + int dirty; + + struct weston_transform cached; + int enabled; + } transform; + /* * Which output to vsync this surface to. * Used to determine, whether to send or queue frame events. diff --git a/src/util.c b/src/util.c index 99dc09e2..4faa8b4e 100644 --- a/src/util.c +++ b/src/util.c @@ -37,7 +37,7 @@ weston_matrix_init(struct weston_matrix *matrix) memcpy(matrix, &identity, sizeof identity); } -static void +WL_EXPORT void weston_matrix_multiply(struct weston_matrix *m, const struct weston_matrix *n) { struct weston_matrix tmp; @@ -162,7 +162,8 @@ weston_zoom_destroy(struct weston_zoom *zoom) { wl_list_remove(&zoom->animation.link); wl_list_remove(&zoom->listener.link); - zoom->surface->transform = NULL; + wl_list_remove(&zoom->transform.link); + zoom->surface->transform.dirty = 1; if (zoom->done) zoom->done(zoom, zoom->data); free(zoom); @@ -212,6 +213,8 @@ weston_zoom_frame(struct weston_animation *animation, weston_matrix_init(&zoom->transform.inverse); weston_matrix_scale(&zoom->transform.inverse, scale, scale, scale); + zoom->surface->transform.dirty = 1; + weston_compositor_damage_all(es->compositor); } @@ -230,7 +233,7 @@ weston_zoom_run(struct weston_surface *surface, GLfloat start, GLfloat stop, zoom->data = data; zoom->start = start; zoom->stop = stop; - surface->transform = &zoom->transform; + wl_list_insert(&surface->transform.list, &zoom->transform.link); weston_spring_init(&zoom->spring, 200.0, 0.0, 1.0); zoom->spring.friction = 700; zoom->spring.timestamp = weston_compositor_get_time(); From daaf01b3e104047326d072e8bd8c849537129530 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 10 Jan 2012 15:40:18 +0200 Subject: [PATCH 02/30] util: document matrices Add comments explaining the matrix storage and multiplication, so that no-one else needs to decipher them again. Signed-off-by: Pekka Paalanen --- src/util.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/util.c b/src/util.c index 4faa8b4e..8a1742a7 100644 --- a/src/util.c +++ b/src/util.c @@ -27,6 +27,14 @@ #include "compositor.h" +/* + * Matrices are stored in column-major order, that is the array indices are: + * 0 4 8 12 + * 1 5 9 13 + * 2 6 10 14 + * 3 7 11 15 + */ + WL_EXPORT void weston_matrix_init(struct weston_matrix *matrix) { @@ -37,6 +45,7 @@ weston_matrix_init(struct weston_matrix *matrix) memcpy(matrix, &identity, sizeof identity); } +/* m <- n * m, that is, m is multiplied on the LEFT. */ WL_EXPORT void weston_matrix_multiply(struct weston_matrix *m, const struct weston_matrix *n) { @@ -76,6 +85,7 @@ weston_matrix_scale(struct weston_matrix *matrix, GLfloat x, GLfloat y, GLfloat weston_matrix_multiply(matrix, &scale); } +/* v <- m * v */ WL_EXPORT void weston_matrix_transform(struct weston_matrix *matrix, struct weston_vector *v) { From 668ca37b19926e57b414497f3881f3939e804c0d Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 12 Jan 2012 14:30:47 +0200 Subject: [PATCH 03/30] compositor: move matrix code to separate files Move matrix code to separate files to allow writing unit tests for it. Signed-off-by: Pekka Paalanen --- src/Makefile.am | 2 + src/compositor.h | 20 +--------- src/matrix.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++ src/matrix.h | 46 +++++++++++++++++++++ src/util.c | 74 ---------------------------------- 5 files changed, 151 insertions(+), 93 deletions(-) create mode 100644 src/matrix.c create mode 100644 src/matrix.h diff --git a/src/Makefile.am b/src/Makefile.am index cea7cd32..efb2acd0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -20,6 +20,8 @@ weston_SOURCES = \ screenshooter-protocol.c \ screenshooter-server-protocol.h \ util.c \ + matrix.c \ + matrix.h \ $(xserver_launcher_sources) if ENABLE_SETUID_INSTALL diff --git a/src/compositor.h b/src/compositor.h index 3ac65584..bec45c46 100644 --- a/src/compositor.h +++ b/src/compositor.h @@ -32,25 +32,7 @@ #include #include -struct weston_matrix { - GLfloat d[16]; -}; - -struct weston_vector { - GLfloat f[4]; -}; - -void -weston_matrix_init(struct weston_matrix *matrix); -void -weston_matrix_multiply(struct weston_matrix *m, const struct weston_matrix *n); -void -weston_matrix_scale(struct weston_matrix *matrix, GLfloat x, GLfloat y, GLfloat z); -void -weston_matrix_translate(struct weston_matrix *matrix, - GLfloat x, GLfloat y, GLfloat z); -void -weston_matrix_transform(struct weston_matrix *matrix, struct weston_vector *v); +#include "matrix.h" struct weston_transform { struct weston_matrix matrix; diff --git a/src/matrix.c b/src/matrix.c new file mode 100644 index 00000000..c71471f1 --- /dev/null +++ b/src/matrix.c @@ -0,0 +1,102 @@ +/* + * Copyright © 2011 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include "matrix.h" + +/* + * Matrices are stored in column-major order, that is the array indices are: + * 0 4 8 12 + * 1 5 9 13 + * 2 6 10 14 + * 3 7 11 15 + */ + +WL_EXPORT void +weston_matrix_init(struct weston_matrix *matrix) +{ + static const struct weston_matrix identity = { + { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 } + }; + + memcpy(matrix, &identity, sizeof identity); +} + +/* m <- n * m, that is, m is multiplied on the LEFT. */ +WL_EXPORT void +weston_matrix_multiply(struct weston_matrix *m, const struct weston_matrix *n) +{ + struct weston_matrix tmp; + const GLfloat *row, *column; + div_t d; + int i, j; + + for (i = 0; i < 16; i++) { + tmp.d[i] = 0; + d = div(i, 4); + row = m->d + d.quot * 4; + column = n->d + d.rem; + for (j = 0; j < 4; j++) + tmp.d[i] += row[j] * column[j * 4]; + } + memcpy(m, &tmp, sizeof tmp); +} + +WL_EXPORT void +weston_matrix_translate(struct weston_matrix *matrix, GLfloat x, GLfloat y, GLfloat z) +{ + struct weston_matrix translate = { + { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1 } + }; + + weston_matrix_multiply(matrix, &translate); +} + +WL_EXPORT void +weston_matrix_scale(struct weston_matrix *matrix, GLfloat x, GLfloat y, GLfloat z) +{ + struct weston_matrix scale = { + { x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1 } + }; + + weston_matrix_multiply(matrix, &scale); +} + +/* v <- m * v */ +WL_EXPORT void +weston_matrix_transform(struct weston_matrix *matrix, struct weston_vector *v) +{ + int i, j; + struct weston_vector t; + + for (i = 0; i < 4; i++) { + t.f[i] = 0; + for (j = 0; j < 4; j++) + t.f[i] += v->f[j] * matrix->d[i + j * 4]; + } + + *v = t; +} diff --git a/src/matrix.h b/src/matrix.h new file mode 100644 index 00000000..f149d876 --- /dev/null +++ b/src/matrix.h @@ -0,0 +1,46 @@ +/* + * Copyright © 2008-2011 Kristian Høgsberg + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WESTON_MATRIX_H +#define WESTON_MATRIX_H + +struct weston_matrix { + GLfloat d[16]; +}; + +struct weston_vector { + GLfloat f[4]; +}; + +void +weston_matrix_init(struct weston_matrix *matrix); +void +weston_matrix_multiply(struct weston_matrix *m, const struct weston_matrix *n); +void +weston_matrix_scale(struct weston_matrix *matrix, GLfloat x, GLfloat y, GLfloat z); +void +weston_matrix_translate(struct weston_matrix *matrix, + GLfloat x, GLfloat y, GLfloat z); +void +weston_matrix_transform(struct weston_matrix *matrix, struct weston_vector *v); + +#endif /* WESTON_MATRIX_H */ diff --git a/src/util.c b/src/util.c index 8a1742a7..c8037161 100644 --- a/src/util.c +++ b/src/util.c @@ -27,80 +27,6 @@ #include "compositor.h" -/* - * Matrices are stored in column-major order, that is the array indices are: - * 0 4 8 12 - * 1 5 9 13 - * 2 6 10 14 - * 3 7 11 15 - */ - -WL_EXPORT void -weston_matrix_init(struct weston_matrix *matrix) -{ - static const struct weston_matrix identity = { - { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 } - }; - - memcpy(matrix, &identity, sizeof identity); -} - -/* m <- n * m, that is, m is multiplied on the LEFT. */ -WL_EXPORT void -weston_matrix_multiply(struct weston_matrix *m, const struct weston_matrix *n) -{ - struct weston_matrix tmp; - const GLfloat *row, *column; - div_t d; - int i, j; - - for (i = 0; i < 16; i++) { - tmp.d[i] = 0; - d = div(i, 4); - row = m->d + d.quot * 4; - column = n->d + d.rem; - for (j = 0; j < 4; j++) - tmp.d[i] += row[j] * column[j * 4]; - } - memcpy(m, &tmp, sizeof tmp); -} - -WL_EXPORT void -weston_matrix_translate(struct weston_matrix *matrix, GLfloat x, GLfloat y, GLfloat z) -{ - struct weston_matrix translate = { - { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1 } - }; - - weston_matrix_multiply(matrix, &translate); -} - -WL_EXPORT void -weston_matrix_scale(struct weston_matrix *matrix, GLfloat x, GLfloat y, GLfloat z) -{ - struct weston_matrix scale = { - { x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1 } - }; - - weston_matrix_multiply(matrix, &scale); -} - -/* v <- m * v */ -WL_EXPORT void -weston_matrix_transform(struct weston_matrix *matrix, struct weston_vector *v) -{ - int i, j; - struct weston_vector t; - - for (i = 0; i < 4; i++) { - t.f[i] = 0; - for (j = 0; j < 4; j++) - t.f[i] += v->f[j] * matrix->d[i + j * 4]; - } - - *v = t; -} - WL_EXPORT void weston_spring_init(struct weston_spring *spring, double k, double current, double target) From 061b7471f1cca72c98ebec53d0e89e8166ed30f3 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 12 Jan 2012 15:00:57 +0200 Subject: [PATCH 04/30] compositor: drop inverse matrix from weston_transform Remove the inverse matrix member from struct weston_transform. It is easier (and probably faster, too) to create and store only forward transformation matrices in a list, multiply them once, and then invert the final matrix, rather than creating both forward and inverse matrices, and multiplying both. Add a stub for the 4x4 matrix inversion function. Signed-off-by: Pekka Paalanen --- src/compositor.c | 10 ++++------ src/compositor.h | 5 +++-- src/matrix.c | 7 +++++++ src/matrix.h | 4 ++++ src/util.c | 3 --- 5 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/compositor.c b/src/compositor.c index 0a7dea3e..69ad60e4 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -505,7 +505,7 @@ transform_vertex(struct weston_surface *surface, t.f[2] = 0.0; t.f[3] = 1.0; - weston_matrix_transform(&surface->transform.cached.matrix, &t); + weston_matrix_transform(&surface->transform.matrix, &t); /* XXX: assumes last row of matrix is [0 0 * 1] */ @@ -544,8 +544,8 @@ texture_transformed_surface(struct weston_surface *es) static void weston_surface_update_transform(struct weston_surface *surface) { - struct weston_matrix *matrix = &surface->transform.cached.matrix; - struct weston_matrix *inverse = &surface->transform.cached.inverse; + struct weston_matrix *matrix = &surface->transform.matrix; + struct weston_matrix *inverse = &surface->transform.inverse; struct weston_transform *tform; if (!surface->transform.dirty) @@ -564,9 +564,7 @@ weston_surface_update_transform(struct weston_surface *surface) wl_list_for_each(tform, &surface->transform.list, link) weston_matrix_multiply(matrix, &tform->matrix); - weston_matrix_init(inverse); - wl_list_for_each_reverse(tform, &surface->transform.list, link) - weston_matrix_multiply(inverse, &tform->inverse); + weston_matrix_invert(inverse, matrix); } WL_EXPORT void diff --git a/src/compositor.h b/src/compositor.h index bec45c46..53126651 100644 --- a/src/compositor.h +++ b/src/compositor.h @@ -36,7 +36,6 @@ struct weston_transform { struct weston_matrix matrix; - struct weston_matrix inverse; struct wl_list link; }; @@ -229,7 +228,9 @@ struct weston_surface { struct wl_list list; int dirty; - struct weston_transform cached; + /* derived state, set up by weston_surface_update_transform */ + struct weston_matrix matrix; + struct weston_matrix inverse; int enabled; } transform; diff --git a/src/matrix.c b/src/matrix.c index c71471f1..941e9584 100644 --- a/src/matrix.c +++ b/src/matrix.c @@ -100,3 +100,10 @@ weston_matrix_transform(struct weston_matrix *matrix, struct weston_vector *v) *v = t; } + +WL_EXPORT int +weston_matrix_invert(struct weston_matrix *inverse, + const struct weston_matrix *matrix) +{ + return -1; /* fail */ +} diff --git a/src/matrix.h b/src/matrix.h index f149d876..2c3285a6 100644 --- a/src/matrix.h +++ b/src/matrix.h @@ -43,4 +43,8 @@ weston_matrix_translate(struct weston_matrix *matrix, void weston_matrix_transform(struct weston_matrix *matrix, struct weston_vector *v); +int +weston_matrix_invert(struct weston_matrix *inverse, + const struct weston_matrix *matrix); + #endif /* WESTON_MATRIX_H */ diff --git a/src/util.c b/src/util.c index c8037161..6b8477a4 100644 --- a/src/util.c +++ b/src/util.c @@ -145,9 +145,6 @@ weston_zoom_frame(struct weston_animation *animation, es->alpha = zoom->spring.current * 255; if (es->alpha > 255) es->alpha = 255; - scale = 1.0 / zoom->spring.current; - weston_matrix_init(&zoom->transform.inverse); - weston_matrix_scale(&zoom->transform.inverse, scale, scale, scale); zoom->surface->transform.dirty = 1; From 75b47ec45d8076732b3f30c1b3ebc3d35e6ed7fe Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 16 Jan 2012 14:27:00 +0200 Subject: [PATCH 05/30] compositor: implement inverse matrix transformation Implement 4x4 matrix inversion based on LU-decomposition with partial pivoting. Instead of simply computing the inverse matrix explicitly, introduce the type struct weston_inverse_matrix for storing the LU-decomposition and the permutation from pivoting. Using doubles, this struct has greater precision than struct weston_matrix. If you need only few (less than 5, presumably) multiplications with the inverse matrix, is it cheaper to use weston_inverse_matrix, and not compute the inverse matrix explicitly into a weston_matrix. Signed-off-by: Pekka Paalanen --- src/compositor.c | 2 +- src/compositor.h | 2 +- src/matrix.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++- src/matrix.h | 11 ++++- 4 files changed, 133 insertions(+), 5 deletions(-) diff --git a/src/compositor.c b/src/compositor.c index 69ad60e4..cb19f342 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -545,7 +545,7 @@ static void weston_surface_update_transform(struct weston_surface *surface) { struct weston_matrix *matrix = &surface->transform.matrix; - struct weston_matrix *inverse = &surface->transform.inverse; + struct weston_inverse_matrix *inverse = &surface->transform.inverse; struct weston_transform *tform; if (!surface->transform.dirty) diff --git a/src/compositor.h b/src/compositor.h index 53126651..2064d676 100644 --- a/src/compositor.h +++ b/src/compositor.h @@ -230,7 +230,7 @@ struct weston_surface { /* derived state, set up by weston_surface_update_transform */ struct weston_matrix matrix; - struct weston_matrix inverse; + struct weston_inverse_matrix inverse; int enabled; } transform; diff --git a/src/matrix.c b/src/matrix.c index 941e9584..06cdc5e4 100644 --- a/src/matrix.c +++ b/src/matrix.c @@ -1,5 +1,6 @@ /* * Copyright © 2011 Intel Corporation + * Copyright © 2012 Collabora, Ltd. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided @@ -22,6 +23,7 @@ #include #include +#include #include #include @@ -101,9 +103,126 @@ weston_matrix_transform(struct weston_matrix *matrix, struct weston_vector *v) *v = t; } +static inline void +swap_rows(double *a, double *b) +{ + unsigned k; + double tmp; + + for (k = 0; k < 13; k += 4) { + tmp = a[k]; + a[k] = b[k]; + b[k] = tmp; + } +} + +static inline unsigned +find_pivot(double *column, unsigned k) +{ + unsigned p = k; + for (++k; k < 4; ++k) + if (fabs(column[p]) < fabs(column[k])) + p = k; + + return p; +} + +/* + * reference: Gene H. Golub and Charles F. van Loan. Matrix computations. + * 3rd ed. The Johns Hopkins University Press. 1996. + * LU decomposition, forward and back substitution: Chapter 3. + */ + WL_EXPORT int -weston_matrix_invert(struct weston_matrix *inverse, +weston_matrix_invert(struct weston_inverse_matrix *inverse, const struct weston_matrix *matrix) { - return -1; /* fail */ + double A[16]; + unsigned p[4] = { 0, 1, 2, 3 }; + unsigned i, j, k; + unsigned pivot; + double pv; + + for (i = 16; i--; ) + A[i] = matrix->d[i]; + + /* LU decomposition with partial pivoting */ + for (k = 0; k < 4; ++k) { + pivot = find_pivot(&A[k * 4], k); + if (pivot != k) { + unsigned tmp = p[k]; + p[k] = p[pivot]; + p[pivot] = tmp; + swap_rows(&A[k], &A[pivot]); + } + + pv = A[k * 4 + k]; + if (fabs(pv) < 1e-9) + return -1; /* zero pivot, not invertible */ + + for (i = k + 1; i < 4; ++i) { + A[i + k * 4] /= pv; + + for (j = k + 1; j < 4; ++j) + A[i + j * 4] -= A[i + k * 4] * A[k + j * 4]; + } + } + + memcpy(inverse->LU, A, sizeof(A)); + memcpy(inverse->p, p, sizeof(p)); + return 0; +} + +WL_EXPORT void +weston_matrix_inverse_transform(struct weston_inverse_matrix *inverse, + struct weston_vector *v) +{ + /* Solve A * x = v, when we have P * A = L * U. + * P * A * x = P * v => L * U * x = P * v + * Let U * x = b, then L * b = P * v. + */ + unsigned *p = inverse->p; + double *LU = inverse->LU; + double b[4]; + unsigned k, j; + + /* Forward substitution, column version, solves L * b = P * v */ + /* The diagonal of L is all ones, and not explicitly stored. */ + b[0] = v->f[p[0]]; + b[1] = (double)v->f[p[1]] - b[0] * LU[1 + 0 * 4]; + b[2] = (double)v->f[p[2]] - b[0] * LU[2 + 0 * 4]; + b[3] = (double)v->f[p[3]] - b[0] * LU[3 + 0 * 4]; + b[2] -= b[1] * LU[2 + 1 * 4]; + b[3] -= b[1] * LU[3 + 1 * 4]; + b[3] -= b[2] * LU[3 + 2 * 4]; + + /* backward substitution, column version, solves U * y = b */ +#if 1 + /* hand-unrolled, 25% faster for whole function */ + b[3] /= LU[3 + 3 * 4]; + b[0] -= b[3] * LU[0 + 3 * 4]; + b[1] -= b[3] * LU[1 + 3 * 4]; + b[2] -= b[3] * LU[2 + 3 * 4]; + + b[2] /= LU[2 + 2 * 4]; + b[0] -= b[2] * LU[0 + 2 * 4]; + b[1] -= b[2] * LU[1 + 2 * 4]; + + b[1] /= LU[1 + 1 * 4]; + b[0] -= b[1] * LU[0 + 1 * 4]; + + b[0] /= LU[0 + 0 * 4]; +#else + for (j = 3; j > 0; --j) { + b[j] /= LU[j + j * 4]; + for (k = 0; k < j; ++k) + b[k] -= b[j] * LU[k + j * 4]; + } + + b[0] /= LU[0 + 0 * 4]; +#endif + + /* the result */ + for (j = 0; j < 4; ++j) + v->f[j] = b[j]; } diff --git a/src/matrix.h b/src/matrix.h index 2c3285a6..21c1e785 100644 --- a/src/matrix.h +++ b/src/matrix.h @@ -1,5 +1,6 @@ /* * Copyright © 2008-2011 Kristian Høgsberg + * Copyright © 2012 Collabora, Ltd. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided @@ -27,6 +28,11 @@ struct weston_matrix { GLfloat d[16]; }; +struct weston_inverse_matrix { + double LU[16]; /* column-major */ + unsigned p[4]; /* permutation */ +}; + struct weston_vector { GLfloat f[4]; }; @@ -44,7 +50,10 @@ void weston_matrix_transform(struct weston_matrix *matrix, struct weston_vector *v); int -weston_matrix_invert(struct weston_matrix *inverse, +weston_matrix_invert(struct weston_inverse_matrix *inverse, const struct weston_matrix *matrix); +void +weston_matrix_inverse_transform(struct weston_inverse_matrix *inverse, + struct weston_vector *v); #endif /* WESTON_MATRIX_H */ From 4520d5cafbb26732922127fc8fb3b25241215a0a Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 16 Jan 2012 15:04:28 +0200 Subject: [PATCH 06/30] tests: add matrix-test Add a new directory tests/ for unit test applications. This directory will be built only if --enable-tests is given to ./configure. Add matrix-test application. It excercises especially the weston_matrix_invert() and weston_matrix_inverse_transform() functions. It has one test for correctness and precision, and other tests for measuring the speed of various matrix operations. For the record, the correctness test prints: a random matrix: 1.112418e-02 2.628150e+00 8.205844e+02 -1.147526e-04 4.943677e-04 -1.117819e-04 -9.158849e-06 3.678122e-02 7.915063e-03 -3.093254e-04 -4.376583e+02 3.424706e-02 -2.504038e+02 2.481788e+03 -7.545445e+01 1.752909e-03 The matrix multiplied by its inverse, error: 0.000000e+00 -0.000000e+00 -0.000000e+00 -0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 -0.000000e+00 -0.000000e+00 0.000000e+00 -0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 max abs error: 0, original determinant 11595.2 Running a test loop for 10 seconds... test fail, det: -0.00464805, error sup: inf test fail, det: -0.0424053, error sup: 1.30787e-06 test fail, det: 5.15191, error sup: 1.15956e-06 tests: 6791767 ok, 1 not invertible but ok, 3 failed. Total: 6791771 iterations. These results are expected with the current precision thresholds in src/matrix.c and tests/matrix-test.c. The random number generator is seeded with a constant, so the random numbers should be the same on every run. Machine speed and scheduling affect how many iterations are run. Signed-off-by: Pekka Paalanen --- Makefile.am | 2 +- configure.ac | 6 +- src/matrix.c | 3 +- tests/.gitignore | 2 + tests/Makefile.am | 15 ++ tests/matrix-test.c | 434 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 459 insertions(+), 3 deletions(-) create mode 100644 tests/.gitignore create mode 100644 tests/Makefile.am create mode 100644 tests/matrix-test.c diff --git a/Makefile.am b/Makefile.am index e00e475f..c7d6431c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1 +1 @@ -SUBDIRS = shared src clients data protocol +SUBDIRS = shared src clients data protocol tests diff --git a/configure.ac b/configure.ac index 3522bee5..62d36ebd 100644 --- a/configure.ac +++ b/configure.ac @@ -148,6 +148,9 @@ AC_ARG_ENABLE(tablet-shell, [ --enable-tablet-shell],, AM_CONDITIONAL(ENABLE_TABLET_SHELL, test x$enable_tablet_shell == xyes) +AC_ARG_ENABLE(tests, [ --enable-tests],,enable_tests=yes) +AM_CONDITIONAL(BUILD_TESTS, test x$enable_tests == xyes) + if test "x$GCC" = "xyes"; then GCC_CFLAGS="-Wall -g -Wstrict-prototypes -Wmissing-prototypes -fvisibility=hidden" fi @@ -160,7 +163,8 @@ AC_CONFIG_FILES([Makefile src/Makefile clients/Makefile data/Makefile - protocol/Makefile]) + protocol/Makefile + tests/Makefile]) AC_OUTPUT if test "x$enable_setuid_install" == xyes; then diff --git a/src/matrix.c b/src/matrix.c index 06cdc5e4..46ce56a8 100644 --- a/src/matrix.c +++ b/src/matrix.c @@ -184,7 +184,7 @@ weston_matrix_inverse_transform(struct weston_inverse_matrix *inverse, unsigned *p = inverse->p; double *LU = inverse->LU; double b[4]; - unsigned k, j; + unsigned j; /* Forward substitution, column version, solves L * b = P * v */ /* The diagonal of L is all ones, and not explicitly stored. */ @@ -214,6 +214,7 @@ weston_matrix_inverse_transform(struct weston_inverse_matrix *inverse, b[0] /= LU[0 + 0 * 4]; #else for (j = 3; j > 0; --j) { + unsigned k; b[j] /= LU[j + j * 4]; for (k = 0; k < j; ++k) b[k] -= b[j] * LU[k + j * 4]; diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 00000000..e8df81b8 --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,2 @@ +matrix-test + diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 00000000..e9d96acd --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,15 @@ +if BUILD_TESTS + +noinst_PROGRAMS = matrix-test + +endif + + +AM_CFLAGS = $(GCC_CFLAGS) +AM_CPPFLAGS = -I../src + +matrix_test_SOURCES = \ + matrix-test.c \ + ../src/matrix.c \ + ../src/matrix.h +matrix_test_LDADD = -lm -lrt diff --git a/tests/matrix-test.c b/tests/matrix-test.c new file mode 100644 index 00000000..76d93ada --- /dev/null +++ b/tests/matrix-test.c @@ -0,0 +1,434 @@ +/* + * Copyright © 2012 Collabora, Ltd. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "matrix.h" + +static struct timespec begin_time; + +static void +reset_timer(void) +{ + clock_gettime(CLOCK_MONOTONIC, &begin_time); +} + +static double +read_timer(void) +{ + struct timespec t; + + clock_gettime(CLOCK_MONOTONIC, &t); + return (double)(t.tv_sec - begin_time.tv_sec) + + 1e-9 * (t.tv_nsec - begin_time.tv_nsec); +} + +static double +det3x3(const GLfloat *c0, const GLfloat *c1, const GLfloat *c2) +{ + return (double) + c0[0] * c1[1] * c2[2] + + c1[0] * c2[1] * c0[2] + + c2[0] * c0[1] * c1[2] - + c0[2] * c1[1] * c2[0] - + c1[2] * c2[1] * c0[0] - + c2[2] * c0[1] * c1[0]; +} + +static double +determinant(const struct weston_matrix *m) +{ + double det = 0; +#if 1 + /* develop on last row */ + det -= m->d[3 + 0 * 4] * det3x3(&m->d[4], &m->d[8], &m->d[12]); + det += m->d[3 + 1 * 4] * det3x3(&m->d[0], &m->d[8], &m->d[12]); + det -= m->d[3 + 2 * 4] * det3x3(&m->d[0], &m->d[4], &m->d[12]); + det += m->d[3 + 3 * 4] * det3x3(&m->d[0], &m->d[4], &m->d[8]); +#else + /* develop on first row */ + det += m->d[0 + 0 * 4] * det3x3(&m->d[5], &m->d[9], &m->d[13]); + det -= m->d[0 + 1 * 4] * det3x3(&m->d[1], &m->d[9], &m->d[13]); + det += m->d[0 + 2 * 4] * det3x3(&m->d[1], &m->d[5], &m->d[13]); + det -= m->d[0 + 3 * 4] * det3x3(&m->d[1], &m->d[5], &m->d[9]); +#endif + return det; +} + +static void +print_permutation_matrix(const struct weston_inverse_matrix *m) +{ + const unsigned *p = m->p; + const char *row[4] = { + "1 0 0 0\n", + "0 1 0 0\n", + "0 0 1 0\n", + "0 0 0 1\n" + }; + + printf(" P =\n%s%s%s%s", row[p[0]], row[p[1]], row[p[2]], row[p[3]]); +} + +static void +print_LU_decomposition(const struct weston_inverse_matrix *m) +{ + unsigned r, c; + + printf(" L " + " U\n"); + for (r = 0; r < 4; ++r) { + double v; + + for (c = 0; c < 4; ++c) { + if (c < r) + v = m->LU[r + c * 4]; + else if (c == r) + v = 1.0; + else + v = 0.0; + printf(" %12.6f", v); + } + + printf(" | "); + + for (c = 0; c < 4; ++c) { + if (c >= r) + v = m->LU[r + c * 4]; + else + v = 0.0; + printf(" %12.6f", v); + } + printf("\n"); + } +} + +static void +print_inverse_data_matrix(const struct weston_inverse_matrix *m) +{ + unsigned r, c; + + for (r = 0; r < 4; ++r) { + for (c = 0; c < 4; ++c) + printf(" %12.6f", m->LU[r + c * 4]); + printf("\n"); + } + + printf("permutation: "); + for (r = 0; r < 4; ++r) + printf(" %u", m->p[r]); + printf("\n"); +} + +static void +print_matrix(const struct weston_matrix *m) +{ + unsigned r, c; + + for (r = 0; r < 4; ++r) { + for (c = 0; c < 4; ++c) + printf(" %14.6e", m->d[r + c * 4]); + printf("\n"); + } +} + +static double +frand(void) +{ + double r = random(); + return r / (double)(RAND_MAX / 2) - 1.0f; +} + +static void +randomize_matrix(struct weston_matrix *m) +{ + unsigned i; + for (i = 0; i < 16; ++i) +#if 1 + m->d[i] = frand() * exp(10.0 * frand()); +#else + m->d[i] = frand(); +#endif +} + +static void +invert_matrix(struct weston_matrix *m) +{ + struct weston_inverse_matrix q; + unsigned i; + + if (weston_matrix_invert(&q, m) != 0) { + m->d[0] = NAN; + return; + } + + for (i = 0; i < 4; ++i) + weston_matrix_inverse_transform(&q, + (struct weston_vector *)&m->d[i * 4]); +} + +/* Take a matrix, compute inverse, multiply together + * and subtract the identity matrix to get the error matrix. + * Return the largest absolute value from the error matrix. + */ +static double +test_inverse(struct weston_matrix *m) +{ + unsigned i; + struct weston_inverse_matrix q; + double errsup = 0.0; + + if (weston_matrix_invert(&q, m) != 0) + return INFINITY; + + for (i = 0; i < 4; ++i) + weston_matrix_inverse_transform(&q, + (struct weston_vector *)&m->d[i * 4]); + + m->d[0] -= 1.0f; + m->d[5] -= 1.0f; + m->d[10] -= 1.0f; + m->d[15] -= 1.0f; + + for (i = 0; i < 16; ++i) { + double err = fabs(m->d[i]); + if (err > errsup) + errsup = err; + } + + return errsup; +} + +enum { + TEST_OK, + TEST_NOT_INVERTIBLE_OK, + TEST_FAIL, + TEST_COUNT +}; + +static int +test(void) +{ + struct weston_matrix m; + struct weston_matrix n; + double det, errsup; + + randomize_matrix(&m); + n = m; + det = determinant(&m); + + errsup = test_inverse(&m); + if (errsup < 1e-6) + return TEST_OK; + + if (fabs(det) < 1e-5 && isinf(errsup)) + return TEST_NOT_INVERTIBLE_OK; + + printf("test fail, det: %g, error sup: %g\n", det, errsup); +/* print_matrix(&n);*/ + return TEST_FAIL; +} + +static int running; +static void +stopme(int n) +{ + running = 0; +} + +static void +test_loop_precision(void) +{ + int counts[TEST_COUNT] = { 0 }; + + printf("\nRunning a test loop for 10 seconds...\n"); + running = 1; + alarm(10); + while (running) { + counts[test()]++; + } + + printf("tests: %d ok, %d not invertible but ok, %d failed.\n" + "Total: %d iterations.\n", + counts[TEST_OK], counts[TEST_NOT_INVERTIBLE_OK], + counts[TEST_FAIL], + counts[TEST_OK] + counts[TEST_NOT_INVERTIBLE_OK] + + counts[TEST_FAIL]); +} + +static void __attribute__((noinline)) +test_loop_speed_matrixvector(void) +{ + struct weston_matrix m; + struct weston_vector v = { { 0.5, 0.5, 0.5, 1.0 } }; + unsigned long count = 0; + double t; + + printf("\nRunning 3 s test on weston_matrix_transform()...\n"); + + weston_matrix_init(&m); + + running = 1; + alarm(3); + reset_timer(); + while (running) { + weston_matrix_transform(&m, &v); + count++; + } + t = read_timer(); + + printf("%lu iterations in %f seconds, avg. %.1f us/iter.\n", + count, t, 1e9 * t / count); +} + +static void __attribute__((noinline)) +test_loop_speed_inversetransform(void) +{ + struct weston_matrix m; + struct weston_inverse_matrix inv; + struct weston_vector v = { { 0.5, 0.5, 0.5, 1.0 } }; + unsigned long count = 0; + double t; + + printf("\nRunning 3 s test on weston_matrix_inverse_transform()...\n"); + + weston_matrix_init(&m); + weston_matrix_invert(&inv, &m); + + running = 1; + alarm(3); + reset_timer(); + while (running) { + weston_matrix_inverse_transform(&inv, &v); + count++; + } + t = read_timer(); + + printf("%lu iterations in %f seconds, avg. %.1f us/iter.\n", + count, t, 1e9 * t / count); +} + +static void __attribute__((noinline)) +test_loop_speed_invert(void) +{ + struct weston_matrix m; + struct weston_inverse_matrix inv; + unsigned long count = 0; + double t; + + printf("\nRunning 3 s test on weston_matrix_invert()...\n"); + + weston_matrix_init(&m); + + running = 1; + alarm(3); + reset_timer(); + while (running) { + weston_matrix_invert(&inv, &m); + count++; + } + t = read_timer(); + + printf("%lu iterations in %f seconds, avg. %.1f ns/iter.\n", + count, t, 1e9 * t / count); +} + +static void __attribute__((noinline)) +test_loop_speed_invert_explicit(void) +{ + struct weston_matrix m; + unsigned long count = 0; + double t; + + printf("\nRunning 3 s test on computing the explicit inverse matrix...\n"); + + weston_matrix_init(&m); + + running = 1; + alarm(3); + reset_timer(); + while (running) { + invert_matrix(&m); + count++; + } + t = read_timer(); + + printf("%lu iterations in %f seconds, avg. %.1f ns/iter.\n", + count, t, 1e9 * t / count); +} + +int main(void) +{ + struct sigaction ding; + struct weston_matrix M; + struct weston_inverse_matrix Q; + int ret; + double errsup; + double det; + + ding.sa_handler = stopme; + sigemptyset(&ding.sa_mask); + ding.sa_flags = 0; + sigaction(SIGALRM, &ding, NULL); + + srandom(13); + + M.d[0] = 3.0; M.d[4] = 17.0; M.d[8] = 10.0; M.d[12] = 0.0; + M.d[1] = 2.0; M.d[5] = 4.0; M.d[9] = -2.0; M.d[13] = 0.0; + M.d[2] = 6.0; M.d[6] = 18.0; M.d[10] = -12; M.d[14] = 0.0; + M.d[3] = 0.0; M.d[7] = 0.0; M.d[11] = 0.0; M.d[15] = 1.0; + + ret = weston_matrix_invert(&Q, &M); + printf("ret = %d\n", ret); + printf("det = %g\n\n", determinant(&M)); + + if (ret != 0) + return 1; + + print_inverse_data_matrix(&Q); + printf("P * A = L * U\n"); + print_permutation_matrix(&Q); + print_LU_decomposition(&Q); + + + printf("a random matrix:\n"); + randomize_matrix(&M); + det = determinant(&M); + print_matrix(&M); + errsup = test_inverse(&M); + printf("\nThe matrix multiplied by its inverse, error:\n"); + print_matrix(&M); + printf("max abs error: %g, original determinant %g\n", errsup, det); + + test_loop_precision(); + test_loop_speed_matrixvector(); + test_loop_speed_inversetransform(); + test_loop_speed_invert(); + test_loop_speed_invert_explicit(); + + return 0; +} From 1ef94c87422c1950d9c73545ddfe3d5a6bfaae99 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 16 Jan 2012 15:38:17 +0200 Subject: [PATCH 07/30] update git ignores Signed-off-by: Pekka Paalanen --- clients/.gitignore | 5 +++-- src/.gitignore | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/clients/.gitignore b/clients/.gitignore index 0ce4b08a..a937dec1 100644 --- a/clients/.gitignore +++ b/clients/.gitignore @@ -12,11 +12,12 @@ screenshooter-protocol.c screenshot simple-egl simple-shm +simple-touch smoke tablet-shell-client-protocol.h tablet-shell-protocol.c terminal view -wayland-desktop-shell -wayland-tablet-shell +weston-desktop-shell +weston-tablet-shell wscreensaver diff --git a/src/.gitignore b/src/.gitignore index 0a6ccbb4..c53b4022 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1,4 +1,4 @@ -wayland-compositor +weston screenshooter-protocol.c screenshooter-server-protocol.h tablet-shell-protocol.c From d1f0ab63431cfa9b558afd026db87a59b841db40 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 20 Jan 2012 10:47:57 +0200 Subject: [PATCH 08/30] compositor: simplify the matrix inversion API The compositor will likely do an order of magnitude less matrix inversions than point transformations with an inverse, hence we do not really need the optimised path for single-shot invert-and-transform. Expose only the computing of the explicit inverse matrix in the API. However, the matrix inversion tests need access to the internal functions. Designate a unit test build by #defining UNIT_TEST, and export the internal functions in that case. Signed-off-by: Pekka Paalanen --- src/compositor.c | 2 +- src/compositor.h | 2 +- src/matrix.c | 61 ++++++++++++++++++++++++++++++--------------- src/matrix.h | 21 ++++++++++------ tests/Makefile.am | 2 +- tests/matrix-test.c | 60 ++++++++++++++++++-------------------------- 6 files changed, 81 insertions(+), 67 deletions(-) diff --git a/src/compositor.c b/src/compositor.c index cb19f342..69ad60e4 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -545,7 +545,7 @@ static void weston_surface_update_transform(struct weston_surface *surface) { struct weston_matrix *matrix = &surface->transform.matrix; - struct weston_inverse_matrix *inverse = &surface->transform.inverse; + struct weston_matrix *inverse = &surface->transform.inverse; struct weston_transform *tform; if (!surface->transform.dirty) diff --git a/src/compositor.h b/src/compositor.h index 2064d676..53126651 100644 --- a/src/compositor.h +++ b/src/compositor.h @@ -230,7 +230,7 @@ struct weston_surface { /* derived state, set up by weston_surface_update_transform */ struct weston_matrix matrix; - struct weston_inverse_matrix inverse; + struct weston_matrix inverse; int enabled; } transform; diff --git a/src/matrix.c b/src/matrix.c index 46ce56a8..98ccf4ca 100644 --- a/src/matrix.c +++ b/src/matrix.c @@ -29,6 +29,7 @@ #include "matrix.h" + /* * Matrices are stored in column-major order, that is the array indices are: * 0 4 8 12 @@ -116,6 +117,16 @@ swap_rows(double *a, double *b) } } +static inline void +swap_unsigned(unsigned *a, unsigned *b) +{ + unsigned tmp; + + tmp = *a; + *a = *b; + *b = tmp; +} + static inline unsigned find_pivot(double *column, unsigned k) { @@ -133,16 +144,15 @@ find_pivot(double *column, unsigned k) * LU decomposition, forward and back substitution: Chapter 3. */ -WL_EXPORT int -weston_matrix_invert(struct weston_inverse_matrix *inverse, - const struct weston_matrix *matrix) +MATRIX_TEST_EXPORT inline int +matrix_invert(double *A, unsigned *p, const struct weston_matrix *matrix) { - double A[16]; - unsigned p[4] = { 0, 1, 2, 3 }; unsigned i, j, k; unsigned pivot; double pv; + for (i = 0; i < 4; ++i) + p[i] = i; for (i = 16; i--; ) A[i] = matrix->d[i]; @@ -150,9 +160,7 @@ weston_matrix_invert(struct weston_inverse_matrix *inverse, for (k = 0; k < 4; ++k) { pivot = find_pivot(&A[k * 4], k); if (pivot != k) { - unsigned tmp = p[k]; - p[k] = p[pivot]; - p[pivot] = tmp; + swap_unsigned(&p[k], &p[pivot]); swap_rows(&A[k], &A[pivot]); } @@ -168,30 +176,25 @@ weston_matrix_invert(struct weston_inverse_matrix *inverse, } } - memcpy(inverse->LU, A, sizeof(A)); - memcpy(inverse->p, p, sizeof(p)); return 0; } -WL_EXPORT void -weston_matrix_inverse_transform(struct weston_inverse_matrix *inverse, - struct weston_vector *v) +MATRIX_TEST_EXPORT inline void +inverse_transform(const double *LU, const unsigned *p, GLfloat *v) { /* Solve A * x = v, when we have P * A = L * U. * P * A * x = P * v => L * U * x = P * v * Let U * x = b, then L * b = P * v. */ - unsigned *p = inverse->p; - double *LU = inverse->LU; double b[4]; unsigned j; /* Forward substitution, column version, solves L * b = P * v */ /* The diagonal of L is all ones, and not explicitly stored. */ - b[0] = v->f[p[0]]; - b[1] = (double)v->f[p[1]] - b[0] * LU[1 + 0 * 4]; - b[2] = (double)v->f[p[2]] - b[0] * LU[2 + 0 * 4]; - b[3] = (double)v->f[p[3]] - b[0] * LU[3 + 0 * 4]; + b[0] = v[p[0]]; + b[1] = (double)v[p[1]] - b[0] * LU[1 + 0 * 4]; + b[2] = (double)v[p[2]] - b[0] * LU[2 + 0 * 4]; + b[3] = (double)v[p[3]] - b[0] * LU[3 + 0 * 4]; b[2] -= b[1] * LU[2 + 1 * 4]; b[3] -= b[1] * LU[3 + 1 * 4]; b[3] -= b[2] * LU[3 + 2 * 4]; @@ -225,5 +228,23 @@ weston_matrix_inverse_transform(struct weston_inverse_matrix *inverse, /* the result */ for (j = 0; j < 4; ++j) - v->f[j] = b[j]; + v[j] = b[j]; +} + +WL_EXPORT int +weston_matrix_invert(struct weston_matrix *inverse, + const struct weston_matrix *matrix) +{ + double LU[16]; /* column-major */ + unsigned perm[4]; /* permutation */ + unsigned c; + + if (matrix_invert(LU, perm, matrix) < 0) + return -1; + + weston_matrix_init(inverse); + for (c = 0; c < 4; ++c) + inverse_transform(LU, perm, &inverse->d[c * 4]); + + return 0; } diff --git a/src/matrix.h b/src/matrix.h index 21c1e785..04b179ff 100644 --- a/src/matrix.h +++ b/src/matrix.h @@ -28,11 +28,6 @@ struct weston_matrix { GLfloat d[16]; }; -struct weston_inverse_matrix { - double LU[16]; /* column-major */ - unsigned p[4]; /* permutation */ -}; - struct weston_vector { GLfloat f[4]; }; @@ -50,10 +45,20 @@ void weston_matrix_transform(struct weston_matrix *matrix, struct weston_vector *v); int -weston_matrix_invert(struct weston_inverse_matrix *inverse, +weston_matrix_invert(struct weston_matrix *inverse, const struct weston_matrix *matrix); + +#ifdef UNIT_TEST +# define MATRIX_TEST_EXPORT WL_EXPORT + +int +matrix_invert(double *A, unsigned *p, const struct weston_matrix *matrix); + void -weston_matrix_inverse_transform(struct weston_inverse_matrix *inverse, - struct weston_vector *v); +inverse_transform(const double *LU, const unsigned *p, GLfloat *v); + +#else +# define MATRIX_TEST_EXPORT static +#endif #endif /* WESTON_MATRIX_H */ diff --git a/tests/Makefile.am b/tests/Makefile.am index e9d96acd..a1f361e1 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -6,7 +6,7 @@ endif AM_CFLAGS = $(GCC_CFLAGS) -AM_CPPFLAGS = -I../src +AM_CPPFLAGS = -I../src -DUNIT_TEST matrix_test_SOURCES = \ matrix-test.c \ diff --git a/tests/matrix-test.c b/tests/matrix-test.c index 76d93ada..6fdab839 100644 --- a/tests/matrix-test.c +++ b/tests/matrix-test.c @@ -30,6 +30,11 @@ #include "matrix.h" +struct inverse_matrix { + double LU[16]; /* column-major */ + unsigned perm[4]; /* permutation */ +}; + static struct timespec begin_time; static void @@ -81,9 +86,9 @@ determinant(const struct weston_matrix *m) } static void -print_permutation_matrix(const struct weston_inverse_matrix *m) +print_permutation_matrix(const struct inverse_matrix *m) { - const unsigned *p = m->p; + const unsigned *p = m->perm; const char *row[4] = { "1 0 0 0\n", "0 1 0 0\n", @@ -95,7 +100,7 @@ print_permutation_matrix(const struct weston_inverse_matrix *m) } static void -print_LU_decomposition(const struct weston_inverse_matrix *m) +print_LU_decomposition(const struct inverse_matrix *m) { unsigned r, c; @@ -128,7 +133,7 @@ print_LU_decomposition(const struct weston_inverse_matrix *m) } static void -print_inverse_data_matrix(const struct weston_inverse_matrix *m) +print_inverse_data_matrix(const struct inverse_matrix *m) { unsigned r, c; @@ -140,7 +145,7 @@ print_inverse_data_matrix(const struct weston_inverse_matrix *m) printf("permutation: "); for (r = 0; r < 4; ++r) - printf(" %u", m->p[r]); + printf(" %u", m->perm[r]); printf("\n"); } @@ -175,22 +180,6 @@ randomize_matrix(struct weston_matrix *m) #endif } -static void -invert_matrix(struct weston_matrix *m) -{ - struct weston_inverse_matrix q; - unsigned i; - - if (weston_matrix_invert(&q, m) != 0) { - m->d[0] = NAN; - return; - } - - for (i = 0; i < 4; ++i) - weston_matrix_inverse_transform(&q, - (struct weston_vector *)&m->d[i * 4]); -} - /* Take a matrix, compute inverse, multiply together * and subtract the identity matrix to get the error matrix. * Return the largest absolute value from the error matrix. @@ -199,15 +188,14 @@ static double test_inverse(struct weston_matrix *m) { unsigned i; - struct weston_inverse_matrix q; + struct inverse_matrix q; double errsup = 0.0; - if (weston_matrix_invert(&q, m) != 0) + if (matrix_invert(q.LU, q.perm, m) != 0) return INFINITY; for (i = 0; i < 4; ++i) - weston_matrix_inverse_transform(&q, - (struct weston_vector *)&m->d[i * 4]); + inverse_transform(q.LU, q.perm, &m->d[i * 4]); m->d[0] -= 1.0f; m->d[5] -= 1.0f; @@ -309,21 +297,21 @@ static void __attribute__((noinline)) test_loop_speed_inversetransform(void) { struct weston_matrix m; - struct weston_inverse_matrix inv; + struct inverse_matrix inv; struct weston_vector v = { { 0.5, 0.5, 0.5, 1.0 } }; unsigned long count = 0; double t; - printf("\nRunning 3 s test on weston_matrix_inverse_transform()...\n"); + printf("\nRunning 3 s test on inverse_transform()...\n"); weston_matrix_init(&m); - weston_matrix_invert(&inv, &m); + matrix_invert(inv.LU, inv.perm, &m); running = 1; alarm(3); reset_timer(); while (running) { - weston_matrix_inverse_transform(&inv, &v); + inverse_transform(inv.LU, inv.perm, v.f); count++; } t = read_timer(); @@ -336,11 +324,11 @@ static void __attribute__((noinline)) test_loop_speed_invert(void) { struct weston_matrix m; - struct weston_inverse_matrix inv; + struct inverse_matrix inv; unsigned long count = 0; double t; - printf("\nRunning 3 s test on weston_matrix_invert()...\n"); + printf("\nRunning 3 s test on matrix_invert()...\n"); weston_matrix_init(&m); @@ -348,7 +336,7 @@ test_loop_speed_invert(void) alarm(3); reset_timer(); while (running) { - weston_matrix_invert(&inv, &m); + matrix_invert(inv.LU, inv.perm, &m); count++; } t = read_timer(); @@ -364,7 +352,7 @@ test_loop_speed_invert_explicit(void) unsigned long count = 0; double t; - printf("\nRunning 3 s test on computing the explicit inverse matrix...\n"); + printf("\nRunning 3 s test on weston_matrix_invert()...\n"); weston_matrix_init(&m); @@ -372,7 +360,7 @@ test_loop_speed_invert_explicit(void) alarm(3); reset_timer(); while (running) { - invert_matrix(&m); + weston_matrix_invert(&m, &m); count++; } t = read_timer(); @@ -385,7 +373,7 @@ int main(void) { struct sigaction ding; struct weston_matrix M; - struct weston_inverse_matrix Q; + struct inverse_matrix Q; int ret; double errsup; double det; @@ -402,7 +390,7 @@ int main(void) M.d[2] = 6.0; M.d[6] = 18.0; M.d[10] = -12; M.d[14] = 0.0; M.d[3] = 0.0; M.d[7] = 0.0; M.d[11] = 0.0; M.d[15] = 1.0; - ret = weston_matrix_invert(&Q, &M); + ret = matrix_invert(Q.LU, Q.perm, &M); printf("ret = %d\n", ret); printf("det = %g\n\n", determinant(&M)); From 6920190013ce55fd249bfe612ccfaa31ad01cba4 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 20 Jan 2012 11:09:13 +0200 Subject: [PATCH 09/30] clickdot: a copy of resizor Start a new application clickdot as a copy of resizor, with the name changed. Signed-off-by: Pekka Paalanen --- clients/.gitignore | 1 + clients/Makefile.am | 4 + clients/clickdot.c | 259 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 264 insertions(+) create mode 100644 clients/clickdot.c diff --git a/clients/.gitignore b/clients/.gitignore index a937dec1..5954a540 100644 --- a/clients/.gitignore +++ b/clients/.gitignore @@ -1,3 +1,4 @@ +clickdot desktop-shell-client-protocol.h desktop-shell-protocol.c dnd diff --git a/clients/Makefile.am b/clients/Makefile.am index bf73e406..b64c38a6 100644 --- a/clients/Makefile.am +++ b/clients/Makefile.am @@ -37,6 +37,7 @@ clients_programs = \ smoke \ resizor \ eventdemo \ + clickdot \ $(full_gl_client_programs) desktop_shell = weston-desktop-shell @@ -83,6 +84,9 @@ resizor_LDADD = $(toolkit_libs) eventdemo_SOURCES = eventdemo.c eventdemo_LDADD = $(toolkit_libs) +clickdot_SOURCES = clickdot.c +clickdot_LDADD = $(toolkit_libs) + weston_desktop_shell_SOURCES = \ desktop-shell.c \ desktop-shell-client-protocol.h \ diff --git a/clients/clickdot.c b/clients/clickdot.c new file mode 100644 index 00000000..a707037c --- /dev/null +++ b/clients/clickdot.c @@ -0,0 +1,259 @@ +/* + * Copyright © 2010 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "window.h" + +#include + +struct clickdot { + struct display *display; + struct window *window; + struct widget *widget; + struct window *menu; + int32_t width; + + struct { + double current; + double target; + double previous; + } height; + struct wl_callback *frame_callback; +}; + +static void +frame_callback(void *data, struct wl_callback *callback, uint32_t time) +{ + struct clickdot *clickdot = data; + double force, height; + + assert(!callback || callback == clickdot->frame_callback); + + height = clickdot->height.current; + force = (clickdot->height.target - height) / 10.0 + + (clickdot->height.previous - height); + + clickdot->height.current = + height + (height - clickdot->height.previous) + force; + clickdot->height.previous = height; + + if (clickdot->height.current >= 400) { + clickdot->height.current = 400; + clickdot->height.previous = 400; + } + + if (clickdot->height.current <= 200) { + clickdot->height.current = 200; + clickdot->height.previous = 200; + } + + widget_schedule_resize(clickdot->widget, clickdot->width, height + 0.5); + + if (clickdot->frame_callback) { + wl_callback_destroy(clickdot->frame_callback); + clickdot->frame_callback = NULL; + } +} + +static const struct wl_callback_listener listener = { + frame_callback +}; + +static void +redraw_handler(struct widget *widget, void *data) +{ + struct clickdot *clickdot = data; + cairo_surface_t *surface; + cairo_t *cr; + struct rectangle allocation; + + widget_get_allocation(clickdot->widget, &allocation); + + surface = window_get_surface(clickdot->window); + + cr = cairo_create(surface); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_rectangle(cr, + allocation.x, + allocation.y, + allocation.width, + allocation.height); + cairo_set_source_rgba(cr, 0, 0, 0, 0.8); + cairo_fill(cr); + cairo_destroy(cr); + + cairo_surface_destroy(surface); + + if (fabs(clickdot->height.previous - clickdot->height.target) > 0.1) { + clickdot->frame_callback = + wl_surface_frame( + window_get_wl_surface(clickdot->window)); + wl_callback_add_listener(clickdot->frame_callback, &listener, + clickdot); + } + +} + +static void +keyboard_focus_handler(struct window *window, + struct input *device, void *data) +{ + struct clickdot *clickdot = data; + + window_schedule_redraw(clickdot->window); +} + +static void +key_handler(struct window *window, struct input *input, uint32_t time, + uint32_t key, uint32_t sym, uint32_t state, void *data) +{ + struct clickdot *clickdot = data; + + if (state == 0) + return; + + switch (sym) { + case XK_Down: + clickdot->height.target = 400; + frame_callback(clickdot, NULL, 0); + break; + case XK_Up: + clickdot->height.target = 200; + frame_callback(clickdot, NULL, 0); + break; + case XK_Escape: + display_exit(clickdot->display); + break; + } +} + +static void +menu_func(struct window *window, int index, void *user_data) +{ + fprintf(stderr, "picked entry %d\n", index); +} + +static void +show_menu(struct clickdot *clickdot, struct input *input, uint32_t time) +{ + int32_t x, y; + static const char *entries[] = { + "Roy", "Pris", "Leon", "Zhora" + }; + + input_get_position(input, &x, &y); + window_show_menu(clickdot->display, input, time, clickdot->window, + x - 10, y - 10, menu_func, entries, 4); +} + +static void +button_handler(struct widget *widget, + struct input *input, uint32_t time, + int button, int state, void *data) +{ + struct clickdot *clickdot = data; + + switch (button) { + case BTN_RIGHT: + if (state) + show_menu(clickdot, input, time); + break; + } +} + +static struct clickdot * +clickdot_create(struct display *display) +{ + struct clickdot *clickdot; + int32_t height; + + clickdot = malloc(sizeof *clickdot); + if (clickdot == NULL) + return clickdot; + memset(clickdot, 0, sizeof *clickdot); + + clickdot->window = window_create(display, 500, 400); + clickdot->widget = frame_create(clickdot->window, clickdot); + window_set_title(clickdot->window, "Wayland Resizor"); + clickdot->display = display; + + window_set_key_handler(clickdot->window, key_handler); + window_set_user_data(clickdot->window, clickdot); + widget_set_redraw_handler(clickdot->widget, redraw_handler); + window_set_keyboard_focus_handler(clickdot->window, + keyboard_focus_handler); + + clickdot->width = 300; + clickdot->height.current = 400; + clickdot->height.previous = clickdot->height.current; + clickdot->height.target = clickdot->height.current; + height = clickdot->height.current + 0.5; + + widget_set_button_handler(clickdot->widget, button_handler); + + widget_schedule_resize(clickdot->widget, clickdot->width, height); + + return clickdot; +} + +static void +clickdot_destroy(struct clickdot *clickdot) +{ + if (clickdot->frame_callback) + wl_callback_destroy(clickdot->frame_callback); + + widget_destroy(clickdot->widget); + window_destroy(clickdot->window); + free(clickdot); +} + +int +main(int argc, char *argv[]) +{ + struct display *display; + struct clickdot *clickdot; + + display = display_create(&argc, &argv, NULL); + if (display == NULL) { + fprintf(stderr, "failed to create display: %m\n"); + return -1; + } + + clickdot = clickdot_create(display); + + display_run(display); + + clickdot_destroy(clickdot); + display_destroy(display); + + return 0; +} From b13e84f4e95faaef6ee259bf67edce853ddda732 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 20 Jan 2012 13:04:56 +0200 Subject: [PATCH 10/30] clickdot: implement the purpose Remove all unneeded resizor features, and add the feature why clickdot exists: put a visible marker to exactly where mouse was clicked. This app can be used to check input coordinate transformations in a compositor. Signed-off-by: Pekka Paalanen --- clients/clickdot.c | 124 +++++++++------------------------------------ 1 file changed, 23 insertions(+), 101 deletions(-) diff --git a/clients/clickdot.c b/clients/clickdot.c index a707037c..71bc091c 100644 --- a/clients/clickdot.c +++ b/clients/clickdot.c @@ -1,5 +1,6 @@ /* * Copyright © 2010 Intel Corporation + * Copyright © 2012 Collabora, Ltd. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -39,58 +40,14 @@ struct clickdot { struct display *display; struct window *window; struct widget *widget; - struct window *menu; - int32_t width; - - struct { - double current; - double target; - double previous; - } height; - struct wl_callback *frame_callback; -}; - -static void -frame_callback(void *data, struct wl_callback *callback, uint32_t time) -{ - struct clickdot *clickdot = data; - double force, height; - - assert(!callback || callback == clickdot->frame_callback); - height = clickdot->height.current; - force = (clickdot->height.target - height) / 10.0 + - (clickdot->height.previous - height); - - clickdot->height.current = - height + (height - clickdot->height.previous) + force; - clickdot->height.previous = height; - - if (clickdot->height.current >= 400) { - clickdot->height.current = 400; - clickdot->height.previous = 400; - } - - if (clickdot->height.current <= 200) { - clickdot->height.current = 200; - clickdot->height.previous = 200; - } - - widget_schedule_resize(clickdot->widget, clickdot->width, height + 0.5); - - if (clickdot->frame_callback) { - wl_callback_destroy(clickdot->frame_callback); - clickdot->frame_callback = NULL; - } -} - -static const struct wl_callback_listener listener = { - frame_callback + int32_t x, y; }; static void redraw_handler(struct widget *widget, void *data) { + static const double r = 10.0; struct clickdot *clickdot = data; cairo_surface_t *surface; cairo_t *cr; @@ -109,18 +66,20 @@ redraw_handler(struct widget *widget, void *data) allocation.height); cairo_set_source_rgba(cr, 0, 0, 0, 0.8); cairo_fill(cr); - cairo_destroy(cr); - cairo_surface_destroy(surface); + cairo_translate(cr, clickdot->x + 0.5, clickdot->y + 0.5); + cairo_set_line_width(cr, 1.0); + cairo_set_source_rgb(cr, 0.1, 0.9, 0.9); + cairo_move_to(cr, 0.0, -r); + cairo_line_to(cr, 0.0, r); + cairo_move_to(cr, -r, 0.0); + cairo_line_to(cr, r, 0.0); + cairo_arc(cr, 0.0, 0.0, r, 0.0, 2.0 * M_PI); + cairo_stroke(cr); - if (fabs(clickdot->height.previous - clickdot->height.target) > 0.1) { - clickdot->frame_callback = - wl_surface_frame( - window_get_wl_surface(clickdot->window)); - wl_callback_add_listener(clickdot->frame_callback, &listener, - clickdot); - } + cairo_destroy(cr); + cairo_surface_destroy(surface); } static void @@ -142,39 +101,12 @@ key_handler(struct window *window, struct input *input, uint32_t time, return; switch (sym) { - case XK_Down: - clickdot->height.target = 400; - frame_callback(clickdot, NULL, 0); - break; - case XK_Up: - clickdot->height.target = 200; - frame_callback(clickdot, NULL, 0); - break; case XK_Escape: display_exit(clickdot->display); break; } } -static void -menu_func(struct window *window, int index, void *user_data) -{ - fprintf(stderr, "picked entry %d\n", index); -} - -static void -show_menu(struct clickdot *clickdot, struct input *input, uint32_t time) -{ - int32_t x, y; - static const char *entries[] = { - "Roy", "Pris", "Leon", "Zhora" - }; - - input_get_position(input, &x, &y); - window_show_menu(clickdot->display, input, time, clickdot->window, - x - 10, y - 10, menu_func, entries, 4); -} - static void button_handler(struct widget *widget, struct input *input, uint32_t time, @@ -182,19 +114,16 @@ button_handler(struct widget *widget, { struct clickdot *clickdot = data; - switch (button) { - case BTN_RIGHT: - if (state) - show_menu(clickdot, input, time); - break; - } + if (state && button == BTN_LEFT) + input_get_position(input, &clickdot->x, &clickdot->y); + + widget_schedule_redraw(widget); } static struct clickdot * clickdot_create(struct display *display) { struct clickdot *clickdot; - int32_t height; clickdot = malloc(sizeof *clickdot); if (clickdot == NULL) @@ -203,24 +132,20 @@ clickdot_create(struct display *display) clickdot->window = window_create(display, 500, 400); clickdot->widget = frame_create(clickdot->window, clickdot); - window_set_title(clickdot->window, "Wayland Resizor"); + window_set_title(clickdot->window, "Wayland ClickDot"); clickdot->display = display; window_set_key_handler(clickdot->window, key_handler); window_set_user_data(clickdot->window, clickdot); - widget_set_redraw_handler(clickdot->widget, redraw_handler); window_set_keyboard_focus_handler(clickdot->window, keyboard_focus_handler); - clickdot->width = 300; - clickdot->height.current = 400; - clickdot->height.previous = clickdot->height.current; - clickdot->height.target = clickdot->height.current; - height = clickdot->height.current + 0.5; - + widget_set_redraw_handler(clickdot->widget, redraw_handler); widget_set_button_handler(clickdot->widget, button_handler); - widget_schedule_resize(clickdot->widget, clickdot->width, height); + widget_schedule_resize(clickdot->widget, 500, 400); + clickdot->x = 250; + clickdot->y = 200; return clickdot; } @@ -228,9 +153,6 @@ clickdot_create(struct display *display) static void clickdot_destroy(struct clickdot *clickdot) { - if (clickdot->frame_callback) - wl_callback_destroy(clickdot->frame_callback); - widget_destroy(clickdot->widget); window_destroy(clickdot->window); free(clickdot); From 2a5ceccd57839dc9b23c4c9ca3862b9cc0762829 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 20 Jan 2012 14:24:25 +0200 Subject: [PATCH 11/30] compositor: apply full transformation to input coordinates When converting input coordinates from global to surface-local system, apply the full inverse surface transformation instead of just translation. Move weston_surface_update_transform() implementation realier in the file, no changes. Signed-off-by: Pekka Paalanen --- src/compositor.c | 61 +++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/src/compositor.c b/src/compositor.c index 69ad60e4..7b1b0f4d 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -227,6 +227,32 @@ weston_surface_set_color(struct weston_surface *surface, surface->shader = &surface->compositor->solid_shader; } +static void +weston_surface_update_transform(struct weston_surface *surface) +{ + struct weston_matrix *matrix = &surface->transform.matrix; + struct weston_matrix *inverse = &surface->transform.inverse; + struct weston_transform *tform; + + if (!surface->transform.dirty) + return; + + surface->transform.dirty = 0; + + if (wl_list_empty(&surface->transform.list)) { + surface->transform.enabled = 0; + return; + } + + surface->transform.enabled = 1; + + weston_matrix_init(matrix); + wl_list_for_each(tform, &surface->transform.list, link) + weston_matrix_multiply(matrix, &tform->matrix); + + weston_matrix_invert(inverse, matrix); +} + WL_EXPORT void weston_surface_damage_rectangle(struct weston_surface *surface, int32_t x, int32_t y, @@ -310,6 +336,15 @@ static void weston_surface_transform(struct weston_surface *surface, int32_t x, int32_t y, int32_t *sx, int32_t *sy) { + weston_surface_update_transform(surface); + + if (surface->transform.enabled) { + struct weston_vector v = { { x, y, 0.0f, 1.0f } }; + weston_matrix_transform(&surface->transform.inverse, &v); + x = floorf(v.f[0] / v.f[3]); + y = floorf(v.f[1] / v.f[3]); + } + *sx = x - surface->x; *sy = y - surface->y; } @@ -541,32 +576,6 @@ texture_transformed_surface(struct weston_surface *es) return 1; } -static void -weston_surface_update_transform(struct weston_surface *surface) -{ - struct weston_matrix *matrix = &surface->transform.matrix; - struct weston_matrix *inverse = &surface->transform.inverse; - struct weston_transform *tform; - - if (!surface->transform.dirty) - return; - - surface->transform.dirty = 0; - - if (wl_list_empty(&surface->transform.list)) { - surface->transform.enabled = 0; - return; - } - - surface->transform.enabled = 1; - - weston_matrix_init(matrix); - wl_list_for_each(tform, &surface->transform.list, link) - weston_matrix_multiply(matrix, &tform->matrix); - - weston_matrix_invert(inverse, matrix); -} - WL_EXPORT void weston_surface_draw(struct weston_surface *es, struct weston_output *output) { From 460099f143f5e322be5381a4df241afa245c15eb Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 20 Jan 2012 16:48:25 +0200 Subject: [PATCH 12/30] shell: add key binding for rotating a surface Add the key binding Super+Alt+MouseLeftButton to start rotating a surface by dragging. The rotation is removed, when the drag is near the rotation origin. Rotated surface are a stress test for input event coordinate transformations, damage region tracking, draw transformations, and window move and resize orientation. Signed-off-by: Pekka Paalanen --- src/compositor.c | 2 +- src/compositor.h | 3 ++ src/shell.c | 132 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+), 1 deletion(-) diff --git a/src/compositor.c b/src/compositor.c index 7b1b0f4d..02358b25 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -227,7 +227,7 @@ weston_surface_set_color(struct weston_surface *surface, surface->shader = &surface->compositor->solid_shader; } -static void +WL_EXPORT void weston_surface_update_transform(struct weston_surface *surface) { struct weston_matrix *matrix = &surface->transform.matrix; diff --git a/src/compositor.h b/src/compositor.h index 53126651..580800db 100644 --- a/src/compositor.h +++ b/src/compositor.h @@ -250,6 +250,9 @@ struct weston_surface { struct wl_listener buffer_destroy_listener; }; +void +weston_surface_update_transform(struct weston_surface *surface); + void weston_device_repick(struct wl_input_device *device, uint32_t time); diff --git a/src/shell.c b/src/shell.c index 92196994..49b0b109 100644 --- a/src/shell.c +++ b/src/shell.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include "compositor.h" @@ -93,6 +94,10 @@ struct shell_surface { enum shell_surface_type type; int32_t saved_x, saved_y; + struct { + struct weston_transform transform; + } rotation; + struct { struct wl_grab grab; uint32_t time; @@ -110,6 +115,15 @@ struct weston_move_grab { int32_t dx, dy; }; +struct rotate_grab { + struct wl_grab grab; + struct shell_surface *surface; + struct { + int32_t x; + int32_t y; + } center; +}; + static void shell_configuration(struct wl_shell *shell) { @@ -621,6 +635,9 @@ shell_get_shell_surface(struct wl_client *client, /* init link so its safe to always remove it in destroy_shell_surface */ wl_list_init(&shsurf->link); + /* empty when not in use */ + wl_list_init(&shsurf->rotation.transform.link); + shsurf->type = SHELL_SURFACE_NONE; wl_client_add_resource(client, &shsurf->resource); @@ -946,6 +963,118 @@ terminate_binding(struct wl_input_device *device, uint32_t time, wl_display_terminate(compositor->wl_display); } +static void +rotate_grab_motion(struct wl_grab *grab, + uint32_t time, int32_t x, int32_t y) +{ + struct rotate_grab *rotate = + container_of(grab, struct rotate_grab, grab); + struct wl_input_device *device = grab->input_device; + struct shell_surface *surface = rotate->surface; + GLfloat dx, dy; + GLfloat r; + + dx = device->x - rotate->center.x; + dy = device->y - rotate->center.y; + r = sqrtf(dx * dx + dy * dy); + + wl_list_remove(&surface->rotation.transform.link); + surface->surface->transform.dirty = 1; + + if (r > 20.0f) { + struct weston_matrix roto; + struct weston_matrix *matrix = + &surface->rotation.transform.matrix; + + weston_matrix_init(&roto); + roto.d[0] = dx / r; + roto.d[4] = -dy / r; + roto.d[1] = -roto.d[4]; + roto.d[5] = roto.d[0]; + + weston_matrix_init(matrix); + weston_matrix_translate(matrix, -rotate->center.x, + -rotate->center.y, 0.0f); + weston_matrix_multiply(matrix, &roto); + weston_matrix_translate(matrix, rotate->center.x, + rotate->center.y, 0.0f); + + wl_list_insert(surface->surface->transform.list.prev, + &surface->rotation.transform.link); + } else { + wl_list_init(&surface->rotation.transform.link); + } + + weston_compositor_damage_all(surface->surface->compositor); +} + +static void +rotate_grab_button(struct wl_grab *grab, + uint32_t time, int32_t button, int32_t state) +{ + struct rotate_grab *rotate = + container_of(grab, struct rotate_grab, grab); + struct wl_input_device *device = grab->input_device; + + if (device->button_count == 0 && state == 0) { + wl_input_device_end_grab(device, time); + free(rotate); + } +} + +static const struct wl_grab_interface rotate_grab_interface = { + noop_grab_focus, + rotate_grab_motion, + rotate_grab_button, +}; + +static void +rotate_binding(struct wl_input_device *device, uint32_t time, + uint32_t key, uint32_t button, uint32_t state, void *data) +{ + struct weston_surface *base_surface = + (struct weston_surface *) device->pointer_focus; + struct shell_surface *surface; + struct rotate_grab *rotate; + struct weston_vector center = { { 0.0f, 0.0f, 0.0f, 1.0f } }; + + if (base_surface == NULL) + return; + + surface = get_shell_surface(base_surface); + if (!surface) + return; + + switch (surface->type) { + case SHELL_SURFACE_PANEL: + case SHELL_SURFACE_BACKGROUND: + case SHELL_SURFACE_FULLSCREEN: + case SHELL_SURFACE_SCREENSAVER: + return; + default: + break; + } + + center.f[0] = 0.5f * surface->surface->width; + center.f[1] = 0.5f * surface->surface->height; + weston_surface_update_transform(surface->surface); + weston_matrix_transform(&surface->surface->transform.matrix, ¢er); + if (fabsf(center.f[3]) < 1e-6) + return; + + rotate = malloc(sizeof *rotate); + if (!rotate) + return; + + rotate->grab.interface = &rotate_grab_interface; + rotate->surface = surface; + rotate->center.x = center.f[0] / center.f[3]; + rotate->center.y = center.f[1] / center.f[3]; + + wl_input_device_start_grab(device, &rotate->grab, time); + wl_input_device_set_pointer_focus(device, NULL, time, 0, 0, 0, 0); +} + static void activate(struct weston_shell *base, struct weston_surface *es, struct weston_input_device *device, uint32_t time) @@ -1474,6 +1603,9 @@ shell_init(struct weston_compositor *ec) terminate_binding, ec); weston_compositor_add_binding(ec, 0, BTN_LEFT, 0, click_to_activate_binding, ec); + weston_compositor_add_binding(ec, 0, BTN_LEFT, + MODIFIER_SUPER | MODIFIER_ALT, + rotate_binding, NULL); ec->shell = &shell->shell; From 7ae02130bdc11d96613ab8ceb3ceb2527516fd58 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 23 Jan 2012 10:02:47 +0200 Subject: [PATCH 13/30] compositor: handle non-invertible surface transformations Detect a non-invertible surface total transformation, disable it, and warn about it. Signed-off-by: Pekka Paalanen --- src/compositor.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/compositor.c b/src/compositor.c index 02358b25..3950ad7a 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -250,7 +250,12 @@ weston_surface_update_transform(struct weston_surface *surface) wl_list_for_each(tform, &surface->transform.list, link) weston_matrix_multiply(matrix, &tform->matrix); - weston_matrix_invert(inverse, matrix); + if (weston_matrix_invert(inverse, matrix) < 0) { + /* Oops, bad total transformation, not invertible */ + surface->transform.enabled = 0; + fprintf(stderr, "error: weston_surface %p" + " transformation not invertible.\n", surface); + } } WL_EXPORT void From bc0b7e7756fe3dec91ca9f3ab3148ca527a669a9 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 24 Jan 2012 09:53:37 +0200 Subject: [PATCH 14/30] compositor: restructure weston_surface::transform Separate mutable data and cached immutable data in struct weston_surface. Signed-off-by: Pekka Paalanen --- src/compositor.c | 12 ++++++------ src/compositor.h | 19 ++++++++++++++++--- src/shell.c | 7 ++++--- src/util.c | 7 ++++--- 4 files changed, 30 insertions(+), 15 deletions(-) diff --git a/src/compositor.c b/src/compositor.c index 3950ad7a..3330ffba 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -205,8 +205,8 @@ weston_surface_create(struct weston_compositor *compositor, surface->buffer_destroy_listener.func = surface_handle_buffer_destroy; - wl_list_init(&surface->transform.list); - surface->transform.dirty = 1; + wl_list_init(&surface->geometry.transformation_list); + surface->geometry.dirty = 1; return surface; } @@ -234,12 +234,12 @@ weston_surface_update_transform(struct weston_surface *surface) struct weston_matrix *inverse = &surface->transform.inverse; struct weston_transform *tform; - if (!surface->transform.dirty) + if (!surface->geometry.dirty) return; - surface->transform.dirty = 0; + surface->geometry.dirty = 0; - if (wl_list_empty(&surface->transform.list)) { + if (wl_list_empty(&surface->geometry.transformation_list)) { surface->transform.enabled = 0; return; } @@ -247,7 +247,7 @@ weston_surface_update_transform(struct weston_surface *surface) surface->transform.enabled = 1; weston_matrix_init(matrix); - wl_list_for_each(tform, &surface->transform.list, link) + wl_list_for_each(tform, &surface->geometry.transformation_list, link) weston_matrix_multiply(matrix, &tform->matrix); if (weston_matrix_invert(inverse, matrix) < 0) { diff --git a/src/compositor.h b/src/compositor.h index 580800db..7555e852 100644 --- a/src/compositor.h +++ b/src/compositor.h @@ -224,14 +224,27 @@ struct weston_surface { uint32_t visual; int overlapped; + /* Surface geometry state, mutable. + * If you change anything, set dirty = 1. + * That includes the transformations referenced from the list. + */ struct { - struct wl_list list; + /* struct weston_transform */ + struct wl_list transformation_list; + int dirty; + } geometry; - /* derived state, set up by weston_surface_update_transform */ + /* State derived from geometry state, read-only. + * This is updated by weston_surface_update_transform(). + */ + struct { + /* matrix and inverse are used only if enabled = 1. + * If enabled = 0, use x, y, width, height directly. + */ + int enabled; struct weston_matrix matrix; struct weston_matrix inverse; - int enabled; } transform; /* diff --git a/src/shell.c b/src/shell.c index 49b0b109..d6e0205d 100644 --- a/src/shell.c +++ b/src/shell.c @@ -979,7 +979,7 @@ rotate_grab_motion(struct wl_grab *grab, r = sqrtf(dx * dx + dy * dy); wl_list_remove(&surface->rotation.transform.link); - surface->surface->transform.dirty = 1; + surface->surface->geometry.dirty = 1; if (r > 20.0f) { struct weston_matrix roto; @@ -999,8 +999,9 @@ rotate_grab_motion(struct wl_grab *grab, weston_matrix_translate(matrix, rotate->center.x, rotate->center.y, 0.0f); - wl_list_insert(surface->surface->transform.list.prev, - &surface->rotation.transform.link); + wl_list_insert( + surface->surface->geometry.transformation_list.prev, + &surface->rotation.transform.link); } else { wl_list_init(&surface->rotation.transform.link); } diff --git a/src/util.c b/src/util.c index 6b8477a4..c749f29d 100644 --- a/src/util.c +++ b/src/util.c @@ -99,7 +99,7 @@ weston_zoom_destroy(struct weston_zoom *zoom) wl_list_remove(&zoom->animation.link); wl_list_remove(&zoom->listener.link); wl_list_remove(&zoom->transform.link); - zoom->surface->transform.dirty = 1; + zoom->surface->geometry.dirty = 1; if (zoom->done) zoom->done(zoom, zoom->data); free(zoom); @@ -146,7 +146,7 @@ weston_zoom_frame(struct weston_animation *animation, if (es->alpha > 255) es->alpha = 255; - zoom->surface->transform.dirty = 1; + zoom->surface->geometry.dirty = 1; weston_compositor_damage_all(es->compositor); } @@ -166,7 +166,8 @@ weston_zoom_run(struct weston_surface *surface, GLfloat start, GLfloat stop, zoom->data = data; zoom->start = start; zoom->stop = stop; - wl_list_insert(&surface->transform.list, &zoom->transform.link); + wl_list_insert(&surface->geometry.transformation_list, + &zoom->transform.link); weston_spring_init(&zoom->spring, 200.0, 0.0, 1.0); zoom->spring.friction = 700; zoom->spring.timestamp = weston_compositor_get_time(); From e0f3cb25e8f1c251891abb203ed3707f810a35d2 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 24 Jan 2012 09:59:29 +0200 Subject: [PATCH 15/30] compositor: rewrite draw and input coordinate transformations For unifying the coordinate system handling, introduce functions for converting explicitly between the global and the surface local coordinate systems. Use these functions in the input path, replacing weston_surface_transform(). In the draw path, rewrite transform_vertex() to take in surface local coordinates. As shell now uses the new functions, the rotation origin is properly placed in the middle of the surface. Signed-off-by: Pekka Paalanen --- src/compositor.c | 123 ++++++++++++++++++++++++++++++++--------------- src/compositor.h | 8 +++ src/shell.c | 15 ++---- 3 files changed, 97 insertions(+), 49 deletions(-) diff --git a/src/compositor.c b/src/compositor.c index 3330ffba..cc77747a 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -258,6 +258,65 @@ weston_surface_update_transform(struct weston_surface *surface) } } +WL_EXPORT void +weston_surface_to_global(struct weston_surface *surface, + int32_t sx, int32_t sy, int32_t *x, int32_t *y) +{ + weston_surface_update_transform(surface); + + if (surface->transform.enabled) { + struct weston_vector v = { { sx, sy, 0.0f, 1.0f } }; + + v.f[0] += surface->x; + v.f[1] += surface->y; + + weston_matrix_transform(&surface->transform.matrix, &v); + + if (fabsf(v.f[3]) < 1e-6) { + fprintf(stderr, "warning: numerical instability in " + "weston_surface_to_global(), divisor = %g\n", + v.f[3]); + *x = 0; + *y = 0; + return; + } + + *x = floorf(v.f[0] / v.f[3]); + *y = floorf(v.f[1] / v.f[3]); + } else { + *x = sx + surface->x; + *y = sy + surface->y; + } +} + +WL_EXPORT void +weston_surface_from_global(struct weston_surface *surface, + int32_t x, int32_t y, int32_t *sx, int32_t *sy) +{ + weston_surface_update_transform(surface); + + if (surface->transform.enabled) { + struct weston_vector v = { { x, y, 0.0f, 1.0f } }; + + weston_matrix_transform(&surface->transform.inverse, &v); + + if (fabsf(v.f[3]) < 1e-6) { + fprintf(stderr, "warning: numerical instability in " + "weston_surface_from_global(), divisor = %g\n", + v.f[3]); + *sx = 0; + *sy = 0; + return; + } + + *sx = floorf(v.f[0] / v.f[3] - surface->x); + *sy = floorf(v.f[1] / v.f[3] - surface->y); + } else { + *sx = x - surface->x; + *sy = y - surface->y; + } +} + WL_EXPORT void weston_surface_damage_rectangle(struct weston_surface *surface, int32_t x, int32_t y, @@ -337,23 +396,6 @@ weston_surface_configure(struct weston_surface *surface, pixman_region32_init(&surface->opaque); } -static void -weston_surface_transform(struct weston_surface *surface, - int32_t x, int32_t y, int32_t *sx, int32_t *sy) -{ - weston_surface_update_transform(surface); - - if (surface->transform.enabled) { - struct weston_vector v = { { x, y, 0.0f, 1.0f } }; - weston_matrix_transform(&surface->transform.inverse, &v); - x = floorf(v.f[0] / v.f[3]); - y = floorf(v.f[1] / v.f[3]); - } - - *sx = x - surface->x; - *sy = y - surface->y; -} - WL_EXPORT uint32_t weston_compositor_get_time(void) { @@ -385,8 +427,8 @@ weston_device_repick(struct wl_input_device *device, uint32_t time) focus = (struct weston_surface *) device->grab->focus; if (focus) - weston_surface_transform(focus, device->x, device->y, - &device->grab->x, &device->grab->y); + weston_surface_from_global(focus, device->x, device->y, + &device->grab->x, &device->grab->y); } WL_EXPORT void @@ -536,23 +578,27 @@ texture_region(struct weston_surface *es, pixman_region32_t *region) static void transform_vertex(struct weston_surface *surface, - GLfloat x, GLfloat y, GLfloat u, GLfloat v, GLfloat *r) + GLfloat sx, GLfloat sy, + GLfloat tex_x, GLfloat tex_y, GLfloat *r) { - struct weston_vector t; + /* surface->transform.enabled must always be 1 here. */ - t.f[0] = x; - t.f[1] = y; - t.f[2] = 0.0; - t.f[3] = 1.0; + struct weston_vector v = { { sx, sy, 0.0f, 1.0f } }; - weston_matrix_transform(&surface->transform.matrix, &t); + v.f[0] += surface->x; + v.f[1] += surface->y; - /* XXX: assumes last row of matrix is [0 0 * 1] */ + weston_matrix_transform(&surface->transform.matrix, &v); + + if (fabsf(v.f[3]) < 1e-6) { + fprintf(stderr, "warning: numerical instability in " + "transform_vertex(), divisor = %g\n", v.f[3]); + } - r[ 0] = t.f[0]; - r[ 1] = t.f[1]; - r[ 2] = u; - r[ 3] = v; + r[ 0] = v.f[0] / v.f[3]; + r[ 1] = v.f[1] / v.f[3]; + r[ 2] = tex_x; + r[ 3] = tex_y; } static int @@ -565,11 +611,10 @@ texture_transformed_surface(struct weston_surface *es) v = wl_array_add(&ec->vertices, 16 * sizeof *v); p = wl_array_add(&ec->indices, 6 * sizeof *p); - transform_vertex(es, es->x, es->y, 0.0, 0.0, &v[0]); - transform_vertex(es, es->x, es->y + es->height, 0.0, 1.0, &v[4]); - transform_vertex(es, es->x + es->width, es->y, 1.0, 0.0, &v[8]); - transform_vertex(es, es->x + es->width, es->y + es->height, - 1.0, 1.0, &v[12]); + transform_vertex(es, 0, 0, 0.0, 0.0, &v[0]); + transform_vertex(es, 0, es->height, 0.0, 1.0, &v[4]); + transform_vertex(es, es->width, 0, 1.0, 0.0, &v[8]); + transform_vertex(es, es->width, es->height, 1.0, 1.0, &v[12]); p[0] = 0; p[1] = 1; @@ -1096,7 +1141,7 @@ weston_compositor_pick_surface(struct weston_compositor *compositor, wl_list_for_each(surface, &compositor->surface_list, link) { if (surface->surface.resource.client == NULL) continue; - weston_surface_transform(surface, x, y, sx, sy); + weston_surface_from_global(surface, x, y, sx, sy); if (0 <= *sx && *sx < surface->width && 0 <= *sy && *sy < surface->height) return surface; @@ -1484,7 +1529,7 @@ notify_touch(struct wl_input_device *device, uint32_t time, int touch_id, touch_set_focus(wd, &es->surface, time); } else if (wd->touch_focus) { es = (struct weston_surface *) wd->touch_focus; - weston_surface_transform(es, x, y, &sx, &sy); + weston_surface_from_global(es, x, y, &sx, &sy); } if (wd->touch_focus_resource && wd->touch_focus) @@ -1498,7 +1543,7 @@ notify_touch(struct wl_input_device *device, uint32_t time, int touch_id, if (!es) break; - weston_surface_transform(es, x, y, &sx, &sy); + weston_surface_from_global(es, x, y, &sx, &sy); if (wd->touch_focus_resource) wl_resource_post_event(wd->touch_focus_resource, touch_type, time, diff --git a/src/compositor.h b/src/compositor.h index 7555e852..76239213 100644 --- a/src/compositor.h +++ b/src/compositor.h @@ -266,6 +266,14 @@ struct weston_surface { void weston_surface_update_transform(struct weston_surface *surface); +void +weston_surface_to_global(struct weston_surface *surface, + int32_t sx, int32_t sy, int32_t *x, int32_t *y); + +void +weston_surface_from_global(struct weston_surface *surface, + int32_t x, int32_t y, int32_t *sx, int32_t *sy); + void weston_device_repick(struct wl_input_device *device, uint32_t time); diff --git a/src/shell.c b/src/shell.c index d6e0205d..d2b23794 100644 --- a/src/shell.c +++ b/src/shell.c @@ -1037,7 +1037,6 @@ rotate_binding(struct wl_input_device *device, uint32_t time, (struct weston_surface *) device->pointer_focus; struct shell_surface *surface; struct rotate_grab *rotate; - struct weston_vector center = { { 0.0f, 0.0f, 0.0f, 1.0f } }; if (base_surface == NULL) return; @@ -1056,21 +1055,17 @@ rotate_binding(struct wl_input_device *device, uint32_t time, break; } - center.f[0] = 0.5f * surface->surface->width; - center.f[1] = 0.5f * surface->surface->height; - weston_surface_update_transform(surface->surface); - weston_matrix_transform(&surface->surface->transform.matrix, ¢er); - if (fabsf(center.f[3]) < 1e-6) - return; - rotate = malloc(sizeof *rotate); if (!rotate) return; rotate->grab.interface = &rotate_grab_interface; rotate->surface = surface; - rotate->center.x = center.f[0] / center.f[3]; - rotate->center.y = center.f[1] / center.f[3]; + + weston_surface_to_global(surface->surface, + surface->surface->width / 2, + surface->surface->height / 2, + &rotate->center.x, &rotate->center.y); wl_input_device_start_grab(device, &rotate->grab, time); wl_input_device_set_pointer_focus(device, NULL, time, 0, 0, 0, 0); From 74abeac2f617aa7715a9df5fb1be7f1d52d6ea02 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 24 Jan 2012 14:50:14 +0200 Subject: [PATCH 16/30] compositor: remove unused *_uniform members Signed-off-by: Pekka Paalanen --- src/compositor.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/compositor.h b/src/compositor.h index 76239213..be1740bf 100644 --- a/src/compositor.h +++ b/src/compositor.h @@ -146,7 +146,6 @@ struct weston_compositor { EGLContext context; EGLConfig config; GLuint fbo; - GLuint proj_uniform, tex_uniform, alpha_uniform; uint32_t current_alpha; struct weston_shader texture_shader; struct weston_shader solid_shader; From 3df327f4c99afdc97a0850f80f2d3b9538797722 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 24 Jan 2012 15:53:35 +0200 Subject: [PATCH 17/30] compositor: disable attrib arrays after use Not strictly necessary right now, but nice to clean up. Signed-off-by: Pekka Paalanen --- src/compositor.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/compositor.c b/src/compositor.c index cc77747a..d8d78ceb 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -690,8 +690,12 @@ weston_surface_draw(struct weston_surface *es, struct weston_output *output) glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof *v, &v[2]); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); + glDrawElements(GL_TRIANGLES, n * 6, GL_UNSIGNED_INT, ec->indices.data); + glDisableVertexAttribArray(1); + glDisableVertexAttribArray(0); + ec->vertices.size = 0; ec->indices.size = 0; pixman_region32_fini(&repaint); From 0e151bb487ab55bef96feeec7623b791c316fc91 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 24 Jan 2012 14:47:37 +0200 Subject: [PATCH 18/30] compositor: honour repaint regions with transformed surfaces Previously, if a surface was transformed, it repainted as a whole, regardless of the computed repaint region. As damage regions determine repaint regions and whether a surface is considered for drawing at all, this lead to disappearing surfaces if all surfaces were considered transformed. Also transparent transformed surfaces were redrawn without the surfaces below being properly redrawn, leading to alpha-saturation. Fix that by making texture_region() use the proper global-to-surface coordinate transformation for texture coordinates. This makes it possible to call texture_region() also for transformed surfaces. As texture coordinates may now lie outside the valid texture image, the fragment shader is modified to check the fragment texture coordinates. The special path texture_transformed_surface() is no longer used and is removed. This change fixes many of the rendering artifacts related to transformed surfaces. Signed-off-by: Pekka Paalanen --- src/compositor.c | 119 +++++++++++++++++++---------------------------- src/compositor.h | 1 + 2 files changed, 50 insertions(+), 70 deletions(-) diff --git a/src/compositor.c b/src/compositor.c index d8d78ceb..f2fa7899 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -289,12 +289,10 @@ weston_surface_to_global(struct weston_surface *surface, } } -WL_EXPORT void -weston_surface_from_global(struct weston_surface *surface, - int32_t x, int32_t y, int32_t *sx, int32_t *sy) +static void +surface_from_global_float(struct weston_surface *surface, + int32_t x, int32_t y, GLfloat *sx, GLfloat *sy) { - weston_surface_update_transform(surface); - if (surface->transform.enabled) { struct weston_vector v = { { x, y, 0.0f, 1.0f } }; @@ -309,14 +307,27 @@ weston_surface_from_global(struct weston_surface *surface, return; } - *sx = floorf(v.f[0] / v.f[3] - surface->x); - *sy = floorf(v.f[1] / v.f[3] - surface->y); + *sx = v.f[0] / v.f[3] - surface->x; + *sy = v.f[1] / v.f[3] - surface->y; } else { *sx = x - surface->x; *sy = y - surface->y; } } +WL_EXPORT void +weston_surface_from_global(struct weston_surface *surface, + int32_t x, int32_t y, int32_t *sx, int32_t *sy) +{ + GLfloat sxf, syf; + + weston_surface_update_transform(surface); + + surface_from_global_float(surface, x, y, &sxf, &syf); + *sx = floorf(sxf); + *sy = floorf(syf); +} + WL_EXPORT void weston_surface_damage_rectangle(struct weston_surface *surface, int32_t x, int32_t y, @@ -534,6 +545,7 @@ texture_region(struct weston_surface *es, pixman_region32_t *region) { struct weston_compositor *ec = es->compositor; GLfloat *v, inv_width, inv_height; + GLfloat sx, sy; pixman_box32_t *rectangles; unsigned int *p; int i, n; @@ -545,25 +557,33 @@ texture_region(struct weston_surface *es, pixman_region32_t *region) inv_height = 1.0 / es->height; for (i = 0; i < n; i++, v += 16, p += 6) { + surface_from_global_float(es, rectangles[i].x1, + rectangles[i].y1, &sx, &sy); v[ 0] = rectangles[i].x1; v[ 1] = rectangles[i].y1; - v[ 2] = (GLfloat) (rectangles[i].x1 - es->x) * inv_width; - v[ 3] = (GLfloat) (rectangles[i].y1 - es->y) * inv_height; + v[ 2] = sx * inv_width; + v[ 3] = sy * inv_height; + surface_from_global_float(es, rectangles[i].x1, + rectangles[i].y2, &sx, &sy); v[ 4] = rectangles[i].x1; v[ 5] = rectangles[i].y2; - v[ 6] = v[ 2]; - v[ 7] = (GLfloat) (rectangles[i].y2 - es->y) * inv_height; + v[ 6] = sx * inv_width; + v[ 7] = sy * inv_height; + surface_from_global_float(es, rectangles[i].x2, + rectangles[i].y1, &sx, &sy); v[ 8] = rectangles[i].x2; v[ 9] = rectangles[i].y1; - v[10] = (GLfloat) (rectangles[i].x2 - es->x) * inv_width; - v[11] = v[ 3]; + v[10] = sx * inv_width; + v[11] = sy * inv_height; + surface_from_global_float(es, rectangles[i].x2, + rectangles[i].y2, &sx, &sy); v[12] = rectangles[i].x2; v[13] = rectangles[i].y2; - v[14] = v[10]; - v[15] = v[ 7]; + v[14] = sx * inv_width; + v[15] = sy * inv_height; p[0] = i * 4 + 0; p[1] = i * 4 + 1; @@ -576,56 +596,6 @@ texture_region(struct weston_surface *es, pixman_region32_t *region) return n; } -static void -transform_vertex(struct weston_surface *surface, - GLfloat sx, GLfloat sy, - GLfloat tex_x, GLfloat tex_y, GLfloat *r) -{ - /* surface->transform.enabled must always be 1 here. */ - - struct weston_vector v = { { sx, sy, 0.0f, 1.0f } }; - - v.f[0] += surface->x; - v.f[1] += surface->y; - - weston_matrix_transform(&surface->transform.matrix, &v); - - if (fabsf(v.f[3]) < 1e-6) { - fprintf(stderr, "warning: numerical instability in " - "transform_vertex(), divisor = %g\n", v.f[3]); - } - - r[ 0] = v.f[0] / v.f[3]; - r[ 1] = v.f[1] / v.f[3]; - r[ 2] = tex_x; - r[ 3] = tex_y; -} - -static int -texture_transformed_surface(struct weston_surface *es) -{ - struct weston_compositor *ec = es->compositor; - GLfloat *v; - unsigned int *p; - - v = wl_array_add(&ec->vertices, 16 * sizeof *v); - p = wl_array_add(&ec->indices, 6 * sizeof *p); - - transform_vertex(es, 0, 0, 0.0, 0.0, &v[0]); - transform_vertex(es, 0, es->height, 0.0, 1.0, &v[4]); - transform_vertex(es, es->width, 0, 1.0, 0.0, &v[8]); - transform_vertex(es, es->width, es->height, 1.0, 1.0, &v[12]); - - p[0] = 0; - p[1] = 1; - p[2] = 2; - p[3] = 2; - p[4] = 1; - p[5] = 3; - - return 1; -} - WL_EXPORT void weston_surface_draw(struct weston_surface *es, struct weston_output *output) { @@ -672,14 +642,17 @@ weston_surface_draw(struct weston_surface *es, struct weston_output *output) ec->current_alpha = es->alpha; } + if (es->shader->texwidth_uniform != GL_NONE) + glUniform1f(es->shader->texwidth_uniform, + (GLfloat)es->width / es->pitch); + weston_surface_update_transform(es); - if (es->transform.enabled) { + if (es->transform.enabled) filter = GL_LINEAR; - n = texture_transformed_surface(es); - } else { + else filter = GL_NEAREST; - n = texture_region(es, &repaint); - } + + n = texture_region(es, &repaint); glBindTexture(GL_TEXTURE_2D, es->texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); @@ -1717,8 +1690,12 @@ static const char texture_fragment_shader[] = "varying vec2 v_texcoord;\n" "uniform sampler2D tex;\n" "uniform float alpha;\n" + "uniform float texwidth;\n" "void main()\n" "{\n" + " if (v_texcoord.x < 0.0 || v_texcoord.x > texwidth ||\n" + " v_texcoord.y < 0.0 || v_texcoord.y > 1.0)\n" + " discard;\n" " gl_FragColor = texture2D(tex, v_texcoord)\n;" " gl_FragColor = alpha * gl_FragColor;\n" "}\n"; @@ -1780,6 +1757,8 @@ weston_shader_init(struct weston_shader *shader, shader->proj_uniform = glGetUniformLocation(shader->program, "proj"); shader->tex_uniform = glGetUniformLocation(shader->program, "tex"); shader->alpha_uniform = glGetUniformLocation(shader->program, "alpha"); + shader->texwidth_uniform = glGetUniformLocation(shader->program, + "texwidth"); return 0; } diff --git a/src/compositor.h b/src/compositor.h index be1740bf..d8e9ab88 100644 --- a/src/compositor.h +++ b/src/compositor.h @@ -102,6 +102,7 @@ struct weston_shader { GLuint tex_uniform; GLuint alpha_uniform; GLuint color_uniform; + GLuint texwidth_uniform; }; struct weston_animation { From cd40362bbaf07d3344e740d85910dd67ed20e746 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 25 Jan 2012 13:37:39 +0200 Subject: [PATCH 19/30] compositor: make position a surface transformation Put the surface translation (absolute position) into the surface transformations list. This allows to set additional transformations before and after the global translation. Having the translation cached, changing the surface x,y now requires to set the geometry.dirty flag. Signed-off-by: Pekka Paalanen --- src/compositor.c | 22 ++++++++++++++++------ src/compositor.h | 2 ++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/compositor.c b/src/compositor.c index f2fa7899..27408e7c 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -206,6 +206,9 @@ weston_surface_create(struct weston_compositor *compositor, surface->buffer_destroy_listener.func = surface_handle_buffer_destroy; wl_list_init(&surface->geometry.transformation_list); + wl_list_insert(&surface->geometry.transformation_list, + &surface->transform.position.link); + weston_matrix_init(&surface->transform.position.matrix); surface->geometry.dirty = 1; return surface; @@ -239,13 +242,20 @@ weston_surface_update_transform(struct weston_surface *surface) surface->geometry.dirty = 0; - if (wl_list_empty(&surface->geometry.transformation_list)) { + /* transform.position is always in transformation_list */ + if (surface->geometry.transformation_list.next == + &surface->transform.position.link && + surface->geometry.transformation_list.prev == + &surface->transform.position.link) { surface->transform.enabled = 0; return; } surface->transform.enabled = 1; + surface->transform.position.matrix.d[12] = surface->x; + surface->transform.position.matrix.d[13] = surface->y; + weston_matrix_init(matrix); wl_list_for_each(tform, &surface->geometry.transformation_list, link) weston_matrix_multiply(matrix, &tform->matrix); @@ -267,9 +277,6 @@ weston_surface_to_global(struct weston_surface *surface, if (surface->transform.enabled) { struct weston_vector v = { { sx, sy, 0.0f, 1.0f } }; - v.f[0] += surface->x; - v.f[1] += surface->y; - weston_matrix_transform(&surface->transform.matrix, &v); if (fabsf(v.f[3]) < 1e-6) { @@ -307,8 +314,8 @@ surface_from_global_float(struct weston_surface *surface, return; } - *sx = v.f[0] / v.f[3] - surface->x; - *sy = v.f[1] / v.f[3] - surface->y; + *sx = v.f[0] / v.f[3]; + *sy = v.f[1] / v.f[3]; } else { *sx = x - surface->x; *sy = y - surface->y; @@ -394,6 +401,7 @@ weston_surface_configure(struct weston_surface *surface, surface->y = y; surface->width = width; surface->height = height; + surface->geometry.dirty = 1; weston_surface_assign_output(surface); weston_surface_damage(surface); @@ -1231,6 +1239,7 @@ notify_motion(struct wl_input_device *device, uint32_t time, int x, int y) wd->sprite->x = device->x - wd->hotspot_x; wd->sprite->y = device->y - wd->hotspot_y; + wd->sprite->geometry.dirty = 1; weston_surface_damage(wd->sprite); } @@ -1585,6 +1594,7 @@ input_device_attach(struct wl_client *client, device->sprite->height = buffer->height; device->sprite->x = device->input_device.x - device->hotspot_x; device->sprite->y = device->input_device.y - device->hotspot_y; + device->sprite->geometry.dirty = 1; weston_surface_damage(device->sprite); } diff --git a/src/compositor.h b/src/compositor.h index d8e9ab88..c05e5966 100644 --- a/src/compositor.h +++ b/src/compositor.h @@ -245,6 +245,8 @@ struct weston_surface { int enabled; struct weston_matrix matrix; struct weston_matrix inverse; + + struct weston_transform position; /* matrix from x, y */ } transform; /* From ba3cf95c0eed209feb25ac9b9de92af062630965 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 25 Jan 2012 16:22:05 +0200 Subject: [PATCH 20/30] compositor: move weston_surface::x,y into geometry weston_surface::transform.position depends on x,y, and therefore the dirty flag, so move x and y into geometry. Also add the missing dirty flags. Signed-off-by: Pekka Paalanen --- src/compositor-drm.c | 11 ++++--- src/compositor.c | 55 +++++++++++++++++++-------------- src/compositor.h | 4 ++- src/shell.c | 73 +++++++++++++++++++++++++------------------- src/tablet-shell.c | 23 ++++++++------ src/util.c | 8 ++--- 6 files changed, 101 insertions(+), 73 deletions(-) diff --git a/src/compositor-drm.c b/src/compositor-drm.c index 661ee324..95669b84 100644 --- a/src/compositor-drm.c +++ b/src/compositor-drm.c @@ -97,8 +97,8 @@ drm_output_prepare_scanout_surface(struct drm_output *output) struct weston_surface, link); if (es->visual != WESTON_RGB_VISUAL || - es->x != output->base.x || - es->y != output->base.y || + es->geometry.x != output->base.x || + es->geometry.y != output->base.y || es->width != output->base.current->width || es->height != output->base.current->height || es->image == EGL_NO_IMAGE_KHR) @@ -231,7 +231,8 @@ drm_output_set_cursor(struct weston_output *output_base, } pixman_region32_init_rect(&cursor_region, - eid->sprite->x, eid->sprite->y, + eid->sprite->geometry.x, + eid->sprite->geometry.y, eid->sprite->width, eid->sprite->height); pixman_region32_intersect_rect(&cursor_region, &cursor_region, @@ -272,8 +273,8 @@ drm_output_set_cursor(struct weston_output *output_base, } ret = drmModeMoveCursor(c->drm.fd, output->crtc_id, - eid->sprite->x - output->base.x, - eid->sprite->y - output->base.y); + eid->sprite->geometry.x - output->base.x, + eid->sprite->geometry.y - output->base.y); if (ret) { fprintf(stderr, "failed to move cursor: %s\n", strerror(-ret)); goto out; diff --git a/src/compositor.c b/src/compositor.c index 27408e7c..cee052fb 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -189,8 +189,8 @@ weston_surface_create(struct weston_compositor *compositor, surface->compositor = compositor; surface->visual = WESTON_NONE_VISUAL; surface->image = EGL_NO_IMAGE_KHR; - surface->x = x; - surface->y = y; + surface->geometry.x = x; + surface->geometry.y = y; surface->width = width; surface->height = height; surface->alpha = 255; @@ -253,8 +253,8 @@ weston_surface_update_transform(struct weston_surface *surface) surface->transform.enabled = 1; - surface->transform.position.matrix.d[12] = surface->x; - surface->transform.position.matrix.d[13] = surface->y; + surface->transform.position.matrix.d[12] = surface->geometry.x; + surface->transform.position.matrix.d[13] = surface->geometry.y; weston_matrix_init(matrix); wl_list_for_each(tform, &surface->geometry.transformation_list, link) @@ -291,8 +291,8 @@ weston_surface_to_global(struct weston_surface *surface, *x = floorf(v.f[0] / v.f[3]); *y = floorf(v.f[1] / v.f[3]); } else { - *x = sx + surface->x; - *y = sy + surface->y; + *x = sx + surface->geometry.x; + *y = sy + surface->geometry.y; } } @@ -317,8 +317,8 @@ surface_from_global_float(struct weston_surface *surface, *sx = v.f[0] / v.f[3]; *sy = v.f[1] / v.f[3]; } else { - *sx = x - surface->x; - *sy = y - surface->y; + *sx = x - surface->geometry.x; + *sy = y - surface->geometry.y; } } @@ -344,7 +344,8 @@ weston_surface_damage_rectangle(struct weston_surface *surface, pixman_region32_union_rect(&surface->damage, &surface->damage, - surface->x + x, surface->y + y, + surface->geometry.x + x, + surface->geometry.y + y, width, height); weston_compositor_schedule_repaint(compositor); } @@ -371,7 +372,7 @@ weston_surface_damage_below(struct weston_surface *surface) pixman_region32_union_rect(&below->damage, &below->damage, - surface->x, surface->y, + surface->geometry.x, surface->geometry.y, surface->width, surface->height); weston_compositor_schedule_repaint(surface->compositor); } @@ -397,8 +398,8 @@ weston_surface_configure(struct weston_surface *surface, { weston_surface_damage_below(surface); - surface->x = x; - surface->y = y; + surface->geometry.x = x; + surface->geometry.y = y; surface->width = width; surface->height = height; surface->geometry.dirty = 1; @@ -409,7 +410,8 @@ weston_surface_configure(struct weston_surface *surface, pixman_region32_fini(&surface->opaque); if (surface->visual == WESTON_RGB_VISUAL) pixman_region32_init_rect(&surface->opaque, - surface->x, surface->y, + surface->geometry.x, + surface->geometry.y, surface->width, surface->height); else pixman_region32_init(&surface->opaque); @@ -614,7 +616,8 @@ weston_surface_draw(struct weston_surface *es, struct weston_output *output) int n; pixman_region32_init_rect(&repaint, - es->x, es->y, es->width, es->height); + es->geometry.x, es->geometry.y, + es->width, es->height); pixman_region32_intersect(&repaint, &repaint, &output->region); pixman_region32_intersect(&repaint, &repaint, &es->damage); @@ -791,7 +794,8 @@ weston_output_set_cursor(struct weston_output *output, return; pixman_region32_init_rect(&cursor_region, - device->sprite->x, device->sprite->y, + device->sprite->geometry.x, + device->sprite->geometry.y, device->sprite->width, device->sprite->height); @@ -846,11 +850,13 @@ weston_output_repaint(struct weston_output *output, int msecs) wl_list_for_each(es, &ec->surface_list, link) { pixman_region32_init(&surface_overlap); pixman_region32_intersect_rect(&surface_overlap, - &overlap, es->x, es->y, + &overlap, + es->geometry.x, es->geometry.y, es->width, es->height); es->overlapped = pixman_region32_not_empty(&surface_overlap); pixman_region32_fini(&surface_overlap); - pixman_region32_union_rect(&overlap, &overlap, es->x, es->y, + pixman_region32_union_rect(&overlap, &overlap, + es->geometry.x, es->geometry.y, es->width, es->height); } @@ -981,7 +987,8 @@ weston_surface_assign_output(struct weston_surface *es) max = 0; wl_list_for_each(output, &ec->output_list, link) { pixman_region32_init_rect(®ion, - es->x, es->y, es->width, es->height); + es->geometry.x, es->geometry.y, + es->width, es->height); pixman_region32_intersect(®ion, ®ion, &output->region); e = pixman_region32_extents(®ion); @@ -1027,7 +1034,9 @@ surface_attach(struct wl_client *client, } else if (x != 0 || y != 0 || es->width != buffer->width || es->height != buffer->height) { - shell->configure(shell, es, es->x + x, es->y + y, + /* FIXME: the x,y delta should be in surface-local coords */ + shell->configure(shell, es, es->geometry.x + x, + es->geometry.y + y, buffer->width, buffer->height); } @@ -1237,8 +1246,8 @@ notify_motion(struct wl_input_device *device, uint32_t time, int x, int y) if (wd->sprite) { weston_surface_damage_below(wd->sprite); - wd->sprite->x = device->x - wd->hotspot_x; - wd->sprite->y = device->y - wd->hotspot_y; + wd->sprite->geometry.x = device->x - wd->hotspot_x; + wd->sprite->geometry.y = device->y - wd->hotspot_y; wd->sprite->geometry.dirty = 1; weston_surface_damage(wd->sprite); @@ -1592,8 +1601,8 @@ input_device_attach(struct wl_client *client, device->hotspot_y = y; device->sprite->width = buffer->width; device->sprite->height = buffer->height; - device->sprite->x = device->input_device.x - device->hotspot_x; - device->sprite->y = device->input_device.y - device->hotspot_y; + device->sprite->geometry.x = device->input_device.x - device->hotspot_x; + device->sprite->geometry.y = device->input_device.y - device->hotspot_y; device->sprite->geometry.dirty = 1; weston_surface_damage(device->sprite); diff --git a/src/compositor.h b/src/compositor.h index c05e5966..cb60e5f6 100644 --- a/src/compositor.h +++ b/src/compositor.h @@ -214,7 +214,7 @@ struct weston_surface { GLuint texture; pixman_region32_t damage; pixman_region32_t opaque; - int32_t x, y, width, height; + int32_t width, height; int32_t pitch; struct wl_list link; struct wl_list buffer_link; @@ -229,6 +229,8 @@ struct weston_surface { * That includes the transformations referenced from the list. */ struct { + int32_t x, y; /* surface translation on display */ + /* struct weston_transform */ struct wl_list transformation_list; diff --git a/src/shell.c b/src/shell.c index d2b23794..cae13c55 100644 --- a/src/shell.c +++ b/src/shell.c @@ -198,8 +198,8 @@ weston_surface_move(struct weston_surface *es, return -1; move->grab.interface = &move_grab_interface; - move->dx = es->x - wd->input_device.grab_x; - move->dy = es->y - wd->input_device.grab_y; + move->dx = es->geometry.x - wd->input_device.grab_x; + move->dy = es->geometry.y - wd->input_device.grab_y; move->surface = es; wl_input_device_start_grab(&wd->input_device, &move->grab, time); @@ -296,8 +296,8 @@ weston_surface_resize(struct shell_surface *shsurf, resize->grab.interface = &resize_grab_interface; resize->edges = edges; - resize->dx = es->x - wd->input_device.grab_x; - resize->dy = es->y - wd->input_device.grab_y; + resize->dx = es->geometry.x - wd->input_device.grab_x; + resize->dy = es->geometry.y - wd->input_device.grab_y; resize->width = es->width; resize->height = es->height; resize->shsurf = shsurf; @@ -342,8 +342,9 @@ reset_shell_surface_type(struct shell_surface *surface) { switch (surface->type) { case SHELL_SURFACE_FULLSCREEN: - surface->surface->x = surface->saved_x; - surface->surface->y = surface->saved_y; + surface->surface->geometry.x = surface->saved_x; + surface->surface->geometry.y = surface->saved_y; + surface->surface->geometry.dirty = 1; surface->surface->fullscreen_output = NULL; break; case SHELL_SURFACE_PANEL: @@ -399,8 +400,9 @@ shell_surface_set_transient(struct wl_client *client, /* assign to parents output */ es->output = pes->output; - es->x = pes->x + x; - es->y = pes->y + y; + es->geometry.x = pes->geometry.x + x; + es->geometry.y = pes->geometry.y + y; + es->geometry.dirty = 1; weston_surface_damage(es); shsurf->type = SHELL_SURFACE_TRANSIENT; @@ -430,10 +432,11 @@ shell_surface_set_fullscreen(struct wl_client *client, output = get_default_output(es->compositor); es->output = output; - shsurf->saved_x = es->x; - shsurf->saved_y = es->y; - es->x = (output->current->width - es->width) / 2; - es->y = (output->current->height - es->height) / 2; + shsurf->saved_x = es->geometry.x; + shsurf->saved_y = es->geometry.y; + es->geometry.x = (output->current->width - es->width) / 2; + es->geometry.y = (output->current->height - es->height) / 2; + es->geometry.dirty = 1; es->fullscreen_output = output; weston_surface_damage(es); shsurf->type = SHELL_SURFACE_FULLSCREEN; @@ -515,8 +518,9 @@ shell_map_popup(struct shell_surface *shsurf, uint32_t time) shsurf->popup.grab.interface = &popup_grab_interface; device = es->compositor->input_device; - es->x = shsurf->parent->surface->x + shsurf->popup.x; - es->y = shsurf->parent->surface->y + shsurf->popup.y; + es->geometry.x = shsurf->parent->surface->geometry.x + shsurf->popup.x; + es->geometry.y = shsurf->parent->surface->geometry.y + shsurf->popup.y; + es->geometry.dirty = 1; shsurf->popup.grab.input_device = device; shsurf->popup.time = device->grab_time; @@ -690,8 +694,8 @@ show_screensaver(struct wl_shell *shell, struct shell_surface *surface) wl_list_remove(&surface->surface->link); wl_list_insert(list, &surface->surface->link); weston_surface_configure(surface->surface, - surface->surface->x, - surface->surface->y, + surface->surface->geometry.x, + surface->surface->geometry.y, surface->surface->width, surface->surface->height); surface->surface->output = surface->output; @@ -733,8 +737,9 @@ desktop_shell_set_background(struct wl_client *client, wl_list_insert(&shell->backgrounds, &shsurf->link); - surface->x = shsurf->output->x; - surface->y = shsurf->output->y; + surface->geometry.x = shsurf->output->x; + surface->geometry.y = shsurf->output->y; + surface->geometry.dirty = 1; wl_resource_post_event(resource, DESKTOP_SHELL_CONFIGURE, @@ -771,8 +776,9 @@ desktop_shell_set_panel(struct wl_client *client, wl_list_insert(&shell->panels, &shsurf->link); - surface->x = shsurf->output->x; - surface->y = shsurf->output->y; + surface->geometry.x = shsurf->output->x; + surface->geometry.y = shsurf->output->y; + surface->geometry.dirty = 1; wl_resource_post_event(resource, DESKTOP_SHELL_CONFIGURE, @@ -830,8 +836,9 @@ resume_desktop(struct wl_shell *shell) terminate_screensaver(shell); wl_list_for_each(surface, &shell->hidden_surface_list, link) - weston_surface_configure(surface, surface->x, surface->y, - surface->width, surface->height); + weston_surface_configure(surface, surface->geometry.x, + surface->geometry.y, + surface->width, surface->height); if (wl_list_empty(&shell->backgrounds)) { list = &shell->compositor->surface_list; @@ -932,8 +939,8 @@ resize_binding(struct wl_input_device *device, uint32_t time, break; } - x = device->grab_x - surface->x; - y = device->grab_y - surface->y; + x = device->grab_x - surface->geometry.x; + y = device->grab_y - surface->geometry.y; if (x < surface->width / 3) edges |= WL_SHELL_SURFACE_RESIZE_LEFT; @@ -1223,8 +1230,9 @@ center_on_output(struct weston_surface *surface, struct weston_output *output) { struct weston_mode *mode = output->current; - surface->x = output->x + (mode->width - surface->width) / 2; - surface->y = output->y + (mode->height - surface->height) / 2; + surface->geometry.x = output->x + (mode->width - surface->width) / 2; + surface->geometry.y = output->y + (mode->height - surface->height) / 2; + surface->geometry.dirty = 1; } static void @@ -1256,8 +1264,9 @@ map(struct weston_shell *base, /* initial positioning, see also configure() */ switch (surface_type) { case SHELL_SURFACE_TOPLEVEL: - surface->x = 10 + random() % 400; - surface->y = 10 + random() % 400; + surface->geometry.x = 10 + random() % 400; + surface->geometry.y = 10 + random() % 400; + surface->geometry.dirty = 1; break; case SHELL_SURFACE_SCREENSAVER: case SHELL_SURFACE_FULLSCREEN: @@ -1314,8 +1323,9 @@ map(struct weston_shell *base, switch (surface_type) { case SHELL_SURFACE_TOPLEVEL: - surface->x = 10 + random() % 400; - surface->y = 10 + random() % 400; + surface->geometry.x = 10 + random() % 400; + surface->geometry.y = 10 + random() % 400; + surface->geometry.dirty = 1; break; case SHELL_SURFACE_POPUP: shell_map_popup(shsurf, shsurf->popup.time); @@ -1327,7 +1337,8 @@ map(struct weston_shell *base, surface->width = width; surface->height = height; if (do_configure) { - weston_surface_configure(surface, surface->x, surface->y, + weston_surface_configure(surface, surface->geometry.x, + surface->geometry.y, width, height); weston_compositor_repick(compositor); } diff --git a/src/tablet-shell.c b/src/tablet-shell.c index 51b259cc..14ac8895 100644 --- a/src/tablet-shell.c +++ b/src/tablet-shell.c @@ -110,8 +110,9 @@ tablet_shell_map(struct weston_shell *base, struct weston_surface *surface, struct tablet_shell *shell = container_of(base, struct tablet_shell, shell); - surface->x = 0; - surface->y = 0; + surface->geometry.x = 0; + surface->geometry.y = 0; + surface->geometry.dirty = 1; if (surface == shell->lockscreen_surface) { /* */ @@ -133,7 +134,8 @@ tablet_shell_map(struct weston_shell *base, struct weston_surface *surface, } wl_list_insert(&shell->compositor->surface_list, &surface->link); - weston_surface_configure(surface, surface->x, surface->y, width, height); + weston_surface_configure(surface, surface->geometry.x, + surface->geometry.y, width, height); } static void @@ -165,8 +167,9 @@ tablet_shell_set_lockscreen(struct wl_client *client, struct tablet_shell *shell = resource->data; struct weston_surface *es = surface_resource->data; - es->x = 0; - es->y = 0; + es->geometry.x = 0; + es->geometry.y = 0; + es->geometry.dirty = 1; shell->lockscreen_surface = es; shell->lockscreen_listener.func = handle_lockscreen_surface_destroy; wl_list_insert(es->surface.resource.destroy_listener_list.prev, @@ -199,8 +202,9 @@ tablet_shell_set_switcher(struct wl_client *client, * layer idea, we should be able to hit the framerate on the * fade/zoom in. */ shell->switcher_surface = es; - shell->switcher_surface->x = 0; - shell->switcher_surface->y = 0; + shell->switcher_surface->geometry.x = 0; + shell->switcher_surface->geometry.y = 0; + shell->switcher_surface->geometry.dirty = 1; shell->switcher_listener.func = handle_switcher_surface_destroy; wl_list_insert(es->surface.resource.destroy_listener_list.prev, @@ -215,8 +219,9 @@ tablet_shell_set_homescreen(struct wl_client *client, struct tablet_shell *shell = resource->data; shell->home_surface = surface_resource->data; - shell->home_surface->x = 0; - shell->home_surface->y = 0; + shell->home_surface->geometry.x = 0; + shell->home_surface->geometry.y = 0; + shell->home_surface->geometry.dirty = 1; } static void diff --git a/src/util.c b/src/util.c index c749f29d..a19df51a 100644 --- a/src/util.c +++ b/src/util.c @@ -135,12 +135,12 @@ weston_zoom_frame(struct weston_animation *animation, (zoom->stop - zoom->start) * zoom->spring.current; weston_matrix_init(&zoom->transform.matrix); weston_matrix_translate(&zoom->transform.matrix, - -(es->x + es->width / 2.0), - -(es->y + es->height / 2.0), 0); + -(es->geometry.x + es->width / 2.0), + -(es->geometry.y + es->height / 2.0), 0); weston_matrix_scale(&zoom->transform.matrix, scale, scale, scale); weston_matrix_translate(&zoom->transform.matrix, - es->x + es->width / 2.0, - es->y + es->height / 2.0, 0); + es->geometry.x + es->width / 2.0, + es->geometry.y + es->height / 2.0, 0); es->alpha = zoom->spring.current * 255; if (es->alpha > 255) From f1f5b36aeb86b877a853a15cfe241ff38b3a1d08 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 25 Jan 2012 14:33:33 +0200 Subject: [PATCH 21/30] compositor-drm: do not scan out transformed surfaces Not sure this check belongs here, but as the position checks are here too, I added this. Just so we don't forget. Signed-off-by: Pekka Paalanen --- src/compositor-drm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/compositor-drm.c b/src/compositor-drm.c index 95669b84..3adac880 100644 --- a/src/compositor-drm.c +++ b/src/compositor-drm.c @@ -101,6 +101,7 @@ drm_output_prepare_scanout_surface(struct drm_output *output) es->geometry.y != output->base.y || es->width != output->base.current->width || es->height != output->base.current->height || + es->transform.enabled || es->image == EGL_NO_IMAGE_KHR) return -1; From 73772043154700751894d6698a5d5ef6d6bccc4f Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 25 Jan 2012 14:45:18 +0200 Subject: [PATCH 22/30] compositor: fix zoom origin Now that we can insert a transformation before the surface position translation, we can drop geometry.x,y from the zoom transformation. That was just undoing and redoing the position translation. Signed-off-by: Pekka Paalanen --- src/util.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/util.c b/src/util.c index a19df51a..fed4b8a9 100644 --- a/src/util.c +++ b/src/util.c @@ -135,12 +135,10 @@ weston_zoom_frame(struct weston_animation *animation, (zoom->stop - zoom->start) * zoom->spring.current; weston_matrix_init(&zoom->transform.matrix); weston_matrix_translate(&zoom->transform.matrix, - -(es->geometry.x + es->width / 2.0), - -(es->geometry.y + es->height / 2.0), 0); + -0.5f * es->width, -0.5f * es->height, 0); weston_matrix_scale(&zoom->transform.matrix, scale, scale, scale); weston_matrix_translate(&zoom->transform.matrix, - es->geometry.x + es->width / 2.0, - es->geometry.y + es->height / 2.0, 0); + 0.5f * es->width, 0.5f * es->height, 0); es->alpha = zoom->spring.current * 255; if (es->alpha > 255) @@ -178,7 +176,7 @@ weston_zoom_run(struct weston_surface *surface, GLfloat start, GLfloat stop, wl_list_insert(surface->surface.resource.destroy_listener_list.prev, &zoom->listener.link); - wl_list_insert(surface->compositor->animation_list.prev, + wl_list_insert(&surface->compositor->animation_list, &zoom->animation.link); return zoom; From 6720d8f3bdc095bd8fb3d044e5793cde158b4564 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 25 Jan 2012 15:17:40 +0200 Subject: [PATCH 23/30] compositor: add weston_surface bounding box Compute a surface bounding box, especially for transformed surfaces, for which one cannot simply use x,y,width,height. The bounding box depends on width and height, so these are now under the geometry.dirty flag. Signed-off-by: Pekka Paalanen --- src/compositor.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ src/compositor.h | 2 ++ 2 files changed, 48 insertions(+) diff --git a/src/compositor.c b/src/compositor.c index cee052fb..80130879 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -209,6 +209,7 @@ weston_surface_create(struct weston_compositor *compositor, wl_list_insert(&surface->geometry.transformation_list, &surface->transform.position.link); weston_matrix_init(&surface->transform.position.matrix); + pixman_region32_init(&surface->transform.boundingbox); surface->geometry.dirty = 1; return surface; @@ -230,6 +231,40 @@ weston_surface_set_color(struct weston_surface *surface, surface->shader = &surface->compositor->solid_shader; } +static void +surface_compute_bbox(struct weston_surface *surface, int32_t sx, int32_t sy, + int32_t width, int32_t height, + pixman_region32_t *bbox) +{ + int min_x = INT_MAX, min_y = INT_MAX, max_x = INT_MIN, max_y = INT_MIN; + int32_t s[4][2] = { + { sx, sy }, + { sx, sy + height }, + { sx + width, sy }, + { sx + width, sy + height } + }; + int i; + + for (i = 0; i < 4; ++i) { + int32_t x, y; + weston_surface_to_global(surface, s[i][0], s[i][1], &x, &y); + if (x < min_x) + min_x = x; + if (x > max_x) + max_x = x; + if (y < min_y) + min_y = y; + if (y > max_y) + max_y = y; + } + + /* weston_surface_to_global rounds with floor(), add the + * minimum required safety margin. + */ + pixman_region32_init_rect(bbox, min_x, min_y, + max_x - min_x + 1, max_y - min_y + 1); +} + WL_EXPORT void weston_surface_update_transform(struct weston_surface *surface) { @@ -242,12 +277,19 @@ weston_surface_update_transform(struct weston_surface *surface) surface->geometry.dirty = 0; + pixman_region32_fini(&surface->transform.boundingbox); + /* transform.position is always in transformation_list */ if (surface->geometry.transformation_list.next == &surface->transform.position.link && surface->geometry.transformation_list.prev == &surface->transform.position.link) { surface->transform.enabled = 0; + + pixman_region32_init_rect(&surface->transform.boundingbox, + surface->geometry.x, + surface->geometry.y, + surface->width, surface->height); return; } @@ -266,6 +308,9 @@ weston_surface_update_transform(struct weston_surface *surface) fprintf(stderr, "error: weston_surface %p" " transformation not invertible.\n", surface); } + + surface_compute_bbox(surface, 0, 0, surface->width, surface->height, + &surface->transform.boundingbox); } WL_EXPORT void @@ -491,6 +536,7 @@ destroy_surface(struct wl_resource *resource) wl_list_remove(&surface->buffer_link); + pixman_region32_fini(&surface->transform.boundingbox); pixman_region32_fini(&surface->damage); pixman_region32_fini(&surface->opaque); diff --git a/src/compositor.h b/src/compositor.h index cb60e5f6..2edcce2a 100644 --- a/src/compositor.h +++ b/src/compositor.h @@ -241,6 +241,8 @@ struct weston_surface { * This is updated by weston_surface_update_transform(). */ struct { + pixman_region32_t boundingbox; + /* matrix and inverse are used only if enabled = 1. * If enabled = 0, use x, y, width, height directly. */ From 60921e578795ab9235dd657ed45e6d0a4075e324 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 25 Jan 2012 15:55:43 +0200 Subject: [PATCH 24/30] compositor: move weston_surface::width,height into geometry weston_surface::transform.boundingbox depends on width and height, and therefore geometry.dirty flag, so move width and height into geometry. Fix all users and check that the dirty flag is set. Signed-off-by: Pekka Paalanen --- src/compositor-drm.c | 13 ++++++---- src/compositor.c | 56 +++++++++++++++++++++++++------------------- src/compositor.h | 2 +- src/shell.c | 51 +++++++++++++++++++++++----------------- src/util.c | 6 +++-- 5 files changed, 74 insertions(+), 54 deletions(-) diff --git a/src/compositor-drm.c b/src/compositor-drm.c index 3adac880..de6feb4f 100644 --- a/src/compositor-drm.c +++ b/src/compositor-drm.c @@ -99,15 +99,16 @@ drm_output_prepare_scanout_surface(struct drm_output *output) if (es->visual != WESTON_RGB_VISUAL || es->geometry.x != output->base.x || es->geometry.y != output->base.y || - es->width != output->base.current->width || - es->height != output->base.current->height || + es->geometry.width != output->base.current->width || + es->geometry.height != output->base.current->height || es->transform.enabled || es->image == EGL_NO_IMAGE_KHR) return -1; bo = gbm_bo_create_from_egl_image(c->gbm, c->base.display, es->image, - es->width, es->height, + es->geometry.width, + es->geometry.height, GBM_BO_USE_SCANOUT); handle = gbm_bo_get_handle(bo).s32; @@ -234,7 +235,8 @@ drm_output_set_cursor(struct weston_output *output_base, pixman_region32_init_rect(&cursor_region, eid->sprite->geometry.x, eid->sprite->geometry.y, - eid->sprite->width, eid->sprite->height); + eid->sprite->geometry.width, + eid->sprite->geometry.height); pixman_region32_intersect_rect(&cursor_region, &cursor_region, output->base.x, output->base.y, @@ -247,7 +249,8 @@ drm_output_set_cursor(struct weston_output *output_base, if (eid->sprite->image == EGL_NO_IMAGE_KHR) goto out; - if (eid->sprite->width > 64 || eid->sprite->height > 64) + if (eid->sprite->geometry.width > 64 || + eid->sprite->geometry.height > 64) goto out; bo = gbm_bo_create_from_egl_image(c->gbm, diff --git a/src/compositor.c b/src/compositor.c index 80130879..8afd949f 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -191,8 +191,8 @@ weston_surface_create(struct weston_compositor *compositor, surface->image = EGL_NO_IMAGE_KHR; surface->geometry.x = x; surface->geometry.y = y; - surface->width = width; - surface->height = height; + surface->geometry.width = width; + surface->geometry.height = height; surface->alpha = 255; surface->fullscreen_output = NULL; @@ -289,7 +289,8 @@ weston_surface_update_transform(struct weston_surface *surface) pixman_region32_init_rect(&surface->transform.boundingbox, surface->geometry.x, surface->geometry.y, - surface->width, surface->height); + surface->geometry.width, + surface->geometry.height); return; } @@ -309,7 +310,8 @@ weston_surface_update_transform(struct weston_surface *surface) " transformation not invertible.\n", surface); } - surface_compute_bbox(surface, 0, 0, surface->width, surface->height, + surface_compute_bbox(surface, 0, 0, surface->geometry.width, + surface->geometry.height, &surface->transform.boundingbox); } @@ -399,7 +401,8 @@ WL_EXPORT void weston_surface_damage(struct weston_surface *surface) { weston_surface_damage_rectangle(surface, 0, 0, - surface->width, surface->height); + surface->geometry.width, + surface->geometry.height); } WL_EXPORT void @@ -418,7 +421,8 @@ weston_surface_damage_below(struct weston_surface *surface) pixman_region32_union_rect(&below->damage, &below->damage, surface->geometry.x, surface->geometry.y, - surface->width, surface->height); + surface->geometry.width, + surface->geometry.height); weston_compositor_schedule_repaint(surface->compositor); } @@ -445,8 +449,8 @@ weston_surface_configure(struct weston_surface *surface, surface->geometry.x = x; surface->geometry.y = y; - surface->width = width; - surface->height = height; + surface->geometry.width = width; + surface->geometry.height = height; surface->geometry.dirty = 1; weston_surface_assign_output(surface); @@ -457,7 +461,8 @@ weston_surface_configure(struct weston_surface *surface, pixman_region32_init_rect(&surface->opaque, surface->geometry.x, surface->geometry.y, - surface->width, surface->height); + surface->geometry.width, + surface->geometry.height); else pixman_region32_init(&surface->opaque); } @@ -592,7 +597,7 @@ weston_buffer_attach(struct wl_buffer *buffer, struct wl_surface *surface) ec->image_target_texture_2d(GL_TEXTURE_2D, es->image); es->visual = WESTON_ARGB_VISUAL; - es->pitch = es->width; + es->pitch = es->geometry.width; } } @@ -610,7 +615,7 @@ texture_region(struct weston_surface *es, pixman_region32_t *region) v = wl_array_add(&ec->vertices, n * 16 * sizeof *v); p = wl_array_add(&ec->indices, n * 6 * sizeof *p); inv_width = 1.0 / es->pitch; - inv_height = 1.0 / es->height; + inv_height = 1.0 / es->geometry.height; for (i = 0; i < n; i++, v += 16, p += 6) { surface_from_global_float(es, rectangles[i].x1, @@ -663,7 +668,7 @@ weston_surface_draw(struct weston_surface *es, struct weston_output *output) pixman_region32_init_rect(&repaint, es->geometry.x, es->geometry.y, - es->width, es->height); + es->geometry.width, es->geometry.height); pixman_region32_intersect(&repaint, &repaint, &output->region); pixman_region32_intersect(&repaint, &repaint, &es->damage); @@ -701,7 +706,7 @@ weston_surface_draw(struct weston_surface *es, struct weston_output *output) if (es->shader->texwidth_uniform != GL_NONE) glUniform1f(es->shader->texwidth_uniform, - (GLfloat)es->width / es->pitch); + (GLfloat)es->geometry.width / es->pitch); weston_surface_update_transform(es); if (es->transform.enabled) @@ -842,8 +847,8 @@ weston_output_set_cursor(struct weston_output *output, pixman_region32_init_rect(&cursor_region, device->sprite->geometry.x, device->sprite->geometry.y, - device->sprite->width, - device->sprite->height); + device->sprite->geometry.width, + device->sprite->geometry.height); pixman_region32_intersect(&cursor_region, &cursor_region, &output->region); @@ -898,12 +903,14 @@ weston_output_repaint(struct weston_output *output, int msecs) pixman_region32_intersect_rect(&surface_overlap, &overlap, es->geometry.x, es->geometry.y, - es->width, es->height); + es->geometry.width, + es->geometry.height); es->overlapped = pixman_region32_not_empty(&surface_overlap); pixman_region32_fini(&surface_overlap); pixman_region32_union_rect(&overlap, &overlap, es->geometry.x, es->geometry.y, - es->width, es->height); + es->geometry.width, + es->geometry.height); } weston_output_set_cursor(output, ec->input_device); @@ -1034,7 +1041,8 @@ weston_surface_assign_output(struct weston_surface *es) wl_list_for_each(output, &ec->output_list, link) { pixman_region32_init_rect(®ion, es->geometry.x, es->geometry.y, - es->width, es->height); + es->geometry.width, + es->geometry.height); pixman_region32_intersect(®ion, ®ion, &output->region); e = pixman_region32_extents(®ion); @@ -1078,8 +1086,8 @@ surface_attach(struct wl_client *client, if (es->visual == WESTON_NONE_VISUAL) { shell->map(shell, es, buffer->width, buffer->height); } else if (x != 0 || y != 0 || - es->width != buffer->width || - es->height != buffer->height) { + es->geometry.width != buffer->width || + es->geometry.height != buffer->height) { /* FIXME: the x,y delta should be in surface-local coords */ shell->configure(shell, es, es->geometry.x + x, es->geometry.y + y, @@ -1182,8 +1190,8 @@ weston_compositor_pick_surface(struct weston_compositor *compositor, if (surface->surface.resource.client == NULL) continue; weston_surface_from_global(surface, x, y, sx, sy); - if (0 <= *sx && *sx < surface->width && - 0 <= *sy && *sy < surface->height) + if (0 <= *sx && *sx < surface->geometry.width && + 0 <= *sy && *sy < surface->geometry.height) return surface; } @@ -1645,8 +1653,8 @@ input_device_attach(struct wl_client *client, device->hotspot_x = x; device->hotspot_y = y; - device->sprite->width = buffer->width; - device->sprite->height = buffer->height; + device->sprite->geometry.width = buffer->width; + device->sprite->geometry.height = buffer->height; device->sprite->geometry.x = device->input_device.x - device->hotspot_x; device->sprite->geometry.y = device->input_device.y - device->hotspot_y; device->sprite->geometry.dirty = 1; diff --git a/src/compositor.h b/src/compositor.h index 2edcce2a..51562a0c 100644 --- a/src/compositor.h +++ b/src/compositor.h @@ -214,7 +214,6 @@ struct weston_surface { GLuint texture; pixman_region32_t damage; pixman_region32_t opaque; - int32_t width, height; int32_t pitch; struct wl_list link; struct wl_list buffer_link; @@ -230,6 +229,7 @@ struct weston_surface { */ struct { int32_t x, y; /* surface translation on display */ + int32_t width, height; /* struct weston_transform */ struct wl_list transformation_list; diff --git a/src/shell.c b/src/shell.c index cae13c55..752ebbf1 100644 --- a/src/shell.c +++ b/src/shell.c @@ -166,7 +166,7 @@ move_grab_motion(struct wl_grab *grab, weston_surface_configure(es, device->x + move->dx, device->y + move->dy, - es->width, es->height); + es->geometry.width, es->geometry.height); } static void @@ -298,8 +298,8 @@ weston_surface_resize(struct shell_surface *shsurf, resize->edges = edges; resize->dx = es->geometry.x - wd->input_device.grab_x; resize->dy = es->geometry.y - wd->input_device.grab_y; - resize->width = es->width; - resize->height = es->height; + resize->width = es->geometry.width; + resize->height = es->geometry.height; resize->shsurf = shsurf; if (edges == 0 || edges > 15 || @@ -434,8 +434,8 @@ shell_surface_set_fullscreen(struct wl_client *client, shsurf->saved_x = es->geometry.x; shsurf->saved_y = es->geometry.y; - es->geometry.x = (output->current->width - es->width) / 2; - es->geometry.y = (output->current->height - es->height) / 2; + es->geometry.x = (output->current->width - es->geometry.width) / 2; + es->geometry.y = (output->current->height - es->geometry.height) / 2; es->geometry.dirty = 1; es->fullscreen_output = output; weston_surface_damage(es); @@ -696,8 +696,8 @@ show_screensaver(struct wl_shell *shell, struct shell_surface *surface) weston_surface_configure(surface->surface, surface->surface->geometry.x, surface->surface->geometry.y, - surface->surface->width, - surface->surface->height); + surface->surface->geometry.width, + surface->surface->geometry.height); surface->surface->output = surface->output; } @@ -838,7 +838,8 @@ resume_desktop(struct wl_shell *shell) wl_list_for_each(surface, &shell->hidden_surface_list, link) weston_surface_configure(surface, surface->geometry.x, surface->geometry.y, - surface->width, surface->height); + surface->geometry.width, + surface->geometry.height); if (wl_list_empty(&shell->backgrounds)) { list = &shell->compositor->surface_list; @@ -939,19 +940,20 @@ resize_binding(struct wl_input_device *device, uint32_t time, break; } + /* FIXME: convert properly to surface coordinates */ x = device->grab_x - surface->geometry.x; y = device->grab_y - surface->geometry.y; - if (x < surface->width / 3) + if (x < surface->geometry.width / 3) edges |= WL_SHELL_SURFACE_RESIZE_LEFT; - else if (x < 2 * surface->width / 3) + else if (x < 2 * surface->geometry.width / 3) edges |= 0; else edges |= WL_SHELL_SURFACE_RESIZE_RIGHT; - if (y < surface->height / 3) + if (y < surface->geometry.height / 3) edges |= WL_SHELL_SURFACE_RESIZE_TOP; - else if (y < 2 * surface->height / 3) + else if (y < 2 * surface->geometry.height / 3) edges |= 0; else edges |= WL_SHELL_SURFACE_RESIZE_BOTTOM; @@ -1070,8 +1072,8 @@ rotate_binding(struct wl_input_device *device, uint32_t time, rotate->surface = surface; weston_surface_to_global(surface->surface, - surface->surface->width / 2, - surface->surface->height / 2, + surface->surface->geometry.width / 2, + surface->surface->geometry.height / 2, &rotate->center.x, &rotate->center.y); wl_input_device_start_grab(device, &rotate->grab, time); @@ -1230,8 +1232,10 @@ center_on_output(struct weston_surface *surface, struct weston_output *output) { struct weston_mode *mode = output->current; - surface->geometry.x = output->x + (mode->width - surface->width) / 2; - surface->geometry.y = output->y + (mode->height - surface->height) / 2; + surface->geometry.x = + output->x + (mode->width - surface->geometry.width) / 2; + surface->geometry.y = + output->y + (mode->height - surface->geometry.height) / 2; surface->geometry.dirty = 1; } @@ -1258,8 +1262,9 @@ map(struct weston_shell *base, do_configure = 1; } - surface->width = width; - surface->height = height; + surface->geometry.width = width; + surface->geometry.height = height; + surface->geometry.dirty = 1; /* initial positioning, see also configure() */ switch (surface_type) { @@ -1334,8 +1339,9 @@ map(struct weston_shell *base, break; } - surface->width = width; - surface->height = height; + surface->geometry.width = width; + surface->geometry.height = height; + surface->geometry.dirty = 1; if (do_configure) { weston_surface_configure(surface, surface->geometry.x, surface->geometry.y, @@ -1374,8 +1380,9 @@ configure(struct weston_shell *base, struct weston_surface *surface, if (shsurf) surface_type = shsurf->type; - surface->width = width; - surface->height = height; + surface->geometry.width = width; + surface->geometry.height = height; + surface->geometry.dirty = 1; switch (surface_type) { case SHELL_SURFACE_SCREENSAVER: diff --git a/src/util.c b/src/util.c index fed4b8a9..ee8e7cf3 100644 --- a/src/util.c +++ b/src/util.c @@ -135,10 +135,12 @@ weston_zoom_frame(struct weston_animation *animation, (zoom->stop - zoom->start) * zoom->spring.current; weston_matrix_init(&zoom->transform.matrix); weston_matrix_translate(&zoom->transform.matrix, - -0.5f * es->width, -0.5f * es->height, 0); + -0.5f * es->geometry.width, + -0.5f * es->geometry.height, 0); weston_matrix_scale(&zoom->transform.matrix, scale, scale, scale); weston_matrix_translate(&zoom->transform.matrix, - 0.5f * es->width, 0.5f * es->height, 0); + 0.5f * es->geometry.width, + 0.5f * es->geometry.height, 0); es->alpha = zoom->spring.current * 255; if (es->alpha > 255) From a9f8a21c75201ced51acdc564510a3bbe7f0846b Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 26 Jan 2012 11:28:08 +0200 Subject: [PATCH 25/30] compositor: weston_surface_draw() to use bounding box Use the proper bounding box in clipping the surface repaint area. Fixes excessive clipping for transformed surfaces. Also don't leak the region32 on early return. Signed-off-by: Pekka Paalanen --- src/compositor.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/compositor.c b/src/compositor.c index 8afd949f..a2065e7b 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -666,14 +666,15 @@ weston_surface_draw(struct weston_surface *es, struct weston_output *output) GLint filter; int n; - pixman_region32_init_rect(&repaint, - es->geometry.x, es->geometry.y, - es->geometry.width, es->geometry.height); - pixman_region32_intersect(&repaint, &repaint, &output->region); + weston_surface_update_transform(es); + + pixman_region32_init(&repaint); + pixman_region32_intersect(&repaint, &es->transform.boundingbox, + &output->region); pixman_region32_intersect(&repaint, &repaint, &es->damage); if (!pixman_region32_not_empty(&repaint)) - return; + goto out; switch (es->visual) { case WESTON_ARGB_VISUAL: @@ -708,7 +709,6 @@ weston_surface_draw(struct weston_surface *es, struct weston_output *output) glUniform1f(es->shader->texwidth_uniform, (GLfloat)es->geometry.width / es->pitch); - weston_surface_update_transform(es); if (es->transform.enabled) filter = GL_LINEAR; else @@ -733,6 +733,8 @@ weston_surface_draw(struct weston_surface *es, struct weston_output *output) ec->vertices.size = 0; ec->indices.size = 0; + +out: pixman_region32_fini(&repaint); } From 3bfcd4d907870e063a36bdb776f0d78a8469070a Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 26 Jan 2012 11:31:01 +0200 Subject: [PATCH 26/30] compositor: weston_output_set_cursor() to use bounding box If we ever have transformed cursor surfaces, we would better use the bounding box to check if it is on the given output. Signed-off-by: Pekka Paalanen --- src/compositor.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/compositor.c b/src/compositor.c index a2065e7b..050227fb 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -846,13 +846,12 @@ weston_output_set_cursor(struct weston_output *output, if (device->sprite == NULL) return; - pixman_region32_init_rect(&cursor_region, - device->sprite->geometry.x, - device->sprite->geometry.y, - device->sprite->geometry.width, - device->sprite->geometry.height); + weston_surface_update_transform(device->sprite); - pixman_region32_intersect(&cursor_region, &cursor_region, &output->region); + pixman_region32_init(&cursor_region); + pixman_region32_intersect(&cursor_region, + &device->sprite->transform.boundingbox, + &output->region); if (!pixman_region32_not_empty(&cursor_region)) { output->set_hardware_cursor(output, NULL); From 45f3e405c6e556f50eeeb319d2191b8a4aa61796 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 26 Jan 2012 11:34:16 +0200 Subject: [PATCH 27/30] compositor: weston_surface_assign_output() to use bounding box Use the bounding box to compute an approximation of which output contains most of the surface. Move the region32 init outside the loop, and fini it, too. Signed-off-by: Pekka Paalanen --- src/compositor.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/compositor.c b/src/compositor.c index 050227fb..b8ccf6db 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -1037,14 +1037,14 @@ weston_surface_assign_output(struct weston_surface *es) uint32_t max, area; pixman_box32_t *e; + weston_surface_update_transform(es); + new_output = NULL; max = 0; + pixman_region32_init(®ion); wl_list_for_each(output, &ec->output_list, link) { - pixman_region32_init_rect(®ion, - es->geometry.x, es->geometry.y, - es->geometry.width, - es->geometry.height); - pixman_region32_intersect(®ion, ®ion, &output->region); + pixman_region32_intersect(®ion, &es->transform.boundingbox, + &output->region); e = pixman_region32_extents(®ion); area = (e->x2 - e->x1) * (e->y2 - e->y1); @@ -1054,6 +1054,7 @@ weston_surface_assign_output(struct weston_surface *es) max = area; } } + pixman_region32_fini(®ion); es->output = new_output; if (!wl_list_empty(&es->frame_callback_list)) { From 2267d45f7c55ab452ad827c6997d623b54065a69 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 26 Jan 2012 13:12:45 +0200 Subject: [PATCH 28/30] compositor: use bounding box for damage regions Change weston_surface_damage*() functions to use the full surface bounding box or call surface_compute_bbox() to find the bounding box for an arbitrary rectangle. This should fix all rendering artifacts for non-opaque (i.e. ARGB) transformed surfaces. Signed-off-by: Pekka Paalanen --- src/compositor.c | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/src/compositor.c b/src/compositor.c index b8ccf6db..9f94abf7 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -384,25 +384,36 @@ weston_surface_from_global(struct weston_surface *surface, WL_EXPORT void weston_surface_damage_rectangle(struct weston_surface *surface, - int32_t x, int32_t y, - int32_t width, int32_t height) + int32_t sx, int32_t sy, + int32_t width, int32_t height) { - struct weston_compositor *compositor = surface->compositor; + weston_surface_update_transform(surface); - pixman_region32_union_rect(&surface->damage, - &surface->damage, - surface->geometry.x + x, - surface->geometry.y + y, - width, height); - weston_compositor_schedule_repaint(compositor); + if (surface->transform.enabled) { + pixman_region32_t box; + surface_compute_bbox(surface, sx, sy, width, height, &box); + pixman_region32_union(&surface->damage, &surface->damage, + &box); + pixman_region32_fini(&box); + } else { + int32_t x, y; + weston_surface_to_global(surface, sx, sy, &x, &y); + pixman_region32_union_rect(&surface->damage, &surface->damage, + x, y, width, height); + } + + weston_compositor_schedule_repaint(surface->compositor); } WL_EXPORT void weston_surface_damage(struct weston_surface *surface) { - weston_surface_damage_rectangle(surface, 0, 0, - surface->geometry.width, - surface->geometry.height); + weston_surface_update_transform(surface); + + pixman_region32_union(&surface->damage, &surface->damage, + &surface->transform.boundingbox); + + weston_compositor_schedule_repaint(surface->compositor); } WL_EXPORT void @@ -418,11 +429,10 @@ weston_surface_damage_below(struct weston_surface *surface) below = container_of(surface->link.next, struct weston_surface, link); - pixman_region32_union_rect(&below->damage, - &below->damage, - surface->geometry.x, surface->geometry.y, - surface->geometry.width, - surface->geometry.height); + weston_surface_update_transform(surface); + pixman_region32_union(&below->damage, &below->damage, + &surface->transform.boundingbox); + weston_compositor_schedule_repaint(surface->compositor); } From 15d60efee9ecd92c8ff73f2b6972994adab1e279 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 27 Jan 2012 14:38:33 +0200 Subject: [PATCH 29/30] compositor: make overlap computation use the boundingbox This makes the overlap to account for surface transformations. Signed-off-by: Pekka Paalanen --- src/compositor.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/compositor.c b/src/compositor.c index 9f94abf7..87c0fbe6 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -910,18 +910,15 @@ weston_output_repaint(struct weston_output *output, int msecs) pixman_region32_init(&overlap); wl_list_for_each(es, &ec->surface_list, link) { + weston_surface_update_transform(es); + pixman_region32_init(&surface_overlap); - pixman_region32_intersect_rect(&surface_overlap, - &overlap, - es->geometry.x, es->geometry.y, - es->geometry.width, - es->geometry.height); + pixman_region32_intersect(&surface_overlap, &overlap, + &es->transform.boundingbox); es->overlapped = pixman_region32_not_empty(&surface_overlap); pixman_region32_fini(&surface_overlap); - pixman_region32_union_rect(&overlap, &overlap, - es->geometry.x, es->geometry.y, - es->geometry.width, - es->geometry.height); + pixman_region32_union(&overlap, &overlap, + &es->transform.boundingbox); } weston_output_set_cursor(output, ec->input_device); From d581a8fcc8f9c061bb34c68d383ebed9d50e09fd Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 27 Jan 2012 16:25:16 +0200 Subject: [PATCH 30/30] Collabora copyright updates which I forgot to add while working on these files. Signed-off-by: Pekka Paalanen --- src/compositor.c | 1 + src/compositor.h | 1 + src/shell.c | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/compositor.c b/src/compositor.c index 87c0fbe6..9593ec8e 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -1,6 +1,7 @@ /* * Copyright © 2010-2011 Intel Corporation * Copyright © 2008-2011 Kristian Høgsberg + * Copyright © 2012 Collabora, Ltd. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided diff --git a/src/compositor.h b/src/compositor.h index 51562a0c..a7b29648 100644 --- a/src/compositor.h +++ b/src/compositor.h @@ -1,5 +1,6 @@ /* * Copyright © 2008-2011 Kristian Høgsberg + * Copyright © 2012 Collabora, Ltd. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided diff --git a/src/shell.c b/src/shell.c index 752ebbf1..c7806106 100644 --- a/src/shell.c +++ b/src/shell.c @@ -1,6 +1,6 @@ /* * Copyright © 2010 Intel Corporation - * Copyright © 2011 Collabora, Ltd. + * Copyright © 2011-2012 Collabora, Ltd. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided