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 TUNGSTEN GRAPHICS 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#include <assert.h>
29#include <stdio.h>
30#include <string.h>
31#include <error.h>
32#include <sys/time.h>
33#include "testlib.h"
34
35#define MACROBLOCK_WIDTH		16
36#define MACROBLOCK_HEIGHT		16
37#define BLOCKS_PER_MACROBLOCK		6
38
39#define DEFAULT_INPUT_WIDTH		720
40#define DEFAULT_INPUT_HEIGHT		480
41#define DEFAULT_REPS			100
42
43#define PIPELINE_STEP_MC		1
44#define PIPELINE_STEP_CSC		2
45#define PIPELINE_STEP_SWAP		4
46
47#define MB_TYPE_I			1
48#define MB_TYPE_P			2
49#define MB_TYPE_B			4
50
51struct Config
52{
53	unsigned int input_width;
54	unsigned int input_height;
55	unsigned int output_width;
56	unsigned int output_height;
57	unsigned int pipeline;
58	unsigned int mb_types;
59	unsigned int reps;
60};
61
62void ParseArgs(int argc, char **argv, struct Config *config);
63
64void ParseArgs(int argc, char **argv, struct Config *config)
65{
66	int fail = 0;
67	int i;
68
69	config->input_width = DEFAULT_INPUT_WIDTH;
70	config->input_height = DEFAULT_INPUT_HEIGHT;
71	config->output_width = 0;
72	config->output_height = 0;
73	config->pipeline = 0;
74	config->mb_types = 0;
75	config->reps = DEFAULT_REPS;
76
77	for (i = 1; i < argc && !fail; ++i)
78	{
79		if (!strcmp(argv[i], "-iw"))
80		{
81			if (sscanf(argv[++i], "%u", &config->input_width) != 1)
82				fail = 1;
83		}
84		else if (!strcmp(argv[i], "-ih"))
85		{
86			if (sscanf(argv[++i], "%u", &config->input_height) != 1)
87				fail = 1;
88		}
89		else if (!strcmp(argv[i], "-ow"))
90		{
91			if (sscanf(argv[++i], "%u", &config->output_width) != 1)
92				fail = 1;
93		}
94		else if (!strcmp(argv[i], "-oh"))
95		{
96			if (sscanf(argv[++i], "%u", &config->output_height) != 1)
97				fail = 1;
98		}
99		else if (!strcmp(argv[i], "-p"))
100		{
101			char *token = strtok(argv[++i], ",");
102
103			while (token && !fail)
104			{
105				if (!strcmp(token, "mc"))
106					config->pipeline |= PIPELINE_STEP_MC;
107				else if (!strcmp(token, "csc"))
108					config->pipeline |= PIPELINE_STEP_CSC;
109				else if (!strcmp(token, "swp"))
110					config->pipeline |= PIPELINE_STEP_SWAP;
111				else
112					fail = 1;
113
114				if (!fail)
115					token = strtok(NULL, ",");
116			}
117		}
118		else if (!strcmp(argv[i], "-mb"))
119		{
120			char *token = strtok(argv[++i], ",");
121
122			while (token && !fail)
123			{
124				if (strcmp(token, "i"))
125					config->mb_types |= MB_TYPE_I;
126				else if (strcmp(token, "p"))
127					config->mb_types |= MB_TYPE_P;
128				else if (strcmp(token, "b"))
129					config->mb_types |= MB_TYPE_B;
130				else
131					fail = 1;
132
133				if (!fail)
134					token = strtok(NULL, ",");
135			}
136		}
137		else if (!strcmp(argv[i], "-r"))
138		{
139			if (sscanf(argv[++i], "%u", &config->reps) != 1)
140				fail = 1;
141		}
142		else
143			fail = 1;
144	}
145
146	if (fail)
147		error
148		(
149			1, 0,
150			"Bad argument.\n"
151			"\n"
152			"Usage: %s [options]\n"
153			"\t-iw <width>\tInput width\n"
154			"\t-ih <height>\tInput height\n"
155			"\t-ow <width>\tOutput width\n"
156			"\t-oh <height>\tOutput height\n"
157			"\t-p <pipeline>\tPipeline to test\n"
158			"\t-mb <mb type>\tMacroBlock types to use\n"
159			"\t-r <reps>\tRepetitions\n\n"
160			"\tPipeline steps: mc,csc,swap\n"
161			"\tMB types: i,p,b\n",
162			argv[0]
163		);
164
165	if (config->output_width == 0)
166		config->output_width = config->input_width;
167	if (config->output_height == 0)
168		config->output_height = config->input_height;
169	if (!config->pipeline)
170		config->pipeline = PIPELINE_STEP_MC | PIPELINE_STEP_CSC | PIPELINE_STEP_SWAP;
171	if (!config->mb_types)
172		config->mb_types = MB_TYPE_I | MB_TYPE_P | MB_TYPE_B;
173}
174
175int main(int argc, char **argv)
176{
177	struct Config		config;
178	Display			*display;
179	Window			root, window;
180	const unsigned int	mc_types[2] = {XVMC_MOCOMP | XVMC_MPEG_2, XVMC_IDCT | XVMC_MPEG_2};
181	XvPortID		port_num;
182	int			surface_type_id;
183	unsigned int		is_overlay, intra_unsigned;
184	int			colorkey;
185	XvMCContext		context;
186	XvMCSurface		surface;
187	XvMCBlockArray		block_array;
188	XvMCMacroBlockArray	mb_array;
189	unsigned int		mbw, mbh;
190	unsigned int		mbx, mby;
191	unsigned int		reps;
192	struct timeval		start, stop, diff;
193	double			diff_secs;
194
195	ParseArgs(argc, argv, &config);
196
197	mbw = align(config.input_width, MACROBLOCK_WIDTH) / MACROBLOCK_WIDTH;
198	mbh = align(config.input_height, MACROBLOCK_HEIGHT) / MACROBLOCK_HEIGHT;
199
200	display = XOpenDisplay(NULL);
201
202	if (!GetPort
203	(
204		display,
205		config.input_width,
206		config.input_height,
207		XVMC_CHROMA_FORMAT_420,
208		mc_types,
209		2,
210		&port_num,
211		&surface_type_id,
212		&is_overlay,
213		&intra_unsigned
214	))
215	{
216		XCloseDisplay(display);
217		error(1, 0, "Error, unable to find a good port.\n");
218	}
219
220	if (is_overlay)
221	{
222		Atom xv_colorkey = XInternAtom(display, "XV_COLORKEY", 0);
223		XvGetPortAttribute(display, port_num, xv_colorkey, &colorkey);
224	}
225
226	root = XDefaultRootWindow(display);
227	window = XCreateSimpleWindow(display, root, 0, 0, config.output_width, config.output_height, 0, 0, colorkey);
228
229	assert(XvMCCreateContext(display, port_num, surface_type_id, config.input_width, config.input_height, XVMC_DIRECT, &context) == Success);
230	assert(XvMCCreateSurface(display, &context, &surface) == Success);
231	assert(XvMCCreateBlocks(display, &context, mbw * mbh * BLOCKS_PER_MACROBLOCK, &block_array) == Success);
232	assert(XvMCCreateMacroBlocks(display, &context, mbw * mbh, &mb_array) == Success);
233
234	for (mby = 0; mby < mbh; ++mby)
235		for (mbx = 0; mbx < mbw; ++mbx)
236		{
237			mb_array.macro_blocks[mby * mbw + mbx].x = mbx;
238			mb_array.macro_blocks[mby * mbw + mbx].y = mby;
239			mb_array.macro_blocks[mby * mbw + mbx].macroblock_type = XVMC_MB_TYPE_INTRA;
240			/*mb->motion_type = ;*/
241			/*mb->motion_vertical_field_select = ;*/
242			mb_array.macro_blocks[mby * mbw + mbx].dct_type = XVMC_DCT_TYPE_FRAME;
243			/*mb->PMV[0][0][0] = ;
244			mb->PMV[0][0][1] = ;
245			mb->PMV[0][1][0] = ;
246			mb->PMV[0][1][1] = ;
247			mb->PMV[1][0][0] = ;
248			mb->PMV[1][0][1] = ;
249			mb->PMV[1][1][0] = ;
250			mb->PMV[1][1][1] = ;*/
251			mb_array.macro_blocks[mby * mbw + mbx].index = (mby * mbw + mbx) * BLOCKS_PER_MACROBLOCK;
252			mb_array.macro_blocks[mby * mbw + mbx].coded_block_pattern = 0x3F;
253		}
254
255	XSelectInput(display, window, ExposureMask | KeyPressMask);
256	XMapWindow(display, window);
257	XSync(display, 0);
258
259	gettimeofday(&start, NULL);
260
261	for (reps = 0; reps < config.reps; ++reps)
262	{
263		if (config.pipeline & PIPELINE_STEP_MC)
264		{
265			assert(XvMCRenderSurface(display, &context, XVMC_FRAME_PICTURE, &surface, NULL, NULL, 0, mbw * mbh, 0, &mb_array, &block_array) == Success);
266			assert(XvMCFlushSurface(display, &surface) == Success);
267		}
268		if (config.pipeline & PIPELINE_STEP_CSC)
269			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);
270	}
271
272	gettimeofday(&stop, NULL);
273
274	timeval_subtract(&diff, &stop, &start);
275	diff_secs = (double)diff.tv_sec + (double)diff.tv_usec / 1000000.0;
276
277	printf("XvMC Benchmark\n");
278	printf("Input: %u,%u\nOutput: %u,%u\n", config.input_width, config.input_height, config.output_width, config.output_height);
279	printf("Pipeline: ");
280	if (config.pipeline & PIPELINE_STEP_MC)
281		printf("|mc|");
282	if (config.pipeline & PIPELINE_STEP_CSC)
283		printf("|csc|");
284	if (config.pipeline & PIPELINE_STEP_SWAP)
285		printf("|swap|");
286	printf("\n");
287	printf("Reps: %u\n", config.reps);
288	printf("Total time: %.2lf (%.2lf reps / sec)\n", diff_secs, config.reps / diff_secs);
289
290	assert(XvMCDestroyBlocks(display, &block_array) == Success);
291	assert(XvMCDestroyMacroBlocks(display, &mb_array) == Success);
292	assert(XvMCDestroySurface(display, &surface) == Success);
293	assert(XvMCDestroyContext(display, &context) == Success);
294
295	XvUngrabPort(display, port_num, CurrentTime);
296	XDestroyWindow(display, window);
297	XCloseDisplay(display);
298
299	return 0;
300}
301