diff --git a/include/render/pixman.h b/include/render/pixman.h new file mode 100644 index 000000000..f4cab45e7 --- /dev/null +++ b/include/render/pixman.h @@ -0,0 +1,49 @@ +#ifndef RENDER_PIXMAN_H +#define RENDER_PIXMAN_H + +#include +#include +#include +#include "render/pixel_format.h" + +struct wlr_pixman_pixel_format { + uint32_t drm_format; + pixman_format_code_t pixman_format; +}; + +struct wlr_pixman_buffer; + +struct wlr_pixman_renderer { + struct wlr_renderer wlr_renderer; + + struct wl_list buffers; // wlr_pixman_buffer.link + + struct wlr_pixman_buffer *current_buffer; + int32_t width, height; + + struct wlr_drm_format_set drm_formats; +}; + +struct wlr_pixman_buffer { + struct wlr_buffer *buffer; + struct wlr_pixman_renderer *renderer; + + pixman_image_t *image; + + struct wl_listener buffer_destroy; + struct wl_list link; // wlr_pixman_renderer.buffers +}; + +struct wlr_pixman_texture { + struct wlr_texture wlr_texture; + struct wlr_pixman_renderer *renderer; + + void *data; + pixman_image_t *image; + const struct wlr_pixel_format_info *format; +}; + +pixman_format_code_t get_pixman_format_from_drm(uint32_t fmt); +const uint32_t *get_pixman_drm_formats(size_t *len); + +#endif diff --git a/include/wlr/render/pixman.h b/include/wlr/render/pixman.h new file mode 100644 index 000000000..713e431d6 --- /dev/null +++ b/include/wlr/render/pixman.h @@ -0,0 +1,17 @@ +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * future consistency of this API. + */ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + +#ifndef WLR_RENDER_PIXMAN_H +#define WLR_RENDER_PIXMAN_H + +#include +#include + +struct wlr_renderer *wlr_pixman_renderer_create(void); + +#endif diff --git a/meson.build b/meson.build index 5095e877a..560a14ad6 100644 --- a/meson.build +++ b/meson.build @@ -17,8 +17,13 @@ project( # between minor releases. soversion = 9 +little_endian = host_machine.endian() == 'little' +big_endian = host_machine.endian() == 'big' + add_project_arguments([ '-DWLR_USE_UNSTABLE', + '-DWLR_LITTLE_ENDIAN=@0@'.format(little_endian.to_int()), + '-DWLR_BIG_ENDIAN=@0@'.format(big_endian.to_int()), ], language: 'c') cc = meson.get_compiler('c') diff --git a/render/meson.build b/render/meson.build index ee9453774..ab0c44244 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 000000000..eb7a0d3ad --- /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 000000000..a57c6c34b --- /dev/null +++ b/render/pixman/pixel_format.c @@ -0,0 +1,59 @@ +#include +#include + +#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 000000000..466b71d01 --- /dev/null +++ b/render/pixman/renderer.c @@ -0,0 +1,383 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#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 ac7629e42..6aee5ac32 100644 --- a/render/wlr_renderer.c +++ b/render/wlr_renderer.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include