/**************************************************************************
*
* Copyright 2009 Younes Manton.
* All Rights Reserved.
*
* 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, sub license, 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 (including the
* next paragraph) 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 NON-INFRINGEMENT.
* IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS 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.
*
**************************************************************************/
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <error.h>
#include <sys/time.h>
#include "testlib.h"
#define MACROBLOCK_WIDTH 16
#define MACROBLOCK_HEIGHT 16
#define BLOCKS_PER_MACROBLOCK 6
#define DEFAULT_INPUT_WIDTH 720
#define DEFAULT_INPUT_HEIGHT 480
#define DEFAULT_REPS 100
#define PIPELINE_STEP_MC 1
#define PIPELINE_STEP_CSC 2
#define PIPELINE_STEP_SWAP 4
#define MB_TYPE_I 1
#define MB_TYPE_P 2
#define MB_TYPE_B 4
struct Config
{
unsigned int input_width;
unsigned int input_height;
unsigned int output_width;
unsigned int output_height;
unsigned int pipeline;
unsigned int mb_types;
unsigned int reps;
};
void ParseArgs(int argc, char **argv, struct Config *config);
void ParseArgs(int argc, char **argv, struct Config *config)
{
int fail = 0;
int i;
config->input_width = DEFAULT_INPUT_WIDTH;
config->input_height = DEFAULT_INPUT_HEIGHT;
config->output_width = 0;
config->output_height = 0;
config->pipeline = 0;
config->mb_types = 0;
config->reps = DEFAULT_REPS;
for (i = 1; i < argc && !fail; ++i)
{
if (!strcmp(argv[i], "-iw"))
{
if (sscanf(argv[++i], "%u", &config->input_width) != 1)
fail = 1;
}
else if (!strcmp(argv[i], "-ih"))
{
if (sscanf(argv[++i], "%u", &config->input_height) != 1)
fail = 1;
}
else if (!strcmp(argv[i], "-ow"))
{
if (sscanf(argv[++i], "%u", &config->output_width) != 1)
fail = 1;
}
else if (!strcmp(argv[i], "-oh"))
{
if (sscanf(argv[++i], "%u", &config->output_height) != 1)
fail = 1;
}
else if (!strcmp(argv[i], "-p"))
{
char *token = strtok(argv[++i], ",");
while (token && !fail)
{
if (!strcmp(token, "mc"))
config->pipeline |= PIPELINE_STEP_MC;
else if (!strcmp(token, "csc"))
config->pipeline |= PIPELINE_STEP_CSC;
else if (!strcmp(token, "swp"))
config->pipeline |= PIPELINE_STEP_SWAP;
else
fail = 1;
if (!fail)
token = strtok(NULL, ",");
}
}
else if (!strcmp(argv[i], "-mb"))
{
char *token = strtok(argv[++i], ",");
while (token && !fail)
{
if (strcmp(token, "i"))
config->mb_types |= MB_TYPE_I;
else if (strcmp(token, "p"))
config->mb_types |= MB_TYPE_P;
else if (strcmp(token, "b"))
config->mb_types |= MB_TYPE_B;
else
fail = 1;
if (!fail)
token = strtok(NULL, ",");
}
}
else if (!strcmp(argv[i], "-r"))
{
if (sscanf(argv[++i], "%u", &config->reps) != 1)
fail = 1;
}
else
fail = 1;
}
if (fail)
error
(
1, 0,
"Bad argument.\n"
"\n"
"Usage: %s [options]\n"
"\t-iw <width>\tInput width\n"
"\t-ih <height>\tInput height\n"
"\t-ow <width>\tOutput width\n"
"\t-oh <height>\tOutput height\n"
"\t-p <pipeline>\tPipeline to test\n"
"\t-mb <mb type>\tMacroBlock types to use\n"
"\t-r <reps>\tRepetitions\n\n"
"\tPipeline steps: mc,csc,swap\n"
"\tMB types: i,p,b\n",
argv[0]
);
if (config->output_width == 0)
config->output_width = config->input_width;
if (config->output_height == 0)
config->output_height = config->input_height;
if (!config->pipeline)
config->pipeline = PIPELINE_STEP_MC | PIPELINE_STEP_CSC | PIPELINE_STEP_SWAP;
if (!config->mb_types)
config->mb_types = MB_TYPE_I | MB_TYPE_P | MB_TYPE_B;
}
int main(int argc, char **argv)
{
struct Config config;
Display *display;
Window root, window;
const unsigned int mc_types[2] = {XVMC_MOCOMP | XVMC_MPEG_2, XVMC_IDCT | XVMC_MPEG_2};
XvPortID port_num;
int surface_type_id;
unsigned int is_overlay, intra_unsigned;
int colorkey;
XvMCContext context;
XvMCSurface surface;
XvMCBlockArray block_array;
XvMCMacroBlockArray mb_array;
unsigned int mbw, mbh;
unsigned int mbx, mby;
unsigned int reps;
struct timeval start, stop, diff;
double diff_secs;
ParseArgs(argc, argv, &config);
mbw = align(config.input_width, MACROBLOCK_WIDTH) / MACROBLOCK_WIDTH;
mbh = align(config.input_height, MACROBLOCK_HEIGHT) / MACROBLOCK_HEIGHT;
display = XOpenDisplay(NULL);
if (!GetPort
(
display,
config.input_width,
config.input_height,
XVMC_CHROMA_FORMAT_420,
mc_types,
2,
&port_num,
&surface_type_id,
&is_overlay,
&intra_unsigned
))
{
XCloseDisplay(display);
error(1, 0, "Error, unable to find a good port.\n");
}
if (is_overlay)
{
Atom xv_colorkey = XInternAtom(display, "XV_COLORKEY", 0);
XvGetPortAttribute(display, port_num, xv_colorkey, &colorkey);
}
root = XDefaultRootWindow(display);
window = XCreateSimpleWindow(display, root, 0, 0, config.output_width, config.output_height, 0, 0, colorkey);
assert(XvMCCreateContext(display, port_num, surface_type_id, config.input_width, config.input_height, XVMC_DIRECT, &context) == Success);
assert(XvMCCreateSurface(display, &context, &surface) == Success);
assert(XvMCCreateBlocks(display, &context, mbw * mbh * BLOCKS_PER_MACROBLOCK, &block_array) == Success);
assert(XvMCCreateMacroBlocks(display, &context, mbw * mbh, &mb_array) == Success);
for (mby = 0; mby < mbh; ++mby)
for (mbx = 0; mbx < mbw; ++mbx)
{
mb_array.macro_blocks[mby * mbw + mbx].x = mbx;
mb_array.macro_blocks[mby * mbw + mbx].y = mby;
mb_array.macro_blocks[mby * mbw + mbx].macroblock_type = XVMC_MB_TYPE_INTRA;
/*mb->motion_type = ;*/
/*mb->motion_vertical_field_select = ;*/
mb_array.macro_blocks[mby * mbw + mbx].dct_type = XVMC_DCT_TYPE_FRAME;
/*mb->PMV[0][0][0] = ;
mb->PMV[0][0][1] = ;
mb->PMV[0][1][0] = ;
mb->PMV[0][1][1] = ;
mb->PMV[1][0][0] = ;
mb->PMV[1][0][1] = ;
mb->PMV[1][1][0] = ;
mb->PMV[1][1][1] = ;*/
mb_array.macro_blocks[mby * mbw + mbx].index = (mby * mbw + mbx) * BLOCKS_PER_MACROBLOCK;
mb_array.macro_blocks[mby * mbw + mbx].coded_block_pattern = 0x3F;
}
XSelectInput(display, window, ExposureMask | KeyPressMask);
XMapWindow(display, window);
XSync(display, 0);
gettimeofday(&start, NULL);
for (reps = 0; reps < config.reps; ++reps)
{
if (config.pipeline & PIPELINE_STEP_MC)
{
assert(XvMCRenderSurface(display, &context, XVMC_FRAME_PICTURE, &surface, NULL, NULL, 0, mbw * mbh, 0, &mb_array, &block_array) == Success);
assert(XvMCFlushSurface(display, &surface) == Success);
}
if (config.pipeline & PIPELINE_STEP_CSC)
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);
}
gettimeofday(&stop, NULL);
timeval_subtract(&diff, &stop, &start);
diff_secs = (double)diff.tv_sec + (double)diff.tv_usec / 1000000.0;
printf("XvMC Benchmark\n");
printf("Input: %u,%u\nOutput: %u,%u\n", config.input_width, config.input_height, config.output_width, config.output_height);
printf("Pipeline: ");
if (config.pipeline & PIPELINE_STEP_MC)
printf("|mc|");
if (config.pipeline & PIPELINE_STEP_CSC)
printf("|csc|");
if (config.pipeline & PIPELINE_STEP_SWAP)
printf("|swap|");
printf("\n");
printf("Reps: %u\n", config.reps);
printf("Total time: %.2lf (%.2lf reps / sec)\n", diff_secs, config.reps / diff_secs);
assert(XvMCDestroyBlocks(display, &block_array) == Success);
assert(XvMCDestroyMacroBlocks(display, &mb_array) == Success);
assert(XvMCDestroySurface(display, &surface) == Success);
assert(XvMCDestroyContext(display, &context) == Success);
XvUngrabPort(display, port_num, CurrentTime);
XDestroyWindow(display, window);
XCloseDisplay(display);
return 0;
}