* Taeheon Kim <th908.kim@samsung.com>,
* YoungJun Cho <yj44.cho@samsung.com>,
* SooChan Lim <sc1.lim@samsung.com>,
- * Boram Park <sc1.lim@samsung.com>
+ * Boram Park <boram1288.park@samsung.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
#include <errno.h>
#include <time.h>
#include <stdint.h>
+#include <png.h>
-#include <tdm_client.h>
+#include "tdm_client.h"
#include "tdm_macro.h"
+#include "buffers.h"
+
+#define CHECK_V_STEP 0
typedef struct _tdm_test_client_arg {
- char output_name[512];
+ char *output_name;
int fps;
int sync;
int interval;
int offset;
int enable_fake;
+ int pid;
+ char *vblank_name;
} tdm_test_client_arg;
typedef struct _tdm_test_client {
int do_query;
int do_vblank;
+ int do_voutput;
int waiting;
tdm_client *client;
+ tdm_client_voutput *voutput;
+ tdm_client_output *output;
} tdm_test_client;
struct typestrings {
static struct optstrings optstrs[] = {
{OPT_QRY, "qo", "output objects info", "<output_name>", "primary"},
- {OPT_TST, "v", "vblank test", "<output_name>[,<sync>][@<fps>][~<interval>][+<offset>][*fake]", "primary,0@60~1+0*1"},
+ {OPT_TST, "v", "vblank test", "<output_name>[,<sync>][@<fps>][~<interval>][+<offset>][*fake][^vblank_name]", "primary,0@60~1+0*1^test"},
+ {OPT_TST, "V", "virtual output test", NULL, NULL},
};
static void
static void
parse_arg_qo(tdm_test_client *data, char *arg)
{
- strtostr(data->args.output_name, 512, arg, TDM_DELIM);
+ char name[TDM_NAME_LEN];
+ strtostr(name, TDM_NAME_LEN, arg, TDM_DELIM);
+ data->args.output_name = strndup(name, TDM_NAME_LEN);
}
//"<output_name>[,<sync>][@<fps>][~<interval>][+<offset>][*fake]"
parse_arg_v(tdm_test_client *data, char *arg)
{
char *end = arg;
+ char name[TDM_NAME_LEN];
- end = strtostr(data->args.output_name, 512, arg, TDM_DELIM);
+ end = strtostr(name, TDM_NAME_LEN, arg, TDM_DELIM);
+ data->args.output_name = strndup(name, TDM_NAME_LEN);
if (*end == ',') {
arg = end + 1;
if (*end == '*') {
arg = end + 1;
- data->args.enable_fake= strtol(arg, &end, 10);
+ data->args.enable_fake = strtol(arg, &end, 10);
+ }
+
+ if (*end == '^') {
+ char name[TDM_NAME_LEN];
+ arg = end + 1;
+ end = strtostr(name, TDM_NAME_LEN, arg, TDM_DELIM);
+ data->args.vblank_name = strndup(name, TDM_NAME_LEN);
}
}
{
int i;
- if (argc < 3) {
+ if (argc < 2) {
usage(argv[0]);
exit(0);
}
data->args.interval = 1;
for (i = 1; i < argc; i++) {
- if (!strncmp(argv[i]+1, "qo", 2)) {
+ if (!strncmp(argv[i] + 1, "qo", 2)) {
data->do_query = 1;
parse_arg_qo(data, argv[++i]);
- } else if (!strncmp(argv[i]+1, "v", 1)) {
+ } else if (!strncmp(argv[i] + 1, "v", 1)) {
data->do_vblank = 1;
parse_arg_v(data, argv[++i]);
+ } else if (!strncmp(argv[i] + 1, "V", 1)) {
+ data->do_voutput = 1;
} else {
usage(argv[0]);
exit(0);
}
}
-static unsigned long
-get_time_in_micros(void)
+static double
+get_time(void)
{
struct timespec tp;
if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0)
- return (unsigned long)(tp.tv_sec * 1000000) + (tp.tv_nsec / 1000L);
+ return (double)tp.tv_sec + ((double)tp.tv_nsec) / 1000000000.0;
return 0;
}
unsigned int tv_sec, unsigned int tv_usec, void *user_data)
{
tdm_test_client *data = user_data;
- unsigned long cur, vbl;
- static unsigned long p_vbl = 0;
+ double cur, vbl;
+ static double p_vbl = 0;
data->waiting = 0;
exit(0);
}
- cur = get_time_in_micros();
- vbl = (unsigned long)tv_sec * (unsigned long)1000000 + (unsigned long)tv_usec;
+ cur = get_time();
+ vbl = (double)tv_sec + ((double)tv_usec) / 1000000.0;
- printf("vblank : %ld us vbl(%lu)\n", vbl - p_vbl, vbl);
+ printf("vblank : %.6f us vbl(%.6f)\n", vbl - p_vbl, vbl);
- if (cur - vbl > 2000) /* 2ms */
- printf("kernel -> tdm-client: %ld us\n", cur - vbl);
+ if (cur - vbl > 0.002) /* 2ms */
+ printf("kernel -> tdm-client: %.0f us\n", (cur - vbl) * 1000000.0);
p_vbl = vbl;
}
return;
}
- tdm_client_output_get_conn_status(output, &status);
- tdm_client_output_get_dpms(output, &dpms);
- tdm_client_output_get_refresh_rate(output, &refresh);
+ error = tdm_client_output_get_conn_status(output, &status);
+ TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
+ error = tdm_client_output_get_dpms(output, &dpms);
+ TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
+ error = tdm_client_output_get_refresh_rate(output, &refresh);
+ TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
printf("tdm_output \"%s\"\n", data->args.output_name);
printf("\tstatus : %s\n", conn_str[status]);
return;
}
- tdm_client_output_add_change_handler(output, _client_output_handler, NULL);
+ error = tdm_client_output_add_change_handler(output, _client_output_handler, NULL);
+ TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
vblank = tdm_client_output_create_vblank(output, &error);
if (error != TDM_ERROR_NONE) {
return;
}
- tdm_client_vblank_set_enable_fake(vblank, data->args.enable_fake);
- tdm_client_vblank_set_sync(vblank, data->args.sync);
- if (data->args.fps > 0)
- tdm_client_vblank_set_fps(vblank, data->args.fps);
- tdm_client_vblank_set_offset(vblank, data->args.offset);
+ error = tdm_client_vblank_set_name(vblank, data->args.vblank_name);
+ TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
+ error = tdm_client_vblank_set_enable_fake(vblank, data->args.enable_fake);
+ TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
+ error = tdm_client_vblank_set_sync(vblank, data->args.sync);
+ TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
+ if (data->args.fps > 0) {
+ error = tdm_client_vblank_set_fps(vblank, data->args.fps);
+ TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
+ }
+ error = tdm_client_vblank_set_offset(vblank, data->args.offset);
+ TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
error = tdm_client_get_fd(data->client, &fd);
if (error != TDM_ERROR_NONE || fd < 0) {
tdm_client_vblank_destroy(vblank);
}
+#define PNG_DEPTH 8
+
+void
+_tdm_client_get_buffer_full_size(tbm_surface_h buffer, int *buffer_w, int *buffer_h)
+{
+ tbm_surface_info_s info;
+ int ret;
+
+ TDM_RETURN_IF_FAIL(buffer != NULL);
+
+ ret = tbm_surface_get_info(buffer, &info);
+ TDM_RETURN_IF_FAIL(ret == TBM_SURFACE_ERROR_NONE);
+
+ if (buffer_w) {
+ if (IS_RGB(info.format))
+ *buffer_w = info.planes[0].stride >> 2;
+ else
+ *buffer_w = info.planes[0].stride;
+ }
+
+ if (buffer_h)
+ *buffer_h = info.planes[0].size / info.planes[0].stride;
+}
+
+static void
+_tdm_client_dump_png(const char *file, const void *data, int width,
+ int height)
+{
+ FILE *fp;
+
+ fp = fopen(file, "wb");
+ TDM_RETURN_IF_FAIL(fp != NULL);
+
+ png_structp pPngStruct =
+ png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!pPngStruct) {
+ fclose(fp);
+ return;
+ }
+
+ png_infop pPngInfo = png_create_info_struct(pPngStruct);
+ if (!pPngInfo) {
+ png_destroy_write_struct(&pPngStruct, NULL);
+ fclose(fp);
+ return;
+ }
+
+ png_init_io(pPngStruct, fp);
+ png_set_IHDR(pPngStruct,
+ pPngInfo,
+ width,
+ height,
+ PNG_DEPTH,
+ PNG_COLOR_TYPE_RGBA,
+ PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+
+ png_set_bgr(pPngStruct);
+ png_write_info(pPngStruct, pPngInfo);
+
+ const int pixel_size = 4; // RGBA
+ png_bytep *row_pointers =
+ png_malloc(pPngStruct, height * sizeof(png_byte *));
+ if (!row_pointers) {
+ png_destroy_write_struct(&pPngStruct, &pPngInfo);
+ fclose(fp);
+ return;
+ }
+
+ unsigned int *blocks = (unsigned int *)data;
+ int y = 0;
+ int x = 0;
+
+ for (; y < height; ++y) {
+ png_bytep row =
+ png_malloc(pPngStruct, sizeof(png_byte) * width * pixel_size);
+ if (!row) {
+ for (x = 0; x < y; x++)
+ png_free(pPngStruct, row_pointers[x]);
+ png_free(pPngStruct, row_pointers);
+ png_destroy_write_struct(&pPngStruct, &pPngInfo);
+ fclose(fp);
+ return;
+ }
+
+ row_pointers[y] = (png_bytep)row;
+ for (x = 0; x < width; ++x) {
+ unsigned int curBlock = blocks[y * width + x];
+ row[x * pixel_size] = (curBlock & 0xFF);
+ row[1 + x * pixel_size] = (curBlock >> 8) & 0xFF;
+ row[2 + x * pixel_size] = (curBlock >> 16) & 0xFF;
+ row[3 + x * pixel_size] = (curBlock >> 24) & 0xFF;
+ }
+ }
+
+ png_write_image(pPngStruct, row_pointers);
+ png_write_end(pPngStruct, pPngInfo);
+
+ for (y = 0; y < height; y++)
+ png_free(pPngStruct, row_pointers[y]);
+ png_free(pPngStruct, row_pointers);
+
+ png_destroy_write_struct(&pPngStruct, &pPngInfo);
+
+ fclose(fp);
+}
+
+void
+_tdm_client_dump_buffer(tbm_surface_h buffer, const char *file)
+{
+ char temp[TDM_PATH_LEN] = {0,};
+ tbm_surface_info_s info;
+ int len, ret;
+ const char *ext;
+ int bo_cnt;
+ int bw, bh;
+ char *dot, *p = temp;
+ const char *file_exts[2] = {"png", "raw"};
+
+ TDM_RETURN_IF_FAIL(buffer != NULL);
+ TDM_RETURN_IF_FAIL(file != NULL);
+
+ ret = tbm_surface_map(buffer, TBM_OPTION_READ, &info);
+ TDM_RETURN_IF_FAIL(ret == TBM_SURFACE_ERROR_NONE);
+
+ if (IS_RGB(info.format))
+ ext = file_exts[0];
+ else
+ ext = file_exts[1];
+
+ dot = strrchr(file, '.');
+ if (!dot || strlen(dot + 1) != 3 || strncmp(dot + 1, ext, 3)) {
+ len = strnlen(file, TDM_PATH_LEN - 5);
+ strncat(p, file, len);
+ p += len;
+ *(p++) = '.';
+ strncat(p, ext, 4);
+ p += 3;
+ *p = '\0';
+ } else {
+ len = strnlen(file, TDM_PATH_LEN - 1);
+ strncat(p, file, len);
+ p += len;
+ *p = '\0';
+ }
+
+ _tdm_client_get_buffer_full_size(buffer, &bw, &bh);
+
+ bo_cnt = tbm_surface_internal_get_num_bos(buffer);
+ TDM_DBG("buffer: bo_cnt(%d) %dx%d(%dx%d) %c%c%c%c, plane: (%p+%d, %d,%d) (%p+%d, %d,%d) (%p+%d, %d,%d)",
+ bo_cnt, bw, bh, info.width, info.height, FOURCC_STR(info.format),
+ info.planes[0].ptr, info.planes[0].offset, info.planes[0].stride, info.planes[0].size,
+ info.planes[1].ptr, info.planes[1].offset, info.planes[1].stride, info.planes[1].size,
+ info.planes[2].ptr, info.planes[2].offset, info.planes[2].stride, info.planes[2].size);
+
+ _tdm_client_dump_png(temp, info.planes[0].ptr, bw, bh);
+
+ tbm_surface_unmap(buffer);
+
+ printf("dump %s\n", temp);
+}
+
+static void
+_dump_buffer(tbm_surface_h buffer, int count)
+{
+ char temp[TDM_PATH_LEN] = {0,};
+
+ snprintf(temp, TDM_PATH_LEN, "/tmp/%c%c%c%c_%dx%d_%d",
+ FOURCC_STR(tbm_surface_get_format(buffer)),
+ tbm_surface_get_width(buffer),
+ tbm_surface_get_height(buffer),
+ count);
+ _tdm_client_dump_buffer(buffer, temp);
+}
+
+static void
+_voutput_commit(tdm_client_voutput *voutput, tbm_surface_h buffer, void *user_data)
+{
+ tdm_test_client *data = (tdm_test_client *)user_data;
+ static int count = 0;
+
+ TDM_EXIT_IF_FAIL(data != NULL);
+ TDM_EXIT_IF_FAIL(buffer != NULL);
+
+ if ((count < 10) || (count >= 31 && count <= 40))
+ _dump_buffer(buffer, count);
+ count++;
+
+ if (count == 30) {
+ printf("client: %d commited(%p), mode change request to index 1\n", count, buffer);
+ tdm_client_voutput_set_mode(data->voutput, 1);
+ } else if (count == 50) {
+ printf("client: %d commited(%p), disconnect\n", count, buffer);
+ tdm_client_voutput_disconnect(data->voutput);
+ } else {
+ printf("client: %d commited(%p)\n", count, buffer);
+ }
+
+ tdm_client_voutput_commit_done(voutput);
+}
+
+static void
+_voutput_output_handler(tdm_client_output *output, tdm_output_change_type type,
+ tdm_value value, void *user_data)
+{
+ tdm_client_voutput *voutput = NULL;
+ tdm_output_conn_status status;
+ tdm_test_client *data;
+ unsigned int width, height;
+
+ data = (tdm_test_client *) user_data;
+ TDM_RETURN_IF_FAIL(data != NULL);
+ voutput = data->voutput;
+ TDM_RETURN_IF_FAIL(voutput != NULL);
+
+ if (type == TDM_OUTPUT_CHANGE_CONNECTION) {
+ status = (tdm_output_conn_status)value.u32;
+ printf("output %s.\n", conn_str[value.u32]);
+
+ if (status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) {
+ printf("client: disconnected, destroy voutput\n");
+ tdm_client_output_remove_change_handler(output, _voutput_output_handler, data);
+#if CHECK_V_STEP
+ printf("press enter to continuet\n");
+ getchar();
+#endif
+ tdm_client_voutput_destroy(voutput);
+ } else if (status == TDM_OUTPUT_CONN_STATUS_CONNECTED) {
+ printf("client: connected\n");
+ } else if (status == TDM_OUTPUT_CONN_STATUS_MODE_SETTED) {
+ tdm_client_output_get_mode(output, &width, &height);
+ printf("client: mode setted(%dx%d)\n", width, height);
+#if CHECK_V_STEP
+ printf("press enter to continuet\n");
+ getchar();
+#endif
+ }
+ } else if (type == TDM_OUTPUT_CHANGE_DPMS) {
+ printf("output %s.\n", dpms_str[value.u32]);
+ }
+}
+
+static void
+_voutput_make_available_mode(tdm_client_output_mode *modes, int count)
+{
+ int i;
+ for (i = 0 ; i < count; i++) {
+ modes[i].clock = 25200;
+ modes[i].hdisplay = 640 * (count - i);
+ modes[i].hsync_start = 656;
+ modes[i].hsync_end = 752;
+ modes[i].htotal = 800;
+ modes[i].hskew = 0;
+ modes[i].vdisplay = 480 * (count - i);
+ modes[i].vsync_start = 490;
+ modes[i].vsync_end = 492;
+ modes[i].vtotal = 525;
+ modes[i].vscan = 0;
+ modes[i].vrefresh = 30;
+ modes[i].flags = 0;
+ modes[i].type = 0;
+ snprintf(modes[i].name, TDM_NAME_LEN, "%dx%d_%d", modes[i].hdisplay, modes[i].vdisplay, i);
+ }
+}
+
+static void
+do_voutput(tdm_test_client *data)
+{
+ tdm_client_voutput *voutput = NULL;
+ tdm_client_output *output = NULL;
+ tdm_client_output_mode modes[2];
+ tdm_error ret = TDM_ERROR_NONE;
+
+ printf("virtual output test - client\n");
+
+ voutput = tdm_client_create_voutput(data->client, "virtual-test", &ret);
+ TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
+
+ ret = tdm_client_voutput_add_commit_handler(voutput, _voutput_commit, data);
+ TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, done);
+
+ output = tdm_client_voutput_get_client_output(voutput, &ret);
+ TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, done);
+
+ ret = tdm_client_output_add_change_handler(output, _voutput_output_handler, data);
+ TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, done);
+
+ ret = tdm_client_voutput_set_physical_size(voutput, 300, 200);
+ TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, done);
+
+ _voutput_make_available_mode(modes, 2);
+ ret = tdm_client_voutput_set_available_modes(voutput, modes, 2);
+ TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, done);
+#if CHECK_V_STEP
+ printf("virtual output test - press enter to connect\n");
+ getchar();
+#endif
+ ret = tdm_client_voutput_connect(voutput);
+ TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, done);
+
+ data->voutput = voutput;
+ data->output = output;
+
+ while (1) {
+ tdm_client_handle_events_timeout(data->client, 1000);
+ }
+
+done:
+ if (voutput)
+ tdm_client_voutput_destroy(voutput);
+}
+
static tdm_test_client ttc_data;
int
tdm_test_client *data = &ttc_data;
tdm_error error;
-#if 1 /* for testing */
+ /* for testing */
const char *xdg = (const char*)getenv("XDG_RUNTIME_DIR");
if (!xdg) {
char buf[32];
snprintf(buf, sizeof(buf), "/run");
int ret = setenv("XDG_RUNTIME_DIR", (const char*)buf, 1);
if (ret != 0)
- exit (0);
+ exit(0);
+ }
+
+ /* for tbm_bufmgr_init */
+ const char *s = (const char*)getenv("TBM_DISPLAY_SERVER");
+ if (!s) {
+ char buf[32];
+ snprintf(buf, sizeof(buf), "1");
+ int ret = setenv("TBM_DISPLAY_SERVER", (const char*)buf, 1);
+ if (ret != 0)
+ exit(0);
}
-#endif
parse_args(data, argc, argv);
- printf("sync(%d) fps(%d) interval(%d) offset(%d) enable_fake(%d)\n",
+ printf("sync(%d) fps(%d) interval(%d) offset(%d) enable_fake(%d) pid(%d)\n",
data->args.sync, data->args.fps, data->args.interval,
- data->args.offset, data->args.enable_fake);
+ data->args.offset, data->args.enable_fake, data->args.pid);
data->client = tdm_client_create(&error);
if (error != TDM_ERROR_NONE) {
do_query(data);
if (data->do_vblank)
do_vblank(data);
+ if (data->do_voutput)
+ do_voutput(data);
done:
+ if (data->args.output_name)
+ free(data->args.output_name);
+ if (data->args.vblank_name)
+ free(data->args.vblank_name);
if (data->client)
tdm_client_destroy(data->client);