1/*
2 * Copyright © 2009 Nokia Corporation
3 * Copyright © 2010 Movial Creative Technologies Oy
4 * Copyright © 2013 Intel Corporation
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the next
14 * paragraph) shall be included in all copies or substantial portions of the
15 * Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 * DEALINGS IN THE SOFTWARE.
24 */
25
26#ifdef HAVE_CONFIG_H
27#include "config.h"
28#endif
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <stdint.h>
34#include <stdbool.h>
35
36#include <X11/X.h>
37#include <X11/Xutil.h> /* for XDestroyImage */
38#include <X11/Xlibint.h>
39#include <X11/extensions/Xrender.h>
40#if HAVE_MIT_SHM
41#include <X11/extensions/XShm.h>
42#if HAVE_X11_EXTENSIONS_SHMPROTO_H
43#include <X11/extensions/shmproto.h>
44#elif HAVE_X11_EXTENSIONS_SHMSTR_H
45#include <X11/extensions/shmstr.h>
46#else
47#error Failed to find the right header for X11 MIT-SHM protocol definitions
48#endif
49#include <sys/ipc.h>
50#include <sys/shm.h>
51#endif
52#include <pixman.h> /* for pixman blt functions */
53
54#include "test.h"
55
56static const struct format {
57	const char *name;
58	pixman_format_code_t pixman_format;
59} formats[] = {
60	{ "a8r8g8b8", PIXMAN_a8r8g8b8 },
61	{ "x8r8g8b8", PIXMAN_x8r8g8b8 },
62	{ "a8", PIXMAN_a8 },
63	{ "a4", PIXMAN_a4 },
64	{ "a1", PIXMAN_a1 },
65};
66
67static const struct op {
68	int value;
69	const char *name;
70} ops[] = {
71	{ PictOpClear, "Clear" },
72	{ PictOpSrc, "Src" },
73	{ PictOpDst, "Dst" },
74	{ PictOpOver, "Over" },
75	{ PictOpOverReverse, "OverReverse" },
76	{ PictOpIn, "In" },
77	{ PictOpInReverse, "InReverse" },
78	{ PictOpOut, "Out" },
79	{ PictOpOutReverse, "OutReverse" },
80	{ PictOpAtop, "Atop" },
81	{ PictOpAtopReverse, "AtopReverse" },
82	{ PictOpXor, "Xor" },
83	{ PictOpAdd, "Add" },
84	{ PictOpSaturate, "Saturate" },
85	{ PictOpMultiply, "Multiply" },
86	{ PictOpScreen, "Screen" },
87	{ PictOpOverlay, "Overlay" },
88	{ PictOpDarken, "Darken" },
89	{ PictOpLighten, "Lighten" },
90	{ PictOpColorDodge, "Dodge" },
91	{ PictOpColorBurn, "Burn" },
92	{ PictOpHardLight, "HardLight" },
93	{ PictOpSoftLight, "SoftLight" },
94};
95
96static Picture source_pixmap(struct test_display *t, struct test_target *target, int format)
97{
98	XRenderColor render_color[2] = {
99		{ 0x8000, 0x8000, 0x8000, 0x8000 },
100		{ 0xffff, 0xffff, 0xffff, 0xffff },
101	};
102	Pixmap pixmap;
103	Picture picture;
104
105	pixmap = XCreatePixmap(t->dpy, t->root,
106			       target->width, target->height,
107			       PIXMAN_FORMAT_DEPTH(formats[format].pixman_format));
108
109	picture = XRenderCreatePicture(t->dpy, pixmap,
110				       XRenderFindStandardFormat(t->dpy, format),
111				       0, NULL);
112	XFreePixmap(t->dpy, pixmap);
113
114	XRenderFillRectangle(t->dpy, PictOpSrc, picture, &render_color[0],
115			     0, 0, target->width, target->height/2);
116	XRenderFillRectangle(t->dpy, PictOpSrc, picture, &render_color[1],
117			     0, target->height/2, target->width, target->height/2);
118
119	return picture;
120}
121
122static Picture source_a8r8g8b8(struct test_display *t, struct test_target *target)
123{
124	return source_pixmap(t, target, 0);
125}
126
127static Picture source_x8r8g8b8(struct test_display *t, struct test_target *target)
128{
129	return source_pixmap(t, target, 1);
130}
131
132static Picture source_a8(struct test_display *t, struct test_target *target)
133{
134	return source_pixmap(t, target, 2);
135}
136
137static Picture source_a4(struct test_display *t, struct test_target *target)
138{
139	return source_pixmap(t, target, 3);
140}
141
142static Picture source_a1(struct test_display *t, struct test_target *target)
143{
144	return source_pixmap(t, target, 3);
145}
146
147static Picture source_1x1r(struct test_display *t, struct test_target *target)
148{
149	XRenderColor render_color = { 0x8000, 0x8000, 0x8000, 0x8000 };
150	XRenderPictureAttributes pa;
151	Pixmap pixmap;
152	Picture picture;
153
154	pa.repeat = RepeatNormal;
155
156	pixmap = XCreatePixmap(t->dpy, t->root, 1, 1, 32);
157	picture = XRenderCreatePicture(t->dpy, pixmap,
158				       XRenderFindStandardFormat(t->dpy, 0),
159				       CPRepeat, &pa);
160	XFreePixmap(t->dpy, pixmap);
161
162	XRenderFillRectangle(t->dpy, PictOpSrc, picture, &render_color,
163			     0, 0, 1, 1);
164
165	return picture;
166}
167
168static Picture source_solid(struct test_display *t, struct test_target *target)
169{
170	XRenderColor render_color = { 0x8000, 0x8000, 0x8000, 0x8000 };
171	return XRenderCreateSolidFill(t->dpy, &render_color);
172}
173
174static Picture source_linear_horizontal(struct test_display *t, struct test_target *target)
175{
176	XRenderColor colors[2] = {{0}, {0xffff, 0xffff, 0xffff, 0xffff}};
177	XFixed stops[2] = {0, 0xffff};
178	XLinearGradient gradient = { {0, 0}, {target->width << 16, 0}};
179
180	return XRenderCreateLinearGradient(t->dpy, &gradient, stops, colors, 2);
181}
182
183static Picture source_linear_vertical(struct test_display *t, struct test_target *target)
184{
185	XRenderColor colors[2] = {{0}, {0xffff, 0xffff, 0xffff, 0xffff}};
186	XFixed stops[2] = {0, 0xffff};
187	XLinearGradient gradient = { {0, 0}, {0, target->height << 16}};
188
189	return XRenderCreateLinearGradient(t->dpy, &gradient, stops, colors, 2);
190}
191
192static Picture source_linear_diagonal(struct test_display *t, struct test_target *target)
193{
194	XRenderColor colors[2] = {{0}, {0xffff, 0xffff, 0xffff, 0xffff}};
195	XFixed stops[2] = {0, 0xffff};
196	XLinearGradient gradient = { {0, 0}, {target->width << 16, target->height << 16}};
197
198	return XRenderCreateLinearGradient(t->dpy, &gradient, stops, colors, 2);
199}
200
201static Picture source_radial_concentric(struct test_display *t, struct test_target *target)
202{
203	XRenderColor colors[2] = {{0}, {0xffff, 0xffff, 0xffff, 0xffff}};
204	XFixed stops[2] = {0, 0xffff};
205	XRadialGradient gradient = {
206		{
207			((target->width << 16) + 1) / 2,
208			((target->height << 16) + 1) / 2,
209			0,
210		},
211		{
212			((target->width << 16) + 1) / 2,
213			((target->height << 16) + 1) / 2,
214			target->width << 15,
215		}
216	};
217
218	return XRenderCreateRadialGradient(t->dpy, &gradient, stops, colors, 2);
219}
220
221static Picture source_radial_generic(struct test_display *t, struct test_target *target)
222{
223	XRenderColor colors[2] = {{0}, {0xffff, 0xffff, 0xffff, 0xffff}};
224	XFixed stops[2] = {0, 0xffff};
225	XRadialGradient gradient = {
226		{ 0, 0, target->width << 14, },
227		{ target->width << 16, target->height << 16, target->width << 14, }
228	};
229
230	return XRenderCreateRadialGradient(t->dpy, &gradient, stops, colors, 2);
231}
232
233#if HAVE_MIT_SHM
234static XShmSegmentInfo shmref, shmout;
235
236static void setup_shm(struct test *t)
237{
238	XShmSegmentInfo shm;
239	int size;
240
241	shm.shmid = -1;
242
243	if (!(t->ref.has_shm_pixmaps && t->out.has_shm_pixmaps))
244		return;
245
246	size = t->ref.width * t->ref.height * 4;
247	size = (size + 4095) & -4096;
248
249	shm.shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | 0666);
250	if (shm.shmid == -1)
251		return;
252
253	shm.shmaddr = shmat(shm.shmid, 0, 0);
254	if (shm.shmaddr == (char *) -1) {
255		shmctl(shm.shmid, IPC_RMID, NULL);
256		shm.shmid = -1;
257		return;
258	}
259
260	shm.readOnly = False;
261
262	shmref = shm;
263	XShmAttach(t->ref.dpy, &shmref);
264	XSync(t->ref.dpy, True);
265
266	shmout = shm;
267	XShmAttach(t->out.dpy, &shmout);
268	XSync(t->out.dpy, True);
269}
270
271static Picture source_shm(struct test_display *t, struct test_target *target)
272{
273	XShmSegmentInfo *shm = t->target == REF ? &shmref : &shmout;
274	Pixmap pixmap;
275	Picture picture;
276	int size;
277
278	if (shm->shmid == -1)
279		return 0;
280
281	pixmap = XShmCreatePixmap(t->dpy, t->root,
282				  shm->shmaddr, shm,
283				  target->width, target->height, 32);
284
285	picture = XRenderCreatePicture(t->dpy, pixmap,
286				       XRenderFindStandardFormat(t->dpy, 0),
287				       0, NULL);
288	XFreePixmap(t->dpy, pixmap);
289
290	size = target->width * target->height * 4;
291	memset(shm->shmaddr, 0x80, size/2);
292	memset(shm->shmaddr+size/2, 0xff, size/2);
293
294	return picture;
295}
296#else
297static void setup_shm(struct test *t) { }
298static Picture source_shm(struct test_display *t, struct test_target *target) { return 0; }
299#endif
300
301static const struct {
302	Picture (*create)(struct test_display *, struct test_target *);
303	const char *name;
304} source[] = {
305	{ source_a8r8g8b8, "a8r8g8b8 pixmap" },
306	{ source_x8r8g8b8, "x8r8g8b8 pixmap" },
307	{ source_a8, "a8 pixmap" },
308	{ source_a4, "a4 pixmap" },
309	{ source_a1, "a1 pixmap" },
310	{ source_1x1r, "a8r8g8b8 1x1R pixmap" },
311	{ source_solid, "solid" },
312	{ source_shm, "a8r8g8b8 shm" },
313	{ source_linear_horizontal, "linear (horizontal gradient)" },
314	{ source_linear_vertical, "linear (vertical gradient)" },
315	{ source_linear_diagonal, "linear (diagonal gradient)" },
316	{ source_radial_concentric, "radial (concentric)" },
317	{ source_radial_generic, "radial (generic)" },
318};
319
320static double _bench_source(struct test_display *t, enum target target_type,
321			    int op, int src, int loops)
322{
323	XRenderColor render_color = { 0x8000, 0x8000, 0x8000, 0x8000 };
324	struct test_target target;
325	Picture picture;
326	struct timespec tv;
327	double elapsed;
328
329	test_target_create_render(t, target_type, &target);
330	XRenderFillRectangle(t->dpy, PictOpClear, target.picture, &render_color,
331			     0, 0, target.width, target.height);
332
333	picture = source[src].create(t, &target);
334	if (picture) {
335		test_timer_start(t, &tv);
336		while (loops--)
337			XRenderComposite(t->dpy, op,
338					 picture, 0, target.picture,
339					 0, 0,
340					 0, 0,
341					 0, 0,
342					 target.width, target.height);
343		elapsed = test_timer_stop(t, &tv);
344		XRenderFreePicture(t->dpy, picture);
345	} else
346		elapsed = -1;
347
348	test_target_destroy_render(t, &target);
349
350	return elapsed;
351}
352
353static void bench_source(struct test *t, enum target target, int op, int src)
354{
355	double out, ref;
356
357	fprintf(stdout, "%28s with %s: ", source[src].name, ops[op].name);
358	fflush(stdout);
359
360	op = ops[op].value;
361
362	ref = _bench_source(&t->ref, target, op, src, 1000);
363	if (ref < 0) {
364		fprintf(stdout, "SKIP\n");
365		return;
366	}
367	fprintf(stdout, "ref=%f, ", ref);
368	fflush(stdout);
369
370	out = _bench_source(&t->out, target, op, src, 1000);
371	if (out < 0) {
372		fprintf(stdout, "SKIP\n");
373		return;
374	}
375
376	fprintf(stdout, "out=%f\n", out);
377}
378
379static double _bench_mask(struct test_display *t, enum target target_type,
380			    int op, int src, int mask, int loops)
381{
382	XRenderColor render_color = { 0x8000, 0x8000, 0x8000, 0x8000 };
383	struct test_target target;
384	Picture ps, pm;
385	struct timespec tv;
386	double elapsed;
387
388	test_target_create_render(t, target_type, &target);
389	XRenderFillRectangle(t->dpy, PictOpClear, target.picture, &render_color,
390			     0, 0, target.width, target.height);
391
392	ps = source[src].create(t, &target);
393	pm = source[mask].create(t, &target);
394	if (ps && pm) {
395		test_timer_start(t, &tv);
396		while (loops--)
397			XRenderComposite(t->dpy, op,
398					 ps, pm, target.picture,
399					 0, 0,
400					 0, 0,
401					 0, 0,
402					 target.width, target.height);
403		elapsed = test_timer_stop(t, &tv);
404	} else
405		elapsed = -1;
406
407	if (ps)
408		XRenderFreePicture(t->dpy, ps);
409	if (pm)
410		XRenderFreePicture(t->dpy, pm);
411
412	test_target_destroy_render(t, &target);
413
414	return elapsed;
415}
416
417static void bench_mask(struct test *t, enum target target, int op, int src, int mask)
418{
419	double out, ref;
420
421	fprintf(stdout, "%28s In %28s with %s: ",
422		source[src].name, source[mask].name, ops[op].name);
423	fflush(stdout);
424
425	op = ops[op].value;
426
427	ref = _bench_mask(&t->ref, target, op, src, mask, 1000);
428	if (ref < 0) {
429		fprintf(stdout, "SKIP\n");
430		return;
431	}
432	fprintf(stdout, "ref=%f, ", ref);
433	fflush(stdout);
434
435	out = _bench_mask(&t->out, target, op, src, mask, 1000);
436	if (out < 0) {
437		fprintf(stdout, "SKIP\n");
438		return;
439	}
440
441	fprintf(stdout, "out=%f\n", out);
442}
443
444int main(int argc, char **argv)
445{
446	struct test test;
447	unsigned op, src, mask;
448
449	test_init(&test, argc, argv);
450
451	setup_shm(&test);
452
453	for (op = 0; op < sizeof(ops)/sizeof(ops[0]); op++) {
454		for (src = 0; src < sizeof(source)/sizeof(source[0]); src++)
455			bench_source(&test, ROOT, op, src);
456		fprintf (stdout, "\n");
457
458		for (src = 0; src < sizeof(source)/sizeof(source[0]); src++)
459			for (mask = 0; mask < sizeof(source)/sizeof(source[0]); mask++)
460				bench_mask(&test, ROOT, op, src, mask);
461		fprintf (stdout, "\n");
462	}
463
464	return 0;
465}
466