98606d6a03
wayland upstream has introduced wl_signal_emit_mutable now we can use it [1]: https://gitlab.freedesktop.org/wayland/wayland/-/merge_requests/208 Signed-off-by: He Yong <hyyoxhk@163.com>
1228 lines
34 KiB
C
1228 lines
34 KiB
C
/*
|
|
* Copyright © 2012 Intel Corporation
|
|
* Copyright © 2013 Vasily Khoruzhick <anarsoul@gmail.com>
|
|
* Copyright © 2015 Collabora, Ltd.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
* a copy of this software and associated documentation files (the
|
|
* "Software"), to deal in the Software without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
* the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice (including the
|
|
* next paragraph) shall be included in all copies or substantial
|
|
* portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <errno.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
|
|
#include "pixman-renderer.h"
|
|
#include "color.h"
|
|
#include "pixel-formats.h"
|
|
#include "output-capture.h"
|
|
#include "shared/helpers.h"
|
|
#include "shared/weston-drm-fourcc.h"
|
|
#include "shared/xalloc.h"
|
|
|
|
#include <linux/input.h>
|
|
|
|
struct pixman_output_state {
|
|
pixman_image_t *shadow_image;
|
|
const struct pixel_format_info *shadow_format;
|
|
pixman_image_t *hw_buffer;
|
|
const struct pixel_format_info *hw_format;
|
|
struct weston_size fb_size;
|
|
struct wl_list renderbuffer_list;
|
|
};
|
|
|
|
struct pixman_surface_state {
|
|
struct weston_surface *surface;
|
|
|
|
pixman_image_t *image;
|
|
struct weston_buffer_reference buffer_ref;
|
|
struct weston_buffer_release_reference buffer_release_ref;
|
|
|
|
struct wl_listener buffer_destroy_listener;
|
|
struct wl_listener surface_destroy_listener;
|
|
struct wl_listener renderer_destroy_listener;
|
|
};
|
|
|
|
struct pixman_renderbuffer {
|
|
struct weston_renderbuffer base;
|
|
|
|
pixman_image_t *image;
|
|
struct wl_list link;
|
|
};
|
|
|
|
struct pixman_renderer {
|
|
struct weston_renderer base;
|
|
|
|
int repaint_debug;
|
|
pixman_image_t *debug_color;
|
|
struct weston_binding *debug_binding;
|
|
|
|
struct wl_signal destroy_signal;
|
|
};
|
|
|
|
static inline struct pixman_renderbuffer *
|
|
to_pixman_renderbuffer(struct weston_renderbuffer *renderbuffer)
|
|
{
|
|
return container_of(renderbuffer, struct pixman_renderbuffer, base);
|
|
}
|
|
|
|
static pixman_image_t *
|
|
pixman_renderer_renderbuffer_get_image(struct weston_renderbuffer *renderbuffer)
|
|
{
|
|
struct pixman_renderbuffer *rb = to_pixman_renderbuffer(renderbuffer);
|
|
|
|
return rb->image;
|
|
}
|
|
|
|
static inline struct pixman_output_state *
|
|
get_output_state(struct weston_output *output)
|
|
{
|
|
return (struct pixman_output_state *)output->renderer_state;
|
|
}
|
|
|
|
static int
|
|
pixman_renderer_create_surface(struct weston_surface *surface);
|
|
|
|
static inline struct pixman_surface_state *
|
|
get_surface_state(struct weston_surface *surface)
|
|
{
|
|
if (!surface->renderer_state)
|
|
pixman_renderer_create_surface(surface);
|
|
|
|
return (struct pixman_surface_state *)surface->renderer_state;
|
|
}
|
|
|
|
static inline struct pixman_renderer *
|
|
get_renderer(struct weston_compositor *ec)
|
|
{
|
|
return (struct pixman_renderer *)ec->renderer;
|
|
}
|
|
|
|
static int
|
|
pixman_renderer_read_pixels(struct weston_output *output,
|
|
const struct pixel_format_info *format, void *pixels,
|
|
uint32_t x, uint32_t y,
|
|
uint32_t width, uint32_t height)
|
|
{
|
|
struct pixman_output_state *po = get_output_state(output);
|
|
pixman_image_t *out_buf;
|
|
|
|
if (!po->hw_buffer) {
|
|
errno = ENODEV;
|
|
return -1;
|
|
}
|
|
|
|
out_buf = pixman_image_create_bits(format->pixman_format,
|
|
width,
|
|
height,
|
|
pixels,
|
|
(PIXMAN_FORMAT_BPP(format->pixman_format) / 8) * width);
|
|
|
|
pixman_image_composite32(PIXMAN_OP_SRC,
|
|
po->hw_buffer, /* src */
|
|
NULL /* mask */,
|
|
out_buf, /* dest */
|
|
x, y, /* src_x, src_y */
|
|
0, 0, /* mask_x, mask_y */
|
|
0, 0, /* dest_x, dest_y */
|
|
po->fb_size.width, /* width */
|
|
po->fb_size.height /* height */);
|
|
|
|
pixman_image_unref(out_buf);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define D2F(v) pixman_double_to_fixed((double)v)
|
|
|
|
static void
|
|
weston_matrix_to_pixman_transform(pixman_transform_t *pt,
|
|
const struct weston_matrix *wm)
|
|
{
|
|
/* Pixman supports only 2D transform matrix, but Weston uses 3D, *
|
|
* so we're omitting Z coordinate here. */
|
|
pt->matrix[0][0] = pixman_double_to_fixed(wm->d[0]);
|
|
pt->matrix[0][1] = pixman_double_to_fixed(wm->d[4]);
|
|
pt->matrix[0][2] = pixman_double_to_fixed(wm->d[12]);
|
|
pt->matrix[1][0] = pixman_double_to_fixed(wm->d[1]);
|
|
pt->matrix[1][1] = pixman_double_to_fixed(wm->d[5]);
|
|
pt->matrix[1][2] = pixman_double_to_fixed(wm->d[13]);
|
|
pt->matrix[2][0] = pixman_double_to_fixed(wm->d[3]);
|
|
pt->matrix[2][1] = pixman_double_to_fixed(wm->d[7]);
|
|
pt->matrix[2][2] = pixman_double_to_fixed(wm->d[15]);
|
|
}
|
|
|
|
static bool
|
|
view_transformation_is_translation(struct weston_view *view)
|
|
{
|
|
if (!view->transform.enabled)
|
|
return true;
|
|
|
|
if (view->transform.matrix.type <= WESTON_MATRIX_TRANSFORM_TRANSLATE)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
static void
|
|
region_intersect_only_translation(pixman_region32_t *result_global,
|
|
pixman_region32_t *global,
|
|
pixman_region32_t *surf,
|
|
struct weston_view *view)
|
|
{
|
|
struct weston_coord_surface cs;
|
|
struct weston_coord_global cg;
|
|
|
|
cs = weston_coord_surface(0, 0, view->surface);
|
|
assert(view_transformation_is_translation(view));
|
|
|
|
/* Convert from surface to global coordinates */
|
|
pixman_region32_copy(result_global, surf);
|
|
cg = weston_coord_surface_to_global(view, cs);
|
|
pixman_region32_translate(result_global, cg.c.x, cg.c.y);
|
|
|
|
pixman_region32_intersect(result_global, result_global, global);
|
|
}
|
|
|
|
static void
|
|
composite_whole(pixman_op_t op,
|
|
pixman_image_t *src,
|
|
pixman_image_t *mask,
|
|
pixman_image_t *dest,
|
|
const pixman_transform_t *transform,
|
|
pixman_filter_t filter)
|
|
{
|
|
int32_t dest_width;
|
|
int32_t dest_height;
|
|
|
|
dest_width = pixman_image_get_width(dest);
|
|
dest_height = pixman_image_get_height(dest);
|
|
|
|
pixman_image_set_transform(src, transform);
|
|
pixman_image_set_filter(src, filter, NULL, 0);
|
|
|
|
/* bilinear filtering needs the equivalent of OpenGL CLAMP_TO_EDGE */
|
|
if (filter == PIXMAN_FILTER_NEAREST)
|
|
pixman_image_set_repeat(src, PIXMAN_REPEAT_NONE);
|
|
else
|
|
pixman_image_set_repeat(src, PIXMAN_REPEAT_PAD);
|
|
|
|
pixman_image_composite32(op, src, mask, dest,
|
|
0, 0, /* src_x, src_y */
|
|
0, 0, /* mask_x, mask_y */
|
|
0, 0, /* dest_x, dest_y */
|
|
dest_width, dest_height);
|
|
}
|
|
|
|
static void
|
|
composite_clipped(struct weston_output *output,
|
|
pixman_image_t *src,
|
|
pixman_image_t *mask,
|
|
pixman_image_t *dest,
|
|
const pixman_transform_t *transform,
|
|
pixman_filter_t filter,
|
|
pixman_region32_t *src_clip)
|
|
{
|
|
int n_box;
|
|
pixman_box32_t *boxes;
|
|
int32_t dest_width;
|
|
int32_t dest_height;
|
|
int src_stride;
|
|
int bitspp;
|
|
pixman_format_code_t src_format;
|
|
void *src_data;
|
|
int i;
|
|
|
|
/*
|
|
* Hardcoded to use PIXMAN_OP_OVER, because sampling outside of
|
|
* a Pixman image produces (0,0,0,0) instead of discarding the
|
|
* fragment.
|
|
*
|
|
* Also repeat mode must be PIXMAN_REPEAT_NONE (the default) to
|
|
* actually sample (0,0,0,0). This may cause issues for clients that
|
|
* expect OpenGL CLAMP_TO_EDGE sampling behavior on their buffer.
|
|
* Using temporary 'boximg' it is not possible to apply CLAMP_TO_EDGE
|
|
* correctly with bilinear filter. Maybe trapezoid rendering could be
|
|
* the answer instead of source clip?
|
|
*/
|
|
|
|
dest_width = pixman_image_get_width(dest);
|
|
dest_height = pixman_image_get_height(dest);
|
|
src_format = pixman_image_get_format(src);
|
|
src_stride = pixman_image_get_stride(src);
|
|
bitspp = PIXMAN_FORMAT_BPP(src_format);
|
|
src_data = pixman_image_get_data(src);
|
|
|
|
assert(src_format);
|
|
|
|
/* This would be massive overdraw, except when n_box is 1. */
|
|
boxes = pixman_region32_rectangles(src_clip, &n_box);
|
|
for (i = 0; i < n_box; i++) {
|
|
uint8_t *ptr = src_data;
|
|
pixman_image_t *boximg;
|
|
pixman_transform_t adj = *transform;
|
|
|
|
ptr += boxes[i].y1 * src_stride;
|
|
ptr += boxes[i].x1 * bitspp / 8;
|
|
boximg = pixman_image_create_bits_no_clear(src_format,
|
|
boxes[i].x2 - boxes[i].x1,
|
|
boxes[i].y2 - boxes[i].y1,
|
|
(uint32_t *)ptr, src_stride);
|
|
|
|
pixman_transform_translate(&adj, NULL,
|
|
pixman_int_to_fixed(-boxes[i].x1),
|
|
pixman_int_to_fixed(-boxes[i].y1));
|
|
pixman_image_set_transform(boximg, &adj);
|
|
|
|
pixman_image_set_filter(boximg, filter, NULL, 0);
|
|
pixman_image_composite32(PIXMAN_OP_OVER, boximg, mask, dest,
|
|
0, 0, /* src_x, src_y */
|
|
0, 0, /* mask_x, mask_y */
|
|
0, 0, /* dest_x, dest_y */
|
|
dest_width, dest_height);
|
|
|
|
pixman_image_unref(boximg);
|
|
}
|
|
|
|
if (n_box > 1) {
|
|
weston_log_paced(&output->pixman_overdraw_pacer, 1, 0,
|
|
"Pixman-renderer warning: %dx overdraw\n",
|
|
n_box);
|
|
}
|
|
}
|
|
|
|
/** Paint an intersected region
|
|
*
|
|
* \param pnode The paint node to be painted.
|
|
* \param repaint_output The region to be painted in output coordinates.
|
|
* \param source_clip The region of the source image to use, in source image
|
|
* coordinates. If NULL, use the whole source image.
|
|
* \param pixman_op Compositing operator, either SRC or OVER.
|
|
*/
|
|
static void
|
|
repaint_region(struct weston_paint_node *pnode,
|
|
pixman_region32_t *repaint_output,
|
|
pixman_region32_t *source_clip,
|
|
pixman_op_t pixman_op)
|
|
{
|
|
struct weston_output *output = pnode->output;
|
|
struct weston_view *ev = pnode->view;
|
|
struct pixman_renderer *pr =
|
|
(struct pixman_renderer *) output->compositor->renderer;
|
|
struct pixman_surface_state *ps = get_surface_state(ev->surface);
|
|
struct pixman_output_state *po = get_output_state(output);
|
|
pixman_image_t *target_image;
|
|
pixman_transform_t transform;
|
|
pixman_filter_t filter;
|
|
pixman_image_t *mask_image;
|
|
pixman_color_t mask = { 0, };
|
|
|
|
if (po->shadow_image)
|
|
target_image = po->shadow_image;
|
|
else
|
|
target_image = po->hw_buffer;
|
|
|
|
/* Clip rendering to the damaged output region */
|
|
pixman_image_set_clip_region32(target_image, repaint_output);
|
|
|
|
weston_matrix_to_pixman_transform(&transform,
|
|
&pnode->output_to_buffer_matrix);
|
|
|
|
if (pnode->needs_filtering)
|
|
filter = PIXMAN_FILTER_BILINEAR;
|
|
else
|
|
filter = PIXMAN_FILTER_NEAREST;
|
|
|
|
if (ps->buffer_ref.buffer)
|
|
wl_shm_buffer_begin_access(ps->buffer_ref.buffer->shm_buffer);
|
|
|
|
if (ev->alpha < 1.0) {
|
|
mask.alpha = 0xffff * ev->alpha;
|
|
mask_image = pixman_image_create_solid_fill(&mask);
|
|
} else {
|
|
mask_image = NULL;
|
|
}
|
|
|
|
if (source_clip)
|
|
composite_clipped(output, ps->image, mask_image, target_image,
|
|
&transform, filter, source_clip);
|
|
else
|
|
composite_whole(pixman_op, ps->image, mask_image,
|
|
target_image, &transform, filter);
|
|
|
|
if (mask_image)
|
|
pixman_image_unref(mask_image);
|
|
|
|
if (ps->buffer_ref.buffer)
|
|
wl_shm_buffer_end_access(ps->buffer_ref.buffer->shm_buffer);
|
|
|
|
if (pr->repaint_debug)
|
|
pixman_image_composite32(PIXMAN_OP_OVER,
|
|
pr->debug_color, /* src */
|
|
NULL /* mask */,
|
|
target_image, /* dest */
|
|
0, 0, /* src_x, src_y */
|
|
0, 0, /* mask_x, mask_y */
|
|
0, 0, /* dest_x, dest_y */
|
|
po->fb_size.width, /* width */
|
|
po->fb_size.height /* height */);
|
|
|
|
pixman_image_set_clip_region32(target_image, NULL);
|
|
}
|
|
|
|
static void
|
|
draw_node_translated(struct weston_paint_node *pnode,
|
|
pixman_region32_t *repaint_global)
|
|
{
|
|
struct weston_output *output = pnode->output;
|
|
struct weston_surface *surface = pnode->surface;
|
|
struct weston_view *view = pnode->view;
|
|
|
|
/* non-opaque region in surface coordinates: */
|
|
pixman_region32_t surface_blend;
|
|
/* region to be painted in output coordinates: */
|
|
pixman_region32_t repaint_output;
|
|
|
|
pixman_region32_init(&repaint_output);
|
|
|
|
/* Blended region is whole surface minus opaque region,
|
|
* unless surface alpha forces us to blend all.
|
|
*/
|
|
pixman_region32_init_rect(&surface_blend, 0, 0,
|
|
surface->width, surface->height);
|
|
|
|
if (!(view->alpha < 1.0)) {
|
|
pixman_region32_subtract(&surface_blend, &surface_blend,
|
|
&surface->opaque);
|
|
|
|
if (pixman_region32_not_empty(&surface->opaque)) {
|
|
region_intersect_only_translation(&repaint_output,
|
|
repaint_global,
|
|
&surface->opaque,
|
|
view);
|
|
weston_region_global_to_output(&repaint_output,
|
|
output,
|
|
&repaint_output);
|
|
|
|
repaint_region(pnode, &repaint_output, NULL,
|
|
PIXMAN_OP_SRC);
|
|
}
|
|
}
|
|
|
|
if (pixman_region32_not_empty(&surface_blend)) {
|
|
region_intersect_only_translation(&repaint_output,
|
|
repaint_global,
|
|
&surface_blend, view);
|
|
weston_region_global_to_output(&repaint_output,
|
|
output,
|
|
&repaint_output);
|
|
|
|
repaint_region(pnode, &repaint_output, NULL, PIXMAN_OP_OVER);
|
|
}
|
|
|
|
pixman_region32_fini(&surface_blend);
|
|
pixman_region32_fini(&repaint_output);
|
|
}
|
|
|
|
static void
|
|
draw_node_source_clipped(struct weston_paint_node *pnode,
|
|
pixman_region32_t *repaint_global)
|
|
{
|
|
struct weston_surface *surface = pnode->surface;
|
|
struct weston_output *output = pnode->output;
|
|
struct weston_view *view = pnode->view;
|
|
pixman_region32_t surf_region;
|
|
pixman_region32_t buffer_region;
|
|
pixman_region32_t repaint_output;
|
|
|
|
/* Do not bother separating the opaque region from non-opaque.
|
|
* Source clipping requires PIXMAN_OP_OVER in all cases, so painting
|
|
* opaque separately has no benefit.
|
|
*/
|
|
|
|
pixman_region32_init_rect(&surf_region, 0, 0,
|
|
surface->width, surface->height);
|
|
if (view->geometry.scissor_enabled)
|
|
pixman_region32_intersect(&surf_region, &surf_region,
|
|
&view->geometry.scissor);
|
|
|
|
pixman_region32_init(&buffer_region);
|
|
weston_surface_to_buffer_region(surface, &surf_region, &buffer_region);
|
|
|
|
pixman_region32_init(&repaint_output);
|
|
pixman_region32_copy(&repaint_output, repaint_global);
|
|
weston_region_global_to_output(&repaint_output, output,
|
|
&repaint_output);
|
|
|
|
repaint_region(pnode, &repaint_output, &buffer_region, PIXMAN_OP_OVER);
|
|
|
|
pixman_region32_fini(&repaint_output);
|
|
pixman_region32_fini(&buffer_region);
|
|
pixman_region32_fini(&surf_region);
|
|
}
|
|
|
|
static void
|
|
draw_paint_node(struct weston_paint_node *pnode,
|
|
pixman_region32_t *damage /* in global coordinates */)
|
|
{
|
|
struct pixman_surface_state *ps = get_surface_state(pnode->surface);
|
|
/* repaint bounding region in global coordinates: */
|
|
pixman_region32_t repaint;
|
|
|
|
if (!pnode->surf_xform_valid)
|
|
return;
|
|
|
|
assert(pnode->surf_xform.transform == NULL);
|
|
|
|
/* No buffer attached */
|
|
if (!ps->image)
|
|
return;
|
|
|
|
/* if we still have a reference, but the underlying buffer is no longer
|
|
* available signal that we should unref image_t as well. This happens
|
|
* when using close animations, with the reference surviving the
|
|
* animation while the underlying buffer went away as the client was
|
|
* terminated. This is a particular use-case and should probably be
|
|
* refactored to provide some analogue with the GL-renderer (as in, to
|
|
* still maintain the buffer and let the compositor dispose of it). */
|
|
if (ps->buffer_ref.buffer && !ps->buffer_ref.buffer->shm_buffer) {
|
|
pixman_image_unref(ps->image);
|
|
ps->image = NULL;
|
|
return;
|
|
}
|
|
|
|
pixman_region32_init(&repaint);
|
|
pixman_region32_intersect(&repaint,
|
|
&pnode->visible, damage);
|
|
|
|
if (!pixman_region32_not_empty(&repaint))
|
|
goto out;
|
|
|
|
if (view_transformation_is_translation(pnode->view)) {
|
|
/* The simple case: The surface regions opaque, non-opaque,
|
|
* etc. are convertible to global coordinate space.
|
|
* There is no need to use a source clip region.
|
|
* It is possible to paint opaque region as PIXMAN_OP_SRC.
|
|
* Also the boundingbox is accurate rather than an
|
|
* approximation.
|
|
*/
|
|
draw_node_translated(pnode, &repaint);
|
|
} else {
|
|
/* The complex case: the view transformation does not allow
|
|
* converting opaque etc. regions into global coordinate space.
|
|
* Therefore we need source clipping to avoid sampling from
|
|
* unwanted source image areas, unless the source image is
|
|
* to be used whole. Source clipping does not work with
|
|
* PIXMAN_OP_SRC.
|
|
*/
|
|
draw_node_source_clipped(pnode, &repaint);
|
|
}
|
|
|
|
out:
|
|
pixman_region32_fini(&repaint);
|
|
}
|
|
static void
|
|
repaint_surfaces(struct weston_output *output, pixman_region32_t *damage)
|
|
{
|
|
struct weston_paint_node *pnode;
|
|
|
|
wl_list_for_each_reverse(pnode, &output->paint_node_z_order_list,
|
|
z_order_link) {
|
|
if (pnode->plane == &output->primary_plane)
|
|
draw_paint_node(pnode, damage);
|
|
}
|
|
}
|
|
|
|
static void
|
|
copy_to_hw_buffer(struct weston_output *output, pixman_region32_t *region)
|
|
{
|
|
struct pixman_output_state *po = get_output_state(output);
|
|
pixman_region32_t output_region;
|
|
|
|
pixman_region32_init(&output_region);
|
|
pixman_region32_copy(&output_region, region);
|
|
|
|
weston_region_global_to_output(&output_region, output, &output_region);
|
|
|
|
pixman_image_set_clip_region32 (po->hw_buffer, &output_region);
|
|
pixman_region32_fini(&output_region);
|
|
|
|
pixman_image_composite32(PIXMAN_OP_SRC,
|
|
po->shadow_image, /* src */
|
|
NULL /* mask */,
|
|
po->hw_buffer, /* dest */
|
|
0, 0, /* src_x, src_y */
|
|
0, 0, /* mask_x, mask_y */
|
|
0, 0, /* dest_x, dest_y */
|
|
po->fb_size.width, /* width */
|
|
po->fb_size.height /* height */);
|
|
|
|
pixman_image_set_clip_region32 (po->hw_buffer, NULL);
|
|
}
|
|
|
|
static void
|
|
pixman_renderer_do_capture(struct weston_buffer *into, pixman_image_t *from)
|
|
{
|
|
struct wl_shm_buffer *shm = into->shm_buffer;
|
|
pixman_image_t *dest;
|
|
|
|
assert(into->type == WESTON_BUFFER_SHM);
|
|
assert(shm);
|
|
|
|
wl_shm_buffer_begin_access(shm);
|
|
|
|
dest = pixman_image_create_bits(into->pixel_format->pixman_format,
|
|
into->width, into->height,
|
|
wl_shm_buffer_get_data(shm),
|
|
wl_shm_buffer_get_stride(shm));
|
|
abort_oom_if_null(dest);
|
|
|
|
pixman_image_composite32(PIXMAN_OP_SRC, from, NULL /* mask */, dest,
|
|
0, 0, /* src_x, src_y */
|
|
0, 0, /* mask_x, mask_y */
|
|
0, 0, /* dest_x, dest_y */
|
|
into->width, into->height);
|
|
|
|
pixman_image_unref(dest);
|
|
|
|
wl_shm_buffer_end_access(shm);
|
|
}
|
|
|
|
static void
|
|
pixman_renderer_do_capture_tasks(struct weston_output *output,
|
|
enum weston_output_capture_source source,
|
|
pixman_image_t *from,
|
|
const struct pixel_format_info *pfmt)
|
|
{
|
|
int width = pixman_image_get_width(from);
|
|
int height = pixman_image_get_height(from);
|
|
struct weston_capture_task *ct;
|
|
|
|
while ((ct = weston_output_pull_capture_task(output, source,
|
|
width, height,
|
|
pfmt))) {
|
|
struct weston_buffer *buffer = weston_capture_task_get_buffer(ct);
|
|
|
|
assert(buffer->width == width);
|
|
assert(buffer->height == height);
|
|
assert(buffer->pixel_format->format == pfmt->format);
|
|
|
|
if (buffer->type != WESTON_BUFFER_SHM) {
|
|
weston_capture_task_retire_failed(ct, "pixman: unsupported buffer");
|
|
continue;
|
|
}
|
|
|
|
pixman_renderer_do_capture(buffer, from);
|
|
weston_capture_task_retire_complete(ct);
|
|
}
|
|
}
|
|
|
|
static void
|
|
pixman_renderer_output_set_buffer(struct weston_output *output,
|
|
pixman_image_t *buffer);
|
|
|
|
static void
|
|
pixman_renderer_repaint_output(struct weston_output *output,
|
|
pixman_region32_t *output_damage,
|
|
struct weston_renderbuffer *renderbuffer)
|
|
{
|
|
struct pixman_output_state *po = get_output_state(output);
|
|
struct pixman_renderbuffer *rb;
|
|
|
|
assert(renderbuffer);
|
|
|
|
rb = to_pixman_renderbuffer(renderbuffer);
|
|
|
|
pixman_renderer_output_set_buffer(output, rb->image);
|
|
|
|
assert(output->from_blend_to_output_by_backend ||
|
|
output->color_outcome->from_blend_to_output == NULL);
|
|
|
|
if (!po->hw_buffer)
|
|
return;
|
|
|
|
/* Accumulate damage in all renderbuffers */
|
|
wl_list_for_each(rb, &po->renderbuffer_list, link) {
|
|
pixman_region32_union(&rb->base.damage,
|
|
&rb->base.damage,
|
|
output_damage);
|
|
}
|
|
|
|
if (po->shadow_image) {
|
|
repaint_surfaces(output, output_damage);
|
|
pixman_renderer_do_capture_tasks(output,
|
|
WESTON_OUTPUT_CAPTURE_SOURCE_BLENDING,
|
|
po->shadow_image, po->shadow_format);
|
|
copy_to_hw_buffer(output, &renderbuffer->damage);
|
|
} else {
|
|
repaint_surfaces(output, &renderbuffer->damage);
|
|
}
|
|
pixman_renderer_do_capture_tasks(output,
|
|
WESTON_OUTPUT_CAPTURE_SOURCE_FRAMEBUFFER,
|
|
po->hw_buffer, po->hw_format);
|
|
pixman_region32_clear(&renderbuffer->damage);
|
|
|
|
wl_signal_emit(&output->frame_signal, output_damage);
|
|
|
|
/* Actual flip should be done by caller */
|
|
}
|
|
|
|
static void
|
|
pixman_renderer_flush_damage(struct weston_surface *surface,
|
|
struct weston_buffer *buffer,
|
|
struct weston_output *output)
|
|
{
|
|
/* No-op for pixman renderer */
|
|
}
|
|
|
|
static void
|
|
buffer_state_handle_buffer_destroy(struct wl_listener *listener, void *data)
|
|
{
|
|
struct pixman_surface_state *ps;
|
|
|
|
ps = container_of(listener, struct pixman_surface_state,
|
|
buffer_destroy_listener);
|
|
|
|
if (ps->image) {
|
|
pixman_image_unref(ps->image);
|
|
ps->image = NULL;
|
|
}
|
|
|
|
ps->buffer_destroy_listener.notify = NULL;
|
|
}
|
|
|
|
static void
|
|
pixman_renderer_surface_set_color(struct weston_surface *es,
|
|
float red, float green, float blue, float alpha)
|
|
{
|
|
struct pixman_surface_state *ps = get_surface_state(es);
|
|
pixman_color_t color;
|
|
|
|
color.red = red * 0xffff;
|
|
color.green = green * 0xffff;
|
|
color.blue = blue * 0xffff;
|
|
color.alpha = alpha * 0xffff;
|
|
|
|
if (ps->image) {
|
|
pixman_image_unref(ps->image);
|
|
ps->image = NULL;
|
|
}
|
|
|
|
ps->image = pixman_image_create_solid_fill(&color);
|
|
}
|
|
|
|
static void
|
|
pixman_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer)
|
|
{
|
|
struct pixman_surface_state *ps = get_surface_state(es);
|
|
struct wl_shm_buffer *shm_buffer;
|
|
const struct pixel_format_info *pixel_info;
|
|
|
|
weston_buffer_reference(&ps->buffer_ref, buffer,
|
|
buffer ? BUFFER_MAY_BE_ACCESSED :
|
|
BUFFER_WILL_NOT_BE_ACCESSED);
|
|
weston_buffer_release_reference(&ps->buffer_release_ref,
|
|
es->buffer_release_ref.buffer_release);
|
|
|
|
if (ps->buffer_destroy_listener.notify) {
|
|
wl_list_remove(&ps->buffer_destroy_listener.link);
|
|
ps->buffer_destroy_listener.notify = NULL;
|
|
}
|
|
|
|
if (ps->image) {
|
|
pixman_image_unref(ps->image);
|
|
ps->image = NULL;
|
|
}
|
|
|
|
if (!buffer)
|
|
return;
|
|
|
|
if (buffer->type == WESTON_BUFFER_SOLID) {
|
|
pixman_renderer_surface_set_color(es,
|
|
buffer->solid.r,
|
|
buffer->solid.g,
|
|
buffer->solid.b,
|
|
buffer->solid.a);
|
|
weston_buffer_reference(&ps->buffer_ref, NULL,
|
|
BUFFER_WILL_NOT_BE_ACCESSED);
|
|
weston_buffer_release_reference(&ps->buffer_release_ref, NULL);
|
|
return;
|
|
}
|
|
|
|
if (buffer->type != WESTON_BUFFER_SHM) {
|
|
weston_log("Pixman renderer supports only SHM buffers\n");
|
|
weston_buffer_reference(&ps->buffer_ref, NULL,
|
|
BUFFER_WILL_NOT_BE_ACCESSED);
|
|
weston_buffer_release_reference(&ps->buffer_release_ref, NULL);
|
|
return;
|
|
}
|
|
|
|
shm_buffer = buffer->shm_buffer;
|
|
|
|
pixel_info = pixel_format_get_info_shm(wl_shm_buffer_get_format(shm_buffer));
|
|
if (!pixel_info || !pixman_format_supported_source(pixel_info->pixman_format)) {
|
|
weston_log("Unsupported SHM buffer format 0x%x\n",
|
|
wl_shm_buffer_get_format(shm_buffer));
|
|
weston_buffer_reference(&ps->buffer_ref, NULL,
|
|
BUFFER_WILL_NOT_BE_ACCESSED);
|
|
weston_buffer_release_reference(&ps->buffer_release_ref, NULL);
|
|
weston_buffer_send_server_error(buffer,
|
|
"disconnecting due to unhandled buffer type");
|
|
return;
|
|
}
|
|
|
|
ps->image = pixman_image_create_bits(pixel_info->pixman_format,
|
|
buffer->width, buffer->height,
|
|
wl_shm_buffer_get_data(shm_buffer),
|
|
wl_shm_buffer_get_stride(shm_buffer));
|
|
|
|
ps->buffer_destroy_listener.notify =
|
|
buffer_state_handle_buffer_destroy;
|
|
wl_signal_add(&buffer->destroy_signal,
|
|
&ps->buffer_destroy_listener);
|
|
}
|
|
|
|
static void
|
|
pixman_renderer_surface_state_destroy(struct pixman_surface_state *ps)
|
|
{
|
|
wl_list_remove(&ps->surface_destroy_listener.link);
|
|
wl_list_remove(&ps->renderer_destroy_listener.link);
|
|
if (ps->buffer_destroy_listener.notify) {
|
|
wl_list_remove(&ps->buffer_destroy_listener.link);
|
|
ps->buffer_destroy_listener.notify = NULL;
|
|
}
|
|
|
|
ps->surface->renderer_state = NULL;
|
|
|
|
if (ps->image) {
|
|
pixman_image_unref(ps->image);
|
|
ps->image = NULL;
|
|
}
|
|
weston_buffer_reference(&ps->buffer_ref, NULL,
|
|
BUFFER_WILL_NOT_BE_ACCESSED);
|
|
weston_buffer_release_reference(&ps->buffer_release_ref, NULL);
|
|
free(ps);
|
|
}
|
|
|
|
static void
|
|
surface_state_handle_surface_destroy(struct wl_listener *listener, void *data)
|
|
{
|
|
struct pixman_surface_state *ps;
|
|
|
|
ps = container_of(listener, struct pixman_surface_state,
|
|
surface_destroy_listener);
|
|
|
|
pixman_renderer_surface_state_destroy(ps);
|
|
}
|
|
|
|
static void
|
|
surface_state_handle_renderer_destroy(struct wl_listener *listener, void *data)
|
|
{
|
|
struct pixman_surface_state *ps;
|
|
|
|
ps = container_of(listener, struct pixman_surface_state,
|
|
renderer_destroy_listener);
|
|
|
|
pixman_renderer_surface_state_destroy(ps);
|
|
}
|
|
|
|
static int
|
|
pixman_renderer_create_surface(struct weston_surface *surface)
|
|
{
|
|
struct pixman_surface_state *ps;
|
|
struct pixman_renderer *pr = get_renderer(surface->compositor);
|
|
|
|
ps = zalloc(sizeof *ps);
|
|
if (ps == NULL)
|
|
return -1;
|
|
|
|
surface->renderer_state = ps;
|
|
|
|
ps->surface = surface;
|
|
|
|
ps->surface_destroy_listener.notify =
|
|
surface_state_handle_surface_destroy;
|
|
wl_signal_add(&surface->destroy_signal,
|
|
&ps->surface_destroy_listener);
|
|
|
|
ps->renderer_destroy_listener.notify =
|
|
surface_state_handle_renderer_destroy;
|
|
wl_signal_add(&pr->destroy_signal,
|
|
&ps->renderer_destroy_listener);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
pixman_renderer_destroy(struct weston_compositor *ec)
|
|
{
|
|
struct pixman_renderer *pr = get_renderer(ec);
|
|
|
|
wl_signal_emit(&pr->destroy_signal, pr);
|
|
weston_binding_destroy(pr->debug_binding);
|
|
free(pr);
|
|
|
|
ec->renderer = NULL;
|
|
}
|
|
|
|
static int
|
|
pixman_renderer_surface_copy_content(struct weston_surface *surface,
|
|
void *target, size_t size,
|
|
int src_x, int src_y,
|
|
int width, int height)
|
|
{
|
|
const pixman_format_code_t format = PIXMAN_a8b8g8r8;
|
|
const size_t bytespp = 4; /* PIXMAN_a8b8g8r8 */
|
|
struct pixman_surface_state *ps = get_surface_state(surface);
|
|
pixman_image_t *out_buf;
|
|
|
|
if (!ps->image)
|
|
return -1;
|
|
|
|
out_buf = pixman_image_create_bits(format, width, height,
|
|
target, width * bytespp);
|
|
|
|
pixman_image_set_transform(ps->image, NULL);
|
|
pixman_image_composite32(PIXMAN_OP_SRC,
|
|
ps->image, /* src */
|
|
NULL, /* mask */
|
|
out_buf, /* dest */
|
|
src_x, src_y, /* src_x, src_y */
|
|
0, 0, /* mask_x, mask_y */
|
|
0, 0, /* dest_x, dest_y */
|
|
width, height);
|
|
|
|
pixman_image_unref(out_buf);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool
|
|
pixman_renderer_resize_output(struct weston_output *output,
|
|
const struct weston_size *fb_size,
|
|
const struct weston_geometry *area)
|
|
{
|
|
struct pixman_output_state *po = get_output_state(output);
|
|
struct pixman_renderbuffer *renderbuffer, *tmp;
|
|
|
|
check_compositing_area(fb_size, area);
|
|
|
|
/*
|
|
* Pixman-renderer does not implement output decorations blitting,
|
|
* wayland-backend does it on its own.
|
|
*/
|
|
assert(area->x == 0);
|
|
assert(area->y == 0);
|
|
assert(fb_size->width == area->width);
|
|
assert(fb_size->height == area->height);
|
|
|
|
pixman_renderer_output_set_buffer(output, NULL);
|
|
|
|
wl_list_for_each_safe(renderbuffer, tmp, &po->renderbuffer_list, link) {
|
|
wl_list_remove(&renderbuffer->link);
|
|
weston_renderbuffer_unref(&renderbuffer->base);
|
|
}
|
|
|
|
po->fb_size = *fb_size;
|
|
|
|
/*
|
|
* Have a hw_format only after the first call to
|
|
* pixman_renderer_output_set_buffer().
|
|
*/
|
|
if (po->hw_format) {
|
|
weston_output_update_capture_info(output,
|
|
WESTON_OUTPUT_CAPTURE_SOURCE_FRAMEBUFFER,
|
|
po->fb_size.width,
|
|
po->fb_size.height,
|
|
po->hw_format);
|
|
}
|
|
|
|
if (!po->shadow_format)
|
|
return true;
|
|
|
|
if (po->shadow_image)
|
|
pixman_image_unref(po->shadow_image);
|
|
|
|
po->shadow_image =
|
|
pixman_image_create_bits_no_clear(po->shadow_format->pixman_format,
|
|
fb_size->width, fb_size->height,
|
|
NULL, 0);
|
|
|
|
weston_output_update_capture_info(output,
|
|
WESTON_OUTPUT_CAPTURE_SOURCE_BLENDING,
|
|
po->fb_size.width,
|
|
po->fb_size.height,
|
|
po->shadow_format);
|
|
|
|
return !!po->shadow_image;
|
|
}
|
|
|
|
static void
|
|
debug_binding(struct weston_keyboard *keyboard, const struct timespec *time,
|
|
uint32_t key, void *data)
|
|
{
|
|
struct weston_compositor *ec = data;
|
|
struct pixman_renderer *pr = (struct pixman_renderer *) ec->renderer;
|
|
|
|
pr->repaint_debug ^= 1;
|
|
|
|
if (pr->repaint_debug) {
|
|
pixman_color_t red = {
|
|
0x3fff, 0x0000, 0x0000, 0x3fff
|
|
};
|
|
|
|
pr->debug_color = pixman_image_create_solid_fill(&red);
|
|
} else {
|
|
pixman_image_unref(pr->debug_color);
|
|
weston_compositor_damage_all(ec);
|
|
}
|
|
}
|
|
|
|
static struct pixman_renderer_interface pixman_renderer_interface;
|
|
|
|
WL_EXPORT int
|
|
pixman_renderer_init(struct weston_compositor *ec)
|
|
{
|
|
struct pixman_renderer *renderer;
|
|
const struct pixel_format_info *pixel_info, *info_argb8888, *info_xrgb8888;
|
|
unsigned int i, num_formats;
|
|
|
|
renderer = zalloc(sizeof *renderer);
|
|
if (renderer == NULL)
|
|
return -1;
|
|
|
|
renderer->repaint_debug = 0;
|
|
renderer->debug_color = NULL;
|
|
renderer->base.read_pixels = pixman_renderer_read_pixels;
|
|
renderer->base.repaint_output = pixman_renderer_repaint_output;
|
|
renderer->base.resize_output = pixman_renderer_resize_output;
|
|
renderer->base.flush_damage = pixman_renderer_flush_damage;
|
|
renderer->base.attach = pixman_renderer_attach;
|
|
renderer->base.destroy = pixman_renderer_destroy;
|
|
renderer->base.surface_copy_content =
|
|
pixman_renderer_surface_copy_content;
|
|
renderer->base.type = WESTON_RENDERER_PIXMAN;
|
|
renderer->base.pixman = &pixman_renderer_interface;
|
|
ec->renderer = &renderer->base;
|
|
ec->capabilities |= WESTON_CAP_ROTATION_ANY;
|
|
ec->capabilities |= WESTON_CAP_VIEW_CLIP_MASK;
|
|
|
|
renderer->debug_binding =
|
|
weston_compositor_add_debug_binding(ec, KEY_R,
|
|
debug_binding, ec);
|
|
|
|
info_argb8888 = pixel_format_get_info_shm(WL_SHM_FORMAT_ARGB8888);
|
|
info_xrgb8888 = pixel_format_get_info_shm(WL_SHM_FORMAT_XRGB8888);
|
|
|
|
num_formats = pixel_format_get_info_count();
|
|
for (i = 0; i < num_formats; i++) {
|
|
pixel_info = pixel_format_get_info_by_index(i);
|
|
if (!pixman_format_supported_source(pixel_info->pixman_format))
|
|
continue;
|
|
|
|
/* skip formats which libwayland registers by default */
|
|
if (pixel_info == info_argb8888 || pixel_info == info_xrgb8888)
|
|
continue;
|
|
|
|
wl_display_add_shm_format(ec->wl_display, pixel_info->format);
|
|
}
|
|
|
|
wl_signal_init(&renderer->destroy_signal);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
pixman_renderer_output_set_buffer(struct weston_output *output,
|
|
pixman_image_t *buffer)
|
|
{
|
|
struct weston_compositor *compositor = output->compositor;
|
|
struct pixman_output_state *po = get_output_state(output);
|
|
pixman_format_code_t pixman_format;
|
|
|
|
if (po->hw_buffer)
|
|
pixman_image_unref(po->hw_buffer);
|
|
po->hw_buffer = buffer;
|
|
|
|
if (!po->hw_buffer)
|
|
return;
|
|
|
|
pixman_format = pixman_image_get_format(po->hw_buffer);
|
|
po->hw_format = pixel_format_get_info_by_pixman(pixman_format);
|
|
compositor->read_format = po->hw_format;
|
|
assert(po->hw_format);
|
|
|
|
pixman_image_ref(po->hw_buffer);
|
|
|
|
assert(po->fb_size.width == pixman_image_get_width(po->hw_buffer));
|
|
assert(po->fb_size.height == pixman_image_get_height(po->hw_buffer));
|
|
|
|
/*
|
|
* The size cannot change, but the format might, or we did not have
|
|
* hw_format in pixman_renderer_resize_output() yet.
|
|
*/
|
|
weston_output_update_capture_info(output,
|
|
WESTON_OUTPUT_CAPTURE_SOURCE_FRAMEBUFFER,
|
|
po->fb_size.width,
|
|
po->fb_size.height,
|
|
po->hw_format);
|
|
}
|
|
|
|
static int
|
|
pixman_renderer_output_create(struct weston_output *output,
|
|
const struct pixman_renderer_output_options *options)
|
|
{
|
|
struct pixman_output_state *po;
|
|
struct weston_geometry area = {
|
|
.x = 0,
|
|
.y = 0,
|
|
.width = options->fb_size.width,
|
|
.height = options->fb_size.height
|
|
};
|
|
|
|
po = zalloc(sizeof *po);
|
|
if (po == NULL)
|
|
return -1;
|
|
|
|
output->renderer_state = po;
|
|
|
|
if (options->use_shadow)
|
|
po->shadow_format = pixel_format_get_info(DRM_FORMAT_XRGB8888);
|
|
|
|
wl_list_init(&po->renderbuffer_list);
|
|
|
|
if (!pixman_renderer_resize_output(output, &options->fb_size, &area)) {
|
|
output->renderer_state = NULL;
|
|
free(po);
|
|
return -1;
|
|
}
|
|
|
|
weston_output_update_capture_info(output,
|
|
WESTON_OUTPUT_CAPTURE_SOURCE_FRAMEBUFFER,
|
|
area.width, area.height,
|
|
options->format);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
pixman_renderer_output_destroy(struct weston_output *output)
|
|
{
|
|
struct pixman_output_state *po = get_output_state(output);
|
|
struct pixman_renderbuffer *renderbuffer, *tmp;
|
|
|
|
if (po->shadow_image)
|
|
pixman_image_unref(po->shadow_image);
|
|
|
|
if (po->hw_buffer)
|
|
pixman_image_unref(po->hw_buffer);
|
|
|
|
po->shadow_image = NULL;
|
|
po->hw_buffer = NULL;
|
|
|
|
wl_list_for_each_safe(renderbuffer, tmp, &po->renderbuffer_list, link) {
|
|
wl_list_remove(&renderbuffer->link);
|
|
weston_renderbuffer_unref(&renderbuffer->base);
|
|
}
|
|
|
|
free(po);
|
|
}
|
|
|
|
static void
|
|
pixman_renderer_renderbuffer_destroy(struct weston_renderbuffer *renderbuffer);
|
|
|
|
static struct weston_renderbuffer *
|
|
pixman_renderer_create_image_from_ptr(struct weston_output *output,
|
|
const struct pixel_format_info *format,
|
|
int width, int height, uint32_t *ptr,
|
|
int rowstride)
|
|
{
|
|
struct pixman_output_state *po = get_output_state(output);
|
|
struct pixman_renderbuffer *renderbuffer;
|
|
|
|
assert(po);
|
|
|
|
renderbuffer = xzalloc(sizeof(*renderbuffer));
|
|
|
|
renderbuffer->image = pixman_image_create_bits(format->pixman_format,
|
|
width, height, ptr,
|
|
rowstride);
|
|
if (!renderbuffer->image) {
|
|
free(renderbuffer);
|
|
return NULL;
|
|
}
|
|
|
|
pixman_region32_init(&renderbuffer->base.damage);
|
|
renderbuffer->base.refcount = 2;
|
|
renderbuffer->base.destroy = pixman_renderer_renderbuffer_destroy;
|
|
wl_list_insert(&po->renderbuffer_list, &renderbuffer->link);
|
|
|
|
return &renderbuffer->base;
|
|
}
|
|
|
|
static struct weston_renderbuffer *
|
|
pixman_renderer_create_image(struct weston_output *output,
|
|
const struct pixel_format_info *format, int width,
|
|
int height)
|
|
{
|
|
struct pixman_output_state *po = get_output_state(output);
|
|
struct pixman_renderbuffer *renderbuffer;
|
|
|
|
assert(po);
|
|
|
|
renderbuffer = xzalloc(sizeof(*renderbuffer));
|
|
|
|
renderbuffer->image =
|
|
pixman_image_create_bits_no_clear(format->pixman_format, width,
|
|
height, NULL, 0);
|
|
if (!renderbuffer->image) {
|
|
free(renderbuffer);
|
|
return NULL;
|
|
}
|
|
|
|
pixman_region32_init(&renderbuffer->base.damage);
|
|
renderbuffer->base.refcount = 2;
|
|
renderbuffer->base.destroy = pixman_renderer_renderbuffer_destroy;
|
|
wl_list_insert(&po->renderbuffer_list, &renderbuffer->link);
|
|
|
|
return &renderbuffer->base;
|
|
}
|
|
|
|
static void
|
|
pixman_renderer_renderbuffer_destroy(struct weston_renderbuffer *renderbuffer)
|
|
{
|
|
struct pixman_renderbuffer *rb = to_pixman_renderbuffer(renderbuffer);
|
|
|
|
pixman_image_unref(rb->image);
|
|
pixman_region32_fini(&rb->base.damage);
|
|
free(rb);
|
|
}
|
|
|
|
static struct pixman_renderer_interface pixman_renderer_interface = {
|
|
.output_create = pixman_renderer_output_create,
|
|
.output_destroy = pixman_renderer_output_destroy,
|
|
.create_image_from_ptr = pixman_renderer_create_image_from_ptr,
|
|
.create_image = pixman_renderer_create_image,
|
|
.renderbuffer_get_image = pixman_renderer_renderbuffer_get_image,
|
|
};
|