From fdaa5c64f454983a6dbb18753c1cb1ab1f743cfa Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 19 May 2024 12:30:36 +0200 Subject: [PATCH 01/21] drm: fetch plane color pipelines --- src/Backends/DRMBackend.cpp | 91 ++++++++++++++++++++++++++++++++++++- src/drm_include.h | 4 ++ 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp index 134e19ac5f..d5d8bcaf0f 100644 --- a/src/Backends/DRMBackend.cpp +++ b/src/Backends/DRMBackend.cpp @@ -267,6 +267,7 @@ namespace gamescope static std::optional Instantiate( const char *pszName, CDRMAtomicObject *pObject, const DRMObjectRawProperties& rawProperties ); + uint32_t GetPropertyId() const { return m_uPropertyId; } uint64_t GetPendingValue() const { return m_ulPendingValue; } uint64_t GetCurrentValue() const { return m_ulCurrentValue; } uint64_t GetInitialValue() const { return m_ulInitialValue; } @@ -326,6 +327,7 @@ namespace gamescope std::optional AMD_PLANE_LUT3D; std::optional AMD_PLANE_BLEND_TF; std::optional AMD_PLANE_BLEND_LUT; + std::optional COLOR_PIPELINE; std::optional DUMMY_END; }; PlaneProperties &GetProperties() { return m_Props; } @@ -537,6 +539,28 @@ namespace gamescope ConnectorProperties m_Props; }; + class CDRMColorOp final : public CDRMAtomicTypedObject + { + public: + CDRMColorOp( uint32_t uColorOpId ); + + void RefreshState(); + + struct ColorOpProperties + { + std::optional TYPE; // Immutable + std::optional NEXT; // Immutable + std::optional BYPASS; // Immutable + + std::optional DATA; + std::optional MULTIPLIER; + }; + ColorOpProperties &GetProperties() { return m_Props; } + const ColorOpProperties &GetProperties() const { return m_Props; } + private: + ColorOpProperties m_Props; + }; + class CDRMFb final : public CBaseBackendFb { public: @@ -544,7 +568,7 @@ namespace gamescope ~CDRMFb(); uint32_t GetFbId() const { return m_uFbId; } - + private: uint32_t m_uFbId = 0; }; @@ -915,6 +939,43 @@ static bool refresh_state( drm_t *drm ) return true; } +static bool get_color_pipeline( struct drm_t *drm, uint32_t uHeadId ) +{ + std::vector> pipeline; + + uint32_t uColorOpId = uHeadId; + while ( uColorOpId != 0 ) + { + auto pColorOp = std::make_unique( uHeadId ); + pColorOp->RefreshState(); + uColorOpId = pColorOp->GetProperties().NEXT.value().GetInitialValue(); + pipeline.emplace_back( std::move(pColorOp) ); + } + + return true; +} + +static bool get_plane_color_pipelines( struct drm_t *drm, std::unique_ptr< gamescope::CDRMPlane > &pPlane ) +{ + auto pColorPipelineProp = pPlane->GetProperties().COLOR_PIPELINE; + if ( !pColorPipelineProp ) + return true; + + drmModePropertyRes *pProperty = drmModeGetProperty( g_DRM.fd, pColorPipelineProp->GetPropertyId() ); + if ( !pProperty ) + return false; + defer( drmModeFreeProperty( pProperty ) ); + + for ( int i = 0; i < pProperty->count_enums; i++ ) + { + auto entry = pProperty->enums[ i ]; + if ( !get_color_pipeline( drm, entry.value ) ) + return false; + } + + return true; +} + static bool get_resources(struct drm_t *drm) { { @@ -951,6 +1012,12 @@ static bool get_resources(struct drm_t *drm) } } + for ( std::unique_ptr< gamescope::CDRMPlane > &pPlane : drm->planes ) + { + if ( !get_plane_color_pipelines( drm, pPlane ) ) + return false; + } + return refresh_state( drm ); } @@ -2113,6 +2180,7 @@ namespace gamescope m_Props.AMD_PLANE_LUT3D = CDRMAtomicProperty::Instantiate( "AMD_PLANE_LUT3D", this, *rawProperties ); m_Props.AMD_PLANE_BLEND_TF = CDRMAtomicProperty::Instantiate( "AMD_PLANE_BLEND_TF", this, *rawProperties ); m_Props.AMD_PLANE_BLEND_LUT = CDRMAtomicProperty::Instantiate( "AMD_PLANE_BLEND_LUT", this, *rawProperties ); + m_Props.COLOR_PIPELINE = CDRMAtomicProperty::Instantiate( "COLOR PIPELINE", this, *rawProperties ); } } @@ -2594,6 +2662,27 @@ namespace gamescope } } + ///////////////////////// + // CDRMConnector + ///////////////////////// + CDRMColorOp::CDRMColorOp( uint32_t uColorOpId ) + : CDRMAtomicTypedObject( uColorOpId ) + { + } + + void CDRMColorOp::RefreshState() + { + auto rawProperties = GetRawProperties(); + if ( rawProperties ) + { + m_Props.TYPE = CDRMAtomicProperty::Instantiate( "TYPE", this, *rawProperties ); + m_Props.NEXT = CDRMAtomicProperty::Instantiate( "NEXT", this, *rawProperties ); + m_Props.BYPASS = CDRMAtomicProperty::Instantiate( "BYPASS", this, *rawProperties ); + m_Props.DATA = CDRMAtomicProperty::Instantiate( "DATA", this, *rawProperties ); + m_Props.MULTIPLIER = CDRMAtomicProperty::Instantiate( "MULTIPLIER", this, *rawProperties ); + } + } + ///////////////////////// // CDRMFb ///////////////////////// diff --git a/src/drm_include.h b/src/drm_include.h index d0ed3ce9e2..3924692748 100644 --- a/src/drm_include.h +++ b/src/drm_include.h @@ -86,3 +86,7 @@ enum drm_colorspace { #define DRM_MODE_CONTENT_TYPE_PHOTO 2 #define DRM_MODE_CONTENT_TYPE_CINEMA 3 #define DRM_MODE_CONTENT_TYPE_GAME 4 + +#define DRM_MODE_OBJECT_COLOROP 0xfafafafa + +#define DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE 7 From 0ac380fa2043a434ce156695d9a2db78e7b3c6d6 Mon Sep 17 00:00:00 2001 From: Melissa Wen Date: Thu, 3 Jul 2025 18:20:02 -0300 Subject: [PATCH 02/21] drm: fixes to fetch color pipelines Signed-off-by: Melissa Wen --- src/Backends/DRMBackend.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp index d5d8bcaf0f..49131f86e0 100644 --- a/src/Backends/DRMBackend.cpp +++ b/src/Backends/DRMBackend.cpp @@ -946,7 +946,7 @@ static bool get_color_pipeline( struct drm_t *drm, uint32_t uHeadId ) uint32_t uColorOpId = uHeadId; while ( uColorOpId != 0 ) { - auto pColorOp = std::make_unique( uHeadId ); + auto pColorOp = std::make_unique( uColorOpId ); pColorOp->RefreshState(); uColorOpId = pColorOp->GetProperties().NEXT.value().GetInitialValue(); pipeline.emplace_back( std::move(pColorOp) ); @@ -2180,7 +2180,7 @@ namespace gamescope m_Props.AMD_PLANE_LUT3D = CDRMAtomicProperty::Instantiate( "AMD_PLANE_LUT3D", this, *rawProperties ); m_Props.AMD_PLANE_BLEND_TF = CDRMAtomicProperty::Instantiate( "AMD_PLANE_BLEND_TF", this, *rawProperties ); m_Props.AMD_PLANE_BLEND_LUT = CDRMAtomicProperty::Instantiate( "AMD_PLANE_BLEND_LUT", this, *rawProperties ); - m_Props.COLOR_PIPELINE = CDRMAtomicProperty::Instantiate( "COLOR PIPELINE", this, *rawProperties ); + m_Props.COLOR_PIPELINE = CDRMAtomicProperty::Instantiate( "COLOR_PIPELINE", this, *rawProperties ); } } From 10f698aaab83f783c8972176fc7e84e844f1bb18 Mon Sep 17 00:00:00 2001 From: Melissa Wen Date: Fri, 4 Jul 2025 09:51:18 -0300 Subject: [PATCH 03/21] drm: check if color pipeline api is supported Signed-off-by: Melissa Wen --- src/Backends/DRMBackend.cpp | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp index 49131f86e0..77ff4220d2 100644 --- a/src/Backends/DRMBackend.cpp +++ b/src/Backends/DRMBackend.cpp @@ -607,6 +607,7 @@ struct drm_color_ctm2 { bool g_bSupportsAsyncFlips = false; bool g_bSupportsSyncObjs = false; +bool g_bSupportsColorPipeline = false; extern gamescope::GamescopeModeGeneration g_eGamescopeModeGeneration; extern GamescopePanelOrientation g_DesiredInternalOrientation; @@ -1012,10 +1013,13 @@ static bool get_resources(struct drm_t *drm) } } - for ( std::unique_ptr< gamescope::CDRMPlane > &pPlane : drm->planes ) + if ( g_bSupportsColorPipeline ) { - if ( !get_plane_color_pipelines( drm, pPlane ) ) - return false; + for ( std::unique_ptr< gamescope::CDRMPlane > &pPlane : drm->planes ) + { + if ( !get_plane_color_pipelines( drm, pPlane ) ) + return false; + } } return refresh_state( drm ); @@ -1392,6 +1396,8 @@ bool init_drm(struct drm_t *drm, int width, int height, int refresh) drm_log.errorf("Immediate flips disabled from environment"); } + g_bSupportsColorPipeline = drmSetClientCap(drm->fd, DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE, 1) == 0; + if (!get_resources(drm)) { return false; } @@ -3523,12 +3529,29 @@ bool drm_supports_color_mgmt(struct drm_t *drm) if ( g_bForceDisableColorMgmt ) return false; + if ( g_bSupportsColorPipeline ) + return false; + if ( !drm->pPrimaryPlane ) return false; return drm->pPrimaryPlane->GetProperties().AMD_PLANE_CTM.has_value() && drm->pPrimaryPlane->GetProperties().AMD_PLANE_BLEND_TF.has_value(); } +bool drm_supports_color_pipeline(struct drm_t *drm) +{ + if ( g_bForceDisableColorMgmt ) + return false; + + if ( !g_bSupportsColorPipeline ) + return false; + + if ( !drm->pPrimaryPlane ) + return false; + + return drm->pPrimaryPlane->GetProperties().COLOR_PIPELINE.has_value() ; +} + std::span drm_get_valid_refresh_rates( struct drm_t *drm ) { if ( drm && drm->pConnector ) From ea83fa4a6a6e338bb27a4d7ac9e7aceb1762e043 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 15 May 2024 16:55:53 +0200 Subject: [PATCH 04/21] drm: check pipeline shape --- src/Backends/DRMBackend.cpp | 43 +++++++++++++++++++++++++++++++++---- src/drm_include.h | 18 ++++++++++++++++ 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp index 77ff4220d2..c939ed5348 100644 --- a/src/Backends/DRMBackend.cpp +++ b/src/Backends/DRMBackend.cpp @@ -561,6 +561,16 @@ namespace gamescope ColorOpProperties m_Props; }; + struct CDRMColorPipeline + { + std::unique_ptr degamma; + std::unique_ptr CTM; + std::unique_ptr HDRMult; + std::unique_ptr shaper; + std::unique_ptr lut3D; + std::unique_ptr blend; + }; + class CDRMFb final : public CBaseBackendFb { public: @@ -940,7 +950,7 @@ static bool refresh_state( drm_t *drm ) return true; } -static bool get_color_pipeline( struct drm_t *drm, uint32_t uHeadId ) +static std::optional get_color_pipeline( struct drm_t *drm, uint32_t uHeadId ) { std::vector> pipeline; @@ -953,7 +963,31 @@ static bool get_color_pipeline( struct drm_t *drm, uint32_t uHeadId ) pipeline.emplace_back( std::move(pColorOp) ); } - return true; + // Check if the pipeline has what we need + if ( pipeline.size() != 6 ) + return {}; + if ( pipeline[0]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_1D_CURVE ) + return {}; + if ( pipeline[1]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_CTM_3X4 ) + return {}; + if ( pipeline[2]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_MULTIPLIER ) + return {}; + if ( pipeline[3]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_1D_CURVE ) + return {}; + if ( pipeline[4]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_3D_LUT ) + return {}; + if ( pipeline[5]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_1D_CURVE ) + return {}; + + gamescope::CDRMColorPipeline p { + .degamma = std::move(pipeline[0]), + .CTM = std::move(pipeline[1]), + .HDRMult = std::move(pipeline[2]), + .shaper = std::move(pipeline[3]), + .lut3D = std::move(pipeline[4]), + .blend = std::move(pipeline[5]), + }; + return p; } static bool get_plane_color_pipelines( struct drm_t *drm, std::unique_ptr< gamescope::CDRMPlane > &pPlane ) @@ -970,8 +1004,7 @@ static bool get_plane_color_pipelines( struct drm_t *drm, std::unique_ptr< games for ( int i = 0; i < pProperty->count_enums; i++ ) { auto entry = pProperty->enums[ i ]; - if ( !get_color_pipeline( drm, entry.value ) ) - return false; + get_color_pipeline( drm, entry.value ); } return true; @@ -1356,6 +1389,8 @@ bool init_drm(struct drm_t *drm, int width, int height, int refresh) return false; } + drmSetClientCap(drm->fd, DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE, 1); + if (drmGetCap(drm->fd, DRM_CAP_CURSOR_WIDTH, &drm->cursor_width) != 0) { drm->cursor_width = 64; } diff --git a/src/drm_include.h b/src/drm_include.h index 3924692748..86d9a5ab08 100644 --- a/src/drm_include.h +++ b/src/drm_include.h @@ -90,3 +90,21 @@ enum drm_colorspace { #define DRM_MODE_OBJECT_COLOROP 0xfafafafa #define DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE 7 + +enum drm_colorop_type { + DRM_COLOROP_1D_CURVE, + DRM_COLOROP_1D_LUT, + DRM_COLOROP_CTM_3X4, + DRM_COLOROP_MULTIPLIER, + DRM_COLOROP_3D_LUT, /* TODO: check value w/ kernel */ +}; + +enum drm_colorop_curve_1d_type { + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF, + DRM_COLOROP_1D_CURVE_BT2020_INV_OETF, + DRM_COLOROP_1D_CURVE_BT2020_OETF, + DRM_COLOROP_1D_CURVE_PQ_125_EOTF, + DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF, + DRM_COLOROP_1D_CURVE_COUNT +}; From 12a0e51af6783cd7b99af4a43acefd7c70a677a8 Mon Sep 17 00:00:00 2001 From: Melissa Wen Date: Thu, 3 Jul 2025 18:33:30 -0300 Subject: [PATCH 05/21] drm: update plane color pipeline Signed-off-by: Melissa Wen --- src/Backends/DRMBackend.cpp | 44 +++++++++++++++++++++++-------------- src/drm_include.h | 22 +++++++------------ 2 files changed, 36 insertions(+), 30 deletions(-) diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp index c939ed5348..3682866bf9 100644 --- a/src/Backends/DRMBackend.cpp +++ b/src/Backends/DRMBackend.cpp @@ -564,11 +564,13 @@ namespace gamescope struct CDRMColorPipeline { std::unique_ptr degamma; - std::unique_ptr CTM; std::unique_ptr HDRMult; + std::unique_ptr CTM; std::unique_ptr shaper; + std::unique_ptr shaperLut; std::unique_ptr lut3D; std::unique_ptr blend; + std::unique_ptr blendLut; }; class CDRMFb final : public CBaseBackendFb @@ -964,50 +966,59 @@ static std::optional get_color_pipeline( struct dr } // Check if the pipeline has what we need - if ( pipeline.size() != 6 ) + if ( pipeline.size() != 8 ) return {}; if ( pipeline[0]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_1D_CURVE ) return {}; - if ( pipeline[1]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_CTM_3X4 ) + if ( pipeline[1]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_MULTIPLIER ) return {}; - if ( pipeline[2]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_MULTIPLIER ) + if ( pipeline[2]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_CTM_3X4 ) return {}; if ( pipeline[3]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_1D_CURVE ) return {}; - if ( pipeline[4]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_3D_LUT ) + if ( pipeline[4]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_1D_LUT ) + return {}; + if ( pipeline[5]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_3D_LUT ) return {}; - if ( pipeline[5]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_1D_CURVE ) + if ( pipeline[6]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_1D_CURVE ) + return {}; + if ( pipeline[7]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_1D_LUT ) return {}; gamescope::CDRMColorPipeline p { .degamma = std::move(pipeline[0]), - .CTM = std::move(pipeline[1]), - .HDRMult = std::move(pipeline[2]), + .HDRMult = std::move(pipeline[1]), + .CTM = std::move(pipeline[2]), .shaper = std::move(pipeline[3]), - .lut3D = std::move(pipeline[4]), - .blend = std::move(pipeline[5]), + .shaperLut = std::move(pipeline[4]), + .lut3D = std::move(pipeline[5]), + .blend = std::move(pipeline[6]), + .blendLut = std::move(pipeline[7]), }; return p; } -static bool get_plane_color_pipelines( struct drm_t *drm, std::unique_ptr< gamescope::CDRMPlane > &pPlane ) +static std::optional get_plane_color_pipelines( struct drm_t *drm, std::unique_ptr< gamescope::CDRMPlane > &pPlane ) { auto pColorPipelineProp = pPlane->GetProperties().COLOR_PIPELINE; if ( !pColorPipelineProp ) - return true; + return {}; drmModePropertyRes *pProperty = drmModeGetProperty( g_DRM.fd, pColorPipelineProp->GetPropertyId() ); if ( !pProperty ) - return false; + return {}; + defer( drmModeFreeProperty( pProperty ) ); for ( int i = 0; i < pProperty->count_enums; i++ ) { auto entry = pProperty->enums[ i ]; - get_color_pipeline( drm, entry.value ); + std::optional p = get_color_pipeline( drm, entry.value ); + if ( p.has_value() ) + return p; } - return true; + return {}; } static bool get_resources(struct drm_t *drm) @@ -1050,7 +1061,8 @@ static bool get_resources(struct drm_t *drm) { for ( std::unique_ptr< gamescope::CDRMPlane > &pPlane : drm->planes ) { - if ( !get_plane_color_pipelines( drm, pPlane ) ) + // AMD Cursor plane doesn't support color management + if ( pPlane->GetProperties().type->GetCurrentValue() != DRM_PLANE_TYPE_CURSOR && !get_plane_color_pipelines( drm, pPlane ) ) return false; } } diff --git a/src/drm_include.h b/src/drm_include.h index 86d9a5ab08..c9c4a23ee7 100644 --- a/src/drm_include.h +++ b/src/drm_include.h @@ -87,24 +87,18 @@ enum drm_colorspace { #define DRM_MODE_CONTENT_TYPE_CINEMA 3 #define DRM_MODE_CONTENT_TYPE_GAME 4 -#define DRM_MODE_OBJECT_COLOROP 0xfafafafa - -#define DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE 7 - -enum drm_colorop_type { - DRM_COLOROP_1D_CURVE, - DRM_COLOROP_1D_LUT, - DRM_COLOROP_CTM_3X4, - DRM_COLOROP_MULTIPLIER, - DRM_COLOROP_3D_LUT, /* TODO: check value w/ kernel */ -}; - enum drm_colorop_curve_1d_type { DRM_COLOROP_1D_CURVE_SRGB_EOTF, DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF, - DRM_COLOROP_1D_CURVE_BT2020_INV_OETF, - DRM_COLOROP_1D_CURVE_BT2020_OETF, 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_GAMMA24, + DRM_COLOROP_1D_CURVE_GAMMA24_INV, + DRM_COLOROP_1D_CURVE_GAMMA26, + DRM_COLOROP_1D_CURVE_GAMMA26_INV, DRM_COLOROP_1D_CURVE_COUNT }; From daa6e4e94051c6ceae291a625e01a70f832f64d6 Mon Sep 17 00:00:00 2001 From: Melissa Wen Date: Thu, 5 Mar 2026 14:12:39 -0300 Subject: [PATCH 06/21] drm: store Color Pipeline id 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 --- src/Backends/DRMBackend.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp index 3682866bf9..5d6390189f 100644 --- a/src/Backends/DRMBackend.cpp +++ b/src/Backends/DRMBackend.cpp @@ -563,6 +563,7 @@ namespace gamescope struct CDRMColorPipeline { + uint32_t id; std::unique_ptr degamma; std::unique_ptr HDRMult; std::unique_ptr CTM; @@ -986,6 +987,7 @@ static std::optional get_color_pipeline( struct dr return {}; gamescope::CDRMColorPipeline p { + .id = uHeadId, .degamma = std::move(pipeline[0]), .HDRMult = std::move(pipeline[1]), .CTM = std::move(pipeline[2]), From 25b172cfdda753327ecf73af17926b8abd1bcaea Mon Sep 17 00:00:00 2001 From: Melissa Wen Date: Thu, 5 Mar 2026 14:15:52 -0300 Subject: [PATCH 07/21] drm: set color pipeline that fits plane color mgmt needs Check if there is any color pipeline with the properties needed for plane color mgmt. Signed-off-by: Melissa Wen --- src/Backends/DRMBackend.cpp | 40 ++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp index 5d6390189f..1a98f34f3e 100644 --- a/src/Backends/DRMBackend.cpp +++ b/src/Backends/DRMBackend.cpp @@ -608,6 +608,7 @@ extern std::string g_reshade_effect; bool drm_update_color_mgmt(struct drm_t *drm); bool drm_supports_color_mgmt(struct drm_t *drm); +bool drm_supports_color_pipeline(struct drm_t *drm); bool drm_set_connector( struct drm_t *drm, gamescope::CDRMConnector *conn ); struct drm_color_ctm2 { @@ -2976,6 +2977,39 @@ drm_prepare_liftoff( struct drm_t *drm, const struct FrameInfo_t *frameInfo, boo } } + if ( ret == 0 && drm_supports_color_pipeline( drm ) ) + { + auto entry = FrameInfoToLiftoffStateCacheEntry( drm, frameInfo ); + for ( int i = 0; i < frameInfo->layerCount; i++ ) + { + if ( !frameInfo->layers[i].applyColorMgmt ) + continue; + + struct liftoff_plane *plane = liftoff_layer_get_plane( drm->lo_layers[ i ] ); + uint32_t plane_id = plane ? liftoff_plane_get_id( plane ) : 0; + + if ( plane_id == 0 ) + continue; + + for ( std::unique_ptr< gamescope::CDRMPlane > &pPlane : drm->planes ) + { + if ( pPlane->GetObjectId() != plane_id ) + continue; + + bool bYCbCr = entry.layerState[i].ycbcr; + std::optional p = get_plane_color_pipelines( drm, pPlane ); + if ( !p ) { + drm_log.debugf( "drm_prepare_liftoff: No color pipeline fits color mgmt needs of plane_id %d", plane_id ); + break; + } + + pPlane->GetProperties().COLOR_PIPELINE->SetPendingValue( drm->req, p->id, true ); ++ + break; + } + } + } + if ( ret == 0 ) { // We don't support partial composition yet @@ -3088,7 +3122,7 @@ int drm_prepare( struct drm_t *drm, bool async, const struct FrameInfo_t *frameI bool bSinglePlane = frameInfo->layerCount < 2 && cv_drm_single_plane_optimizations; - if ( drm_supports_color_mgmt( &g_DRM ) && frameInfo->applyOutputColorMgmt ) + if ( ( drm_supports_color_mgmt( &g_DRM ) || drm_supports_color_pipeline( &g_DRM ) ) && frameInfo->applyOutputColorMgmt ) { if ( !cv_drm_debug_disable_output_tf && !bSinglePlane ) { @@ -3376,7 +3410,7 @@ gamescope::GamescopeScreenType drm_get_screen_type(struct drm_t *drm) bool drm_update_color_mgmt(struct drm_t *drm) { - if ( !drm_supports_color_mgmt( drm ) ) + if ( !drm_supports_color_mgmt( drm ) && !drm_supports_color_pipeline ( &g_DRM ) ) return true; if ( g_ColorMgmt.serial == drm->current.color_mgmt_serial ) @@ -4127,7 +4161,7 @@ namespace gamescope bool SupportsColorManagement() const { - return drm_supports_color_mgmt( &g_DRM ); + return drm_supports_color_mgmt( &g_DRM ) || drm_supports_color_pipeline( &g_DRM ); } int Commit( const FrameInfo_t *pFrameInfo ) From d79c62c72330ccc58711c1165d608322467a4768 Mon Sep 17 00:00:00 2001 From: Melissa Wen Date: Fri, 13 Mar 2026 21:16:26 -0300 Subject: [PATCH 08/21] drm: add 1D degamma curve colorop 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 --- src/Backends/DRMBackend.cpp | 40 ++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp index 1a98f34f3e..49f95e1fc3 100644 --- a/src/Backends/DRMBackend.cpp +++ b/src/Backends/DRMBackend.cpp @@ -550,8 +550,8 @@ namespace gamescope { std::optional TYPE; // Immutable std::optional NEXT; // Immutable - std::optional BYPASS; // Immutable - + std::optional BYPASS; + std::optional CURVE_1D_TYPE; std::optional DATA; std::optional MULTIPLIER; }; @@ -1971,6 +1971,21 @@ static inline amdgpu_transfer_function colorspace_to_plane_degamma_tf(GamescopeA } } +static inline std::optional colorspace_to_drm_plane_degamma_curve(GamescopeAppTextureColorspace colorspace) +{ + switch ( colorspace ) + { + default: // Linear in this sense is SRGB. Linear = sRGB image view doing automatic sRGB -> Linear which doesn't happen on DRM side. + case GAMESCOPE_APP_TEXTURE_COLORSPACE_SRGB: + return DRM_COLOROP_1D_CURVE_SRGB_EOTF; + case GAMESCOPE_APP_TEXTURE_COLORSPACE_PASSTHRU: + case GAMESCOPE_APP_TEXTURE_COLORSPACE_SCRGB: + return std::nullopt; // DEFAULT doesn't exist anymore + case GAMESCOPE_APP_TEXTURE_COLORSPACE_HDR10_PQ: + return DRM_COLOROP_1D_CURVE_PQ_125_EOTF; + } +} + static inline amdgpu_transfer_function colorspace_to_plane_shaper_tf(GamescopeAppTextureColorspace colorspace) { switch ( colorspace ) @@ -2735,6 +2750,7 @@ namespace gamescope m_Props.NEXT = CDRMAtomicProperty::Instantiate( "NEXT", this, *rawProperties ); m_Props.BYPASS = CDRMAtomicProperty::Instantiate( "BYPASS", this, *rawProperties ); m_Props.DATA = CDRMAtomicProperty::Instantiate( "DATA", this, *rawProperties ); + m_Props.CURVE_1D_TYPE = CDRMAtomicProperty::Instantiate( "CURVE_1D_TYPE", this, *rawProperties ); m_Props.MULTIPLIER = CDRMAtomicProperty::Instantiate( "MULTIPLIER", this, *rawProperties ); } } @@ -3004,7 +3020,25 @@ drm_prepare_liftoff( struct drm_t *drm, const struct FrameInfo_t *frameInfo, boo } pPlane->GetProperties().COLOR_PIPELINE->SetPendingValue( drm->req, p->id, true ); -+ + + GamescopeAppTextureColorspace colorspace = entry.layerState[i].colorspace; + std::optional degamma_tf = colorspace_to_drm_plane_degamma_curve( colorspace ); + + if ( bYCbCr ) + { + degamma_tf = DRM_COLOROP_1D_CURVE_BT2020_INV_OETF; + } + + bool bUseDegamma = !cv_drm_debug_disable_degamma_tf; + if ( bUseDegamma && degamma_tf.has_value() ) + { + p->degamma->GetProperties().BYPASS->SetPendingValue( drm->req, 0, true ); + p->degamma->GetProperties().CURVE_1D_TYPE->SetPendingValue( drm->req, *degamma_tf, true ); + } + else + { + p->degamma->GetProperties().BYPASS->SetPendingValue( drm->req, 1, true ); + } break; } } From 1bfc678ed3a83a4da7292e64f0ba58b5b2d17ecc Mon Sep 17 00:00:00 2001 From: Melissa Wen Date: Fri, 13 Mar 2026 21:22:03 -0300 Subject: [PATCH 09/21] drm: add shaper 1D curve Create helper to use DRM_COLOROP curves instead of AMD_TRANSFER_FUNCTIONs and set properties accordingly Signed-off-by: Melissa Wen --- src/Backends/DRMBackend.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp index 49f95e1fc3..948d8dba5d 100644 --- a/src/Backends/DRMBackend.cpp +++ b/src/Backends/DRMBackend.cpp @@ -2001,6 +2001,22 @@ static inline amdgpu_transfer_function colorspace_to_plane_shaper_tf(GamescopeAp } } +static inline std::optional colorspace_to_drm_plane_shaper_curve(GamescopeAppTextureColorspace colorspace) +{ + switch ( colorspace ) + { + default: // Linear in this sense is SRGB. Linear = sRGB image view doing automatic sRGB -> Linear which doesn't happen on DRM side. + case GAMESCOPE_APP_TEXTURE_COLORSPACE_SRGB: + return DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF; + case GAMESCOPE_APP_TEXTURE_COLORSPACE_HDR10_PQ: + case GAMESCOPE_APP_TEXTURE_COLORSPACE_SCRGB: + return DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF; + case GAMESCOPE_APP_TEXTURE_COLORSPACE_PASSTHRU: + return std::nullopt; // DEFAULT doesn't exist anymore + } +} + + static inline amdgpu_transfer_function inverse_tf(amdgpu_transfer_function tf) { switch ( tf ) @@ -3023,10 +3039,12 @@ drm_prepare_liftoff( struct drm_t *drm, const struct FrameInfo_t *frameInfo, boo GamescopeAppTextureColorspace colorspace = entry.layerState[i].colorspace; std::optional degamma_tf = colorspace_to_drm_plane_degamma_curve( colorspace ); + std::optional shaper_tf = colorspace_to_drm_plane_shaper_curve( colorspace ); if ( bYCbCr ) { degamma_tf = DRM_COLOROP_1D_CURVE_BT2020_INV_OETF; + shaper_tf = DRM_COLOROP_1D_CURVE_BT2020_OETF; } bool bUseDegamma = !cv_drm_debug_disable_degamma_tf; @@ -3039,6 +3057,18 @@ drm_prepare_liftoff( struct drm_t *drm, const struct FrameInfo_t *frameInfo, boo { p->degamma->GetProperties().BYPASS->SetPendingValue( drm->req, 1, true ); } + + bool bUseShaperAnd3DLUT = !cv_drm_debug_disable_shaper_and_3dlut; + if ( bUseShaperAnd3DLUT && shaper_tf.has_value() ) + { + p->shaper->GetProperties().BYPASS->SetPendingValue( drm->req, 0, true ); + p->shaper->GetProperties().CURVE_1D_TYPE->SetPendingValue( drm->req, *shaper_tf, true ); + } + else + { + p->shaper->GetProperties().BYPASS->SetPendingValue( drm->req, 1, true ); + } + break; } } From 8576f69177e030a01ca9b9839d7de0109825545f Mon Sep 17 00:00:00 2001 From: Melissa Wen Date: Fri, 13 Mar 2026 21:25:17 -0300 Subject: [PATCH 10/21] drm: add blend 1D curve and LUT Create a helper to translate output tf of AMD_TRANSFER_FUNCTION enum type to DRM_COLOROP type Signed-off-by: Melissa Wen --- src/Backends/DRMBackend.cpp | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp index 948d8dba5d..4d46ba7369 100644 --- a/src/Backends/DRMBackend.cpp +++ b/src/Backends/DRMBackend.cpp @@ -2053,6 +2053,27 @@ static inline amdgpu_transfer_function inverse_tf(amdgpu_transfer_function tf) } } +static inline std::optional amd_tf_to_drm_curve ( amdgpu_transfer_function tf ) +{ + switch ( tf ) + { + case AMDGPU_TRANSFER_FUNCTION_SRGB_EOTF: + return DRM_COLOROP_1D_CURVE_SRGB_EOTF; + case AMDGPU_TRANSFER_FUNCTION_PQ_EOTF: + return DRM_COLOROP_1D_CURVE_PQ_125_EOTF; + case AMDGPU_TRANSFER_FUNCTION_SRGB_INV_EOTF: + return DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF; + case AMDGPU_TRANSFER_FUNCTION_PQ_INV_EOTF: + return DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF; + case AMDGPU_TRANSFER_FUNCTION_GAMMA22_EOTF: + return DRM_COLOROP_1D_CURVE_GAMMA22; + case AMDGPU_TRANSFER_FUNCTION_GAMMA22_INV_EOTF: + return DRM_COLOROP_1D_CURVE_GAMMA22_INV; + default: + return std::nullopt; + } +} + static inline uint32_t ColorSpaceToEOTFIndex( GamescopeAppTextureColorspace colorspace ) { switch ( colorspace ) @@ -3069,6 +3090,19 @@ drm_prepare_liftoff( struct drm_t *drm, const struct FrameInfo_t *frameInfo, boo p->shaper->GetProperties().BYPASS->SetPendingValue( drm->req, 1, true ); } + std::optional blend_tf = amd_tf_to_drm_curve(drm->pending.output_tf); + if (!cv_drm_debug_disable_blend_tf && !bSinglePlane && blend_tf.has_value() ) + { + p->blend->GetProperties().BYPASS->SetPendingValue( drm->req, 0, true ); + p->blend->GetProperties().CURVE_1D_TYPE->SetPendingValue( drm->req, *blend_tf, true ); + } + else + { + p->blend->GetProperties().BYPASS->SetPendingValue( drm->req, 1, true ); + } + + p->blendLut->GetProperties().BYPASS->SetPendingValue( drm->req, 1, true ); + break; } } From e1f02094738dbc85385a33814b526dcabdb0e873 Mon Sep 17 00:00:00 2001 From: Melissa Wen Date: Fri, 13 Mar 2026 21:28:22 -0300 Subject: [PATCH 11/21] drm: add CTM colorop Gamescope already use 3x4 matrix, so doesn't needed additional helper. Signed-off-by: Melissa Wen --- src/Backends/DRMBackend.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp index 4d46ba7369..7379aebdd0 100644 --- a/src/Backends/DRMBackend.cpp +++ b/src/Backends/DRMBackend.cpp @@ -3103,6 +3103,16 @@ drm_prepare_liftoff( struct drm_t *drm, const struct FrameInfo_t *frameInfo, boo p->blendLut->GetProperties().BYPASS->SetPendingValue( drm->req, 1, true ); + if ( frameInfo->layers[i].ctm != nullptr ) + { + p->CTM->GetProperties().BYPASS->SetPendingValue( drm->req, 0, true ); + p->CTM->GetProperties().DATA->SetPendingValue( drm->req, frameInfo->layers[i].ctm->GetBlobValue(), true ); + } + else + { + p->CTM->GetProperties().BYPASS->SetPendingValue( drm->req, 1, true ); + } + break; } } From 141bad539e2a3293f57b8794984b25859f37f9bb Mon Sep 17 00:00:00 2001 From: Melissa Wen Date: Fri, 13 Mar 2026 21:32:20 -0300 Subject: [PATCH 12/21] drm: add HDR multiplier colorop Signed-off-by: Melissa Wen --- src/Backends/DRMBackend.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp index 7379aebdd0..4cfda86a7f 100644 --- a/src/Backends/DRMBackend.cpp +++ b/src/Backends/DRMBackend.cpp @@ -3113,6 +3113,9 @@ drm_prepare_liftoff( struct drm_t *drm, const struct FrameInfo_t *frameInfo, boo p->CTM->GetProperties().BYPASS->SetPendingValue( drm->req, 1, true ); } + p->HDRMult->GetProperties().BYPASS->SetPendingValue( drm->req, 0, true ); + p->HDRMult->GetProperties().MULTIPLIER->SetPendingValue( drm->req, 0x100000000ULL, true ); + break; } } From a8dcb89ac1213581d81396b0235e626e24b9fb8f Mon Sep 17 00:00:00 2001 From: Melissa Wen Date: Fri, 13 Mar 2026 21:33:24 -0300 Subject: [PATCH 13/21] drm: add shaper LUT and 3D LUT colorops 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 --- src/Backends/DRMBackend.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp index 4cfda86a7f..834d3511e0 100644 --- a/src/Backends/DRMBackend.cpp +++ b/src/Backends/DRMBackend.cpp @@ -135,6 +135,8 @@ struct drm_t { uint32_t color_mgmt_serial; std::shared_ptr lut3d_id[ EOTF_Count ]; std::shared_ptr shaperlut_id[ EOTF_Count ]; + std::shared_ptr lut3d_colorop_id[ EOTF_Count ]; + std::shared_ptr shaperlut_colorop_id[ EOTF_Count ]; amdgpu_transfer_function output_tf = AMDGPU_TRANSFER_FUNCTION_DEFAULT; } current, pending; @@ -3090,6 +3092,20 @@ drm_prepare_liftoff( struct drm_t *drm, const struct FrameInfo_t *frameInfo, boo p->shaper->GetProperties().BYPASS->SetPendingValue( drm->req, 1, true ); } + if ( bUseShaperAnd3DLUT ) + { + p->shaperLut->GetProperties().BYPASS->SetPendingValue( drm->req, 0, true ); + p->shaperLut->GetProperties().DATA->SetPendingValue( drm->req, drm->pending.shaperlut_colorop_id[ ColorSpaceToEOTFIndex( colorspace ) ]->GetBlobValue(), true ); + + p->lut3D->GetProperties().BYPASS->SetPendingValue( drm->req, 0, true ); + p->lut3D->GetProperties().DATA->SetPendingValue( drm->req, drm->pending.lut3d_colorop_id[ ColorSpaceToEOTFIndex( colorspace ) ]->GetBlobValue(), true ); + } + else + { + p->shaperLut->GetProperties().BYPASS->SetPendingValue( drm->req, 1, true ); + p->lut3D->GetProperties().BYPASS->SetPendingValue( drm->req, 1, true ); + } + std::optional blend_tf = amd_tf_to_drm_curve(drm->pending.output_tf); if (!cv_drm_debug_disable_blend_tf && !bSinglePlane && blend_tf.has_value() ) { @@ -3519,6 +3535,15 @@ gamescope::GamescopeScreenType drm_get_screen_type(struct drm_t *drm) return drm->pConnector->GetScreenType(); } +template +static std::array lut_convert_16bit_to_32bit( const uint16_t (&lut)[N] ) +{ + std::array out; + for ( size_t i = 0; i < N; i++ ) + out[i] = (uint32_t)lut[i] * 0x10001u; + return out; +} + bool drm_update_color_mgmt(struct drm_t *drm) { if ( !drm_supports_color_mgmt( drm ) && !drm_supports_color_pipeline ( &g_DRM ) ) @@ -3533,6 +3558,8 @@ bool drm_update_color_mgmt(struct drm_t *drm) { drm->pending.shaperlut_id[ i ] = 0; drm->pending.lut3d_id[ i ] = 0; + drm->pending.shaperlut_colorop_id[ i ] = 0; + drm->pending.lut3d_colorop_id[ i ] = 0; } for ( uint32_t i = 0; i < EOTF_Count; i++ ) @@ -3542,6 +3569,9 @@ bool drm_update_color_mgmt(struct drm_t *drm) drm->pending.shaperlut_id[ i ] = GetBackend()->CreateBackendBlob( g_ColorMgmtLuts[i].lut1d ); drm->pending.lut3d_id[ i ] = GetBackend()->CreateBackendBlob( g_ColorMgmtLuts[i].lut3d ); + + drm->pending.shaperlut_colorop_id[ i ] = GetBackend()->CreateBackendBlob( lut_convert_16bit_to_32bit( g_ColorMgmtLuts[i].lut1d ) ); + drm->pending.lut3d_colorop_id[ i ] = GetBackend()->CreateBackendBlob( lut_convert_16bit_to_32bit( g_ColorMgmtLuts[i].lut3d ) ); } return true; From 56c01fef411dfa01e5ce5973a45e30ab840514c0 Mon Sep 17 00:00:00 2001 From: Melissa Wen Date: Fri, 13 Mar 2026 22:32:10 -0300 Subject: [PATCH 14/21] drm: bypass COLOR PIPELINE prop when finishing drm Signed-off-by: Melissa Wen --- src/Backends/DRMBackend.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp index 834d3511e0..458945bc86 100644 --- a/src/Backends/DRMBackend.cpp +++ b/src/Backends/DRMBackend.cpp @@ -1696,6 +1696,10 @@ void finish_drm(struct drm_t *drm) if ( pPlane->GetProperties().AMD_PLANE_BLEND_LUT ) pPlane->GetProperties().AMD_PLANE_BLEND_LUT->SetPendingValue( req, 0, true ); + + if ( pPlane->GetProperties().COLOR_PIPELINE ) + pPlane->GetProperties().COLOR_PIPELINE->SetPendingValue( req, 0, true ); + } // We can't do a non-blocking commit here or else risk EBUSY in case the From 2f4afe81cceaa2491016a451fbf3701f321d1f41 Mon Sep 17 00:00:00 2001 From: Matthew Schwartz Date: Thu, 19 Mar 2026 15:05:52 -0700 Subject: [PATCH 15/21] drm: reset COLOR_PIPELINE to bypass for non-color-managed layers 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 --- src/Backends/DRMBackend.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp index 458945bc86..bbd2017125 100644 --- a/src/Backends/DRMBackend.cpp +++ b/src/Backends/DRMBackend.cpp @@ -3041,9 +3041,6 @@ drm_prepare_liftoff( struct drm_t *drm, const struct FrameInfo_t *frameInfo, boo auto entry = FrameInfoToLiftoffStateCacheEntry( drm, frameInfo ); for ( int i = 0; i < frameInfo->layerCount; i++ ) { - if ( !frameInfo->layers[i].applyColorMgmt ) - continue; - struct liftoff_plane *plane = liftoff_layer_get_plane( drm->lo_layers[ i ] ); uint32_t plane_id = plane ? liftoff_plane_get_id( plane ) : 0; @@ -3055,6 +3052,12 @@ drm_prepare_liftoff( struct drm_t *drm, const struct FrameInfo_t *frameInfo, boo if ( pPlane->GetObjectId() != plane_id ) continue; + if ( !frameInfo->layers[i].applyColorMgmt ) + { + pPlane->GetProperties().COLOR_PIPELINE->SetPendingValue( drm->req, 0, true ); + break; + } + bool bYCbCr = entry.layerState[i].ycbcr; std::optional p = get_plane_color_pipelines( drm, pPlane ); if ( !p ) { From 83e1cd708486c90d8ba97b6dafeeecc25581be06 Mon Sep 17 00:00:00 2001 From: Melissa Wen Date: Wed, 22 Apr 2026 18:18:50 -0300 Subject: [PATCH 16/21] drm: don't set COLOR_ENCODING and COLOR_RANGE if colorop is enabled Those properties are deprecated when using KMS colorop API Signed-off-by: Melissa Wen --- src/Backends/DRMBackend.cpp | 43 ++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp index bbd2017125..343a32be33 100644 --- a/src/Backends/DRMBackend.cpp +++ b/src/Backends/DRMBackend.cpp @@ -2899,23 +2899,28 @@ drm_prepare_liftoff( struct drm_t *drm, const struct FrameInfo_t *frameInfo, boo if ( frameInfo->layers[i].applyColorMgmt ) { bool bYCbCr = entry.layerState[i].ycbcr; - - if ( !cv_drm_debug_disable_color_encoding && bYCbCr ) - { - liftoff_layer_set_property( drm->lo_layers[ i ], "COLOR_ENCODING", entry.layerState[i].colorEncoding ); - } - else + /* If the new KMS colorop API is in use, + * COLOR_ENCODING and COLOR_RANGE are deprecated + */ + if (!drm_supports_color_pipeline( drm )) { - liftoff_layer_unset_property( drm->lo_layers[ i ], "COLOR_ENCODING" ); - } + if ( !cv_drm_debug_disable_color_encoding && bYCbCr ) + { + liftoff_layer_set_property( drm->lo_layers[ i ], "COLOR_ENCODING", entry.layerState[i].colorEncoding ); + } + else + { + liftoff_layer_unset_property( drm->lo_layers[ i ], "COLOR_ENCODING" ); + } - if ( !cv_drm_debug_disable_color_range && bYCbCr ) - { - liftoff_layer_set_property( drm->lo_layers[ i ], "COLOR_RANGE", entry.layerState[i].colorRange ); - } - else - { - liftoff_layer_unset_property( drm->lo_layers[ i ], "COLOR_RANGE" ); + if ( !cv_drm_debug_disable_color_range && bYCbCr ) + { + liftoff_layer_set_property( drm->lo_layers[ i ], "COLOR_RANGE", entry.layerState[i].colorRange ); + } + else + { + liftoff_layer_unset_property( drm->lo_layers[ i ], "COLOR_RANGE" ); + } } if ( drm_supports_color_mgmt( drm ) ) @@ -2989,8 +2994,12 @@ drm_prepare_liftoff( struct drm_t *drm, const struct FrameInfo_t *frameInfo, boo liftoff_layer_set_property( drm->lo_layers[ i ], "FB_ID", 0 ); liftoff_layer_set_property( drm->lo_layers[ i ], "IN_FENCE_FD", -1 ); - liftoff_layer_unset_property( drm->lo_layers[ i ], "COLOR_ENCODING" ); - liftoff_layer_unset_property( drm->lo_layers[ i ], "COLOR_RANGE" ); + if (!drm_supports_color_pipeline( drm )) + { + liftoff_layer_unset_property( drm->lo_layers[ i ], "COLOR_ENCODING" ); + liftoff_layer_unset_property( drm->lo_layers[ i ], "COLOR_RANGE" ); + } + liftoff_layer_unset_property( drm->lo_layers[ i ], "pixel blend mode" ); if ( drm_supports_color_mgmt( drm ) ) From 494fd9b35798c828d2440a636fe8bd73fff1670e Mon Sep 17 00:00:00 2001 From: Melissa Wen Date: Tue, 21 Apr 2026 09:21:23 -0300 Subject: [PATCH 17/21] [for-downstream-usage] drm: define colorop enums Signed-off-by: Melissa Wen --- src/drm_include.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/drm_include.h b/src/drm_include.h index c9c4a23ee7..4755b41bbd 100644 --- a/src/drm_include.h +++ b/src/drm_include.h @@ -87,6 +87,18 @@ enum drm_colorspace { #define DRM_MODE_CONTENT_TYPE_CINEMA 3 #define DRM_MODE_CONTENT_TYPE_GAME 4 +#define DRM_MODE_OBJECT_COLOROP 0xfafafafa + +#define DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE 7 + +enum drm_colorop_type { + DRM_COLOROP_1D_CURVE, + DRM_COLOROP_1D_LUT, + DRM_COLOROP_CTM_3X4, + DRM_COLOROP_MULTIPLIER, + DRM_COLOROP_3D_LUT, +}; + enum drm_colorop_curve_1d_type { DRM_COLOROP_1D_CURVE_SRGB_EOTF, DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF, From 0b17b7148c7501e5b7a2ca9c0e812f75e70edd9e Mon Sep 17 00:00:00 2001 From: Melissa Wen Date: Wed, 22 Apr 2026 18:17:54 -0300 Subject: [PATCH 18/21] drm: add support to DRM Fixed Matrix colorop Signed-off-by: Melissa Wen --- src/Backends/DRMBackend.cpp | 76 ++++++++++++++++++++++++++++--------- src/drm_include.h | 19 ++++++---- 2 files changed, 71 insertions(+), 24 deletions(-) diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp index 343a32be33..8277de674b 100644 --- a/src/Backends/DRMBackend.cpp +++ b/src/Backends/DRMBackend.cpp @@ -556,6 +556,7 @@ namespace gamescope std::optional CURVE_1D_TYPE; std::optional DATA; std::optional MULTIPLIER; + std::optional FIXED_MATRIX_TYPE; }; ColorOpProperties &GetProperties() { return m_Props; } const ColorOpProperties &GetProperties() const { return m_Props; } @@ -566,6 +567,7 @@ namespace gamescope struct CDRMColorPipeline { uint32_t id; + std::unique_ptr fixedMatrix; std::unique_ptr degamma; std::unique_ptr HDRMult; std::unique_ptr CTM; @@ -970,35 +972,38 @@ static std::optional get_color_pipeline( struct dr } // Check if the pipeline has what we need - if ( pipeline.size() != 8 ) + if ( pipeline.size() != 9 ) return {}; - if ( pipeline[0]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_1D_CURVE ) + if ( pipeline[0]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_FIXED_MATRIX ) return {}; - if ( pipeline[1]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_MULTIPLIER ) + if ( pipeline[1]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_1D_CURVE ) return {}; - if ( pipeline[2]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_CTM_3X4 ) + if ( pipeline[2]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_MULTIPLIER ) return {}; - if ( pipeline[3]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_1D_CURVE ) + if ( pipeline[3]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_CTM_3X4 ) return {}; - if ( pipeline[4]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_1D_LUT ) + if ( pipeline[4]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_1D_CURVE ) return {}; - if ( pipeline[5]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_3D_LUT ) + if ( pipeline[5]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_1D_LUT ) return {}; - if ( pipeline[6]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_1D_CURVE ) + if ( pipeline[6]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_3D_LUT ) return {}; - if ( pipeline[7]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_1D_LUT ) + if ( pipeline[7]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_1D_CURVE ) + return {}; + if ( pipeline[8]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_1D_LUT ) return {}; gamescope::CDRMColorPipeline p { .id = uHeadId, - .degamma = std::move(pipeline[0]), - .HDRMult = std::move(pipeline[1]), - .CTM = std::move(pipeline[2]), - .shaper = std::move(pipeline[3]), - .shaperLut = std::move(pipeline[4]), - .lut3D = std::move(pipeline[5]), - .blend = std::move(pipeline[6]), - .blendLut = std::move(pipeline[7]), + .fixedMatrix = std::move(pipeline[0]), + .degamma = std::move(pipeline[1]), + .HDRMult = std::move(pipeline[2]), + .CTM = std::move(pipeline[3]), + .shaper = std::move(pipeline[4]), + .shaperLut = std::move(pipeline[5]), + .lut3D = std::move(pipeline[6]), + .blend = std::move(pipeline[7]), + .blendLut = std::move(pipeline[8]), }; return p; } @@ -1893,6 +1898,27 @@ static drm_color_range drm_get_color_range(EStreamColorspace colorspace) } } +// Only used for NV12 buffers +static drm_colorop_fixed_matrix_type drm_get_colorop_fixed_matrix(EStreamColorspace colorspace) +{ + switch (colorspace) + { + default: + case k_EStreamColorspace_Unknown: + return DRM_COLOROP_FM_YCBCR709_FULL_RGB; + + case k_EStreamColorspace_BT601: + return DRM_COLOROP_FM_YCBCR601_LIMITED_RGB; + case k_EStreamColorspace_BT601_Full: + return DRM_COLOROP_FM_YCBCR601_FULL_RGB; + + case k_EStreamColorspace_BT709: + return DRM_COLOROP_FM_YCBCR709_LIMITED_RGB; + case k_EStreamColorspace_BT709_Full: + return DRM_COLOROP_FM_YCBCR709_FULL_RGB; + } +} + template void hash_combine(size_t& s, const T& v) { @@ -1918,6 +1944,7 @@ struct LiftoffStateCacheEntry uint16_t opacity; drm_color_encoding colorEncoding; drm_color_range colorRange; + drm_colorop_fixed_matrix_type coloropFM; GamescopeAppTextureColorspace colorspace; AlphaBlendingMode_t eAlphaBlendingMode; } layerState[ k_nMaxLayers ]; @@ -1947,6 +1974,7 @@ struct LiftoffStateCacheEntryKasher hash_combine(hash, k.layerState[i].opacity); hash_combine(hash, k.layerState[i].colorEncoding); hash_combine(hash, k.layerState[i].colorRange); + hash_combine(hash, k.layerState[i].coloropFM); hash_combine(hash, k.layerState[i].colorspace); hash_combine(hash, k.layerState[i].eAlphaBlendingMode); } @@ -2140,6 +2168,7 @@ LiftoffStateCacheEntry FrameInfoToLiftoffStateCacheEntry( struct drm_t *drm, con { entry.layerState[i].colorEncoding = drm_get_color_encoding( g_ForcedNV12ColorSpace ); entry.layerState[i].colorRange = drm_get_color_range( g_ForcedNV12ColorSpace ); + entry.layerState[i].coloropFM = drm_get_colorop_fixed_matrix( g_ForcedNV12ColorSpace ); entry.layerState[i].colorspace = GAMESCOPE_APP_TEXTURE_COLORSPACE_SRGB; } else @@ -2795,6 +2824,7 @@ namespace gamescope m_Props.DATA = CDRMAtomicProperty::Instantiate( "DATA", this, *rawProperties ); m_Props.CURVE_1D_TYPE = CDRMAtomicProperty::Instantiate( "CURVE_1D_TYPE", this, *rawProperties ); m_Props.MULTIPLIER = CDRMAtomicProperty::Instantiate( "MULTIPLIER", this, *rawProperties ); + m_Props.FIXED_MATRIX_TYPE = CDRMAtomicProperty::Instantiate( "FIXED_MATRIX_TYPE", this, *rawProperties ); } } @@ -3076,6 +3106,18 @@ drm_prepare_liftoff( struct drm_t *drm, const struct FrameInfo_t *frameInfo, boo pPlane->GetProperties().COLOR_PIPELINE->SetPendingValue( drm->req, p->id, true ); + if ( !cv_drm_debug_disable_color_encoding && + !cv_drm_debug_disable_color_range && + bYCbCr ) + { + p->fixedMatrix->GetProperties().BYPASS->SetPendingValue( drm->req, 0, true ); + p->fixedMatrix->GetProperties().FIXED_MATRIX_TYPE->SetPendingValue( drm->req, entry.layerState[i].coloropFM, true ); + } + else + { + p->fixedMatrix->GetProperties().BYPASS->SetPendingValue( drm->req, 1, true ); + } + GamescopeAppTextureColorspace colorspace = entry.layerState[i].colorspace; std::optional degamma_tf = colorspace_to_drm_plane_degamma_curve( colorspace ); std::optional shaper_tf = colorspace_to_drm_plane_shaper_curve( colorspace ); diff --git a/src/drm_include.h b/src/drm_include.h index 4755b41bbd..31f39ffd4a 100644 --- a/src/drm_include.h +++ b/src/drm_include.h @@ -91,13 +91,7 @@ enum drm_colorspace { #define DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE 7 -enum drm_colorop_type { - DRM_COLOROP_1D_CURVE, - DRM_COLOROP_1D_LUT, - DRM_COLOROP_CTM_3X4, - DRM_COLOROP_MULTIPLIER, - DRM_COLOROP_3D_LUT, -}; +#define DRM_COLOROP_FIXED_MATRIX 5 enum drm_colorop_curve_1d_type { DRM_COLOROP_1D_CURVE_SRGB_EOTF, @@ -114,3 +108,14 @@ enum drm_colorop_curve_1d_type { DRM_COLOROP_1D_CURVE_GAMMA26_INV, DRM_COLOROP_1D_CURVE_COUNT }; + +enum drm_colorop_fixed_matrix_type { + DRM_COLOROP_FM_YCBCR601_FULL_RGB, + DRM_COLOROP_FM_YCBCR601_LIMITED_RGB, + DRM_COLOROP_FM_YCBCR709_FULL_RGB, + DRM_COLOROP_FM_YCBCR709_LIMITED_RGB, + DRM_COLOROP_FM_YCBCR2020_FULL_RGB_NC, + DRM_COLOROP_FM_YCBCR2020_LIMITED_RGB_NC, + DRM_COLOROP_FM_RGB709_RGB2020, + DRM_COLOROP_FM_COUNT +}; From 17f6e52b3a6e8260784d4e98c9338c2dcbaae674 Mon Sep 17 00:00:00 2001 From: Melissa Wen Date: Wed, 15 Jul 2026 07:00:59 -0300 Subject: [PATCH 19/21] drm: fetch CRTC color pipelines 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 --- src/Backends/DRMBackend.cpp | 60 +++++++++++++++++++++++++++++++++++++ src/drm_include.h | 1 + 2 files changed, 61 insertions(+) diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp index 8277de674b..44e1f9908d 100644 --- a/src/Backends/DRMBackend.cpp +++ b/src/Backends/DRMBackend.cpp @@ -361,6 +361,7 @@ namespace gamescope std::optional VRR_ENABLED; std::optional OUT_FENCE_PTR; std::optional AMD_CRTC_REGAMMA_TF; + std::optional COLOR_PIPELINE; std::optional DUMMY_END; }; CRTCProperties &GetProperties() { return m_Props; } @@ -576,6 +577,8 @@ namespace gamescope std::unique_ptr lut3D; std::unique_ptr blend; std::unique_ptr blendLut; + std::unique_ptr regamma; + std::unique_ptr regammaLut; }; class CDRMFb final : public CBaseBackendFb @@ -1031,6 +1034,62 @@ static std::optional get_plane_color_pipelines( st return {}; } +static std::optional get_crtc_colorop_pipeline( struct drm_t *drm, uint32_t uHeadId ) +{ + std::vector> pipeline; + + uint32_t uColorOpId = uHeadId; + while ( uColorOpId != 0 ) + { + auto pColorOp = std::make_unique( uColorOpId ); + pColorOp->RefreshState(); + uColorOpId = pColorOp->GetProperties().NEXT.value().GetInitialValue(); + pipeline.emplace_back( std::move(pColorOp) ); + } + + // Check if the pipeline has what we need + if ( pipeline.size() != 3 ) + return {}; + if ( pipeline[0]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_CTM_3X4 ) + return {}; + if ( pipeline[1]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_1D_CURVE ) + return {}; + if ( pipeline[2]->GetProperties().TYPE.value().GetInitialValue() != DRM_COLOROP_1D_LUT ) + return {}; + + gamescope::CDRMColorPipeline p { + .id = uHeadId, + .CTM = std::move(pipeline[0]), + .regamma = std::move(pipeline[1]), + .regammaLut = std::move(pipeline[2]), + }; + return p; +} + + +static std::optional get_crtc_color_pipelines( struct drm_t *drm, gamescope::CDRMCRTC *pCRTC ) +{ + auto pColorPipelineProp = pCRTC->GetProperties().COLOR_PIPELINE; + if ( !pColorPipelineProp ) + return {}; + + drmModePropertyRes *pProperty = drmModeGetProperty( g_DRM.fd, pColorPipelineProp->GetPropertyId() ); + if ( !pProperty ) + return {}; + + defer( drmModeFreeProperty( pProperty ) ); + + for ( int i = 0; i < pProperty->count_enums; i++ ) + { + auto entry = pProperty->enums[ i ]; + std::optional p = get_crtc_colorop_pipeline( drm, entry.value ); + if ( p.has_value() ) + return p; + } + + return {}; +} + static bool get_resources(struct drm_t *drm) { { @@ -2351,6 +2410,7 @@ namespace gamescope m_Props.VRR_ENABLED = CDRMAtomicProperty::Instantiate( "VRR_ENABLED", this, *rawProperties ); m_Props.OUT_FENCE_PTR = CDRMAtomicProperty::Instantiate( "OUT_FENCE_PTR", this, *rawProperties ); m_Props.AMD_CRTC_REGAMMA_TF = CDRMAtomicProperty::Instantiate( "AMD_CRTC_REGAMMA_TF", this, *rawProperties ); + m_Props.COLOR_PIPELINE = CDRMAtomicProperty::Instantiate( "COLOR_PIPELINE", this, *rawProperties ); } } diff --git a/src/drm_include.h b/src/drm_include.h index 31f39ffd4a..58a1913c24 100644 --- a/src/drm_include.h +++ b/src/drm_include.h @@ -90,6 +90,7 @@ enum drm_colorspace { #define DRM_MODE_OBJECT_COLOROP 0xfafafafa #define DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE 7 +#define DRM_CLIENT_CAP_CRTC_COLOR_PIPELINE 8 #define DRM_COLOROP_FIXED_MATRIX 5 From 80ae0896c29602e7affc82901050fae582e58f3e Mon Sep 17 00:00:00 2001 From: Melissa Wen Date: Wed, 15 Jul 2026 07:12:19 -0300 Subject: [PATCH 20/21] drm: check if CRTC color pipeline API is supported Signed-off-by: Melissa Wen --- src/Backends/DRMBackend.cpp | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp index 44e1f9908d..dde73e4e87 100644 --- a/src/Backends/DRMBackend.cpp +++ b/src/Backends/DRMBackend.cpp @@ -629,6 +629,7 @@ struct drm_color_ctm2 { bool g_bSupportsAsyncFlips = false; bool g_bSupportsSyncObjs = false; bool g_bSupportsColorPipeline = false; +bool g_bSupportsCRTCColorPipeline = false; extern gamescope::GamescopeModeGeneration g_eGamescopeModeGeneration; extern GamescopePanelOrientation g_DesiredInternalOrientation; @@ -1136,6 +1137,16 @@ static bool get_resources(struct drm_t *drm) } } + if ( g_bSupportsCRTCColorPipeline ) + { + for ( std::unique_ptr< gamescope::CDRMCRTC > &pCRTC : drm->crtcs ) + { + if ( !get_crtc_color_pipelines( drm, pCRTC.get() ) ) + return false; + } + } + + return refresh_state( drm ); } @@ -1513,6 +1524,7 @@ bool init_drm(struct drm_t *drm, int width, int height, int refresh) } g_bSupportsColorPipeline = drmSetClientCap(drm->fd, DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE, 1) == 0; + g_bSupportsCRTCColorPipeline = drmSetClientCap(drm->fd, DRM_CLIENT_CAP_CRTC_COLOR_PIPELINE, 1) == 0; if (!get_resources(drm)) { return false; @@ -3874,7 +3886,10 @@ bool drm_supports_color_mgmt(struct drm_t *drm) if ( g_bSupportsColorPipeline ) return false; - if ( !drm->pPrimaryPlane ) + if ( g_bSupportsCRTCColorPipeline ) + return false; + + if ( !drm->pPrimaryPlane || !drm->pCRTC ) return false; return drm->pPrimaryPlane->GetProperties().AMD_PLANE_CTM.has_value() && drm->pPrimaryPlane->GetProperties().AMD_PLANE_BLEND_TF.has_value(); @@ -3888,10 +3903,13 @@ bool drm_supports_color_pipeline(struct drm_t *drm) if ( !g_bSupportsColorPipeline ) return false; + if ( !g_bSupportsCRTCColorPipeline ) + return false; + if ( !drm->pPrimaryPlane ) return false; - return drm->pPrimaryPlane->GetProperties().COLOR_PIPELINE.has_value() ; + return drm->pPrimaryPlane->GetProperties().COLOR_PIPELINE.has_value() && drm->pCRTC->GetProperties().COLOR_PIPELINE.has_value(); } std::span drm_get_valid_refresh_rates( struct drm_t *drm ) From 83f278ab765aaf3b89cabd0668632cdcc2172ac8 Mon Sep 17 00:00:00 2001 From: Melissa Wen Date: Wed, 15 Jul 2026 08:02:18 -0300 Subject: [PATCH 21/21] drm: set CRTC color pipeline 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 --- src/Backends/DRMBackend.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp index dde73e4e87..5332353cae 100644 --- a/src/Backends/DRMBackend.cpp +++ b/src/Backends/DRMBackend.cpp @@ -1721,6 +1721,9 @@ void finish_drm(struct drm_t *drm) if ( pCRTC->GetProperties().AMD_CRTC_REGAMMA_TF ) pCRTC->GetProperties().AMD_CRTC_REGAMMA_TF->SetPendingValue( req, 0, true ); + + if ( pCRTC->GetProperties().COLOR_PIPELINE ) + pCRTC->GetProperties().COLOR_PIPELINE->SetPendingValue( req, 0, true ); } for ( std::unique_ptr< gamescope::CDRMPlane > &pPlane : drm->planes ) @@ -3477,6 +3480,10 @@ int drm_prepare( struct drm_t *drm, bool async, const struct FrameInfo_t *frameI if ( pCRTC->GetProperties().AMD_CRTC_REGAMMA_TF ) pCRTC->GetProperties().AMD_CRTC_REGAMMA_TF->SetPendingValue( drm->req, 0, bForceInRequest ); + + if ( pCRTC->GetProperties().COLOR_PIPELINE ) + pCRTC->GetProperties().COLOR_PIPELINE->SetPendingValue( drm->req, 0, bForceInRequest ); + } if ( drm->pConnector && !bSleep ) @@ -3535,6 +3542,27 @@ int drm_prepare( struct drm_t *drm, bool async, const struct FrameInfo_t *frameI else drm->pCRTC->GetProperties().AMD_CRTC_REGAMMA_TF->SetPendingValue( drm->req, AMDGPU_TRANSFER_FUNCTION_DEFAULT, bForceInRequest ); } + + if ( drm_supports_color_pipeline( drm ) && drm->pCRTC->GetProperties().COLOR_PIPELINE ) + { + std::optional p = get_crtc_color_pipelines( drm, drm->pCRTC ); + if ( !p ) { + drm_log.debugf( "No color pipeline fits color mgmt needs for CRTC %u", drm->pCRTC->GetObjectId()); + } else { + drm->pCRTC->GetProperties().COLOR_PIPELINE->SetPendingValue( drm->req, p->id, bForceInRequest ); + drm_log.debugf( "Color pipeline %u for CRTC %u", p->id, drm->pCRTC->GetObjectId()); + std::optional regamma_tf = amd_tf_to_drm_curve( inverse_tf( drm->pending.output_tf ) ); + if ( !cv_drm_debug_disable_regamma_tf && regamma_tf.has_value() ) + { + p->regamma->GetProperties().BYPASS->SetPendingValue( drm->req, 0, bForceInRequest ); + p->regamma->GetProperties().CURVE_1D_TYPE->SetPendingValue( drm->req, *regamma_tf, bForceInRequest ); + } else { + p->regamma->GetProperties().BYPASS->SetPendingValue( drm->req, 1, bForceInRequest ); + } + p->CTM->GetProperties().BYPASS->SetPendingValue( drm->req, 1, bForceInRequest ); + p->regammaLut->GetProperties().BYPASS->SetPendingValue( drm->req, 1, bForceInRequest ); + } + } } drm->flags = flags;