From 22d907bc039d515b19919d045dc3c5eb483cf448 Mon Sep 17 00:00:00 2001 From: Leandro Ribeiro Date: Tue, 2 Sep 2025 20:49:28 -0300 Subject: [PATCH] backend-drm: offload pre-blend color xform Use the per-plane color pipelines to offload pre-blend Weston color transformations when possible. Signed-off-by: Leandro Ribeiro --- libweston/backend-drm/colorops.c | 605 +++++++++++++++++++++++++- libweston/backend-drm/colorops.h | 89 ++++ libweston/backend-drm/drm-internal.h | 5 + libweston/backend-drm/drm.c | 2 + libweston/backend-drm/kms.c | 185 ++++++++ libweston/backend-drm/state-helpers.c | 3 + libweston/backend-drm/state-propose.c | 28 +- 7 files changed, 914 insertions(+), 3 deletions(-) diff --git a/libweston/backend-drm/colorops.c b/libweston/backend-drm/colorops.c index 05f4f330a..21a56b236 100644 --- a/libweston/backend-drm/colorops.c +++ b/libweston/backend-drm/colorops.c @@ -186,6 +186,193 @@ drm_colorop_3x1d_lut_blob_create(struct drm_device *device, return lut; } +enum lowering_curve_policy { + LOWERING_CURVE_POLICY_ALLOW = true, + LOWERING_CURVE_POLICY_DENY = false, +}; + +static const char * +lowering_curve_policy_str(enum lowering_curve_policy policy) +{ + switch (policy) { + case LOWERING_CURVE_POLICY_DENY: + return "deny lowering curve"; + case LOWERING_CURVE_POLICY_ALLOW: + return "allow lowering curve"; + } + return "???"; +} + +static struct drm_colorop_3x1d_lut_blob * +drm_colorop_3x1d_lut_blob_from_curve(struct drm_device *device, + struct weston_color_transform *xform, + enum weston_color_curve_step curve_step, + uint32_t lut_len) +{ + struct weston_compositor *compositor = xform->cm->compositor; + struct drm_backend *b = device->backend; + struct drm_colorop_3x1d_lut_blob *colorop_lut; + char *err_msg; + struct weston_vec3f *cm_lut; + + /* No need to create, 3x1D LUT colorop already exists. */ + colorop_lut = drm_colorop_3x1d_lut_blob_search(device, xform, curve_step, + DRM_COLOROP_3X1D_LUT_BLOB_QUANTIZATION_U32, + lut_len); + if (colorop_lut) + return colorop_lut; + + cm_lut = weston_color_curve_to_3x1D_LUT(compositor, xform, curve_step, + WESTON_COLOR_PRECISION_CARELESS, + lut_len, &err_msg); + if (!cm_lut) { + drm_debug(b, "[colorop] failed to create colorop 3x1D from curve: %s\n", + err_msg); + free(err_msg); + return NULL; + } + + colorop_lut = + drm_colorop_3x1d_lut_blob_create(device, xform, curve_step, + DRM_COLOROP_3X1D_LUT_BLOB_QUANTIZATION_U32, + cm_lut, lut_len); + free(cm_lut); + if (!colorop_lut) { + drm_debug(b, "[colorop] failed to create colorop 3x1D from curve\n"); + return NULL; + } + + return colorop_lut; +} + +static void +drm_colorop_matrix_blob_destroy(struct drm_colorop_matrix_blob *mat) +{ + wl_list_remove(&mat->destroy_listener.link); + wl_list_remove(&mat->link); + drmModeDestroyPropertyBlob(mat->device->kms_device->fd, mat->blob_id); + free(mat); +} + +static void +drm_colorop_matrix_blob_destroy_handler(struct wl_listener *l, void *data) +{ + struct drm_colorop_matrix_blob *mat = + wl_container_of(l, mat, destroy_listener); + + drm_colorop_matrix_blob_destroy(mat); +} + +static struct drm_colorop_matrix_blob * +drm_colorop_matrix_blob_search(struct drm_device *device, + struct weston_color_transform *xform) +{ + struct drm_colorop_matrix_blob *mat; + + wl_list_for_each(mat, &device->drm_colorop_matrix_blob_list, link) + if (mat->xform == xform) + return mat; + + return NULL; +} + +/** + * Float to S31.32 sign-magnitude representation. + */ +static uint64_t +float_to_s31_32_sign_magnitude(float val) +{ + uint64_t ret; + + if (val < 0) { + ret = (uint64_t) (-val * (1ULL << 32)); + ret |= 1ULL << 63; + } else { + ret = (uint64_t) (val * (1ULL << 32)); + } + + return ret; +} + +static struct drm_colorop_matrix_blob * +drm_colorop_matrix_blob_create(struct drm_device *device, + struct weston_color_transform *xform, + struct drm_color_ctm_3x4 *matrix) +{ + struct drm_backend *b = device->backend; + struct drm_colorop_matrix_blob *colorop_mat; + uint32_t blob_id; + int ret; + + ret = drmModeCreatePropertyBlob(device->kms_device->fd, matrix, + sizeof(*matrix), &blob_id); + if (ret < 0) { + drm_debug(b, "[colorop] failed to create blob for matrix\n"); + return NULL; + } + + colorop_mat = xzalloc(sizeof(*colorop_mat)); + + colorop_mat->blob_id = blob_id; + colorop_mat->device = device; + colorop_mat->xform = xform; + wl_list_insert(&device->drm_colorop_matrix_blob_list, &colorop_mat->link); + colorop_mat->destroy_listener.notify = drm_colorop_matrix_blob_destroy_handler; + wl_signal_add(&xform->destroy_signal, &colorop_mat->destroy_listener); + + return colorop_mat; +} + +static struct drm_colorop_matrix_blob * +drm_colorop_matrix_blob_from_mapping(struct drm_device *device, + struct weston_color_transform *xform) +{ + struct drm_backend *b = device->backend; + struct weston_color_mapping *mapping = &xform->mapping; + struct drm_colorop_matrix_blob *colorop_mat; + struct drm_color_ctm_3x4 *mat_3x4; + unsigned int row, col; + float val; + + /* No need to create, colorop matrix already exists. */ + colorop_mat = drm_colorop_matrix_blob_search(device, xform); + if (colorop_mat) + return colorop_mat; + + mat_3x4 = xzalloc(sizeof(*mat_3x4)); + + /** + * mapping->u.mat.matrix is in column-major order. We transpose it and + * also add a new column with the offset. Also, kernel requires the + * values in S31.32 sign-magnitude representation. + */ + for (row = 0; row < 3; row++) { + for (col = 0; col < 3; col++) { + val = mapping->u.mat.matrix.col[col].el[row]; + mat_3x4->matrix[row * 4 + col] = float_to_s31_32_sign_magnitude(val); + } + val = mapping->u.mat.offset.el[row]; + mat_3x4->matrix[row * 4 + 3] = float_to_s31_32_sign_magnitude(val); + } + + colorop_mat = drm_colorop_matrix_blob_create(device, xform, mat_3x4); + free(mat_3x4); + if (!colorop_mat) { + drm_debug(b, "[colorop] failed to create colorop matrix from mapping\n"); + return NULL; + } + + return colorop_mat; +} + +static enum wdrm_colorop_curve_1d +weston_tf_to_colorop_curve(const struct weston_color_tf_info *tf_info, + enum weston_tf_direction tf_direction) +{ + return (tf_direction == WESTON_INVERSE_TF) ? + tf_info->kms_colorop_inverse : tf_info->kms_colorop; +} + static void drm_colorop_destroy(struct drm_colorop *colorop) { @@ -248,12 +435,428 @@ drm_colorop_create(struct drm_color_pipeline *pipeline, uint32_t colorop_id, return colorop; } -static const char * +/** + * Given a colorop this returns its type as a string. + * + * \param colorop The colorop. + * \return The colorop type as a string. + */ +const char * drm_colorop_type_to_str(struct drm_colorop *colorop) { return colorop->props[WDRM_COLOROP_TYPE].enum_values[colorop->type].name; } +static struct drm_colorop * +drm_colorop_iterate(struct drm_color_pipeline *pipeline, struct drm_colorop *iter) +{ + struct wl_list *list = &pipeline->colorop_list; + struct wl_list *node; + + if (iter) + node = iter->link.next; + else + node = list->next; + + if (node == list) + return NULL; + + return container_of(node, struct drm_colorop, link); +} + +static bool +is_colorop_compatible_with_curve(struct weston_compositor *compositor, + struct drm_colorop *colorop, + struct weston_color_curve *curve) +{ + struct weston_color_curve_parametric param; + struct drm_property_info *prop_info; + enum wdrm_colorop_curve_1d curve_type; + bool ret; + + if (colorop->type == WDRM_COLOROP_TYPE_1D_CURVE) { + if (curve->type != WESTON_COLOR_CURVE_TYPE_ENUM) + return false; + + curve_type = weston_tf_to_colorop_curve(curve->u.enumerated.tf.info, + curve->u.enumerated.tf_direction); + if (curve_type == WDRM_COLOROP_CURVE_1D__COUNT) + return false; + + prop_info = &colorop->props[WDRM_COLOROP_CURVE_1D]; + if (!prop_info->enum_values[curve_type].valid) + return false; + + return true; + } else if (colorop->type == WDRM_COLOROP_TYPE_1D_LUT) { + switch (curve->type) { + case WESTON_COLOR_CURVE_TYPE_LUT_3x1D: + return true; + case WESTON_COLOR_CURVE_TYPE_PARAMETRIC: + /* Parametric can be lowered to LUT. */ + return true; + case WESTON_COLOR_CURVE_TYPE_ENUM: + switch (curve->u.enumerated.tf.info->tf) { + case WESTON_TF_ST2084_PQ: + /* This TF is implemented, so we can lower curve to LUT. */ + return true; + default: + /* If we can lower the TF to parametric, we can use it + * to create a LUT. */ + ret = weston_color_curve_enum_get_parametric(compositor, + &curve->u.enumerated, + ¶m); + return ret; + } + case WESTON_COLOR_CURVE_TYPE_IDENTITY: + /* Dead code, function never called for IDENTITY. */ + weston_assert_not_reached(compositor, + "no need to get colorop for identity curve"); + } + + return false; + } + + return false; +} + +static struct drm_colorop * +search_colorop_compatible_curve(struct drm_color_pipeline *pipeline, + struct drm_colorop *previous_colorop, + struct weston_color_curve *curve, + enum lowering_curve_policy policy) +{ + struct drm_backend *b = pipeline->plane->device->backend; + struct drm_colorop *colorop = previous_colorop; + + /** + * Identity curve should not need a colorop, so calling this func for + * IDENTITY is not allowed. + */ + weston_assert_u32_ne(b->compositor, + curve->type, WESTON_COLOR_CURVE_TYPE_IDENTITY); + + while ((colorop = drm_colorop_iterate(pipeline, colorop))) { + switch (curve->type) { + case WESTON_COLOR_CURVE_TYPE_ENUM: + if (colorop->type == WDRM_COLOROP_TYPE_1D_CURVE && + is_colorop_compatible_with_curve(b->compositor, colorop, curve)) + return colorop; + else if (colorop->type == WDRM_COLOROP_TYPE_1D_LUT && + policy == LOWERING_CURVE_POLICY_ALLOW && + is_colorop_compatible_with_curve(b->compositor, colorop, curve)) + return colorop; + break; + case WESTON_COLOR_CURVE_TYPE_PARAMETRIC: + case WESTON_COLOR_CURVE_TYPE_LUT_3x1D: + if (colorop->type == WDRM_COLOROP_TYPE_1D_LUT) + return colorop; + break; + case WESTON_COLOR_CURVE_TYPE_IDENTITY: + /* Dead code. */ + weston_assert_not_reached(b->compositor, + "no need to get colorop for identity curve"); + } + + if (!colorop->can_bypass) + break; + } + + return NULL; +} + +static struct drm_colorop * +search_colorop_type(struct drm_color_pipeline *pipeline, + struct drm_colorop *previous_colorop, + enum wdrm_colorop_type type) +{ + struct drm_colorop *colorop = previous_colorop; + + while ((colorop = drm_colorop_iterate(pipeline, colorop))) { + if (colorop->type == type) + return colorop; + + if (!colorop->can_bypass) + break; + } + + return NULL; +} + +static struct drm_colorop_state * +drm_colorop_state_create(struct drm_color_pipeline_state *pipeline_state, + struct drm_colorop *colorop, + struct drm_colorop_state_object so) +{ + struct drm_colorop_state *colorop_state; + + colorop_state = xzalloc(sizeof(*colorop_state)); + + wl_list_insert(pipeline_state->colorop_state_list.prev, &colorop_state->link); + + colorop_state->colorop = colorop; + colorop_state->object = so; + + return colorop_state; +} + +static void +drm_colorop_state_destroy(struct drm_colorop_state *colorop_state) +{ + wl_list_remove(&colorop_state->link); + free(colorop_state); +} + +static struct drm_color_pipeline_state * +drm_color_pipeline_state_create(struct drm_color_pipeline *pipeline) +{ + struct drm_color_pipeline_state *state; + + state = xzalloc(sizeof(*state)); + + state->pipeline = pipeline; + + wl_list_init(&state->colorop_state_list); + + return state; +} + +/** + * Destroys a color pipeline state. + * + * @param state The pipeline state to destroy. + */ +void +drm_color_pipeline_state_destroy(struct drm_color_pipeline_state *state) +{ + struct drm_colorop_state *colorop_state, *tmp_colorop_state; + + if (!state) + return; + + wl_list_for_each_safe(colorop_state, tmp_colorop_state, + &state->colorop_state_list, link) + drm_colorop_state_destroy(colorop_state); + + free(state); +} + +static uint64_t +prop_val_from_curve(struct drm_device *device, struct drm_colorop *colorop, + struct weston_color_curve *curve) +{ + struct weston_compositor *compositor = device->backend->compositor; + enum wdrm_colorop_curve_1d curve_type; + struct drm_property_enum_info *prop_info; + + weston_assert_u32_eq(compositor, curve->type, + WESTON_COLOR_CURVE_TYPE_ENUM); + + curve_type = weston_tf_to_colorop_curve(curve->u.enumerated.tf.info, + curve->u.enumerated.tf_direction); + weston_assert_u32_ne(compositor, curve_type, + WDRM_COLOROP_CURVE_1D__COUNT); + + prop_info = &colorop->props[WDRM_COLOROP_CURVE_1D].enum_values[curve_type]; + weston_assert_true(compositor, prop_info->valid); + + return prop_info->value; +} + +static struct drm_colorop_state * +curve_create_colorop_state(struct drm_color_pipeline_state *pipeline_state, + struct drm_colorop *previous_colorop, + struct weston_color_transform *xform, + enum weston_color_curve_step curve_step, + enum lowering_curve_policy policy) +{ + struct drm_color_pipeline *pipeline = pipeline_state->pipeline; + struct weston_compositor *compositor = pipeline->plane->base.compositor; + struct drm_device *device = pipeline->plane->device; + struct drm_colorop_3x1d_lut_blob *lut_blob; + struct weston_color_curve *curve; + struct drm_colorop_state_object so = { 0 }; + struct drm_colorop *colorop; + uint32_t lut_len; + + curve = (curve_step == WESTON_COLOR_CURVE_STEP_PRE) ? &xform->pre_curve : + &xform->post_curve; + + colorop = search_colorop_compatible_curve(pipeline, previous_colorop, + curve, policy); + if (!colorop) + return NULL; + + switch (colorop->type) { + case WDRM_COLOROP_TYPE_1D_CURVE: + so.type = COLOROP_OBJECT_TYPE_CURVE; + so.curve_type_prop_val = prop_val_from_curve(device, colorop, curve); + break; + case WDRM_COLOROP_TYPE_1D_LUT: + lut_len = colorop->size; + lut_blob = drm_colorop_3x1d_lut_blob_from_curve(device, xform, + curve_step, lut_len); + if (!lut_blob) + return NULL; + so.type = COLOROP_OBJECT_TYPE_3x1D_LUT; + so.lut_3x1d_blob_id = lut_blob->blob_id; + break; + default: + weston_assert_not_reached(compositor, + "curve colorop should be 1D curve or 1D LUT"); + } + + return drm_colorop_state_create(pipeline_state, colorop, so); +} + +static struct drm_colorop_state * +mapping_create_colorop_state(struct drm_color_pipeline_state *pipeline_state, + struct drm_colorop *previous_colorop, + struct weston_color_transform *xform) +{ + struct drm_color_pipeline *pipeline = pipeline_state->pipeline; + struct weston_compositor *compositor = pipeline->plane->base.compositor; + struct drm_device *device = pipeline->plane->device; + struct weston_color_mapping *mapping = &xform->mapping; + struct drm_colorop_matrix_blob *mat_blob; + struct drm_colorop_state_object so = { 0 }; + struct drm_colorop *colorop; + + /* For now Weston has only matrices color mapping. */ + weston_assert_u32_eq(compositor, + mapping->type, WESTON_COLOR_MAPPING_TYPE_MATRIX); + + colorop = search_colorop_type(pipeline, previous_colorop, + WDRM_COLOROP_TYPE_CTM_3X4); + if (!colorop) + return NULL; + + mat_blob = drm_colorop_matrix_blob_from_mapping(device, xform); + if (!mat_blob) + return NULL; + + so.type = COLOROP_OBJECT_TYPE_MATRIX; + so.matrix_blob_id = mat_blob->blob_id; + + return drm_colorop_state_create(pipeline_state, colorop, so); +} + +static struct drm_color_pipeline_state * +drm_color_pipeline_state_from_xform_steps(struct drm_color_pipeline *pipeline, + struct weston_color_transform *xform, + enum lowering_curve_policy policy, + const char *indent) +{ + struct drm_backend *b = pipeline->plane->device->backend; + struct drm_color_pipeline_state *pipeline_state; + struct drm_colorop_state *colorop_state; + struct drm_colorop *previous_colorop; + uint32_t type; + + pipeline_state = drm_color_pipeline_state_create(pipeline); + + /* First previous_colorop: none. */ + previous_colorop = NULL; + + /* Find colorop for pre-curve. */ + type = xform->pre_curve.type; + if (type != WESTON_COLOR_CURVE_TYPE_IDENTITY) { + colorop_state = curve_create_colorop_state(pipeline_state, + previous_colorop, xform, + WESTON_COLOR_CURVE_STEP_PRE, + policy); + if (!colorop_state) + goto err; + + previous_colorop = colorop_state->colorop; + } + + /* Find colorop for color mapping. */ + type = xform->mapping.type; + if (type != WESTON_COLOR_MAPPING_TYPE_IDENTITY) { + colorop_state = mapping_create_colorop_state(pipeline_state, + previous_colorop, xform); + if (!colorop_state) + goto err; + + previous_colorop = colorop_state->colorop; + } + + /* Find colorop for post-curve. */ + type = xform->post_curve.type; + if (type != WESTON_COLOR_CURVE_TYPE_IDENTITY) { + colorop_state = curve_create_colorop_state(pipeline_state, + previous_colorop, xform, + WESTON_COLOR_CURVE_STEP_POST, + policy); + if (!colorop_state) + goto err; + } + + drm_debug(b, "%s[colorop] color pipeline id %u IS compatible with xform t%u;\n" \ + "%s policy: %s\n", + indent, pipeline->id, xform->id, indent, + lowering_curve_policy_str(policy)); + return pipeline_state; + +err: + drm_color_pipeline_state_destroy(pipeline_state); + drm_debug(b, "%s[colorop] color pipeline id %u NOT compatible with xform t%u;\n" \ + "%s policy: %s\n", + indent, pipeline->id, xform->id, indent, + lowering_curve_policy_str(policy)); + return NULL; +} + +/** + * Given a color transformation, returns a color pipeline state that can + * be used to offload such xform to KMS. + * + * @param plane The DRM plane that we plan to use to offload the view. + * @param xform The xform to offload. + * @param indent To print debug error messages with proper indentation. + * @return The color pipeline state, or NULL if no color pipelines are + * compatible with the xform. + */ +struct drm_color_pipeline_state * +drm_color_pipeline_state_from_xform(struct drm_plane *plane, + struct weston_color_transform *xform, + const char *indent) +{ + struct drm_backend *b = plane->device->backend; + struct drm_color_pipeline_state *pipeline_state; + unsigned int i, mode_index; + enum lowering_curve_policy policy; + enum lowering_curve_policy policy_modes[2] = { + LOWERING_CURVE_POLICY_DENY, LOWERING_CURVE_POLICY_ALLOW + }; + + drm_debug(b, "%s[colorop] searching color pipeline compatible with xform t%u\n", + indent, xform->id); + + /** + * Try to find a compatible pipeline. + * + * First, we try to find a compatible pipeline but not allowing Weston + * enumerated color curves to be lowered to parametric. If we can't find + * something, we start allowing that. + */ + if (xform->steps_valid) { + for (mode_index = 0; mode_index < ARRAY_LENGTH(policy_modes); mode_index++) { + policy = policy_modes[mode_index]; + for (i = 0; i < plane->num_color_pipelines; i++) { + pipeline_state = + drm_color_pipeline_state_from_xform_steps(&plane->pipelines[i], + xform, policy, indent); + if (pipeline_state) + return pipeline_state; + } + } + } + + return NULL; +} + static void drm_color_pipeline_print(struct drm_color_pipeline *pipeline, FILE *fp) { diff --git a/libweston/backend-drm/colorops.h b/libweston/backend-drm/colorops.h index f95ddb2ba..d07850ca1 100644 --- a/libweston/backend-drm/colorops.h +++ b/libweston/backend-drm/colorops.h @@ -28,6 +28,34 @@ #include "drm-internal.h" #include "drm-kms-enums.h" +struct drm_colorop_clut_blob { + /* drm_device::drm_colorop_clut_blob_list */ + struct wl_list link; + struct drm_device *device; + + /* Lifetime matches the xform. */ + struct weston_color_transform *xform; + struct wl_listener destroy_listener; + + uint32_t shaper_len; + uint32_t clut_len; + + uint32_t shaper_blob_id; + uint32_t clut_blob_id; +}; + +struct drm_colorop_matrix_blob { + /* drm_device::drm_colorop_matrix_blob_list */ + struct wl_list link; + struct drm_device *device; + + /* Lifetime matches the xform. */ + struct weston_color_transform *xform; + struct wl_listener destroy_listener; + + uint32_t blob_id; +}; + struct drm_colorop { struct drm_color_pipeline *pipeline; struct wl_list link; /* drm_pipeline::colorop_list */ @@ -46,14 +74,53 @@ struct drm_colorop { struct drm_property_info props[WDRM_COLOROP__COUNT]; }; +enum colorop_object_type { + COLOROP_OBJECT_TYPE_CURVE = 0, + COLOROP_OBJECT_TYPE_MATRIX, + COLOROP_OBJECT_TYPE_3x1D_LUT, +}; + +struct drm_colorop_state_object { + /* Defines which of the below is valid. The others are zero. */ + enum colorop_object_type type; + + uint64_t curve_type_prop_val; + uint32_t matrix_blob_id; + uint32_t lut_3x1d_blob_id; +}; + +struct drm_colorop_state { + struct drm_colorop *colorop; + /* struct drm_color_pipeline_state::colorop_state_list */ + struct wl_list link; + + /* Object that should be programmed through the colorop. */ + struct drm_colorop_state_object object; +}; + struct drm_color_pipeline { struct drm_plane *plane; struct wl_list colorop_list; /* drm_colorop::link */ uint32_t id; }; +struct drm_color_pipeline_state { + struct drm_color_pipeline *pipeline; + + /* struct drm_colorop_state::link */ + struct wl_list colorop_state_list; +}; + #if CAN_OFFLOAD_COLOR_PIPELINE +void +drm_color_pipeline_state_destroy(struct drm_color_pipeline_state *state); + +struct drm_color_pipeline_state * +drm_color_pipeline_state_from_xform(struct drm_plane *plane, + struct weston_color_transform *xform, + const char *indent); + struct drm_colorop_3x1d_lut_blob * drm_colorop_3x1d_lut_blob_create(struct drm_device *device, struct weston_color_transform *xform, @@ -68,6 +135,9 @@ drm_colorop_3x1d_lut_blob_search(struct drm_device *device, enum drm_colorop_3x1d_lut_blob_quantization quantization, uint32_t lut_len); +const char * +drm_colorop_type_to_str(struct drm_colorop *colorop); + void drm_plane_populate_color_pipelines(struct drm_plane *plane, drmModeObjectPropertiesPtr plane_props); @@ -77,6 +147,19 @@ drm_plane_release_color_pipelines(struct drm_plane *plane); #else /* CAN_OFFLOAD_COLOR_PIPELINE */ +static inline void +drm_color_pipeline_state_destroy(struct drm_color_pipeline_state *state) +{ +} + +static inline struct drm_color_pipeline_state * +drm_color_pipeline_state_from_xform(struct drm_plane *plane, + struct weston_color_transform *xform, + const char *indent) +{ + return NULL; +} + static inline struct drm_colorop_3x1d_lut_blob * drm_colorop_3x1d_lut_blob_create(struct drm_device *device, struct weston_color_transform *xform, @@ -97,6 +180,12 @@ drm_colorop_3x1d_lut_blob_search(struct drm_device *device, return NULL; } +static inline const char * +drm_colorop_type_to_str(struct drm_colorop *colorop) +{ + return "undefined"; +} + static inline void drm_plane_populate_color_pipelines(struct drm_plane *plane, drmModeObjectPropertiesPtr plane_props) diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index ac298c1d5..27c2a114b 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -253,6 +253,8 @@ struct drm_device { /* struct drm_colorop_3x1d_lut_blob::link */ struct wl_list drm_colorop_3x1d_lut_blob_list; + /* struct drm_colorop_matrix_blob::link */ + struct wl_list drm_colorop_matrix_blob_list; int reused_state_failures; }; @@ -441,6 +443,9 @@ struct drm_plane_state { struct weston_paint_node *paint_node; /**< maintained for drm_assign_planes only */ + /* only when a color transformation is being offloaded */ + struct drm_color_pipeline_state *pipeline_state; + int32_t src_x, src_y; uint32_t src_w, src_h; int32_t dest_x, dest_y; diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index 4b44e2689..d22d03745 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -4162,6 +4162,7 @@ drm_device_destroy(struct drm_device *device) drm_writeback_destroy(writeback); weston_assert_list_empty(ec, &device->drm_colorop_3x1d_lut_blob_list); + weston_assert_list_empty(ec, &device->drm_colorop_matrix_blob_list); if (device->drm_event_source) wl_event_source_remove(device->drm_event_source); @@ -4502,6 +4503,7 @@ drm_device_create(struct drm_backend *backend, create_planes(device); wl_list_init(&device->drm_colorop_3x1d_lut_blob_list); + wl_list_init(&device->drm_colorop_matrix_blob_list); wl_list_init(&device->writeback_connector_list); if (drm_backend_discover_connectors(device, device->kms_device->udev_device, res) < 0) { diff --git a/libweston/backend-drm/kms.c b/libweston/backend-drm/kms.c index 36e247480..789f7fb48 100644 --- a/libweston/backend-drm/kms.c +++ b/libweston/backend-drm/kms.c @@ -37,7 +37,10 @@ #include #include #include "shared/helpers.h" +#include "shared/string-helpers.h" +#include "shared/weston-assert.h" #include "shared/weston-drm-fourcc.h" +#include "colorops.h" #include "drm-internal.h" #include "pixel-formats.h" #include "presentation-time-server-protocol.h" @@ -1159,6 +1162,28 @@ plane_add_prop(drmModeAtomicReq *req, struct drm_plane *plane, return (ret <= 0) ? -1 : 0; } +static int +colorop_add_prop(drmModeAtomicReq *req, struct drm_colorop *colorop, + enum wdrm_colorop_property prop, uint64_t val) +{ + struct drm_plane *plane = colorop->pipeline->plane; + struct drm_device *device = plane->device; + struct drm_backend *b = device->backend; + struct drm_property_info *info = &colorop->props[prop]; + int ret; + + drm_debug(b, "\t\t\t[COLOROP:%lu] %lu (%s) -> %llu (0x%llx)\n", + (unsigned long) colorop->id, + (unsigned long) info->prop_id, info->name, + (unsigned long long) val, (unsigned long long) val); + + if (info->prop_id == 0) + return -1; + + ret = drmModeAtomicAddProperty(req, colorop->id, info->prop_id, val); + return (ret <= 0) ? -1 : 0; +} + static bool drm_connector_has_prop(struct drm_connector *connector, enum wdrm_connector_property prop) @@ -1360,6 +1385,156 @@ drm_plane_set_color_range(struct drm_plane *plane, return plane_add_prop(req, plane, WDRM_PLANE_COLOR_RANGE, color_range); } +static bool +colorop_program(drmModeAtomicReq *req, struct drm_colorop *colorop, + enum wdrm_colorop_property colorop_prop, + uint64_t prop_val, char **err_msg) +{ + int ret; + + if (colorop->can_bypass) { + ret = colorop_add_prop(req, colorop, WDRM_COLOROP_BYPASS, 0); + if (ret < 0) { + str_printf(err_msg, "failed to set colorop id %u bypass == false", + colorop->id); + return false; + } + } + + ret = colorop_add_prop(req, colorop, colorop_prop, prop_val); + if (ret < 0) { + str_printf(err_msg, "failed to program colorop id %u type %s", + colorop->id, drm_colorop_type_to_str(colorop)); + return false; + } + + return true; +} + +static bool +set_interp(drmModeAtomicReq *req, struct drm_colorop *colorop, + enum wdrm_colorop_property interp_prop, uint64_t interp_val) +{ + struct drm_property_info *info = &colorop->props[interp_prop]; + int ret; + + if (info->enum_values[interp_val].valid) { + ret = colorop_add_prop(req, colorop, interp_prop, interp_val); + if (ret >= 0) + return true; + } + + return false; +} + +static bool +drm_colorop_program(drmModeAtomicReq *req, struct drm_colorop_state *colorop_state, + const char *indent, char **err_msg) +{ + struct drm_colorop *colorop = colorop_state->colorop; + struct drm_color_pipeline *pipeline = colorop->pipeline; + struct drm_backend *b = pipeline->plane->device->backend; + struct weston_compositor *compositor = pipeline->plane->base.compositor; + enum wdrm_colorop_property colorop_prop; + uint64_t prop_val; + + switch (colorop_state->object.type) { + case COLOROP_OBJECT_TYPE_CURVE: + colorop_prop = WDRM_COLOROP_CURVE_1D; + prop_val = colorop_state->object.curve_type_prop_val; + return colorop_program(req, colorop, colorop_prop, prop_val, err_msg); + case COLOROP_OBJECT_TYPE_MATRIX: + colorop_prop = WDRM_COLOROP_DATA; + prop_val = colorop_state->object.matrix_blob_id; + return colorop_program(req, colorop, colorop_prop, prop_val, err_msg); + case COLOROP_OBJECT_TYPE_3x1D_LUT: + if (!set_interp(req, colorop, WDRM_COLOROP_LUT1D_INTERPOLATION, + WDRM_COLOROP_LUT1D_INTERPOLATION_LINEAR)) + drm_debug(b, "%s[colorop] linear LUT1D interpolation not supported or failed to set;\n" + "%susing current value set on driver\n", indent, indent); + colorop_prop = WDRM_COLOROP_DATA; + prop_val = colorop_state->object.lut_3x1d_blob_id; + return colorop_program(req, colorop, colorop_prop, prop_val, err_msg); + } + weston_assert_not_reached(compositor, + "unknown drm_colorop_state object type"); +} + +static struct drm_colorop_state * +drm_colorop_state_iter(struct drm_color_pipeline_state *pipeline_state, + struct drm_colorop_state *iter) +{ + struct wl_list *list = &pipeline_state->colorop_state_list; + struct wl_list *node; + + if (iter) + node = iter->link.next; + else + node = list->next; + + if (node == list) + return NULL; + + return container_of(node, struct drm_colorop_state, link); +} + +static int +drm_color_pipeline_program(drmModeAtomicReq *req, + struct drm_color_pipeline_state *pipeline_state, + const char *indent) +{ + struct drm_color_pipeline *pipeline = pipeline_state->pipeline; + struct drm_plane *plane = pipeline->plane; + struct drm_backend *b = plane->device->backend; + struct drm_colorop_state *colorop_state; + struct drm_colorop *colorop; + char *err_msg; + int ret_drm; + bool ret; + + drm_debug(b, "%s[PLANE:%lu] %lu (%s) -> %llu (0x%llx)\n", + indent, (unsigned long) plane->plane_id, + (unsigned long) plane->pipeline_props_id, "COLOR_PIPELINE", + (unsigned long long) pipeline_state->pipeline->id, + (unsigned long long) pipeline_state->pipeline->id); + + colorop_state = drm_colorop_state_iter(pipeline_state, + NULL /* previous colorop state (none) */); + wl_list_for_each(colorop, &pipeline->colorop_list, link) { + /* If a colorop is not in the colorop state list, bypass it. */ + if (!colorop_state || colorop != colorop_state->colorop) { + weston_assert_true(b->compositor, colorop->can_bypass); + + ret_drm = colorop_add_prop(req, colorop, WDRM_COLOROP_BYPASS, 1); + if (ret_drm >= 0) + continue; + + drm_debug(b, "%s%s[colorop] failed to set colorop id %u bypass == true", + indent, indent, colorop->id); + goto err; + } + + ret = drm_colorop_program(req, colorop_state, indent, &err_msg); + if (!ret) { + drm_debug(b, "%s%s[colorop] %s\n", indent, indent, err_msg); + free(err_msg); + goto err; + } + + colorop_state = drm_colorop_state_iter(pipeline_state, colorop_state); + } + weston_assert_ptr_null(b->compositor, colorop_state); + + /* Set plane pipeline. */ + drmModeAtomicAddProperty(req, plane->plane_id, + plane->pipeline_props_id, pipeline->id); + return 0; + +err: + drm_debug(b, "%s%s[colorop] failed to program pipeline\n", indent, indent); + return -1; +} + static int drm_output_apply_state_atomic(struct drm_output_state *state, drmModeAtomicReq *req, @@ -1521,6 +1696,16 @@ drm_output_apply_state_atomic(struct drm_output_state *state, ret |= plane_add_prop(req, plane, WDRM_PLANE_FB_DAMAGE_CLIPS, plane_state->damage_blob_id); + if (plane->props[WDRM_PLANE_COLOR_PIPELINE].prop_id != 0) { + if (plane_state->pipeline_state) { + ret |= drm_color_pipeline_program(req, plane_state->pipeline_state, + "\t\t\t"); + } else { + ret |= plane_add_prop(req, plane, + WDRM_PLANE_COLOR_PIPELINE, 0); + } + } + if (plane_state->fb && plane_state->fb->format) pinfo = plane_state->fb->format; diff --git a/libweston/backend-drm/state-helpers.c b/libweston/backend-drm/state-helpers.c index dbcef7696..dd8743246 100644 --- a/libweston/backend-drm/state-helpers.c +++ b/libweston/backend-drm/state-helpers.c @@ -32,6 +32,7 @@ #include #include +#include "colorops.h" #include "drm-internal.h" #include "shared/weston-assert.h" #include "shared/weston-drm-fourcc.h" @@ -95,6 +96,8 @@ drm_plane_state_free(struct drm_plane_state *state, bool force) state->in_fence_fd = -1; state->zpos = DRM_PLANE_ZPOS_INVALID_PLANE; state->alpha = DRM_PLANE_ALPHA_OPAQUE; + drm_color_pipeline_state_destroy(state->pipeline_state); + state->pipeline_state = NULL; /* Once the damage blob has been submitted, it is refcounted internally * by the kernel, which means we can safely discard it. diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index 9e4c1f19b..0babeb4f9 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -39,6 +39,7 @@ #include "drm-internal.h" #include "color.h" +#include "colorops.h" #include "color-representation.h" #include "linux-dmabuf.h" #include "presentation-time-server-protocol.h" @@ -141,6 +142,21 @@ drm_output_try_paint_node_on_plane(struct drm_plane_handle *handle, state->fb = drm_fb_ref(fb); state->in_fence_fd = surface->acquire_fence_fd; + drm_color_pipeline_state_destroy(state->pipeline_state); + state->pipeline_state = NULL; + if (pnode->surf_xform.transform) { + state->pipeline_state = + drm_color_pipeline_state_from_xform(plane, + pnode->surf_xform.transform, + "\t\t\t\t"); + if (!state->pipeline_state) { + drm_debug(b, "\t\t\t\t[view] not placing paint node %s on plane %lu: " + "not compatible with surface color xform\n", + pnode->internal_name, (unsigned long) plane->plane_id); + goto out; + } + } + if (fb->format && fb->format->color_model == COLOR_MODEL_YUV) { struct weston_color_representation color_rep; const struct weston_color_matrix_coef_info *matrix_coef_info; @@ -584,6 +600,14 @@ pnode_can_use_plane(struct drm_output_state *output_state, return false; } + /* If we have a surf color xform we need to be able to offload that to KMS. */ + if (pnode->surf_xform.transform && plane->num_color_pipelines == 0) { + drm_debug(b, "\t\t\t\t[plane] not trying plane %d: surf_xform present " + "but plane has no color pipelines\n", + plane->plane_id); + return false; + } + /* If the surface buffer has an in-fence fd, but the plane doesn't * support fences, we can't place the buffer on this plane. */ if (pnode->surface->acquire_fence_fd >= 0 && @@ -1371,8 +1395,8 @@ drm_output_propose_state(struct weston_output *output_base, pnode->try_view_on_plane_failure_reasons |= FAILURE_REASONS_OUTPUT_COLOR_EFFECT; - if (pnode->surf_xform.transform != NULL || - !pnode->surf_xform.identity_pipeline) + if (pnode->surf_xform.transform && (!device->color_pipeline_supported || + !pnode->output->from_blend_to_output_by_backend)) pnode->try_view_on_plane_failure_reasons |= FAILURE_REASONS_NO_COLOR_TRANSFORM;