From 42385127648d607ec63d344ac3dd11ec4f3acde9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 1 Nov 2023 22:28:39 +0800 Subject: [PATCH] clutter: Introduce color transform aware pipeline cache Color aware rendering needs shaders / pipelines that adapt to what output they render to. For example if we want to render to a linear BT.2020 intermediate framebuffer on one monitor, and a non-linear sRGB direct target buffer on another, the shader for the same paint node or content will depend on where they are going to be presented. In order to help keeping track of what shader should target what monitor, without having to regenerate them each time, introduce a pipeline cache that knows how to handle differentiating between transforming between different color state. The cache is meant to handle caches for multiple pipeline users, where each user might potentially want to keep track of multiple pipelines itself. Lookup should be O(1), and in order to achieve this, separate the cache into 3 levels. The first level is the "pipeline group", where e.g. a ClutterContent type allocates a group where it can store its pipelines. Each group has a fixed number of "slots" where it can store a pipeline. Each slot has a hash table where the key is derived from a color state transform, and where the value is a CoglPipeline where the thame color state transformation is expected to be handled. A content will when painting know about its own color state, and the target state it should render into, retrieve a cached pipeline for the correct transform, or if the cache didn't have it, generate it. Part-of: --- clutter/clutter/clutter-context.c | 19 ++ clutter/clutter/clutter-context.h | 3 + clutter/clutter/clutter-pipeline-cache.c | 226 +++++++++++++++++++++ clutter/clutter/clutter-pipeline-cache.h | 59 ++++++ clutter/clutter/clutter-private.h | 1 + clutter/clutter/clutter-types.h | 1 + clutter/clutter/clutter.h | 1 + clutter/clutter/meson.build | 2 + src/tests/clutter/conform/meson.build | 1 + src/tests/clutter/conform/pipeline-cache.c | 208 +++++++++++++++++++ 10 files changed, 521 insertions(+) create mode 100644 clutter/clutter/clutter-pipeline-cache.c create mode 100644 clutter/clutter/clutter-pipeline-cache.h create mode 100644 src/tests/clutter/conform/pipeline-cache.c diff --git a/clutter/clutter/clutter-context.c b/clutter/clutter/clutter-context.c index 72e99c346..90f68ba1c 100644 --- a/clutter/clutter/clutter-context.c +++ b/clutter/clutter/clutter-context.c @@ -79,6 +79,8 @@ static const GDebugKey clutter_paint_debug_keys[] = { typedef struct _ClutterContextPrivate { ClutterTextDirection text_direction; + + ClutterPipelineCache *pipeline_cache; } ClutterContextPrivate; G_DEFINE_TYPE_WITH_PRIVATE (ClutterContext, clutter_context, G_TYPE_OBJECT) @@ -87,7 +89,9 @@ static void clutter_context_dispose (GObject *object) { ClutterContext *context = CLUTTER_CONTEXT (object); + ClutterContextPrivate *priv = clutter_context_get_instance_private (context); + g_clear_object (&priv->pipeline_cache); g_clear_pointer (&context->events_queue, g_async_queue_unref); g_clear_pointer (&context->backend, clutter_backend_destroy); @@ -252,8 +256,10 @@ clutter_context_new (ClutterContextFlags flags, GError **error) { ClutterContext *context; + ClutterContextPrivate *priv; context = g_object_new (CLUTTER_TYPE_CONTEXT, NULL); + priv = clutter_context_get_instance_private (context); init_clutter_debug (context); context->show_fps = clutter_show_fps; @@ -268,6 +274,8 @@ clutter_context_new (ClutterContextFlags flags, g_async_queue_new_full ((GDestroyNotify) clutter_event_free); context->last_repaint_id = 1; + priv->pipeline_cache = g_object_new (CLUTTER_TYPE_PIPELINE_CACHE, NULL); + if (!clutter_context_init_real (context, flags, error)) return NULL; @@ -317,3 +325,14 @@ clutter_context_get_text_direction (ClutterContext *context) return priv->text_direction; } + +/** + * clutter_context_get_pipeline_cache: (skip) + */ +ClutterPipelineCache * +clutter_context_get_pipeline_cache (ClutterContext *context) +{ + ClutterContextPrivate *priv = clutter_context_get_instance_private (context); + + return priv->pipeline_cache; +} diff --git a/clutter/clutter/clutter-context.h b/clutter/clutter/clutter-context.h index 8bc2aa73a..d4be1f631 100644 --- a/clutter/clutter/clutter-context.h +++ b/clutter/clutter/clutter-context.h @@ -65,3 +65,6 @@ ClutterBackend * clutter_context_get_backend (ClutterContext *context); CoglPangoFontMap * clutter_context_get_pango_fontmap (ClutterContext *context); ClutterTextDirection clutter_context_get_text_direction (ClutterContext *context); + +CLUTTER_EXPORT +ClutterPipelineCache * clutter_context_get_pipeline_cache (ClutterContext *clutter_context); diff --git a/clutter/clutter/clutter-pipeline-cache.c b/clutter/clutter/clutter-pipeline-cache.c new file mode 100644 index 000000000..74dc70c58 --- /dev/null +++ b/clutter/clutter/clutter-pipeline-cache.c @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2023 Red Hat + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + */ + +#include "config.h" + +#include "clutter-pipeline-cache.h" + +typedef struct _PipelineGroupEntry +{ + GHashTable **slots; + size_t n_slots; +} PipelineGroupEntry; + +struct _ClutterPipelineCache +{ + GObject parent; + + GHashTable *groups; +}; + +G_DEFINE_TYPE (ClutterPipelineCache, clutter_pipeline_cache, G_TYPE_OBJECT) + +static PipelineGroupEntry * +pipeline_group_entry_new (void) +{ + PipelineGroupEntry *group_entry; + + group_entry = g_new0 (PipelineGroupEntry, 1); + + return group_entry; +} + +static void +pipeline_group_entry_free (PipelineGroupEntry *group_entry) +{ + size_t i; + + for (i = 0; i < group_entry->n_slots; i++) + g_clear_pointer (&group_entry->slots[i], g_hash_table_unref); + g_free (group_entry->slots); + g_free (group_entry); +} + +static void +clutter_pipeline_cache_dispose (GObject *object) +{ + ClutterPipelineCache *pipeline_cache = CLUTTER_PIPELINE_CACHE (object); + + g_clear_pointer (&pipeline_cache->groups, g_hash_table_unref); + + G_OBJECT_CLASS (clutter_pipeline_cache_parent_class)->dispose (object); +} + +static void +clutter_pipeline_cache_class_init (ClutterPipelineCacheClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = clutter_pipeline_cache_dispose; +} + +static void +clutter_pipeline_cache_init (ClutterPipelineCache *pipeline_cache) +{ + pipeline_cache->groups = + g_hash_table_new_full (NULL, + NULL, + NULL, + (GDestroyNotify) pipeline_group_entry_free); +} + +static uint32_t +calculate_color_state_key (ClutterColorState *color_state) +{ + ClutterColorspace colorspace = + clutter_color_state_get_colorspace (color_state); + ClutterTransferFunction transfer_function = + clutter_color_state_get_transfer_function (color_state); + + return (colorspace | + transfer_function << 8); +} + +static uint64_t +calculate_key (ClutterColorState *source_color_state, + ClutterColorState *target_color_state) +{ + uint64_t source_key; + uint64_t target_key; + + source_key = calculate_color_state_key (source_color_state); + target_key = calculate_color_state_key (target_color_state); + + return (source_key | + target_key << 32); +} + +/** + * clutter_pipeline_cache_get_pipeline: (skip) + */ +CoglPipeline * +clutter_pipeline_cache_get_pipeline (ClutterPipelineCache *pipeline_cache, + ClutterPipelineGroup group, + int slot, + ClutterColorState *source_color_state, + ClutterColorState *target_color_state) +{ + PipelineGroupEntry *group_entry; + uint64_t key; + + group_entry = g_hash_table_lookup (pipeline_cache->groups, group); + if (!group_entry) + return NULL; + + if (slot >= group_entry->n_slots) + return NULL; + + if (!group_entry->slots[slot]) + return NULL; + + key = calculate_key (source_color_state, target_color_state); + return g_hash_table_lookup (group_entry->slots[slot], &key); +} + +/** + * clutter_pipeline_cache_sdd_pipeline: (skip) + */ +void +clutter_pipeline_cache_set_pipeline (ClutterPipelineCache *pipeline_cache, + ClutterPipelineGroup group, + int slot, + ClutterColorState *source_color_state, + ClutterColorState *target_color_state, + CoglPipeline *pipeline) +{ + PipelineGroupEntry *group_entry; + uint64_t key; + + group_entry = g_hash_table_lookup (pipeline_cache->groups, group); + if (!group_entry) + { + group_entry = pipeline_group_entry_new (); + g_hash_table_insert (pipeline_cache->groups, group, group_entry); + } + + if (slot >= group_entry->n_slots) + { + size_t new_n_slots; + + new_n_slots = slot + 1; + group_entry->slots = g_realloc_n (group_entry->slots, + new_n_slots, + sizeof (GHashTable *)); + memset (group_entry->slots + group_entry->n_slots, + 0, + (new_n_slots - group_entry->n_slots) * sizeof (GHashTable *)); + group_entry->n_slots = new_n_slots; + } + + if (!group_entry->slots[slot]) + { + group_entry->slots[slot] = g_hash_table_new_full (g_int64_hash, + g_int64_equal, + g_free, + g_object_unref); + } + + key = calculate_key (source_color_state, target_color_state); + g_hash_table_replace (group_entry->slots[slot], + g_memdup2 (&key, sizeof (key)), + g_object_ref (pipeline)); +} + +/** + * clutter_pipeline_cache_unset_pipeline: (skip) + */ +void +clutter_pipeline_cache_unset_pipeline (ClutterPipelineCache *pipeline_cache, + ClutterPipelineGroup group, + int slot, + ClutterColorState *source_color_state, + ClutterColorState *target_color_state) + +{ + PipelineGroupEntry *group_entry; + uint64_t key; + + group_entry = g_hash_table_lookup (pipeline_cache->groups, group); + + if (!group_entry) + return; + + if (slot >= group_entry->n_slots) + return; + + if (!group_entry->slots[slot]) + return; + + key = calculate_key (source_color_state, target_color_state); + g_hash_table_remove (group_entry->slots[slot], &key); +} + +/** + * clutter_pipeline_cache_unset_all_pipeline: (skip) + */ +void +clutter_pipeline_cache_unset_all_pipelines (ClutterPipelineCache *pipeline_cache, + ClutterPipelineGroup group) +{ + g_hash_table_remove (pipeline_cache->groups, group); +} diff --git a/clutter/clutter/clutter-pipeline-cache.h b/clutter/clutter/clutter-pipeline-cache.h new file mode 100644 index 000000000..bc4585211 --- /dev/null +++ b/clutter/clutter/clutter-pipeline-cache.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2023 Red Hat + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + */ + +#pragma once + +#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) +#error "Only can be included directly." +#endif + +#include "clutter/clutter-color-state.h" +#include "cogl/cogl.h" + +typedef gpointer ClutterPipelineGroup; + +#define CLUTTER_TYPE_PIPELINE_CACHE (clutter_pipeline_cache_get_type ()) +CLUTTER_EXPORT +G_DECLARE_FINAL_TYPE (ClutterPipelineCache, clutter_pipeline_cache, + CLUTTER, PIPELINE_CACHE, GObject) + +CLUTTER_EXPORT +CoglPipeline * clutter_pipeline_cache_get_pipeline (ClutterPipelineCache *pipeline_cache, + ClutterPipelineGroup group, + int slot, + ClutterColorState *source_color_state, + ClutterColorState *target_color_state); + +CLUTTER_EXPORT +void clutter_pipeline_cache_set_pipeline (ClutterPipelineCache *pipeline_cache, + ClutterPipelineGroup group, + int slot, + ClutterColorState *source_color_state, + ClutterColorState *target_color_state, + CoglPipeline *pipeline); + +CLUTTER_EXPORT +void clutter_pipeline_cache_unset_pipeline (ClutterPipelineCache *pipeline_cache, + ClutterPipelineGroup group, + int slot, + ClutterColorState *source_color_state, + ClutterColorState *target_color_state); + +CLUTTER_EXPORT +void clutter_pipeline_cache_unset_all_pipelines (ClutterPipelineCache *pipeline_cache, + ClutterPipelineGroup group); diff --git a/clutter/clutter/clutter-private.h b/clutter/clutter/clutter-private.h index 172fa8b22..48cfef25d 100644 --- a/clutter/clutter/clutter-private.h +++ b/clutter/clutter/clutter-private.h @@ -36,6 +36,7 @@ #include "clutter/clutter-effect.h" #include "clutter/clutter-event.h" #include "clutter/clutter-layout-manager.h" +#include "clutter/clutter-pipeline-cache.h" #include "clutter/clutter-settings.h" #include "clutter/clutter-stage-manager.h" #include "clutter/clutter-stage.h" diff --git a/clutter/clutter/clutter-types.h b/clutter/clutter/clutter-types.h index 211f1d2d6..7408d0d30 100644 --- a/clutter/clutter/clutter-types.h +++ b/clutter/clutter/clutter-types.h @@ -52,6 +52,7 @@ typedef struct _ClutterLayoutManager ClutterLayoutManager; typedef struct _ClutterActorIter ClutterActorIter; typedef struct _ClutterPaintContext ClutterPaintContext; typedef struct _ClutterPaintNode ClutterPaintNode; +typedef struct _ClutterPipelineCache ClutterPipelineCache; typedef struct _ClutterContent ClutterContent; /* dummy */ typedef struct _ClutterScrollActor ClutterScrollActor; typedef struct _ClutterFrameClock ClutterFrameClock; diff --git a/clutter/clutter/clutter.h b/clutter/clutter/clutter.h index 360333a6c..109358540 100644 --- a/clutter/clutter/clutter.h +++ b/clutter/clutter/clutter.h @@ -80,6 +80,7 @@ #include "clutter/clutter-paint-nodes.h" #include "clutter/clutter-paint-node.h" #include "clutter/clutter-pan-action.h" +#include "clutter/clutter-pipeline-cache.h" #include "clutter/clutter-property-transition.h" #include "clutter/clutter-rotate-action.h" #include "clutter/clutter-scroll-actor.h" diff --git a/clutter/clutter/meson.build b/clutter/clutter/meson.build index 190499930..22af0b2b1 100644 --- a/clutter/clutter/meson.build +++ b/clutter/clutter/meson.build @@ -58,6 +58,7 @@ clutter_headers = [ 'clutter-paint-node.h', 'clutter-pan-action.h', 'clutter-pick-context.h', + 'clutter-pipeline-cache.h', 'clutter-property-transition.h', 'clutter-rotate-action.h', 'clutter-scroll-actor.h', @@ -144,6 +145,7 @@ clutter_sources = [ 'clutter-pan-action.c', 'clutter-pick-context.c', 'clutter-pick-stack.c', + 'clutter-pipeline-cache.c', 'clutter-property-transition.c', 'clutter-rotate-action.c', 'clutter-scroll-actor.c', diff --git a/src/tests/clutter/conform/meson.build b/src/tests/clutter/conform/meson.build index 019c1f223..adcd3b095 100644 --- a/src/tests/clutter/conform/meson.build +++ b/src/tests/clutter/conform/meson.build @@ -39,6 +39,7 @@ clutter_conform_tests_general_tests = [ 'gesture', 'gesture-relationship', 'interval', + 'pipeline-cache', 'timeline', 'timeline-interpolate', 'timeline-progress', diff --git a/src/tests/clutter/conform/pipeline-cache.c b/src/tests/clutter/conform/pipeline-cache.c new file mode 100644 index 000000000..2e53391f4 --- /dev/null +++ b/src/tests/clutter/conform/pipeline-cache.c @@ -0,0 +1,208 @@ +#include +#include + +#include + +#include "tests/clutter-test-utils.h" + +static void +take_snippet (CoglPipeline *pipeline, + CoglSnippet *snippet) +{ + cogl_pipeline_add_snippet (pipeline, snippet); + g_object_unref (snippet); +} + +static void +pipeline_cache_group_pipelines (void) +{ + ClutterContext *context = clutter_test_get_context (); + ClutterBackend *backend = clutter_test_get_backend (); + CoglContext *cogl_context = clutter_backend_get_cogl_context (backend); + ClutterPipelineCache *pipeline_cache = clutter_context_get_pipeline_cache (context); + static ClutterPipelineGroup group1 = &group1; + static ClutterPipelineGroup group2 = &group2; + ClutterColorState *srgb_srgb; + ClutterColorState *srgb_linear; + ClutterColorState *bt2020_pq; + ClutterColorState *bt2020_linear; + /* SDR content with HDR output */ + CoglPipeline *srgb_srgb_to_bt2020_linear; + CoglPipeline *bt2020_linear_to_bt2020_pq; + /* HDR content with HDR output */ + CoglPipeline *bt2020_pq_to_bt2020_linear; + CoglPipeline *srgb_linear_to_srgb_srgb; + /* Copy for group2 */ + CoglPipeline *srgb_srgb_to_bt2020_linear_copy; + + srgb_srgb = clutter_color_state_new (context, + CLUTTER_COLORSPACE_SRGB, + CLUTTER_TRANSFER_FUNCTION_SRGB); + srgb_linear = clutter_color_state_new (context, + CLUTTER_COLORSPACE_SRGB, + CLUTTER_TRANSFER_FUNCTION_LINEAR); + bt2020_pq = clutter_color_state_new (context, + CLUTTER_COLORSPACE_BT2020, + CLUTTER_TRANSFER_FUNCTION_PQ); + bt2020_linear = clutter_color_state_new (context, + CLUTTER_COLORSPACE_BT2020, + CLUTTER_TRANSFER_FUNCTION_LINEAR); + + srgb_srgb_to_bt2020_linear = cogl_pipeline_new (cogl_context); + bt2020_linear_to_bt2020_pq = cogl_pipeline_new (cogl_context); + bt2020_pq_to_bt2020_linear = cogl_pipeline_new (cogl_context); + srgb_linear_to_srgb_srgb = cogl_pipeline_new (cogl_context); + + take_snippet (srgb_srgb_to_bt2020_linear, + clutter_color_state_get_transform_snippet (srgb_srgb, + bt2020_linear)); + take_snippet (bt2020_linear_to_bt2020_pq, + clutter_color_state_get_transform_snippet (bt2020_linear, + bt2020_pq)); + take_snippet (bt2020_pq_to_bt2020_linear, + clutter_color_state_get_transform_snippet (bt2020_pq, + bt2020_linear)); + take_snippet (srgb_linear_to_srgb_srgb, + clutter_color_state_get_transform_snippet (srgb_linear, + srgb_srgb)); + + /* Check that it's all empty. */ + g_assert_null (clutter_pipeline_cache_get_pipeline (pipeline_cache, group1, 0, + srgb_srgb, bt2020_linear)); + g_assert_null (clutter_pipeline_cache_get_pipeline (pipeline_cache, group1, 0, + bt2020_linear, bt2020_pq)); + g_assert_null (clutter_pipeline_cache_get_pipeline (pipeline_cache, group2, 0, + srgb_srgb, bt2020_linear)); + g_assert_null (clutter_pipeline_cache_get_pipeline (pipeline_cache, group2, 0, + bt2020_linear, bt2020_pq)); + + /* Adding sRGB to HDR pipeline to group1 should not effect group2. */ + clutter_pipeline_cache_set_pipeline (pipeline_cache, group1, 0, + srgb_srgb, bt2020_linear, + srgb_srgb_to_bt2020_linear); + clutter_pipeline_cache_set_pipeline (pipeline_cache, group1, 0, + bt2020_linear, bt2020_pq, + bt2020_linear_to_bt2020_pq); + + g_assert_true (clutter_pipeline_cache_get_pipeline (pipeline_cache, group1, 0, + srgb_srgb, bt2020_linear) == + srgb_srgb_to_bt2020_linear); + g_assert_true (clutter_pipeline_cache_get_pipeline (pipeline_cache, group1, 0, + bt2020_linear, bt2020_pq) == + bt2020_linear_to_bt2020_pq); + g_assert_null (clutter_pipeline_cache_get_pipeline (pipeline_cache, group2, 0, + srgb_srgb, bt2020_linear)); + g_assert_null (clutter_pipeline_cache_get_pipeline (pipeline_cache, group2, 0, + bt2020_linear, bt2020_pq)); + + srgb_srgb_to_bt2020_linear_copy = + cogl_pipeline_copy (srgb_srgb_to_bt2020_linear); + g_assert_true (srgb_srgb_to_bt2020_linear_copy != + srgb_srgb_to_bt2020_linear); + + clutter_pipeline_cache_set_pipeline (pipeline_cache, group2, 0, + srgb_srgb, bt2020_linear, + srgb_srgb_to_bt2020_linear_copy); + g_assert_true (clutter_pipeline_cache_get_pipeline (pipeline_cache, group1, 0, + srgb_srgb, bt2020_linear) == + srgb_srgb_to_bt2020_linear); + g_assert_true (clutter_pipeline_cache_get_pipeline (pipeline_cache, group2, 0, + srgb_srgb, bt2020_linear) == + srgb_srgb_to_bt2020_linear_copy); +} + +static void +pipeline_cache_replace_pipeline (void) +{ + ClutterContext *context = clutter_test_get_context (); + ClutterBackend *backend = clutter_test_get_backend (); + CoglContext *cogl_context = clutter_backend_get_cogl_context (backend); + ClutterPipelineCache *pipeline_cache = clutter_context_get_pipeline_cache (context); + static ClutterPipelineGroup group = &group; + ClutterColorState *srgb_srgb; + ClutterColorState *bt2020_linear; + CoglPipeline *srgb_srgb_to_bt2020_linear; + CoglPipeline *srgb_srgb_to_bt2020_linear_copy; + + srgb_srgb = clutter_color_state_new (context, + CLUTTER_COLORSPACE_SRGB, + CLUTTER_TRANSFER_FUNCTION_SRGB); + bt2020_linear = clutter_color_state_new (context, + CLUTTER_COLORSPACE_BT2020, + CLUTTER_TRANSFER_FUNCTION_PQ); + + srgb_srgb_to_bt2020_linear = cogl_pipeline_new (cogl_context); + srgb_srgb_to_bt2020_linear_copy = + cogl_pipeline_copy (srgb_srgb_to_bt2020_linear); + + g_object_add_weak_pointer (G_OBJECT (srgb_srgb_to_bt2020_linear), + (gpointer *) &srgb_srgb_to_bt2020_linear); + + take_snippet (srgb_srgb_to_bt2020_linear, + clutter_color_state_get_transform_snippet (srgb_srgb, + bt2020_linear)); + + clutter_pipeline_cache_set_pipeline (pipeline_cache, group, 0, + srgb_srgb, bt2020_linear, + srgb_srgb_to_bt2020_linear); + + g_object_unref (srgb_srgb_to_bt2020_linear); + g_assert_nonnull (srgb_srgb_to_bt2020_linear); + + take_snippet (srgb_srgb_to_bt2020_linear_copy, + clutter_color_state_get_transform_snippet (srgb_srgb, + bt2020_linear)); + clutter_pipeline_cache_set_pipeline (pipeline_cache, group, 0, + srgb_srgb, bt2020_linear, + srgb_srgb_to_bt2020_linear_copy); + g_assert_null (srgb_srgb_to_bt2020_linear); + + g_assert_true (clutter_pipeline_cache_get_pipeline (pipeline_cache, group, 0, + srgb_srgb, bt2020_linear) == + srgb_srgb_to_bt2020_linear_copy); +} + +static void +pipeline_slots (void) +{ + ClutterContext *context = clutter_test_get_context (); + ClutterBackend *backend = clutter_test_get_backend (); + CoglContext *cogl_context = clutter_backend_get_cogl_context (backend); + ClutterPipelineCache *pipeline_cache = clutter_context_get_pipeline_cache (context); + static ClutterPipelineGroup group = &group; + ClutterColorState *srgb_srgb; + ClutterColorState *bt2020_linear; + CoglPipeline *srgb_srgb_to_bt2020_linear; + CoglPipeline *srgb_srgb_to_bt2020_linear_copy; + + srgb_srgb = clutter_color_state_new (context, + CLUTTER_COLORSPACE_SRGB, + CLUTTER_TRANSFER_FUNCTION_SRGB); + bt2020_linear = clutter_color_state_new (context, + CLUTTER_COLORSPACE_BT2020, + CLUTTER_TRANSFER_FUNCTION_PQ); + + srgb_srgb_to_bt2020_linear = cogl_pipeline_new (cogl_context); + srgb_srgb_to_bt2020_linear_copy = + cogl_pipeline_copy (srgb_srgb_to_bt2020_linear); + + clutter_pipeline_cache_set_pipeline (pipeline_cache, group, 0, + srgb_srgb, bt2020_linear, + srgb_srgb_to_bt2020_linear); + clutter_pipeline_cache_set_pipeline (pipeline_cache, group, 1, + srgb_srgb, bt2020_linear, + srgb_srgb_to_bt2020_linear_copy); + + g_assert_true (clutter_pipeline_cache_get_pipeline (pipeline_cache, group, 0, + srgb_srgb, bt2020_linear) == + srgb_srgb_to_bt2020_linear); + g_assert_true (clutter_pipeline_cache_get_pipeline (pipeline_cache, group, 1, + srgb_srgb, bt2020_linear) == + srgb_srgb_to_bt2020_linear_copy); +} + +CLUTTER_TEST_SUITE ( + CLUTTER_TEST_UNIT ("/pipeline-cache/group-pipelines", pipeline_cache_group_pipelines) + CLUTTER_TEST_UNIT ("/pipeline-cache/replace-pipeline", pipeline_cache_replace_pipeline) + CLUTTER_TEST_UNIT ("/pipeline-cache/pipeline-slots", pipeline_slots) +)