sna/glyphs: Improve handling of low bitdepth mask format conversions

We shouldn't just discard the mask if the user requests that we render
the glyphs through a low bitdepth mask - and in doing so we should also
be careful not to improve the bitdepth of that mask (since we don't take
into account the extra quantisation desired).

Testcase: render-glyphs
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
This commit is contained in:
Chris Wilson 2015-05-17 20:45:25 +01:00
parent 6d64063750
commit b10ef9cf5c
7 changed files with 515 additions and 36 deletions

View File

@ -817,8 +817,8 @@ create_pixmap(struct sna *sna, ScreenPtr screen,
datasize += adjust;
}
DBG(("%s: allocating pixmap %dx%d, depth=%d, size=%ld\n",
__FUNCTION__, width, height, depth, (long)datasize));
DBG(("%s: allocating pixmap %dx%d, depth=%d/%d, size=%ld\n",
__FUNCTION__, width, height, depth, bpp, (long)datasize));
pixmap = AllocatePixmap(screen, datasize);
if (!pixmap)
return NullPixmap;
@ -4091,7 +4091,7 @@ sna_pixmap_create_upload(ScreenPtr screen,
assert(width);
assert(height);
if (depth == 1)
if (depth < 8)
return create_pixmap(sna, screen, width, height, depth,
CREATE_PIXMAP_USAGE_SCRATCH);

View File

@ -74,7 +74,7 @@
#define NO_GLYPHS_VIA_MASK 0
#define FORCE_SMALL_MASK 0 /* -1 = never, 1 = always */
#define NO_GLYPHS_SLOW 0
#define NO_DISCARD_MASK 0
#define DISCARD_MASK 0 /* -1 = never, 1 = always */
#define CACHE_PICTURE_SIZE 1024
#define GLYPH_MIN_SIZE 8
@ -1094,6 +1094,9 @@ sna_glyph_get_image(GlyphPtr g, ScreenPtr s)
static inline bool use_small_mask(struct sna *sna, int16_t width, int16_t height, int depth)
{
if (depth < 8)
return true;
if (FORCE_SMALL_MASK)
return FORCE_SMALL_MASK > 0;
@ -1156,12 +1159,6 @@ glyphs_via_mask(struct sna *sna,
src_x += box.x1 - list->xOff;
src_y += box.y1 - list->yOff;
if (format->depth < 8) {
format = PictureMatchFormat(screen, 8, PICT_a8);
if (!format)
return false;
}
component_alpha = NeedsComponent(format->format);
if (use_small_mask(sna, width, height, format->depth)) {
pixman_image_t *mask_image;
@ -1179,7 +1176,7 @@ use_small_mask:
return false;
mask_image =
pixman_image_create_bits(format->depth << 24 | format->format,
pixman_image_create_bits(pixmap->drawable.bitsPerPixel << 24 | format->format,
width, height,
pixmap->devPrivate.ptr,
pixmap->devKind);
@ -1386,10 +1383,11 @@ next_image:
DBG(("%s: atlas format=%08x, mask format=%08x\n",
__FUNCTION__,
(int)p->atlas->format,
(int)(format->depth << 24 | format->format)));
(int)mask->format));
memset(&tmp, 0, sizeof(tmp));
if (p->atlas->format == (format->depth << 24 | format->format)) {
if (p->atlas->format == mask->format ||
alphaless(p->atlas->format) == mask->format) {
ok = sna->render.composite(sna, PictOpAdd,
p->atlas, NULL, mask,
0, 0, 0, 0, 0, 0,
@ -1564,6 +1562,8 @@ skip_glyph:
out:
if (list_extents != stack_extents)
free(list_extents);
DBG(("%s: format=%08d, depth=%d\n",
__FUNCTION__, format->format, format->depth));
return format;
}
@ -1573,24 +1573,34 @@ static bool can_discard_mask(uint8_t op, PicturePtr src, PictFormatPtr mask,
PictFormatPtr g;
uint32_t color;
if (NO_DISCARD_MASK)
return false;
if (DISCARD_MASK)
return DISCARD_MASK > 0;
DBG(("%s: nlist=%d, mask=%08x, depth %d, op=%d (bounded? %d)\n",
__FUNCTION__, nlist,
mask ? (unsigned)mask->format : 0, mask ? mask->depth : 0,
op, op_is_bounded(op)));
if (nlist == 1 && list->len == 1)
return true;
if (nlist == 1 && list->len == 1) {
if (mask == list->format)
return true;
if (!op_is_bounded(op))
g = list->format;
goto skip;
}
if (!op_is_bounded(op)) {
DBG(("%s: unbounded op, not discarding\n", __FUNCTION__));
return false;
}
/* No glyphs overlap and we are not performing a mask conversion. */
g = glyphs_format(nlist, list, glyphs);
if (mask == g)
if (mask == g) {
DBG(("%s: mask matches glyphs format, no conversion, so discard mask\n",
__FUNCTION__));
return true;
}
DBG(("%s: preferred mask format %08x, depth %d\n",
__FUNCTION__, g ? (unsigned)g->format : 0, g ? g->depth : 0));
@ -1605,18 +1615,41 @@ static bool can_discard_mask(uint8_t op, PicturePtr src, PictFormatPtr mask,
list++;
}
if (!sna_picture_is_solid(src, &color))
return false;
return color >> 24 == 0xff;
} else {
if (PICT_FORMAT_A(mask->format) >= PICT_FORMAT_A(g->format))
skip:
if (mask->format == g->format)
return true;
if (g->depth != 1)
return false;
}
if (mask->format == alphaless(g->format))
return true;
if (PICT_FORMAT_TYPE(g->format) == PICT_TYPE_A &&
PICT_FORMAT_TYPE(mask->format) != PICT_TYPE_A)
return true;
if (!sna_picture_is_solid(src, &color))
return false;
}
}
return color >> 24 == 0xff;
static uint32_t pixman_format(PictFormatPtr short_format)
{
uint32_t bpp;
bpp = short_format->depth;
if (bpp <= 1)
bpp = 1;
else if (bpp <= 8)
bpp = 8;
else if (bpp <= 16)
bpp = 16;
else
bpp = 32;
return bpp << 24 | short_format->format;
}
static void
@ -1756,7 +1789,7 @@ next:
if (sigtrap_get() == 0) {
if (mask_format) {
pixman_composite_glyphs(op, src_image, dst_image,
mask_format->format | (mask_format->depth << 24),
pixman_format(mask_format),
src_x + src_dx + region.extents.x1 - dst_x,
src_y + src_dy + region.extents.y1 - dst_y,
region.extents.x1, region.extents.y1,
@ -1815,10 +1848,10 @@ out:
x, y,
mask_format->depth,
(long)mask_format->format,
(long)(mask_format->depth << 24 | mask_format->format),
(long)pixman_format(mask_format),
NeedsComponent(mask_format->format)));
mask_image =
pixman_image_create_bits(mask_format->depth << 24 | mask_format->format,
pixman_image_create_bits(pixman_format(mask_format),
region.extents.x2 - region.extents.x1,
region.extents.y2 - region.extents.y1,
NULL, 0);
@ -2086,12 +2119,6 @@ glyphs_via_image(struct sna *sna,
src_x += box.x1 - list->xOff;
src_y += box.y1 - list->yOff;
if (format->depth < 8) {
format = PictureMatchFormat(screen, 8, PICT_a8);
if (!format)
return false;
}
DBG(("%s: small mask [format=%lx, depth=%d, size=%d], rendering glyphs to upload buffer\n",
__FUNCTION__, (unsigned long)format->format,
format->depth, (uint32_t)width*height*format->depth));
@ -2104,7 +2131,7 @@ glyphs_via_image(struct sna *sna,
return false;
mask_image =
pixman_image_create_bits(format->depth << 24 | format->format,
pixman_image_create_bits(pixmap->drawable.bitsPerPixel << 24 | format->format,
width, height,
pixmap->devPrivate.ptr,
pixmap->devKind);

View File

@ -54,7 +54,7 @@ sna_format_for_depth(int depth)
{
switch (depth) {
case 1: return PICT_a1;
case 4: return PICT_a4;
case 4: return PICT_x4a4;
case 8: return PICT_a8;
case 15: return PICT_x1r5g5b5;
case 16: return PICT_r5g6b5;

1
test/.gitignore vendored
View File

@ -18,6 +18,7 @@ render-copyarea
render-copyarea-mask
render-copyarea-size
render-copy-alphaless
render-glyphs
mixed-stress
lowlevel-blt-bench
vsync.avi

View File

@ -12,6 +12,7 @@ stress_TESTS = \
DrawSegments \
cursor-test \
render-fill \
render-glyphs \
render-trapezoid \
render-trapezoid-image \
render-fill-copy \

441
test/render-glyphs.c Normal file
View File

@ -0,0 +1,441 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdarg.h>
#include <string.h>
#include <X11/Xutil.h> /* for XDestroyImage */
#include <pixman.h> /* for pixman blt functions */
#include "test.h"
static const XRenderColor colors[] = {
/* red, green, blue, alpha */
{ 0 },
{ 0, 0, 0, 0xffff },
{ 0xffff, 0, 0, 0xffff },
{ 0, 0xffff, 0, 0xffff },
{ 0, 0, 0xffff, 0xffff },
{ 0xffff, 0xffff, 0xffff, 0xffff },
};
static struct clip {
void *func;
} clips[] = {
{ NULL },
};
static int _x_error_occurred;
static int
_check_error_handler(Display *display,
XErrorEvent *event)
{
_x_error_occurred = 1;
return False; /* ignored */
}
static void clear(struct test_display *dpy,
struct test_target *tt,
const XRenderColor *c)
{
XRenderFillRectangle(dpy->dpy, PictOpClear, tt->picture, c,
0, 0, tt->width, tt->height);
}
static bool check_op(struct test_display *dpy, int op, struct test_target *tt)
{
XRenderColor render_color = {0};
XSync(dpy->dpy, True);
_x_error_occurred = 0;
XRenderFillRectangle(dpy->dpy, op,
tt->picture, &render_color,
0, 0, 0, 0);
XSync(dpy->dpy, True);
return _x_error_occurred == 0;
}
struct glyph_iter {
enum {
GLYPHS, OP, DST, SRC, MASK, CLIP,
} stage;
int glyph_format;
int op;
int dst_color;
int src_color;
int mask_format;
int clip;
struct {
struct test_display *dpy;
struct test_target tt;
GlyphSet glyphset;
Picture src;
XRenderPictFormat *mask_format;
} ref, out;
};
static void glyph_iter_init(struct glyph_iter *gi,
struct test *t, enum target target)
{
memset(gi, 0, sizeof(*gi));
gi->out.dpy = &t->out;
test_target_create_render(&t->out, target, &gi->out.tt);
gi->ref.dpy = &t->ref;
test_target_create_render(&t->ref, target, &gi->ref.tt);
gi->stage = GLYPHS;
gi->glyph_format = -1;
gi->op = -1;
gi->dst_color = -1;
gi->src_color = -1;
gi->mask_format = -1;
gi->clip = -1;
}
static void render_clear(char *image, int image_size, int bpp)
{
memset(image, 0, image_size);
}
static void render_black(char *image, int image_size, int bpp)
{
if (bpp == 4) {
uint32_t *p = (uint32_t *)image;
image_size /= 4;
while (image_size--)
*p++ = 0x000000ff;
} else
memset(image, 0x55, image_size);
}
static void render_green(char *image, int image_size, int bpp)
{
if (bpp == 4) {
uint32_t *p = (uint32_t *)image;
image_size /= 4;
while (image_size--)
*p++ = 0xffff0000;
} else
memset(image, 0xaa, image_size);
}
static void render_white(char *image, int image_size, int bpp)
{
memset(image, 0xff, image_size);
}
static GlyphSet create_glyphs(Display *dpy, int format_id)
{
#define N_GLYPHS 4
XRenderPictFormat *format;
XGlyphInfo glyph = { 8, 8, 0, 0, 8, 0 };
char image[4*8*8];
GlyphSet glyphset;
Glyph gid;
int image_size;
int bpp;
int n;
format = XRenderFindStandardFormat(dpy, format_id);
if (format == NULL)
return 0;
switch (format_id) {
case PictStandardARGB32:
case PictStandardRGB24:
image_size = 4 * 8 * 8;
bpp = 4;
break;
case PictStandardA8:
case PictStandardA4:
image_size = 8 * 8;
bpp = 1;
break;
case PictStandardA1:
image_size = 8;
bpp = 0;
break;
default:
return 0;
}
glyphset = XRenderCreateGlyphSet(dpy, format);
for (n = 0; n < N_GLYPHS; n++) {
gid = n;
switch (n) {
case 0: render_clear(image, image_size, bpp); break;
case 1: render_black(image, image_size, bpp); break;
case 2: render_green(image, image_size, bpp); break;
case 3: render_white(image, image_size, bpp); break;
}
XRenderAddGlyphs(dpy, glyphset,
&gid, &glyph, 1, image, image_size);
}
return glyphset;
}
static const char *glyph_name(int n)
{
switch (n) {
case 0: return "clear";
case 1: return "black";
case 2: return "green";
case 3: return "white";
default: return "unknown";
}
}
static bool glyph_iter_next(struct glyph_iter *gi)
{
restart:
if (gi->stage == GLYPHS) {
if (++gi->glyph_format == PictStandardNUM)
return false;
if (gi->out.glyphset)
XRenderFreeGlyphSet(gi->out.dpy->dpy,
gi->out.glyphset);
gi->out.glyphset = create_glyphs(gi->out.dpy->dpy,
gi->glyph_format);
if (gi->ref.glyphset)
XRenderFreeGlyphSet(gi->ref.dpy->dpy,
gi->ref.glyphset);
gi->ref.glyphset = create_glyphs(gi->ref.dpy->dpy,
gi->glyph_format);
gi->stage++;
}
if (gi->stage == OP) {
do {
if (++gi->op == 255)
goto reset_op;
} while (!check_op(gi->out.dpy, gi->op, &gi->out.tt) ||
!check_op(gi->ref.dpy, gi->op, &gi->ref.tt));
gi->stage++;
}
if (gi->stage == DST) {
if (++gi->dst_color == ARRAY_SIZE(colors))
goto reset_dst;
gi->stage++;
}
if (gi->stage == SRC) {
if (++gi->src_color == ARRAY_SIZE(colors))
goto reset_src;
if (gi->ref.src)
XRenderFreePicture(gi->ref.dpy->dpy, gi->ref.src);
gi->ref.src = XRenderCreateSolidFill(gi->ref.dpy->dpy,
&colors[gi->src_color]);
if (gi->out.src)
XRenderFreePicture(gi->out.dpy->dpy, gi->out.src);
gi->out.src = XRenderCreateSolidFill(gi->out.dpy->dpy,
&colors[gi->src_color]);
gi->stage++;
}
if (gi->stage == MASK) {
if (++gi->mask_format > PictStandardNUM)
goto reset_mask;
if (gi->mask_format == PictStandardRGB24)
gi->mask_format++;
if (gi->mask_format < PictStandardNUM) {
gi->out.mask_format = XRenderFindStandardFormat(gi->out.dpy->dpy,
gi->mask_format);
gi->ref.mask_format = XRenderFindStandardFormat(gi->ref.dpy->dpy,
gi->mask_format);
} else {
gi->out.mask_format = NULL;
gi->ref.mask_format = NULL;
}
gi->stage++;
}
if (gi->stage == CLIP) {
if (++gi->clip == ARRAY_SIZE(clips))
goto reset_clip;
gi->stage++;
}
gi->stage--;
return true;
reset_op:
gi->op = -1;
reset_dst:
gi->dst_color = -1;
reset_src:
gi->src_color = -1;
reset_mask:
gi->mask_format = -1;
reset_clip:
gi->clip = -1;
gi->stage--;
goto restart;
}
static void glyph_iter_fini(struct glyph_iter *gi)
{
if (gi->out.glyphset)
XRenderFreeGlyphSet (gi->out.dpy->dpy, gi->out.glyphset);
if (gi->ref.glyphset)
XRenderFreeGlyphSet (gi->ref.dpy->dpy, gi->ref.glyphset);
test_target_destroy_render(gi->out.dpy, &gi->out.tt);
test_target_destroy_render(gi->ref.dpy, &gi->ref.tt);
}
static const char *stdformat_to_str(int id)
{
switch (id) {
case PictStandardARGB32: return "ARGB32";
case PictStandardRGB24: return "RGB24";
case PictStandardA8: return "A8";
case PictStandardA4: return "A4";
case PictStandardA1: return "A1";
default: return "none";
}
}
static char *glyph_iter_to_string(struct glyph_iter *gi,
const char *format,
...)
{
static char buf[100];
va_list ap;
int len;
len = sprintf(buf, "glyphs=%s, op=%d, dst=%08x, src=%08x, mask=%s",
stdformat_to_str(gi->glyph_format), gi->op,
xrender_color(&colors[gi->dst_color]),
xrender_color(&colors[gi->src_color]),
stdformat_to_str(gi->mask_format));
if (format) {
buf[len++] = ' ';
va_start(ap, format);
vsprintf(buf+len, format, ap);
va_end(ap);
}
return buf;
}
static void single(struct test *t, enum target target)
{
struct glyph_iter gi;
int n;
printf("Testing single glyph (%s): ", test_target_name(target));
fflush(stdout);
glyph_iter_init(&gi, t, target);
while (glyph_iter_next(&gi)) {
XGlyphElt8 elt;
char id[N_GLYPHS];
for (n = 0; n < N_GLYPHS; n++) {
id[n] = n;
elt.chars = &id[n];
elt.nchars = 1;
elt.xOff = 0;
elt.yOff = 0;
clear(gi.out.dpy, &gi.out.tt, &colors[gi.dst_color]);
elt.glyphset = gi.out.glyphset;
XRenderCompositeText8 (gi.out.dpy->dpy, gi.op,
gi.out.src,
gi.out.tt.picture,
gi.out.mask_format,
0, 0,
0, 8,
&elt, 1);
clear(gi.ref.dpy, &gi.ref.tt, &colors[gi.dst_color]);
elt.glyphset = gi.ref.glyphset;
XRenderCompositeText8 (gi.ref.dpy->dpy, gi.op,
gi.ref.src,
gi.ref.tt.picture,
gi.ref.mask_format,
0, 0,
0, 8,
&elt, 1);
test_compare(t,
gi.out.tt.draw, gi.out.tt.format,
gi.ref.tt.draw, gi.ref.tt.format,
0, 0, gi.out.tt.width, gi.out.tt.height,
glyph_iter_to_string(&gi,
"glyph=%s",
glyph_name(n)));
}
elt.chars = &id[0];
elt.nchars = n;
clear(gi.out.dpy, &gi.out.tt, &colors[gi.dst_color]);
elt.glyphset = gi.out.glyphset;
XRenderCompositeText8 (gi.out.dpy->dpy, gi.op,
gi.out.src,
gi.out.tt.picture,
gi.out.mask_format,
0, 0,
0, 8,
&elt, 1);
clear(gi.ref.dpy, &gi.ref.tt, &colors[gi.dst_color]);
elt.glyphset = gi.ref.glyphset;
XRenderCompositeText8 (gi.ref.dpy->dpy, gi.op,
gi.ref.src,
gi.ref.tt.picture,
gi.ref.mask_format,
0, 0,
0, 8,
&elt, 1);
test_compare(t,
gi.out.tt.draw, gi.out.tt.format,
gi.ref.tt.draw, gi.ref.tt.format,
0, 0, gi.out.tt.width, gi.out.tt.height,
glyph_iter_to_string(&gi, "all"));
}
glyph_iter_fini(&gi);
}
int main(int argc, char **argv)
{
struct test test;
int t;
test_init(&test, argc, argv);
XSetErrorHandler(_check_error_handler);
for (t = TARGET_FIRST; t <= TARGET_LAST; t++) {
single(&test, t);
//overlapping(&test, t);
//gap(&test, t);
//mixed(&test, t);
}
return 0;
}

View File

@ -107,6 +107,15 @@ static inline uint32_t color(uint8_t red, uint8_t green, uint8_t blue, uint8_t a
return alpha << 24 | ra >> 8 << 16 | ga >> 8 << 8 | ba >> 8;
}
static inline uint32_t xrender_color(const XRenderColor *c)
{
uint32_t ra = c->red * c->alpha;
uint32_t ga = c->green * c->alpha;
uint32_t ba = c->blue * c->alpha;
return c->alpha >> 8 << 24 | ra >> 24 << 16 | ga >> 24 << 8 | ba >> 24;
}
void test_timer_start(struct test_display *t, struct timespec *tv);
double test_timer_stop(struct test_display *t, struct timespec *tv);