diff options
| author | Simon Zeni <simon@bl4ckb0ne.ca> | 2021-01-17 23:15:36 -0500 | 
|---|---|---|
| committer | Simon Ser <contact@emersion.fr> | 2021-04-17 09:54:39 +0200 | 
| commit | 0d90dddfab48a12a7519c11c6062b717ca3b7581 (patch) | |
| tree | fad73cd0ce24586d5b8a4495910343d59905b010 /render | |
| parent | 9de93a866fd4c7a51ffde7c73454dc0c39bf5fb1 (diff) | |
| download | wlroots-0d90dddfab48a12a7519c11c6062b717ca3b7581.tar.xz | |
render: introduce pixman renderer
Diffstat (limited to 'render')
| -rw-r--r-- | render/meson.build | 1 | ||||
| -rw-r--r-- | render/pixman/meson.build | 7 | ||||
| -rw-r--r-- | render/pixman/pixel_format.c | 59 | ||||
| -rw-r--r-- | render/pixman/renderer.c | 383 | ||||
| -rw-r--r-- | render/wlr_renderer.c | 1 | 
5 files changed, 451 insertions, 0 deletions
| diff --git a/render/meson.build b/render/meson.build index ee945377..ab0c4424 100644 --- a/render/meson.build +++ b/render/meson.build @@ -12,3 +12,4 @@ wlr_files += files(  )  subdir('gles2') +subdir('pixman') diff --git a/render/pixman/meson.build b/render/pixman/meson.build new file mode 100644 index 00000000..eb7a0d3a --- /dev/null +++ b/render/pixman/meson.build @@ -0,0 +1,7 @@ +pixman = dependency('pixman-1') +wlr_deps += pixman + +wlr_files += files( +	'pixel_format.c', +	'renderer.c', +) diff --git a/render/pixman/pixel_format.c b/render/pixman/pixel_format.c new file mode 100644 index 00000000..a57c6c34 --- /dev/null +++ b/render/pixman/pixel_format.c @@ -0,0 +1,59 @@ +#include <drm_fourcc.h> +#include <wlr/util/log.h> + +#include "render/pixman.h" + +static const struct wlr_pixman_pixel_format formats[] = { +	{ +		.drm_format = DRM_FORMAT_ARGB8888, +#if WLR_BIG_ENDIAN +		.pixman_format = PIXMAN_b8g8r8a8, +#else +		.pixman_format = PIXMAN_a8r8g8b8, +#endif +	}, +	{ +		.drm_format = DRM_FORMAT_XBGR8888, +#if WLR_BIG_ENDIAN +		.pixman_format = PIXMAN_r8g8b8x8, +#else +		.pixman_format = PIXMAN_x8b8g8r8, +#endif +	}, +	{ +		.drm_format = DRM_FORMAT_XRGB8888, +#if WLR_BIG_ENDIAN +		.pixman_format = PIXMAN_b8g8r8x8, +#else +		.pixman_format = PIXMAN_x8r8g8b8, +#endif +	}, +	{ +		.drm_format = DRM_FORMAT_ABGR8888, +#if WLR_BIG_ENDIAN +		.pixman_format = PIXMAN_r8g8b8a8, +#else +		.pixman_format = PIXMAN_a8b8g8r8, +#endif +	} +}; + +pixman_format_code_t get_pixman_format_from_drm(uint32_t fmt) { +	for (size_t i = 0; i < sizeof(formats) / sizeof(*formats); ++i) { +		if (formats[i].drm_format == fmt) { +			return formats[i].pixman_format; +		} +	} + +	wlr_log(WLR_ERROR, "DRM format 0x%"PRIX32" has no pixman equivalent", fmt); +	return 0; +} + +const uint32_t *get_pixman_drm_formats(size_t *len) { +	static uint32_t drm_formats[sizeof(formats) / sizeof(formats[0])]; +	*len = sizeof(formats) / sizeof(formats[0]); +	for (size_t i = 0; i < sizeof(formats) / sizeof(formats[0]); i++) { +		drm_formats[i] = formats[i].drm_format; +	} +	return drm_formats; +} diff --git a/render/pixman/renderer.c b/render/pixman/renderer.c new file mode 100644 index 00000000..466b71d0 --- /dev/null +++ b/render/pixman/renderer.c @@ -0,0 +1,383 @@ +#include <assert.h> +#include <drm_fourcc.h> +#include <pixman.h> +#include <stdlib.h> +#include <wayland-server.h> +#include <wlr/render/interface.h> +#include <wlr/types/wlr_matrix.h> +#include <wlr/util/log.h> + +#include "render/pixman.h" +#include "types/wlr_buffer.h" + +static const struct wlr_renderer_impl renderer_impl; + +static struct wlr_pixman_renderer *get_renderer( +		struct wlr_renderer *wlr_renderer) { +	assert(wlr_renderer->impl == &renderer_impl); +	return (struct wlr_pixman_renderer *)wlr_renderer; +} + +static struct wlr_pixman_buffer *get_buffer( +		struct wlr_pixman_renderer *renderer, struct wlr_buffer *wlr_buffer) { +	struct wlr_pixman_buffer *buffer; +	wl_list_for_each(buffer, &renderer->buffers, link) { +		if (buffer->buffer == wlr_buffer) { +			return buffer; +		} +	} +	return NULL; +} + +static const struct wlr_texture_impl texture_impl; + +static struct wlr_pixman_texture *get_texture( +		struct wlr_texture *wlr_texture) { +	assert(wlr_texture->impl == &texture_impl); +	return (struct wlr_pixman_texture *)wlr_texture; +} + +static bool texture_is_opaque(struct wlr_texture *wlr_texture) { +	struct wlr_pixman_texture *texture = get_texture(wlr_texture); +	return texture->format->has_alpha; +} + +static void texture_destroy(struct wlr_texture *wlr_texture) { +	if (wlr_texture == NULL) { +		return; +	} +	struct wlr_pixman_texture *texture = get_texture(wlr_texture); + +	pixman_image_unref(texture->image); +	free(texture->data); +	free(texture); +} + +static const struct wlr_texture_impl texture_impl = { +	.is_opaque = texture_is_opaque, +	.destroy = texture_destroy, +}; + +struct wlr_pixman_texture *pixman_create_texture( +		struct wlr_texture *wlr_texture, struct wlr_pixman_renderer *renderer); + +static void handle_destroy_buffer(struct wl_listener *listener, void *data) { +	struct wlr_pixman_buffer *buffer = +		wl_container_of(listener, buffer, buffer_destroy); +	wl_list_remove(&buffer->link); +	wl_list_remove(&buffer->buffer_destroy.link); + +	pixman_image_unref(buffer->image); + +	free(buffer); +} + +static struct wlr_pixman_buffer *create_buffer( +		struct wlr_pixman_renderer *renderer, struct wlr_buffer *wlr_buffer) { +	struct wlr_pixman_buffer *buffer = calloc(1, sizeof(*buffer)); +	if (buffer == NULL) { +		wlr_log_errno(WLR_ERROR, "Allocation failed"); +		return NULL; +	} +	buffer->buffer = wlr_buffer; +	buffer->renderer = renderer; + +	uint32_t drm_format; +	struct wlr_dmabuf_attributes dmabuf = {0}; +	struct wlr_shm_attributes shm = {0}; +	if (wlr_buffer_get_dmabuf(wlr_buffer, &dmabuf)) { +		drm_format = dmabuf.format; +	} else if (wlr_buffer_get_shm(wlr_buffer, &shm)) { +		drm_format = shm.format; +	} else { +		goto error_buffer; +	} + +	uint32_t format = get_pixman_format_from_drm(drm_format); + +	void *data = NULL; +	size_t stride; +	if (!buffer_get_data_ptr(wlr_buffer, &data, &stride)) { +		wlr_log(WLR_ERROR, "Failed to get buffer data"); +		goto error_buffer; +	} + +	buffer->image = pixman_image_create_bits(format, wlr_buffer->width, +			wlr_buffer->height, data, stride); +	if (!buffer->image) { +		wlr_log(WLR_ERROR, "Failed to allocate pixman image"); +		goto error_buffer; +	} + +	buffer->buffer_destroy.notify = handle_destroy_buffer; +	wl_signal_add(&wlr_buffer->events.destroy, &buffer->buffer_destroy); + +	wl_list_insert(&renderer->buffers, &buffer->link); + +	wlr_log(WLR_DEBUG, "Created pixman buffer %dx%d", +		wlr_buffer->width, wlr_buffer->height); + +	return buffer; + +error_buffer: +	free(buffer); +	return NULL; +} + +static void pixman_begin(struct wlr_renderer *wlr_renderer, uint32_t width, +		uint32_t height) { +	struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); +	renderer->width = width; +	renderer->height = height; +} + +static void pixman_clear(struct wlr_renderer *wlr_renderer, +		const float color[static 4]) { +	struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); +	struct wlr_pixman_buffer *buffer = renderer->current_buffer; + +	const struct pixman_color colour = { +		.red = color[0] * 0xFFFF, +		.green = color[1] * 0xFFFF, +		.blue = color[2] * 0xFFFF, +		.alpha = color[3] * 0xFFFF, +	}; + +	pixman_image_t *fill = pixman_image_create_solid_fill(&colour); + +	pixman_image_composite32(PIXMAN_OP_SRC, fill, NULL, buffer->image, 0, 0, 0, +			0, 0, 0, renderer->width, renderer->height); + +	pixman_image_unref(fill); +} + +static void pixman_scissor(struct wlr_renderer *wlr_renderer, +		struct wlr_box *box) { +	struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); +	struct wlr_pixman_buffer *buffer = renderer->current_buffer; + +	if (box != NULL) { +		struct pixman_region32 region = {0}; +		pixman_region32_init_rect(®ion, box->x, box->y, box->width, +				box->height); +		pixman_image_set_clip_region32(buffer->image, ®ion); +		pixman_region32_fini(®ion); +	} else { +		pixman_image_set_clip_region32(buffer->image, NULL); +	} +} + +static void matrix_to_pixman_transform(struct pixman_transform *transform, +		const float mat[static 9]) { +	struct pixman_f_transform ftr; +	ftr.m[0][0] = mat[0]; +	ftr.m[0][1] = mat[1]; +	ftr.m[0][2] = mat[2]; +	ftr.m[1][0] = mat[3]; +	ftr.m[1][1] = mat[4]; +	ftr.m[1][2] = mat[5]; +	ftr.m[2][0] = mat[6]; +	ftr.m[2][1] = mat[7]; +	ftr.m[2][2] = mat[8]; + +	pixman_transform_from_pixman_f_transform(transform, &ftr); +} + +static bool pixman_render_subtexture_with_matrix( +		struct wlr_renderer *wlr_renderer, struct wlr_texture *wlr_texture, +		const struct wlr_fbox *fbox, const float matrix[static 9], +		float alpha) { +	struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); +	struct wlr_pixman_texture *texture = get_texture(wlr_texture); +	struct wlr_pixman_buffer *buffer = renderer->current_buffer; + +	// TODO: don't create a mask if alpha == 1.0 +	struct pixman_color mask_colour = {0}; +	mask_colour.alpha = 0xFFFF * alpha; +	pixman_image_t *mask = pixman_image_create_solid_fill(&mask_colour); + +	float m[9]; +	memcpy(m, matrix, sizeof(m)); +	wlr_matrix_scale(m, 1.0 / fbox->width, 1.0 / fbox->height); + +	struct pixman_transform transform = {0}; +	matrix_to_pixman_transform(&transform, m); +	pixman_transform_invert(&transform, &transform); + +	pixman_image_set_transform(texture->image, &transform); + +	// TODO clip properly with src_x and src_y +	pixman_image_composite32(PIXMAN_OP_OVER, texture->image, mask, +			buffer->image, 0, 0, 0, 0, 0, 0, renderer->width, +			renderer->height); + +	pixman_image_unref(mask); + +	return true; +} + +static void pixman_render_quad_with_matrix(struct wlr_renderer *wlr_renderer, +		const float color[static 4], const float matrix[static 9]) { +	struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); +	struct wlr_pixman_buffer *buffer = renderer->current_buffer; + +	struct pixman_color colour = { +		.red = color[0] * 0xFFFF, +		.green = color[1] * 0xFFFF, +		.blue = color[2] * 0xFFFF, +		.alpha = color[3] * 0xFFFF, +	}; + +	pixman_image_t *fill = pixman_image_create_solid_fill(&colour); + +	float m[9]; +	memcpy(m, matrix, sizeof(m)); + +	// TODO get the width/height from the caller instead of extracting them +	// TODO detect non rotation with matrix[1] == 0 and matrix[4] == 0 to avoid +	// doing the calculation +	float width = sqrt(matrix[0] * matrix[0] + matrix[1] * matrix[1]); +	float height = sqrt(matrix[3] * matrix[3] + matrix[4] * matrix[4]); + +	wlr_matrix_scale(m, 1.0 / width, 1.0 / height); + +	pixman_image_t *image = pixman_image_create_bits(PIXMAN_a8r8g8b8, width, +			height, NULL, 0); + +	// TODO find a way to fill the image without allocating 2 images +	pixman_image_composite32 (PIXMAN_OP_SRC, fill, NULL, image, +		0, 0, 0, 0, 0, 0, width, height); +	pixman_image_unref(fill); + +	struct pixman_transform transform = {0}; +	matrix_to_pixman_transform(&transform, m); +	pixman_transform_invert(&transform, &transform); + +	pixman_image_set_transform(image, &transform); + +	pixman_image_composite32(PIXMAN_OP_OVER, image, NULL, buffer->image, +			0, 0, 0, 0, 0, 0, renderer->width, renderer->height); + +	pixman_image_unref(image); +} + +static const uint32_t *pixman_get_shm_texture_formats( +		struct wlr_renderer *wlr_renderer, size_t *len) { +	return get_pixman_drm_formats(len); +} + +static const struct wlr_drm_format_set *pixman_get_render_formats( +		struct wlr_renderer *wlr_renderer) { +	struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); +	return &renderer->drm_formats; +} + +static struct wlr_texture *pixman_texture_from_pixels( +		struct wlr_renderer *wlr_renderer, uint32_t drm_format, +		uint32_t stride, uint32_t width, uint32_t height, const void *data) { +	struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); +	struct wlr_pixman_texture *texture = +		calloc(1, sizeof(struct wlr_pixman_texture)); +	if (texture == NULL) { +		wlr_log(WLR_ERROR, "Failed to allocate pixman texture"); +		return NULL; +	} + +	wlr_texture_init(&texture->wlr_texture, &texture_impl, width, height); +	texture->renderer = renderer; + +	texture->format = drm_get_pixel_format_info(drm_format); +	if (!texture->format) { +		wlr_log(WLR_ERROR, "Unsupported drm format 0x%"PRIX32, drm_format); +		return NULL; +	} + +	pixman_format_code_t format = get_pixman_format_from_drm(drm_format); +	if (format == 0) { +		wlr_log(WLR_ERROR, "Unsupported pixman drm format 0x%"PRIX32, +				drm_format); +		free(texture); +		return NULL; +	} + +	// TODO: avoid this copy +	texture->data = malloc(stride * height); +	if (texture->data == NULL) { +		wlr_log_errno(WLR_ERROR, "Allocation failed"); +		free(texture); +		return NULL; +	} +	memcpy(texture->data, data, stride * height); + +	texture->image = pixman_image_create_bits_no_clear(format, width, height, +			texture->data, stride); +	if (!texture->image) { +		wlr_log(WLR_ERROR, "Failed to create pixman image"); +		free(texture->data); +		free(texture); +		return NULL; +	} + +	return &texture->wlr_texture; +} + +static bool pixman_bind_buffer(struct wlr_renderer *wlr_renderer, +		struct wlr_buffer *wlr_buffer) { +	struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); + +	if (renderer->current_buffer != NULL) { +		wlr_buffer_unlock(renderer->current_buffer->buffer); +		renderer->current_buffer = NULL; +	} + +	if (wlr_buffer == NULL) { +		return true; +	} + +	struct wlr_pixman_buffer *buffer = get_buffer(renderer, wlr_buffer); +	if (buffer == NULL) { +		buffer = create_buffer(renderer, wlr_buffer); +	} +	if (buffer == NULL) { +		return false; +	} + +	wlr_buffer_lock(wlr_buffer); +	renderer->current_buffer = buffer; + +	return true; +} + +static const struct wlr_renderer_impl renderer_impl = { +	.begin = pixman_begin, +	.clear = pixman_clear, +	.scissor = pixman_scissor, +	.render_subtexture_with_matrix = pixman_render_subtexture_with_matrix, +	.render_quad_with_matrix = pixman_render_quad_with_matrix, +	.get_shm_texture_formats = pixman_get_shm_texture_formats, +	.get_render_formats = pixman_get_render_formats, +	.texture_from_pixels = pixman_texture_from_pixels, +	.bind_buffer = pixman_bind_buffer, +}; + +struct wlr_renderer *wlr_pixman_renderer_create(void) { +	struct wlr_pixman_renderer *renderer = +		calloc(1, sizeof(struct wlr_pixman_renderer)); +	if (renderer == NULL) { +		return NULL; +	} + +	wlr_log(WLR_INFO, "Creating pixman renderer"); +	wlr_renderer_init(&renderer->wlr_renderer, &renderer_impl); +	wl_list_init(&renderer->buffers); + +	size_t len = 0; +	const uint32_t *formats = get_pixman_drm_formats(&len); + +	for (size_t i = 0; i < len; ++i) { +		wlr_drm_format_set_add(&renderer->drm_formats, formats[i], +				DRM_FORMAT_MOD_LINEAR); +	} + +	return &renderer->wlr_renderer; +} diff --git a/render/wlr_renderer.c b/render/wlr_renderer.c index ac7629e4..6aee5ac3 100644 --- a/render/wlr_renderer.c +++ b/render/wlr_renderer.c @@ -4,6 +4,7 @@  #include <gbm.h>  #include <wlr/render/egl.h>  #include <wlr/render/gles2.h> +#include <wlr/render/pixman.h>  #include <wlr/render/interface.h>  #include <wlr/render/wlr_renderer.h>  #include <wlr/types/wlr_matrix.h> | 
