From 7ac8982b9df32fb6b8bebcccf8361ce8132bbabb Mon Sep 17 00:00:00 2001 From: Roman Marchenko Date: Fri, 26 May 2017 12:54:14 +0300 Subject: [PATCH 1/1] HWC: first draft Change-Id: I1a43cedfbff4c75ff77f167428787ccdbee6a8e8 Signed-off-by: Roman Marchenko --- include/tdm.h | 202 +++++++++++++++++++++++++++++++++ include/tdm_backend.h | 209 ++++++++++++++++++++++++++++++++-- include/tdm_common.h | 5 + include/tdm_types.h | 64 +++++++++++ src/Makefile.am | 1 + src/tdm.c | 11 ++ src/tdm_backend.c | 21 ++++ src/tdm_hwc_window.c | 308 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/tdm_output.c | 213 ++++++++++++++++++++++++++++++++++ src/tdm_private.h | 22 ++++ 10 files changed, 1049 insertions(+), 7 deletions(-) create mode 100644 src/tdm_hwc_window.c diff --git a/include/tdm.h b/include/tdm.h index 29c8193..d10b691 100644 --- a/include/tdm.h +++ b/include/tdm.h @@ -62,6 +62,7 @@ extern "C" { typedef enum { TDM_DISPLAY_CAPABILITY_PP = (1 << 0), /**< if hardware supports pp operation */ TDM_DISPLAY_CAPABILITY_CAPTURE = (1 << 1), /**< if hardware supports capture operation */ + TDM_DISPLAY_CAPABILITY_HWC = (1 << 2), /**< if hardware supports hwc operation */ } tdm_display_capability; /** @@ -552,6 +553,128 @@ tdm_capture * tdm_output_create_capture(tdm_output *output, tdm_error *error); /** + * @brief Creates a new window on the given display. + * @param[in] output A output object + * @param[out] error #TDM_ERROR_NONE if success. Otherwise, error value. + * @return A created window object + */ +tdm_hwc_window * +tdm_output_create_hwc_window(tdm_output *output, tdm_error *error); + +/** + * @brief Destroys the given window. + * @param[in] output A output object + * @param[in] window the pointer of the window to destroy + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ +tdm_error +tdm_output_destroy_hwc_window(tdm_output *output, tdm_hwc_window *hwc_window); + +/** + * @brief Set the client(relative to the TDM) target buffer + * @details Sets the buffer which will receive the output of client composition. + * Window marked as TDM_COMPOSITION_CLIENT or TDM_COMPOSITION_DEVICE_CANDIDATE + * will be composited into this buffer prior to the call to tdm_output_commit(), + * and windows not marked as TDM_COMPOSITION_CLIENT and + * TDM_COMPOSITION_DEVICE_CANDIDATE should be composited with this buffer by the + * device. + * + * The buffer handle provided may be null if no windows are being composited by + * the client. This must not result in an error (unless an invalid display + * handle is also provided). + * + * The damage parameter describes a surface damage region as defined in the + * description of tdm_hwc_window_set_surface_damage(). + * + * Will be called before tdm_output_commit() if any of the layers are marked as + * TDM_COMPOSITION_CLIENT or TDM_COMPOSITION_DEVICE_CANDIDATE. If no layers are + * so marked, then it is not necessary to call this function. It is not necessary + * to call tdm_output_validate() after changing the target through this function. + * @param[in] output A output object + * @param[in] target The new target buffer + * @param[in] damage The surface damage region + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ +tdm_error +tdm_output_set_client_target_buffer(tdm_output *output, tbm_surface_h target, + tdm_hwc_region damage); + +/** + * @brief Validate the output + * @details Instructs the device to inspect all of the layer state and + * determine if there are any composition type changes necessary before + * presenting the output. Permitted changes are described in the definition + * of tdm_hwc_window_composition_t above. + * @param[in] output A output object + * @param[out] num_types The number of composition type changes required by + * the device; if greater than 0, the client must either set and validate new + * types, or call tdm_output_accept_changes() to accept the changes returned by + * tdm_output_get_changed_composition_types(); must be the same as the number of + * changes returned by tdm_output_get_changed_composition_types (see the + * declaration of that function for more information); pointer will be non-NULL + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ +tdm_error +tdm_output_validate(tdm_output *output, uint32_t *num_types); + +/** + * @brief Get changed composition types + * @details Retrieves the windows for which the device requires a different + * composition type than had been set prior to the last call to tdm_output_validate(). + * The client will either update its state with these types and call + * tdm_output_accept_changes, or will set new types and attempt to validate the + * display again. + * windows and types may be NULL to retrieve the number of elements which + * will be returned. The number of elements returned must be the same as the + * value returned in num_types from the last call to tdm_output_validate(). + * @param[in] output A output object + * @param[out] num_elements If windows or types were NULL, the number of layers + * and types which would have been returned; if both were non-NULL, the + * number of elements returned in layers and types, which must not exceed + * the value stored in num_elements prior to the call; pointer will be + * non-NULL + * @param[in] output A output object + * @param[out] windows An array of windows + * @param[out] composition_types An array of composition types, each corresponding + * to an element of windows + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ +tdm_error +tdm_output_get_changed_composition_types(tdm_output *output,uint32_t *num_elements, + tdm_hwc_window **hwc_window, + tdm_hwc_window_composition_t *composition_types); + +/** + * @brief Accepts the changes required by the device + * @details Accepts the changes required by the device from the previous + * tdm_output_validate() call (which may be queried using + * tdm_output_get_chaged_composition_types()) and revalidates the display. This + * function is equivalent to requesting the changed types from + * tdm_output_get_chaged_composition_types(), setting those types on the + * corresponding windows, and then calling tdm_output_validate again. + * After this call it must be valid to present this display. Calling this after + * tdm_output_validate() returns 0 changes must succeed with TDM_ERROR_NONE, but + * should have no other effect. + * @param[in] output A output object + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ +tdm_error +tdm_output_accept_changes(tdm_output *output); + +/** + * @brief Get a target surface queue + * @details Surfaces from target surface queue will receive the output of + * client composition. Window marked as TDM_COMPOSITION_CLIENT or + * TDM_COMPOSITION_DEVICE_CANDIDATE will be composited into this surfaces + * prior to the call to tdm_output_commit(). + * @param[in] output A output object + * @param[out] error #TDM_ERROR_NONE if success. Otherwise, error value. + * @return A surface queue + */ +tbm_surface_queue_h +tdm_output_get_target_surface_queue(tdm_output *output, tdm_error *error); + +/** * @brief Get the capabilities of a layer object. * @param[in] layer A layer object * @param[out] capabilities The capabilities of a layer object @@ -771,6 +894,85 @@ tdm_error tdm_layer_get_buffer_flags(tdm_layer *layer, unsigned int *flags); /** + * @brief Get a tbm surface queue for the window object + * @details These surfaces are used to composite by hardware a client content in + * the nocomp mode. + * @param[in] hwc_window A window object + * @param[out] error #TDM_ERROR_NONE if success. Otherwise, error value. + * @return A tbm surface queue + */ +tbm_surface_queue_h +tdm_hwc_window_get_tbm_surface_queue(tdm_hwc_window *hwc_window, tdm_error *error); + +/** + * @brief Sets the desired Z order of the given window. A window with + * a greater Z value occludes a window with a lesser Z value. + * @param[in] hwc_window A window object + * @param[in] z the new Z order + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ +tdm_error +tdm_hwc_window_set_zpos(tdm_hwc_window *hwc_window, uint32_t zpos); + +/** + * @brief Sets the desired composition type of the given window. + * @details During tdm_output_validate(), the device may request changes to + * the composition types of any of the layers as described in the definition + * of tdm_hwc_window_composition_t above. + * @param[in] hwc_window A window object + * @param[in] composition_type The new composition type + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ +tdm_error +tdm_hwc_window_set_composition_type(tdm_hwc_window *hwc_window, + tdm_hwc_window_composition_t composition_type); + +/** + * @brief Set the surface damage + * @details Provides the region of the source buffer which has been modified + * since the last frame. This region does not need to be validated before + * calling tdm_output_commit(). + * Once set through this function, the damage region remains the same until a + * subsequent call to this function. + * If damage.num_rects > 0, then it may be assumed that any portion of the source + * buffer not covered by one of the rects has not been modified this frame. If + * damage.num_rects == 0, then the whole source buffer must be treated as if it + * has been modified. + * If the layer's contents are not modified relative to the prior frame, damage + * will contain exactly one empty rect([0, 0, 0, 0]). + * The damage rects are relative to the pre-transformed buffer, and their origin + * is the top-left corner. They will not exceed the dimensions of the latched + * buffer. + * @param[in] hwc_window A window object + * @param[in] damage The new surface damage region + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ +tdm_error +tdm_hwc_window_set_surface_damage(tdm_hwc_window *hwc_window, tdm_hwc_region damage); + +/** + * @brief Set the information to a window object + * @details The information will be applied when the output object of a window + * object is committed. + * @param[in] hwc_window A window object + * @param[in] info The information + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ +tdm_error +tdm_hwc_window_set_info(tdm_hwc_window *hwc_window, tdm_hwc_window_info *info); + +/** + * @brief Set a TBM buffer to a window object + * @details A TBM buffer will be applied when the output object of a layer + * object is committed. + * @param[in] hwc_window A layer object + * @param[in] buffer A TDM buffer + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ +tdm_error +tdm_hwc_window_set_buffer(tdm_hwc_window *hwc_window, tbm_surface_h buffer); + +/** * @brief Destroy a pp object * @param[in] pp A pp object * @see tdm_display_create_pp diff --git a/include/tdm_backend.h b/include/tdm_backend.h index 0949372..3ecdf92 100644 --- a/include/tdm_backend.h +++ b/include/tdm_backend.h @@ -523,13 +523,116 @@ typedef struct _tdm_func_output { tdm_error (*output_set_dpms_handler)(tdm_output *output, tdm_output_dpms_handler func, void *user_data); - void (*reserved2)(void); - void (*reserved3)(void); - void (*reserved4)(void); - void (*reserved5)(void); - void (*reserved6)(void); - void (*reserved7)(void); - void (*reserved8)(void); + /** + * @brief Creates a new window on the given display. + * @param[in] output A output object + * @param[out] error #TDM_ERROR_NONE if success. Otherwise, error value. + * @return A created window object + */ + tdm_hwc_window *(*output_create_hwc_window)(tdm_output *output, tdm_error *error); + + /** + * @brief Destroys the given window. + * @param[in] output A output object + * @param[in] window the pointer of the window to destroy + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ + tdm_error (*output_destroy_hwc_window)(tdm_output *output, tdm_hwc_window *hwc_window); + + /** + * @brief Set the client(relative to the TDM) target buffer + * @details Sets the buffer which will receive the output of client composition. + * Window marked as TDM_COMPOSITION_CLIENT or TDM_COMPOSITION_DEVICE_CANDIDATE + * will be composited into this buffer prior to the call to output_commit(), + * and windows not marked as TDM_COMPOSITION_CLIENT and + * TDM_COMPOSITION_DEVICE_CANDIDATE should be composited with this buffer by the + * device. + * + * The buffer handle provided may be null if no windows are being composited by + * the client. This must not result in an error (unless an invalid display + * handle is also provided). + * + * The damage parameter describes a surface damage region as defined in the + * description of hwc_window_set_surface_damage(). + * + * Will be called before output_commit() if any of the layers are marked as + * TDM_COMPOSITION_CLIENT or TDM_COMPOSITION_DEVICE_CANDIDATE. If no layers are + * so marked, then it is not necessary to call this function. It is not necessary + * to call output_validate() after changing the target through this function. + * @param[in] output A output object + * @param[in] target The new target buffer + * @param[in] damage The surface damage region + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ + tdm_error (*output_set_client_target_buffer)(tdm_output *output, tbm_surface_h target, + tdm_hwc_region damage); + + /** + * @brief Validate the output + * @details Instructs the device to inspect all of the layer state and + * determine if there are any composition type changes necessary before + * presenting the output. Permitted changes are described in the definition + * of tdm_composition_t above. + * @param[in] output A output object + * @param[out] num_types The number of composition type changes required by + * the device; if greater than 0, the client must either set and validate new + * types, or call output_accept_changes() to accept the changes returned by + * output_get_changed_composition_types(); must be the same as the number of + * changes returned by output_get_changed_composition_types (see the + * declaration of that function for more information); pointer will be non-NULL + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ + tdm_error (*output_validate)(tdm_output *output, uint32_t *num_types); + + /** + * @brief Get changed composition types + * @details Retrieves the windows for which the device requires a different + * composition type than had been set prior to the last call to output_validate(). + * The client will either update its state with these types and call + * output_accept_changes, or will set new types and attempt to validate the + * display again. + * layers and types may be NULL to retrieve the number of elements which + * will be returned. The number of elements returned must be the same as the + * value returned in num_types from the last call to output_validate(). + * @param[in] output A output object + * @param[out] num_elements If windows or types were NULL, the number of layers + * and types which would have been returned; if both were non-NULL, the + * number of elements returned in layers and types, which must not exceed + * the value stored in num_elements prior to the call; pointer will be + * non-NULL + * @param[out] windows An array of windows + * @param[out] composition_types An array of composition types, each + * corresponding to an element of windows + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ + tdm_error (*output_get_changed_composition_types)(tdm_output *output, + uint32_t *num_elements, + tdm_hwc_window **hwc_window, + tdm_hwc_window_composition_t *composition_types); + /** + * @brief Accepts the changes required by the device + * @details Accepts the changes required by the device from the previous + * output_validate() call (which may be queried using + * output_get_chaged_composition_types()) and revalidates the display. This + * function is equivalent to requesting the changed types from + * output_get_chaged_composition_types(), setting those types on the + * corresponding windows, and then calling output_validate again. + * After this call it must be valid to present this display. Calling this after + * output_validate() returns 0 changes must succeed with TDM_ERROR_NONE, but + * should have no other effect. + * @param[in] output A output object + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ + tdm_error (*output_accept_changes)(tdm_output *output); + + /** + * @brief Get a target surface queue + * @param[in] output A output object + * @param[out] error #TDM_ERROR_NONE if success. Otherwise, error value. + * @return A surface queue + */ + tbm_surface_queue_h (*output_get_target_surface_queue)(tdm_output *output, + tdm_error *error); } tdm_func_output; /** @@ -662,6 +765,86 @@ typedef struct _tdm_func_layer { } tdm_func_layer; /** + * @brief The window functions for a backend module. + */ +typedef struct _tdm_func_window { + /** + * @brief Get a tbm surface queue for the window object + * @param[in] hwc_window A window object + * @param[out] error #TDM_ERROR_NONE if success. Otherwise, error value. + * @return A tbm surface queue + */ + tbm_surface_queue_h (*hwc_window_get_tbm_surface_queue)(tdm_hwc_window *hwc_window, + tdm_error *error); + + /** + * @brief Sets the desired Z order (height) of the given window. A window with + * a greater Z value occludes a window with a lesser Z value. + * @param[in] hwc_window A window object + * @param[in] z the new Z order + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ + tdm_error (*hwc_window_set_zpos)(tdm_hwc_window *hwc_window, uint32_t zpos); + + /** + * @brief Sets the desired composition type of the given window. + * @details During output_validate(), the device may request changes to + * the composition types of any of the layers as described in the definition + * of tdm_hwc_window_composition_t above. + * @param[in] hwc_window A window object + * @param[in] composition_type The new composition type + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ + tdm_error (*hwc_window_set_composition_type)(tdm_hwc_window *hwc_window, + tdm_hwc_window_composition_t composition_type); + + /** + * @brief Set the surface damage + * @details Provides the region of the source buffer which has been modified + * since the last frame. This region does not need to be validated before + * calling output_commit(). + * Once set through this function, the damage region remains the same until a + * subsequent call to this function. + * If damage.num_rects > 0, then it may be assumed that any portion of the source + * buffer not covered by one of the rects has not been modified this frame. If + * damage.num_rects == 0, then the whole source buffer must be treated as if it + * has been modified. + * If the layer's contents are not modified relative to the prior frame, damage + * will contain exactly one empty rect([0, 0, 0, 0]). + * The damage rects are relative to the pre-transformed buffer, and their origin + * is the top-left corner. They will not exceed the dimensions of the latched + * buffer. + * @param[in] hwc_window A window object + * @param[in] damage The new surface damage region + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ + tdm_error (*hwc_window_set_surface_damage)(tdm_hwc_window *hwc_window, + tdm_hwc_region damage); + + /** + * @brief Set the information to a window object + * @details The information will be applied when the output object + * of a layer object is committed. + * @param[in] hwc_window A window object + * @param[in] info The geometry information + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ + tdm_error (*hwc_window_set_info)(tdm_hwc_window *hwc_window, + tdm_hwc_window_info *info); + + /** + * @brief Set a TDM buffer to a window object + * @details A TDM buffer will be applied when the output object + * of a layer object is committed. + * @param[in] hwc_window A layer object + * @param[in] buffer A TDM buffer + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ + tdm_error (*hwc_window_set_buffer)(tdm_hwc_window *hwc_window, + tbm_surface_h buffer); +} tdm_func_hwc_window; + +/** * @brief The pp functions for a backend module. */ typedef struct _tdm_func_pp { @@ -916,6 +1099,18 @@ tdm_error tdm_backend_register_func_layer(tdm_display *dpy, tdm_func_layer *func_layer); /** + * @brief Register the backend hwc_window functions to a display + * @param[in] dpy A display object + * @param[in] func_hwc_window hwc_window functions + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @see tdm_backend_register_func_display, tdm_backend_register_func_output + * @remarks + * A backend module @b SHOULD set the backend hwc_window functions at least. + */ +tdm_error +tdm_backend_register_func_hwc_window(tdm_display *dpy, tdm_func_hwc_window *func_hwc_window); + +/** * @brief Register the backend pp functions to a display * @param[in] dpy A display object * @param[in] func_pp pp functions diff --git a/include/tdm_common.h b/include/tdm_common.h index dfb3f8e..d7a10b5 100644 --- a/include/tdm_common.h +++ b/include/tdm_common.h @@ -261,6 +261,11 @@ typedef struct _tdm_pos { unsigned int h; } tdm_pos; +typedef struct _tdm_hwc_region { + unsigned int num_rects; + tdm_pos const *rects; +} tdm_hwc_region; + /** * @brief The tdm value type enumeration */ diff --git a/include/tdm_types.h b/include/tdm_types.h index 2cda6d6..56e3616 100644 --- a/include/tdm_types.h +++ b/include/tdm_types.h @@ -107,6 +107,15 @@ typedef struct _tdm_info_layer { } tdm_info_layer; /** + * @brief The hwc window info structure + */ +typedef struct _tdm_hwc_window_info { + tdm_info_config src_config; + tdm_pos dst_pos; + tdm_transform transform; +} tdm_hwc_window_info; + +/** * @brief The pp info structre */ typedef struct _tdm_info_pp { @@ -129,6 +138,56 @@ typedef struct _tdm_info_capture { } tdm_info_capture; /** + * @brief Possible composition types for a given window + */ +typedef enum { + /** The client will composite this window into the client target window + * + * User can choose this type for window to avoid a hardware composition for + * this window. + * + * The device must not request any composition type changes for windows of + * this type. + */ + TDM_COMPOSITION_CLIENT = 0, + + /** Set by the client before tdm_output_validate(). + * + * Upon tdm_output_validate(), the device may request a change from this type to + * TDM_COMPOSITION_DEVICE or TDM_COMPOSITION_CLIENT. */ + TDM_COMPOSITION_DEVICE_CANDIDATE = 2, + + /** Set by the HWC after tdm_output_validate(). + * + * The device will handle the composition of this window through a hardware + * overlay or other similar means. + * + * Upon tdm_output_validate(), the device may request a change from this type to + * TDM_COMPOSITION_CLIENT or TDM_COMPOSITION_DEVICE_CANDIDATE. */ + TDM_COMPOSITION_DEVICE = 1, + + /** Similar to DEVICE, but the position of this layer may also be set + * asynchronously through layer_set_cursor_position. If this functionality is not + * supported on a layer that the client sets to TDM_COMPOSITION_CURSOR, the + * device must request that the composition type of that layer is changed to + * TDM_COMPOSITION_CLIENT upon the next call to tdm_output_validate(). + * + * Upon tdm_output_validate(), the device may request a change from this type to + * either TDM_COMPOSITION_DEVICE or TDM_COMPOSITION_CLIENT. Changing to + * TDM_COMPOSITION_DEVICE will prevent the use of layer_set_cursor_position but + * still permit the device to composite the layer. */ + TDM_COMPOSITION_CURSOR = 3, + + /** The device will handle the composition of this layer through a hardware + * overlay or other similar means. + * + * Upon tdm_output_validate(), the device may request a change from this type to + * either TDM_COMPOSITION_DEVICE or TDM_COMPOSITION_CLIENT, but it is + * unlikely that content will display correctly in these cases. */ + TDM_COMPOSITION_VIDEO = 4, +} tdm_hwc_window_composition_t; + +/** * @brief The tdm display object */ typedef void tdm_display; @@ -144,6 +203,11 @@ typedef void tdm_output; typedef void tdm_layer; /** + * @brief The tdm window object + */ +typedef void tdm_hwc_window; + +/** * @brief The tdm capture object */ typedef void tdm_capture; diff --git a/src/Makefile.am b/src/Makefile.am index 87169ff..8542130 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -22,6 +22,7 @@ libtdm_la_SOURCES = \ tdm_display.c \ tdm_output.c \ tdm_layer.c \ + tdm_hwc_window.c \ tdm_pp.c \ tdm_capture.c \ tdm_monitor_server.c \ diff --git a/src/tdm.c b/src/tdm.c index 9baace2..e17b3f3 100644 --- a/src/tdm.c +++ b/src/tdm.c @@ -402,6 +402,7 @@ tdm_display_update_output(tdm_private_display *private_display, private_output->index = pipe; LIST_INITHEAD(&private_output->layer_list); + LIST_INITHEAD(&private_output->hwc_window_list); LIST_INITHEAD(&private_output->capture_list); LIST_INITHEAD(&private_output->vblank_handler_list); LIST_INITHEAD(&private_output->output_commit_handler_list); @@ -930,6 +931,13 @@ tdm_display_init(tdm_error *error) tdm_display_enable_commit_per_vblank(private_display, enable); } + str = getenv("TDM_HWC"); + if (str) { + char *end; + int enable = strtol(str, &end, 10); + private_display->hwc_enable = enable; + } + if (pthread_mutex_init(&private_display->lock, NULL)) { ret = TDM_ERROR_OPERATION_FAILED; TDM_ERR("mutex init failed: %m"); @@ -954,6 +962,9 @@ tdm_display_init(tdm_error *error) if (ret != TDM_ERROR_NONE) goto failed_load; + if (!(private_display->capabilities & TDM_DISPLAY_CAPABILITY_HWC)) + private_display->hwc_enable = 0; + stamp2 = tdm_helper_get_time(); TDM_DBG("loading backend time: %.3f ms", (stamp2 - stamp1) * 1000.0); stamp1 = stamp2; diff --git a/src/tdm_backend.c b/src/tdm_backend.c index acbde8e..d25066f 100644 --- a/src/tdm_backend.c +++ b/src/tdm_backend.c @@ -128,6 +128,27 @@ tdm_backend_register_func_layer(tdm_display *dpy, tdm_func_layer *func_layer) } EXTERN tdm_error +tdm_backend_register_func_hwc_window(tdm_display *dpy, tdm_func_hwc_window *func_hwc_window) +{ + tdm_backend_module *module; + + TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED); + + BACKEND_FUNC_ENTRY(); + + TDM_RETURN_VAL_IF_FAIL(func_hwc_window != NULL, TDM_ERROR_INVALID_PARAMETER); + + module = private_display->module_data; + if (_check_abi_version(module, 1, 1) < 0) + return TDM_ERROR_BAD_MODULE; + + private_display->capabilities |= TDM_DISPLAY_CAPABILITY_HWC; + private_display->func_hwc_window = *func_hwc_window; + + return TDM_ERROR_NONE; +} + +EXTERN tdm_error tdm_backend_register_func_pp(tdm_display *dpy, tdm_func_pp *func_pp) { TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED); diff --git a/src/tdm_hwc_window.c b/src/tdm_hwc_window.c new file mode 100644 index 0000000..94ac7a2 --- /dev/null +++ b/src/tdm_hwc_window.c @@ -0,0 +1,308 @@ +/************************************************************************** + * + * libtdm + * + * Copyright 2015 Samsung Electronics co., Ltd. All Rights Reserved. + * + * Contact: Eunchul Kim , + * JinYoung Jeon , + * Taeheon Kim , + * YoungJun Cho , + * SooChan Lim , + * Boram Park + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * +**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "tdm.h" +#include "tdm_backend.h" +#include "tdm_private.h" +#include "tdm_helper.h" + +#define COUNT_MAX 10 + +#define HWC_WINDOW_FUNC_ENTRY() \ + tdm_private_display *private_display; \ + tdm_private_output *private_output; \ + tdm_private_hwc_window *private_hwc_window; \ + tdm_error ret = TDM_ERROR_NONE; /* default TDM_ERROR_NONE */\ + TDM_RETURN_VAL_IF_FAIL(hwc_window != NULL, TDM_ERROR_INVALID_PARAMETER); \ + private_hwc_window = (tdm_private_hwc_window*)hwc_window; \ + private_output = private_hwc_window->private_output; \ + private_display = private_output->private_display + +#define HWC_WINDOW_FUNC_ENTRY_ERROR() \ + tdm_private_display *private_display; \ + tdm_private_output *private_output; \ + tdm_private_hwc_window *private_hwc_window; \ + tdm_error ret = TDM_ERROR_NONE; /* default TDM_ERROR_NONE */\ + TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(hwc_window != NULL, TDM_ERROR_INVALID_PARAMETER, NULL); \ + private_hwc_window = (tdm_private_hwc_window*)hwc_window; \ + private_output = private_hwc_window->private_output; \ + private_display = private_output->private_display + +#define HWC_WINDOW_FUNC_ENTRY_VOID_RETURN() \ + tdm_private_display *private_display; \ + tdm_private_output *private_output; \ + tdm_private_hwc_window *private_hwc_window; \ + tdm_error ret = TDM_ERROR_NONE; /* default TDM_ERROR_NONE */\ + TDM_RETURN_IF_FAIL(hwc_window != NULL); \ + private_hwc_window = (tdm_private_hwc_window*)hwc_window; \ + private_output = private_hwc_window->private_output; \ + private_display = private_output->private_display + +tbm_surface_queue_h +tdm_hwc_window_get_tbm_surface_queue(tdm_hwc_window *hwc_window, tdm_error *error) +{ + tdm_func_hwc_window *func_hwc_window = NULL; + tbm_surface_queue_h queue = NULL; + + HWC_WINDOW_FUNC_ENTRY_ERROR(); + + _pthread_mutex_lock(&private_display->lock); + + func_hwc_window = &private_display->func_hwc_window; + + if (!func_hwc_window->hwc_window_get_tbm_surface_queue) { + _pthread_mutex_unlock(&private_display->lock); + TDM_ERR("not implemented!!"); + *error = TDM_ERROR_NOT_IMPLEMENTED; + return NULL; + } + + queue = func_hwc_window->hwc_window_get_tbm_surface_queue(private_hwc_window->hwc_window_backend, error); + + _pthread_mutex_unlock(&private_display->lock); + + return queue; +} + +EXTERN tdm_error +tdm_hwc_window_set_zpos(tdm_hwc_window *hwc_window, uint32_t zpos) +{ + tdm_func_hwc_window *func_hwc_window = NULL; + + HWC_WINDOW_FUNC_ENTRY(); + + _pthread_mutex_lock(&private_display->lock); + + func_hwc_window = &private_display->func_hwc_window; + + if (!func_hwc_window->hwc_window_set_zpos) { + _pthread_mutex_unlock(&private_display->lock); + TDM_ERR("not implemented!!"); + return TDM_ERROR_NOT_IMPLEMENTED; + } + + ret = func_hwc_window->hwc_window_set_zpos(private_hwc_window->hwc_window_backend, zpos); + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} + +EXTERN tdm_error +tdm_hwc_window_set_composition_type(tdm_hwc_window *hwc_window, + tdm_hwc_window_composition_t composition_type) +{ + tdm_func_hwc_window *func_hwc_window = NULL; + + HWC_WINDOW_FUNC_ENTRY(); + + _pthread_mutex_lock(&private_display->lock); + + func_hwc_window = &private_display->func_hwc_window; + + if (!func_hwc_window->hwc_window_set_composition_type) { + _pthread_mutex_unlock(&private_display->lock); + TDM_ERR("not implemented!!"); + return TDM_ERROR_NOT_IMPLEMENTED; + } + + ret = func_hwc_window->hwc_window_set_composition_type(private_hwc_window->hwc_window_backend, composition_type); + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} + +EXTERN tdm_error +tdm_hwc_window_set_surface_damage(tdm_hwc_window *hwc_window, tdm_hwc_region damage) +{ + tdm_func_hwc_window *func_hwc_window = NULL; + + HWC_WINDOW_FUNC_ENTRY(); + + _pthread_mutex_lock(&private_display->lock); + + func_hwc_window = &private_display->func_hwc_window; + + if (!func_hwc_window->hwc_window_set_surface_damage) { + _pthread_mutex_unlock(&private_display->lock); + TDM_ERR("not implemented!!"); + return TDM_ERROR_NOT_IMPLEMENTED; + } + + ret = func_hwc_window->hwc_window_set_surface_damage(private_hwc_window->hwc_window_backend, damage); + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} + + +EXTERN tdm_error +tdm_hwc_window_set_info(tdm_hwc_window *hwc_window, tdm_hwc_window_info *info) +{ + tdm_func_hwc_window *func_hwc_window = NULL; + char fmtstr[128]; + + HWC_WINDOW_FUNC_ENTRY(); + + TDM_RETURN_VAL_IF_FAIL(info != NULL, TDM_ERROR_INVALID_PARAMETER); + + _pthread_mutex_lock(&private_display->lock); + + func_hwc_window = &private_display->func_hwc_window; + + if (!func_hwc_window->hwc_window_set_info) { + _pthread_mutex_unlock(&private_display->lock); + TDM_ERR("not implemented!!"); + return TDM_ERROR_NOT_IMPLEMENTED; + } + + if (info->src_config.format) + snprintf(fmtstr, 128, "%c%c%c%c", FOURCC_STR(info->src_config.format)); + else + snprintf(fmtstr, 128, "NONE"); + + TDM_INFO("hwc_window(%p) info: src(%dx%d %d,%d %dx%d %s) dst(%d,%d %dx%d) trans(%d)", + private_hwc_window, info->src_config.size.h, info->src_config.size.v, + info->src_config.pos.x, info->src_config.pos.y, + info->src_config.pos.w, info->src_config.pos.h, + fmtstr, + info->dst_pos.x, info->dst_pos.y, + info->dst_pos.w, info->dst_pos.h, + info->transform); + + ret = func_hwc_window->hwc_window_set_info(private_hwc_window->hwc_window_backend, info); + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} + +EXTERN tdm_error +tdm_hwc_window_set_buffer(tdm_hwc_window *hwc_window, tbm_surface_h buffer) +{ + tdm_func_hwc_window *func_hwc_window; + + HWC_WINDOW_FUNC_ENTRY(); + + _pthread_mutex_lock(&private_display->lock); + + + func_hwc_window = &private_display->func_hwc_window; + + if (!func_hwc_window->hwc_window_set_buffer) { + _pthread_mutex_unlock(&private_display->lock); + TDM_ERR("not implemented!!"); + return TDM_ERROR_NOT_IMPLEMENTED; + } + + ret = func_hwc_window->hwc_window_set_buffer(private_hwc_window->hwc_window_backend, buffer); + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} + +INTERN tdm_hwc_window * +tdm_hwc_window_create_internal(tdm_private_output *private_output, + tdm_error *error) +{ + tdm_private_display *private_display = private_output->private_display; + tdm_func_output *func_output = &private_display->func_output; + tdm_private_hwc_window *private_hwc_window = NULL; + tdm_hwc_window *hwc_window_backend = NULL; + tdm_error ret = TDM_ERROR_NONE; + + TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), NULL); + + hwc_window_backend = func_output->output_create_hwc_window( + private_output->output_backend, &ret); + if (ret != TDM_ERROR_NONE) { + if (error) + *error = ret; + return NULL; + } + + private_hwc_window = calloc(1, sizeof(tdm_private_capture)); + if (!private_hwc_window) { + TDM_ERR("failed: alloc memory"); + func_output->output_destroy_hwc_window(private_output->output_backend, hwc_window_backend); + if (error) + *error = TDM_ERROR_OUT_OF_MEMORY; + return NULL; + } + + LIST_ADD(&private_hwc_window->link, &private_output->hwc_window_list); + + private_hwc_window->private_display = private_display; + private_hwc_window->private_output = private_output; + private_hwc_window->hwc_window_backend = hwc_window_backend; + + TDM_DBG("hwc_window(%p) create", private_hwc_window); + + if (error) + *error = TDM_ERROR_NONE; + + return private_hwc_window; +} + +INTERN tdm_error +tdm_hwc_window_destroy_internal(tdm_private_hwc_window * private_hwc_window) +{ + tdm_private_display *private_display; + tdm_private_output *private_output; + tdm_func_output *func_output; + + TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED); + + if (!private_hwc_window) + return TDM_ERROR_OPERATION_FAILED; + + private_display = private_hwc_window->private_display; + private_output = private_hwc_window->private_output; + + LIST_DEL(&private_hwc_window->link); + + func_output = &private_display->func_output; + func_output->output_destroy_hwc_window(private_output->output_backend, private_hwc_window->hwc_window_backend); + + free(private_hwc_window); + return TDM_ERROR_NONE; +} diff --git a/src/tdm_output.c b/src/tdm_output.c index 62ba065..ab2d991 100644 --- a/src/tdm_output.c +++ b/src/tdm_output.c @@ -60,6 +60,21 @@ private_output = (tdm_private_output*)output; \ private_display = private_output->private_display + +static tdm_private_hwc_window * +_tdm_output_find_private_hwc_window(tdm_private_output *private_output, + tdm_hwc_window *hwc_window_backend) +{ + tdm_private_hwc_window *private_hwc_window = NULL; + + LIST_FOR_EACH_ENTRY(private_hwc_window, &private_output->hwc_window_list, link) { + if (private_hwc_window->hwc_window_backend == hwc_window_backend) + return private_hwc_window; + } + + return NULL; +} + EXTERN tdm_error tdm_output_get_model_info(tdm_output *output, const char **maker, const char **model, const char **name) @@ -283,6 +298,11 @@ tdm_output_get_layer_count(tdm_output *output, int *count) _pthread_mutex_lock(&private_display->lock); + if (private_display->hwc_enable) { + *count = 0; + return TDM_ERROR_NONE; + } + *count = 0; LIST_FOR_EACH_ENTRY(private_layer, &private_output->layer_list, link) (*count)++; @@ -309,6 +329,11 @@ tdm_output_get_layer(tdm_output *output, int index, tdm_error *error) if (error) *error = TDM_ERROR_NONE; + if (private_display->hwc_enable) { + *error = TDM_ERROR_INVALID_PARAMETER; + return NULL; + } + LIST_FOR_EACH_ENTRY(private_layer, &private_output->layer_list, link) { if (private_layer->index == index) { _pthread_mutex_unlock(&private_display->lock); @@ -1244,6 +1269,194 @@ tdm_output_create_capture(tdm_output *output, tdm_error *error) return capture; } +EXTERN tdm_hwc_window * +tdm_output_create_hwc_window(tdm_output *output, tdm_error *error) +{ + tdm_hwc_window *hwc_window = NULL; + + OUTPUT_FUNC_ENTRY_ERROR(); + + _pthread_mutex_lock(&private_display->lock); + + TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(private_display->hwc_enable, TDM_ERROR_BAD_REQUEST, NULL); + + hwc_window = (tdm_hwc_window *)tdm_hwc_window_create_internal(private_output, error); + + _pthread_mutex_unlock(&private_display->lock); + + return hwc_window; +} + +EXTERN tdm_error +tdm_output_destroy_hwc_window(tdm_output *output, tdm_hwc_window *hwc_window) +{ + OUTPUT_FUNC_ENTRY(); + + _pthread_mutex_lock(&private_display->lock); + + ret = tdm_hwc_window_destroy_internal(hwc_window); + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} + +EXTERN tdm_error +tdm_output_validate(tdm_output *output, uint32_t *num_types) +{ + tdm_func_output *func_output = NULL; + + OUTPUT_FUNC_ENTRY(); + + TDM_RETURN_VAL_IF_FAIL(num_types != NULL, TDM_ERROR_INVALID_PARAMETER); + + _pthread_mutex_lock(&private_display->lock); + + func_output = &private_display->func_output; + + if (!func_output->output_validate) { + _pthread_mutex_unlock(&private_display->lock); + TDM_ERR("not implemented!!"); + return TDM_ERROR_NOT_IMPLEMENTED; + } + + ret = func_output->output_validate(private_output->output_backend, num_types); + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} + +EXTERN tdm_error +tdm_output_get_changed_composition_types(tdm_output *output, + uint32_t *num_elements, + tdm_hwc_window **hwc_window, + tdm_hwc_window_composition_t *composition_types) +{ + tdm_func_output *func_output = NULL; + tdm_private_hwc_window * private_hwc_window = NULL; + int i = 0; + + OUTPUT_FUNC_ENTRY(); + + TDM_RETURN_VAL_IF_FAIL(num_elements != NULL, TDM_ERROR_INVALID_PARAMETER); + + _pthread_mutex_lock(&private_display->lock); + + func_output = &private_display->func_output; + + if (!func_output->output_get_changed_composition_types) { + _pthread_mutex_unlock(&private_display->lock); + TDM_ERR("not implemented!!"); + return TDM_ERROR_NOT_IMPLEMENTED; + } + + ret = func_output->output_get_changed_composition_types(private_output->output_backend, + num_elements, hwc_window, composition_types); + TDM_RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, ret); + + if (hwc_window == NULL || composition_types == NULL) + return TDM_ERROR_NONE; + + for (i = 0; i < *num_elements; i++) { + + private_hwc_window = _tdm_output_find_private_hwc_window(private_output, hwc_window[i]); + + if (private_hwc_window == NULL) { + TDM_ERR("failed! This should never happen!"); + func_output->output_destroy_hwc_window(private_output->output_backend, hwc_window[i]); + free(hwc_window); + free(composition_types); + num_elements = 0; + hwc_window = NULL; + composition_types = NULL; + _pthread_mutex_unlock(&private_display->lock); + return TDM_ERROR_OPERATION_FAILED; + } + + hwc_window[i] = (tdm_hwc_window*)private_hwc_window; + } + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} + +tdm_error +tdm_output_accept_changes(tdm_output *output) +{ + tdm_func_output *func_output = NULL; + + OUTPUT_FUNC_ENTRY(); + + _pthread_mutex_lock(&private_display->lock); + + func_output = &private_display->func_output; + + if (!func_output->output_validate) { + _pthread_mutex_unlock(&private_display->lock); + TDM_ERR("not implemented!!"); + return TDM_ERROR_NOT_IMPLEMENTED; + } + + ret = func_output->output_accept_changes(private_output->output_backend); + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} + +tbm_surface_queue_h +tdm_output_get_target_surface_queue(tdm_output *output, tdm_error *error) +{ + tdm_func_output *func_output = NULL; + tbm_surface_queue_h queue = NULL; + + OUTPUT_FUNC_ENTRY_ERROR(); + + _pthread_mutex_lock(&private_display->lock); + + func_output = &private_display->func_output; + + if (!func_output->output_get_target_surface_queue) { + _pthread_mutex_unlock(&private_display->lock); + TDM_ERR("not implemented!!"); + return NULL; + } + + queue = func_output->output_get_target_surface_queue(private_output->output_backend, error); + + _pthread_mutex_unlock(&private_display->lock); + + return queue; +} + +EXTERN tdm_error +tdm_output_set_client_target_buffer(tdm_output *output, tbm_surface_h buffer, + tdm_hwc_region damage) +{ + tdm_func_output *func_output = NULL; + + OUTPUT_FUNC_ENTRY(); + + _pthread_mutex_lock(&private_display->lock); + + + func_output = &private_display->func_output; + + if (!func_output->output_set_client_target_buffer) { + _pthread_mutex_unlock(&private_display->lock); + TDM_ERR("not implemented!!"); + return TDM_ERROR_NOT_IMPLEMENTED; + } + + ret = func_output->output_set_client_target_buffer(private_output->output_backend, buffer, damage); + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} + INTERN void tdm_output_call_change_handler_internal(tdm_private_output *private_output, struct list_head *change_handler_list, diff --git a/src/tdm_private.h b/src/tdm_private.h index 25b24b7..347e501 100644 --- a/src/tdm_private.h +++ b/src/tdm_private.h @@ -123,6 +123,7 @@ typedef enum { typedef struct _tdm_private_display tdm_private_display; typedef struct _tdm_private_output tdm_private_output; typedef struct _tdm_private_layer tdm_private_layer; +typedef struct _tdm_private_hwc_window tdm_private_hwc_window; typedef struct _tdm_private_pp tdm_private_pp; typedef struct _tdm_private_capture tdm_private_capture; typedef struct _tdm_private_loop tdm_private_loop; @@ -152,6 +153,7 @@ struct _tdm_private_display { tdm_func_display func_display; tdm_func_output func_output; tdm_func_layer func_layer; + tdm_func_hwc_window func_hwc_window; tdm_func_pp func_pp; tdm_func_capture func_capture; @@ -177,6 +179,8 @@ struct _tdm_private_display { int commit_per_vblank; tdm_commit_type commit_type; + int hwc_enable; + int print_fps; }; @@ -201,6 +205,7 @@ struct _tdm_private_output { int regist_dpms_cb; struct list_head layer_list; + struct list_head hwc_window_list; struct list_head capture_list; struct list_head vblank_handler_list; struct list_head output_commit_handler_list; @@ -250,6 +255,18 @@ struct _tdm_private_layer { unsigned int fps_count; }; +struct _tdm_private_hwc_window { + struct list_head link; + + int index; + + tdm_private_display *private_display; + tdm_private_output *private_output; + + tdm_hwc_window *hwc_window_backend; +}; + + struct _tdm_private_pp { struct list_head link; @@ -467,6 +484,11 @@ tdm_pp_create_internal(tdm_private_display *private_display, tdm_error *error); void tdm_pp_destroy_internal(tdm_private_pp *private_pp); +tdm_hwc_window * +tdm_hwc_window_create_internal(tdm_private_output *private_output, tdm_error *error); +tdm_error +tdm_hwc_window_destroy_internal(tdm_private_hwc_window * private_hwc_window); + tdm_private_capture * tdm_capture_create_output_internal(tdm_private_output *private_output, tdm_error *error); -- 2.7.4