From 79c5ba7185df24ee27c4042f2b903536ff2196ea Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Thu, 24 Jan 2013 12:13:29 +0100 Subject: [PATCH] kmscon: add pixman renderer This is an experimental renderer based on pixman. It is only available for testing-purposes and should not be used in production, yet. The bbulk renderer is still the faster alternative and should be used instead. Signed-off-by: David Herrmann --- Makefile.am | 18 ++ configure.ac | 46 +++++ src/kmscon_mod_pixman.c | 57 ++++++ src/text.h | 1 + src/text_pixman.c | 429 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 551 insertions(+) create mode 100644 src/kmscon_mod_pixman.c create mode 100644 src/text_pixman.c diff --git a/Makefile.am b/Makefile.am index 8502e8c..aeb1e0b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -468,6 +468,24 @@ mod_cairo_la_LDFLAGS = \ -module \ -avoid-version +if BUILD_ENABLE_RENDERER_PIXMAN +module_LTLIBRARIES += mod-pixman.la +endif + +mod_pixman_la_SOURCES = \ + src/kmscon_module_interface.h \ + src/githead.h \ + src/text_pixman.c \ + src/kmscon_mod_pixman.c +mod_pixman_la_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + $(PIXMAN_CFLAGS) +mod_pixman_la_LIBADD = \ + $(PIXMAN_LIBS) +mod_pixman_la_LDFLAGS = \ + -module \ + -avoid-version + # # Binaries # These are the sources for the main binaries and test programs. They mostly diff --git a/configure.ac b/configure.ac index ea7db2d..6075ece 100644 --- a/configure.ac +++ b/configure.ac @@ -109,6 +109,11 @@ PKG_CHECK_MODULES([CAIRO], [cairo], AC_SUBST(CAIRO_CFLAGS) AC_SUBST(CAIRO_LIBS) +PKG_CHECK_MODULES([PIXMAN], [pixman-1], + [have_pixman=yes], [have_pixman=no]) +AC_SUBST(PIXMAN_CFLAGS) +AC_SUBST(PIXMAN_LIBS) + # # Parse arguments # This parses all arguments that are given via "--enable-XY" or "--with-XY" and @@ -256,10 +261,12 @@ AC_ARG_WITH([renderers], enable_renderer_bbulk="no" enable_renderer_gltex="no" enable_renderer_cairo="no" +enable_renderer_pixman="no" if test "x$with_renderers" = "x" ; then enable_renderer_bbulk="yes (default)" enable_renderer_gltex="yes (default)" enable_renderer_cairo="no (default)" + enable_renderer_pixman="no (default)" with_renderers="bbulk,gltex (default)" else SAVEIFS="$IFS" @@ -271,6 +278,8 @@ else enable_renderer_gltex="yes" elif test "x$i" = "xcairo" ; then enable_renderer_cairo="yes" + elif test "x$i" = "xpixman" ; then + enable_renderer_pixman="yes" else IFS="$SAVEIFS" AC_ERROR([Unknown renderer $i]) @@ -569,6 +578,25 @@ else renderer_cairo_missing="enable-renderer-cairo" fi +# renderer pixman +renderer_pixman_avail=no +renderer_pixman_missing="" +if test ! "x$enable_renderer_pixman" = "xno" ; then + renderer_pixman_avail=yes + if test "x$have_pixman" = "xno" ; then + renderer_pixman_avail=no + renderer_pixman_missing="pixman" + fi + + if test "x$renderer_pixman_avail" = "xno" ; then + if test "x$enable_renderer_pixman" = "xyes" ; then + AC_ERROR([missing for renderer-pixman: $renderer_pixman_missing]) + fi + fi +else + renderer_pixman_missing="enable-renderer-pixman" +fi + # font unifont font_unifont_avail=no font_unifont_missing="" @@ -819,6 +847,14 @@ if test "x$renderer_cairo_avail" = "xyes" ; then fi fi +# renderer pixman +renderer_pixman_enabled=no +if test "x$renderer_pixman_avail" = "xyes" ; then + if test "x${enable_renderer_pixman% *}" = "xyes" ; then + renderer_pixman_enabled=yes + fi +fi + # renderer bbulk renderer_bbulk_enabled=no if test "x$renderer_bbulk_avail" = "xyes" ; then @@ -1025,6 +1061,15 @@ fi AM_CONDITIONAL([BUILD_ENABLE_RENDERER_CAIRO], [test "x$renderer_cairo_enabled" = "xyes"]) +# renderer pixman +if test "x$renderer_pixman_enabled" = "xyes" ; then + AC_DEFINE([BUILD_ENABLE_RENDERER_PIXMAN], [1], + [Build pixman rendering backend]) +fi + +AM_CONDITIONAL([BUILD_ENABLE_RENDERER_PIXMAN], + [test "x$renderer_pixman_enabled" = "xyes"]) + # font unifont if test "x$font_unifont_enabled" = "xyes" ; then AC_DEFINE([BUILD_ENABLE_FONT_UNIFONT], [1], @@ -1195,6 +1240,7 @@ AC_MSG_NOTICE([Build configuration: bbulk: $renderer_bbulk_enabled ($renderer_bbulk_avail: $renderer_bbulk_missing) gltex: $renderer_gltex_enabled ($renderer_gltex_avail: $renderer_gltex_missing) cairo: $renderer_cairo_enabled ($renderer_cairo_avail: $renderer_cairo_missing) + pixman: $renderer_pixman_enabled ($renderer_pixman_avail: $renderer_pixman_missing) Session Types: dummy: $session_dummy_enabled ($session_dummy_avail: $session_dummy_missing) diff --git a/src/kmscon_mod_pixman.c b/src/kmscon_mod_pixman.c new file mode 100644 index 0000000..a5adfe1 --- /dev/null +++ b/src/kmscon_mod_pixman.c @@ -0,0 +1,57 @@ +/* + * kmscon - Pixman based rendering backend module + * + * Copyright (c) 2012-2013 David Herrmann + * + * 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 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. + */ + +/* + * Pixman based rendering backend + */ + +#include +#include +#include "text.h" +#include "kmscon_module_interface.h" +#include "log.h" + +#define LOG_SUBSYSTEM "mod_pixman" + +static int kmscon_pixman_load(void) +{ + int ret; + + kmscon_text_pixman_ops.owner = KMSCON_THIS_MODULE; + ret = kmscon_text_register(&kmscon_text_pixman_ops); + if (ret) { + log_error("cannot register pixman renderer"); + return ret; + } + + return 0; +} + +static void kmscon_pixman_unload(void) +{ + kmscon_text_unregister(kmscon_text_pixman_ops.name); +} + +KMSCON_MODULE(NULL, kmscon_pixman_load, kmscon_pixman_unload, NULL); diff --git a/src/text.h b/src/text.h index c61eb94..71f830e 100644 --- a/src/text.h +++ b/src/text.h @@ -114,5 +114,6 @@ extern struct kmscon_text_ops kmscon_text_bblit_ops; extern struct kmscon_text_ops kmscon_text_bbulk_ops; extern struct kmscon_text_ops kmscon_text_gltex_ops; extern struct kmscon_text_ops kmscon_text_cairo_ops; +extern struct kmscon_text_ops kmscon_text_pixman_ops; #endif /* KMSCON_TEXT_H */ diff --git a/src/text_pixman.c b/src/text_pixman.c new file mode 100644 index 0000000..e9ac812 --- /dev/null +++ b/src/text_pixman.c @@ -0,0 +1,429 @@ +/* + * kmscon - Pixman Text Renderer Backend + * + * Copyright (c) 2012-2013 David Herrmann + * + * 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 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. + */ + +/* + * Pixman based text renderer + */ + +#include +#include +#include +#include +#include +#include "log.h" +#include "shl_hashtable.h" +#include "text.h" +#include "uterm_video.h" + +#define LOG_SUBSYSTEM "text_pixman" + +struct tp_glyph { + const struct kmscon_glyph *glyph; + pixman_image_t *surf; +}; + +struct tp_pixman { + struct shl_hashtable *glyphs; + struct shl_hashtable *bold_glyphs; + + struct uterm_video_buffer buf[2]; + pixman_image_t *surf[2]; + unsigned int format[2]; + + bool use_indirect; + uint8_t *data[2]; + struct uterm_video_buffer vbuf; + + /* cache */ + unsigned int cur; + unsigned int c_bpp; + uint32_t *c_data; + unsigned int c_stride; +}; + +static int tp_init(struct kmscon_text *txt) +{ + struct tp_pixman *tp; + + tp = malloc(sizeof(*tp)); + if (!tp) + return -ENOMEM; + + txt->data = tp; + return 0; +} + +static void tp_destroy(struct kmscon_text *txt) +{ + struct tp_pixman *tp = txt->data; + + free(tp); +} + +static void free_glyph(void *data) +{ + struct tp_glyph *glyph = data; + + pixman_image_unref(glyph->surf); + free(glyph); +} + +static unsigned int format_u2p(unsigned int f) +{ + switch (f) { + case UTERM_FORMAT_XRGB32: + return PIXMAN_x8r8g8b8; + case UTERM_FORMAT_RGB16: + return PIXMAN_r5g6b5; + case UTERM_FORMAT_GREY: + return PIXMAN_a8; + default: + return 0; + } +} + +static int alloc_indirect(struct kmscon_text *txt, + unsigned int w, unsigned int h) +{ + struct tp_pixman *tp = txt->data; + unsigned int s, i, format; + int ret; + + log_info("using blitting engine"); + + format = format_u2p(UTERM_FORMAT_XRGB32); + s = w * 4; + + tp->data[0] = malloc(s * h); + tp->data[1] = malloc(s * h); + if (!tp->data[0] || !tp->data[1]) { + log_error("cannot allocate memory for render-buffer"); + ret = -ENOMEM; + goto err_free; + } + + for (i = 0; i < 2; ++i) { + tp->format[i] = format; + tp->surf[i] = pixman_image_create_bits(format, w, h, + (void*)tp->data[i], s); + if (!tp->surf[i]) { + log_error("cannot create pixman surfaces"); + goto err_pixman; + } + } + + tp->vbuf.width = w; + tp->vbuf.height = h; + tp->vbuf.stride = s; + tp->vbuf.format = UTERM_FORMAT_XRGB32; + tp->use_indirect = true; + return 0; + +err_pixman: + if (tp->surf[1]) + pixman_image_unref(tp->surf[1]); + tp->surf[1] = NULL; + if (tp->surf[0]) + pixman_image_unref(tp->surf[0]); + tp->surf[0] = NULL; +err_free: + free(tp->data[1]); + free(tp->data[0]); + tp->data[1] = NULL; + tp->data[0] = NULL; + return ret; +} + +static int tp_set(struct kmscon_text *txt) +{ + struct tp_pixman *tp = txt->data; + int ret; + unsigned int w, h; + struct uterm_mode *m; + + memset(tp, 0, sizeof(*tp)); + m = uterm_display_get_current(txt->disp); + w = uterm_mode_get_width(m); + h = uterm_mode_get_height(m); + + ret = shl_hashtable_new(&tp->glyphs, shl_direct_hash, + shl_direct_equal, NULL, + free_glyph); + if (ret) + return ret; + + ret = shl_hashtable_new(&tp->bold_glyphs, shl_direct_hash, + shl_direct_equal, NULL, + free_glyph); + if (ret) + goto err_htable; + + /* + * TODO: It is actually faster to use a local shadow buffer and then + * blit all data to the framebuffer afterwards. Reads seem to be + * horribly slow on some mmap'ed framebuffers. However, that's not true + * for all so we actually don't know which to use here. + */ + ret = uterm_display_get_buffers(txt->disp, tp->buf, + UTERM_FORMAT_XRGB32); + if (true || ret) { + log_warning("cannot get buffers for display %p", + txt->disp); + ret = alloc_indirect(txt, w, h); + if (ret) + goto err_htable_bold; + } else { + tp->format[0] = format_u2p(tp->buf[0].format); + tp->surf[0] = pixman_image_create_bits_no_clear(tp->format[0], + tp->buf[0].width, tp->buf[0].height, + (void*)tp->buf[0].data, + tp->buf[0].stride); + tp->format[1] = format_u2p(tp->buf[1].format); + tp->surf[1] = pixman_image_create_bits_no_clear(tp->format[1], + tp->buf[1].width, tp->buf[1].height, + (void*)tp->buf[1].data, + tp->buf[1].stride); + if (!tp->surf[0] || !tp->surf[1]) { + log_error("cannot create pixman surfaces"); + goto err_ctx; + } + } + + txt->cols = w / txt->font->attr.width; + txt->rows = h / txt->font->attr.height; + + return 0; + +err_ctx: + if (tp->surf[1]) + pixman_image_unref(tp->surf[1]); + if (tp->surf[0]) + pixman_image_unref(tp->surf[0]); + free(tp->data[1]); + free(tp->data[0]); +err_htable_bold: + shl_hashtable_free(tp->bold_glyphs); +err_htable: + shl_hashtable_free(tp->glyphs); + return ret; +} + +static void tp_unset(struct kmscon_text *txt) +{ + struct tp_pixman *tp = txt->data; + + pixman_image_unref(tp->surf[1]); + pixman_image_unref(tp->surf[0]); + free(tp->data[1]); + free(tp->data[0]); + shl_hashtable_free(tp->bold_glyphs); + shl_hashtable_free(tp->glyphs); +} + +static int find_glyph(struct kmscon_text *txt, struct tp_glyph **out, + uint32_t id, const uint32_t *ch, size_t len, bool bold) +{ + struct tp_pixman *tp = txt->data; + struct tp_glyph *glyph; + struct shl_hashtable *gtable; + struct kmscon_font *font; + const struct uterm_video_buffer *buf; + unsigned int format; + int ret; + bool res; + + if (bold) { + gtable = tp->bold_glyphs; + font = txt->bold_font; + } else { + gtable = tp->glyphs; + font = txt->font; + } + + res = shl_hashtable_find(gtable, (void**)&glyph, + (void*)(unsigned long)id); + if (res) { + *out = glyph; + return 0; + } + + glyph = malloc(sizeof(*glyph)); + if (!glyph) + return -ENOMEM; + memset(glyph, 0, sizeof(*glyph)); + + if (!len) + ret = kmscon_font_render_empty(font, &glyph->glyph); + else + ret = kmscon_font_render(font, id, ch, len, &glyph->glyph); + + if (ret) { + ret = kmscon_font_render_inval(font, &glyph->glyph); + if (ret) + goto err_free; + } + + buf = &glyph->glyph->buf; + format = format_u2p(buf->format); + glyph->surf = pixman_image_create_bits_no_clear(format, + buf->width, + buf->height, + (void*)buf->data, + buf->stride); + if (!glyph->surf) { + log_error("cannot create pixman-glyph: %d %p %d %d %d %d", + ret, buf->data, format, buf->width, buf->height, + buf->stride); + ret = -EFAULT; + goto err_free; + } + + ret = shl_hashtable_insert(gtable, (void*)(long)id, glyph); + if (ret) + goto err_pixman; + + *out = glyph; + return 0; + +err_pixman: + pixman_image_unref(glyph->surf); +err_free: + free(glyph); + return ret; +} + +static int tp_prepare(struct kmscon_text *txt) +{ + struct tp_pixman *tp = txt->data; + int ret; + pixman_image_t *img; + + ret = uterm_display_use(txt->disp, NULL); + if (ret < 0) { + log_error("cannot use display %p", txt->disp); + return ret; + } + + tp->cur = ret; + img = tp->surf[tp->cur]; + tp->c_bpp = PIXMAN_FORMAT_BPP(tp->format[tp->cur]); + tp->c_data = pixman_image_get_data(img); + tp->c_stride = pixman_image_get_stride(img); + + return 0; +} + +static int tp_draw(struct kmscon_text *txt, + uint32_t id, const uint32_t *ch, size_t len, + unsigned int width, + unsigned int posx, unsigned int posy, + const struct tsm_screen_attr *attr) +{ + struct tp_pixman *tp = txt->data; + struct tp_glyph *glyph; + int ret; + uint32_t bc; + pixman_color_t fc; + pixman_image_t *col; + + if (!width) + return 0; + + ret = find_glyph(txt, &glyph, id, ch, len, attr->bold); + if (ret) + return ret; + + if (attr->inverse) { + bc = (attr->fr << 16) | (attr->fg << 8) | (attr->fb); + fc.red = attr->br << 8; + fc.green = attr->bg << 8; + fc.blue = attr->bb << 8; + fc.alpha = 0xffff; + } else { + bc = (attr->br << 16) | (attr->bg << 8) | (attr->bb); + fc.red = attr->fr << 8; + fc.green = attr->fg << 8; + fc.blue = attr->fb << 8; + fc.alpha = 0xffff; + } + + pixman_fill(tp->c_data, tp->c_stride / 4, tp->c_bpp, + posx * txt->font->attr.width, + posy * txt->font->attr.height, + txt->font->attr.width, + txt->font->attr.height, + bc); + + col = pixman_image_create_solid_fill(&fc); + if (!col) { + log_error("cannot create pixman color image"); + return -ENOMEM; + } + + pixman_image_composite(PIXMAN_OP_OVER, + col, /* src */ + glyph->surf, /* mask */ + tp->surf[tp->cur], /* dst */ + 0, 0, 0, 0, + posx * txt->font->attr.width, + posy * txt->font->attr.height, + txt->font->attr.width, + txt->font->attr.height); + + pixman_image_unref(col); + + return 0; +} + +static int tp_render(struct kmscon_text *txt) +{ + struct tp_pixman *tp = txt->data; + int ret; + + if (!tp->use_indirect) + return 0; + + tp->vbuf.data = tp->data[tp->cur]; + ret = uterm_display_blit(txt->disp, &tp->vbuf, 0, 0); + if (ret) { + log_error("cannot blit back-buffer to display: %d", ret); + return ret; + } + + return 0; +} + +struct kmscon_text_ops kmscon_text_pixman_ops = { + .name = "pixman", + .owner = NULL, + .init = tp_init, + .destroy = tp_destroy, + .set = tp_set, + .unset = tp_unset, + .prepare = tp_prepare, + .draw = tp_draw, + .render = tp_render, + .abort = NULL, +};