From 2a14e2738cd96eb66e839721d4eb77270751cc98 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Thu, 18 Aug 2016 16:29:30 +0900 Subject: [PATCH 01/16] fix function description Change-Id: Ida3819b5e4873e1cac136831f72f24dbcd9b76d1 --- include/tdm_backend.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/tdm_backend.h b/include/tdm_backend.h index c7328ec..0cfc3cd 100644 --- a/include/tdm_backend.h +++ b/include/tdm_backend.h @@ -596,7 +596,7 @@ typedef struct _tdm_func_layer { * @return #TDM_ERROR_NONE if success. Otherwise, error value. * @see output_commit() function of #tdm_func_output * @remark - * A backend module would apply a TDM buffer when the output object + * A backend module would show a TDM buffer on screen when the output object * of a layer object is committed. */ tdm_error (*layer_set_buffer)(tdm_layer *layer, tbm_surface_h buffer); @@ -606,8 +606,7 @@ typedef struct _tdm_func_layer { * @param[in] layer A layer object * @return #TDM_ERROR_NONE if success. Otherwise, error value. * @remark - * A backend module @b SHOULD hide the current showing buffer from screen. - * If needed, cleanup a layer object resource. + * A backend module @b SHOULD remove the current showing buffer from screen. */ tdm_error (*layer_unset_buffer)(tdm_layer *layer); -- 2.7.4 From 2566dabf632bb933c648bf124b8f5e293773d55d Mon Sep 17 00:00:00 2001 From: Boram Park Date: Thu, 18 Aug 2016 16:30:01 +0900 Subject: [PATCH 02/16] enhance the log information Change-Id: Idefad691b51b7b309d0ceb1cabe77a487085387f --- src/tdm_helper.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/tdm_helper.c b/src/tdm_helper.c index 7c2f36f..0d51670 100644 --- a/src/tdm_helper.c +++ b/src/tdm_helper.c @@ -642,13 +642,14 @@ tdm_helper_get_display_information(tdm_display *dpy, char *reply, int *len) ret = func_output->output_get_mode(private_output->output_backend, ¤t_mode); TDM_DBG_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, unlock); - TDM_SNPRINTF(reply, len, "\t\t name refresh (Hz) hdisp hss hse htot vdisp vss vse vtot\n"); + TDM_SNPRINTF(reply, len, "\t\t name refresh (Hz) clk hdisp hss hse htot vdisp vss vse vtot vscan\n"); for (i = 0; i < private_output->caps.mode_count; i++) { char *current = (current_mode == private_output->caps.modes + i) ? "*" : " "; - TDM_SNPRINTF(reply, len, "\t\t%s%s %d %d %d %d %d %d %d %d %d ", + TDM_SNPRINTF(reply, len, "\t\t%s%s %d %d %d %d %d %d %d %d %d %d %d ", current, private_output->caps.modes[i].name, private_output->caps.modes[i].vrefresh, + private_output->caps.modes[i].clock, private_output->caps.modes[i].hdisplay, private_output->caps.modes[i].hsync_start, private_output->caps.modes[i].hsync_end, @@ -656,7 +657,8 @@ tdm_helper_get_display_information(tdm_display *dpy, char *reply, int *len) private_output->caps.modes[i].vdisplay, private_output->caps.modes[i].vsync_start, private_output->caps.modes[i].vsync_end, - private_output->caps.modes[i].vtotal); + private_output->caps.modes[i].vtotal, + private_output->caps.modes[i].vscan); tdm_mode_flag_str(private_output->caps.modes[i].flags, &reply, len); TDM_SNPRINTF(reply, len, " "); tdm_mode_type_str(private_output->caps.modes[i].type, &reply, len); -- 2.7.4 From 27146b62eac2097bb21a3ebc109513e19290faab Mon Sep 17 00:00:00 2001 From: Boram Park Date: Fri, 19 Aug 2016 10:43:40 +0900 Subject: [PATCH 03/16] enhance log during loading backend module Change-Id: I362f196ac7d5e3acf040f6a38448d4c742cb9dc2 --- src/tdm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tdm.c b/src/tdm.c index 8c7843a..371553d 100644 --- a/src/tdm.c +++ b/src/tdm.c @@ -756,7 +756,7 @@ _tdm_display_load_module_with_file(tdm_private_display *private_display, private_display->bdata = module_data->init((tdm_display *)private_display, &ret); TDM_TRACE_END(); if (ret != TDM_ERROR_NONE) { - TDM_ERR("'%s' init failed", file); + TDM_ERR("failed to init '%s' module", module_data->name); goto failed_load; } @@ -767,7 +767,7 @@ _tdm_display_load_module_with_file(tdm_private_display *private_display, goto failed_load; } - TDM_INFO("Success to load module(%s)", file); + TDM_INFO("Success to load '%s' module", module_data->name); return TDM_ERROR_NONE; failed_load: -- 2.7.4 From ed8ff15a0f917f57387d444a6cd4845e05bd7803 Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Fri, 19 Aug 2016 13:44:23 +0900 Subject: [PATCH 04/16] systemd: support for lazy mount of user data Change-Id: Ib328791d52f6c8a107984c88611842a39c2ebc19 --- packaging/libtdm.spec | 11 +++++++++++ service/tdm-socket-user.service | 1 + 2 files changed, 12 insertions(+) diff --git a/packaging/libtdm.spec b/packaging/libtdm.spec index 789371f..2817a78 100644 --- a/packaging/libtdm.spec +++ b/packaging/libtdm.spec @@ -80,13 +80,24 @@ install -m 644 service/tdm-socket-user.path %{buildroot}%{_unitdir_user} %pre %__mkdir_p %{_unitdir}/graphical.target.wants ln -sf ../tdm-socket.path %{_unitdir}/graphical.target.wants/ + +%if "%{?profile}" == "mobile" || "%{?profile}" == "wearable" +%__mkdir_p %{_unitdir_user}/basic.target.wants +ln -sf ../tdm-socket-user.path %{_unitdir_user}/basic.target.wants/ +%else %__mkdir_p %{_unitdir_user}/default.target.wants ln -sf ../tdm-socket-user.path %{_unitdir_user}/default.target.wants/ +%endif %post -p /sbin/ldconfig %postun -p /sbin/ldconfig rm -f %{_unitdir}/graphical.target.wants/tdm-socket.path + +%if "%{?profile}" == "mobile" || "%{?profile}" == "wearable" +rm -f %{_unitdir_user}/basic.target.wants/tdm-socket-user.path +%else rm -f %{_unitdir_user}/default.target.wants/tdm-socket-user.path +%endif %files %manifest %{name}.manifest diff --git a/service/tdm-socket-user.service b/service/tdm-socket-user.service index 97d0f6f..367a531 100644 --- a/service/tdm-socket-user.service +++ b/service/tdm-socket-user.service @@ -1,5 +1,6 @@ [Unit] Description=Creating a link file for user to access tdm-socket +DefaultDependencies=no [Service] Type=oneshot -- 2.7.4 From 969d9f15369b577ba5b8cd29c1e818e6e7e0c2ca Mon Sep 17 00:00:00 2001 From: Junkyeong Kim Date: Mon, 22 Aug 2016 21:32:48 +0900 Subject: [PATCH 05/16] avoid dereference null, fix memory leak Change-Id: I8981af9e4b1dd888382cd52d9cc5e17c536329d2 Signed-off-by: Junkyeong Kim --- src/tdm.c | 6 ++++++ src/tdm_monitor_server.c | 8 ++++++++ tools/tdm_test_server.c | 28 ++++++++++++++++++++++------ 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/tdm.c b/src/tdm.c index 371553d..3f6388c 100644 --- a/src/tdm.c +++ b/src/tdm.c @@ -966,6 +966,11 @@ tdm_display_deinit(tdm_display *dpy) free(private_display); g_private_display = NULL; + if (tdm_debug_dump_dir) { + free(tdm_debug_dump_dir); + tdm_debug_dump_dir = NULL; + } + _pthread_mutex_unlock(&gLock); TDM_INFO("done"); @@ -1047,6 +1052,7 @@ tdm_display_enable_dump(tdm_private_display *private_display, const char *dump_s snprintf(temp, sizeof(temp), "%s", dump_str); arg = strtok_r(temp, ",", &end); + TDM_GOTO_IF_FAIL(arg != NULL, done); if (!strncmp(arg, "none", 4)) { tdm_debug_dump = 0; diff --git a/src/tdm_monitor_server.c b/src/tdm_monitor_server.c index c5bda95..25874ab 100644 --- a/src/tdm_monitor_server.c +++ b/src/tdm_monitor_server.c @@ -195,6 +195,10 @@ _tdm_monitor_server_prop(unsigned int pid, char *cwd, int argc, char *argv[], ch arg = end + 1; prop_name = strtok_r(arg, ",", &end); + if (!prop_name) { + TDM_SNPRINTF(reply, len, "failed: get prop_name by strtok_r\n"); + return; + } if (*end == '\0') { TDM_SNPRINTF(reply, len, "failed: no value\n"); @@ -202,6 +206,10 @@ _tdm_monitor_server_prop(unsigned int pid, char *cwd, int argc, char *argv[], ch } arg = strtok_r(NULL, TDM_DELIM, &end); + if (!arg) { + TDM_SNPRINTF(reply, len, "failed: get arg by strtok_r\n"); + return; + } value.u32 = strtol(arg, &end, 10); output = tdm_display_get_output(dpy, output_idx, &ret); diff --git a/tools/tdm_test_server.c b/tools/tdm_test_server.c index 0045be5..d59c6ce 100644 --- a/tools/tdm_test_server.c +++ b/tools/tdm_test_server.c @@ -989,6 +989,8 @@ static void layer_cb_buffer_release(tbm_surface_h b, void *user_data) { tdm_test_server_buffer *tb = get_tts_buffer(b); + TDM_EXIT_IF_FAIL(tb != NULL); + tb->in_use = 0; tdm_buffer_remove_release_handler(b, layer_cb_buffer_release, NULL); if (tb->done) @@ -1083,6 +1085,10 @@ pp_get_buffer(tdm_test_server_pp *p) } for (i = 0; i < size; i++) { tdm_test_server_buffer *tb = get_tts_buffer(p->bufs[i]); + + if (tb == NULL) + continue; + if (!tb->in_use) { tdm_test_buffer_fill(p->bufs[i], p->data->b_fill); return p->bufs[i]; @@ -1169,10 +1175,16 @@ pp_cb_timeout(void *user_data) static void pp_setup(tdm_test_server_pp *p, tbm_surface_h sb, tbm_surface_h db) { - tdm_test_server *data = p->l->o->data; + tdm_test_server *data; tbm_surface_info_s info; tdm_error ret; + if (!p || !p->l || !p->l->o || !p->l->o->data) { + TDM_ERR("invalid parameter failed"); + exit(0); + } + data = p->l->o->data; + p->pp = tdm_display_create_pp(data->display, &ret); TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); @@ -1209,8 +1221,7 @@ pp_setup(tdm_test_server_pp *p, tbm_surface_h sb, tbm_surface_h db) printf(" ! "); print_config(&p->info.dst_config); printf(" fps(%d) transform(%s)\n", p->fps, tdm_transform_str(p->info.transform)); - if (p->l) - printf("\toutput_idx(%d) layer_idx(%d)\n", p->l->o->idx, p->l->idx); + printf("\toutput_idx(%d) layer_idx(%d)\n", p->l->o->idx, p->l->idx); layer_setup(p->l, db); @@ -1282,12 +1293,18 @@ capture_attach(tdm_test_server_capture *c, tbm_surface_h b) static void capture_setup(tdm_test_server_capture *c, tbm_surface_h b) { - tdm_test_server *data = c->l->o->data; + tdm_test_server *data; tdm_output *output; tdm_layer *layer; tbm_surface_info_s info; tdm_error ret; + if (!c || !c->l || !c->l->o || !c->l->o->data) { + TDM_ERR("invalid parameter failed"); + exit(0); + } + data = c->l->o->data; + if (c->output_idx != -1 && c->layer_idx == -1) { output = tdm_display_get_output(data->display, c->output_idx, &ret); TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); @@ -1324,8 +1341,7 @@ capture_setup(tdm_test_server_capture *c, tbm_surface_h b) printf("capture: o(%d) l(%d) ", c->output_idx, c->layer_idx); print_config(&c->info.dst_config); printf(" transform(%s)\n", tdm_transform_str(c->info.transform)); - if (c->l) - printf("\toutput_idx(%d) layer_idx(%d)\n", c->l->o->idx, c->l->idx); + printf("\toutput_idx(%d) layer_idx(%d)\n", c->l->o->idx, c->l->idx); layer_setup(c->l, b); } -- 2.7.4 From bd87786f990480e392080faf42405a055dadd2d1 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Tue, 23 Aug 2016 11:32:06 +0900 Subject: [PATCH 06/16] fix memory leak Change-Id: I28934b0ddcbf1df4e6d079db9ca2dbc7ba79216c --- src/tdm_helper.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/tdm_helper.c b/src/tdm_helper.c index 0d51670..e9da37f 100644 --- a/src/tdm_helper.c +++ b/src/tdm_helper.c @@ -214,17 +214,20 @@ tdm_helper_dump_buffer_str(tbm_surface_h buffer, char *dir, char *str) const char *ext; char file[TDM_PATH_LEN]; int ret, bw; + char *dump_dir = NULL; TDM_RETURN_IF_FAIL(buffer != NULL); TDM_RETURN_IF_FAIL(str != NULL); - if (!dir) { - dir = tdm_helper_dump_make_directory(TDM_DUMP_DIR, NULL, NULL); - TDM_RETURN_IF_FAIL(dir != NULL); - } + dump_dir = tdm_helper_dump_make_directory(TDM_DUMP_DIR, NULL, NULL); + + if (!dir) + dir = dump_dir; + + TDM_GOTO_IF_FAIL(dir != NULL, done); ret = tbm_surface_get_info(buffer, &info); - TDM_RETURN_IF_FAIL(ret == TBM_SURFACE_ERROR_NONE); + TDM_GOTO_IF_FAIL(ret == TBM_SURFACE_ERROR_NONE, done); if (info.format == TBM_FORMAT_ARGB8888 || info.format == TBM_FORMAT_XRGB8888) { ext = file_exts[0]; @@ -238,6 +241,9 @@ tdm_helper_dump_buffer_str(tbm_surface_h buffer, char *dir, char *str) dir, FOURCC_STR(info.format), bw, info.height, str, ext); tdm_helper_dump_buffer(buffer, file); + +done: + free(dump_dir); } EXTERN void -- 2.7.4 From 9863c6acbed5b8f9c07221a7e82a54f03012300d Mon Sep 17 00:00:00 2001 From: Boram Park Date: Tue, 23 Aug 2016 13:49:34 +0900 Subject: [PATCH 07/16] make sure the directory path is not null for dump Change-Id: I87849dc858ad5011e4fc1d10b7f811a38cc513c0 --- src/tdm_helper.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/tdm_helper.c b/src/tdm_helper.c index e9da37f..69278fc 100644 --- a/src/tdm_helper.c +++ b/src/tdm_helper.c @@ -214,20 +214,13 @@ tdm_helper_dump_buffer_str(tbm_surface_h buffer, char *dir, char *str) const char *ext; char file[TDM_PATH_LEN]; int ret, bw; - char *dump_dir = NULL; TDM_RETURN_IF_FAIL(buffer != NULL); + TDM_RETURN_IF_FAIL(dir != NULL); TDM_RETURN_IF_FAIL(str != NULL); - dump_dir = tdm_helper_dump_make_directory(TDM_DUMP_DIR, NULL, NULL); - - if (!dir) - dir = dump_dir; - - TDM_GOTO_IF_FAIL(dir != NULL, done); - ret = tbm_surface_get_info(buffer, &info); - TDM_GOTO_IF_FAIL(ret == TBM_SURFACE_ERROR_NONE, done); + TDM_RETURN_IF_FAIL(ret == TBM_SURFACE_ERROR_NONE); if (info.format == TBM_FORMAT_ARGB8888 || info.format == TBM_FORMAT_XRGB8888) { ext = file_exts[0]; @@ -241,9 +234,6 @@ tdm_helper_dump_buffer_str(tbm_surface_h buffer, char *dir, char *str) dir, FOURCC_STR(info.format), bw, info.height, str, ext); tdm_helper_dump_buffer(buffer, file); - -done: - free(dump_dir); } EXTERN void -- 2.7.4 From 689f54dea968a207ec421ea57020205446de2529 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Tue, 23 Aug 2016 18:51:41 +0900 Subject: [PATCH 08/16] fix reallocation-free issue of output modes Change-Id: I76fb49f0d00906ff380531e5ab021da9ecc122a1 --- include/tdm_backend.h | 1 + src/tdm.c | 36 ++++++++++++++++++++++++++++++------ src/tdm_display.c | 14 +++----------- src/tdm_private.h | 1 + 4 files changed, 35 insertions(+), 17 deletions(-) diff --git a/include/tdm_backend.h b/include/tdm_backend.h index 0cfc3cd..18d0f72 100644 --- a/include/tdm_backend.h +++ b/include/tdm_backend.h @@ -477,6 +477,7 @@ typedef struct _tdm_func_output { /** * @brief Get the mode of a output object + * @deprecated * @param[in] output A output object * @param[out] mode A output mode * @return #TDM_ERROR_NONE if success. Otherwise, error value. diff --git a/src/tdm.c b/src/tdm.c index 3f6388c..a4dcdf8 100644 --- a/src/tdm.c +++ b/src/tdm.c @@ -389,13 +389,35 @@ tdm_display_update_output(tdm_private_display *private_display, private_output->regist_change_cb = 1; } - } else - _tdm_display_destroy_caps_output(&private_output->caps); + ret = _tdm_display_update_caps_output(private_display, pipe, output_backend, + &private_output->caps); + if (ret != TDM_ERROR_NONE) + return ret; + } else { + tdm_caps_output new_caps; - ret = _tdm_display_update_caps_output(private_display, pipe, output_backend, - &private_output->caps); - if (ret != TDM_ERROR_NONE) - return ret; + ret = _tdm_display_update_caps_output(private_display, pipe, output_backend, + &new_caps); + if (ret != TDM_ERROR_NONE) + return ret; + + /* FIXME: This is very ugly. need to fix after the TDM ABI is changed. */ + if (private_output->caps.status != new_caps.status) { + _tdm_display_destroy_caps_output(&private_output->caps); + private_output->caps = new_caps; + private_output->current_mode = NULL; + } else { + tdm_output_mode *old_modes = private_output->caps.modes; + unsigned int old_mode_count = private_output->caps.mode_count; + if (new_caps.modes) + free(new_caps.modes); + new_caps.modes = old_modes; + new_caps.mode_count = old_mode_count; + if (private_output->caps.props) + free(private_output->caps.props); + private_output->caps = new_caps; + } + } layers = func_output->output_get_layers(output_backend, &layer_count, &ret); if (ret != TDM_ERROR_NONE) @@ -503,6 +525,8 @@ _tdm_display_get_ordered_outputs(tdm_private_display *private_display, int *coun break; } } + + _tdm_display_destroy_caps_output(&caps); } /* ordering : main output is first */ diff --git a/src/tdm_display.c b/src/tdm_display.c index 38cec19..7d9b08f 100644 --- a/src/tdm_display.c +++ b/src/tdm_display.c @@ -1084,7 +1084,8 @@ tdm_output_set_mode(tdm_output *output, const tdm_output_mode *mode) } ret = func_output->output_set_mode(private_output->output_backend, mode); - + if (ret == TDM_ERROR_NONE) + private_output->current_mode = mode; _pthread_mutex_unlock(&private_display->lock); return ret; @@ -1093,22 +1094,13 @@ tdm_output_set_mode(tdm_output *output, const tdm_output_mode *mode) EXTERN tdm_error tdm_output_get_mode(tdm_output *output, const tdm_output_mode **mode) { - tdm_func_output *func_output; OUTPUT_FUNC_ENTRY(); TDM_RETURN_VAL_IF_FAIL(mode != NULL, TDM_ERROR_INVALID_PARAMETER); _pthread_mutex_lock(&private_display->lock); - func_output = &private_display->func_output; - - if (!func_output->output_get_mode) { - _pthread_mutex_unlock(&private_display->lock); - TDM_ERR("not implemented!!"); - return TDM_ERROR_NOT_IMPLEMENTED; - } - - ret = func_output->output_get_mode(private_output->output_backend, mode); + *mode = private_output->current_mode; _pthread_mutex_unlock(&private_display->lock); diff --git a/src/tdm_private.h b/src/tdm_private.h index e56a19a..bc04971 100644 --- a/src/tdm_private.h +++ b/src/tdm_private.h @@ -172,6 +172,7 @@ struct _tdm_private_output { unsigned int pipe; tdm_output_dpms current_dpms_value; + const tdm_output_mode *current_mode; int regist_vblank_cb; int regist_commit_cb; -- 2.7.4 From adff2d3031547ac6d596346e521bc1502bdee598 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Mon, 29 Aug 2016 14:37:02 +0900 Subject: [PATCH 09/16] add -ttrace_vblank option to tdm-monitor Change-Id: I9939c5bd2cca6f2fed1a9cc1f9d518cc5bf068c0 --- src/tdm.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++ src/tdm_monitor_server.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++ src/tdm_private.h | 4 ++++ 3 files changed, 103 insertions(+) diff --git a/src/tdm.c b/src/tdm.c index a4dcdf8..550b043 100644 --- a/src/tdm.c +++ b/src/tdm.c @@ -1172,3 +1172,53 @@ tdm_display_enable_path(const char *path) return TDM_ERROR_NONE; } + + +static void +_tdm_display_ttrace_vblank_cb(tdm_vblank *vblank, tdm_error error, unsigned int sequence, + unsigned int tv_sec, unsigned int tv_usec, void *user_data) +{ + tdm_error ret = TDM_ERROR_NONE; + + TDM_TRACE_COUNT(VBlank, sequence); + + ret = tdm_vblank_wait(vblank, 0, 0, 1, _tdm_display_ttrace_vblank_cb, NULL); + TDM_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); +} + +INTERN tdm_error +tdm_display_enable_ttrace_vblank(tdm_display *dpy, tdm_output *output, int enable) +{ + static tdm_vblank *vblank = NULL; + tdm_error ret = TDM_ERROR_NONE; + + if (!enable) { + if (vblank) + tdm_vblank_destroy(vblank); + vblank = NULL; + return TDM_ERROR_NONE; + } else { + const tdm_output_mode *mode = NULL; + + if (vblank) + return TDM_ERROR_NONE; + + vblank = tdm_vblank_create(dpy, output, &ret); + TDM_RETURN_VAL_IF_FAIL(vblank != NULL, ret); + + ret = tdm_output_get_mode(output, &mode); + TDM_RETURN_VAL_IF_FAIL(mode != NULL, ret); + + ret = tdm_vblank_set_fps(vblank, mode->vrefresh); + TDM_RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, ret); + + ret = tdm_vblank_set_enable_fake(vblank, 1); + TDM_RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, ret); + + ret = tdm_vblank_wait(vblank, 0, 0, 1, _tdm_display_ttrace_vblank_cb, NULL); + TDM_RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, ret); + } + + return TDM_ERROR_NONE; +} + diff --git a/src/tdm_monitor_server.c b/src/tdm_monitor_server.c index 25874ab..a3e24b4 100644 --- a/src/tdm_monitor_server.c +++ b/src/tdm_monitor_server.c @@ -87,6 +87,50 @@ _tdm_monitor_server_dpms(unsigned int pid, char *cwd, int argc, char *argv[], ch } static void +_tdm_monitor_server_ttrace_vblank(unsigned int pid, char *cwd, int argc, char *argv[], char *reply, int *len, tdm_display *dpy) +{ + int enable, output_id = 0; + tdm_output *output; + const char *name = NULL; + char *arg; + char *end; + tdm_error ret; + tdm_output_type type; + + if (argc < 3) { + _tdm_monitor_server_usage(argv[0], reply, len); + return; + } + + arg = argv[2]; + enable = strtol(arg, &end, 10); + + if (*end == '@') { + arg = end + 1; + output_id = strtol(arg, &end, 10); + } + + output = tdm_display_get_output(dpy, output_id, NULL); + if (!output) { + TDM_SNPRINTF(reply, len, "can't find the output_id(%d)\n", output_id); + return; + } + + ret = tdm_output_get_output_type(output, &type); + if (ret != TDM_ERROR_NONE) { + TDM_SNPRINTF(reply, len, "can't find the type of output_id(%d)\n", output_id); + return; + } + + tdm_output_get_model_info(output, NULL, NULL, &name); + + tdm_display_enable_ttrace_vblank(dpy, output, enable); + + TDM_SNPRINTF(reply, len, "%s ttrace vblank for '%s'\n", + (enable)?"enable":"disable", tdm_conn_str(type)); +} + +static void _tdm_monitor_server_debug(unsigned int pid, char *cwd, int argc, char *argv[], char *reply, int *len, tdm_display *dpy) { int level; @@ -279,6 +323,11 @@ static struct { "set output dpms", ":", "0:3 or 0:0" }, { + "ttrace_vblank", _tdm_monitor_server_ttrace_vblank, + "enable/disable the vblank for ttrace [0:disable 1:enable]", + "[@]", "0 or 1" + }, + { "debug", _tdm_monitor_server_debug, "set the debug level and modules(module: none, mutex, buffer, thread, vblank)", "[@[,]]", diff --git a/src/tdm_private.h b/src/tdm_private.h index bc04971..5cfddc7 100644 --- a/src/tdm_private.h +++ b/src/tdm_private.h @@ -89,9 +89,11 @@ extern int tdm_debug_dump; #include #define TDM_TRACE_BEGIN(NAME) traceBegin(TTRACE_TAG_GRAPHICS, "TDM:"#NAME) #define TDM_TRACE_END() traceEnd(TTRACE_TAG_GRAPHICS) +#define TDM_TRACE_COUNT(NAME, COUNT) traceCounter(TTRACE_TAG_GRAPHICS, COUNT, "TDM:"#NAME) #else #define TDM_TRACE_BEGIN(NAME) #define TDM_TRACE_END() +#define TDM_TRACE_COUNT(NAME, COUNT) #endif typedef enum { @@ -575,6 +577,8 @@ tdm_error tdm_display_enable_dump(tdm_private_display *private_display, const char *dump_str, char *reply, int *len); tdm_error tdm_display_enable_path(const char *path); +tdm_error +tdm_display_enable_ttrace_vblank(tdm_display *dpy, tdm_output *output, int enable); /** * @brief The tdm vblank object -- 2.7.4 From 58c6aaa1b7df5ae6c0c74305a7bec634e4f53e6a Mon Sep 17 00:00:00 2001 From: Boram Park Date: Mon, 29 Aug 2016 17:17:09 +0900 Subject: [PATCH 10/16] destroy a vblank object when failed Change-Id: I699b81b18efc3d9323dcea3d605f7247f3abb742 --- src/tdm.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/tdm.c b/src/tdm.c index 550b043..e4e8eb5 100644 --- a/src/tdm.c +++ b/src/tdm.c @@ -1207,18 +1207,24 @@ tdm_display_enable_ttrace_vblank(tdm_display *dpy, tdm_output *output, int enabl TDM_RETURN_VAL_IF_FAIL(vblank != NULL, ret); ret = tdm_output_get_mode(output, &mode); - TDM_RETURN_VAL_IF_FAIL(mode != NULL, ret); + TDM_GOTO_IF_FAIL(mode != NULL, enable_fail); ret = tdm_vblank_set_fps(vblank, mode->vrefresh); - TDM_RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, ret); + TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, enable_fail); ret = tdm_vblank_set_enable_fake(vblank, 1); - TDM_RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, ret); + TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, enable_fail); ret = tdm_vblank_wait(vblank, 0, 0, 1, _tdm_display_ttrace_vblank_cb, NULL); - TDM_RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, ret); + TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, enable_fail); } return TDM_ERROR_NONE; + +enable_fail: + if (vblank) + tdm_vblank_destroy(vblank); + vblank = NULL; + return ret; } -- 2.7.4 From 6a4b60c921ac8d37c5084ba1d9fa00411ac2ecad Mon Sep 17 00:00:00 2001 From: Boram Park Date: Mon, 29 Aug 2016 17:29:18 +0900 Subject: [PATCH 11/16] remove no effective code Change-Id: Id043e59eef996ad74e7d63f5f704b454d549b8be --- src/tdm_monitor_server.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/tdm_monitor_server.c b/src/tdm_monitor_server.c index a3e24b4..5b4ff18 100644 --- a/src/tdm_monitor_server.c +++ b/src/tdm_monitor_server.c @@ -91,7 +91,6 @@ _tdm_monitor_server_ttrace_vblank(unsigned int pid, char *cwd, int argc, char *a { int enable, output_id = 0; tdm_output *output; - const char *name = NULL; char *arg; char *end; tdm_error ret; @@ -122,8 +121,6 @@ _tdm_monitor_server_ttrace_vblank(unsigned int pid, char *cwd, int argc, char *a return; } - tdm_output_get_model_info(output, NULL, NULL, &name); - tdm_display_enable_ttrace_vblank(dpy, output, enable); TDM_SNPRINTF(reply, len, "%s ttrace vblank for '%s'\n", -- 2.7.4 From 17b8b22a6a8317d913eb396dab0452844cadfc76 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Mon, 29 Aug 2016 17:42:45 +0900 Subject: [PATCH 12/16] fix syntax error Change-Id: If6463e9661c94d3c6332855e4af8a5eba0b96d78 --- src/tdm_monitor_server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tdm_monitor_server.c b/src/tdm_monitor_server.c index 5b4ff18..4ea1184 100644 --- a/src/tdm_monitor_server.c +++ b/src/tdm_monitor_server.c @@ -348,7 +348,7 @@ static struct { "\t\t layer, pp, capture - start to dump buffers of layer, pp, capture\n" "\t\t none - stop to dump buffers\n" "\t\t current - dump the current buffer of all layers", - "[,[,...]]@[]", + "[,[,...]][@]", NULL }, }; -- 2.7.4 From 5ca2793d1c329ad7f1a21ced0c688a641467029e Mon Sep 17 00:00:00 2001 From: Boram Park Date: Wed, 31 Aug 2016 14:00:29 +0900 Subject: [PATCH 13/16] add ttrace for vblank debugging Change-Id: I030304241a4983e176e5052899006efabcca34c3 --- client/tdm_client.c | 6 ++++++ configure.ac | 6 ++++-- src/tdm_server.c | 4 ++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/client/tdm_client.c b/client/tdm_client.c index b7a80c1..8d1ffe6 100644 --- a/client/tdm_client.c +++ b/client/tdm_client.c @@ -47,6 +47,8 @@ #include "tdm_log.h" #include "tdm_macro.h" #include "tdm_list.h" +#include "tdm.h" +#include "tdm_private.h" #include "tdm-client-protocol.h" typedef struct _tdm_private_client_vblank tdm_private_client_vblank; @@ -127,6 +129,8 @@ _tdm_client_vblank_cb_done(void *data, struct wl_tdm_vblank *wl_tdm_vblank, TDM_RETURN_IF_FAIL(private_vblank != NULL); + TDM_TRACE_COUNT(ClientDoneVBlank, req_id); + LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->wait_list, link) { if (w->req_id != req_id) continue; @@ -762,6 +766,8 @@ tdm_client_vblank_wait(tdm_client_vblank *vblank, unsigned int interval, tdm_cli wl_tdm_vblank_wait_vblank(private_vblank->vblank, interval, w->req_id, w->req_sec, w->req_usec); + TDM_TRACE_COUNT(ClientWaitVBlank, w->req_id); + if (!private_vblank->sync) { wl_display_flush(private_client->display); return TDM_ERROR_NONE; diff --git a/configure.ac b/configure.ac index ada64f8..9679295 100644 --- a/configure.ac +++ b/configure.ac @@ -38,6 +38,7 @@ fi PKG_CHECK_MODULES(WAYLAND_SCANNER, wayland-scanner >= 1.7.0) PKG_CHECK_MODULES(TDM, dlog libtbm libpng pixman-1 wayland-server) +PKG_CHECK_MODULES(TDM_CLIENT, dlog wayland-client) PKG_CHECK_MODULES(TTRACE, [ttrace], @@ -45,13 +46,14 @@ PKG_CHECK_MODULES(TTRACE, if test "x$have_ttrace" = "xyes"; then TDM_CFLAGS="$TDM_CFLAGS $TTRACE_CFLAGS" TDM_LIBS="$TDM_LIBS $TTRACE_LIBS" - AC_DEFINE(HAVE_TTRACE, 1, [ttrace available]) + TDM_CLIENT_CFLAGS="$TDM_CLIENT_CFLAGS $TTRACE_CFLAGS" + TDM_CLIENT_LIBS="$TDM_CLIENT_LIBS $TTRACE_LIBS" + AC_DEFINE(HAVE_TTRACE, 1, [ttrace available]) fi AC_SUBST(TDM_CFLAGS) AC_SUBST(TDM_LIBS) -PKG_CHECK_MODULES(TDM_CLIENT, dlog wayland-client) AC_SUBST(TDM_CLIENT_CFLAGS) AC_SUBST(TDM_CLIENT_LIBS) diff --git a/src/tdm_server.c b/src/tdm_server.c index d92b7a1..90f46ad 100644 --- a/src/tdm_server.c +++ b/src/tdm_server.c @@ -143,6 +143,8 @@ _tdm_server_send_done(tdm_server_wait_info *wait_info, tdm_error error, if (tdm_debug_module & TDM_DEBUG_VBLANK) TDM_INFO("req_id(%d) done", wait_info->req_id); + TDM_TRACE_COUNT(ServerDoneVBlank, wait_info->req_id); + vblank_info = wait_info->vblank_info; wl_tdm_vblank_send_done(vblank_info->resource, wait_info->req_id, sequence, tv_sec, tv_usec, error); @@ -243,6 +245,8 @@ _tdm_server_vblank_cb_wait_vblank(struct wl_client *client, struct wl_resource * tdm_server_wait_info *wait_info; tdm_error ret; + TDM_TRACE_COUNT(ServerWaitVBlank, req_id); + wait_info = calloc(1, sizeof *wait_info); if (!wait_info) { TDM_ERR("alloc failed"); -- 2.7.4 From db2a05ccfb5b0d491289b03a7346a45dd1bf7268 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Wed, 31 Aug 2016 19:22:16 +0900 Subject: [PATCH 14/16] use traceMark for ttrace_vblank Change-Id: Id0f9767dc9315e13778287c0f47fc9a54749e933 --- src/tdm.c | 2 +- src/tdm_private.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tdm.c b/src/tdm.c index e4e8eb5..273fc27 100644 --- a/src/tdm.c +++ b/src/tdm.c @@ -1180,7 +1180,7 @@ _tdm_display_ttrace_vblank_cb(tdm_vblank *vblank, tdm_error error, unsigned int { tdm_error ret = TDM_ERROR_NONE; - TDM_TRACE_COUNT(VBlank, sequence); + TDM_TRACE_MARK(VBlank); ret = tdm_vblank_wait(vblank, 0, 0, 1, _tdm_display_ttrace_vblank_cb, NULL); TDM_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); diff --git a/src/tdm_private.h b/src/tdm_private.h index 5cfddc7..8678e25 100644 --- a/src/tdm_private.h +++ b/src/tdm_private.h @@ -90,10 +90,12 @@ extern int tdm_debug_dump; #define TDM_TRACE_BEGIN(NAME) traceBegin(TTRACE_TAG_GRAPHICS, "TDM:"#NAME) #define TDM_TRACE_END() traceEnd(TTRACE_TAG_GRAPHICS) #define TDM_TRACE_COUNT(NAME, COUNT) traceCounter(TTRACE_TAG_GRAPHICS, COUNT, "TDM:"#NAME) +#define TDM_TRACE_MARK(NAME) traceMark(TTRACE_TAG_GRAPHICS, "TDM:"#NAME) #else #define TDM_TRACE_BEGIN(NAME) #define TDM_TRACE_END() #define TDM_TRACE_COUNT(NAME, COUNT) +#define TDM_TRACE_MARK(NAME) #endif typedef enum { -- 2.7.4 From 78194c62535a9485933d59c7da969ddc17a2583c Mon Sep 17 00:00:00 2001 From: Boram Park Date: Tue, 30 Aug 2016 16:02:54 +0900 Subject: [PATCH 15/16] refactoring tdm vblank Change-Id: Id688bf2108f54cff797c96f67a8f5ed31ab23ba2 --- src/tdm_private.h | 2 + src/tdm_server.c | 5 + src/tdm_vblank.c | 577 +++++++++++++++++++++--------------------------------- 3 files changed, 229 insertions(+), 355 deletions(-) diff --git a/src/tdm_private.h b/src/tdm_private.h index 8678e25..44af51f 100644 --- a/src/tdm_private.h +++ b/src/tdm_private.h @@ -600,6 +600,8 @@ tdm_error tdm_vblank_set_offset(tdm_vblank *vblank, int offset); tdm_error tdm_vblank_set_enable_fake(tdm_vblank *vblank, unsigned int enable_fake); +unsigned int +tdm_vblank_get_enable_fake(tdm_vblank *vblank); tdm_error tdm_vblank_wait(tdm_vblank *vblank, unsigned int req_sec, unsigned int req_usec, unsigned int interval, tdm_vblank_handler func, void *user_data); diff --git a/src/tdm_server.c b/src/tdm_server.c index 90f46ad..9e88787 100644 --- a/src/tdm_server.c +++ b/src/tdm_server.c @@ -262,11 +262,16 @@ _tdm_server_vblank_cb_wait_vblank(struct wl_client *client, struct wl_resource * TDM_INFO("req_id(%d) wait", req_id); ret = tdm_vblank_wait(vblank_info->vblank, req_sec, req_usec, interval, _tdm_server_cb_vblank, wait_info); + + if (!tdm_vblank_get_enable_fake(vblank_info->vblank) && ret == TDM_ERROR_DPMS_OFF) + goto wait_failed; + TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, wait_failed); return; wait_failed: wl_tdm_vblank_send_done(vblank_info->resource, req_id, 0, 0, 0, ret); + destroy_wait(wait_info); } static const struct wl_tdm_vblank_interface tdm_vblank_implementation = { diff --git a/src/tdm_vblank.c b/src/tdm_vblank.c index 53554fa..e63c14c 100644 --- a/src/tdm_vblank.c +++ b/src/tdm_vblank.c @@ -57,6 +57,13 @@ #define VIN(fmt, arg...) TDM_INFO("[%p] "fmt, private_vblank, ##arg) #define VDB(fmt, arg...) TDM_DBG("[%p] "fmt, private_vblank, ##arg) +typedef enum { + VBLANK_TYPE_SW, + VBLANK_TYPE_SW_FAKE, + VBLANK_TYPE_HW, + VBLANK_TYPE_HW_SW, +} tdm_vblank_wait_type; + typedef struct _tdm_vblank_wait_info tdm_vblank_wait_info; typedef struct _tdm_private_vblank { @@ -73,26 +80,20 @@ typedef struct _tdm_private_vblank { unsigned int enable_fake; double vblank_gap; + unsigned int quotient; + unsigned int last_seq; unsigned int last_tv_sec; unsigned int last_tv_usec; /* for HW */ double HW_vblank_gap; - unsigned int HW_enable; - unsigned int HW_quotient; struct list_head HW_wait_list; + unsigned int HW_seq_margin; /* for SW */ tdm_event_loop_source *SW_timer; - struct list_head SW_pending_wait_list; struct list_head SW_wait_list; -#if 0 - tdm_vblank_wait_info *SW_align_wait; - double SW_align_offset; - unsigned int SW_align_sec; - unsigned int SW_align_usec; -#endif } tdm_private_vblank; struct _tdm_vblank_wait_info { @@ -109,14 +110,11 @@ struct _tdm_vblank_wait_info { void *user_data; tdm_private_vblank *private_vblank; - /* target_sec can be 0 when last_tv_sec is 0 because we can't calculate - * target_sec without last_tv_sec. So we have to call tdm_output_wait_vblank - * to fill last_tv_sec at the first time. - */ + tdm_vblank_wait_type type; + unsigned int target_sec; unsigned int target_usec; unsigned int target_seq; - int target_hw_interval; }; static struct list_head vblank_list; @@ -124,10 +122,8 @@ static struct list_head valid_wait_list; static unsigned int vblank_list_inited; static unsigned int stamp = 0; +static tdm_error _tdm_vblank_cb_vblank_SW(void *user_data); static tdm_error _tdm_vblank_wait_SW(tdm_vblank_wait_info *wait_info); -#if 0 -static void _tdm_vblank_sw_timer_align(tdm_private_vblank *private_vblank); -#endif #if 0 static void @@ -186,18 +182,19 @@ _tdm_vblank_insert_wait(tdm_vblank_wait_info *wait_info, struct list_head *list) } LIST_FOR_EACH_ENTRY(w, list, link) { - /* If last_tv_sec == 0, we can't calculate target_sec. */ - if (wait_info->target_sec == 0) { - if (w->interval <= wait_info->interval) { + if (wait_info->type == VBLANK_TYPE_SW) { + if (wait_info->target_sec == 0) + TDM_NEVER_GET_HERE(); + if (w->target_sec < wait_info->target_sec) { found = w; continue; } - } else { - if (w->target_sec < wait_info->target_sec) { + if (w->target_sec == wait_info->target_sec && w->target_usec <= wait_info->target_usec) { found = w; continue; } - if (w->target_sec == wait_info->target_sec && w->target_usec <= wait_info->target_usec) { + } else { + if (w->interval <= wait_info->interval) { found = w; continue; } @@ -215,10 +212,9 @@ _tdm_vblank_change_to_SW(tdm_private_vblank *private_vblank) { tdm_vblank_wait_info *w = NULL, *ww = NULL; - VIN("Change to SW"); - LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->HW_wait_list, link) { LIST_DEL(&w->link); + w->type = VBLANK_TYPE_SW_FAKE; _tdm_vblank_wait_SW(w); } } @@ -255,14 +251,6 @@ _tdm_vblank_cb_output_change(tdm_output *output, tdm_output_change_type type, private_vblank->dpms = value.u32; private_vblank->check_HW_or_SW = 1; if (private_vblank->dpms != TDM_OUTPUT_DPMS_ON) { -#if 0 - if (private_vblank->SW_align_wait) { - LIST_DEL(&private_vblank->SW_align_wait->valid_link); - free(private_vblank->SW_align_wait); - private_vblank->SW_align_wait = NULL; - } -#endif - if (private_vblank->enable_fake) _tdm_vblank_change_to_SW(private_vblank); else @@ -329,8 +317,6 @@ tdm_vblank_create(tdm_display *dpy, tdm_output *output, tdm_error *error) private_vblank->fps = mode->vrefresh; LIST_INITHEAD(&private_vblank->HW_wait_list); - - LIST_INITHEAD(&private_vblank->SW_pending_wait_list); LIST_INITHEAD(&private_vblank->SW_wait_list); LIST_ADD(&private_vblank->link, &vblank_list); @@ -353,13 +339,6 @@ tdm_vblank_destroy(tdm_vblank *vblank) LIST_DEL(&private_vblank->link); -#if 0 - if (private_vblank->SW_align_wait) { - LIST_DEL(&private_vblank->SW_align_wait->valid_link); - free(private_vblank->SW_align_wait); - } -#endif - if (private_vblank->SW_timer) { tdm_display_lock(private_vblank->dpy); tdm_event_loop_source_remove(private_vblank->SW_timer); @@ -371,12 +350,6 @@ tdm_vblank_destroy(tdm_vblank *vblank) _tdm_vblank_free_HW_wait(private_vblank, 0, 0); - LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->SW_pending_wait_list, link) { - LIST_DEL(&w->link); - LIST_DEL(&w->valid_link); - free(w); - } - LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->SW_wait_list, link) { LIST_DEL(&w->link); LIST_DEL(&w->valid_link); @@ -446,163 +419,14 @@ tdm_vblank_set_enable_fake(tdm_vblank *vblank, unsigned int enable_fake) return TDM_ERROR_NONE; } -static void -_tdm_vblank_cb_vblank_HW(tdm_output *output, unsigned int sequence, - unsigned int tv_sec, unsigned int tv_usec, - void *user_data) -{ - tdm_vblank_wait_info *wait_info = user_data; - tdm_private_vblank *private_vblank; - - if (!_tdm_vblank_check_valid(wait_info)) { - TDM_DBG("can't find wait(%p) from valid_wait_list", wait_info); - return; - } - - private_vblank = wait_info->private_vblank; - TDM_RETURN_IF_FAIL(private_vblank != NULL); - - if (!_tdm_vblank_find_wait(wait_info, &private_vblank->HW_wait_list)) { - VDB("can't find wait(%p)", wait_info); - return; - } - - LIST_DEL(&wait_info->link); - LIST_DEL(&wait_info->valid_link); - - private_vblank->last_seq = wait_info->target_seq; - private_vblank->last_tv_sec = tv_sec; - private_vblank->last_tv_usec = tv_usec; - - if (wait_info->func) - wait_info->func(private_vblank, TDM_ERROR_NONE, private_vblank->last_seq, - tv_sec, tv_usec, wait_info->user_data); - - if (tdm_debug_module & TDM_DEBUG_VBLANK) - VIN("wait(%p) done", wait_info); - - free(wait_info); -} - -static tdm_error -_tdm_vblank_wait_HW(tdm_vblank_wait_info *wait_info) -{ - tdm_private_vblank *private_vblank = wait_info->private_vblank; - tdm_error ret; - - TDM_RETURN_VAL_IF_FAIL(wait_info->target_hw_interval > 0, TDM_ERROR_OPERATION_FAILED); - - _tdm_vblank_insert_wait(wait_info, &private_vblank->HW_wait_list); - - ret = tdm_output_wait_vblank(private_vblank->output, wait_info->target_hw_interval, 0, - _tdm_vblank_cb_vblank_HW, wait_info); - - if (ret != TDM_ERROR_NONE) { - VWR("wait(%p) failed", wait_info); - LIST_DEL(&wait_info->link); - return ret; - } - - if (tdm_debug_module & TDM_DEBUG_VBLANK) - VIN("wait(%p) waiting", wait_info); - - return TDM_ERROR_NONE; -} - -static tdm_error -_tdm_vblank_cb_vblank_SW(void *user_data) +unsigned int +tdm_vblank_get_enable_fake(tdm_vblank *vblank) { - tdm_private_vblank *private_vblank = user_data; - tdm_vblank_wait_info *first_wait_info = NULL, *w = NULL, *ww = NULL; - - TDM_RETURN_VAL_IF_FAIL(private_vblank != NULL, TDM_ERROR_OPERATION_FAILED); - - if (LIST_IS_EMPTY(&private_vblank->SW_wait_list)) { - VER("no wait_info"); - return TDM_ERROR_OPERATION_FAILED; - } - - first_wait_info = container_of(private_vblank->SW_wait_list.next, first_wait_info, link); - TDM_RETURN_VAL_IF_FAIL(first_wait_info != NULL, TDM_ERROR_OPERATION_FAILED); - - if (tdm_debug_module & TDM_DEBUG_VBLANK) - VIN("wait(%p) done", first_wait_info); - - private_vblank->last_seq = first_wait_info->target_seq; - private_vblank->last_tv_sec = first_wait_info->target_sec; - private_vblank->last_tv_usec = first_wait_info->target_usec; - - LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->SW_wait_list, link) { - if (w->target_sec != first_wait_info->target_sec || - w->target_usec != first_wait_info->target_usec) - break; - - LIST_DEL(&w->link); - LIST_DEL(&w->valid_link); - - if (w->func) - w->func(private_vblank, TDM_ERROR_NONE, w->target_seq, - w->target_sec, w->target_usec, - w->user_data); - - free(w); - } - - return TDM_ERROR_NONE; -} - -static void -_tdm_vblank_cb_vblank_SW_first(tdm_output *output, unsigned int sequence, - unsigned int tv_sec, unsigned int tv_usec, - void *user_data) -{ - tdm_vblank_wait_info *wait_info = user_data; - tdm_private_vblank *private_vblank; - tdm_vblank_wait_info *w = NULL, *ww = NULL; - unsigned int min_interval = 0; - unsigned long last; - - if (!_tdm_vblank_check_valid(wait_info)) - return; - - private_vblank = wait_info->private_vblank; - TDM_RETURN_IF_FAIL(private_vblank != NULL); - - if (LIST_IS_EMPTY(&private_vblank->SW_pending_wait_list)) { - VER("no wait_info"); - return; - } - - w = container_of((&private_vblank->SW_pending_wait_list)->next, w, link); - TDM_RETURN_IF_FAIL(w != NULL); - - if (tdm_debug_module & TDM_DEBUG_VBLANK) - VIN("wait(%p) done", w); - - min_interval = w->interval; - - last = (unsigned long)tv_sec * 1000000 + tv_usec; - last -= private_vblank->offset * 1000; - - private_vblank->last_seq = min_interval; - private_vblank->last_tv_sec = last / 1000000; - private_vblank->last_tv_usec = last % 1000000; + tdm_private_vblank *private_vblank = vblank; - LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->SW_pending_wait_list, link) { - if (w->interval == min_interval) { - LIST_DEL(&w->link); - LIST_DEL(&w->valid_link); + TDM_RETURN_VAL_IF_FAIL(private_vblank != NULL, 0); - if (w->func) - w->func(private_vblank, TDM_ERROR_NONE, private_vblank->last_seq, - tv_sec, tv_usec, w->user_data); - free(w); - } else { - LIST_DEL(&w->link); - w->interval -= min_interval; - _tdm_vblank_wait_SW(w); - } - } + return private_vblank->enable_fake; } static tdm_error @@ -622,6 +446,7 @@ _tdm_vblank_sw_timer_update(tdm_private_vblank *private_vblank) curr = tdm_helper_get_time_in_micros(); target = first_wait_info->target_sec * 1000000 + first_wait_info->target_usec; + /* ms_delay should be more that 1 */ if (target < curr) ms_delay = 1; else @@ -662,194 +487,238 @@ _tdm_vblank_sw_timer_update(tdm_private_vblank *private_vblank) return TDM_ERROR_NONE; } -#if 0 static void -_tdm_vblank_cb_vblank_align(tdm_output *output, unsigned int sequence, - unsigned int tv_sec, unsigned int tv_usec, - void *user_data) +_tdm_vblank_cb_vblank_HW(tdm_output *output, unsigned int sequence, + unsigned int tv_sec, unsigned int tv_usec, + void *user_data) { - tdm_vblank_wait_info *align_info = user_data; + tdm_vblank_wait_info *wait_info = user_data; tdm_private_vblank *private_vblank; - unsigned int diff_sec, diff_usec; - if (!_tdm_vblank_check_valid(align_info)) + if (!_tdm_vblank_check_valid(wait_info)) { + TDM_DBG("can't find wait(%p) from valid_wait_list", wait_info); return; + } - private_vblank = align_info->private_vblank; + private_vblank = wait_info->private_vblank; TDM_RETURN_IF_FAIL(private_vblank != NULL); - private_vblank->SW_align_wait = NULL; - private_vblank->SW_align_sec = tv_sec; - private_vblank->SW_align_usec = tv_usec; + if (!_tdm_vblank_find_wait(wait_info, &private_vblank->HW_wait_list)) { + VDB("can't find wait(%p)", wait_info); + return; + } - LIST_DEL(&align_info->valid_link); + /* sequence is the relative value of fps. If fps = 10, sequence should be + * increased by 10 during 1 second. + */ + sequence /= private_vblank->quotient; - if (tv_usec > align_info->req_usec) { - diff_usec = tv_usec - align_info->req_usec; - diff_sec = tv_sec - align_info->req_sec; - } else { - diff_usec = 1000000 + tv_usec - align_info->req_usec; - diff_sec = tv_sec - align_info->req_sec - 1; + /* If VBLANK_TYPE_SW_FAKE, HW sequeuce can become less than SW sequeuce. + * so we will correct it with HW_seq_margin. + */ + if (private_vblank->last_seq > sequence) { + unsigned long last, tv; + last = (unsigned long)private_vblank->last_tv_sec * 1000000 + private_vblank->last_tv_usec; + tv = (unsigned long)tv_sec * 1000000 + tv_usec; + private_vblank->HW_seq_margin = ((tv - last) / (unsigned long)private_vblank->vblank_gap) + 1; + private_vblank->HW_seq_margin += private_vblank->last_seq - sequence; } - private_vblank->SW_align_offset = (double)(1000000 - diff_sec * 1000000 - diff_usec) / private_vblank->vrefresh; + sequence += private_vblank->HW_seq_margin; - free(align_info); + if (wait_info->type == VBLANK_TYPE_HW_SW) { + unsigned long target; + tdm_error ret; - /* align vblank continously only if non HW and DPMS on */ - if (!private_vblank->HW_enable && private_vblank->dpms == TDM_OUTPUT_DPMS_ON) - _tdm_vblank_sw_timer_align(private_vblank); -} + LIST_DEL(&wait_info->link); -static void -_tdm_vblank_sw_timer_align(tdm_private_vblank *private_vblank) -{ - tdm_vblank_wait_info *align_info; - unsigned long curr; - tdm_error ret; + target = (unsigned long)tv_sec * 1000000 + tv_usec; + target += (private_vblank->offset * 1000); - if (private_vblank->SW_align_wait) - return; + wait_info->target_seq = sequence; + wait_info->target_sec = target / 1000000; + wait_info->target_usec = target % 1000000; - TDM_RETURN_IF_FAIL(private_vblank->dpms == TDM_OUTPUT_DPMS_ON); + _tdm_vblank_insert_wait(wait_info, &private_vblank->SW_wait_list); - align_info = calloc(1, sizeof *align_info); - if (!align_info) { - VER("alloc failed"); - return; + ret = _tdm_vblank_sw_timer_update(private_vblank); + + /* wait_info will be freed in _tdm_vblank_cb_vblank_SW() */ + if (ret == TDM_ERROR_NONE) { + if (tdm_debug_module & TDM_DEBUG_VBLANK) + VIN("wait(%p) SW timer", wait_info); + return; + } + + VWR("couldn't update sw timer"); } - LIST_ADDTAIL(&align_info->valid_link, &valid_wait_list); - align_info->stamp = ++stamp; - align_info->private_vblank = private_vblank; + if (tdm_debug_module & TDM_DEBUG_VBLANK) + VIN("wait(%p) sequence(%u) done", wait_info, sequence); - curr = tdm_helper_get_time_in_micros(); - align_info->req_sec = curr / 1000000; - align_info->req_usec = curr % 1000000; + private_vblank->last_seq = sequence; + private_vblank->last_tv_sec = tv_sec; + private_vblank->last_tv_usec = tv_usec; - ret = tdm_output_wait_vblank(private_vblank->output, private_vblank->vrefresh, 0, - _tdm_vblank_cb_vblank_align, align_info); - if (ret != TDM_ERROR_NONE) { - LIST_DEL(&align_info->valid_link); - free(align_info); - return; - } + if (wait_info->func) + wait_info->func(private_vblank, TDM_ERROR_NONE, sequence, + tv_sec, tv_usec, wait_info->user_data); - private_vblank->SW_align_wait = align_info; + LIST_DEL(&wait_info->link); + LIST_DEL(&wait_info->valid_link); + free(wait_info); } -#endif static tdm_error -_tdm_vblank_wait_SW(tdm_vblank_wait_info *wait_info) +_tdm_vblank_wait_HW(tdm_vblank_wait_info *wait_info) { tdm_private_vblank *private_vblank = wait_info->private_vblank; + int hw_interval; tdm_error ret; - if (private_vblank->last_tv_sec == 0 && private_vblank->dpms == TDM_OUTPUT_DPMS_ON) { - unsigned int do_wait = LIST_IS_EMPTY(&private_vblank->SW_pending_wait_list); - _tdm_vblank_insert_wait(wait_info, &private_vblank->SW_pending_wait_list); - if (do_wait) { - ret = tdm_output_wait_vblank(private_vblank->output, 1, 0, - _tdm_vblank_cb_vblank_SW_first, wait_info); - if (ret == TDM_ERROR_DPMS_OFF) { - TDM_WRN("use SW"); - goto use_sw; - } - if (ret != TDM_ERROR_NONE) { - LIST_DEL(&wait_info->link); - return ret; - } - if (tdm_debug_module & TDM_DEBUG_VBLANK) - VIN("wait(%p) waiting", wait_info); - } - return TDM_ERROR_NONE; + _tdm_vblank_insert_wait(wait_info, &private_vblank->HW_wait_list); + + hw_interval = wait_info->interval * private_vblank->quotient; + + if (private_vblank->last_tv_sec != 0) { + unsigned long last, prev, req, curr; + unsigned int skip = 0; + unsigned int hw_skip; + + last = (unsigned long)private_vblank->last_tv_sec * 1000000 + private_vblank->last_tv_usec; + req = (unsigned long)wait_info->req_sec * 1000000 + wait_info->req_usec; + + skip = (unsigned int)((req - last) / private_vblank->vblank_gap); + prev = last + skip * private_vblank->vblank_gap; + + curr = tdm_helper_get_time_in_micros(); + hw_skip = (unsigned int)((curr - prev) / private_vblank->HW_vblank_gap); + + hw_interval -= hw_skip; + + if (tdm_debug_module & TDM_DEBUG_VBLANK) + VIN("wait(%p) last(%4lu) req(%4lu) prev(%4lu) curr(%4lu) skip(%d) hw_interval(%d)", + wait_info, last, req - last, prev - last, curr - last, + skip, hw_interval); } -use_sw: - TDM_RETURN_VAL_IF_FAIL(wait_info->target_sec > 0, TDM_ERROR_OPERATION_FAILED); + if (hw_interval < 1) + hw_interval = 1; - _tdm_vblank_insert_wait(wait_info, &private_vblank->SW_wait_list); + ret = tdm_output_wait_vblank(private_vblank->output, hw_interval, 0, + _tdm_vblank_cb_vblank_HW, wait_info); - ret = _tdm_vblank_sw_timer_update(private_vblank); if (ret != TDM_ERROR_NONE) { LIST_DEL(&wait_info->link); - VER("couldn't update sw timer"); return ret; } + if (tdm_debug_module & TDM_DEBUG_VBLANK) + VIN("wait(%p) waiting", wait_info); + return TDM_ERROR_NONE; } -static void -_tdm_vblank_calculate_target(tdm_vblank_wait_info *wait_info) +static tdm_error +_tdm_vblank_cb_vblank_SW(void *user_data) { - tdm_private_vblank *private_vblank = wait_info->private_vblank; - unsigned long last, prev, req, curr, target; - unsigned int skip = 0; + tdm_private_vblank *private_vblank = user_data; + tdm_vblank_wait_info *first_wait_info = NULL, *w = NULL, *ww = NULL; - curr = tdm_helper_get_time_in_micros(); + TDM_RETURN_VAL_IF_FAIL(private_vblank != NULL, TDM_ERROR_OPERATION_FAILED); - if (!private_vblank->HW_enable) { - if (private_vblank->last_tv_sec == 0) { - /* If last == 0 and DPMS == on, we will use HW vblank to sync with HW vblank. */ - if (private_vblank->dpms == TDM_OUTPUT_DPMS_ON) { - return; - } else { - private_vblank->last_tv_sec = curr / 1000000; - private_vblank->last_tv_usec = curr % 1000000; - } - } + if (LIST_IS_EMPTY(&private_vblank->SW_wait_list)) { + VER("no wait_info"); + return TDM_ERROR_OPERATION_FAILED; } - /* last can be 0 when HW enable. But it doesn't matter if HW enable. */ - if (!private_vblank->HW_enable) - TDM_RETURN_IF_FAIL(private_vblank->last_tv_sec != 0); + first_wait_info = container_of(private_vblank->SW_wait_list.next, first_wait_info, link); + TDM_RETURN_VAL_IF_FAIL(first_wait_info != NULL, TDM_ERROR_OPERATION_FAILED); + + if (tdm_debug_module & TDM_DEBUG_VBLANK) + VIN("wait(%p) sequence(%u) done", first_wait_info, first_wait_info->target_seq); + + private_vblank->last_seq = first_wait_info->target_seq; + private_vblank->last_tv_sec = first_wait_info->target_sec; + private_vblank->last_tv_usec = first_wait_info->target_usec; + + LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->SW_wait_list, link) { + if (w->target_sec != first_wait_info->target_sec || + w->target_usec != first_wait_info->target_usec) + break; + + LIST_DEL(&w->link); + LIST_DEL(&w->valid_link); + + if (w->func) + w->func(private_vblank, TDM_ERROR_NONE, w->target_seq, + w->target_sec, w->target_usec, + w->user_data); - last = (unsigned long)private_vblank->last_tv_sec * 1000000 + private_vblank->last_tv_usec; - req = (unsigned long)wait_info->req_sec * 1000000 + wait_info->req_usec; - skip = (unsigned int)((req - last) / private_vblank->vblank_gap); - prev = last + skip * private_vblank->vblank_gap; + free(w); + } - if (private_vblank->last_seq == 0) - skip = 0; + return TDM_ERROR_NONE; +} - skip += wait_info->interval; +static tdm_error +_tdm_vblank_wait_SW(tdm_vblank_wait_info *wait_info) +{ + tdm_private_vblank *private_vblank = wait_info->private_vblank; + tdm_error ret; - if (private_vblank->HW_enable) { - unsigned int hw_skip = (unsigned int)((curr - prev) / private_vblank->HW_vblank_gap); + if (private_vblank->last_tv_sec == 0) { + unsigned long curr = tdm_helper_get_time_in_micros(); - wait_info->target_hw_interval = wait_info->interval * private_vblank->HW_quotient; - wait_info->target_hw_interval -= hw_skip; + /* SW vblank starts from now. SW vblank doesn't need to be aligned with HW vblank. */ + private_vblank->last_seq = 0; + private_vblank->last_tv_sec = curr / 1000000; + private_vblank->last_tv_usec = curr % 1000000; - if (wait_info->target_hw_interval < 1) - wait_info->target_hw_interval = 1; + /* +1 ms to call the handler ASAP at the first. no matter for SW timer. */ + curr += 1000; - target = prev + wait_info->target_hw_interval * private_vblank->HW_vblank_gap; + wait_info->target_seq = 1; + wait_info->target_sec = curr / 1000000; + wait_info->target_usec = curr % 1000000; } else { + unsigned long last, prev, req, curr, target; + unsigned int skip; + + last = (unsigned long)private_vblank->last_tv_sec * 1000000 + private_vblank->last_tv_usec; + req = (unsigned long)wait_info->req_sec * 1000000 + wait_info->req_usec; + + skip = (unsigned int)((req - last) / private_vblank->vblank_gap); + prev = last + skip * private_vblank->vblank_gap; + + curr = tdm_helper_get_time_in_micros(); target = prev + (unsigned long)(private_vblank->vblank_gap * wait_info->interval); - while (target < curr) { + while (target < curr) target += (unsigned long)private_vblank->vblank_gap; - skip++; - } - } - if (tdm_debug_module & TDM_DEBUG_VBLANK) - VIN("target_seq(%d) last_seq(%d) skip(%d)", - wait_info->target_seq, private_vblank->last_seq, skip); + wait_info->target_seq = private_vblank->last_seq; + wait_info->target_seq += (target - last) / (unsigned long)private_vblank->vblank_gap; -#if 0 - target -= (private_vblank->SW_align_offset * skip * private_vblank->HW_quotient); -#endif + wait_info->target_sec = target / 1000000; + wait_info->target_usec = target % 1000000; - wait_info->target_seq = private_vblank->last_seq + skip; - wait_info->target_sec = target / 1000000; - wait_info->target_usec = target % 1000000; + if (tdm_debug_module & TDM_DEBUG_VBLANK) + VIN("wait(%p) last(%4lu) req(%4lu) prev(%4lu) curr(%4lu) target(%4lu,%4lu)", + wait_info, last, req - last, prev - last, curr - last, + target, target - last); + } - if (tdm_debug_module & TDM_DEBUG_VBLANK) - VIN("wait(%p) last(%4lu) req(%4lu) prev(%4lu) curr(%4lu) skip(%d) hw_interval(%d) target(%4lu,%4lu)", - wait_info, last, req - last, prev - last, curr - last, - skip, wait_info->target_hw_interval, target, target - last); + _tdm_vblank_insert_wait(wait_info, &private_vblank->SW_wait_list); + + ret = _tdm_vblank_sw_timer_update(private_vblank); + if (ret != TDM_ERROR_NONE) { + LIST_DEL(&wait_info->link); + VER("couldn't update sw timer"); + return ret; + } + + return TDM_ERROR_NONE; } tdm_error @@ -868,26 +737,6 @@ tdm_vblank_wait(tdm_vblank *vblank, unsigned int req_sec, unsigned int req_usec, return TDM_ERROR_DPMS_OFF; } -#if 0 - if (!private_vblank->SW_align_wait && private_vblank->dpms == TDM_OUTPUT_DPMS_ON) - _tdm_vblank_sw_timer_align(private_vblank); -#endif - - if (private_vblank->check_HW_or_SW) { - private_vblank->check_HW_or_SW = 0; - private_vblank->vblank_gap = (double)1000000 / private_vblank->fps; - private_vblank->HW_quotient = private_vblank->vrefresh / private_vblank->fps; - - if (private_vblank->dpms == TDM_OUTPUT_DPMS_ON && - !(private_vblank->vrefresh % private_vblank->fps)) { - private_vblank->HW_enable = 1; - VIN("Use HW vblank"); - } else { - private_vblank->HW_enable = 0; - VIN("Use SW vblank"); - } - } - wait_info = calloc(1, sizeof *wait_info); if (!wait_info) { VER("alloc failed"); @@ -904,16 +753,34 @@ tdm_vblank_wait(tdm_vblank *vblank, unsigned int req_sec, unsigned int req_usec, wait_info->user_data = user_data; wait_info->private_vblank = private_vblank; - _tdm_vblank_calculate_target(wait_info); + if (private_vblank->check_HW_or_SW) { + private_vblank->check_HW_or_SW = 0; + private_vblank->vblank_gap = (double)1000000 / private_vblank->fps; + private_vblank->quotient = private_vblank->vrefresh / private_vblank->fps; + } + + /* 1) if fps != factor of vrefresh, SW timer + * 2) if fps == factor of vrefresh && dpms == off, SW timer (Fake HW vblank) + * 2) if fps == factor of vrefresh && dpms == on && offset == 0, HW vblank + * 3) if fps == factor of vrefresh && dpms == on && offset != 0, HW vblank + SW timer + * In case of 1), we really don't need to align with HW vblank. + */ + if (private_vblank->vrefresh % private_vblank->fps) + wait_info->type = VBLANK_TYPE_SW; + else if (private_vblank->dpms == TDM_OUTPUT_DPMS_OFF) + wait_info->type = VBLANK_TYPE_SW_FAKE; + else if (private_vblank->offset == 0) + wait_info->type = VBLANK_TYPE_HW; + else + wait_info->type = VBLANK_TYPE_HW_SW; - if (private_vblank->HW_enable) { + if (wait_info->type == VBLANK_TYPE_SW || wait_info->type == VBLANK_TYPE_SW_FAKE) + ret = _tdm_vblank_wait_SW(wait_info); + else { ret = _tdm_vblank_wait_HW(wait_info); - if (ret == TDM_ERROR_DPMS_OFF) { - TDM_WRN("try to use SW"); + if (ret == TDM_ERROR_DPMS_OFF) ret = _tdm_vblank_wait_SW(wait_info); - } - } else - ret = _tdm_vblank_wait_SW(wait_info); + } if (ret != TDM_ERROR_NONE) { LIST_DEL(&wait_info->link); -- 2.7.4 From 9c112765e3e44849ca21171c1d554f93c58982a0 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Thu, 1 Sep 2016 12:30:59 +0900 Subject: [PATCH 16/16] add wait_vblank_seq to request a vblank with the target sequence Change-Id: I8ee758b154df1290f6d4f8a0dd353c6cc0ffcecb --- client/tdm_client.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++ client/tdm_client.h | 22 +++++++++++++++++- protocol/tdm.xml | 7 ++++++ src/tdm_private.h | 6 ++++- src/tdm_server.c | 40 ++++++++++++++++++++++++++++++++ src/tdm_vblank.c | 50 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 190 insertions(+), 2 deletions(-) diff --git a/client/tdm_client.c b/client/tdm_client.c index 8d1ffe6..7e17d1e 100644 --- a/client/tdm_client.c +++ b/client/tdm_client.c @@ -786,3 +786,70 @@ tdm_client_vblank_wait(tdm_client_vblank *vblank, unsigned int interval, tdm_cli return TDM_ERROR_NONE; } + +tdm_error +tdm_client_vblank_wait_seq(tdm_client_vblank *vblank, unsigned int sequence, + tdm_client_vblank_handler func, void *user_data) +{ + tdm_private_client *private_client; + tdm_private_client_output *private_output; + tdm_private_client_vblank *private_vblank; + tdm_client_wait_info *w; + struct timespec tp; + int ret = 0; + + TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER); + + private_vblank = vblank; + private_output = private_vblank->private_output; + private_client = private_output->private_client; + + if (!private_vblank->started) + private_vblank->started = 1; + + if (private_output->dpms != TDM_OUTPUT_DPMS_ON && !private_vblank->enable_fake) { + TDM_INFO("dpms off"); + return TDM_ERROR_DPMS_OFF; + } + + w = calloc(1, sizeof *w); + if (!w) { + TDM_ERR("alloc failed"); + return TDM_ERROR_OUT_OF_MEMORY; + } + + w->private_vblank = private_vblank; + w->func = func; + w->user_data = user_data; + + LIST_ADDTAIL(&w->link, &private_vblank->wait_list); + + clock_gettime(CLOCK_MONOTONIC, &tp); + w->req_id = ++private_output->req_id; + w->req_sec = (unsigned int)tp.tv_sec; + w->req_usec = (unsigned int)(tp.tv_nsec / 1000); + w->need_free = (private_vblank->sync) ? 0 : 1; + + wl_tdm_vblank_wait_vblank_seq(private_vblank->vblank, sequence, w->req_id, w->req_sec, w->req_usec); + + TDM_TRACE_COUNT(ClientWaitVBlank, w->req_id); + + if (!private_vblank->sync) { + wl_display_flush(private_client->display); + return TDM_ERROR_NONE; + } + + while (ret != -1 && !w->need_free) + ret = wl_display_dispatch(private_client->display); + + clock_gettime(CLOCK_MONOTONIC, &tp); + TDM_DBG("block during %d us", + ((unsigned int)(tp.tv_sec * 1000000) + (unsigned int)(tp.tv_nsec / 1000)) + - (w->req_sec * 1000000 + w->req_usec)); + + LIST_DEL(&w->link); + free(w); + + return TDM_ERROR_NONE; +} \ No newline at end of file diff --git a/client/tdm_client.h b/client/tdm_client.h index c64391e..1c2ef7d 100644 --- a/client/tdm_client.h +++ b/client/tdm_client.h @@ -296,7 +296,9 @@ tdm_client_vblank_set_enable_fake(tdm_client_vblank *vblank, unsigned int enable * #tdm_client_vblank_wait will return TDM_ERROR_NONE as success if * #tdm_client_vblank_set_enable_fake sets true. Once #tdm_client_vblank_wait * returns TDM_ERROR_NONE, the user client vblank handler(#tdm_client_vblank_handler) - * SHOULD be called after the given interval. + * SHOULD be called after the given interval. \n + * The sequence value of tdm_client_vblank_handler is the relative value of fps. + * If fps = 10, this sequence value should be increased by 10 during 1 second. * @param[in] vblank The client vblank object * @param[in] interval The vblank interval * @param[in] func The user client vblank handler @@ -375,6 +377,24 @@ tdm_client_vblank_set_enable_fake(tdm_client_vblank *vblank, unsigned int enable tdm_error tdm_client_vblank_wait(tdm_client_vblank *vblank, unsigned int interval, tdm_client_vblank_handler func, void *user_data); +/** + * @brief Wait for a vblank with the target sequence number + * @details + * This function will return TDM_ERROR_DPMS_OFF when DPMS off. However, + * #tdm_client_vblank_wait will return TDM_ERROR_NONE as success if + * #tdm_client_vblank_set_enable_fake sets true. Once #tdm_client_vblank_wait_seq + * returns TDM_ERROR_NONE, the user client vblank handler(#tdm_client_vblank_handler) + * SHOULD be called on reaching the target sequence. + * If the sequence value is 0, the result will be the same with calling + * #tdm_client_vblank_wait with (interval = 1). + * @param[in] vblank The client vblank object + * @param[in] sequence The target sequence number + * @param[in] func The user client vblank handler + * @param[in] user_data The user data + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ +tdm_error +tdm_client_vblank_wait_seq(tdm_client_vblank *vblank, unsigned int sequence, tdm_client_vblank_handler func, void *user_data); #ifdef __cplusplus } diff --git a/protocol/tdm.xml b/protocol/tdm.xml index e338958..3f98fd1 100644 --- a/protocol/tdm.xml +++ b/protocol/tdm.xml @@ -89,6 +89,13 @@ + + + + + + + diff --git a/src/tdm_private.h b/src/tdm_private.h index 44af51f..2e12f46 100644 --- a/src/tdm_private.h +++ b/src/tdm_private.h @@ -603,7 +603,11 @@ tdm_vblank_set_enable_fake(tdm_vblank *vblank, unsigned int enable_fake); unsigned int tdm_vblank_get_enable_fake(tdm_vblank *vblank); tdm_error -tdm_vblank_wait(tdm_vblank *vblank, unsigned int req_sec, unsigned int req_usec, unsigned int interval, tdm_vblank_handler func, void *user_data); +tdm_vblank_wait(tdm_vblank *vblank, unsigned int req_sec, unsigned int req_usec, + unsigned int interval, tdm_vblank_handler func, void *user_data); +tdm_error +tdm_vblank_wait_seq(tdm_vblank *vblank, unsigned int req_sec, unsigned int req_usec, + unsigned int sequence, tdm_vblank_handler func, void *user_data); void tdm_monitor_server_command(tdm_display *dpy, const char *options, char *reply, int *len); diff --git a/src/tdm_server.c b/src/tdm_server.c index 9e88787..37c50d9 100644 --- a/src/tdm_server.c +++ b/src/tdm_server.c @@ -274,12 +274,52 @@ wait_failed: destroy_wait(wait_info); } +static void +_tdm_server_vblank_cb_wait_vblank_seq(struct wl_client *client, struct wl_resource *resource, + uint32_t sequence, uint32_t req_id, uint32_t req_sec, uint32_t req_usec) +{ + tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource); + tdm_server_output_info *output_info = vblank_info->output_info; + tdm_private_server *private_server = output_info->private_server; + tdm_server_wait_info *wait_info; + tdm_error ret; + + TDM_TRACE_COUNT(ServerWaitVBlank, req_id); + + wait_info = calloc(1, sizeof *wait_info); + if (!wait_info) { + TDM_ERR("alloc failed"); + ret = TDM_ERROR_OUT_OF_MEMORY; + goto wait_failed; + } + + LIST_ADDTAIL(&wait_info->link, &private_server->wait_list); + wait_info->vblank_info = vblank_info; + wait_info->req_id = req_id; + + if (tdm_debug_module & TDM_DEBUG_VBLANK) + TDM_INFO("req_id(%d) wait", req_id); + + ret = tdm_vblank_wait_seq(vblank_info->vblank, req_sec, req_usec, sequence, _tdm_server_cb_vblank, wait_info); + + if (!tdm_vblank_get_enable_fake(vblank_info->vblank) && ret == TDM_ERROR_DPMS_OFF) + goto wait_failed; + + TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, wait_failed); + + return; +wait_failed: + wl_tdm_vblank_send_done(vblank_info->resource, req_id, 0, 0, 0, ret); + destroy_wait(wait_info); +} + static const struct wl_tdm_vblank_interface tdm_vblank_implementation = { _tdm_server_vblank_cb_destroy, _tdm_server_vblank_cb_set_fps, _tdm_server_vblank_cb_set_offset, _tdm_server_vblank_cb_set_enable_fake, _tdm_server_vblank_cb_wait_vblank, + _tdm_server_vblank_cb_wait_vblank_seq, }; static void diff --git a/src/tdm_vblank.c b/src/tdm_vblank.c index e63c14c..8ff13e5 100644 --- a/src/tdm_vblank.c +++ b/src/tdm_vblank.c @@ -791,3 +791,53 @@ tdm_vblank_wait(tdm_vblank *vblank, unsigned int req_sec, unsigned int req_usec, return TDM_ERROR_NONE; } + +tdm_error +tdm_vblank_wait_seq(tdm_vblank *vblank, unsigned int req_sec, unsigned int req_usec, + unsigned int sequence, tdm_vblank_handler func, void *user_data) +{ + tdm_private_vblank *private_vblank = vblank; + unsigned int interval; + + TDM_RETURN_VAL_IF_FAIL(private_vblank != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER); + + if (sequence == 0) { + interval = 1; + } else if (private_vblank->last_seq == 0) { + TDM_WRN("can't calculate interval with sequence(%u)", sequence); + interval = 1; + } else if (sequence > private_vblank->last_seq) { + unsigned long last, curr, seq_target; + + last = (unsigned long)private_vblank->last_tv_sec * 1000000 + private_vblank->last_tv_usec; + curr = tdm_helper_get_time_in_micros(); + seq_target = last + (unsigned long)(private_vblank->vblank_gap * (sequence - private_vblank->last_seq)); + + /* If target is old or too close(1ms) to current, we call the handler immediately. + * 2ms? it seems too much. + */ + if (seq_target < curr) { + func(vblank, TDM_ERROR_NONE, sequence, + seq_target / 1000000, seq_target % 1000000, + user_data); + return TDM_ERROR_NONE; + } else if ((seq_target - curr) < 1000) { /* 1ms */ + func(vblank, TDM_ERROR_NONE, sequence, + curr / 1000000, curr % 1000000, + user_data); + return TDM_ERROR_NONE; + } + + interval = ((seq_target - curr) / (unsigned long)private_vblank->vblank_gap) + 1; + } else { + /* this seems error. But we handle this like normal to avoid the unexpected error in cliend side */ + TDM_WRN("sequence(%u) should be over the last sequence(%u)", sequence, private_vblank->last_seq); + func(vblank, TDM_ERROR_NONE, private_vblank->last_seq, + private_vblank->last_tv_sec, private_vblank->last_tv_usec, + user_data); + return TDM_ERROR_NONE; + } + + return tdm_vblank_wait(vblank, req_sec, req_usec, interval, func, user_data); +} -- 2.7.4