Merge branch 'offload-pre-blend' into 'main'

Introduce support to KMS color pipelines to offload pre-blend xform

See merge request wayland/weston!1702
This commit is contained in:
Leandro Ribeiro 2026-05-05 14:07:42 +00:00
commit 229743ca53
19 changed files with 2158 additions and 181 deletions

View file

@ -4076,9 +4076,13 @@ load_drm_backend(struct weston_compositor *c, int *argc, char **argv,
weston_config_section_get_bool(section, "offload-blend-to-output",
&offload_blend_to_output, false);
if (!c->color_manager && offload_blend_to_output)
if (!c->color_manager)
offload_blend_to_output = false;
#if !CAN_OFFLOAD_COLOR_PIPELINE
offload_blend_to_output = false;
#endif
config.offload_blend_to_output = offload_blend_to_output;
weston_config_section_get_string(section,

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,204 @@
/*
* Copyright © 2025-2026 Collabora, Ltd.
*
* 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, sublicense, 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
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* 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.
*/
#pragma once
#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 */
enum wdrm_colorop_type type;
uint32_t id;
/* Some colorop's can be bypassed. */
bool can_bypass;
/* Only useful for 1D and 3D LUT colorop's. */
uint32_t size;
/* Holds the properties for the 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,
COLOROP_OBJECT_TYPE_3D_LUT,
COLOROP_OBJECT_TYPE_MULTIPLIER,
};
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;
uint32_t lut_3d_blob_id;
uint64_t multiplier;
};
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,
enum weston_color_curve_step curve_step,
enum drm_colorop_3x1d_lut_blob_quantization quantization,
struct weston_vec3f *cm_lut, uint32_t lut_len);
struct drm_colorop_3x1d_lut_blob *
drm_colorop_3x1d_lut_blob_search(struct drm_device *device,
struct weston_color_transform *xform,
enum weston_color_curve_step curve_step,
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);
void
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,
enum weston_color_curve_step curve_step,
enum drm_colorop_3x1d_lut_blob_quantization quantization,
struct weston_vec3f *cm_lut, uint32_t lut_len)
{
return NULL;
}
static inline struct drm_colorop_3x1d_lut_blob *
drm_colorop_3x1d_lut_blob_search(struct drm_device *device,
struct weston_color_transform *xform,
enum weston_color_curve_step curve_step,
enum drm_colorop_3x1d_lut_blob_quantization quantization,
uint32_t lut_len)
{
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)
{
}
static inline void
drm_plane_release_color_pipelines(struct drm_plane *plane)
{
}
#endif /* CAN_OFFLOAD_COLOR_PIPELINE */

View file

@ -27,6 +27,8 @@
* SOFTWARE.
*/
#pragma once
#include "config.h"
#include <errno.h>
@ -227,6 +229,8 @@ struct drm_device {
bool aspect_ratio_supported;
bool color_pipeline_supported;
int32_t cursor_width;
int32_t cursor_height;
@ -247,8 +251,12 @@ struct drm_device {
/* drm_backend::kms_list */
struct wl_list link;
/* struct drm_colorop_3x1d_lut::link */
struct wl_list drm_colorop_3x1d_lut_list;
/* struct drm_colorop_3x1d_lut_blob::link */
struct wl_list drm_colorop_3x1d_lut_blob_list;
/* struct drm_colorop_clut_blob::link */
struct wl_list drm_colorop_clut_blob_list;
/* struct drm_colorop_matrix_blob::link */
struct wl_list drm_colorop_matrix_blob_list;
int reused_state_failures;
};
@ -393,6 +401,30 @@ struct drm_output_state {
bool planes_enabled;
};
enum drm_colorop_3x1d_lut_blob_quantization {
DRM_COLOROP_3X1D_LUT_BLOB_QUANTIZATION_U16 = 0,
DRM_COLOROP_3X1D_LUT_BLOB_QUANTIZATION_U32,
};
struct drm_colorop_3x1d_lut_blob {
/* drm_device::drm_colorop_3x1d_lut_blob_list */
struct wl_list link;
struct drm_device *device;
/* Lifetime matches the xform. */
struct weston_color_transform *xform;
struct wl_listener destroy_listener;
/* Which curve of the xform the 3x1D LUT was generated from. */
enum weston_color_curve_step curve_step;
enum drm_colorop_3x1d_lut_blob_quantization quantization;
uint32_t lut_len;
uint32_t blob_id;
};
/**
* Plane state holds the dynamic state for a plane: where it is positioned,
* and which buffer it is currently displaying.
@ -413,6 +445,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;
@ -477,6 +512,10 @@ struct drm_plane {
struct wl_list link;
struct weston_drm_format_array formats;
uint32_t pipeline_props_id;
uint32_t num_color_pipelines;
struct drm_color_pipeline *pipelines;
};
struct drm_plane_handle {
@ -548,19 +587,6 @@ struct drm_writeback {
struct weston_drm_format_array formats;
};
struct drm_colorop_3x1d_lut {
/* drm_device::drm_colorop_3x1d_lut_list */
struct wl_list link;
struct drm_device *device;
uint64_t lut_size;
struct weston_color_transform *xform;
struct wl_listener destroy_listener;
uint32_t blob_id;
};
struct drm_head {
struct weston_head base;
struct drm_connector connector;
@ -643,7 +669,7 @@ struct drm_output {
bool legacy_gamma_not_supported;
uint16_t legacy_gamma_size;
struct drm_colorop_3x1d_lut *blend_to_output_xform;
struct drm_colorop_3x1d_lut_blob *blend_to_output_xform;
/* Plane being displayed directly on the CRTC */
struct drm_plane_handle *scanout_handle;
@ -868,6 +894,7 @@ extern struct drm_property_enum_info content_protection_enums[];
extern struct drm_property_enum_info hdcp_content_type_enums[];
extern const struct drm_property_info connector_props[];
extern const struct drm_property_info crtc_props[];
extern const struct drm_property_info colorop_props[];
int
init_kms_caps(struct drm_device *device);

View file

@ -57,6 +57,7 @@ enum wdrm_plane_property {
WDRM_PLANE_ROTATION,
WDRM_PLANE_ALPHA,
WDRM_PLANE_COLOR_ENCODING,
WDRM_PLANE_COLOR_PIPELINE,
WDRM_PLANE_COLOR_RANGE,
WDRM_PLANE__COUNT
};
@ -95,6 +96,20 @@ enum wdrm_plane_color_encoding {
};
#define WDRM_PLANE_COLOR_ENCODING_DEFAULT WDRM_PLANE_COLOR_ENCODING_BT709
/**
* Possible values for the WDRM_PLANE_COLOR_PIPELINE property.
*
* This property is special: the enum values are not deterministic. Each enum
* value corresponds to the id of a colorop created at runtime by the KMS
* driver. drm_property_info_populate() expects well-known values for enum
* properties, so a dummy value is defined to allow it to populate plane->props
* correctly.
*/
enum wdrm_plane_color_pipeline {
WDRM_PLANE_COLOR_PIPELINE_DUMMY = 0,
WDRM_PLANE_COLOR_PIPELINE__COUNT
};
/**
* Possible values for the WDRM_PLANE_COLOR_RANGE property.
*/
@ -105,6 +120,65 @@ enum wdrm_plane_color_range {
};
#define WDRM_PLANE_COLOR_RANGE_DEFAULT WDRM_PLANE_COLOR_RANGE_LIMITED
/**
* List of properties attached to a DRM colorop.
*/
enum wdrm_colorop_property {
WDRM_COLOROP_TYPE = 0,
WDRM_COLOROP_NEXT,
WDRM_COLOROP_BYPASS,
WDRM_COLOROP_SIZE,
WDRM_COLOROP_DATA,
WDRM_COLOROP_MULTIPLIER,
WDRM_COLOROP_LUT1D_INTERPOLATION,
WDRM_COLOROP_LUT3D_INTERPOLATION,
WDRM_COLOROP_CURVE_1D,
WDRM_COLOROP__COUNT,
};
/**
* Possible values for the WDRM_COLOROP_TYPE property.
*/
enum wdrm_colorop_type {
WDRM_COLOROP_TYPE_1D_CURVE = 0,
WDRM_COLOROP_TYPE_1D_LUT,
WDRM_COLOROP_TYPE_CTM_3X4,
WDRM_COLOROP_TYPE_MULTIPLIER,
WDRM_COLOROP_TYPE_3D_LUT,
WDRM_COLOROP_TYPE__COUNT,
};
/**
* Possible values for the WDRM_COLOROP_CURVE_1D property.
*/
enum wdrm_colorop_curve_1d {
WDRM_COLOROP_CURVE_1D_SRGB_EOTF = 0,
WDRM_COLOROP_CURVE_1D_SRGB_INV_EOTF,
WDRM_COLOROP_CURVE_1D_PQ_125_EOTF,
WDRM_COLOROP_CURVE_1D_PQ_125_INV_EOTF,
WDRM_COLOROP_CURVE_1D_BT2020_INV_OETF,
WDRM_COLOROP_CURVE_1D_BT2020_OETF,
WDRM_COLOROP_CURVE_1D_GAMMA_22,
WDRM_COLOROP_CURVE_1D_GAMMA_22_INV,
WDRM_COLOROP_CURVE_1D__COUNT,
};
/**
* Possible values for the WDRM_COLOROP_LUT1D_INTERPOLATION property.
*/
enum wdrm_colorop_lut1d_interpolation {
WDRM_COLOROP_LUT1D_INTERPOLATION_LINEAR = 0,
WDRM_COLOROP_LUT1D_INTERPOLATION__COUNT,
};
/**
* Possible values for the WDRM_COLOROP_LUT3D_INTERPOLATION property.
*/
enum wdrm_colorop_lut3d_interpolation {
WDRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL = 0,
WDRM_COLOROP_LUT3D_INTERPOLATION__COUNT,
};
/**
* List of properties attached to a DRM connector
*/

View file

@ -51,6 +51,7 @@
#include <libweston/libweston.h>
#include <libweston/backend-drm.h>
#include <libweston/weston-log.h>
#include "colorops.h"
#include "drm-internal.h"
#include "shared/hash.h"
#include "shared/helpers.h"
@ -1485,6 +1486,8 @@ drm_plane_create(struct drm_device *device, const drmModePlane *kplane)
goto err;
}
drm_plane_populate_color_pipelines(plane, props);
drmModeFreeObjectProperties(props);
if (plane->type == WDRM_PLANE_TYPE__COUNT)
@ -1588,6 +1591,7 @@ drm_plane_destroy(struct drm_plane *plane)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
drm_plane_state_free(plane->state_cur, true);
drm_property_info_free(plane->props, WDRM_PLANE__COUNT);
drm_plane_release_color_pipelines(plane);
weston_plane_release(&plane->base);
weston_drm_format_array_fini(&plane->formats);
wl_list_remove(&plane->link);
@ -2231,62 +2235,6 @@ drm_output_init_legacy_gamma_size(struct drm_output *output)
return 0;
}
static void
drm_colorop_3x1d_lut_destroy(struct drm_colorop_3x1d_lut *lut)
{
wl_list_remove(&lut->destroy_listener.link);
wl_list_remove(&lut->link);
drmModeDestroyPropertyBlob(lut->device->kms_device->fd, lut->blob_id);
free(lut);
}
static void
drm_colorop_3x1d_lut_destroy_handler(struct wl_listener *l, void *data)
{
struct drm_colorop_3x1d_lut *lut;
lut = wl_container_of(l, lut, destroy_listener);
assert(lut->xform == data);
drm_colorop_3x1d_lut_destroy(lut);
}
static struct drm_colorop_3x1d_lut *
drm_colorop_3x1d_lut_search(struct drm_device *device,
struct weston_color_transform *xform,
uint64_t lut_size)
{
struct drm_colorop_3x1d_lut *colorop_lut;
wl_list_for_each(colorop_lut, &device->drm_colorop_3x1d_lut_list, link)
if (colorop_lut->xform == xform && colorop_lut->lut_size == lut_size)
return colorop_lut;
return NULL;
}
static struct drm_colorop_3x1d_lut *
drm_colorop_3x1d_lut_create(struct weston_color_transform *xform,
struct drm_device *device, uint64_t lut_size,
uint32_t blob_id)
{
struct drm_colorop_3x1d_lut *lut;
lut = xzalloc(sizeof(*lut));
lut->device = device;
lut->blob_id = blob_id;
lut->xform = xform;
lut->lut_size = lut_size;
wl_list_insert(&device->drm_colorop_3x1d_lut_list, &lut->link);
lut->destroy_listener.notify = drm_colorop_3x1d_lut_destroy_handler;
wl_signal_add(&lut->xform->destroy_signal, &lut->destroy_listener);
return lut;
}
static struct weston_vec3f *
lut_3x1d_from_blend_to_output(struct weston_compositor *compositor,
struct weston_color_transform *xform,
@ -2295,8 +2243,8 @@ lut_3x1d_from_blend_to_output(struct weston_compositor *compositor,
/**
* We expect steps to be valid for blend-to-output, as LittleCMS is
* always able to optimize such xform. If that's invalid, we'd need to
* use to_shaper_plus_3dlut() to offload the xform, but the DRM API
* currently only supports us programming a LUT after blending.
* use to_clut() to offload the xform, but the DRM API currently only
* supports us programming a LUT after blending.
*/
if (!xform->steps_valid) {
str_printf(err_msg, "xform color steps are invalid");
@ -2333,15 +2281,12 @@ drm_output_pick_blend_to_output(struct drm_output *output)
struct weston_compositor *compositor = output->base.compositor;
struct drm_device *device = output->device;
struct drm_backend *b = device->backend;
struct drm_colorop_3x1d_lut *colorop_lut;
struct drm_colorop_3x1d_lut_blob *colorop_lut;
struct weston_color_transform *xform;
struct drm_color_lut *drm_lut;
size_t lut_size;
uint32_t gamma_lut_blob_id;
enum weston_color_curve_step curve_step;
size_t lut_len;
struct weston_vec3f *cm_lut;
char *err_msg;
unsigned int i;
int ret;
/* Check if there's actually something to offload. */
weston_assert_ptr_not_null(compositor, output->base.color_outcome);
@ -2349,23 +2294,32 @@ drm_output_pick_blend_to_output(struct drm_output *output)
if (!xform)
return 0;
lut_size = output->crtc->lut_size;
if (lut_size == 0) {
lut_len = output->crtc->lut_size;
if (lut_len == 0) {
drm_debug(b, "[output] can't offload blend-to-output: GAMMA_LUT_SIZE unsupported\n");
return -1;
}
/**
* First let's check if the xform has already been cached. If that's the
* For now we expect blend-to-output to be composed of pre-curve only,
* so lut_3x1d_from_blend_to_output() will return a LUT it creates from
* the xform pre-curve.
*/
curve_step = WESTON_COLOR_CURVE_STEP_PRE;
/**
* First let's check if the LUT has already been cached. If that's the
* case, we make use of it.
*/
colorop_lut = drm_colorop_3x1d_lut_search(device, xform, lut_size);
colorop_lut = drm_colorop_3x1d_lut_blob_search(device, xform, curve_step,
DRM_COLOROP_3X1D_LUT_BLOB_QUANTIZATION_U16,
lut_len);
if (colorop_lut) {
output->blend_to_output_xform = colorop_lut;
return 0;
}
cm_lut = lut_3x1d_from_blend_to_output(compositor, xform, lut_size, &err_msg);
cm_lut = lut_3x1d_from_blend_to_output(compositor, xform, lut_len, &err_msg);
if (!cm_lut) {
drm_debug(b, "[output] failed to create 3x1D LUT for blend-to-output: %s\n",
err_msg);
@ -2373,24 +2327,16 @@ drm_output_pick_blend_to_output(struct drm_output *output)
return -1;
}
drm_lut = xzalloc(lut_size * sizeof(*drm_lut));
for (i = 0; i < lut_size; i++) {
drm_lut[i].red = cm_lut[i].r * 0xffff;
drm_lut[i].green = cm_lut[i].g * 0xffff;
drm_lut[i].blue = cm_lut[i].b * 0xffff;
}
output->blend_to_output_xform =
drm_colorop_3x1d_lut_blob_create(device, xform, curve_step,
DRM_COLOROP_3X1D_LUT_BLOB_QUANTIZATION_U16,
cm_lut, lut_len);
free(cm_lut);
ret = drmModeCreatePropertyBlob(device->kms_device->fd, drm_lut, lut_size * sizeof(*drm_lut),
&gamma_lut_blob_id);
free(drm_lut);
if (ret < 0) {
drm_debug(b, "[output] failed to create blob for gamma LUT\n");
if (!output->blend_to_output_xform) {
drm_debug(b, "[output] failed to create colorop 3x1D LUT");
return -1;
}
output->blend_to_output_xform =
drm_colorop_3x1d_lut_create(xform, device, lut_size,
gamma_lut_blob_id);
return 0;
}
@ -4200,6 +4146,7 @@ drm_kms_device_destroy(struct drm_kms_device *kms_device)
static void
drm_device_destroy(struct drm_device *device)
{
struct weston_compositor *ec = device->backend->compositor;
struct drm_crtc *crtc, *crtc_tmp;
struct drm_writeback *writeback, *writeback_tmp;
@ -4214,7 +4161,9 @@ drm_device_destroy(struct drm_device *device)
&device->writeback_connector_list, link)
drm_writeback_destroy(writeback);
weston_assert_true(ec, wl_list_empty(&device->drm_colorop_3x1d_lut_list));
weston_assert_list_empty(ec, &device->drm_colorop_3x1d_lut_blob_list);
weston_assert_list_empty(ec, &device->drm_colorop_clut_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);
@ -4554,7 +4503,9 @@ drm_device_create(struct drm_backend *backend,
wl_list_init(&device->plane_list);
create_planes(device);
wl_list_init(&device->drm_colorop_3x1d_lut_list);
wl_list_init(&device->drm_colorop_3x1d_lut_blob_list);
wl_list_init(&device->drm_colorop_clut_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) {

View file

@ -37,7 +37,10 @@
#include <libweston/libweston.h>
#include <libweston/backend-drm.h>
#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"
@ -91,6 +94,12 @@ struct drm_property_enum_info plane_color_encoding_enums[] = {
},
};
struct drm_property_enum_info plane_color_pipeline_enums[] = {
[WDRM_PLANE_COLOR_PIPELINE_DUMMY] = {
.name = "dummy",
},
};
struct drm_property_enum_info plane_color_range_enums[] = {
[WDRM_PLANE_COLOR_RANGE_LIMITED] = {
.name = "YCbCr limited range",
@ -131,6 +140,11 @@ const struct drm_property_info plane_props[] = {
.enum_values = plane_color_encoding_enums,
.num_enum_values = WDRM_PLANE_COLOR_ENCODING__COUNT,
},
[WDRM_PLANE_COLOR_PIPELINE] = {
.name = "COLOR_PIPELINE",
.enum_values = plane_color_pipeline_enums,
.num_enum_values = WDRM_PLANE_COLOR_PIPELINE__COUNT,
},
[WDRM_PLANE_COLOR_RANGE] = {
.name = "COLOR_RANGE",
.enum_values = plane_color_range_enums,
@ -138,6 +152,61 @@ const struct drm_property_info plane_props[] = {
},
};
static struct drm_property_enum_info colorop_type_enums[] = {
[WDRM_COLOROP_TYPE_1D_CURVE] = { .name = "1D Curve", },
[WDRM_COLOROP_TYPE_1D_LUT] = { .name = "1D LUT", },
[WDRM_COLOROP_TYPE_CTM_3X4] = { .name = "3x4 Matrix", },
[WDRM_COLOROP_TYPE_MULTIPLIER] = { .name = "Multiplier", },
[WDRM_COLOROP_TYPE_3D_LUT] = { .name = "3D LUT", },
};
static struct drm_property_enum_info colorop_curve_1d_enums[] = {
[WDRM_COLOROP_CURVE_1D_SRGB_EOTF] = { .name = "sRGB EOTF", },
[WDRM_COLOROP_CURVE_1D_SRGB_INV_EOTF] = { .name = "sRGB Inverse EOTF", },
[WDRM_COLOROP_CURVE_1D_PQ_125_EOTF] = { .name = "PQ 125 EOTF", },
[WDRM_COLOROP_CURVE_1D_PQ_125_INV_EOTF] = { .name = "PQ 125 Inverse EOTF", },
[WDRM_COLOROP_CURVE_1D_BT2020_INV_OETF] = { .name = "BT.2020 Inverse OETF", },
[WDRM_COLOROP_CURVE_1D_BT2020_OETF] = { .name = "BT.2020 OETF", },
[WDRM_COLOROP_CURVE_1D_GAMMA_22] = { .name = "Gamma 2.2", },
[WDRM_COLOROP_CURVE_1D_GAMMA_22_INV] = { .name = "Gamma 2.2 Inverse", },
};
static struct drm_property_enum_info colorop_lut1d_interpolation_enums[] = {
[WDRM_COLOROP_LUT1D_INTERPOLATION_LINEAR] = { .name = "Linear", },
};
static struct drm_property_enum_info colorop_lut3d_interpolation_enums[] = {
[WDRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL] = { .name = "Tetrahedral", },
};
const struct drm_property_info colorop_props[] = {
[WDRM_COLOROP_TYPE] = {
.name = "TYPE",
.enum_values = colorop_type_enums,
.num_enum_values = WDRM_COLOROP_TYPE__COUNT,
},
[WDRM_COLOROP_NEXT] = { .name = "NEXT", },
[WDRM_COLOROP_BYPASS] = { .name = "BYPASS", },
[WDRM_COLOROP_SIZE] = { .name = "SIZE", },
[WDRM_COLOROP_DATA] = { .name = "DATA", },
[WDRM_COLOROP_MULTIPLIER] = { .name = "MULTIPLIER", },
[WDRM_COLOROP_CURVE_1D] = {
.name = "CURVE_1D_TYPE",
.enum_values = colorop_curve_1d_enums,
.num_enum_values = WDRM_COLOROP_CURVE_1D__COUNT,
},
[WDRM_COLOROP_LUT1D_INTERPOLATION] = {
.name = "LUT1D_INTERPOLATION",
.enum_values = colorop_lut1d_interpolation_enums,
.num_enum_values = WDRM_COLOROP_LUT1D_INTERPOLATION__COUNT,
},
[WDRM_COLOROP_LUT3D_INTERPOLATION] = {
.name = "LUT3D_INTERPOLATION",
.enum_values = colorop_lut3d_interpolation_enums,
.num_enum_values = WDRM_COLOROP_LUT3D_INTERPOLATION__COUNT,
},
};
struct drm_property_enum_info dpms_state_enums[] = {
[WDRM_DPMS_STATE_OFF] = {
.name = "Off",
@ -1093,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)
@ -1294,6 +1385,168 @@ 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);
case COLOROP_OBJECT_TYPE_3D_LUT:
if (!set_interp(req, colorop, WDRM_COLOROP_LUT3D_INTERPOLATION,
WDRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL))
drm_debug(b, "%s[colorop] tetrahedral LUT3D 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_3d_blob_id;
return colorop_program(req, colorop, colorop_prop, prop_val, err_msg);
case COLOROP_OBJECT_TYPE_MULTIPLIER:
colorop_prop = WDRM_COLOROP_MULTIPLIER;
prop_val = colorop_state->object.multiplier;
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,
@ -1455,6 +1708,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;
@ -2079,6 +2342,13 @@ init_kms_caps(struct drm_device *device)
drmSetClientCap(device->kms_device->fd, DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1);
#ifdef DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE
ret = drmSetClientCap(device->kms_device->fd, DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE, 1);
device->color_pipeline_supported = (ret == 0);
#else
device->color_pipeline_supported = false;
#endif
ret = drmGetCap(device->kms_device->fd, DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP, &cap);
if (ret == 0)
device->tearing_supported = cap;

View file

@ -32,6 +32,10 @@ srcs_drm = [
presentation_time_server_protocol_h,
]
if (libdrm_supports_color_pipeline)
srcs_drm += 'colorops.c'
endif
deps_drm = [
dep_egl, # optional
dep_vulkan, # optional

View file

@ -32,6 +32,7 @@
#include <xf86drm.h>
#include <xf86drmMode.h>
#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.

View file

@ -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;

View file

@ -417,8 +417,7 @@ lcms_curve_matches_any_tf(struct weston_compositor *compositor,
const float lcms_curve_params[3][MAX_PARAMS_LCMS_PARAM_CURVE])
{
struct weston_color_curve_parametric curve = { 0 };
unsigned int i, j;
uint32_t n_lcms_curve_params;
unsigned int i;
curve.clamped_input = clamped_input;
@ -428,28 +427,34 @@ lcms_curve_matches_any_tf(struct weston_compositor *compositor,
* LittleCMS type 1 is the pure power-law curve, which is a
* special case of LINPOW. See init_curve_from_type_1().
*/
n_lcms_curve_params = 1;
curve.type = WESTON_COLOR_CURVE_PARAMETRIC_TYPE_LINPOW;
for (i = 0; i < 3; i++) {
curve.params.chan[i].g = lcms_curve_params[i][0];
/* a = 1, b = 0, c = 1, d = 0 */
curve.params.chan[i].a = 1.0f;
curve.params.chan[i].b = 0.0f;
curve.params.chan[i].c = 1.0f;
curve.params.chan[i].d = 0.0f;
}
break;
case 4:
/**
* LittleCMS type 4 is almost exactly the same as LINPOW. See
* init_curve_from_type_4().
*/
n_lcms_curve_params = 5;
curve.type = WESTON_COLOR_CURVE_PARAMETRIC_TYPE_LINPOW;
for (i = 0; i < 3; i++) {
curve.params.chan[i].g = lcms_curve_params[i][0];
curve.params.chan[i].a = lcms_curve_params[i][1];
curve.params.chan[i].b = lcms_curve_params[i][2];
curve.params.chan[i].c = lcms_curve_params[i][3];
curve.params.chan[i].d = lcms_curve_params[i][4];
}
break;
default:
return NULL;
}
weston_assert_u32_le(compositor,
n_lcms_curve_params, MAX_PARAMS_LCMS_PARAM_CURVE);
for (i = 0; i < 3; i++)
for (j = 0; j < n_lcms_curve_params; j++)
curve.params.chan[i].data[j] = lcms_curve_params[i][j];
return weston_color_tf_info_from_parametric_curve(&curve);
}
@ -2058,10 +2063,10 @@ cmlcms_color_transform_recipe_string(const struct cmlcms_color_transform_recipe
}
static bool
build_3d_lut(struct weston_compositor *compositor,
const struct cmlcms_color_transformer *transformer,
unsigned int len_shaper, const float *shaper,
unsigned int len_lut3d, float *lut3d)
build_clut(struct weston_compositor *compositor,
const struct cmlcms_color_transformer *transformer,
unsigned int len_shaper, const float *shaper,
unsigned int len_clut, float *clut)
{
const float *const red_curve = &shaper[0];
const float *const green_curve = &shaper[len_shaper];
@ -2077,27 +2082,26 @@ build_3d_lut(struct weston_compositor *compositor,
* Ensure the indices and byte counts cannot overflow,
* and memory usage does not get ridiculous. Arbitrary limit.
*/
weston_assert_u32_lt(compositor, len_lut3d, 100);
weston_assert_u32_lt(compositor, len_clut, 100);
/*
* A temporary allocation that holds two 1D LUTs of length len_lut3d
* and one scratch array of vec3f of length len_lut3d.
* A temporary allocation that holds two 1D LUTs of length len_clut
* and one scratch array of vec3f of length len_clut.
*/
const uint32_t bytes_per_elem = 2 * sizeof (float) + sizeof *rgb_in;
tmp = malloc(len_lut3d * bytes_per_elem);
tmp = malloc(len_clut * bytes_per_elem);
inverse_r = &tmp[0];
inverse_g = &tmp[len_lut3d];
rgb_in = (struct weston_vec3f *)&tmp[2 * len_lut3d];
inverse_g = &tmp[len_clut];
rgb_in = (struct weston_vec3f *)&tmp[2 * len_clut];
/*
* For each channel, use the shaper to compute the value x such that
* y(x) = index / (len - 1). As the shaper is a LUT, we find the closest
* neighbors of such point (x, y) and then use linear interpolation to
* estimate x.
*/
for (i = 0; i < len_lut3d; i++) {
float y = (float)i / (len_lut3d - 1);
for (i = 0; i < len_clut; i++) {
float y = (float)i / (len_clut - 1);
inverse_r[i] = weston_inverse_evaluate_lut1d(compositor,
len_shaper,
red_curve,
@ -2109,9 +2113,9 @@ build_3d_lut(struct weston_compositor *compositor,
}
/*
* Fill in the 3D LUT: LUT(Rin, Gin, Bin) = { Rout, Gout, Bout }
* Fill in the 3D cLUT: LUT(Rin, Gin, Bin) = { Rout, Gout, Bout }
* Each of Rin, Gin and Bin varies from 0.0 to 1.0. The range [0.0, 1.0]
* is evenly divided into len_lut3d number of sampling points. The
* is evenly divided into len_clut number of sampling points. The
* indices of the sampling points are index_r, index_g, index_b.
*
* To compute { Rout, Gout, Bout }, first Rin, Gin, Bin must go through
@ -2121,29 +2125,29 @@ build_3d_lut(struct weston_compositor *compositor,
* separable.
*
* The next step is not separable, so we iterate through all points in
* the 3D volume. The points are transformed len_lut3d points at a time
* the 3D volume. The points are transformed len_clut points at a time
* (rgb_in array) to strike a balance between the number of function
* calls and the memory requirements.
*/
for (index_b = 0; index_b < len_lut3d; index_b++) {
for (index_b = 0; index_b < len_clut; index_b++) {
float inverse_b = weston_inverse_evaluate_lut1d(compositor,
len_shaper,
blue_curve,
(float)index_b / (len_lut3d - 1));
for (i = 0; i < len_lut3d; i++)
(float)index_b / (len_clut - 1));
for (i = 0; i < len_clut; i++)
rgb_in[i].b = inverse_b;
for (index_g = 0; index_g < len_lut3d; index_g++) {
for (index_r = 0; index_r < len_lut3d; index_r++) {
for (index_g = 0; index_g < len_clut; index_g++) {
for (index_r = 0; index_r < len_clut; index_r++) {
rgb_in[index_r].g = inverse_g[index_g];
rgb_in[index_r].r = inverse_r[index_r];
}
index_r = 0;
i = 3 * (index_r + len_lut3d * (index_g + len_lut3d * index_b));
i = 3 * (index_r + len_clut * (index_g + len_clut * index_b));
cmlcms_color_transformer_eval(compositor, transformer,
(struct weston_vec3f *)&lut3d[i],
rgb_in, len_lut3d);
(struct weston_vec3f *)&clut[i],
rgb_in, len_clut);
}
}
@ -2256,16 +2260,16 @@ out:
/**
* Based on [1]. We get the transformer and decompose into a shaper
* (3x1D LUT) + 3D LUT. With that, we can reduce the 3D LUT dimension size
* (3x1D LUT) + 3D cLUT. With that, we can reduce the 3D LUT dimension size
* without losing precision. 3D LUT dimension size is problematic because it
* demands n³ memory.
*
* [1] https://www.littlecms.com/ASICprelinerization_CGIV08.pdf
*/
static bool
xform_to_shaper_plus_3dlut(struct weston_color_transform *xform_base,
uint32_t len_shaper, float *shaper,
uint32_t len_lut3d, float *lut3d)
xform_to_clut(struct weston_color_transform *xform_base,
uint32_t len_shaper, float *shaper,
uint32_t len_clut, float *clut)
{
struct cmlcms_color_transform *xform = to_cmlcms_xform(xform_base);
struct weston_compositor *compositor = xform_base->cm->compositor;
@ -2276,8 +2280,8 @@ xform_to_shaper_plus_3dlut(struct weston_color_transform *xform_base,
if (!ret)
return false;
ret = build_3d_lut(compositor, &xform->transformer,
len_shaper, shaper, len_lut3d, lut3d);
ret = build_clut(compositor, &xform->transformer,
len_shaper, shaper, len_clut, clut);
if (!ret)
return false;
@ -2306,7 +2310,7 @@ cmlcms_color_transform_create(struct weston_color_manager_lcms *cm,
xform = xzalloc(sizeof *xform);
weston_color_transform_init(&xform->base, &cm->base);
wl_list_init(&xform->link);
xform->base.to_shaper_plus_3dlut = xform_to_shaper_plus_3dlut;
xform->base.to_clut = xform_to_clut;
cmlcms_color_transform_recipe_copy(&xform->search_key, recipe);
weston_log_scope_printf(cm->transforms_scope,

View file

@ -279,6 +279,8 @@ static const struct weston_color_tf_info color_tf_info_table[] = {
.tf = WESTON_TF_BT1886,
.desc = "BT.1886",
.protocol_tf = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_BT1886,
.kms_colorop = WDRM_COLOROP_CURVE_1D__COUNT,
.kms_colorop_inverse = WDRM_COLOROP_CURVE_1D__COUNT,
.count_parameters = 0,
/**
* NOTE: This is the BT.1886 special case of L_B = 0 and
@ -292,6 +294,8 @@ static const struct weston_color_tf_info color_tf_info_table[] = {
.tf = WESTON_TF_GAMMA22,
.desc = "assumed display gamma 2.2",
.protocol_tf = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22,
.kms_colorop = WDRM_COLOROP_CURVE_1D_GAMMA_22,
.kms_colorop_inverse = WDRM_COLOROP_CURVE_1D_GAMMA_22_INV,
.count_parameters = 0,
.curve_params_valid = true,
.curve = POWER_LAW(2.2, true),
@ -301,6 +305,8 @@ static const struct weston_color_tf_info color_tf_info_table[] = {
.tf = WESTON_TF_GAMMA28,
.desc = "assumed display gamma 2.8",
.protocol_tf = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA28,
.kms_colorop = WDRM_COLOROP_CURVE_1D__COUNT,
.kms_colorop_inverse = WDRM_COLOROP_CURVE_1D__COUNT,
.count_parameters = 0,
.curve_params_valid = true,
.curve = POWER_LAW(2.8, true),
@ -310,12 +316,16 @@ static const struct weston_color_tf_info color_tf_info_table[] = {
.tf = WESTON_TF_EXT_LINEAR,
.desc = "extended linear",
.protocol_tf = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR,
.kms_colorop = WDRM_COLOROP_CURVE_1D__COUNT,
.kms_colorop_inverse = WDRM_COLOROP_CURVE_1D__COUNT,
.count_parameters = 0,
},
{
.tf = WESTON_TF_SRGB,
.desc = "sRGB piece-wise",
.protocol_tf = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB,
.kms_colorop = WDRM_COLOROP_CURVE_1D_SRGB_EOTF,
.kms_colorop_inverse = WDRM_COLOROP_CURVE_1D_SRGB_INV_EOTF,
.count_parameters = 0,
.curve_params_valid = true,
.curve = SRGB_PIECE_WISE(true),
@ -325,6 +335,8 @@ static const struct weston_color_tf_info color_tf_info_table[] = {
.tf = WESTON_TF_EXT_SRGB,
.desc = "Extended sRGB piece-wise",
.protocol_tf = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_SRGB,
.kms_colorop = WDRM_COLOROP_CURVE_1D__COUNT,
.kms_colorop_inverse = WDRM_COLOROP_CURVE_1D__COUNT,
.count_parameters = 0,
.curve_params_valid = true,
.curve = SRGB_PIECE_WISE(false),
@ -334,47 +346,63 @@ static const struct weston_color_tf_info color_tf_info_table[] = {
.tf = WESTON_TF_ST240,
.desc = "SMPTE ST 240",
.protocol_tf = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST240,
.kms_colorop = WDRM_COLOROP_CURVE_1D__COUNT,
.kms_colorop_inverse = WDRM_COLOROP_CURVE_1D__COUNT,
.count_parameters = 0,
},
{
.tf = WESTON_TF_ST428,
.desc = "SMPTE ST 428",
.protocol_tf = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST428,
.kms_colorop = WDRM_COLOROP_CURVE_1D__COUNT,
.kms_colorop_inverse = WDRM_COLOROP_CURVE_1D__COUNT,
.count_parameters = 0,
},
{
.tf = WESTON_TF_ST2084_PQ,
.desc = "Perceptual Quantizer",
.protocol_tf = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ,
.kms_colorop = WDRM_COLOROP_CURVE_1D__COUNT,
.kms_colorop_inverse = WDRM_COLOROP_CURVE_1D__COUNT,
.count_parameters = 0,
},
{
.tf = WESTON_TF_LOG_100,
.desc = "logarithmic 100:1",
.protocol_tf = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_LOG_100,
.kms_colorop = WDRM_COLOROP_CURVE_1D__COUNT,
.kms_colorop_inverse = WDRM_COLOROP_CURVE_1D__COUNT,
.count_parameters = 0,
},
{
.tf = WESTON_TF_LOG_316,
.desc = "logarithmic (100*Sqrt(10) : 1)",
.protocol_tf = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_LOG_316,
.kms_colorop = WDRM_COLOROP_CURVE_1D__COUNT,
.kms_colorop_inverse = WDRM_COLOROP_CURVE_1D__COUNT,
.count_parameters = 0,
},
{
.tf = WESTON_TF_XVYCC,
.desc = "IEC 61966-2-4 (xvYCC)",
.protocol_tf = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_XVYCC,
.kms_colorop = WDRM_COLOROP_CURVE_1D__COUNT,
.kms_colorop_inverse = WDRM_COLOROP_CURVE_1D__COUNT,
.count_parameters = 0,
},
{
.tf = WESTON_TF_HLG,
.desc = "Hybrid log-gamma",
.protocol_tf = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_HLG,
.kms_colorop = WDRM_COLOROP_CURVE_1D__COUNT,
.kms_colorop_inverse = WDRM_COLOROP_CURVE_1D__COUNT,
.count_parameters = 0,
},
{
.tf = WESTON_TF_POWER,
.desc = "power-law with custom exponent",
.kms_colorop = WDRM_COLOROP_CURVE_1D__COUNT,
.kms_colorop_inverse = WDRM_COLOROP_CURVE_1D__COUNT,
.count_parameters = 1,
},
};

View file

@ -110,6 +110,10 @@ struct weston_color_tf_info {
/** CM&HDR protocol extension value representing the tf. */
uint32_t protocol_tf;
/** KMS 1D curve colorop value representing the tf. */
uint32_t kms_colorop;
uint32_t kms_colorop_inverse;
/* The protocol also has support for parameterized functions, i.e.
* certain known functions that clients can define passing arbitrary
* parameters. */

View file

@ -383,8 +383,8 @@ struct weston_color_transform {
/**
* When this is true, users are allowed to use the steps described below
* (pre curve, color mapping and post curve) and implement the color
* transformation themselves. Otherwise this is forbidden and
* to_shaper_plus_3dlut() must be used.
* transformation themselves. Otherwise this is forbidden and to_clut()
* must be used.
*/
bool steps_valid;
@ -403,23 +403,23 @@ struct weston_color_transform {
struct weston_color_curve post_curve;
/**
* Decompose the color transformation into a shaper (3x1D LUT) and a 3D
* LUT.
* Decompose the color transformation into a shaper (3x1D LUT) followed
* by a 3D cLUT.
*
* \param xform_base The color transformation to decompose.
* \param len_shaper Number of taps in each of the 1D LUT.
* \param shaper Where the shaper is saved, caller's responsibility to
* allocate.
* \param len_lut3d The 3D LUT's length for each dimension.
* \param lut3d Where the 3D LUT is saved, caller's responsibility to
* allocate. Its layout on memory is: lut3d[B][G][R], i.e. R is the
* \param len_clut The 3D cLUT's length for each dimension.
* \param clut Where the 3D cLUT is saved, caller's responsibility to
* allocate. Its layout on memory is: clut[B][G][R], i.e. R is the
* innermost and its index grow faster, followed by G and then B.
* \return True on success, false otherwise.
*/
bool
(*to_shaper_plus_3dlut)(struct weston_color_transform *xform_base,
uint32_t len_shaper, float *shaper,
uint32_t len_lut3d, float *lut3d);
(*to_clut)(struct weston_color_transform *xform_base,
uint32_t len_shaper, float *shaper,
uint32_t len_clut, float *clut);
};
struct weston_cvd_correction {

View file

@ -800,7 +800,7 @@ weston_dmabuf_feedback_send_all(struct weston_compositor *compositor,
{
struct wl_resource *res;
weston_assert_true(compositor, !wl_list_empty(&dmabuf_feedback->resource_list));
weston_assert_list_not_empty(compositor, &dmabuf_feedback->resource_list);
wl_resource_for_each(res, &dmabuf_feedback->resource_list)
weston_dmabuf_feedback_send(dmabuf_feedback,
format_table, res, false);

View file

@ -208,6 +208,10 @@ if get_option('backend-drm')
dep_session_helper = declare_dependency(link_with: lib_session_helper)
endif
libdrm_version = dep_libdrm.version()
libdrm_supports_color_pipeline = libdrm_version.version_compare('>=2.4.130')
config_h.set10('CAN_OFFLOAD_COLOR_PIPELINE', libdrm_supports_color_pipeline)
lib_libinput_backend = static_library(
'libinput-backend',
[

View file

@ -376,9 +376,9 @@ gl_renderer_color_transform_create_3dlut(struct gl_renderer *gr,
{
struct gl_renderer_color_transform *gl_xform = NULL;
float *shaper = NULL;
float *lut3d = NULL;
float len_shaper;
float len_lut3d;
float *clut = NULL;
uint32_t len_shaper;
uint32_t len_clut;
bool ok;
/**
@ -386,22 +386,21 @@ gl_renderer_color_transform_create_3dlut(struct gl_renderer *gr,
* excessive memory consumption.
*/
len_shaper = 1024;
len_lut3d = 33;
len_clut = 33;
shaper = zalloc(len_shaper * 3 * sizeof(*shaper));
if (!shaper)
goto err;
lut3d = zalloc(3 * len_lut3d * len_lut3d * len_lut3d * sizeof(*lut3d));
if (!lut3d)
clut = zalloc(3 * len_clut * len_clut * len_clut * sizeof(*clut));
if (!clut)
goto err;
gl_xform = gl_renderer_color_transform_create(xform);
if (!gl_xform)
goto err;
ok = xform->to_shaper_plus_3dlut(xform, len_shaper, shaper,
len_lut3d, lut3d);
ok = xform->to_clut(xform, len_shaper, shaper, len_clut, clut);
if (!ok)
goto err;
@ -411,18 +410,18 @@ gl_renderer_color_transform_create_3dlut(struct gl_renderer *gr,
goto err;
gl_color_mapping_lut_3d_init(gr, &gl_xform->mapping,
len_lut3d, lut3d);
len_clut, clut);
free(shaper);
free(lut3d);
free(clut);
return gl_xform;
err:
if (shaper)
free(shaper);
if (lut3d)
free(lut3d);
if (clut)
free(clut);
if (gl_xform)
gl_renderer_color_transform_destroy(gl_xform);
return NULL;

View file

@ -32,6 +32,8 @@
#include <stdbool.h>
#include <inttypes.h>
#include <wayland-util.h>
struct weston_compositor;
__attribute__((noreturn, format(printf, 2, 3)))
@ -53,24 +55,24 @@ weston_assert_fail_(const struct weston_compositor *compositor, const char *fmt,
#define weston_assert_(compositor, a, b, val_type, val_fmt, cmp) \
({ \
struct weston_compositor *ec = compositor; \
struct weston_compositor *wc_ = compositor; \
val_type a_ = (a); \
val_type b_ = (b); \
bool cond = a_ cmp b_; \
if (!cond) \
custom_assert_fail_(ec, "%s:%u: Assertion %s %s %s (" val_fmt " %s " val_fmt ") failed!\n", \
custom_assert_fail_(wc_, "%s:%u: Assertion %s %s %s (" val_fmt " %s " val_fmt ") failed!\n", \
__FILE__, __LINE__, #a, #cmp, #b, a_, #cmp, b_); \
cond; \
})
#define weston_assert_fn_(compositor, fn, a, b, val_type, val_fmt, cmp) \
({ \
struct weston_compositor *ec = compositor; \
struct weston_compositor *wc_ = compositor; \
val_type a_ = (a); \
val_type b_ = (b); \
bool cond = fn(a_, b_) cmp 0; \
if (!cond) \
custom_assert_fail_(ec, "%s:%u: Assertion %s %s %s (" val_fmt " %s " val_fmt ") failed!\n", \
custom_assert_fail_(wc_, "%s:%u: Assertion %s %s %s (" val_fmt " %s " val_fmt ") failed!\n", \
__FILE__, __LINE__, #a, #cmp, #b, a_, #cmp, b_); \
cond; \
})
@ -185,51 +187,75 @@ weston_assert_fail_(const struct weston_compositor *compositor, const char *fmt,
#define weston_assert_bit_set(compositor, value, bit) \
({ \
struct weston_compositor *ec = compositor; \
struct weston_compositor *wc_ = compositor; \
uint64_t v = (value); \
uint64_t b = (bit); \
bool cond = (v & b) == b; \
weston_assert_true(compositor, is_pow2_64(bit)); \
if (!cond) \
custom_assert_fail_(ec, "%s:%u: Assertion failed! Bit \"%s\" (%" PRIu64 ") of \"%s\" (0x%" PRIx64 ") is not set.\n", \
custom_assert_fail_(wc_, "%s:%u: Assertion failed! Bit \"%s\" (%" PRIu64 ") of \"%s\" (0x%" PRIx64 ") is not set.\n", \
__FILE__, __LINE__, #bit, b, #value, v); \
cond; \
})
#define weston_assert_bit_not_set(compositor, value, bit) \
({ \
struct weston_compositor *ec = compositor; \
struct weston_compositor *wc_ = compositor; \
uint64_t v = (value); \
uint64_t b = (bit); \
bool cond = (v & b) == 0; \
weston_assert_true(compositor, is_pow2_64(bit)); \
if (!cond) \
custom_assert_fail_(ec, "%s:%u: Assertion failed! Bit \"%s\" (%" PRIu64 ") of \"%s\" (0x%" PRIx64 ") is set.\n", \
custom_assert_fail_(wc_, "%s:%u: Assertion failed! Bit \"%s\" (%" PRIu64 ") of \"%s\" (0x%" PRIx64 ") is set.\n", \
__FILE__, __LINE__, #bit, b, #value, v); \
cond; \
})
#define weston_assert_legal_bits(compositor, value, mask) \
({ \
struct weston_compositor *ec = compositor; \
struct weston_compositor *wc_ = compositor; \
uint64_t v_ = (value); \
uint64_t m_ = (mask); \
uint64_t ill = v_ & ~m_; \
bool cond = ill == 0; \
if (!cond) \
custom_assert_fail_(ec, "%s:%u: Assertion failed! " \
custom_assert_fail_(wc_, "%s:%u: Assertion failed! " \
"Value %s (0x%" PRIx64 ") contains illegal bits 0x%" PRIx64 ". " \
"Legal mask is %s (0x%" PRIx64 ").\n", \
__FILE__, __LINE__, #value, v_, ill, #mask, m_); \
cond; \
})
/* wl_list asserts */
#define weston_assert_list_empty(compositor, list) \
({ \
struct weston_compositor *wc_ = compositor; \
struct wl_list *l_ = list; \
bool cond = wl_list_empty(l_); \
if (!cond) \
custom_assert_fail_(wc_, "%s:%u: Assertion failed! wl_list '%s' is not empty.\n", \
__FILE__, __LINE__, #list); \
cond; \
})
#define weston_assert_list_not_empty(compositor, list) \
({ \
struct weston_compositor *wc_ = compositor; \
struct wl_list *l_ = list; \
bool cond = !wl_list_empty(l_); \
if (!cond) \
custom_assert_fail_(wc_, "%s:%u: Assertion failed! wl_list '%s' is empty.\n", \
__FILE__, __LINE__, #list); \
cond; \
})
/* Misc asserts. */
#define weston_assert_not_reached(compositor, reason) \
do { \
struct weston_compositor *ec = compositor; \
custom_assert_fail_(ec, "%s:%u: Assertion failed! This should not be reached: %s\n", \
struct weston_compositor *wc_ = compositor; \
custom_assert_fail_(wc_, "%s:%u: Assertion failed! This should not be reached: %s\n", \
__FILE__, __LINE__, reason); \
} while (0)

View file

@ -110,6 +110,35 @@ TEST(asserts_boolean)
return RESULT_OK;
}
TEST(asserts_list)
{
/* Unused by the macros for now, so let's just use NULL. */
struct weston_compositor *compositor = NULL;
struct wl_list list;
struct wl_list link;
bool ret;
wl_list_init(&list);
ret = weston_assert_list_empty(compositor, &list);
abort_if_not(ret);
ret = weston_assert_list_not_empty(compositor, &list);
abort_if_not(ret == false);
wl_list_insert(&list, &link);
ret = weston_assert_list_empty(compositor, &list);
abort_if_not(ret == false);
ret = weston_assert_list_not_empty(compositor, &list);
abort_if_not(ret);
/* If we reach that point, it's a success so reset the assert counter
* that's been incremented to check that assertions work. */
weston_assert_counter_reset();
return RESULT_OK;
}
TEST(asserts_pointer)
{
/* Unused by the macros for now, so let's just use NULL. */