Skip to content

Draft: migrate color mgmt from AMD driver specific properties to new KMS color API#2113

Open
melissawen wants to merge 21 commits into
ValveSoftware:masterfrom
melissawen:kms_color_v2
Open

Draft: migrate color mgmt from AMD driver specific properties to new KMS color API#2113
melissawen wants to merge 21 commits into
ValveSoftware:masterfrom
melissawen:kms_color_v2

Conversation

@melissawen

@melissawen melissawen commented Mar 16, 2026

Copy link
Copy Markdown

With the new KMS color API for plane color mgmt, we can replace most of AMD driver-specific color properties by plane color pipeline and its colorops, except CRTC regamma TF [1] and color_range/color_encoding (bypass on kernel) [2]. As libliftoff doesn't support the colorop objs yet, I'm not sure if what I did is acceptable: to program colorops only after libliftoff has mapped layers-planes. Besides those comments, I'd like to mention that I used Claude code to debug a problem with blobs and figure out that shaper and 3D LUTs use a larger LUT struct with 32-bits per channel (drm_color_lut32 instead of drm_color_lut) and to create a helper that maps uint16 to uint32 values when creating LUTs for a given colorspace.

My main goal is validating and exercising KMS colorop code.

This branch is based on @emersion work at: #1309

Lemme know your thoughts.

[1] custom kernel that still exposes regamma TF: https://gitlab.freedesktop.org/mwen/drm-misc/-/commit/428cc3331d678d8e6ce6de5322a8be62ca2ff08b
[2] https://gitlab.freedesktop.org/agd5f/linux/-/commit/e0edb5809f1ec477b7b969200dc89bc171944ad4

@melissawen
melissawen force-pushed the kms_color_v2 branch 2 times, most recently from d1e4e71 to 1a56328 Compare March 16, 2026 21:29
@matte-schwartz

Copy link
Copy Markdown
Collaborator

@melissawen feel free to just squash this into one of your existing commits or replace it with your own version, but I was getting what appeared to be a "deep-fried" image when testing this on Steam Deck OLED with a kernel built from the branch you linked plus the fixes that have been sent out on amd-gfx while compositing an image. it looked like color management was being applied twice even though scanout was fine. this fixed the issue on my end after digging into why it was only occurring with the color pipeline path: matte-schwartz@e097b54

@melissawen

Copy link
Copy Markdown
Author

Hi @matte-schwartz, thanks for testing and debugging. I'll add your commit to this branch. Indeed, your fix is aligned with the AMD driver-specific-prop path.

@emersion

Copy link
Copy Markdown
Collaborator

Thanks a lot for pushing this forward! I won't have time to review sadly.

@Zamundaaa

Copy link
Copy Markdown

lgtm

Comment thread src/drm_include.h Outdated
Comment on lines +91 to +99
DRM_COLOROP_1D_CURVE_SRGB_EOTF,
DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF,
DRM_COLOROP_1D_CURVE_PQ_125_EOTF,
DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF,
DRM_COLOROP_1D_CURVE_BT2020_INV_OETF,
DRM_COLOROP_1D_CURVE_BT2020_OETF,
DRM_COLOROP_1D_CURVE_GAMMA22,
DRM_COLOROP_1D_CURVE_GAMMA22_INV,
DRM_COLOROP_1D_CURVE_COUNT

@Zamundaaa Zamundaaa May 31, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
DRM_COLOROP_1D_CURVE_SRGB_EOTF,
DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF,
DRM_COLOROP_1D_CURVE_PQ_125_EOTF,
DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF,
DRM_COLOROP_1D_CURVE_BT2020_INV_OETF,
DRM_COLOROP_1D_CURVE_BT2020_OETF,
DRM_COLOROP_1D_CURVE_GAMMA22,
DRM_COLOROP_1D_CURVE_GAMMA22_INV,
DRM_COLOROP_1D_CURVE_COUNT
DRM_COLOROP_1D_CURVE_SRGB_EOTF,
DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF,
DRM_COLOROP_1D_CURVE_PQ_125_EOTF,
DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF,
DRM_COLOROP_1D_CURVE_BT2020_INV_OETF,
DRM_COLOROP_1D_CURVE_BT2020_OETF,
DRM_COLOROP_1D_CURVE_GAMMA22,
DRM_COLOROP_1D_CURVE_GAMMA22_INV,
DRM_COLOROP_1D_CURVE_COUNT

@matte-schwartz

Copy link
Copy Markdown
Collaborator

It looks like Intel DRM plane color support landed in v6.19, right? I'd like to double check that this doesn't regress anything on Xe before we merge this. I should have time early next week to confirm.

@flibitijibibo

