1/**************************************************************************
2 *
3 * Copyright 2009 Younes Manton.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28/* Force assertions, even on release builds. */
29#undef NDEBUG
30#include <assert.h>
31#include <stdio.h>
32#include <string.h>
33#include <stdlib.h>
34#include <sys/time.h>
35#include "testlib.h"
36
37#define MACROBLOCK_WIDTH		16
38#define MACROBLOCK_HEIGHT		16
39#define BLOCKS_PER_MACROBLOCK		6
40
41#define DEFAULT_INPUT_WIDTH		720
42#define DEFAULT_INPUT_HEIGHT		480
43#define DEFAULT_REPS			100
44
45#define PIPELINE_STEP_MC		1
46#define PIPELINE_STEP_CSC		2
47#define PIPELINE_STEP_SWAP		4
48
49#define MB_TYPE_I			1
50#define MB_TYPE_P			2
51#define MB_TYPE_B			4
52
53struct Config
54{
55	unsigned int input_width;
56	unsigned int input_height;
57	unsigned int output_width;
58	unsigned int output_height;
59	unsigned int pipeline;
60	unsigned int mb_types;
61	unsigned int reps;
62};
63
64void ParseArgs(int argc, char **argv, struct Config *config);
65
66void ParseArgs(int argc, char **argv, struct Config *config)
67{
68	int fail = 0;
69	int i;
70
71	config->input_width = DEFAULT_INPUT_WIDTH;
72	config->input_height = DEFAULT_INPUT_HEIGHT;
73	config->output_width = 0;
74	config->output_height = 0;
75	config->pipeline = 0;
76	config->mb_types = 0;
77	config->reps = DEFAULT_REPS;
78
79	for (i = 1; i < argc && !fail; ++i)
80	{
81		if (!strcmp(argv[i], "-iw"))
82		{
83			if (sscanf(argv[++i], "%u", &config->input_width) != 1)
84				fail = 1;
85		}
86		else if (!strcmp(argv[i], "-ih"))
87		{
88			if (sscanf(argv[++i], "%u", &config->input_height) != 1)
89				fail = 1;
90		}
91		else if (!strcmp(argv[i], "-ow"))
92		{
93			if (sscanf(argv[++i], "%u", &config->output_width) != 1)
94				fail = 1;
95		}
96		else if (!strcmp(argv[i], "-oh"))
97		{
98			if (sscanf(argv[++i], "%u", &config->output_height) != 1)
99				fail = 1;
100		}
101		else if (!strcmp(argv[i], "-p"))
102		{
103			char *token = strtok(argv[++i], ",");
104
105			while (token && !fail)
106			{
107				if (!strcmp(token, "mc"))
108					config->pipeline |= PIPELINE_STEP_MC;
109				else if (!strcmp(token, "csc"))
110					config->pipeline |= PIPELINE_STEP_CSC;
111				else if (!strcmp(token, "swp"))
112					config->pipeline |= PIPELINE_STEP_SWAP;
113				else
114					fail = 1;
115
116				if (!fail)
117					token = strtok(NULL, ",");
118			}
119		}
120		else if (!strcmp(argv[i], "-mb"))
121		{
122			char *token = strtok(argv[++i], ",");
123
124			while (token && !fail)
125			{
126				if (strcmp(token, "i") == 0)
127					config->mb_types |= MB_TYPE_I;
128				else if (strcmp(token, "p") == 0)
129					config->mb_types |= MB_TYPE_P;
130				else if (strcmp(token, "b") == 0)
131					config->mb_types |= MB_TYPE_B;
132				else
133					fail = 1;
134
135				if (!fail)
136					token = strtok(NULL, ",");
137			}
138		}
139		else if (!strcmp(argv[i], "-r"))
140		{
141			if (sscanf(argv[++i], "%u", &config->reps) != 1)
142				fail = 1;
143		}
144		else
145			fail = 1;
146	}
147
148	if (fail)
149	{
150		fprintf(
151			stderr,
152			"Bad argument.\n"
153			"\n"
154			"Usage: %s [options]\n"
155			"\t-iw <width>\tInput width\n"
156			"\t-ih <height>\tInput height\n"
157			"\t-ow <width>\tOutput width\n"
158			"\t-oh <height>\tOutput height\n"
159			"\t-p <pipeline>\tPipeline to test\n"
160			"\t-mb <mb type>\tMacroBlock types to use\n"
161			"\t-r <reps>\tRepetitions\n\n"
162			"\tPipeline steps: mc,csc,swap\n"
163			"\tMB types: i,p,b\n",
164			argv[0]
165		);
166		exit(1);
167	}
168
169	if (config->output_width == 0)
170		config->output_width = config->input_width;
171	if (config->output_height == 0)
172		config->output_height = config->input_height;
173	if (!config->pipeline)
174		config->pipeline = PIPELINE_STEP_MC | PIPELINE_STEP_CSC | PIPELINE_STEP_SWAP;
175	if (!config->mb_types)
176		config->mb_types = MB_TYPE_I | MB_TYPE_P | MB_TYPE_B;
177}
178
179int main(int argc, char **argv)
180{
181	struct Config		config;
182	Display			*display;
183	Window			root, window;
184	const unsigned int	mc_types[2] = {XVMC_MOCOMP | XVMC_MPEG_2, XVMC_IDCT | XVMC_MPEG_2};
185	XvPortID		port_num;
186	int			surface_type_id;
187	unsigned int		is_overlay, intra_unsigned;
188	int			colorkey;
189	XvMCContext		context;
190	XvMCSurface		surface;
191	XvMCBlockArray		block_array;
192	XvMCMacroBlockArray	mb_array;
193	unsigned int		mbw, mbh;
194	unsigned int		mbx, mby;
195	unsigned int		reps;
196	struct timeval		start, stop, diff;
197	double			diff_secs;
198
199	ParseArgs(argc, argv, &config);
200
201	mbw = align(config.input_width, MACROBLOCK_WIDTH) / MACROBLOCK_WIDTH;
202	mbh = align(config.input_height, MACROBLOCK_HEIGHT) / MACROBLOCK_HEIGHT;
203
204	display = XOpenDisplay(NULL);
205
206	if (!GetPort
207	(
208		display,
209		config.input_width,
210		config.input_height,
211		XVMC_CHROMA_FORMAT_420,
212		mc_types,
213		2,
214		&port_num,
215		&surface_type_id,
216		&is_overlay,
217		&intra_unsigned
218	))
219	{
220		XCloseDisplay(display);
221		fprintf(stderr, "Error, unable to find a good port.\n");
222		exit(1);
223	}
224
225	if (is_overlay)
226	{
227		Atom xv_colorkey = XInternAtom(display, "XV_COLORKEY", 0);
228		XvGetPortAttribute(display, port_num, xv_colorkey, &colorkey);
229	}
230	else
231	{
232		colorkey = 0;
233	}
234
235	root = XDefaultRootWindow(display);
236	window = XCreateSimpleWindow(display, root, 0, 0, config.output_width, config.output_height, 0, 0, colorkey);
237
238	assert(XvMCCreateContext(display, port_num, surface_type_id, config.input_width, config.input_height, XVMC_DIRECT, &context) == Success);
239	assert(XvMCCreateSurface(display, &context, &surface) == Success);
240	assert(XvMCCreateBlocks(display, &context, mbw * mbh * BLOCKS_PER_MACROBLOCK, &block_array) == Success);
241	assert(XvMCCreateMacroBlocks(display, &context, mbw * mbh, &mb_array) == Success);
242
243	for (mby = 0; mby < mbh; ++mby)
244		for (mbx = 0; mbx < mbw; ++mbx)
245		{
246			mb_array.macro_blocks[mby * mbw + mbx].x = mbx;
247			mb_array.macro_blocks[mby * mbw + mbx].y = mby;
248			mb_array.macro_blocks[mby * mbw + mbx].macroblock_type = XVMC_MB_TYPE_INTRA;
249			/*mb->motion_type = ;*/
250			/*mb->motion_vertical_field_select = ;*/
251			mb_array.macro_blocks[mby * mbw + mbx].dct_type = XVMC_DCT_TYPE_FRAME;
252			/*mb->PMV[0][0][0] = ;
253			mb->PMV[0][0][1] = ;
254			mb->PMV[0][1][0] = ;
255			mb->PMV[0][1][1] = ;
256			mb->PMV[1][0][0] = ;
257			mb->PMV[1][0][1] = ;
258			mb->PMV[1][1][0] = ;
259			mb->PMV[1][1][1] = ;*/
260			mb_array.macro_blocks[mby * mbw + mbx].index = (mby * mbw + mbx) * BLOCKS_PER_MACROBLOCK;
261			mb_array.macro_blocks[mby * mbw + mbx].coded_block_pattern = 0x3F;
262		}
263
264	XSelectInput(display, window, ExposureMask | KeyPressMask);
265	XMapWindow(display, window);
266	XSync(display, 0);
267
268	gettimeofday(&start, NULL);
269
270	for (reps = 0; reps < config.reps; ++reps)
271	{
272		if (config.pipeline & PIPELINE_STEP_MC)
273		{
274			assert(XvMCRenderSurface(display, &context, XVMC_FRAME_PICTURE, &surface, NULL, NULL, 0, mbw * mbh, 0, &mb_array, &block_array) == Success);
275			assert(XvMCFlushSurface(display, &surface) == Success);
276		}
277		if (config.pipeline & PIPELINE_STEP_CSC)
278			assert(XvMCPutSurface(display, &surface, window, 0, 0, config.input_width, config.input_height, 0, 0, config.output_width, config.output_height, XVMC_FRAME_PICTURE) == Success);
279	}
280
281	gettimeofday(&stop, NULL);
282
283	timeval_subtract(&diff, &stop, &start);
284	diff_secs = (double)diff.tv_sec + (double)diff.tv_usec / 1000000.0;
285
286	printf("XvMC Benchmark\n");
287	printf("Input: %u,%u\nOutput: %u,%u\n", config.input_width, config.input_height, config.output_width, config.output_height);
288	printf("Pipeline: ");
289	if (config.pipeline & PIPELINE_STEP_MC)
290		printf("|mc|");
291	if (config.pipeline & PIPELINE_STEP_CSC)
292		printf("|csc|");
293	if (config.pipeline & PIPELINE_STEP_SWAP)
294		printf("|swap|");
295	printf("\n");
296	printf("Reps: %u\n", config.reps);
297	printf("Total time: %.2lf (%.2lf reps / sec)\n", diff_secs, config.reps / diff_secs);
298
299	assert(XvMCDestroyBlocks(display, &block_array) == Success);
300	assert(XvMCDestroyMacroBlocks(display, &mb_array) == Success);
301	assert(XvMCDestroySurface(display, &surface) == Success);
302	assert(XvMCDestroyContext(display, &context) == Success);
303
304	XvUngrabPort(display, port_num, CurrentTime);
305	XDestroyWindow(display, window);
306	XCloseDisplay(display);
307
308	return 0;
309}
310