1 #include "e_zone_video_intern.h"
2 #include "e_client_video_intern.h"
3 #include "e_video_hwc_intern.h"
4 #include "e_comp_screen_intern.h"
5 #include "e_comp_wl_intern.h"
6 #include "e_comp_wl_subsurface_intern.h"
7 #include "e_comp_wl_viewport_intern.h"
8 #include "e_output_intern.h"
9 #include "e_comp_wl_video_buffer_intern.h"
10 #include "e_util_video_intern.h"
11 #include "e_video_debug_intern.h"
12 #include "e_client_intern.h"
14 #include <wayland-tbm-server.h>
17 #include <tdm_helper.h>
20 #define GEO_FMT "(%dx%d+%d+%d) -> (%dx%d+%d+%d) transform(%d)"
22 (g)->input_r.w, (g)->input_r.h, (g)->input_r.x, (g)->input_r.y, \
23 (g)->output_r.w, (g)->output_r.h, (g)->output_r.x, (g)->output_r.y, \
28 evh = container_of(iface, E_Video_Hwc, iface)
30 static Eina_Bool _e_video_hwc_render(E_Video_Hwc *evh, const char *func);
31 static void _e_video_hwc_buffer_show(E_Video_Hwc *evh, E_Comp_Wl_Video_Buf *vbuf, unsigned int transform);
32 static void _e_video_hwc_buffer_commit(E_Video_Hwc *evh, E_Comp_Wl_Video_Buf *vbuf);
35 _e_video_hwc_client_offscreen_parent_get(E_Client *ec)
37 E_Client *parent = NULL;
39 if (!e_comp_wl_subsurface_check(ec))
42 parent = e_comp_wl_subsurface_parent_get(ec);
45 if (!e_comp_wl_subsurface_check(parent))
48 if (parent->comp_data->sub.data->remote_surface.offscreen_parent)
49 return parent->comp_data->sub.data->remote_surface.offscreen_parent;
51 parent = e_comp_wl_subsurface_parent_get(parent);
58 _e_video_hwc_client_visible_get(E_Client *ec)
60 E_Client *offscreen_parent;
62 if (!e_pixmap_resource_get(ec->pixmap))
64 VDB("no comp buffer", ec);
68 if ((e_comp_wl_subsurface_check(ec)) &&
69 (e_comp_wl_subsurface_stand_alone_mode_get(ec)))
72 offscreen_parent = _e_video_hwc_client_offscreen_parent_get(ec);
73 if (offscreen_parent && e_client_visibility_get(offscreen_parent) == E_VISIBILITY_FULLY_OBSCURED)
75 VDB("video surface invisible: offscreen fully obscured", ec);
79 if (!evas_object_visible_get(ec->frame))
81 VDB("evas obj invisible", ec);
88 /* Video Buffer implementation */
89 static E_Comp_Wl_Video_Buf *
90 _e_video_hwc_vbuf_find(Eina_List *list, tbm_surface_h buffer)
92 E_Comp_Wl_Video_Buf *vbuf;
95 EINA_LIST_FOREACH(list, l, vbuf)
97 if (vbuf->tbm_surface == buffer)
104 static E_Comp_Wl_Video_Buf *
105 _e_video_hwc_vbuf_find_with_comp_buffer(Eina_List *list, E_Comp_Wl_Buffer *comp_buffer)
107 E_Comp_Wl_Video_Buf *vbuf;
110 EINA_LIST_FOREACH(list, l, vbuf)
112 if (vbuf->comp_buffer == comp_buffer)
120 _e_video_hwc_video_buffer_scanout_check(E_Comp_Wl_Video_Buf *vbuf)
122 tbm_surface_h tbm_surface = NULL;
126 tbm_surface = vbuf->tbm_surface;
127 EINA_SAFETY_ON_NULL_RETURN_VAL(tbm_surface, EINA_FALSE);
129 bo = tbm_surface_internal_get_bo(tbm_surface, 0);
130 EINA_SAFETY_ON_NULL_RETURN_VAL(bo, EINA_FALSE);
132 flag = tbm_bo_get_flags(bo);
133 if (flag & TBM_BO_SCANOUT)
140 _e_video_hwc_input_buffer_cb_free(E_Comp_Wl_Video_Buf *vbuf, void *data)
142 E_Video_Hwc *evh = data;
143 Eina_Bool need_hide = EINA_FALSE;
145 DBG("Buffer(%p) to be free, refcnt(%d)", vbuf, vbuf->ref_cnt);
147 evh->input_buffer_list = eina_list_remove(evh->input_buffer_list, vbuf);
149 if (vbuf->comp_buffer)
150 e_comp_wl_buffer_reference(&vbuf->buffer_ref, NULL);
152 if (evh->current_fb == vbuf)
154 VIN("current fb destroyed", evh->ec);
155 e_comp_wl_video_buffer_set_use(evh->current_fb, EINA_FALSE);
156 evh->current_fb = NULL;
157 need_hide = EINA_TRUE;
160 if (evh->committed_vbuf == vbuf)
162 VIN("committed fb destroyed", evh->ec);
163 e_comp_wl_video_buffer_set_use(evh->committed_vbuf, EINA_FALSE);
164 evh->committed_vbuf = NULL;
165 need_hide = EINA_TRUE;
168 if (eina_list_data_find(evh->bqueue, vbuf))
170 VIN("waiting fb destroyed", evh->ec);
171 evh->bqueue = eina_list_remove(evh->bqueue, vbuf);
175 evh->backend.buffer_commit(evh, NULL);
178 static E_Comp_Wl_Video_Buf *
179 _e_video_hwc_input_buffer_get(E_Video_Hwc *evh, E_Comp_Wl_Buffer *comp_buffer)
181 E_Comp_Wl_Video_Buf *vbuf;
183 vbuf = _e_video_hwc_vbuf_find_with_comp_buffer(evh->input_buffer_list, comp_buffer);
187 vbuf = e_comp_wl_video_buffer_create_comp(comp_buffer);
190 VER("failed 'e_comp_wl_video_buffer_create_comp()'", evh->ec);
194 DBG("Buffer(%p) created, refcnt:%d", vbuf, vbuf->ref_cnt);
196 evh->input_buffer_list = eina_list_append(evh->input_buffer_list, vbuf);
197 e_comp_wl_video_buffer_free_func_add(vbuf, _e_video_hwc_input_buffer_cb_free, evh);
199 struct wl_resource *surface = e_comp_wl_client_surface_get(evh->ec);
200 DBG("Client(%s):PID(%d) RscID(%d), Buffer(%p) created",
201 e_client_util_name_get(evh->ec) ?: "No Name" , evh->ec->netwm.pid,
202 wl_resource_get_id(surface), vbuf);
204 vbuf->content_r = evh->geo.input_r;
207 /* End of Video Buffer implementation */
209 /* PP implementation */
211 _e_video_hwc_pp_cb_done(tdm_pp *pp, tbm_surface_h sb, tbm_surface_h db, void *user_data)
213 E_Video_Hwc *evh = (E_Video_Hwc *)user_data;
214 E_Comp_Wl_Video_Buf *input_buffer, *pp_buffer;
216 input_buffer = _e_video_hwc_vbuf_find(evh->input_buffer_list, sb);
218 e_comp_wl_video_buffer_unref(input_buffer);
220 pp_buffer = _e_video_hwc_vbuf_find(evh->pp_buffer_list, db);
223 e_comp_wl_video_buffer_set_use(pp_buffer, EINA_FALSE);
224 if (!_e_video_hwc_client_visible_get(evh->ec)) return;
226 _e_video_hwc_buffer_show(evh, pp_buffer, 0);
230 VER("There is no pp_buffer", evh->ec);
231 // there is no way to set in_use flag.
232 // This will cause issue when server get available pp_buffer.
237 _e_video_hwc_pp_destroy(E_Video_Hwc_PP *pp)
239 tdm_pp_destroy(pp->tdm_handle);
243 static E_Video_Hwc_PP *
244 _e_video_hwc_pp_create(tdm_display *display, void *user_data)
247 tdm_pp_capability caps;
250 pp = E_NEW(E_Video_Hwc_PP, 1);
254 pp->tdm_handle = tdm_display_create_pp(display, NULL);
257 VER("Failed 'tdm_display_create_pp()'", NULL);
262 tdm_display_get_pp_available_size(display,
263 &pp->minw, &pp->minh,
264 &pp->maxw, &pp->maxh,
266 tdm_display_get_pp_preferred_align_vertical(display, &pp->align_vertical);
268 err = tdm_display_get_pp_capabilities(display, &caps);
269 if (err == TDM_ERROR_NONE)
271 if ((caps & TDM_PP_CAPABILITY_NO_CSC) || (caps & TDM_PP_CAPABILITY_NO_TRANSFORM_ROTATION))
273 VER("tdm pp not support csc or transform", NULL);
277 if (caps & TDM_PP_CAPABILITY_SCANOUT)
278 pp->scanout = EINA_TRUE;
281 err = tdm_pp_set_done_handler(pp->tdm_handle, _e_video_hwc_pp_cb_done, user_data);
282 if (err != TDM_ERROR_NONE)
284 VER("tdm_pp_set_done_handler() failed", NULL);
291 tdm_pp_destroy(pp->tdm_handle);
297 _e_video_hwc_pp_size_limit_check(E_Video_Hwc_PP *pp, const Eina_Rectangle *input_rect, const Eina_Rectangle *output_rect)
301 if ((input_rect->w < pp->minw) ||
302 (output_rect->w < pp->minw))
308 if ((input_rect->h < pp->minh) ||
309 (output_rect->h < pp->minh))
315 if ((input_rect->w > pp->maxw) ||
316 (output_rect->w > pp->maxw))
322 if ((input_rect->h > pp->maxh) ||
323 (output_rect->h > pp->maxh))
329 INF("size(%dx%d, %dx%d) is out of PP range",
330 input_rect->w, input_rect->h, output_rect->w, output_rect->h);
335 static E_Comp_Wl_Video_Buf *
336 _e_video_hwc_pp_input_buffer_get(E_Video_Hwc *evh, E_Comp_Wl_Buffer *comp_buffer)
338 E_Comp_Wl_Video_Buf *vbuf;
339 Eina_Bool input_buffer_scanout;
341 vbuf = _e_video_hwc_vbuf_find_with_comp_buffer(evh->input_buffer_list, comp_buffer);
345 vbuf = e_comp_wl_video_buffer_create_comp(comp_buffer);
348 VER("failed to create video buffer", evh->ec);
352 input_buffer_scanout = _e_video_hwc_video_buffer_scanout_check(vbuf);
354 if (((evh->pp->align != -1) && (vbuf->width_from_pitch % evh->pp->align != 0)) ||
355 ((evh->pp->scanout) && (!input_buffer_scanout)))
357 VER("cannot use this input buffer as an source buffer for pp: "
358 "pp align(%d) bwidth(%d) pp scanout(%d) bscanout(%d)", evh->ec,
359 evh->pp->align, vbuf->width_from_pitch, evh->pp->scanout,
360 input_buffer_scanout);
361 e_comp_wl_video_buffer_unref(vbuf);
365 DBG("Buffer(%p) created, refcnt:%d scanout:%d",
366 vbuf, vbuf->ref_cnt, (evh->pp->scanout || input_buffer_scanout));
368 evh->input_buffer_list = eina_list_append(evh->input_buffer_list, vbuf);
369 e_comp_wl_video_buffer_free_func_add(vbuf, _e_video_hwc_input_buffer_cb_free, evh);
371 struct wl_resource *surface = e_comp_wl_client_surface_get(evh->ec);
372 DBG("Client(%s):PID(%d) RscID(%d), Buffer(%p) created",
373 e_client_util_name_get(evh->ec) ?: "No Name" , evh->ec->netwm.pid,
374 wl_resource_get_id(surface), vbuf);
376 vbuf->content_r = evh->geo.input_r;
381 _e_video_hwc_pp_buffer_cb_free(E_Comp_Wl_Video_Buf *vbuf, void *data)
383 E_Video_Hwc *evh = data;
385 e_comp_wl_video_buffer_set_use(vbuf, EINA_FALSE);
387 if (evh->current_fb == vbuf)
388 evh->current_fb = NULL;
390 if (evh->committed_vbuf == vbuf)
391 evh->committed_vbuf = NULL;
393 evh->bqueue = eina_list_remove(evh->bqueue, vbuf);
395 evh->pp_buffer_list = eina_list_remove(evh->pp_buffer_list, vbuf);
399 _e_video_hwc_pp_gcd_get(int x, int y)
414 _e_video_hwc_pp_aligned_value_get(E_Video_Hwc *evh, int *aligned_width, int *aligned_height)
418 if (evh->pp->minw != -1)
420 if (evh->pp->minw > *aligned_width)
421 *aligned_width = evh->pp->minw;
424 if (evh->pp->minh != -1)
426 if (evh->pp->minh > *aligned_height)
427 *aligned_height = evh->pp->minh;
430 if (evh->pp->align > 0)
432 if (evh->output_align != -1)
434 if (evh->output_align >= evh->pp->align)
435 width_align = _e_video_hwc_pp_gcd_get(evh->output_align, evh->pp->align);
437 width_align = _e_video_hwc_pp_gcd_get(evh->pp->align, evh->output_align);
439 width_align = evh->output_align * evh->pp->align / width_align;
443 width_align = evh->pp->align;
448 if (evh->output_align != -1)
449 width_align = evh->output_align;
451 width_align = *aligned_width;
454 *aligned_width = ROUNDUP(*aligned_width, width_align);
456 if (evh->pp->align_vertical != -1)
457 *aligned_height = ROUNDUP(*aligned_height, evh->pp->align_vertical);
461 static E_Comp_Wl_Video_Buf *
462 _e_video_hwc_pp_buffer_get(E_Video_Hwc *evh, int width, int height)
464 E_Comp_Wl_Video_Buf *vbuf;
467 int aligned_width = width;
468 int aligned_height = height;
470 _e_video_hwc_pp_aligned_value_get(evh, &aligned_width, &aligned_height);
472 if (evh->pp_buffer_list)
474 vbuf = eina_list_data_get(evh->pp_buffer_list);
475 EINA_SAFETY_ON_NULL_RETURN_VAL(vbuf, NULL);
477 /* if we need bigger pp_buffers, destroy all pp_buffers and create */
478 if (aligned_width > vbuf->width_from_pitch || aligned_height != vbuf->height)
482 VIN("pp buffer changed: %dx%d => %dx%d", evh->ec,
483 vbuf->width_from_pitch, vbuf->height,
484 aligned_width, aligned_height);
486 EINA_LIST_FOREACH_SAFE(evh->pp_buffer_list, l, ll, vbuf)
489 e_comp_wl_video_buffer_set_use(vbuf, EINA_FALSE);
490 e_comp_wl_video_buffer_unref(vbuf);
492 if (evh->pp_buffer_list)
500 if (!evh->pp_buffer_list)
502 for (i = 0; i < BUFFER_MAX_COUNT; i++)
504 vbuf = e_comp_wl_video_buffer_alloc(aligned_width, aligned_height, evh->pp_tbmfmt, EINA_TRUE);
505 EINA_SAFETY_ON_NULL_RETURN_VAL(vbuf, NULL);
507 e_comp_wl_video_buffer_free_func_add(vbuf, _e_video_hwc_pp_buffer_cb_free, evh);
508 evh->pp_buffer_list = eina_list_append(evh->pp_buffer_list, vbuf);
512 VIN("pp buffer created: %dx%d, %c%c%c%c", evh->ec,
513 vbuf->width_from_pitch, aligned_height, FOURCC_STR(evh->pp_tbmfmt));
515 evh->next_buffer = evh->pp_buffer_list;
518 EINA_SAFETY_ON_NULL_RETURN_VAL(evh->pp_buffer_list, NULL);
519 EINA_SAFETY_ON_NULL_RETURN_VAL(evh->next_buffer, NULL);
521 l = evh->next_buffer;
522 while ((vbuf = evh->next_buffer->data))
524 evh->next_buffer = (evh->next_buffer->next) ? evh->next_buffer->next : evh->pp_buffer_list;
528 vbuf->content_r.w = width;
529 vbuf->content_r.h = height;
533 if (l == evh->next_buffer)
535 VWR("all video framebuffers in use (max:%d)", evh->ec, BUFFER_MAX_COUNT);
544 _e_video_hwc_pp_commit(E_Video_Hwc_PP *pp, E_Comp_Wl_Video_Buf *input_buffer, E_Comp_Wl_Video_Buf *pp_buffer, unsigned int transform)
550 info.src_config.size.h = input_buffer->width_from_pitch;
551 info.src_config.size.v = input_buffer->height_from_size;
552 info.src_config.pos.x = input_buffer->content_r.x;
553 info.src_config.pos.y = input_buffer->content_r.y;
554 info.src_config.pos.w = input_buffer->content_r.w;
555 info.src_config.pos.h = input_buffer->content_r.h;
556 info.src_config.format = input_buffer->tbmfmt;
557 info.dst_config.size.h = pp_buffer->width_from_pitch;
558 info.dst_config.size.v = pp_buffer->height_from_size;
559 info.dst_config.pos.w = pp_buffer->content_r.w;
560 info.dst_config.pos.h = pp_buffer->content_r.h;
561 info.dst_config.format = pp_buffer->tbmfmt;
562 info.transform = transform;
564 if (memcmp(&pp->info, &info, sizeof info) != 0)
566 memcpy(&pp->info, &info, sizeof info);
567 err = tdm_pp_set_info(pp->tdm_handle, &info);
568 if (err != TDM_ERROR_NONE)
570 VER("tdm_pp_set_info() failed", NULL);
575 err = tdm_pp_attach(pp->tdm_handle, input_buffer->tbm_surface, pp_buffer->tbm_surface);
576 if (err != TDM_ERROR_NONE)
578 VER("tdm_pp_attach() failed", NULL);
582 err = tdm_pp_commit(pp->tdm_handle);
583 if (err != TDM_ERROR_NONE)
585 VER("tdm_pp_commit() failed", NULL);
593 _e_video_hwc_pp_render(E_Video_Hwc *evh, E_Comp_Wl_Buffer *comp_buffer)
595 E_Comp_Wl_Video_Buf *input_buffer, *pp_buffer;
600 evh->pp = _e_video_hwc_pp_create(e_comp->e_comp_screen->tdisplay, evh);
605 res = _e_video_hwc_pp_size_limit_check(evh->pp,
606 &evh->geo.input_r, &evh->geo.tdm.output_r);
610 input_buffer = _e_video_hwc_pp_input_buffer_get(evh, comp_buffer);
614 pp_buffer = _e_video_hwc_pp_buffer_get(evh,
615 evh->geo.tdm.output_r.w,
616 evh->geo.tdm.output_r.h);
620 e_comp_wl_video_buffer_set_use(pp_buffer, EINA_TRUE);
621 e_comp_wl_buffer_reference(&input_buffer->buffer_ref, input_buffer->comp_buffer);
623 res = _e_video_hwc_pp_commit(evh->pp, input_buffer, pp_buffer, evh->geo.tdm.transform);
630 e_comp_wl_video_buffer_unref(input_buffer);
633 /* End of PP implementation */
636 _e_video_hwc_can_commit(E_Video_Hwc *evh)
638 if (e_output_dpms_get(evh->e_output))
641 return _e_video_hwc_client_visible_get(evh->ec);
645 _e_video_hwc_current_fb_update(E_Video_Hwc *evh)
647 tbm_surface_h displaying_buffer;
649 EINA_SAFETY_ON_NULL_RETURN_VAL(evh, EINA_FALSE);
651 if ((evh->committed_vbuf) &&
652 (_e_video_hwc_can_commit(evh)))
654 displaying_buffer = evh->backend.displaying_buffer_get(evh);
656 if (evh->committed_vbuf->tbm_surface != displaying_buffer)
660 /* client can attachs the same wl_buffer twice. */
661 if ((evh->current_fb) &&
662 (VBUF_IS_VALID(evh->current_fb)) &&
663 (evh->committed_vbuf != evh->current_fb))
665 e_comp_wl_video_buffer_set_use(evh->current_fb, EINA_FALSE);
667 if (evh->current_fb->comp_buffer)
668 e_comp_wl_buffer_reference(&evh->current_fb->buffer_ref, NULL);
671 evh->current_fb = evh->committed_vbuf;
672 evh->committed_vbuf = NULL;
674 VDB("current_fb(%d)", evh->ec, MSTAMP(evh->current_fb));
680 _e_video_hwc_buffer_enqueue(E_Video_Hwc *evh, E_Comp_Wl_Video_Buf *vbuf)
682 /* Remove enqueued video buffer first. */
683 evh->bqueue = eina_list_remove(evh->bqueue, vbuf);
684 evh->bqueue = eina_list_append(evh->bqueue, vbuf);
685 VDB("There are waiting fbs more than 1", evh->ec);
688 static E_Comp_Wl_Video_Buf *
689 _e_video_hwc_buffer_dequeue(E_Video_Hwc *evh)
691 E_Comp_Wl_Video_Buf *vbuf;
696 vbuf = eina_list_nth(evh->bqueue, 0);
697 evh->bqueue = eina_list_remove(evh->bqueue, vbuf);
703 _e_video_hwc_wait_buffer_commit(E_Video_Hwc *evh)
705 E_Comp_Wl_Video_Buf *vbuf;
707 /* committed_vbuf has to be null */
708 EINA_SAFETY_ON_FALSE_RETURN(evh->committed_vbuf == NULL);
710 vbuf = _e_video_hwc_buffer_dequeue(evh);
714 _e_video_hwc_buffer_commit(evh, vbuf);
718 _e_video_hwc_buffer_commit(E_Video_Hwc *evh, E_Comp_Wl_Video_Buf *vbuf)
720 /* Send a message 'wl_surface.frame', right before commit a buffer to
722 e_pixmap_image_clear(evh->ec->pixmap, EINA_TRUE);
724 if ((evh->committed_vbuf) && (evh->committed_vbuf != evh->current_fb))
726 e_comp_wl_video_buffer_set_use(evh->committed_vbuf, EINA_FALSE);
727 e_comp_wl_buffer_reference(&evh->committed_vbuf->buffer_ref, NULL);
730 evh->committed_vbuf = vbuf;
732 if (!_e_video_hwc_can_commit(evh))
735 if (!evh->backend.buffer_commit(evh, vbuf))
741 _e_video_hwc_current_fb_update(evh);
742 _e_video_hwc_wait_buffer_commit(evh);
746 _e_video_hwc_buffer_show(E_Video_Hwc *evh, E_Comp_Wl_Video_Buf *vbuf, unsigned int transform)
748 vbuf->content_t = transform;
750 e_comp_wl_video_buffer_set_use(vbuf, EINA_TRUE);
752 /* 'comp_buffer' of vbuf can be null in case of pp buffer */
753 if (vbuf->comp_buffer)
754 e_comp_wl_buffer_reference(&vbuf->buffer_ref, vbuf->comp_buffer);
756 if ((evh->hwc_policy == E_HWC_POLICY_PLANES) && (evh->committed_vbuf))
757 _e_video_hwc_buffer_enqueue(evh, vbuf);
759 _e_video_hwc_buffer_commit(evh, vbuf);
763 _e_video_hwc_hide(E_Video_Hwc *evh)
765 E_Comp_Wl_Video_Buf *vbuf;
767 if ((evh->current_fb) || (evh->committed_vbuf))
770 e_comp_wl_video_buffer_set_use(evh->current_fb, EINA_FALSE);
772 if (evh->committed_vbuf)
774 e_comp_wl_video_buffer_set_use(evh->committed_vbuf, EINA_FALSE);
775 e_comp_wl_buffer_reference(&evh->committed_vbuf->buffer_ref,
777 evh->committed_vbuf = NULL;
780 evh->backend.buffer_commit(evh, NULL);
783 if (evh->old_comp_buffer)
784 evh->old_comp_buffer = NULL;
786 EINA_LIST_FREE(evh->bqueue, vbuf)
787 e_comp_wl_video_buffer_set_use(vbuf, EINA_FALSE);
790 static Eina_Rectangle
791 _screen_rect_get(E_Zone *zone, enum wl_output_transform transform)
795 switch (transform & 0x3)
797 case WL_OUTPUT_TRANSFORM_90:
798 case WL_OUTPUT_TRANSFORM_270:
799 EINA_RECTANGLE_SET(&ret, zone->x, zone->y, zone->h, zone->w);
802 EINA_RECTANGLE_SET(&ret, zone->x, zone->y, zone->w, zone->h);
810 _point_translate(Evas_Point *point, int x_axis, int y_axis)
817 _rect_to_points(Eina_Rectangle *rect, Evas_Point points[2])
819 points[0].x = rect->x;
820 points[0].y = rect->y;
821 points[1].x = rect->x + rect->w;
822 points[1].y = rect->y + rect->h;
826 _points_to_rect(Evas_Point points[2], Eina_Rectangle *rect)
828 rect->x = MIN(points[0].x, points[1].x);
829 rect->y = MIN(points[0].y, points[1].y);
830 rect->w = MAX(points[0].x, points[1].x) - rect->x;
831 rect->h = MAX(points[0].y, points[1].y) - rect->y;
834 /* Translate ox/oy position of output_rect-local coordinates to
835 * new position of buffer_rect-local coordinates. */
837 _output_to_buffer_point(Eina_Rectangle *output_rect, Eina_Rectangle *buffer_rect, enum wl_output_transform buffer_transform, int ox, int oy, int *bx, int *by)
839 float ratio_w, ratio_h;
841 switch (buffer_transform)
843 case WL_OUTPUT_TRANSFORM_NORMAL:
847 case WL_OUTPUT_TRANSFORM_90:
848 *bx = oy, *by = output_rect->w - ox;
850 case WL_OUTPUT_TRANSFORM_180:
851 *bx = output_rect->w - ox, *by = output_rect->h - oy;
853 case WL_OUTPUT_TRANSFORM_270:
854 *bx = output_rect->h - oy, *by = ox;
856 case WL_OUTPUT_TRANSFORM_FLIPPED:
857 *bx = output_rect->w - ox, *by = oy;
859 case WL_OUTPUT_TRANSFORM_FLIPPED_90:
862 case WL_OUTPUT_TRANSFORM_FLIPPED_180:
863 *bx = ox, *by = output_rect->h - oy;
865 case WL_OUTPUT_TRANSFORM_FLIPPED_270:
866 *bx = output_rect->h - oy, *by = output_rect->w - ox;
870 if (buffer_transform & 0x1)
872 ratio_w = (float)buffer_rect->w / output_rect->h;
873 ratio_h = (float)buffer_rect->h / output_rect->w;
877 ratio_w = (float)buffer_rect->w / output_rect->w;
878 ratio_h = (float)buffer_rect->h / output_rect->h;
881 *bx = (*bx) * ratio_w + buffer_rect->x;
882 *by = (*by) * ratio_h + buffer_rect->y;
885 /* Cropping source/destination of viewport.
886 * @in zone A E_Zone instance to be used to get screen rectangle.
887 * @inout input_r A Source of viewport to be set to tdm_info.src_config
888 * tdm.output_r A Destination of viewport to be set to tdm_info.dst_pos
889 * tdm.output_r will be cropped by given region of zone, and then input_r will be
890 * cropped accordingly.
893 _e_video_hwc_viewport_crop_by_screen(E_Video_Hwc_Geometry *in_out, E_Zone *zone, enum wl_output_transform output_transform)
895 Eina_Rectangle *output_rect, *buffer_rect;
896 Eina_Rectangle cropped_output_rect, screen_rect;
897 Evas_Point points[2];
899 buffer_rect = &in_out->input_r;
900 output_rect = &in_out->tdm.output_r;
901 screen_rect = _screen_rect_get(zone, output_transform);
903 /* No need to crop in case output region doesn't stick out of screen region. */
904 if (E_CONTAINS(screen_rect.x, screen_rect.y,
905 screen_rect.w, screen_rect.h,
906 output_rect->x, output_rect->y,
907 output_rect->w, output_rect->h))
910 EINA_RECTANGLE_SET(&cropped_output_rect,
911 output_rect->x, output_rect->y,
912 output_rect->w, output_rect->h);
913 if (!eina_rectangle_intersection(&cropped_output_rect, &screen_rect))
915 VER("Video won't be displayed because there is no intersection between "
916 "screen region and output region.\n"
917 "\toutput(%d,%d %dx%d) screen(%d,%d %dx%d)",
918 NULL, EINA_RECTANGLE_ARGS(output_rect),
919 EINA_RECTANGLE_ARGS(&screen_rect));
920 EINA_RECTANGLE_SET(output_rect, 0, 0, 0, 0);
921 /* NOTE: Does buffer_rect need to be handled as well? */
925 VIN("Crop video viewport by screen", NULL);
926 VIN("Screen(%d,%d %dx%d)", NULL, EINA_RECTANGLE_ARGS(&screen_rect));
927 VIN("Viewport: source of buffer(%d,%d %dx%d) destination of screen(%d,%d %dx%d)",
928 NULL, EINA_RECTANGLE_ARGS(buffer_rect), EINA_RECTANGLE_ARGS(output_rect));
930 _rect_to_points(&cropped_output_rect, points);
932 /* Get points of cropped rectangle mapped on output_rect-local coordinates. */
933 _point_translate(&points[0], output_rect->x, output_rect->y);
934 _point_translate(&points[1], output_rect->x, output_rect->y);
936 /* Calculate new points of source buffer for cropped output */
937 _output_to_buffer_point(output_rect, buffer_rect,
939 points[0].x, points[0].y,
940 &points[0].x, &points[0].y);
941 _output_to_buffer_point(output_rect, buffer_rect,
943 points[1].x, points[1].y,
944 &points[1].x, &points[1].y);
946 _points_to_rect(points, buffer_rect);
948 EINA_RECTANGLE_SET(output_rect,
949 cropped_output_rect.x, cropped_output_rect.y,
950 cropped_output_rect.w, cropped_output_rect.h);
952 VIN("Cropped source of buffer(%d,%d %dx%d)",
953 NULL, EINA_RECTANGLE_ARGS(buffer_rect));
954 VIN("Cropped destination of screen(%d,%d %dx%d)",
955 NULL, EINA_RECTANGLE_ARGS(output_rect));
959 _e_video_hwc_geometry_tdm_config_update(E_Client *ec, E_Video_Hwc_Geometry *out)
963 E_Comp_Wl_Output *output;
968 topmost = e_comp_wl_topmost_parent_get(ec);
969 EINA_SAFETY_ON_NULL_GOTO(topmost, normal);
971 outputs = e_comp_wl_output_find_all(topmost);
975 /* NOTE: Not support multi output yet. So we use the first output. */
976 output = eina_array_data_get(outputs, 0);
977 eina_array_free(outputs);
979 zone = topmost->zone;
980 EINA_SAFETY_ON_NULL_GOTO(zone, normal);
982 tran = out->transform & 0x3;
983 flip = out->transform & 0x4;
984 transform = flip + (tran + output->transform) % 4;
987 case WL_OUTPUT_TRANSFORM_90:
988 out->tdm.transform = TDM_TRANSFORM_270;
990 case WL_OUTPUT_TRANSFORM_180:
991 out->tdm.transform = TDM_TRANSFORM_180;
993 case WL_OUTPUT_TRANSFORM_270:
994 out->tdm.transform = TDM_TRANSFORM_90;
996 case WL_OUTPUT_TRANSFORM_FLIPPED:
997 out->tdm.transform = TDM_TRANSFORM_FLIPPED;
999 case WL_OUTPUT_TRANSFORM_FLIPPED_90:
1000 out->tdm.transform = TDM_TRANSFORM_FLIPPED_270;
1002 case WL_OUTPUT_TRANSFORM_FLIPPED_180:
1003 out->tdm.transform = TDM_TRANSFORM_FLIPPED_180;
1005 case WL_OUTPUT_TRANSFORM_FLIPPED_270:
1006 out->tdm.transform = TDM_TRANSFORM_FLIPPED_90;
1008 case WL_OUTPUT_TRANSFORM_NORMAL:
1010 out->tdm.transform = TDM_TRANSFORM_NORMAL;
1014 if (output->transform % 2)
1016 if (out->tdm.transform == TDM_TRANSFORM_FLIPPED)
1017 out->tdm.transform = TDM_TRANSFORM_FLIPPED_180;
1018 else if (out->tdm.transform == TDM_TRANSFORM_FLIPPED_90)
1019 out->tdm.transform = TDM_TRANSFORM_FLIPPED_270;
1020 else if (out->tdm.transform == TDM_TRANSFORM_FLIPPED_180)
1021 out->tdm.transform = TDM_TRANSFORM_FLIPPED;
1022 else if (out->tdm.transform == TDM_TRANSFORM_FLIPPED_270)
1023 out->tdm.transform = TDM_TRANSFORM_FLIPPED_90;
1026 if (output->transform == 0)
1027 out->tdm.output_r = out->output_r;
1029 e_comp_wl_rect_convert(zone->w, zone->h, output->transform, 1,
1030 out->output_r.x, out->output_r.y,
1031 out->output_r.w, out->output_r.h,
1032 &out->tdm.output_r.x, &out->tdm.output_r.y,
1033 &out->tdm.output_r.w, &out->tdm.output_r.h);
1035 VDB("geomtry: screen(%d,%d %dx%d | %d) => %d => physical(%d,%d %dx%d | %d)",
1036 ec, EINA_RECTANGLE_ARGS(&out->output_r), out->transform, transform,
1037 EINA_RECTANGLE_ARGS(&out->tdm.output_r), out->tdm.transform);
1039 _e_video_hwc_viewport_crop_by_screen(out, zone, output->transform);
1043 out->tdm.output_r = out->output_r;
1044 out->tdm.transform = out->transform;
1048 * Convert given four coordinates to elements of rectangle
1049 * @in p[4] Coordinates to be converted
1050 * @out rect x, y, width, and height
1051 * transform Angle which represents TDM_TRANSFORM of rectangle
1052 * @return EINA_FALSE in following case, otherwise EINA_TRUE.
1053 * 1. The given coordinates are not represented by rectangle.
1054 * 2. All angles except for 0, 90, 180, 270.
1059 #define IS_CLOSE(x, y, m) (abs((x - y)) < m)
1061 #ifdef IS_PORTRAIT_RECT
1062 #undef IS_PORTRAIT_RECT
1064 #define IS_PORTRAIT_RECT(p, m) \
1065 (IS_CLOSE(p[0].y, p[1].y, m) && IS_CLOSE(p[1].x, p[2].x, m) && \
1066 IS_CLOSE(p[2].y, p[3].y, m) && IS_CLOSE(p[3].x, p[0].x, m))
1068 #ifdef IS_LANDSCAPE_RECT
1069 #undef IS_LANDSCAPE_RECT
1071 #define IS_LANDSCAPE_RECT(p, m) \
1072 (IS_CLOSE(p[0].x, p[1].x, m) && IS_CLOSE(p[1].y, p[2].y, m) && \
1073 IS_CLOSE(p[2].x, p[3].x, m) && IS_CLOSE(p[3].y, p[0].y, m))
1075 #ifdef VERTICES_TO_RECT
1076 #undef VERTICES_TO_RECT
1078 #define VERTICES_TO_RECT(r, vs) \
1079 (r)->x = MIN(vs[0].x, vs[2].x); \
1080 (r)->y = MIN(vs[0].y, vs[2].y); \
1081 (r)->w = MAX(vs[0].x, vs[2].x) - (r)->x; \
1082 (r)->h = MAX(vs[0].y, vs[2].y) - (r)->y
1085 _e_video_hwc_coords_to_rectangle_convert(Evas_Point p[4], Eina_Rectangle *rect, uint *transform)
1087 Eina_Rectangle boundary = {0,};
1088 Eina_Bool ret = EINA_FALSE;
1091 VERTICES_TO_RECT(&boundary, p);
1092 if ((boundary.w < 2) || (boundary.h < 2))
1095 if (IS_PORTRAIT_RECT(p, margin))
1098 if ((p[0].x < p[2].x) && (p[0].y < p[2].y))
1100 *transform = TDM_TRANSFORM_NORMAL;
1101 VERTICES_TO_RECT(rect, p);
1104 else if ((p[0].x > p[2].x) && (p[0].y > p[2].y))
1106 *transform = TDM_TRANSFORM_180;
1107 VERTICES_TO_RECT(rect, p);
1111 else if (IS_LANDSCAPE_RECT(p, margin))
1114 if ((p[0].x > p[2].x) && (p[0].y < p[2].y))
1116 *transform = TDM_TRANSFORM_90;
1117 VERTICES_TO_RECT(rect, p);
1120 else if ((p[0].x < p[2].x) && (p[0].y > p[2].y))
1122 *transform = TDM_TRANSFORM_270;
1123 VERTICES_TO_RECT(rect, p);
1132 #undef IS_PORTRAIT_RECT
1133 #undef IS_LANDSCAPE_RECT
1134 #undef VERTICES_TO_RECT
1137 * Merge transform value of E_Comp_Wl_Buffer_Viewport with given transform.
1138 * @in vp A 'E_Comp_Wl_Buffer_Viewport' instance to be merged
1139 * transform A transform operand to be merged
1140 * @return merged result transform
1143 _e_video_hwc_transform_merge_with_buffer_viewport(E_Comp_Wl_Buffer_Viewport *vp, uint transform)
1145 int vp_transform, flip;
1147 vp_transform = vp->buffer.transform & 0x3;
1148 flip = vp->buffer.transform & 0x4;
1150 return (flip + (transform + vp_transform) % 4);
1154 _e_video_hwc_geometry_map_apply(E_Client *ec, E_Video_Hwc_Geometry *out)
1158 Eina_Rectangle output_r;
1162 EINA_SAFETY_ON_NULL_RETURN_VAL(ec, EINA_FALSE);
1163 EINA_SAFETY_ON_NULL_RETURN_VAL(ec->frame, EINA_FALSE);
1164 EINA_SAFETY_ON_NULL_RETURN_VAL(out, EINA_FALSE);
1166 m = e_client_map_get(ec);
1167 if (!m) return EINA_TRUE;
1169 /* If frame has map, it means that ec's geometry is decided by map's geometry.
1170 * ec->x,y,w,h and ec->client.x,y,w,h is not useful.
1173 for (i = 0; i < 4; i++)
1174 e_map_point_coord_get(m, i, &p[i].x, &p[i].y, NULL);
1176 if (!_e_video_hwc_coords_to_rectangle_convert(p, &output_r, &transform))
1178 VIN("Cannot convert given coords to rectangle.\n"
1179 "p1(%d %d) p2(%d %d) p3(%d %d) p4(%d %d)",
1180 ec, p[0].x, p[0].y, p[1].x, p[1].y,
1181 p[2].x, p[2].y, p[3].x, p[3].y);
1186 /* NOTE Merge transform value from evas_map with E_Comp_Wl_Buffer_Viewport's one.
1187 * Since buffer.transform isn't applied using evas_map,
1188 * it has to be taken into account here to apply buffer.transform
1189 * and rotation of e_client_transform together. */
1191 _e_video_hwc_transform_merge_with_buffer_viewport(&ec->comp_data->scaler.buffer_viewport,
1194 if ((!memcmp(&out->output_r, &output_r, sizeof(Eina_Rectangle))) &&
1195 (out->transform == transform))
1201 VDB("frame(%p) m(%p) output(%d,%d %dx%d) trans(%d) => (%d,%d %dx%d) trans(%d)",
1202 ec, ec->frame, m, EINA_RECTANGLE_ARGS(&out->output_r), out->transform,
1203 EINA_RECTANGLE_ARGS(&output_r), transform);
1205 out->output_r = output_r;
1206 out->transform = transform;
1213 _e_video_hwc_cb_post_client_idler_before(void *data)
1217 Eina_Bool render = EINA_FALSE;
1218 Eina_Bool render_fail = EINA_FALSE;
1222 if (evh->render.map)
1224 evh->render.map = EINA_FALSE;
1225 render = _e_video_hwc_geometry_map_apply(evh->ec, &evh->geo);
1228 if (evh->render.topmost_viewport)
1230 evh->render.topmost_viewport = EINA_FALSE;
1231 topmost = e_comp_wl_topmost_parent_get(evh->ec);
1233 e_comp_wl_viewport_apply(topmost);
1237 if ((render) || (evh->render.redraw))
1239 evh->render.redraw = EINA_FALSE;
1240 render_fail = !_e_video_hwc_render(evh, __FUNCTION__);
1243 if ((render_fail || e_video_debug_display_primary_plane_value_get()) && (evh->render_fail.cb))
1245 evh->render_fail.walking = EINA_TRUE;
1246 evh->render_fail.cb(evh->ecv);
1247 evh->render_fail.walking = EINA_FALSE;
1251 VIN("Delete HWC interface", evh->ec);
1252 evh->backend.destroy(evh);
1256 E_FREE_FUNC(evh->render.post_client_idler_before_hook, e_main_hook_del);
1260 _e_video_hwc_render_queue(E_Video_Hwc *evh)
1262 if (evh->render.post_client_idler_before_hook)
1265 evh->render.post_client_idler_before_hook =
1266 e_main_hook_add(E_MAIN_HOOK_POST_CLIENT_IDLER_BEFORE,
1267 _e_video_hwc_cb_post_client_idler_before,
1272 _e_video_hwc_cb_evas_resize(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
1274 E_Video_Hwc *evh = data;
1276 /* Since video content will be displayed on the overlay plane,
1277 * it's reasonable to keep the size of composite object to 1x1.
1278 * Otherwise, it will cause memory usage to be increased unnecessarily. */
1279 evas_object_resize(evh->ec->frame, 1, 1);
1281 evh->render.map = EINA_TRUE;
1282 _e_video_hwc_render_queue(evh);
1286 _e_video_hwc_cb_evas_move(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
1288 E_Video_Hwc *evh = data;
1290 evh->render.map = EINA_TRUE;
1291 _e_video_hwc_render_queue(evh);
1295 _e_video_hwc_input_buffer_valid(E_Video_Hwc *evh, E_Comp_Wl_Buffer *comp_buffer)
1297 E_Comp_Wl_Video_Buf *vbuf;
1300 EINA_LIST_FOREACH(evh->input_buffer_list, l, vbuf)
1302 tbm_surface_h tbm_surf;
1304 uint32_t size = 0, offset = 0, pitch = 0;
1306 if (!vbuf->comp_buffer) continue;
1307 if (vbuf->resource == comp_buffer->resource)
1309 WRN("got wl_buffer@%d twice", wl_resource_get_id(comp_buffer->resource));
1313 tbm_surf = wayland_tbm_server_get_surface(e_comp->wl_comp_data->tbm.server, comp_buffer->resource);
1314 bo = tbm_surface_internal_get_bo(tbm_surf, 0);
1315 tbm_surface_internal_get_plane_data(tbm_surf, 0, &size, &offset, &pitch);
1317 if (vbuf->names[0] == tbm_bo_export(bo) && vbuf->offsets[0] == offset)
1319 WRN("can tearing: wl_buffer@%d, wl_buffer@%d are same. gem_name(%d)",
1320 wl_resource_get_id(vbuf->resource),
1321 wl_resource_get_id(comp_buffer->resource), vbuf->names[0]);
1328 _e_video_hwc_comp_buffer_tbm_format_get(E_Comp_Wl_Buffer *comp_buffer)
1330 tbm_surface_h tbm_surf;
1332 tbm_surf = wayland_tbm_server_get_surface(e_comp->wl_comp_data->tbm.server, comp_buffer->resource);
1333 EINA_SAFETY_ON_NULL_RETURN_VAL(tbm_surf, 0);
1335 return tbm_surface_get_format(tbm_surf);
1338 static tbm_surface_h
1339 _e_video_hwc_client_tbm_surface_get(E_Client *ec)
1341 E_Comp_Wl_Buffer *comp_buffer;
1342 tbm_surface_h tbmsurf;
1344 comp_buffer = e_pixmap_resource_get(ec->pixmap);
1347 /* No comp buffer */
1351 tbmsurf = wayland_tbm_server_get_surface(e_comp->wl_comp_data->tbm.server,
1352 comp_buffer->resource);
1358 buffer_transform(int width, int height, uint32_t transform, int32_t scale,
1359 int sx, int sy, int *dx, int *dy)
1363 case WL_OUTPUT_TRANSFORM_NORMAL:
1367 case WL_OUTPUT_TRANSFORM_FLIPPED:
1368 *dx = width - sx, *dy = sy;
1370 case WL_OUTPUT_TRANSFORM_90:
1371 *dx = height - sy, *dy = sx;
1373 case WL_OUTPUT_TRANSFORM_FLIPPED_90:
1374 *dx = height - sy, *dy = width - sx;
1376 case WL_OUTPUT_TRANSFORM_180:
1377 *dx = width - sx, *dy = height - sy;
1379 case WL_OUTPUT_TRANSFORM_FLIPPED_180:
1380 *dx = sx, *dy = height - sy;
1382 case WL_OUTPUT_TRANSFORM_270:
1383 *dx = sy, *dy = width - sx;
1385 case WL_OUTPUT_TRANSFORM_FLIPPED_270:
1395 _e_video_hwc_geometry_input_rect_get_with_viewport(tbm_surface_h tbm_surf, E_Comp_Wl_Buffer_Viewport *vp, Eina_Rectangle *out)
1399 int tx1, ty1, tx2, ty2;
1400 int width_from_buffer, height_from_buffer;
1402 bw = tbm_surface_get_width(tbm_surf);
1403 bh = tbm_surface_get_height(tbm_surf);
1404 VDB("TBM buffer size %d %d", NULL, bw, bh);
1406 switch (vp->buffer.transform)
1408 case WL_OUTPUT_TRANSFORM_90:
1409 case WL_OUTPUT_TRANSFORM_270:
1410 case WL_OUTPUT_TRANSFORM_FLIPPED_90:
1411 case WL_OUTPUT_TRANSFORM_FLIPPED_270:
1412 width_from_buffer = bh / vp->buffer.scale;
1413 height_from_buffer = bw / vp->buffer.scale;
1416 width_from_buffer = bw / vp->buffer.scale;
1417 height_from_buffer = bh / vp->buffer.scale;
1421 if (vp->buffer.src_width == wl_fixed_from_int(-1))
1425 x2 = width_from_buffer;
1426 y2 = height_from_buffer;
1430 x1 = wl_fixed_to_int(vp->buffer.src_x);
1431 y1 = wl_fixed_to_int(vp->buffer.src_y);
1432 x2 = wl_fixed_to_int(vp->buffer.src_x + vp->buffer.src_width);
1433 y2 = wl_fixed_to_int(vp->buffer.src_y + vp->buffer.src_height);
1436 VDB("transform(%d) scale(%d) buffer(%dx%d) src(%d,%d %d,%d)",
1437 NULL, vp->buffer.transform, vp->buffer.scale,
1438 width_from_buffer, height_from_buffer,
1439 x1, y1, x2 - x1, y2 - y1);
1441 buffer_transform(width_from_buffer, height_from_buffer,
1442 vp->buffer.transform, vp->buffer.scale, x1, y1, &tx1, &ty1);
1443 buffer_transform(width_from_buffer, height_from_buffer,
1444 vp->buffer.transform, vp->buffer.scale, x2, y2, &tx2, &ty2);
1446 out->x = (tx1 <= tx2) ? tx1 : tx2;
1447 out->y = (ty1 <= ty2) ? ty1 : ty2;
1448 out->w = (tx1 <= tx2) ? tx2 - tx1 : tx1 - tx2;
1449 out->h = (ty1 <= ty2) ? ty2 - ty1 : ty1 - ty2;
1453 _e_video_hwc_geometry_output_rect_get(E_Client *ec, Eina_Rectangle *out)
1455 if (e_comp_wl_subsurface_check(ec))
1456 e_comp_wl_subsurface_global_coord_get(ec, &out->x, &out->y);
1463 out->w = ec->comp_data->width_from_viewport;
1464 out->w = (out->w + 1) & ~1;
1465 out->h = ec->comp_data->height_from_viewport;
1467 e_comp_object_frame_xy_unadjust(ec->frame, out->x, out->y, &out->x, &out->y);
1468 e_comp_object_frame_wh_unadjust(ec->frame, out->w, out->h, &out->w, &out->h);
1471 /* convert from logical screen to physical output */
1473 _e_video_hwc_geometry_viewport_apply(E_Client *ec, E_Video_Hwc_Geometry *out)
1475 E_Comp_Wl_Buffer_Viewport *vp;
1476 tbm_surface_h tbm_surf;
1478 EINA_SAFETY_ON_NULL_RETURN_VAL(ec, EINA_FALSE);
1479 EINA_SAFETY_ON_NULL_RETURN_VAL(out, EINA_FALSE);
1481 tbm_surf = _e_video_hwc_client_tbm_surface_get(ec);
1484 /* No tbm_surface */
1488 vp = &ec->comp_data->scaler.buffer_viewport;
1489 _e_video_hwc_geometry_input_rect_get_with_viewport(tbm_surf, vp, &out->input_r);
1491 _e_video_hwc_geometry_output_rect_get(ec, &out->output_r);
1492 out->transform = vp->buffer.transform;
1494 VDB("geometry(%d,%d %dx%d %d,%d %dx%d %d)", ec,
1495 EINA_RECTANGLE_ARGS(&out->input_r),EINA_RECTANGLE_ARGS(&out->output_r),
1502 _e_video_hwc_geometry_get(E_Client *ec, E_Video_Hwc_Geometry *out)
1504 Eina_Rectangle input_r = {0,};
1506 /* get geometry information with buffer scale, transform and viewport. */
1507 if (!_e_video_hwc_geometry_viewport_apply(ec, out))
1510 _e_video_hwc_geometry_map_apply(ec, out);
1512 _e_video_hwc_geometry_tdm_config_update(ec, out);
1514 e_comp_wl_video_buffer_size_get(ec, &input_r.w, &input_r.h);
1515 // when topmost is not mapped, input size can be abnormal.
1516 // in this case, it will be render by topmost showing.
1517 if (!eina_rectangle_intersection(&out->input_r, &input_r) || (out->input_r.w <= 10 || out->input_r.h <= 10))
1519 VER("input area is empty", ec);
1527 _e_video_hwc_client_parent_viewable_get(E_Client *ec)
1529 E_Client *topmost_parent;
1531 topmost_parent = e_comp_wl_topmost_parent_get(ec);
1533 if (!topmost_parent)
1536 if (topmost_parent == ec)
1538 VDB("There is no video parent surface", ec);
1542 if (!topmost_parent->visible)
1544 VDB("parent(0x%08"PRIxPTR") not viewable", ec,
1545 (Ecore_Window)e_client_util_win_get(topmost_parent));
1549 if (!e_pixmap_resource_get(topmost_parent->pixmap))
1551 VDB("parent(0x%08"PRIxPTR") no comp buffer", ec,
1552 (Ecore_Window)e_client_util_win_get(topmost_parent));
1560 _e_video_hwc_render(E_Video_Hwc *evh, const char *func)
1562 E_Comp_Wl_Buffer *comp_buffer;
1563 E_Comp_Wl_Video_Buf *input_buffer = NULL;
1566 EINA_SAFETY_ON_NULL_GOTO(evh->ec, done);
1568 /* buffer can be NULL when camera/video's mode changed. Do nothing and
1569 * keep previous frame in this case.
1571 if (!evh->ec->pixmap)
1574 if (!_e_video_hwc_client_visible_get(evh->ec))
1576 evh->need_force_render = EINA_TRUE;
1577 _e_video_hwc_hide(evh);
1581 comp_buffer = e_pixmap_resource_get(evh->ec->pixmap);
1582 if (!comp_buffer) goto done;
1584 evh->tbmfmt = _e_video_hwc_comp_buffer_tbm_format_get(comp_buffer);
1586 topmost = e_comp_wl_topmost_parent_get(evh->ec);
1587 EINA_SAFETY_ON_NULL_GOTO(topmost, done);
1589 if(e_comp_wl_viewport_is_changed(topmost))
1591 VIN("need update viewport: apply topmost", evh->ec);
1592 e_comp_wl_viewport_apply(topmost);
1595 if (!_e_video_hwc_geometry_get(evh->ec, &evh->geo))
1597 if(!evh->need_force_render && !_e_video_hwc_client_parent_viewable_get(evh->ec))
1599 VIN("need force render", evh->ec);
1600 evh->need_force_render = EINA_TRUE;
1605 DBG("====================================== (%s)", func);
1606 VDB("old: "GEO_FMT" buf(%p)", evh->ec, GEO_ARG(&evh->old_geo), evh->old_comp_buffer);
1607 VDB("new: "GEO_FMT" buf(%p) %c%c%c%c", evh->ec, GEO_ARG(&evh->geo), comp_buffer, FOURCC_STR(evh->tbmfmt));
1609 if (!memcmp(&evh->old_geo, &evh->geo, sizeof evh->geo) &&
1610 evh->old_comp_buffer == comp_buffer)
1612 /* Try sending 'wl_surface.frame' in case client
1613 * submitted same 'wl_buffer' */
1614 e_pixmap_image_clear(evh->ec->pixmap, EINA_TRUE);
1618 evh->need_force_render = EINA_FALSE;
1620 _e_video_hwc_input_buffer_valid(evh, comp_buffer);
1622 if ((evh->geo.tdm.output_r.w == 0) ||
1623 (evh->geo.tdm.output_r.h == 0))
1625 VIN("Hide video by setting 0 dst size", evh->ec);
1626 e_pixmap_image_clear(evh->ec->pixmap, EINA_TRUE);
1627 _e_video_hwc_hide(evh);
1628 evh->old_geo = evh->geo;
1631 if (!evh->backend.check_if_pp_needed(evh))
1633 /* 1. non converting case */
1634 input_buffer = _e_video_hwc_input_buffer_get(evh, comp_buffer);
1638 _e_video_hwc_buffer_show(evh, input_buffer, evh->geo.tdm.transform);
1642 if (!_e_video_hwc_pp_render(evh, comp_buffer))
1644 VER("Failed to PP render", evh->ec);
1645 e_pixmap_image_clear(evh->ec->pixmap, EINA_TRUE);
1650 evh->old_geo = evh->geo;
1651 evh->old_comp_buffer = comp_buffer;
1653 DBG("======================================.");
1662 _e_video_hwc_child_client_get(E_Client *ec)
1664 E_Client *subc = NULL;
1666 if (!ec) return NULL;
1667 if (e_object_is_del(E_OBJECT(ec))) return NULL;
1668 if (!ec->comp_data) return NULL;
1670 if (e_client_video_hw_composition_check(ec)) return ec;
1672 EINA_LIST_FOREACH(ec->comp_data->sub.below_list, l, subc)
1674 E_Client *temp= NULL;
1675 if (!subc->comp_data || e_object_is_del(E_OBJECT(subc))) continue;
1676 temp = _e_video_hwc_child_client_get(subc);
1677 if(temp) return temp;
1684 _e_video_hwc_cb_client_show(void *data, int type, void *event)
1686 E_Event_Client *ev = event;
1688 E_Client *video_ec = NULL;
1689 E_Video_Hwc *evh = NULL;
1691 EINA_SAFETY_ON_NULL_RETURN_VAL(ev, ECORE_CALLBACK_PASS_ON);
1692 EINA_SAFETY_ON_NULL_RETURN_VAL(ev->ec, ECORE_CALLBACK_PASS_ON);
1695 if (!ec->comp_data) return ECORE_CALLBACK_PASS_ON;
1697 video_ec = _e_video_hwc_child_client_get(ec);
1698 if (!video_ec) return ECORE_CALLBACK_PASS_ON;
1701 if (!evh) return ECORE_CALLBACK_PASS_ON;
1703 if (evh->old_comp_buffer)
1705 VIN("video already rendering..", evh->ec);
1706 return ECORE_CALLBACK_PASS_ON;
1709 if (ec == e_comp_wl_topmost_parent_get(evh->ec))
1711 VIN("video need rendering..", evh->ec);
1712 evh->render.topmost_viewport = EINA_TRUE;
1713 _e_video_hwc_render_queue(evh);
1716 return ECORE_CALLBACK_PASS_ON;
1720 _e_video_hwc_cb_client_buffer_change(void *data, int type, void *event)
1723 E_Event_Client *ev = event;
1726 EINA_SAFETY_ON_NULL_RETURN_VAL(ev, ECORE_CALLBACK_PASS_ON);
1727 EINA_SAFETY_ON_NULL_RETURN_VAL(ev->ec, ECORE_CALLBACK_PASS_ON);
1733 return ECORE_CALLBACK_PASS_ON;
1735 evh->render.redraw = EINA_TRUE;
1736 _e_video_hwc_render_queue(evh);
1738 return ECORE_CALLBACK_PASS_ON;
1742 _e_video_hwc_cb_evas_show(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
1750 if (evh->need_force_render)
1752 VIN("video forcely rendering..", evh->ec);
1753 evh->render.redraw = EINA_TRUE;
1754 _e_video_hwc_render_queue(evh);
1757 /* if stand_alone is true, not show */
1758 if ((e_comp_wl_subsurface_check(ec)) &&
1759 (e_comp_wl_subsurface_stand_alone_mode_get(ec)))
1762 if (evh->current_fb)
1763 _e_video_hwc_buffer_show(evh, evh->current_fb, evh->current_fb->content_t);
1767 _e_video_hwc_cb_surface_viewport(struct wl_listener *listener, void *data)
1771 evh = container_of(listener, E_Video_Hwc, surface_viewport_listener);
1773 VDB("Apply Viewport Signal", evh->ec);
1774 evh->render.redraw = EINA_TRUE;
1775 _e_video_hwc_render_queue(evh);
1779 _e_video_hwc_client_event_init(E_Video_Hwc *evh)
1781 /* In order to try to redraw video whenever viewport changes. */
1782 evh->surface_viewport_listener.notify = _e_video_hwc_cb_surface_viewport;
1783 if (evh->ec->comp_data)
1785 wl_signal_add(&evh->ec->comp_data->apply_viewport_signal,
1786 &evh->surface_viewport_listener);
1789 evas_object_event_callback_add(evh->ec->frame, EVAS_CALLBACK_SHOW,
1790 _e_video_hwc_cb_evas_show, evh);
1791 evas_object_event_callback_add(evh->ec->frame, EVAS_CALLBACK_RESIZE,
1792 _e_video_hwc_cb_evas_resize, evh);
1793 evas_object_event_callback_add(evh->ec->frame, EVAS_CALLBACK_MOVE,
1794 _e_video_hwc_cb_evas_move, evh);
1796 E_LIST_HANDLER_APPEND(evh->ec_event_handler, E_EVENT_CLIENT_SHOW,
1797 _e_video_hwc_cb_client_show, evh);
1798 E_LIST_HANDLER_APPEND(evh->ec_event_handler, E_EVENT_CLIENT_BUFFER_CHANGE,
1799 _e_video_hwc_cb_client_buffer_change, evh);
1803 _e_video_hwc_client_event_deinit(E_Video_Hwc *evh)
1805 /* links for listener have to be removed only in case comp_data is valid. */
1806 if (evh->ec->comp_data)
1807 wl_list_remove(&evh->surface_viewport_listener.link);
1809 evas_object_event_callback_del_full(evh->ec->frame, EVAS_CALLBACK_SHOW,
1810 _e_video_hwc_cb_evas_show, evh);
1811 evas_object_event_callback_del_full(evh->ec->frame, EVAS_CALLBACK_RESIZE,
1812 _e_video_hwc_cb_evas_resize, evh);
1813 evas_object_event_callback_del_full(evh->ec->frame, EVAS_CALLBACK_MOVE,
1814 _e_video_hwc_cb_evas_move, evh);
1816 E_FREE_LIST(evh->ec_event_handler, ecore_event_handler_del);
1820 _e_video_hwc_iface_destroy(E_Video_Comp_Iface *iface)
1822 E_Comp_Wl_Video_Buf *vbuf;
1823 Eina_List *l = NULL, *ll = NULL;
1827 _e_video_hwc_hide(evh);
1829 EINA_LIST_FOREACH_SAFE(evh->input_buffer_list, l, ll, vbuf)
1831 e_comp_wl_video_buffer_set_use(vbuf, EINA_FALSE);
1832 e_comp_wl_video_buffer_unref(vbuf);
1835 EINA_LIST_FOREACH_SAFE(evh->pp_buffer_list, l, ll, vbuf)
1837 e_comp_wl_video_buffer_set_use(vbuf, EINA_FALSE);
1838 e_comp_wl_video_buffer_unref(vbuf);
1841 if (evh->input_buffer_list)
1843 if (evh->pp_buffer_list)
1846 /* destroy converter second */
1847 E_FREE_FUNC(evh->pp, _e_video_hwc_pp_destroy);
1849 if (e_comp_object_mask_has(evh->ec->frame))
1850 e_comp_object_mask_set(evh->ec->frame, EINA_FALSE);
1852 _e_video_hwc_client_event_deinit(evh);
1854 E_FREE_FUNC(evh->render.post_client_idler_before_hook, e_main_hook_del);
1856 if (evh->render_fail.walking)
1858 evh->deleted = EINA_TRUE;
1862 evh->backend.destroy(evh);
1866 _e_video_hwc_iface_property_get(E_Video_Comp_Iface *iface, unsigned int id, tdm_value *value)
1870 return evh->backend.property_get(evh, id, value);
1874 _e_video_hwc_iface_property_set(E_Video_Comp_Iface *iface, unsigned int id, tdm_value value, Eina_Bool sync)
1878 return evh->backend.property_set(evh, id, value, sync);
1882 _e_video_hwc_iface_property_delay_set(E_Video_Comp_Iface *iface, unsigned int id, tdm_value value)
1886 if (evh->hwc_policy != E_HWC_POLICY_PLANES)
1888 return e_video_hwc_planes_property_delay_set(evh, id, value);
1892 _e_video_hwc_iface_available_properties_get(E_Video_Comp_Iface *iface, const tdm_prop **props, int *count)
1896 return evh->backend.available_properties_get(evh, props, count);
1900 _e_video_hwc_iface_info_get(E_Video_Comp_Iface *iface, E_Client_Video_Info *info)
1904 if (evh->hwc_policy != E_HWC_POLICY_WINDOWS)
1906 return e_video_hwc_windows_info_get(evh, info);
1910 _e_video_hwc_iface_commit_data_release(E_Video_Comp_Iface *iface, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec)
1914 if (evh->hwc_policy != E_HWC_POLICY_WINDOWS)
1916 return e_video_hwc_windows_commit_data_release(evh, sequence, tv_sec, tv_usec);
1919 static tbm_surface_h
1920 _e_video_hwc_iface_tbm_surface_get(E_Video_Comp_Iface *iface)
1924 if (evh->hwc_policy != E_HWC_POLICY_WINDOWS)
1926 return e_video_hwc_windows_tbm_surface_get(evh);
1929 static E_Video_Hwc *
1930 _e_video_hwc_create(E_Client *ec)
1933 E_Hwc_Policy hwc_policy;
1937 zone = e_comp_zone_find_by_ec(ec);
1938 EINA_SAFETY_ON_NULL_RETURN_VAL(zone, NULL);
1940 output = e_output_find(zone->output_id);
1943 VER("Failed to find 'E_Output': id %s", ec, zone->output_id);
1947 hwc_policy = e_zone_video_hwc_policy_get(zone);
1948 if (hwc_policy == E_HWC_POLICY_PLANES)
1949 evh = e_video_hwc_planes_create(output, ec);
1950 else if (hwc_policy == E_HWC_POLICY_WINDOWS)
1951 evh = e_video_hwc_windows_create(output, ec);
1954 VER("Unknown HWC mode %d", ec, hwc_policy);
1960 VER("Failed to create 'E_Video_Hwc'", ec);
1964 /* Since video content will be displayed on the overlay plane,
1965 * it's reasonable to keep the size of composite object to 1x1.
1966 * Otherwise, it will cause memory usage to be increased unnecessarily. */
1967 evas_object_resize(ec->frame, 1, 1);
1969 evh->hwc_policy = hwc_policy;
1970 evh->e_output = output;
1973 //TODO: shoud this function be called here?
1974 tdm_output_get_available_size(output->toutput, NULL, NULL, NULL, NULL,
1975 &evh->output_align);
1980 EINTERN E_Video_Comp_Iface *
1981 e_video_hwc_iface_create(E_Client_Video *ecv)
1986 ec = e_client_video_ec_get(ecv);
1988 VIN("Create HWC interface", ec);
1990 evh = _e_video_hwc_create(ec);
1994 _e_video_hwc_client_event_init(evh);
1998 evh->iface.destroy = _e_video_hwc_iface_destroy;
1999 evh->iface.property_get = _e_video_hwc_iface_property_get;
2000 evh->iface.property_set = _e_video_hwc_iface_property_set;
2001 evh->iface.property_delay_set = _e_video_hwc_iface_property_delay_set;
2002 evh->iface.available_properties_get = _e_video_hwc_iface_available_properties_get;
2003 evh->iface.info_get = _e_video_hwc_iface_info_get;
2004 evh->iface.commit_data_release = _e_video_hwc_iface_commit_data_release;
2005 evh->iface.tbm_surface_get = _e_video_hwc_iface_tbm_surface_get;
2007 /* This ec is a video client now. */
2008 e_client_video_hw_composition_set(ecv);
2014 e_video_hwc_current_fb_update(E_Video_Hwc *evh)
2016 return _e_video_hwc_current_fb_update(evh);
2020 e_video_hwc_wait_buffer_commit(E_Video_Hwc *evh)
2022 _e_video_hwc_wait_buffer_commit(evh);
2026 e_video_hwc_client_mask_update(E_Video_Hwc *evh)
2029 Eina_Bool punch = EINA_FALSE;
2032 if (e_video_debug_punch_value_get())
2034 else if ((topmost = e_comp_wl_topmost_parent_get(evh->ec)))
2036 /* if it's laid above main surface */
2037 if ((topmost->comp_data) &&
2038 (eina_list_data_find(topmost->comp_data->sub.list, evh->ec)))
2040 /* if it's laid under main surface and main surface is transparent */
2041 else if (topmost->argb)
2043 /* FIXME: the mask obj can be drawn at the wrong position in the beginnig
2044 * time. It happens caused by window manager policy.
2046 if ((topmost->fullscreen || topmost->maximized) &&
2047 (evh->geo.output_r.x == 0 || evh->geo.output_r.y == 0))
2049 e_pixmap_size_get(topmost->pixmap, &bw, &bh);
2051 if (bw > 100 && bh > 100 &&
2052 evh->geo.output_r.w < 100 && evh->geo.output_r.h < 100)
2054 VIN("don't punch. (%dx%d, %dx%d)", evh->ec,
2055 bw, bh, evh->geo.output_r.w, evh->geo.output_r.h);
2066 if (!e_comp_object_mask_has(evh->ec->frame))
2068 e_comp_object_mask_set(evh->ec->frame, EINA_TRUE);
2069 VIN("punched", evh->ec);
2074 if (e_comp_object_mask_has(evh->ec->frame))
2076 e_comp_object_mask_set(evh->ec->frame, EINA_FALSE);
2077 VIN("Un-punched", evh->ec);
2082 /* Sets render fail callback
2084 * @in iface A instance of this composition mode
2085 * @in func The function which will be called.
2087 * This function will be called if rendering is failed.
2088 * Once this callback funtion has been called, callee will try to replace its
2089 * composition interface to another one. */
2091 e_video_hwc_render_fail_callback_set(E_Video_Comp_Iface *iface, E_Video_Hwc_Render_Fail_Cb func)
2095 evh = container_of(iface, E_Video_Hwc, iface);
2099 evh->render_fail.cb = func;