Copy link
Copy Markdown

NV 610 also appears to have added this, haven't had a chance to test yet:

https://www.nvidia.com/en-us/drivers/details/271414/

@matte-schwartz

Copy link
Copy Markdown
Collaborator

I can try checking NV610 as well, but we already have so many other existing nvidia-drm bugs that it's a lower priority than i915/Xe imo

@matte-schwartz

Copy link
Copy Markdown
Collaborator

this MR seems to just coredump on Xe with a kernel that supports the color pipeline so I'm glad I checked...

Core was generated by `/usr/bin/gamescope --generate-drm-mode fixed --xwayland-count 2 -w 1280 -h 800 --default-touch-mode 4 --hide-cursor-delay 3000 --max-scale 2 --fade-out-duration 200 --cursor-scale-height 720 -e -R /run/user/1000/gamescope.dIWRO2J/startup.socket -T /run/user/1000/gamescope.dIWRO2J/stats.pipe -O \*,eDP-1'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  std::__detail::_Hashtable_base<PipelineInfo_t, std::pair<PipelineInfo_t const, VkPipeline_T*>, std::__detail::_Select1st, std::equal_to<PipelineInfo_t>, std::hash<PipelineInfo_t>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Hashtable_traits<true, false, true> >::_M_equals (this=<optimized out>, __k=..., __c=<optimized out>, __n=...) at /usr/include/c++/15.1.1/bits/hashtable_policy.h:1432

warning: 1432   /usr/include/c++/15.1.1/bits/hashtable_policy.h: No such file or directory
[Current thread is 1 (Thread 0x7f707b8166c0 (LWP 4711))]
(gdb) bt
#0  std::__detail::_Hashtable_base<PipelineInfo_t, std::pair<PipelineInfo_t const, VkPipeline_T*>, std::__detail::_Select1st, std::equal_to<PipelineInfo_t>, std::hash<PipelineInfo_t>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Hashtable_traits<true, false, true> >::_M_equals (this=<optimized out>, __k=..., __c=<optimized out>, __n=...) at /usr/include/c++/15.1.1/bits/hashtable_policy.h:1432
#1  std::_Hashtable<PipelineInfo_t, std::pair<PipelineInfo_t const, VkPipeline_T*>, std::allocator<std::pair<PipelineInfo_t const, VkPipeline_T*> >, std::__detail::_Select1st, std::equal_to<PipelineInfo_t>, std::hash<PipelineInfo_t>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::_M_find_before_node(unsigned long, PipelineInfo_t const&, unsigned long) const [clone .isra.0] (__bkt=19, __k=..., 
    __code=__code@entry=1735320724, this=<optimized out>) at /usr/include/c++/15.1.1/bits/hashtable.h:2212
#2  0x000055ac97bdcd6d in std::_Hashtable<PipelineInfo_t, std::pair<PipelineInfo_t const, VkPipeline_T*>, std::allocator<std::pair<PipelineInfo_t const, VkPipeline_T*> >, std::__detail::_Select1st, std::equal_to<PipelineInfo_t>, std::hash<PipelineInfo_t>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::_M_locate (this=0x55ac97de0488 <g_device+1448>, __k=...)
    at /usr/include/c++/15.1.1/bits/hashtable.h:2283
#3  std::_Hashtable<PipelineInfo_t, std::pair<PipelineInfo_t const, VkPipeline_T*>, std::allocator<std::pair<PipelineInfo_t const, VkPipeline_T*> >, std::__detail::_Select1st, std::equal_to<PipelineInfo_t>, std::hash<PipelineInfo_t>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::_M_emplace_uniq<std::pair<PipelineInfo_t, VkPipeline_T*> > (this=0x55ac97de0488 <g_device+1448>)
    at /usr/include/c++/15.1.1/bits/hashtable.h:2351
#4  std::_Hashtable<PipelineInfo_t, std::pair<PipelineInfo_t const, VkPipeline_T*>, std::allocator<std::pair<PipelineInfo_t const, VkPipeline_T*> >, std::__detail::_Select1st, std::equal_to<PipelineInfo_t>, std::hash<PipelineInfo_t>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::emplace<std::pair<PipelineInfo_t, VkPipeline_T*> > (this=0x55ac97de0488 <g_device+1448>)
    at /usr/include/c++/15.1.1/bits/hashtable.h:1029
#5  std::unordered_map<PipelineInfo_t, VkPipeline_T*, std::hash<PipelineInfo_t>, std::equal_to<PipelineInfo_t>, std::allocator<std::pair<PipelineInfo_t const, VkPipeline_T*> > >::emplace<std::pair<PipelineInfo_t, VkPipeline_T*> > (
    this=0x55ac97de0488 <g_device+1448>) at /usr/include/c++/15.1.1/bits/unordered_map.h:442
#6  CVulkanDevice::compileAllPipelines (this=0x55ac97ddfee0 <g_device>) at ../src/rendervulkan.cpp:1215
#7  0x00007f708eee51a4 in std::execute_native_thread_routine (__p=0x55acc2adaf10) at /usr/src/debug/gcc/gcc/libstdc++-v3/src/c++11/thread.cc:104
#8  0x00007f708eca57eb in ?? () from /usr/lib/libc.so.6
#9  0x00007f708ed2920c in ?? () from /usr/lib/libc.so.6

will see if i can figure out why.

@k2naveen

Copy link
Copy Markdown

HI @matte-schwartz,
I've built on top of this MR to make the color pipeline generic: #2218. The current implementation assumes AMD-specific vendor enum values when selecting a COLOR_PIPELINE, which silently breaks on any other hardware that advertises the property. #2218 replaces that with a generic colorop chain walker (FindByType, same pattern as KWin/Weston) and adds a 1D fallback path for drivers without a 3D LUT - tested working on Intel Arc with HDR and SDR output, while keeping the AMD shaper + 3D LUT path unchanged.

emersion and others added 17 commits July 15, 2026 19:40
Signed-off-by: Melissa Wen <mwen@igalia.com>
Signed-off-by: Melissa Wen <mwen@igalia.com>
Signed-off-by: Melissa Wen <mwen@igalia.com>
Store the value of the color pipeline enum for a given plane that fits
color pipeline needs. So that we can set plane COLOR_PIPELINE property
with this value.

Signed-off-by: Melissa Wen <mwen@igalia.com>
Check if there is any color pipeline with the properties needed for
plane color mgmt.

Signed-off-by: Melissa Wen <mwen@igalia.com>
Add CURVE_1D_TYPE property to colorop, helper for degamma curve using
DRM_COLOROP_* predefined curve instead of AMD_TRANSFER_FUNCTIONs, add
set degamma 1D curve accordingly

Signed-off-by: Melissa Wen <mwen@igalia.com>
Create helper to use DRM_COLOROP curves instead of
AMD_TRANSFER_FUNCTIONs and set properties accordingly

Signed-off-by: Melissa Wen <mwen@igalia.com>
Create a helper to translate output tf of AMD_TRANSFER_FUNCTION enum
type to DRM_COLOROP type

Signed-off-by: Melissa Wen <mwen@igalia.com>
Gamescope already use 3x4 matrix, so doesn't needed additional helper.

Signed-off-by: Melissa Wen <mwen@igalia.com>
Signed-off-by: Melissa Wen <mwen@igalia.com>
Instead of struct drm_color_lut, shaper and 3D LUTs follow struct
drm_color_lut32, which means 32-bit for each channel, not 16-bit
anymore. Create a helper to make possible reuse LUTs already created for
AMD driver-specific color properties.

Signed-off-by: Melissa Wen <mwen@igalia.com>
Signed-off-by: Melissa Wen <mwen@igalia.com>
Match the existing behavior of the legacy AMD plane properties, which
reset layers to DEFAULT without color management applied to them.

Otherwise, stale color pipeline state from the previous scanout frame
persists during composition, resulting in double color management and an
overly-saturated output image.

Signed-off-by: Matthew Schwartz <matthew.schwartz@linux.dev>
Those properties are deprecated when using KMS colorop API

Signed-off-by: Melissa Wen <mwen@igalia.com>
Signed-off-by: Melissa Wen <mwen@igalia.com>
Signed-off-by: Melissa Wen <mwen@igalia.com>
Follow similar approach of plane color pipelines. For AMD, the expected
CRTC color pipeline comprises of CTM, regamma/out 1D Curve (regamma TF)
and regamma LUT.

Signed-off-by: Melissa Wen <mwen@igalia.com>
Signed-off-by: Melissa Wen <mwen@igalia.com>
If post-blend colorop API is supported, set post-blend 1D Curve with
regamma TF values and bypass CTM and regamma LUT. Same as what is done
with AMD driver specific props.

Signed-off-by: Melissa Wen <mwen@igalia.com>
@melissawen

Copy link
Copy Markdown
Author

FWIW, I updated this branch to include plane Fixed Matrix colorop and post-blend/CRTC colorops. So that gamescope fully migrates from AMD private props to KMS genereic color API. A custom kernel is needed for this full migration since some patches weren't merged upstream yet. You can find a kernel with full colorop pipeline support for AMD here: https://gitlab.freedesktop.org/mwen/drm-misc/-/commits/gamescope_pipeline_w_colorop

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants