1
0
Fork 0

clutter/stage-view: Add linear intermediate color space buffer

Allow compositing in a linear color space and do so either when forcing
it via the debug controls D-Bus API, or when the experimental HDR mode
is enabled.

This relies on paint nodes etc to actually transform everything into the
linear target color space, which isn't done yet, so enabling it right
now will cause a broken result. Yet, introduce this now, so that
painting can be fixed piece by piece.

Linear blending is automatically enabled on monitors where HDR is
enabled, as this makes it possible to use an linear color space when
blending content from different color spaces with different transfer
functions.

Linear blending requires extra precision, i.e. 16 bit per channel
in the intermediate buffer due how the values are distributed,
so only enable the experimental HDR mode if the Cogl context supports
half float formats.

By default, no intermadiate linear offscreen framebuffer is used.

To test, do e.g.

  ./tools/debug-control.py --toggle ForceLinearBlending

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3433>
This commit is contained in:
Jonas Ådahl 2023-11-09 13:24:31 +08:00 committed by Sebastian Wick
parent beb3415d7e
commit 0c2fa8e9a8
6 changed files with 303 additions and 24 deletions

View file

@ -42,6 +42,8 @@ enum
PROP_FRAMEBUFFER,
PROP_OFFSCREEN,
PROP_USE_SHADOWFB,
PROP_COLOR_STATE,
PROP_OUTPUT_COLOR_STATE,
PROP_SCALE,
PROP_REFRESH_RATE,
PROP_VBLANK_DURATION_US,
@ -69,6 +71,7 @@ typedef struct _ClutterStageViewPrivate
float scale;
CoglFramebuffer *framebuffer;
ClutterColorState *color_state;
ClutterColorState *output_color_state;
CoglOffscreen *offscreen;
CoglPipeline *offscreen_pipeline;
@ -186,6 +189,7 @@ clutter_stage_view_ensure_offscreen_blit_pipeline (ClutterStageView *view)
clutter_stage_view_get_instance_private (view);
ClutterStageViewClass *view_class =
CLUTTER_STAGE_VIEW_GET_CLASS (view);
g_autoptr (CoglSnippet) snippet = NULL;
g_assert (priv->offscreen != NULL);
@ -197,6 +201,11 @@ clutter_stage_view_ensure_offscreen_blit_pipeline (ClutterStageView *view)
if (view_class->setup_offscreen_transform)
view_class->setup_offscreen_transform (view, priv->offscreen_pipeline);
snippet = clutter_color_state_get_transform_snippet (priv->color_state,
priv->output_color_state);
if (snippet)
cogl_pipeline_add_snippet (priv->offscreen_pipeline, snippet);
}
void
@ -994,6 +1003,21 @@ clutter_stage_view_set_framebuffer (ClutterStageView *view,
}
}
static void
clutter_stage_view_set_color_state (ClutterStageView *view,
ClutterColorState *color_state)
{
ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view);
if (priv->color_state == color_state)
return;
g_set_object (&priv->color_state, color_state);
clutter_stage_view_invalidate_offscreen_blit_pipeline (view);
}
static void
clutter_stage_view_get_property (GObject *object,
guint prop_id,
@ -1024,6 +1048,12 @@ clutter_stage_view_get_property (GObject *object,
case PROP_USE_SHADOWFB:
g_value_set_boolean (value, priv->use_shadowfb);
break;
case PROP_COLOR_STATE:
g_value_set_object (value, priv->color_state);
break;
case PROP_OUTPUT_COLOR_STATE:
g_value_set_object (value, priv->output_color_state);
break;
case PROP_SCALE:
g_value_set_float (value, priv->scale);
break;
@ -1069,6 +1099,12 @@ clutter_stage_view_set_property (GObject *object,
case PROP_USE_SHADOWFB:
priv->use_shadowfb = g_value_get_boolean (value);
break;
case PROP_COLOR_STATE:
clutter_stage_view_set_color_state (view, g_value_get_object (value));
break;
case PROP_OUTPUT_COLOR_STATE:
priv->output_color_state = g_value_dup_object (value);
break;
case PROP_SCALE:
priv->scale = g_value_get_float (value);
break;
@ -1089,7 +1125,6 @@ clutter_stage_view_constructed (GObject *object)
ClutterStageView *view = CLUTTER_STAGE_VIEW (object);
ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view);
ClutterContext *context;
if (priv->use_shadowfb)
init_shadowfb (view);
@ -1100,10 +1135,15 @@ clutter_stage_view_constructed (GObject *object)
&frame_clock_listener_iface,
view);
context = clutter_actor_get_context (CLUTTER_ACTOR (priv->stage));
priv->color_state = clutter_color_state_new (context,
CLUTTER_COLORSPACE_DEFAULT,
CLUTTER_TRANSFER_FUNCTION_DEFAULT);
if (!priv->color_state)
{
ClutterContext *context =
clutter_actor_get_context (CLUTTER_ACTOR (priv->stage));
priv->color_state = clutter_color_state_new (context,
CLUTTER_COLORSPACE_DEFAULT,
CLUTTER_TRANSFER_FUNCTION_DEFAULT);
}
clutter_stage_view_add_redraw_clip (view, NULL);
clutter_stage_view_schedule_update (view);
@ -1214,6 +1254,20 @@ clutter_stage_view_class_init (ClutterStageViewClass *klass)
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
obj_props[PROP_COLOR_STATE] =
g_param_spec_object ("color-state", NULL, NULL,
CLUTTER_TYPE_COLOR_STATE,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT |
G_PARAM_STATIC_STRINGS);
obj_props[PROP_OUTPUT_COLOR_STATE] =
g_param_spec_object ("output-color-state", NULL, NULL,
CLUTTER_TYPE_COLOR_STATE,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT |
G_PARAM_STATIC_STRINGS);
obj_props[PROP_SCALE] =
g_param_spec_float ("scale", NULL, NULL,
0.5, G_MAXFLOAT, 1.0,

View file

@ -6,6 +6,7 @@
<interface name="org.gnome.Mutter.DebugControl">
<property name="EnableHDR" type="b" access="readwrite" />
<property name="ForceLinearBlending" type="b" access="readwrite" />
</interface>

View file

@ -491,18 +491,26 @@ prepare_shutdown (MetaBackend *backend,
}
static void
ensure_hdr_settings (MetaMonitorManager *manager)
set_color_space_and_hdr_metadata (MetaMonitorManager *manager,
gboolean enable,
MetaOutputColorspace *color_space,
MetaOutputHdrMetadata *hdr_metadata)
{
MetaMonitorManagerPrivate *priv =
meta_monitor_manager_get_instance_private (manager);
MetaOutputColorspace color_space;
MetaOutputHdrMetadata hdr_metadata;
GList *l;
MetaBackend *backend = meta_monitor_manager_get_backend (manager);
ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend);
if (g_strcmp0 (priv->experimental_hdr, "on") == 0)
if (enable &&
!cogl_has_feature (cogl_context, COGL_FEATURE_ID_TEXTURE_HALF_FLOAT))
{
color_space = META_OUTPUT_COLORSPACE_BT2020;
hdr_metadata = (MetaOutputHdrMetadata) {
g_warning ("Tried to enable HDR without half float rendering support, ignoring");
enable = FALSE;
}
if (enable)
{
*color_space = META_OUTPUT_COLORSPACE_BT2020;
*hdr_metadata = (MetaOutputHdrMetadata) {
.active = TRUE,
.eotf = META_OUTPUT_HDR_METADATA_EOTF_PQ,
};
@ -513,8 +521,8 @@ ensure_hdr_settings (MetaMonitorManager *manager)
}
else
{
color_space = META_OUTPUT_COLORSPACE_DEFAULT;
hdr_metadata = (MetaOutputHdrMetadata) {
*color_space = META_OUTPUT_COLORSPACE_DEFAULT;
*hdr_metadata = (MetaOutputHdrMetadata) {
.active = FALSE,
};
@ -522,6 +530,21 @@ ensure_hdr_settings (MetaMonitorManager *manager)
"MonitorManager: Trying to enable default mode "
"(Colorimetry: default, TF: default, HDR Metadata: None):");
}
}
static void
ensure_hdr_settings (MetaMonitorManager *manager)
{
MetaMonitorManagerPrivate *priv =
meta_monitor_manager_get_instance_private (manager);
MetaOutputColorspace color_space;
MetaOutputHdrMetadata hdr_metadata;
GList *l;
set_color_space_and_hdr_metadata (manager,
g_strcmp0 (priv->experimental_hdr, "on") == 0,
&color_space,
&hdr_metadata);
for (l = manager->monitors; l; l = l->next)
{
@ -1336,9 +1359,15 @@ static void
on_started (MetaContext *context,
MetaMonitorManager *monitor_manager)
{
MetaDebugControl *debug_control = meta_context_get_debug_control (context);
g_signal_connect (monitor_manager, "notify::experimental-hdr",
G_CALLBACK (meta_monitor_manager_reconfigure),
NULL);
g_signal_connect_data (debug_control, "notify::force-linear-blending",
G_CALLBACK (meta_monitor_manager_reconfigure),
monitor_manager, NULL,
G_CONNECT_SWAPPED | G_CONNECT_AFTER);
}
static void

View file

@ -125,6 +125,25 @@ meta_renderer_native_ensure_gpu_data (MetaRendererNative *renderer_native,
static void
meta_renderer_native_queue_modes_reset (MetaRendererNative *renderer_native);
static char *
enum_to_string (GType type,
unsigned int enum_value)
{
GEnumClass *enum_class;
GEnumValue *value;
char *retval = NULL;
enum_class = g_type_class_ref (type);
value = g_enum_get_value (enum_class, enum_value);
if (value)
retval = g_strdup (value->value_nick);
g_type_class_unref (enum_class);
return retval;
}
static void
meta_renderer_native_gpu_data_free (MetaRendererNativeGpuData *renderer_gpu_data)
{
@ -1253,6 +1272,36 @@ meta_renderer_native_create_offscreen (MetaRendererNative *renderer_native,
return fb;
}
static CoglOffscreen *
create_offscreen_with_formats (MetaRendererNative *renderer_native,
CoglPixelFormat *formats,
size_t n_formats,
int width,
int height,
GError **error)
{
g_autoptr (GError) local_error = NULL;
size_t i;
for (i = 0; i < n_formats; i++)
{
CoglOffscreen *offscreen;
g_clear_error (&local_error);
offscreen = meta_renderer_native_create_offscreen (renderer_native,
formats[i],
width,
height,
&local_error);
if (offscreen)
return offscreen;
}
g_propagate_error (error, g_steal_pointer (&local_error));
return NULL;
}
static const CoglWinsysVtable *
get_native_cogl_winsys_vtable (CoglRenderer *cogl_renderer)
{
@ -1330,6 +1379,57 @@ should_force_shadow_fb (MetaRendererNative *renderer_native,
return meta_kms_device_prefers_shadow_buffer (kms_device);
}
static ClutterColorspace
get_color_space_from_output (MetaOutput *output)
{
switch (meta_output_peek_color_space (output))
{
case META_OUTPUT_COLORSPACE_DEFAULT:
case META_OUTPUT_COLORSPACE_UNKNOWN:
return CLUTTER_COLORSPACE_DEFAULT;
case META_OUTPUT_COLORSPACE_BT2020:
return CLUTTER_COLORSPACE_BT2020;
}
g_assert_not_reached ();
}
static void
get_transfer_function_from_output (MetaOutput *output,
ClutterTransferFunction *view_transfer_function,
ClutterTransferFunction *output_transfer_function)
{
const MetaOutputHdrMetadata *hdr_metadata =
meta_output_peek_hdr_metadata (output);
if (hdr_metadata->active)
{
switch (meta_output_peek_hdr_metadata (output)->eotf)
{
case META_OUTPUT_HDR_METADATA_EOTF_PQ:
*view_transfer_function = CLUTTER_TRANSFER_FUNCTION_LINEAR;
*output_transfer_function = CLUTTER_TRANSFER_FUNCTION_PQ;
return;
case META_OUTPUT_HDR_METADATA_EOTF_TRADITIONAL_GAMMA_SDR:
*view_transfer_function = CLUTTER_TRANSFER_FUNCTION_DEFAULT;
*output_transfer_function = CLUTTER_TRANSFER_FUNCTION_DEFAULT;
return;
case META_OUTPUT_HDR_METADATA_EOTF_TRADITIONAL_GAMMA_HDR:
g_warning ("Unhandled HDR EOTF (traditional gamma hdr)");
return;
case META_OUTPUT_HDR_METADATA_EOTF_HLG:
g_warning ("Unhandled HDR EOTF (HLG)");
return;
}
g_assert_not_reached ();
}
else
{
*view_transfer_function = CLUTTER_TRANSFER_FUNCTION_DEFAULT;
*output_transfer_function = CLUTTER_TRANSFER_FUNCTION_DEFAULT;
}
}
static MetaRendererView *
meta_renderer_native_create_view (MetaRenderer *renderer,
MetaLogicalMonitor *logical_monitor,
@ -1339,13 +1439,23 @@ meta_renderer_native_create_view (MetaRenderer *renderer,
{
MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer);
MetaBackend *backend = meta_renderer_get_backend (renderer);
MetaContext *context = meta_backend_get_context (backend);
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
MetaDebugControl *debug_control = meta_context_get_debug_control (context);
CoglContext *cogl_context =
cogl_context_from_renderer_native (renderer_native);
CoglDisplay *cogl_display = cogl_context_get_display (cogl_context);
ClutterContext *clutter_context = meta_backend_get_clutter_context (backend);
const MetaCrtcConfig *crtc_config;
const MetaCrtcModeInfo *crtc_mode_info;
ClutterColorspace colorspace;
ClutterTransferFunction view_transfer_function =
CLUTTER_TRANSFER_FUNCTION_DEFAULT;
ClutterTransferFunction output_transfer_function =
CLUTTER_TRANSFER_FUNCTION_DEFAULT;
ClutterColorState *view_color_state;
ClutterColorState *output_color_state;
MetaMonitorTransform view_transform;
g_autoptr (CoglFramebuffer) framebuffer = NULL;
g_autoptr (CoglOffscreen) offscreen = NULL;
@ -1421,16 +1531,74 @@ meta_renderer_native_create_view (MetaRenderer *renderer,
framebuffer = COGL_FRAMEBUFFER (virtual_onscreen);
}
colorspace = get_color_space_from_output (output);
get_transfer_function_from_output (output,
&view_transfer_function,
&output_transfer_function);
if (meta_debug_control_is_linear_blending_forced (debug_control))
view_transfer_function = CLUTTER_TRANSFER_FUNCTION_LINEAR;
if (meta_is_topic_enabled (META_DEBUG_RENDER))
{
g_autofree char *colorspace_name = NULL;
g_autofree char *view_transfer_function_name = NULL;
g_autofree char *output_transfer_function_name = NULL;
colorspace_name = enum_to_string (CLUTTER_TYPE_COLORSPACE,
colorspace);
view_transfer_function_name =
enum_to_string (CLUTTER_TYPE_TRANSFER_FUNCTION,
view_transfer_function);
output_transfer_function_name =
enum_to_string (CLUTTER_TYPE_TRANSFER_FUNCTION,
output_transfer_function);
meta_topic (META_DEBUG_RENDER,
"Using colorspace %s "
"and transfer functions %s (view) and %s (output) "
"for view %s",
colorspace_name,
view_transfer_function_name,
output_transfer_function_name,
meta_output_get_name (output));
}
view_color_state = clutter_color_state_new (clutter_context,
colorspace,
view_transfer_function);
output_color_state = clutter_color_state_new (clutter_context,
colorspace,
output_transfer_function);
view_transform = calculate_view_transform (monitor_manager,
logical_monitor,
output,
crtc);
if (view_transform != META_MONITOR_TRANSFORM_NORMAL)
if (view_transform != META_MONITOR_TRANSFORM_NORMAL ||
view_transfer_function != output_transfer_function)
{
int offscreen_width;
int offscreen_height;
CoglPixelFormat formats[10];
size_t n_formats = 0;
CoglPixelFormat format;
if (view_transfer_function == CLUTTER_TRANSFER_FUNCTION_LINEAR)
{
formats[n_formats++] = COGL_PIXEL_FORMAT_XRGB_FP_16161616;
formats[n_formats++] = COGL_PIXEL_FORMAT_XBGR_FP_16161616;
formats[n_formats++] = COGL_PIXEL_FORMAT_RGBA_FP_16161616_PRE;
formats[n_formats++] = COGL_PIXEL_FORMAT_BGRA_FP_16161616_PRE;
formats[n_formats++] = COGL_PIXEL_FORMAT_ARGB_FP_16161616_PRE;
formats[n_formats++] = COGL_PIXEL_FORMAT_ABGR_FP_16161616_PRE;
}
else
{
formats[n_formats++] = cogl_framebuffer_get_internal_format (framebuffer);
}
if (meta_monitor_transform_is_rotated (view_transform))
{
offscreen_width = onscreen_height;
@ -1442,14 +1610,22 @@ meta_renderer_native_create_view (MetaRenderer *renderer,
offscreen_height = onscreen_height;
}
format = cogl_framebuffer_get_internal_format (framebuffer);
offscreen = meta_renderer_native_create_offscreen (renderer_native,
format,
offscreen_width,
offscreen_height,
&local_error);
offscreen = create_offscreen_with_formats (renderer_native,
formats,
n_formats,
offscreen_width,
offscreen_height,
&local_error);
if (!offscreen)
g_error ("Failed to allocate back buffer texture: %s", local_error->message);
format =
cogl_framebuffer_get_internal_format (COGL_FRAMEBUFFER (offscreen));
meta_topic (META_DEBUG_RENDER,
"Using an additional intermediate %s offscreen (%dx%d) for %s",
cogl_pixel_format_to_string (format),
offscreen_width, offscreen_height,
meta_output_get_name (output));
}
if (meta_backend_is_stage_views_scaled (backend))
@ -1468,6 +1644,8 @@ meta_renderer_native_create_view (MetaRenderer *renderer,
"scale", scale,
"framebuffer", framebuffer,
"offscreen", offscreen,
"color-state", view_color_state,
"output-color-state", output_color_state,
"use-shadowfb", use_shadowfb,
"transform", view_transform,
"refresh-rate", crtc_mode_info->refresh_rate,

View file

@ -113,7 +113,7 @@ on_experimental_hdr_changed (MetaMonitorManager *monitor_manager,
g_object_get (G_OBJECT (monitor_manager),
"experimental-hdr", &experimental_hdr,
NULL);
enable = g_strcmp0 (experimental_hdr, "on") == 0;
if (enable == meta_dbus_debug_control_get_enable_hdr (dbus_debug_control))
return;
@ -219,6 +219,19 @@ meta_debug_control_class_init (MetaDebugControlClass *klass)
static void
meta_debug_control_init (MetaDebugControl *debug_control)
{
MetaDBusDebugControl *dbus_debug_control =
META_DBUS_DEBUG_CONTROL (debug_control);
meta_dbus_debug_control_set_force_linear_blending (dbus_debug_control, FALSE);
}
gboolean
meta_debug_control_is_linear_blending_forced (MetaDebugControl *debug_control)
{
MetaDBusDebugControl *dbus_debug_control =
META_DBUS_DEBUG_CONTROL (debug_control);
return meta_dbus_debug_control_get_force_linear_blending (dbus_debug_control);
}
void

View file

@ -20,10 +20,14 @@
#include "meta-dbus-debug-control.h"
#include "clutter/clutter.h"
#define META_TYPE_DEBUG_CONTROL (meta_debug_control_get_type ())
G_DECLARE_FINAL_TYPE (MetaDebugControl,
meta_debug_control,
META, DEBUG_CONTROL,
MetaDBusDebugControlSkeleton)
gboolean meta_debug_control_is_linear_blending_forced (MetaDebugControl *debug_control);
void meta_debug_control_export (MetaDebugControl *debug_control);