From 261dc46d54aa1c7f5a7363807c39ae1f26b3ab49 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Mon, 15 Jul 2024 14:51:16 +0100 Subject: [PATCH 01/19] Utils: Add small Algorithm helper file --- src/Utils/Algorithm.h | 173 ++++++++++++++++++++++++++++++++++++++++++ src/color_bench.cpp | 137 +++++++++++++++++++++++++++++++++ 2 files changed, 310 insertions(+) create mode 100644 src/Utils/Algorithm.h diff --git a/src/Utils/Algorithm.h b/src/Utils/Algorithm.h new file mode 100644 index 0000000..7290367 --- /dev/null +++ b/src/Utils/Algorithm.h @@ -0,0 +1,173 @@ +#pragma once + +#include +#include + +namespace gamescope::Algorithm +{ + template + constexpr TObj *Begin( std::span span ) + { + return span.data(); + } + + template + constexpr TObj *End( std::span span ) + { + return Begin( span ) + span.size(); + } + + template + constexpr TIter FindSimple( TIter pFirst, TIter pEnd, const TObj &obj ) + { + while ( pFirst != pEnd && *pFirst != obj ) + ++pFirst; + + return pFirst; + } + + template + constexpr TIter FindByFour( TIter pFirst, TIter pEnd, const TObj &obj ) + { + typename std::iterator_traits< TIter >::difference_type ulTripCount = ( pEnd - pFirst ) >> 2; + + while ( ulTripCount-- > 0 ) + { + if ( pFirst[0] == obj ) + return &pFirst[0]; + + if ( pFirst[1] == obj ) + return &pFirst[1]; + + if ( pFirst[2] == obj ) + return &pFirst[2]; + + if ( pFirst[3] == obj ) + return &pFirst[3]; + + pFirst += 4; + } + + switch ( pEnd - pFirst ) + { + case 3: + { + if ( pFirst[0] == obj ) + return &pFirst[0]; + + if ( pFirst[1] == obj ) + return &pFirst[1]; + + if ( pFirst[2] == obj ) + return &pFirst[2]; + + return pEnd; + } + case 2: + { + if ( pFirst[0] == obj ) + return &pFirst[0]; + + if ( pFirst[1] == obj ) + return &pFirst[1]; + + return pEnd; + } + case 1: + { + if ( pFirst[0] == obj ) + return &pFirst[0]; + + return pEnd; + } + case 0: + { + return pEnd; + } + default: + { + __builtin_unreachable(); + } + } + } + + template + constexpr TIter Find( TIter pFirst, TIter pEnd, const TObj &obj ) + { + return FindSimple( pFirst, pEnd, obj ); + } + + template + constexpr TIter Find( std::span span, const TObj &obj ) + { + return Find( Begin( span ), End( span ), obj ); + } + + template + constexpr bool ContainsShortcut( TIter pFirst, TIter pEnd, const TObj &obj ) + { + return Find( pFirst, pEnd, obj ) != pEnd; + } + + template + constexpr bool ContainsNoShortcut( TIter pFirst, TIter pEnd, const TObj &obj ) + { + bool bFound = false; + + typename std::iterator_traits< TIter >::difference_type ulTripCount = ( pEnd - pFirst ) >> 2; + + while ( ulTripCount-- > 0 ) + { + bFound |= pFirst[0] == obj || + pFirst[1] == obj || + pFirst[2] == obj || + pFirst[3] == obj; + + pFirst += 4; + } + + switch ( pEnd - pFirst ) + { + case 3: + { + bFound |= pFirst[0] == obj || + pFirst[1] == obj || + pFirst[2] == obj; + break; + } + case 2: + { + bFound |= pFirst[0] == obj || + pFirst[1] == obj; + break; + } + case 1: + { + bFound |= pFirst[0] == obj; + break; + } + case 0: + { + break; + } + default: + { + __builtin_unreachable(); + } + } + + return bFound; + } + + template + constexpr bool Contains( TIter pFirst, TIter pEnd, const TObj &obj ) + { + return ContainsNoShortcut( pFirst, pEnd, obj ); + } + + template + constexpr bool Contains( std::span span, const TObj &obj ) + { + return Contains( Begin( span ), End( span ), obj ); + } +} diff --git a/src/color_bench.cpp b/src/color_bench.cpp index 33dff78..9c9d986 100644 --- a/src/color_bench.cpp +++ b/src/color_bench.cpp @@ -1,5 +1,9 @@ +#include #include +#include +#include "Utils/Algorithm.h" + #include "color_helpers.h" const uint32_t nLutSize1d = 4096; @@ -74,4 +78,137 @@ static void BenchmarkCalcColorTransforms(benchmark::State &state) } BENCHMARK(BenchmarkCalcColorTransforms); +static constexpr uint32_t k_uFindTestValueCountLarge = 524288; +static constexpr uint32_t k_uFindTestValueCountMedium = 16; +static constexpr uint32_t k_uFindTestValueCountSmall = 5; + +template +static __attribute__((noinline)) std::array GetFindTestValues() +{ + static std::array s_Values = []() + { + std::array values; + for ( uint32_t i = 0; i < uSize; i++ ) + values[i] = rand() % 255; + + return values; + }(); + + return s_Values; +} + +// Large + +static void Benchmark_Find_Large_Gamescope(benchmark::State &state) +{ + std::array values = GetFindTestValues(); + + for (auto _ : state) + { + auto iter = gamescope::Algorithm::Find( values.begin(), values.end(), 765678478 ); + benchmark::DoNotOptimize( iter ); + } +} +BENCHMARK(Benchmark_Find_Large_Gamescope); + +static void Benchmark_Find_Large_Std(benchmark::State &state) +{ + std::array values = GetFindTestValues(); + + for (auto _ : state) + { + auto iter = std::find( values.begin(), values.end(), 765678478 ); + benchmark::DoNotOptimize( iter ); + } +} +BENCHMARK(Benchmark_Find_Large_Std); + +static void Benchmark_Contains_Large_Gamescope(benchmark::State &state) +{ + std::array values = GetFindTestValues(); + + for (auto _ : state) + { + bool bContains = gamescope::Algorithm::ContainsNoShortcut( values.begin(), values.end(), 765678478 ); + benchmark::DoNotOptimize( bContains ); + } +} +BENCHMARK(Benchmark_Contains_Large_Gamescope); + +// + +static void Benchmark_Find_Medium_Gamescope(benchmark::State &state) +{ + std::array values = GetFindTestValues(); + + for (auto _ : state) + { + auto iter = gamescope::Algorithm::Find( values.begin(), values.end(), 765678478 ); + benchmark::DoNotOptimize( iter ); + } +} +BENCHMARK(Benchmark_Find_Medium_Gamescope); + +static void Benchmark_Find_Medium_Std(benchmark::State &state) +{ + std::array values = GetFindTestValues(); + + for (auto _ : state) + { + auto iter = std::find( values.begin(), values.end(), 765678478 ); + benchmark::DoNotOptimize( iter ); + } +} +BENCHMARK(Benchmark_Find_Medium_Std); + +static void Benchmark_Contains_Medium_Gamescope(benchmark::State &state) +{ + std::array values = GetFindTestValues(); + + for (auto _ : state) + { + bool bContains = gamescope::Algorithm::ContainsNoShortcut( values.begin(), values.end(), 765678478 ); + benchmark::DoNotOptimize( bContains ); + } +} +BENCHMARK(Benchmark_Contains_Medium_Gamescope); + +// + +static void Benchmark_Find_Small_Gamescope(benchmark::State &state) +{ + std::array values = GetFindTestValues(); + + for (auto _ : state) + { + auto iter = gamescope::Algorithm::Find( values.begin(), values.end(), 765678478 ); + benchmark::DoNotOptimize( iter ); + } +} +BENCHMARK(Benchmark_Find_Small_Gamescope); + +static void Benchmark_Find_Small_Std(benchmark::State &state) +{ + std::array values = GetFindTestValues(); + + for (auto _ : state) + { + auto iter = std::find( values.begin(), values.end(), 765678478 ); + benchmark::DoNotOptimize( iter ); + } +} +BENCHMARK(Benchmark_Find_Small_Std); + +static void Benchmark_Contains_Small_Gamescope(benchmark::State &state) +{ + std::array values = GetFindTestValues(); + + for (auto _ : state) + { + bool bContains = gamescope::Algorithm::ContainsNoShortcut( values.begin(), values.end(), 765678478 ); + benchmark::DoNotOptimize( bContains ); + } +} +BENCHMARK(Benchmark_Contains_Small_Gamescope); + BENCHMARK_MAIN(); -- 2.45.2 From 74a020865906414f9d4374fa5568fffce71e4027 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Mon, 15 Jul 2024 14:52:02 +0100 Subject: [PATCH 02/19] Utils: Use Contains in CloseAllFds --- src/Utils/Process.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Utils/Process.cpp b/src/Utils/Process.cpp index 5688fb1..32c52f1 100644 --- a/src/Utils/Process.cpp +++ b/src/Utils/Process.cpp @@ -1,4 +1,5 @@ #include "Process.h" +#include "../Utils/Algorithm.h" #include "../convar.h" #include "../log.hpp" #include "../Utils/Defer.h" @@ -268,7 +269,7 @@ namespace gamescope::Process int nFd = *onFd; - bool bExcluded = std::find( nExcludedFds.begin(), nExcludedFds.end(), nFd ) != nExcludedFds.end(); + bool bExcluded = Algorithm::Contains( nExcludedFds, nFd ); if ( bExcluded ) continue; -- 2.45.2 From 3c5a232269ef8adef56423bf9b2bb48cb5c0cd1d Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Mon, 15 Jul 2024 15:52:33 +0100 Subject: [PATCH 03/19] steamcompmgr: Only forward relative mouse mode if we have a cursor constraint --- src/steamcompmgr.cpp | 13 ++++++------- src/wlserver.cpp | 32 +++++++++++++++++++------------- src/wlserver.hpp | 26 +++++++++++++++++++++++++- 3 files changed, 50 insertions(+), 21 deletions(-) diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index 9ee265d..f051463 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -682,7 +682,6 @@ constexpr const T& clamp( const T& x, const T& min, const T& max ) } extern bool g_bForceRelativeMouse; -bool bSteamCompMgrGrab = false; CommitDoneList_t g_steamcompmgr_xdg_done_commits; @@ -7116,8 +7115,6 @@ steamcompmgr_main(int argc, char **argv) // Reset getopt() state optind = 1; - bSteamCompMgrGrab = GetBackend()->GetNestedHints() && g_bForceRelativeMouse; - int o; int opt_index = -1; bool bForceWindowsFullscreen = false; @@ -7574,13 +7571,15 @@ steamcompmgr_main(int argc, char **argv) if ( GetBackend()->GetNestedHints() && !g_bForceRelativeMouse ) { - bool bImageEmpty = + const bool bImageEmpty = ( global_focus.cursor && global_focus.cursor->imageEmpty() ) && ( !window_is_steam( global_focus.inputFocusWindow ) ); - if ( GetBackend()->GetNestedHints() ) - GetBackend()->GetNestedHints()->SetRelativeMouseMode( bImageEmpty ); - bSteamCompMgrGrab = GetBackend()->GetNestedHints() && bImageEmpty; + const bool bHasPointerConstraint = wlserver.HasMouseConstraint(); // atomic, no lock needed + + const bool bRelativeMouseMode = bImageEmpty && bHasPointerConstraint; + + GetBackend()->GetNestedHints()->SetRelativeMouseMode( bRelativeMouseMode ); } static int nIgnoredOverlayRepaints = 0; diff --git a/src/wlserver.cpp b/src/wlserver.cpp index ee6891d..1852be9 100644 --- a/src/wlserver.cpp +++ b/src/wlserver.cpp @@ -225,7 +225,8 @@ std::optional PrepareCommit( struct wlr_surface *surf, struct wl struct wlr_surface *pConstraintSurface = wlserver_surface_to_main_surface( surf ); - if ( wlserver.mouse_constraint && wlserver.mouse_constraint->surface == pConstraintSurface ) + struct wlr_pointer_constraint_v1 *pConstraint = wlserver.GetCursorConstraint(); + if ( pConstraint && pConstraint->surface == pConstraintSurface ) wlserver_update_cursor_constraint(); return oNewEntry; @@ -2235,7 +2236,7 @@ struct GamescopePointerConstraint static void wlserver_warp_to_constraint_hint() { - struct wlr_pointer_constraint_v1 *pConstraint = wlserver.mouse_constraint; + struct wlr_pointer_constraint_v1 *pConstraint = wlserver.GetCursorConstraint(); if (pConstraint->current.cursor_hint.enabled) { @@ -2248,7 +2249,7 @@ static void wlserver_warp_to_constraint_hint() static void wlserver_update_cursor_constraint() { - struct wlr_pointer_constraint_v1 *pConstraint = wlserver.mouse_constraint; + struct wlr_pointer_constraint_v1 *pConstraint = wlserver.GetCursorConstraint(); pixman_region32_t *pRegion = &pConstraint->region; if ( wlserver.mouse_constraint_requires_warp && pConstraint->surface ) @@ -2278,27 +2279,29 @@ static void wlserver_update_cursor_constraint() static void wlserver_constrain_cursor( struct wlr_pointer_constraint_v1 *pNewConstraint ) { - if ( wlserver.mouse_constraint == pNewConstraint ) + struct wlr_pointer_constraint_v1 *pOldConstraint = wlserver.GetCursorConstraint(); + + if ( pOldConstraint == pNewConstraint ) return; - if ( wlserver.mouse_constraint ) + if ( pOldConstraint ) { if ( !pNewConstraint ) wlserver_warp_to_constraint_hint(); - wlr_pointer_constraint_v1_send_deactivated(wlserver.mouse_constraint); + wlr_pointer_constraint_v1_send_deactivated( pOldConstraint ); } - wlserver.mouse_constraint = pNewConstraint; + wlserver.SetMouseConstraint( pNewConstraint ); - if ( !wlserver.mouse_constraint ) + if ( !pNewConstraint ) return; wlserver.mouse_constraint_requires_warp = true; wlserver_update_cursor_constraint(); - wlr_pointer_constraint_v1_send_activated( wlserver.mouse_constraint ); + wlr_pointer_constraint_v1_send_activated( pNewConstraint ); } static void handle_pointer_constraint_set_region(struct wl_listener *listener, void *data) @@ -2316,11 +2319,12 @@ void handle_constraint_destroy(struct wl_listener *listener, void *data) wl_list_remove(&pGamescopeConstraint->set_region.link); wl_list_remove(&pGamescopeConstraint->destroy.link); - if (wlserver.mouse_constraint == pGamescopeConstraint->pConstraint) + struct wlr_pointer_constraint_v1 *pCurrentConstraint = wlserver.GetCursorConstraint(); + if ( pCurrentConstraint == pGamescopeConstraint->pConstraint ) { wlserver_warp_to_constraint_hint(); - wlserver.mouse_constraint = nullptr; + wlserver.SetMouseConstraint( nullptr ); } delete pGamescopeConstraint; @@ -2345,9 +2349,11 @@ static void handle_pointer_constraint(struct wl_listener *listener, void *data) static bool wlserver_apply_constraint( double *dx, double *dy ) { - if ( wlserver.mouse_constraint ) + struct wlr_pointer_constraint_v1 *pConstraint = wlserver.GetCursorConstraint(); + + if ( pConstraint ) { - if ( wlserver.mouse_constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED ) + if ( pConstraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED ) return false; double sx = wlserver.mouse_surface_cursorx; diff --git a/src/wlserver.hpp b/src/wlserver.hpp index db7d491..467c54d 100644 --- a/src/wlserver.hpp +++ b/src/wlserver.hpp @@ -70,6 +70,8 @@ struct ResListEntry_t { struct wlserver_content_override; +bool wlserver_is_lock_held(void); + class gamescope_xwayland_server_t { public: @@ -149,7 +151,29 @@ struct wlserver_t { double mouse_surface_cursory = 0.0f; bool mouse_constraint_requires_warp = false; pixman_region32_t confine; - struct wlr_pointer_constraint_v1 *mouse_constraint = nullptr; + std::atomic mouse_constraint = { nullptr }; + + void SetMouseConstraint( struct wlr_pointer_constraint_v1 *pConstraint ) + { + assert( wlserver_is_lock_held() ); + // Set by wlserver only. Read by both wlserver + steamcompmgr with no + // need to actually be sequentially consistent. + mouse_constraint.store( pConstraint, std::memory_order_relaxed ); + } + + struct wlr_pointer_constraint_v1 *GetCursorConstraint() const + { + assert( wlserver_is_lock_held() ); + return mouse_constraint.load( std::memory_order_relaxed ); + } + + bool HasMouseConstraint() const + { + // Does not need to be sequentially consistent. + // Used by the steamcompmgr thread to check if there is currently a mouse constraint. + return mouse_constraint.load( std::memory_order_relaxed ) != nullptr; + } + uint64_t ulLastMovedCursorTime = 0; bool bCursorHidden = true; bool bCursorHasImage = true; -- 2.45.2 From 745d0d6a09e53a8481bc94c67e3a3ae5c86c8796 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Mon, 15 Jul 2024 20:21:16 +0100 Subject: [PATCH 04/19] convar: Add some helpers for std::string convars and callbacks --- src/convar.h | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/convar.h b/src/convar.h index c08dccb..f2a0485 100644 --- a/src/convar.h +++ b/src/convar.h @@ -20,6 +20,24 @@ namespace gamescope { class ConCommand; + template + inline std::string ToString( const T &thing ) + { + return std::to_string( thing ); + } + + template <> + inline std::string ToString( const std::string &sThing ) + { + return sThing; + } + + template <> + inline std::string ToString( const std::string_view &svThing ) + { + return std::string( svThing ); + } + template inline std::optional Parse( std::string_view chars ) { @@ -113,11 +131,15 @@ namespace gamescope { using ConVarCallbackFunc = std::function; public: - ConVar( std::string_view pszName, T defaultValue = T{}, std::string_view pszDescription = "", ConVarCallbackFunc func = nullptr ) + ConVar( std::string_view pszName, T defaultValue = T{}, std::string_view pszDescription = "", ConVarCallbackFunc func = nullptr, bool bRunCallbackAtStartup = false ) : ConCommand( pszName, pszDescription, [this]( std::span pArgs ){ this->InvokeFunc( pArgs ); } ) , m_Value{ defaultValue } , m_Callback{ func } { + if ( bRunCallbackAtStartup ) + { + RunCallback(); + } } const T& Get() const @@ -130,6 +152,11 @@ namespace gamescope { m_Value = T{ newValue }; + RunCallback(); + } + + void RunCallback() + { if ( !m_bInCallback && m_Callback ) { m_bInCallback = true; @@ -143,6 +170,9 @@ namespace gamescope operator T() const { return m_Value; } + // SFINAE for std::string... + operator std::string_view() const { return m_Value; } + template bool operator == ( const J &other ) const { return m_Value == other; } template bool operator != ( const J &other ) const { return m_Value != other; } template auto operator <=>( const J &other ) const { return m_Value <=> other; } @@ -158,7 +188,7 @@ namespace gamescope { // We should move to std format for logging and stuff. // This is kinda gross and grody! - std::string sValue = std::to_string( m_Value ); + std::string sValue = ToString( m_Value ); console_log.infof( "%.*s: %.*s\n%.*s", (int)m_pszName.length(), m_pszName.data(), (int)sValue.length(), sValue.data(), -- 2.45.2 From 69f94d99082f4b0c5e06c384d65705739608ca2f Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Mon, 15 Jul 2024 20:21:30 +0100 Subject: [PATCH 05/19] Utils: Add helpers for std::vector in our Algorithm helpers --- src/Utils/Algorithm.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/Utils/Algorithm.h b/src/Utils/Algorithm.h index 7290367..eb51a79 100644 --- a/src/Utils/Algorithm.h +++ b/src/Utils/Algorithm.h @@ -2,6 +2,7 @@ #include #include +#include namespace gamescope::Algorithm { @@ -17,6 +18,18 @@ namespace gamescope::Algorithm return Begin( span ) + span.size(); } + template + constexpr const TObj *Begin( const std::vector &vec ) + { + return vec.data(); + } + + template + constexpr const TObj *End( const std::vector &vec ) + { + return Begin( vec ) + vec.size(); + } + template constexpr TIter FindSimple( TIter pFirst, TIter pEnd, const TObj &obj ) { @@ -103,6 +116,12 @@ namespace gamescope::Algorithm return Find( Begin( span ), End( span ), obj ); } + template + constexpr TIter Find( const std::vector &vec, const TObj &obj ) + { + return Find( Begin( vec ), End( vec ), obj ); + } + template constexpr bool ContainsShortcut( TIter pFirst, TIter pEnd, const TObj &obj ) { @@ -170,4 +189,10 @@ namespace gamescope::Algorithm { return Contains( Begin( span ), End( span ), obj ); } + + template + constexpr bool Contains( const std::vector &vec, const TObj &obj ) + { + return Contains( Begin( vec ), End( vec ), obj ); + } } -- 2.45.2 From dade66318d852387bf8f1e91427dc7e2b5511826 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Mon, 15 Jul 2024 20:28:05 +0100 Subject: [PATCH 06/19] steamcompmgr: Add filter appids for relative mouse mode --- src/steamcompmgr.cpp | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index f051463..b955a0c 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -92,6 +92,7 @@ #include "commit.h" #include "BufferMemo.h" #include "Utils/Process.h" +#include "Utils/Algorithm.h" #if HAVE_AVIF #include "avif/avif.h" @@ -7022,6 +7023,24 @@ static gamescope::ConCommand cc_launch( "launch", "Launch an application with th gamescope::ConVar cv_shutdown_on_primary_child_death( "shutdown_on_primary_child_death", true, "Should gamescope shutdown when the primary application launched in it was shut down?" ); static LogScope s_LaunchLogScope( "launch" ); +static std::vector s_uRelativeMouseFilteredAppids; +static gamescope::ConVar cv_mouse_relative_filter_appids( "mouse_relative_filter_appids", +"8400" /* Geometry Wars: Retro Evolved */, +"Comma separated appids to filter out using relative mouse mode for.", +[]() +{ + std::vector sFilterAppids = gamescope::Split( cv_mouse_relative_filter_appids, "," ); + std::vector uFilterAppids; + uFilterAppids.reserve( sFilterAppids.size() ); + for ( auto &sFilterAppid : sFilterAppids ) + { + std::optional ouFilterAppid = gamescope::Parse( sFilterAppid ); + uFilterAppids.push_back( *ouFilterAppid ); + } + + s_uRelativeMouseFilteredAppids = std::move( uFilterAppids ); +}, true); + void LaunchNestedChildren( char **ppPrimaryChildArgv ) { std::string sNewPreload; @@ -7577,7 +7596,13 @@ steamcompmgr_main(int argc, char **argv) const bool bHasPointerConstraint = wlserver.HasMouseConstraint(); // atomic, no lock needed - const bool bRelativeMouseMode = bImageEmpty && bHasPointerConstraint; + uint32_t uAppId = global_focus.inputFocusWindow + ? global_focus.inputFocusWindow->appID + : 0; + + const bool bExcludedAppId = uAppId && gamescope::Algorithm::Contains( s_uRelativeMouseFilteredAppids, uAppId ); + + const bool bRelativeMouseMode = bImageEmpty && bHasPointerConstraint && !bExcludedAppId; GetBackend()->GetNestedHints()->SetRelativeMouseMode( bRelativeMouseMode ); } -- 2.45.2 From 6c187b7f69d5f7e7d1a01728c1c22e055a1683f6 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Thu, 18 Jul 2024 00:53:44 +0100 Subject: [PATCH 07/19] steamcompmgr: Add adaptive sync convar --- src/Backends/OpenVRBackend.cpp | 1 - src/Backends/WaylandBackend.cpp | 4 ++-- src/main.cpp | 4 ++-- src/steamcompmgr.cpp | 8 ++++---- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/Backends/OpenVRBackend.cpp b/src/Backends/OpenVRBackend.cpp index acc84ed..79b05d1 100644 --- a/src/Backends/OpenVRBackend.cpp +++ b/src/Backends/OpenVRBackend.cpp @@ -38,7 +38,6 @@ extern int g_nPreferredOutputWidth; extern int g_nPreferredOutputHeight; extern bool g_bForceHDR10OutputDebug; extern bool g_bBorderlessOutputWindow; -extern bool g_bAllowVRR; extern gamescope::ConVar cv_composite_force; extern bool g_bColorSliderInUse; diff --git a/src/Backends/WaylandBackend.cpp b/src/Backends/WaylandBackend.cpp index 6f578a2..3603be7 100644 --- a/src/Backends/WaylandBackend.cpp +++ b/src/Backends/WaylandBackend.cpp @@ -40,7 +40,7 @@ extern int g_nPreferredOutputWidth; extern int g_nPreferredOutputHeight; extern bool g_bForceHDR10OutputDebug; extern bool g_bBorderlessOutputWindow; -extern bool g_bAllowVRR; +extern gamescope::ConVar cv_adaptive_sync; extern gamescope::ConVar cv_composite_force; extern bool g_bColorSliderInUse; @@ -1531,7 +1531,7 @@ namespace gamescope } bool CWaylandBackend::IsVRRActive() const { - return g_bAllowVRR && m_bHostCompositorIsCurrentlyVRR; + return cv_adaptive_sync && m_bHostCompositorIsCurrentlyVRR; } bool CWaylandBackend::SupportsPlaneHardwareCursor() const diff --git a/src/main.cpp b/src/main.cpp index cd4aeca..da1b516 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -42,7 +42,7 @@ using namespace std::literals; EStreamColorspace g_ForcedNV12ColorSpace = k_EStreamColorspace_Unknown; -extern bool g_bAllowVRR; +extern gamescope::ConVar cv_adaptive_sync; const char *gamescope_optstring = nullptr; const char *g_pOriginalDisplay = nullptr; @@ -765,7 +765,7 @@ int main(int argc, char **argv) } else if (strcmp(opt_name, "display-index") == 0) { g_nNestedDisplayIndex = atoi( optarg ); } else if (strcmp(opt_name, "adaptive-sync") == 0) { - g_bAllowVRR = true; + cv_adaptive_sync = true; } else if (strcmp(opt_name, "expose-wayland") == 0) { g_bExposeWayland = true; } else if (strcmp(opt_name, "backend") == 0) { diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index b955a0c..b8102eb 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -187,7 +187,7 @@ timespec nanos_to_timespec( uint64_t ulNanos ) static void update_runtime_info(); -bool g_bAllowVRR = false; +gamescope::ConVar cv_adaptive_sync( "adaptive_sync", false, "Whether or not adaptive sync is enabled if available." ); uint64_t g_SteamCompMgrLimitedAppRefreshCycle = 16'666'666; uint64_t g_SteamCompMgrAppRefreshCycle = 16'666'666; @@ -2229,7 +2229,7 @@ paint_all(bool async) struct FrameInfo_t frameInfo = {}; frameInfo.applyOutputColorMgmt = g_ColorMgmt.pending.enabled; frameInfo.outputEncodingEOTF = g_ColorMgmt.pending.outputEncodingEOTF; - frameInfo.allowVRR = g_bAllowVRR; + frameInfo.allowVRR = cv_adaptive_sync; frameInfo.bFadingOut = fadingOut; // If the window we'd paint as the base layer is the streaming client, @@ -5410,7 +5410,7 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev) if ( ev->atom == ctx->atoms.gamescopeVRREnabled ) { bool enabled = !!get_prop( ctx, ctx->root, ctx->atoms.gamescopeVRREnabled, 0 ); - g_bAllowVRR = enabled; + cv_adaptive_sync = enabled; } if ( ev->atom == ctx->atoms.gamescopeDisplayForceInternal ) { @@ -6886,7 +6886,7 @@ void update_vrr_atoms(xwayland_ctx_t *root_ctx, bool force, bool* needs_flush = // Keep this as a preference, starting with off. if ( force ) { - bool wants_vrr = g_bAllowVRR; + bool wants_vrr = cv_adaptive_sync; uint32_t enabled_value = wants_vrr ? 1 : 0; XChangeProperty(root_ctx->dpy, root_ctx->root, root_ctx->atoms.gamescopeVRREnabled, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&enabled_value, 1 ); -- 2.45.2 From 1ebfacbb7477437ef295eb821a972ff3cfe992df Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Thu, 18 Jul 2024 01:14:19 +0100 Subject: [PATCH 08/19] WaylandBackend: Fix picking output refresh for VRR displays. --- src/Backends/WaylandBackend.cpp | 69 ++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/src/Backends/WaylandBackend.cpp b/src/Backends/WaylandBackend.cpp index 3603be7..b7f275e 100644 --- a/src/Backends/WaylandBackend.cpp +++ b/src/Backends/WaylandBackend.cpp @@ -5,6 +5,7 @@ #include "steamcompmgr.hpp" #include "edid.h" #include "Utils/Defer.h" +#include "Utils/Algorithm.h" #include "convar.h" #include "refresh_rate.h" #include "waitable.h" @@ -186,8 +187,14 @@ namespace gamescope std::optional GetCurrentState() { std::unique_lock lock( m_PlaneStateLock ); return m_oCurrentPlaneState; } + void UpdateVRRRefreshRate(); + private: + void Wayland_Surface_Enter( wl_surface *pSurface, wl_output *pOutput ); + void Wayland_Surface_Leave( wl_surface *pSurface, wl_output *pOutput ); + static const wl_surface_listener s_SurfaceListener; + void LibDecor_Frame_Configure( libdecor_frame *pFrame, libdecor_configuration *pConfiguration ); void LibDecor_Frame_Close( libdecor_frame *pFrame ); void LibDecor_Frame_Commit( libdecor_frame *pFrame ); @@ -228,12 +235,20 @@ namespace gamescope frog_color_managed_surface *m_pFrogColorManagedSurface = nullptr; wp_fractional_scale_v1 *m_pFractionalScale = nullptr; libdecor_window_state m_eWindowState = LIBDECOR_WINDOW_STATE_NONE; + std::vector m_pOutputs; bool m_bNeedsDecorCommit = false; uint32_t m_uFractionalScale = 120; std::mutex m_PlaneStateLock; std::optional m_oCurrentPlaneState; }; + const wl_surface_listener CWaylandPlane::s_SurfaceListener = + { + .enter = WAYLAND_USERDATA_TO_THIS( CWaylandPlane, Wayland_Surface_Enter ), + .leave = WAYLAND_USERDATA_TO_THIS( CWaylandPlane, Wayland_Surface_Leave ), + .preferred_buffer_scale = WAYLAND_NULL(), + .preferred_buffer_transform = WAYLAND_NULL(), + }; // Can't be const, libdecor api bad... libdecor_frame_interface CWaylandPlane::s_LibDecorFrameInterface = { @@ -540,9 +555,19 @@ namespace gamescope bool SupportsFormat( uint32_t uDRMFormat ) const; + bool HostCompositorIsCurrentlyVRR() const { return m_bHostCompositorIsCurrentlyVRR; } void SetHostCompositorIsCurrentlyVRR( bool bActive ) { m_bHostCompositorIsCurrentlyVRR = bActive; } - bool CurrentDisplaySupportsVRR() const { return m_bHostCompositorIsCurrentlyVRR; } + WaylandOutputInfo *GetOutputInfo( wl_output *pOutput ) + { + auto iter = m_pOutputs.find( pOutput ); + if ( iter == m_pOutputs.end() ) + return nullptr; + + return &iter->second; + } + + bool CurrentDisplaySupportsVRR() const { return HostCompositorIsCurrentlyVRR(); } wl_region *GetFullRegion() const { return m_pFullRegion; } private: @@ -819,6 +844,7 @@ namespace gamescope m_pParent = pParent; m_pSurface = wl_compositor_create_surface( m_pBackend->GetCompositor() ); wl_surface_set_user_data( m_pSurface, this ); + wl_surface_add_listener( m_pSurface, &s_SurfaceListener, this ); m_pViewport = wp_viewporter_get_viewport( m_pBackend->GetViewporter(), m_pSurface ); @@ -1005,6 +1031,45 @@ namespace gamescope } } + void CWaylandPlane::UpdateVRRRefreshRate() + { + if ( m_pParent ) + return; + + if ( !m_pBackend->HostCompositorIsCurrentlyVRR() ) + return; + + if ( m_pOutputs.empty() ) + return; + + int32_t nLargestRefreshRateMhz = 0; + for ( wl_output *pOutput : m_pOutputs ) + { + WaylandOutputInfo *pOutputInfo = m_pBackend->GetOutputInfo( pOutput ); + if ( !pOutputInfo ) + continue; + + nLargestRefreshRateMhz = std::max( nLargestRefreshRateMhz, pOutputInfo->nRefresh ); + } + + if ( nLargestRefreshRateMhz && nLargestRefreshRateMhz != g_nOutputRefresh ) + { + xdg_log.infof( "Changed refresh to: %.3fhz", ConvertmHzToHz( (float) nLargestRefreshRateMhz ) ); + g_nOutputRefresh = nLargestRefreshRateMhz; + } + } + + void CWaylandPlane::Wayland_Surface_Enter( wl_surface *pSurface, wl_output *pOutput ) + { + m_pOutputs.emplace_back( pOutput ); + + UpdateVRRRefreshRate(); + } + void CWaylandPlane::Wayland_Surface_Leave( wl_surface *pSurface, wl_output *pOutput ) + { + std::erase( m_pOutputs, pOutput ); + } + void CWaylandPlane::LibDecor_Frame_Configure( libdecor_frame *pFrame, libdecor_configuration *pConfiguration ) { if ( !libdecor_configuration_get_window_state( pConfiguration, &m_eWindowState ) ) @@ -1060,6 +1125,8 @@ namespace gamescope else { m_pBackend->SetHostCompositorIsCurrentlyVRR( true ); + + UpdateVRRRefreshRate(); } GetVBlankTimer().MarkVBlank( ulTime, true ); -- 2.45.2 From 37cc4d368b3804215b9b4bb2719a3ac2b64cf2e6 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Thu, 18 Jul 2024 01:18:17 +0100 Subject: [PATCH 09/19] WaylandBackend: Run UpdateVRRRefreshRate on Wayland_Surface_Leave --- src/Backends/WaylandBackend.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Backends/WaylandBackend.cpp b/src/Backends/WaylandBackend.cpp index b7f275e..3aac004 100644 --- a/src/Backends/WaylandBackend.cpp +++ b/src/Backends/WaylandBackend.cpp @@ -1068,6 +1068,8 @@ namespace gamescope void CWaylandPlane::Wayland_Surface_Leave( wl_surface *pSurface, wl_output *pOutput ) { std::erase( m_pOutputs, pOutput ); + + UpdateVRRRefreshRate(); } void CWaylandPlane::LibDecor_Frame_Configure( libdecor_frame *pFrame, libdecor_configuration *pConfiguration ) -- 2.45.2 From 853cb9879505efea832ddc05517f57e06d410739 Mon Sep 17 00:00:00 2001 From: CakeKing64 Date: Sat, 20 Jul 2024 17:39:35 +1000 Subject: [PATCH 10/19] WaylandBackend: Restore fullscreen state if returning from not being visible --- src/Backends/WaylandBackend.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Backends/WaylandBackend.cpp b/src/Backends/WaylandBackend.cpp index 3aac004..8c98689 100644 --- a/src/Backends/WaylandBackend.cpp +++ b/src/Backends/WaylandBackend.cpp @@ -1863,7 +1863,10 @@ namespace gamescope void CWaylandBackend::UpdateFullscreenState() { - if ( m_bDesiredFullscreenState != g_bFullscreen ) + if ( !m_bVisible ) + g_bFullscreen = false; + + if ( m_bDesiredFullscreenState != g_bFullscreen && m_bVisible ) { if ( m_bDesiredFullscreenState ) libdecor_frame_set_fullscreen( m_Planes[0].GetFrame(), nullptr ); -- 2.45.2 From 96f141d8b8453f9c28872e8ffc94a29d81d0758d Mon Sep 17 00:00:00 2001 From: flightlessmango Date: Mon, 22 Jul 2024 17:47:30 +0200 Subject: [PATCH 11/19] mangoapp: only set env in steammode MANGOHUD_CONFIGFILE should only be set by gamescope if we're in steamMode. This is causing confusion for regular users as it prevents usage of the standard mangohud config paths --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index da1b516..96484dc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -613,7 +613,7 @@ static void UpdateCompatEnvVars() setenv( "GAMESCOPE_NV12_COLORSPACE", "k_EStreamColorspace_BT601", 0 ); const char *pszMangoConfigPath = getenv( "MANGOHUD_CONFIGFILE" ); - if ( g_bLaunchMangoapp && ( !pszMangoConfigPath || !*pszMangoConfigPath ) ) + if ( (g_bLaunchMangoapp && steamMode) && ( !pszMangoConfigPath || !*pszMangoConfigPath ) ) { char szMangoConfigPath[ PATH_MAX ]; FILE *pMangoConfigFile = gamescope::MakeTempFile( szMangoConfigPath, gamescope::k_szGamescopeTempMangoappTemplate, "w", true ); -- 2.45.2 From 634d739ac609c6550d30a759fb2bc2fa616e0997 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Wed, 24 Jul 2024 17:00:13 +0100 Subject: [PATCH 12/19] backend: Add dump debug info command --- src/backend.cpp | 27 +++++++++++++++++++++++++++ src/backend.h | 4 ++++ src/steamcompmgr.cpp | 19 +++++++++++++++++++ 3 files changed, 50 insertions(+) diff --git a/src/backend.cpp b/src/backend.cpp index d11fcca..91ad0ef 100644 --- a/src/backend.cpp +++ b/src/backend.cpp @@ -120,4 +120,31 @@ namespace gamescope return cv_touch_click_mode; } + + void CBaseBackend::DumpDebugInfo() + { + console_log.infof( "Uses Modifiers: %s", this->UsesModifiers() ? "true" : "false" ); + console_log.infof( "VRR Active: %s", this->IsVRRActive() ? "true" : "false" ); + console_log.infof( "Supports Plane Hardware Cursor: %s (not relevant for nested backends)", this->SupportsPlaneHardwareCursor() ? "true" : "false" ); + console_log.infof( "Supports Tearing: %s", this->SupportsTearing() ? "true" : "false" ); + console_log.infof( "Uses Vulkan Swapchain: %s", this->UsesVulkanSwapchain() ? "true" : "false" ); + console_log.infof( "Is Session Based: %s", this->IsSessionBased() ? "true" : "false" ); + console_log.infof( "Supports Explicit Sync: %s", this->SupportsExplicitSync() ? "true" : "false" ); + console_log.infof( "Current Screen Type: %s", this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_INTERNAL ? "Internal" : "External" ); + console_log.infof( "Is Visible: %s", this->IsVisible() ? "true" : "false" ); + console_log.infof( "Is Nested: %s", this->GetNestedHints() != nullptr ? "true" : "false" ); + console_log.infof( "Needs Frame Sync: %s", this->NeedsFrameSync() ? "true" : "false" ); + console_log.infof( "Total Presents Queued: %lu", this->PresentationFeedback().TotalPresentsQueued() ); + console_log.infof( "Total Presents Completed: %lu", this->PresentationFeedback().TotalPresentsCompleted() ); + console_log.infof( "Current Presents In Flight: %lu", this->PresentationFeedback().CurrentPresentsInFlight() ); + } + + ConCommand cc_backend_info( "backend_info", "Dump debug info about the backend state", + []( std::span svArgs ) + { + if ( !GetBackend() ) + return; + + GetBackend()->DumpDebugInfo(); + }); } diff --git a/src/backend.h b/src/backend.h index 9c2db15..4f91fe7 100644 --- a/src/backend.h +++ b/src/backend.h @@ -236,6 +236,8 @@ namespace gamescope virtual TouchClickMode GetTouchClickMode() = 0; + virtual void DumpDebugInfo() = 0; + static IBackend *Get(); template static bool Set(); @@ -263,6 +265,8 @@ namespace gamescope virtual BackendPresentFeedback& PresentationFeedback() override { return m_PresentFeedback; } virtual TouchClickMode GetTouchClickMode() override; + + virtual void DumpDebugInfo() override; protected: BackendPresentFeedback m_PresentFeedback{}; }; diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index b8102eb..60ddbbe 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -856,6 +856,25 @@ void steamcompmgr_set_app_refresh_cycle_override( gamescope::GamescopeScreenType update_app_target_refresh_cycle(); } +gamescope::ConCommand cc_debug_set_fps_limit( "debug_set_fps_limit", "Set refresh cycle (debug)", +[](std::span svArgs) +{ + if ( svArgs.size() < 2 ) + return; + + // TODO: Expose all facets as args. + std::optional onFps = gamescope::Parse( svArgs[1] ); + if ( !onFps ) + { + console_log.errorf( "Failed to parse FPS." ); + return; + } + + int32_t nFps = *onFps; + + steamcompmgr_set_app_refresh_cycle_override( GetBackend()->GetScreenType(), nFps, true, true ); +}); + static int g_nRuntimeInfoFd = -1; bool g_bFSRActive = false; -- 2.45.2 From e2a277e15b7f6b7075e0433a5b9605168faf91b5 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Wed, 24 Jul 2024 17:00:35 +0100 Subject: [PATCH 13/19] WaylandBackend: Beginnings of support for xx-color-management-v3 --- protocol/meson.build | 3 + protocol/xx-color-management-v3.xml | 1421 +++++++++++++++++++++++++++ src/Backends/WaylandBackend.cpp | 229 ++++- src/backend.h | 3 +- 4 files changed, 1645 insertions(+), 11 deletions(-) create mode 100644 protocol/xx-color-management-v3.xml diff --git a/protocol/meson.build b/protocol/meson.build index 9af3607..5eb681e 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -37,6 +37,9 @@ protocols = [ # wlroots protocols 'wlr-layer-shell-unstable-v1.xml', + + # WIP protocols + 'xx-color-management-v3.xml', ] protocols_client_src = [] diff --git a/protocol/xx-color-management-v3.xml b/protocol/xx-color-management-v3.xml new file mode 100644 index 0000000..e637a25 --- /dev/null +++ b/protocol/xx-color-management-v3.xml @@ -0,0 +1,1421 @@ + + + + Copyright 2019 Sebastian Wick + Copyright 2019 Erwin Burema + Copyright 2020 AMD + Copyright 2020-2024 Collabora, Ltd. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + The aim of the color management extension is to allow clients to know + the color properties of outputs, and to tell the compositor about the color + properties of their content on surfaces. Doing this enables a compositor + to perform automatic color management of content for different outputs + according to how content is intended to look like. + + The color properties are represented as an image description object which + is immutable after it has been created. A wl_output always has an + associated image description that clients can observe. A wl_surface + always has an associated preferred image description as a hint chosen by + the compositor that clients can also observe. Clients can set an image + description on a wl_surface to denote the color characteristics of the + surface contents. + + An image description includes SDR and HDR colorimetry and encoding, HDR + metadata, and viewing environment parameters. An image description does + not include the properties set through color-representation extension. + It is expected that the color-representation extension is used in + conjunction with the color management extension when necessary, + particularly with the YUV family of pixel formats. + + Recommendation ITU-T H.273 + "Coding-independent code points for video signal type identification" + shall be referred to as simply H.273 here. + + The color-and-hdr repository + (https://gitlab.freedesktop.org/pq/color-and-hdr) contains + background information on the protocol design and legacy color management. + It also contains a glossary, learning resources for digital color, tools, + samples and more. + + The terminology used in this protocol is based on common color science and + color encoding terminology where possible. The glossary in the color-and-hdr + repository shall be the authority on the definition of terms in this + protocol. + + + + + A global interface used for getting color management extensions for + wl_surface and wl_output objects, and for creating client defined image + description objects. The extension interfaces allow + getting the image description of outputs and setting the image + description of surfaces. + + + + + Destroy the xx_color_manager_v3 object. This does not affect any other + objects in any way. + + + + + + + + + + + See the ICC.1:2022 specification from the International Color Consortium + for more details about rendering intents. + + The principles of ICC defined rendering intents apply with all types of + image descriptions, not only those with ICC file profiles. + + Compositors must support the perceptual rendering intent. Other + rendering intents are optional. + + + + + + + + + + + + + + + + + + + + The compositor supports set_mastering_display_primaries request with a + target color volume fully contained inside the primary color volume. + + + + + The compositor additionally supports target color volumes that + extend outside of the primary color volume. + + This can only be advertised if feature set_mastering_display_primaries + is supported as well. + + + + + + + Named color primaries used to encode well-known sets of primaries. H.273 + is the authority, when it comes to the exact values of primaries and + authoritative specifications, where an equivalent code point exists. + + Descriptions do list the specifications for convenience. + + + + + Color primaries as defined by + - Rec. ITU-R BT.709-6 + - Rec. ITU-R BT.1361-0 conventional colour gamut system and extended + colour gamut system (historical) + - IEC 61966-2-1 sRGB or sYCC + - IEC 61966-2-4 + - Society of Motion Picture and Television Engineers (SMPTE) RP 177 + (1993) Annex B + Equivalent to H.273 ColourPrimaries code point 1. + + + + + Color primaries as defined by + - Rec. ITU-R BT.470-6 System M (historical) + - United States National Television System Committee 1953 + Recommendation for transmission standards for color television + - United States Federal Communications Commission (2003) Title 47 Code + of Federal Regulations 73.682 (a)(20) + Equivalent to H.273 ColourPrimaries code point 4. + + + + + Color primaries as defined by + - Rec. ITU-R BT.470-6 System B, G (historical) + - Rec. ITU-R BT.601-7 625 + - Rec. ITU-R BT.1358-0 625 (historical) + - Rec. ITU-R BT.1700-0 625 PAL and 625 SECAM + Equivalent to H.273 ColourPrimaries code point 5. + + + + + Color primaries as defined by + - Rec. ITU-R BT.601-7 525 + - Rec. ITU-R BT.1358-1 525 or 625 (historical) + - Rec. ITU-R BT.1700-0 NTSC + - SMPTE 170M (2004) + - SMPTE 240M (1999) (historical) + Equivalent to H.273 ColourPrimaries code point 6 and 7. + + + + + Color primaries as defined by H.273 for generic film. + Equivalent to H.273 ColourPrimaries code point 8. + + + + + Color primaries as defined by + - Rec. ITU-R BT.2020-2 + - Rec. ITU-R BT.2100-0 + Equivalent to H.273 ColourPrimaries code point 9. + + + + + Color primaries as defined as the maximum of the CIE 1931 XYZ color + space by + - SMPTE ST 428-1 + - (CIE 1931 XYZ as in ISO 11664-1) + Equivalent to H.273 ColourPrimaries code point 10. + + + + + Color primaries as defined by Digital Cinema System and published in + SMPTE RP 431-2 (2011). Equivalent to H.273 ColourPrimaries code point + 11. + + + + + Color primaries as defined by Digital Cinema System and published in + SMPTE EG 432-1 (2010). + Equivalent to H.273 ColourPrimaries code point 12. + + + + + Color primaries as defined by Adobe as "Adobe RGB" and later published + by ISO 12640-4 (2011). + + + + + + + Named transfer functions used to encode well-known transfer + characteristics. H.273 is the authority, when it comes to the exact + formulas and authoritative specifications, where an equivalent code + point exists. + + Descriptions do list the specifications for convenience. + + + + + Transfer characteristics as defined by + - Rec. ITU-R BT.709-6 + - Rec. ITU-R BT.1361-0 conventional colour gamut system (historical) + Equivalent to H.273 TransferCharacteristics code point 1, 6, 14, 15. + + + + + Transfer characteristics as defined by + - Rec. ITU-R BT.470-6 System M (historical) + - United States National Television System Committee 1953 + Recommendation for transmission standards for color television + - United States Federal Communications Commission (2003) Title 47 Code + of Federal Regulations 73.682 (a) (20) + - Rec. ITU-R BT.1700-0 625 PAL and 625 SECAM + Equivalent to H.273 TransferCharacteristics code point 4. + + + + + Transfer characteristics as defined by + - Rec. ITU-R BT.470-6 System B, G (historical) + Equivalent to H.273 TransferCharacteristics code point 5. + + + + + Transfer characteristics as defined by + - SMPTE ST 240 (1999) + Equivalent to H.273 TransferCharacteristics code point 7. + + + + + Linear transfer characteristics. + Equivalent to H.273 TransferCharacteristics code point 8. + + + + + Logarithmic transfer characteristic (100:1 range). + Equivalent to H.273 TransferCharacteristics code point 9. + + + + + Logarithmic transfer characteristic (100 * Sqrt(10) : 1 range). + Equivalent to H.273 TransferCharacteristics code point 10. + + + + + Transfer characteristics as defined by + - IEC 61966-2-4 + Equivalent to H.273 TransferCharacteristics code point 11. + + + + + Transfer characteristics as defined by + - Rec. ITU-R BT.1361-0 extended colour gamut system (historical) + Equivalent to H.273 TransferCharacteristics code point 12. + + + + + Transfer characteristics as defined by + - IEC 61966-2-1 sRGB + Equivalent to H.273 TransferCharacteristics code point 13 with + MatrixCoefficients set to 0. + + + + + Transfer characteristics as defined by + - IEC 61966-2-1 sYCC + Equivalent to H.273 TransferCharacteristics code point 13 with + MatrixCoefficients set to anything but 0. + + + + + Transfer characteristics as defined by + - SMPTE ST 2084 (2014) for 10-, 12-, 14- and 16-bit systems + - Rec. ITU-R BT.2100-2 perceptual quantization (PQ) system + Equivalent to H.273 TransferCharacteristics code point 16. + + This TF implies these default luminances + - primary color volume minimum: 0.005 cd/m² + - primary color volume maximum: 10000 cd/m² + - reference white: 203 cd/m² + + + + + Transfer characteristics as defined by + - SMPTE ST 428-1 (2019) + Equivalent to H.273 TransferCharacteristics code point 17. + + + + + Transfer characteristics as defined by + - ARIB STD-B67 (2015) + - Rec. ITU-R BT.2100-2 hybrid log-gamma (HLG) system + Equivalent to H.273 TransferCharacteristics code point 18. + + This TF implies these default luminances + - primary color volume minimum: 0.005 cd/m² + - primary color volume maximum: 1000 cd/m² + - reference white: 203 cd/m² + Note: HLG is a scene referred signal. All absolute luminance values + used here for HLG assume a 1000 cd/m² display. + + + + + + + This creates a new xx_color_management_output_v3 object for the + given wl_output. + + See the xx_color_management_output_v3 interface for more details. + + + + + + + + + If a xx_color_management_surface_v3 object already exists for the given + wl_surface, the protocol error surface_exists is raised. + + This creates a new color xx_color_management_surface_v3 object for the + given wl_surface. + + See the xx_color_management_surface_v3 interface for more details. + + + + + + + + + Makes a new ICC-based image description creator object with all + properties initially unset. The client can then use the object's + interface to define all the required properties for an image description + and finally create a xx_image_description_v3 object. + + This request can be used when the compositor advertises + xx_color_manager_v3.feature.icc_v2_v4. + Otherwise this request raises the protocol error unsupported_feature. + + + + + + + + Makes a new parametric image description creator object with all + properties initially unset. The client can then use the object's + interface to define all the required properties for an image description + and finally create a xx_image_description_v3 object. + + This request can be used when the compositor advertises + xx_color_manager_v3.feature.parametric. + Otherwise this request raises the protocol error unsupported_feature. + + + + + + + + When this object is created, it shall immediately send this event once + for each rendering intent the compositor supports. + + + + + + + + When this object is created, it shall immediately send this event once + for each compositor supported feature listed in the enumeration. + + + + + + + + When this object is created, it shall immediately send this event once + for each named transfer function the compositor supports with the + parametric image description creator. + + + + + + + + When this object is created, it shall immediately send this event once + for each named set of primaries the compositor supports with the + parametric image description creator. + + + + + + + + + A xx_color_management_output_v3 describes the color properties of an + output. + + The xx_color_management_output_v3 is associated with the wl_output global + underlying the wl_output object. Therefore the client destroying the + wl_output object has no impact, but the compositor removing the output + global makes the xx_color_management_output_v3 object inert. + + + + + Destroy the color xx_color_management_output_v3 object. This does not + affect any remaining protocol objects. + + + + + + This event is sent whenever the image description of the output changed, + followed by one wl_output.done event common to output events across all + extensions. + + If the client wants to use the updated image description, it needs to do + get_image_description again, because image description objects are + immutable. + + + + + + This creates a new xx_image_description_v3 object for the current image + description of the output. There always is exactly one image description + active for an output so the client should destroy the image description + created by earlier invocations of this request. This request is usually + sent as a reaction to the image_description_changed event or when + creating a xx_color_management_output_v3 object. + + The image description of an output represents the color encoding the + output expects. There might be performance and power advantages, as well + as improved color reproduction, if a content update matches the image + description of the output it is being shown on. If a content update is + shown on any other output than the one it matches the image description + of, then the color reproduction on those outputs might be considerably + worse. + + The created xx_image_description_v3 object preserves the image + description of the output from the time the object was created. + + The resulting image description object allows get_information request. + + If this protocol object is inert, the resulting image description object + shall immediately deliver the xx_image_description_v3.failed event with + the no_output cause. + + If the interface version is inadequate for the output's image + description, meaning that the client does not support all the events + needed to deliver the crucial information, the resulting image + description object shall immediately deliver the + xx_image_description_v3.failed event with the low_version cause. + + Otherwise the object shall immediately deliver the ready event. + + + + + + + + + A xx_color_management_surface_v3 allows the client to set the color + space and HDR properties of a surface. + + If the wl_surface associated with the xx_color_management_surface_v3 is + destroyed, the xx_color_management_surface_v3 object becomes inert. + + + + + Destroy the xx_color_management_surface_v3 object and do the same as + unset_image_description. + + + + + + + + + + + + + + Set the image description of the underlying surface. The image + description and rendering intent are double-buffered state, see + wl_surface.commit. + + It is the client's responsibility to understand the image description + it sets on a surface, and to provide content that matches that image + description. Compositors might convert images to match their own or any + other image descriptions. + + Image description whose creation gracefully failed (received + xx_image_description_v3.failed) are forbidden in this request, and in + such case the protocol error image_description is raised. + + All image descriptions whose creation succeeded (received + xx_image_description_v3.ready) are allowed and must always be accepted + by the compositor. + + A rendering intent provides the client's preference on how content + colors should be mapped to each output. The render_intent value must + be one advertised by the compositor with + xx_color_manager_v3.render_intent event, otherwise the protocol error + render_intent is raised. + + By default, a surface does not have an associated image description + nor a rendering intent. The handling of color on such surfaces is + compositor implementation defined. Compositors should handle such + surfaces as sRGB but may handle them differently if they have specific + requirements. + + + + + + + + + This request removes any image description from the surface. See + set_image_description for how a compositor handles a surface without + an image description. This is double-buffered state, see + wl_surface.commit. + + + + + + The preferred image description is the one which likely has the most + performance and/or quality benefits for the compositor if used by the + client for its wl_surface contents. This event is sent whenever the + compositor changes the wl_surface's preferred image description. + + This is not an initial event. + + This event is merely a notification. When the client wants to know + what the preferred image description is, it shall use the get_preferred + request. + + The preferred image description is not automatically used for anything. + It is only a hint, and clients may set any valid image description with + set_image_description but there might be performance and color accuracy + improvements by providing the wl_surface contents in the preferred + image description. Therefore clients that can, should render according + to the preferred image description + + + + + + If this protocol object is inert, the protocol error inert is raised. + + The preferred image description represents the compositor's preferred + color encoding for this wl_surface at the current time. There might be + performance and power advantages, as well as improved color + reproduction, if the image description of a content update matches the + preferred image description. + + This creates a new xx_image_description_v3 object for the currently + preferred image description for the wl_surface. The client should + stop using and destroy the image descriptions created by earlier + invocations of this request for the associated wl_surface. + This request is usually sent as a reaction to the preferred_changed + event or when creating a xx_color_management_surface_v3 object if + the client is capable of adapting to image descriptions. + + The created xx_image_description_v3 object preserves the preferred image + description of the wl_surface from the time the object was created. + + The resulting image description object allows get_information request. + + If the interface version is inadequate for the preferred image + description, meaning that the client does not support all the + events needed to deliver the crucial information, the resulting image + description object shall immediately deliver the + xx_image_description_v3.failed event with the low_version cause, + otherwise the object shall immediately deliver the ready event. + + + + + + + + + This type of object is used for collecting all the information required + to create a xx_image_description_v3 object from an ICC file. A complete + set of required parameters consists of these properties: + - ICC file + + Each required property must be set exactly once if the client is to create + an image description. The set requests verify that a property was not + already set. The create request verifies that all required properties are + set. There may be several alternative requests for setting each property, + and in that case the client must choose one of them. + + Once all properties have been set, the create request must be used to + create the image description object, destroying the creator in the + process. + + + + + + + + + + + + + + + Create an image description object based on the ICC information + previously set on this object. A compositor must parse the ICC data in + some undefined but finite amount of time. + + The completeness of the parameter set is verified. If the set is not + complete, the protocol error incomplete_set is raised. For the + definition of a complete set, see the description of this interface. + + If the particular combination of the information is not supported + by the compositor, the resulting image description object shall + immediately deliver the xx_image_description_v3.failed event with the + 'unsupported' cause. If a valid image description was created from the + information, the xx_image_description_v3.ready event will eventually + be sent instead. + + This request destroys the xx_image_description_creator_icc_v3 object. + + The resulting image description object does not allow get_information + request. + + + + + + + + Sets the ICC profile file to be used as the basis of the image + description. + + The data shall be found through the given fd at the given offset, having + the given length. The fd must seekable and readable. Violating these + requirements raises the bad_fd protocol error. + + If reading the data fails due to an error independent of the client, the + compositor shall send the xx_image_description_v3.failed event on the + created xx_image_description_v3 with the 'operating_system' cause. + + The maximum size of the ICC profile is 4 MB. If length is greater than + that or zero, the protocol error bad_size is raised. If offset + length + exceeds the file size, the protocol error out_of_file is raised. + + A compositor may read the file at any time starting from this request + and only until whichever happens first: + - If create request was issued, the xx_image_description_v3 object + delivers either failed or ready event; or + - if create request was not issued, this + xx_image_description_creator_icc_v3 object is destroyed. + + A compositor shall not modify the contents of the file, and the fd may + be sealed for writes and size changes. The client must ensure to its + best ability that the data does not change while the compositor is + reading it. + + The data must represent a valid ICC profile. The ICC profile version + must be 2 or 4, it must be a 3 channel profile and the class must be + Display or ColorSpace. Violating these requirements will not result in a + protocol error but will eventually send the + xx_image_description_v3.failed event on the created + xx_image_description_v3 with the 'unsupported' cause. + + See the International Color Consortium specification ICC.1:2022 for more + details about ICC profiles. + + If ICC file has already been set on this object, the protocol error + already_set is raised. + + + + + + + + + + + This type of object is used for collecting all the parameters required + to create a xx_image_description_v3 object. A complete set of required + parameters consists of these properties: + - transfer characteristic function (tf) + - chromaticities of primaries and white point (primary color volume) + + The following properties are optional and have a well-defined default + if not explicitly set: + - primary color volume luminance range + - reference white luminance level + - mastering display primaries and white point (target color volume) + - mastering luminance range + - maximum content light level + - maximum frame-average light level + + Each required property must be set exactly once if the client is to create + an image description. The set requests verify that a property was not + already set. The create request verifies that all required properties are + set. There may be several alternative requests for setting each property, + and in that case the client must choose one of them. + + Once all properties have been set, the create request must be used to + create the image description object, destroying the creator in the + process. + + + + + + + + + + + + + + + + + + Create an image description object based on the parameters previously + set on this object. + + The completeness of the parameter set is verified. If the set is not + complete, the protocol error incomplete_set is raised. For the + definition of a complete set, see the description of this interface. + + Also, the combination of the parameter set is verified. If the set is + not consistent, the protocol error inconsistent_set is raised. + + If the particular combination of the parameter set is not supported + by the compositor, the resulting image description object shall + immediately deliver the xx_image_description_v3.failed event with the + 'unsupported' cause. If a valid image description was created from the + parameter set, the xx_image_description_v3.ready event will eventually + be sent instead. + + This request destroys the xx_image_description_creator_params_v3 + object. + + The resulting image description object does not allow get_information + request. + + + + + + + + Sets the transfer characteristic using explicitly enumerated named + functions. + + When the resulting image description is attached to an image, the + content should be encoded and decoded according to the industry standard + practices for the transfer characteristic. + + Only names advertised with xx_color_manager_v3 event supported_tf_named + are allowed. Other values shall raise the protocol error invalid_tf. + + If transfer characteristic has already been set on this object, the + protocol error already_set is raised. + + + + + + + + Sets the color component transfer characteristic to a power curve with + the given exponent. This curve represents the conversion from electrical + to optical pixel or color values. + + When the resulting image description is attached to an image, the + content should be encoded with the inverse of the power curve. + + The curve exponent shall be multiplied by 10000 to get the argument eexp + value to carry the precision of 4 decimals. + + The curve exponent must be at least 1.0 and at most 10.0. Otherwise the + protocol error invalid_tf is raised. + + If transfer characteristic has already been set on this object, the + protocol error already_set is raised. + + This request can be used when the compositor advertises + xx_color_manager_v3.feature.set_tf_power. Otherwise this request raises + the protocol error unsupported_feature. + + + + + + + + Sets the color primaries and white point using explicitly named sets. + This describes the primary color volume which is the basis for color + value encoding. + + Only names advertised with xx_color_manager_v3 event + supported_primaries_named are allowed. Other values shall raise the + protocol error invalid_primaries. + + If primaries have already been set on this object, the protocol error + already_set is raised. + + + + + + + + Sets the color primaries and white point using CIE 1931 xy chromaticity + coordinates. This describes the primary color volume which is the basis + for color value encoding. + + Each coordinate value is multiplied by 10000 to get the argument value + to carry precision of 4 decimals. + + If primaries have already been set on this object, the protocol error + already_set is raised. + + This request can be used if the compositor advertises + xx_color_manager_v3.feature.set_primaries. Otherwise this request raises + the protocol error unsupported_feature. + + + + + + + + + + + + + + + Sets the primary color volume luminance range and the reference white + luminance level. + + The default luminances are + - primary color volume minimum: 0.2 cd/m² + - primary color volume maximum: 80 cd/m² + - reference white: 80 cd/m² + + Setting a named transfer characteristic can imply other default + luminances. + + The default luminances get overwritten when this request is used. + + 'min_lum' and 'max_lum' specify the minimum and maximum luminances of + the primary color volume as reproduced by the targeted display. + + 'reference_lum' specifies the luminance of the reference white as + reproduced by the targeted display, and reflects the targeted viewing + environment. + + Compositors should make sure that all content is anchored, meaning that + an input signal level of 'reference_lum' on one image description and + another input signal level of 'reference_lum' on another image + description should produce the same output level, even though the + 'reference_lum' on both image representations can be different. + + If 'max_lum' is less than the 'reference_lum', or 'reference_lum' is + less than or equal to 'min_lum', the protocol error invalid_luminance is + raised. + + The minimum luminance is multiplied by 10000 to get the argument + 'min_lum' value and carries precision of 4 decimals. The maximum + luminance and reference white luminance values are unscaled. + + If the primary color volume luminance range and the reference white + luminance level have already been set on this object, the protocol error + already_set is raised. + + This request can be used if the compositor advertises + xx_color_manager_v3.feature.set_luminances. Otherwise this request + raises the protocol error unsupported_feature. + + + + + + + + + + Provides the color primaries and white point of the mastering display + using CIE 1931 xy chromaticity coordinates. This is compatible with the + SMPTE ST 2086 definition of HDR static metadata. + + The mastering display primaries define the target color volume. + + If mastering display primaries are not explicitly set, the target color + volume is assumed to be equal to the primary color volume. + + The target color volume is defined by all tristimulus values between 0.0 + and 1.0 (inclusive) of the color space defined by the given mastering + display primaries and white point. The colorimetry is identical between + the container color space and the mastering display color space, + including that no chromatic adaptation is applied even if the white + points differ. + + The target color volume can exceed the primary color volume to allow for + a greater color volume with an existing color space definition (for + example scRGB). It can be smaller than the primary color volume to + minimize gamut and tone mapping distances for big color spaces (HDR + metadata). + + To make use of the entire target color volume a suitable pixel format + has to be chosen (e.g. floating point to exceed the primary color + volume, or abusing limited quantization range as with xvYCC). + + Each coordinate value is multiplied by 10000 to get the argument value + to carry precision of 4 decimals. + + If mastering display primaries have already been set on this object, the + protocol error already_set is raised. + + This request can be used if the compositor advertises + xx_color_manager_v3.feature.set_mastering_display_primaries. Otherwise + this request raises the protocol error unsupported_feature. The + advertisement implies support only for target color volumes fully + contained within the primary color volume. + + If a compositor additionally supports target color volume exceeding the + primary color volume, it must advertise + xx_color_manager_v3.feature.extended_target_volume. If a client uses + target color volume exceeding the primary color volume and the + compositor does not support it, the result is implementation defined. + Compositors are recommended to detect this case and fail the image + description gracefully, but it may as well result in color artifacts. + + + + + + + + + + + + + + + Sets the luminance range that was used during the content mastering + process as the minimum and maximum absolute luminance L. This is + compatible with the SMPTE ST 2086 definition of HDR static metadata. + + The mastering luminance range is undefined by default. + + If max L is less than or equal to min L, the protocol error + invalid_luminance is raised. + + Min L value is multiplied by 10000 to get the argument min_lum value + and carry precision of 4 decimals. Max L value is unscaled for max_lum. + + + + + + + + + Sets the maximum content light level (max_cll) as defined by CTA-861-H. + + This can only be set when set_tf_cicp is used to set the transfer + characteristic to Rec. ITU-R BT.2100-2 perceptual quantization system. + Otherwise, 'create' request shall raise inconsistent_set protocol + error. + + max_cll is undefined by default. + + + + + + + + Sets the maximum frame-average light level (max_fall) as defined by + CTA-861-H. + + This can only be set when set_tf_cicp is used to set the transfer + characteristic to Rec. ITU-R BT.2100-2 perceptual quantization system. + Otherwise, 'create' request shall raise inconsistent_set protocol error. + + max_fall is undefined by default. + + + + + + + + + An image description carries information about the color encoding used on + a surface when attached to a wl_surface via + xx_color_management_surface_v3.set_image_description. A compositor can use + this information to decode pixel values into colorimetrically meaningful + quantities. + + Note, that the xx_image_description_v3 object is not ready to be used + immediately after creation. The object eventually delivers either the + 'ready' or the 'failed' event, specified in all requests creating it. The + object is deemed "ready" after receiving the 'ready' event. + + An object which is not ready is illegal to use, it can only be destroyed. + Any other request in this interface shall result in the 'not_ready' + protocol error. Attempts to use an object which is not ready through other + interfaces shall raise protocol errors defined there. + + Once created and regardless of how it was created, a + xx_image_description_v3 object always refers to one fixed image + description. It cannot change after creation. + + + + + Destroy this object. It is safe to destroy an object which is not ready. + + Destroying a xx_image_description_v3 object has no side-effects, not + even if a xx_color_management_surface_v3.set_image_description has not + yet been followed by a wl_surface.commit. + + + + + + + + + + + + + + + + + + + + + + If creating a xx_image_description_v3 object fails for a reason that is + not defined as a protocol error, this event is sent. + + The requests that create image description objects define whether and + when this can occur. Only such creation requests can trigger this event. + This event cannot be triggered after the image description was + successfully formed. + + Once this event has been sent, the xx_image_description_v3 object will + never become ready and it can only be destroyed. + + + + + + + + + Once this event has been sent, the xx_image_description_v3 object is + deemed "ready". Ready objects can be used to send requests and can be + used through other interfaces. + + Every ready xx_image_description_v3 protocol object refers to an + underlying image description record in the compositor. Multiple protocol + objects may end up referring to the same record. Clients may identify + these "copies" by comparing their id numbers: if the numbers from two + protocol objects are identical, the protocol objects refer to the same + image description record. Two different image description records + cannot have the same id number simultaneously. The id number does not + change during the lifetime of the image description record. + + The id number is valid only as long as the protocol object is alive. If + all protocol objects referring to the same image description record are + destroyed, the id number may be recycled for a different image + description record. + + Image description id number is not a protocol object id. Zero is + reserved as an invalid id number. It shall not be possible for a client + to refer to an image description by its id number in protocol. The id + numbers might not be portable between Wayland connections. + + This identity allows clients to de-duplicate image description records + and avoid get_information request if they already have the image + description information. + + + + + + + + Creates a xx_image_description_info_v3 object which delivers the + information that makes up the image description. + + Not all image description protocol objects allow get_information + request. Whether it is allowed or not is defined by the request that + created the object. If get_information is not allowed, the protocol + error no_information is raised. + + + + + + + + + Sends all matching events describing an image description object exactly + once and finally sends the 'done' event. + + Once a xx_image_description_info_v3 object has delivered a 'done' event it + is automatically destroyed. + + Every xx_image_description_info_v3 created from the same + xx_image_description_v3 shall always return the exact same data. + + + + + Signals the end of information events and destroys the object. + + + + + + The icc argument provides a file descriptor to the client which may be + memory-mapped to provide the ICC profile matching the image description. + The fd is read-only, and if mapped then it must be mapped with + MAP_PRIVATE by the client. + + The ICC profile version and other details are determined by the + compositor. There is no provision for a client to ask for a specific + kind of a profile. + + + + + + + + + + Delivers the primary color volume primaries and white point using CIE + 1931 xy chromaticity coordinates. + + Each coordinate value is multiplied by 10000 to get the argument value + to carry precision of 4 decimals. + + + + + + + + + + + + + + + Delivers the primary color volume primaries and white point using an + explicitly enumerated named set. + + + + + + + + The color component transfer characteristic of this image description is + a pure power curve. This event provides the exponent of the power + function. This curve represents the conversion from electrical to + optical pixel or color values. + + The curve exponent has been multiplied by 10000 to get the argument eexp + value to carry the precision of 4 decimals. + + + + + + + + Delivers the transfer characteristic using an explicitly enumerated + named function. + + + + + + + + Delivers the primary color volume luminance range and the reference + white luminance level. + + The minimum luminance is multiplied by 10000 to get the argument + 'min_lum' value and carries precision of 4 decimals. The maximum + luminance and reference white luminance values are unscaled. + + + + + + + + + + Provides the color primaries and white point of the target color volume + using CIE 1931 xy chromaticity coordinates. This is compatible with the + SMPTE ST 2086 definition of HDR static metadata for mastering displays. + + While primary color volume is about how color is encoded, the target + color volume is the actually displayable color volume. If target color + volume is equal to the primary color volume, then this event is not + sent. + + Each coordinate value is multiplied by 10000 to get the argument value + to carry precision of 4 decimals. + + + + + + + + + + + + + + + Provides the luminance range that the image description is targeting as + the minimum and maximum absolute luminance L. This is compatible with + the SMPTE ST 2086 definition of HDR static metadata. + + This luminance range is only theoretical and may not correspond to the + luminance of light emitted on an actual display. + + Min L value is multiplied by 10000 to get the argument min_lum value and + carry precision of 4 decimals. Max L value is unscaled for max_lum. + + + + + + + + + Provides the targeted max_cll of the image description. max_cll is + defined by CTA-861-H. + + This luminance is only theoretical and may not correspond to the + luminance of light emitted on an actual display. + + + + + + + + Provides the targeted max_fall of the image description. max_fall is + defined by CTA-861-H. + + This luminance is only theoretical and may not correspond to the + luminance of light emitted on an actual display. + + + + + + diff --git a/src/Backends/WaylandBackend.cpp b/src/Backends/WaylandBackend.cpp index 8c98689..1a09acc 100644 --- a/src/Backends/WaylandBackend.cpp +++ b/src/Backends/WaylandBackend.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -129,6 +130,7 @@ namespace gamescope friend CWaylandPlane; BackendConnectorHDRInfo m_HDRInfo{}; + displaycolorimetry_t m_DisplayColorimetry = displaycolorimetry_709; std::vector m_FakeEdid; CWaylandBackend *m_pBackend = nullptr; @@ -222,6 +224,22 @@ namespace gamescope uint32_t uMaxFullFrameLuminance ); static const frog_color_managed_surface_listener s_FrogColorManagedSurfaceListener; + void Wayland_XXColorManagementSurface_PreferredChanged( xx_color_management_surface_v3 *pColorManagementSurface ); + static const xx_color_management_surface_v3_listener s_XXColorManagementSurfaceListener; + void UpdateXXPreferredColorManagement(); + + void Wayland_XXImageDescriptionInfo_Done( xx_image_description_info_v3 *pImageDescInfo ); + void Wayland_XXImageDescriptionInfo_ICCFile( xx_image_description_info_v3 *pImageDescInfo, int32_t nICCFd, uint32_t uICCSize ); + void Wayland_XXImageDescriptionInfo_Primaries( xx_image_description_info_v3 *pImageDescInfo, int32_t nRedX, int32_t nRedY, int32_t nGreenX, int32_t nGreenY, int32_t nBlueX, int32_t nBlueY, int32_t nWhiteX, int32_t nWhiteY ); + void Wayland_XXImageDescriptionInfo_PrimariesNamed( xx_image_description_info_v3 *pImageDescInfo, uint32_t uPrimaries ); + void Wayland_XXImageDescriptionInfo_TFPower( xx_image_description_info_v3 *pImageDescInfo, uint32_t uExp); + void Wayland_XXImageDescriptionInfo_TFNamed( xx_image_description_info_v3 *pImageDescInfo, uint32_t uTF); + void Wayland_XXImageDescriptionInfo_Luminances( xx_image_description_info_v3 *pImageDescInfo, uint32_t uMinLum, uint32_t uMaxLum, uint32_t uRefLum ); + void Wayland_XXImageDescriptionInfo_TargetPrimaries( xx_image_description_info_v3 *pImageDescInfo, int32_t nRedX, int32_t nRedY, int32_t nGreenX, int32_t nGreenY, int32_t nBlueX, int32_t nBlueY, int32_t nWhiteX, int32_t nWhiteY ); + void Wayland_XXImageDescriptionInfo_TargetLuminance( xx_image_description_info_v3 *pImageDescInfo, uint32_t uMinLum, uint32_t uMaxLum ); + void Wayland_XXImageDescriptionInfo_Target_MaxCLL( xx_image_description_info_v3 *pImageDescInfo, uint32_t uMaxCLL ); + void Wayland_XXImageDescriptionInfo_Target_MaxFALL( xx_image_description_info_v3 *pImageDescInfo, uint32_t uMaxFALL ); + void Wayland_FractionalScale_PreferredScale( wp_fractional_scale_v1 *pFractionalScale, uint32_t uScale ); static const wp_fractional_scale_v1_listener s_FractionalScaleListener; @@ -233,6 +251,7 @@ namespace gamescope libdecor_frame *m_pFrame = nullptr; wl_subsurface *m_pSubsurface = nullptr; frog_color_managed_surface *m_pFrogColorManagedSurface = nullptr; + xx_color_management_surface_v3 *m_pXXColorManagedSurface = nullptr; wp_fractional_scale_v1 *m_pFractionalScale = nullptr; libdecor_window_state m_eWindowState = LIBDECOR_WINDOW_STATE_NONE; std::vector m_pOutputs; @@ -267,6 +286,10 @@ namespace gamescope { .preferred_metadata = WAYLAND_USERDATA_TO_THIS( CWaylandPlane, Wayland_FrogColorManagedSurface_PreferredMetadata ), }; + const xx_color_management_surface_v3_listener CWaylandPlane::s_XXColorManagementSurfaceListener = + { + .preferred_changed = WAYLAND_USERDATA_TO_THIS( CWaylandPlane, Wayland_XXColorManagementSurface_PreferredChanged ), + }; const wp_fractional_scale_v1_listener CWaylandPlane::s_FractionalScaleListener = { .preferred_scale = WAYLAND_USERDATA_TO_THIS( CWaylandPlane, Wayland_FractionalScale_PreferredScale ), @@ -546,6 +569,7 @@ namespace gamescope wp_viewporter *GetViewporter() const { return m_pViewporter; } wp_presentation *GetPresentation() const { return m_pPresentation; } frog_color_management_factory_v1 *GetFrogColorManagementFactory() const { return m_pFrogColorMgmtFactory; } + xx_color_manager_v3 *GetXXColorManager() const { return m_pXXColorManager; } wp_fractional_scale_manager_v1 *GetFractionalScaleManager() const { return m_pFractionalScaleManager; } xdg_toplevel_icon_manager_v1 *GetToplevelIconManager() const { return m_pToplevelIconManager; } libdecor *GetLibDecor() const { return m_pLibDecor; } @@ -596,6 +620,12 @@ namespace gamescope void Wayland_Keyboard_Leave( wl_keyboard *pKeyboard, uint32_t uSerial, wl_surface *pSurface ); static const wl_keyboard_listener s_KeyboardListener; + void Wayland_XXColorManager_SupportedIntent( xx_color_manager_v3 *pXXColorManager, uint32_t uRenderIntent ); + void Wayland_XXColorManager_SupportedFeature( xx_color_manager_v3 *pXXColorManager, uint32_t uFeature ); + void Wayland_XXColorManager_SupportedTFNamed( xx_color_manager_v3 *pXXColorManager, uint32_t uTF ); + void Wayland_XXColorManager_SupportedPrimariesNamed( xx_color_manager_v3 *pXXColorManager, uint32_t uPrimaries ); + static const xx_color_manager_v3_listener s_XXColorManagerListener; + CWaylandInputThread m_InputThread; CWaylandConnector m_Connector; @@ -615,11 +645,22 @@ namespace gamescope OwningRc m_pBlackTexture; wp_presentation *m_pPresentation = nullptr; frog_color_management_factory_v1 *m_pFrogColorMgmtFactory = nullptr; + xx_color_manager_v3 *m_pXXColorManager = nullptr; zwp_pointer_constraints_v1 *m_pPointerConstraints = nullptr; zwp_relative_pointer_manager_v1 *m_pRelativePointerManager = nullptr; wp_fractional_scale_manager_v1 *m_pFractionalScaleManager = nullptr; xdg_toplevel_icon_manager_v1 *m_pToplevelIconManager = nullptr; + struct + { + std::vector ePrimaries; + std::vector eTransferFunctions; + std::vector eRenderIntents; + std::vector eFeatures; + + bool bSupportsGamescopeColorManagement = false; // Has everything we want and need? + } m_XXColorManagerFeatures; + std::unordered_map m_pOutputs; libdecor *m_pLibDecor = nullptr; @@ -690,6 +731,13 @@ namespace gamescope .modifiers = WAYLAND_NULL(), .repeat_info = WAYLAND_NULL(), }; + const xx_color_manager_v3_listener CWaylandBackend::s_XXColorManagerListener + { + .supported_intent = WAYLAND_USERDATA_TO_THIS( CWaylandBackend, Wayland_XXColorManager_SupportedIntent ), + .supported_feature = WAYLAND_USERDATA_TO_THIS( CWaylandBackend, Wayland_XXColorManager_SupportedFeature ), + .supported_tf_named = WAYLAND_USERDATA_TO_THIS( CWaylandBackend, Wayland_XXColorManager_SupportedTFNamed ), + .supported_primaries_named = WAYLAND_USERDATA_TO_THIS( CWaylandBackend, Wayland_XXColorManager_SupportedPrimariesNamed ), + }; ////////////////// // CWaylandFb @@ -752,6 +800,7 @@ namespace gamescope CWaylandConnector::CWaylandConnector( CWaylandBackend *pBackend ) : m_pBackend( pBackend ) { + m_HDRInfo.bAlwaysPatchEdid = true; } CWaylandConnector::~CWaylandConnector() @@ -810,18 +859,20 @@ namespace gamescope displaycolorimetry_t *displayColorimetry, EOTF *displayEOTF, displaycolorimetry_t *outputEncodingColorimetry, EOTF *outputEncodingEOTF ) const { - if ( g_bForceHDR10OutputDebug ) + *displayColorimetry = m_DisplayColorimetry; + *displayEOTF = EOTF_Gamma22; + + if ( bHDR10 && GetHDRInfo().IsHDR10() ) { - *displayColorimetry = displaycolorimetry_2020; - *displayEOTF = EOTF_PQ; + // For HDR10 output, expected content colorspace != native colorspace. *outputEncodingColorimetry = displaycolorimetry_2020; - *outputEncodingEOTF = EOTF_PQ; + *outputEncodingEOTF = GetHDRInfo().eOutputEncodingEOTF; } else { - *displayColorimetry = displaycolorimetry_709; - *displayEOTF = EOTF_Gamma22; - *outputEncodingColorimetry = displaycolorimetry_709; + // We always use default 'perceptual' intent, so + // this should be correct for SDR content. + *outputEncodingColorimetry = m_DisplayColorimetry; *outputEncodingEOTF = EOTF_Gamma22; } } @@ -848,7 +899,17 @@ namespace gamescope m_pViewport = wp_viewporter_get_viewport( m_pBackend->GetViewporter(), m_pSurface ); - if ( m_pBackend->GetFrogColorManagementFactory() ) + if ( m_pBackend->GetXXColorManager() ) + { + m_pXXColorManagedSurface = xx_color_manager_v3_get_surface( m_pBackend->GetXXColorManager(), m_pSurface ); + + // Only add the listener for the toplevel to avoid useless spam. + if ( !pParent ) + xx_color_management_surface_v3_add_listener( m_pXXColorManagedSurface, &s_XXColorManagementSurfaceListener, this ); + + UpdateXXPreferredColorManagement(); + } + else if ( m_pBackend->GetFrogColorManagementFactory() ) { m_pFrogColorManagedSurface = frog_color_management_factory_v1_get_color_managed_surface( m_pBackend->GetFrogColorManagementFactory(), m_pSurface ); @@ -913,7 +974,11 @@ namespace gamescope wp_presentation_feedback_add_listener( pFeedback, &s_PresentationFeedbackListener, this ); } - if ( m_pFrogColorManagedSurface ) + if ( m_pXXColorManagedSurface ) + { + // TODO: Actually use this. + } + else if ( m_pFrogColorManagedSurface ) { frog_color_managed_surface_set_render_intent( m_pFrogColorManagedSurface, FROG_COLOR_MANAGED_SURFACE_RENDER_INTENT_PERCEPTUAL ); switch ( oState->eColorspace ) @@ -1167,6 +1232,12 @@ namespace gamescope pHDRInfo->uMaxFrameAverageLuminance = uMaxFullFrameLuminance; pHDRInfo->uMinContentLightLevel = uMinLuminance; + auto *pDisplayColorimetry = &m_pBackend->m_Connector.m_DisplayColorimetry; + pDisplayColorimetry->primaries.r = glm::vec2{ uOutputDisplayPrimaryRedX * 0.00002f, uOutputDisplayPrimaryRedY * 0.00002f }; + pDisplayColorimetry->primaries.g = glm::vec2{ uOutputDisplayPrimaryGreenX * 0.00002f, uOutputDisplayPrimaryGreenY * 0.00002f }; + pDisplayColorimetry->primaries.b = glm::vec2{ uOutputDisplayPrimaryBlueX * 0.00002f, uOutputDisplayPrimaryBlueY * 0.00002f }; + pDisplayColorimetry->white = glm::vec2{ uOutputWhitePointX * 0.00002f, uOutputWhitePointY * 0.00002f }; + xdg_log.infof( "PreferredMetadata: Red: %g %g, Green: %g %g, Blue: %g %g, White: %g %g, Max Luminance: %u nits, Min Luminance: %g nits, Max Full Frame Luminance: %u nits", uOutputDisplayPrimaryRedX * 0.00002, uOutputDisplayPrimaryRedY * 0.00002, uOutputDisplayPrimaryGreenX * 0.00002, uOutputDisplayPrimaryGreenY * 0.00002, @@ -1177,6 +1248,87 @@ namespace gamescope uint32_t( uMaxFullFrameLuminance ) ); } + // + + void CWaylandPlane::Wayland_XXColorManagementSurface_PreferredChanged( xx_color_management_surface_v3 *pColorManagementSurface ) + { + UpdateXXPreferredColorManagement(); + } + + void CWaylandPlane::UpdateXXPreferredColorManagement() + { + if ( m_pParent ) + return; + + xx_image_description_v3 *pImageDescription = xx_color_management_surface_v3_get_preferred( m_pXXColorManagedSurface ); + xx_image_description_info_v3 *pImageDescInfo = xx_image_description_v3_get_information( pImageDescription ); + static const xx_image_description_info_v3_listener s_Listener + { + + }; + xx_image_description_info_v3_add_listener( pImageDescInfo, &s_Listener, this ); + wl_display_roundtrip( m_pBackend->GetDisplay() ); + + xx_image_description_info_v3_destroy( pImageDescInfo ); + xx_image_description_v3_destroy( pImageDescription ); + } + + void CWaylandPlane::Wayland_XXImageDescriptionInfo_Done( xx_image_description_info_v3 *pImageDescInfo ) + { + + } + void CWaylandPlane::Wayland_XXImageDescriptionInfo_ICCFile( xx_image_description_info_v3 *pImageDescInfo, int32_t nICCFd, uint32_t uICCSize ) + { + if ( nICCFd >= 0 ) + close( nICCFd ); + } + void CWaylandPlane::Wayland_XXImageDescriptionInfo_Primaries( xx_image_description_info_v3 *pImageDescInfo, int32_t nRedX, int32_t nRedY, int32_t nGreenX, int32_t nGreenY, int32_t nBlueX, int32_t nBlueY, int32_t nWhiteX, int32_t nWhiteY ) + { + + } + void CWaylandPlane::Wayland_XXImageDescriptionInfo_PrimariesNamed( xx_image_description_info_v3 *pImageDescInfo, uint32_t uPrimaries ) + { + + } + void CWaylandPlane::Wayland_XXImageDescriptionInfo_TFPower( xx_image_description_info_v3 *pImageDescInfo, uint32_t uExp) + { + + } + void CWaylandPlane::Wayland_XXImageDescriptionInfo_TFNamed( xx_image_description_info_v3 *pImageDescInfo, uint32_t uTF) + { + auto *pHDRInfo = &m_pBackend->m_Connector.m_HDRInfo; + pHDRInfo->bExposeHDRSupport = ( cv_hdr_enabled && uTF == XX_COLOR_MANAGER_V3_TRANSFER_FUNCTION_ST2084_PQ ); + pHDRInfo->eOutputEncodingEOTF = ( cv_hdr_enabled && uTF == XX_COLOR_MANAGER_V3_TRANSFER_FUNCTION_ST2084_PQ ) ? EOTF_PQ : EOTF_Gamma22; + } + void CWaylandPlane::Wayland_XXImageDescriptionInfo_Luminances( xx_image_description_info_v3 *pImageDescInfo, uint32_t uMinLum, uint32_t uMaxLum, uint32_t uRefLum ) + { + + } + void CWaylandPlane::Wayland_XXImageDescriptionInfo_TargetPrimaries( xx_image_description_info_v3 *pImageDescInfo, int32_t nRedX, int32_t nRedY, int32_t nGreenX, int32_t nGreenY, int32_t nBlueX, int32_t nBlueY, int32_t nWhiteX, int32_t nWhiteY ) + { + auto *pDisplayColorimetry = &m_pBackend->m_Connector.m_DisplayColorimetry; + pDisplayColorimetry->primaries.r = glm::vec2{ nRedX / 10000.0f, nRedY / 10000.0f }; + pDisplayColorimetry->primaries.g = glm::vec2{ nGreenX / 10000.0f, nGreenY / 10000.0f }; + pDisplayColorimetry->primaries.b = glm::vec2{ nBlueX / 10000.0f, nBlueY / 10000.0f }; + pDisplayColorimetry->white = glm::vec2{ nWhiteX / 10000.0f, nWhiteY / 10000.0f }; + } + void CWaylandPlane::Wayland_XXImageDescriptionInfo_TargetLuminance( xx_image_description_info_v3 *pImageDescInfo, uint32_t uMinLum, uint32_t uMaxLum ) + { + + } + void CWaylandPlane::Wayland_XXImageDescriptionInfo_Target_MaxCLL( xx_image_description_info_v3 *pImageDescInfo, uint32_t uMaxCLL ) + { + auto *pHDRInfo = &m_pBackend->m_Connector.m_HDRInfo; + pHDRInfo->uMaxContentLightLevel = uMaxCLL; + } + void CWaylandPlane::Wayland_XXImageDescriptionInfo_Target_MaxFALL( xx_image_description_info_v3 *pImageDescInfo, uint32_t uMaxFALL ) + { + auto *pHDRInfo = &m_pBackend->m_Connector.m_HDRInfo; + pHDRInfo->uMaxFrameAverageLuminance = uMaxFALL; + } + + // + void CWaylandPlane::Wayland_FractionalScale_PreferredScale( wp_fractional_scale_v1 *pFractionalScale, uint32_t uScale ) { if ( m_uFractionalScale != uScale ) @@ -1258,6 +1410,39 @@ namespace gamescope wl_registry_destroy( pRegistry ); pRegistry = nullptr; + if ( m_pXXColorManager ) + { + m_XXColorManagerFeatures.bSupportsGamescopeColorManagement = [this]() -> bool + { + // Features + if ( !Algorithm::Contains( m_XXColorManagerFeatures.eFeatures, XX_COLOR_MANAGER_V3_FEATURE_PARAMETRIC ) ) + return false; + if ( !Algorithm::Contains( m_XXColorManagerFeatures.eFeatures, XX_COLOR_MANAGER_V3_FEATURE_SET_PRIMARIES ) ) + return false; + if ( !Algorithm::Contains( m_XXColorManagerFeatures.eFeatures, XX_COLOR_MANAGER_V3_FEATURE_SET_MASTERING_DISPLAY_PRIMARIES ) ) + return false; + if ( !Algorithm::Contains( m_XXColorManagerFeatures.eFeatures, XX_COLOR_MANAGER_V3_FEATURE_EXTENDED_TARGET_VOLUME ) ) + return false; + if ( !Algorithm::Contains( m_XXColorManagerFeatures.eFeatures, XX_COLOR_MANAGER_V3_FEATURE_SET_LUMINANCES ) ) + return false; + + // Transfer Functions + if ( !Algorithm::Contains( m_XXColorManagerFeatures.eTransferFunctions, XX_COLOR_MANAGER_V3_TRANSFER_FUNCTION_SRGB ) ) + return false; + if ( !Algorithm::Contains( m_XXColorManagerFeatures.eTransferFunctions, XX_COLOR_MANAGER_V3_TRANSFER_FUNCTION_ST2084_PQ ) ) + return false; + // TODO: Need scRGB + + // Primaries + if ( !Algorithm::Contains( m_XXColorManagerFeatures.ePrimaries, XX_COLOR_MANAGER_V3_PRIMARIES_SRGB ) ) + return false; + if ( !Algorithm::Contains( m_XXColorManagerFeatures.ePrimaries, XX_COLOR_MANAGER_V3_PRIMARIES_BT2020 ) ) + return false; + + return true; + }(); + } + m_pLibDecor = libdecor_new( m_pDisplay, &s_LibDecorInterface ); if ( !m_pLibDecor ) { @@ -1829,7 +2014,7 @@ namespace gamescope bool CWaylandBackend::SupportsColorManagement() const { - return m_pFrogColorMgmtFactory != nullptr; + return m_pFrogColorMgmtFactory != nullptr || ( m_pXXColorManager != nullptr && m_XXColorManagerFeatures.bSupportsGamescopeColorManagement ); } void CWaylandBackend::UpdateCursor() @@ -1947,6 +2132,11 @@ namespace gamescope { m_pFrogColorMgmtFactory = (frog_color_management_factory_v1 *)wl_registry_bind( pRegistry, uName, &frog_color_management_factory_v1_interface, 1u ); } + else if ( !strcmp( pInterface, xx_color_manager_v3_interface.name ) ) + { + m_pXXColorManager = (xx_color_manager_v3 *)wl_registry_bind( pRegistry, uName, &xx_color_manager_v3_interface, 1u ); + xx_color_manager_v3_add_listener( m_pXXColorManager, &s_XXColorManagerListener, this ); + } else if ( !strcmp( pInterface, zwp_pointer_constraints_v1_interface.name ) ) { m_pPointerConstraints = (zwp_pointer_constraints_v1 *)wl_registry_bind( pRegistry, uName, &zwp_pointer_constraints_v1_interface, 1u ); @@ -2064,6 +2254,25 @@ namespace gamescope UpdateCursor(); } + // XX Color Manager + + void CWaylandBackend::Wayland_XXColorManager_SupportedIntent( xx_color_manager_v3 *pXXColorManager, uint32_t uRenderIntent ) + { + m_XXColorManagerFeatures.eRenderIntents.push_back( static_cast( uRenderIntent ) ); + } + void CWaylandBackend::Wayland_XXColorManager_SupportedFeature( xx_color_manager_v3 *pXXColorManager, uint32_t uFeature ) + { + m_XXColorManagerFeatures.eFeatures.push_back( static_cast( uFeature ) ); + } + void CWaylandBackend::Wayland_XXColorManager_SupportedTFNamed( xx_color_manager_v3 *pXXColorManager, uint32_t uTF ) + { + m_XXColorManagerFeatures.eTransferFunctions.push_back( static_cast( uTF ) ); + } + void CWaylandBackend::Wayland_XXColorManager_SupportedPrimariesNamed( xx_color_manager_v3 *pXXColorManager, uint32_t uPrimaries ) + { + m_XXColorManagerFeatures.ePrimaries.push_back( static_cast( uPrimaries ) ); + } + /////////////////////// // CWaylandInputThread /////////////////////// diff --git a/src/backend.h b/src/backend.h index 4f91fe7..85783c9 100644 --- a/src/backend.h +++ b/src/backend.h @@ -44,6 +44,7 @@ namespace gamescope // target/mapping values for the display brightness for undocking from a HDR display, // but don't want to expose HDR there as it is not good. bool bExposeHDRSupport = false; + bool bAlwaysPatchEdid = false; // The output encoding to use for HDR output. // For typical HDR10 displays, this will be PQ. @@ -62,7 +63,7 @@ namespace gamescope bool ShouldPatchEDID() const { - return IsHDRG22(); + return bAlwaysPatchEdid || IsHDRG22(); } bool IsHDR10() const -- 2.45.2 From 691c72b319efd2c4dcf73aa44e89e5f117cd00c3 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Wed, 24 Jul 2024 17:51:55 +0100 Subject: [PATCH 14/19] steamcompmgr: Fix Disable Frame Limit functionality --- src/steamcompmgr.cpp | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index 60ddbbe..1de35e7 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -794,33 +794,18 @@ static void _update_app_target_refresh_cycle() if ( !GetBackend()->GetCurrentConnector() ) return; - static gamescope::GamescopeScreenType last_type; - static int last_target_fps; - static bool first = true; - gamescope::GamescopeScreenType type = GetBackend()->GetCurrentConnector()->GetScreenType(); - int target_fps = g_nCombinedAppRefreshCycleOverride[type]; - if ( !first && type == last_type && last_target_fps == target_fps ) - { - return; - } + int target_fps = g_nCombinedAppRefreshCycleOverride[type]; - last_type = type; - last_target_fps = target_fps; - first = false; + g_nDynamicRefreshRate[ type ] = 0; + g_nSteamCompMgrTargetFPS = 0; if ( !target_fps ) { - g_nDynamicRefreshRate[ type ] = 0; - g_nSteamCompMgrTargetFPS = 0; return; } - auto rates = GetBackend()->GetCurrentConnector()->GetValidDynamicRefreshRates(); - - g_nDynamicRefreshRate[ type ] = 0; - if ( g_nCombinedAppRefreshCycleChangeFPS[ type ] ) { g_nSteamCompMgrTargetFPS = target_fps; @@ -828,6 +813,8 @@ static void _update_app_target_refresh_cycle() if ( g_nCombinedAppRefreshCycleChangeRefresh[ type ] ) { + auto rates = GetBackend()->GetCurrentConnector()->GetValidDynamicRefreshRates(); + // Find highest mode to do refresh doubling with. for ( auto rate = rates.rbegin(); rate != rates.rend(); rate++ ) { -- 2.45.2 From 69610ec52429fecbe94c4c042cc42ab43e0491f8 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Wed, 24 Jul 2024 18:22:11 +0100 Subject: [PATCH 15/19] steamcompmgr: Relative mouse fixes and improvements --- src/steamcompmgr.cpp | 21 ++++++++++++++++++--- src/steamcompmgr.hpp | 3 +++ src/wlserver.cpp | 9 ++++++--- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index 1de35e7..9c98f9c 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -94,6 +94,10 @@ #include "Utils/Process.h" #include "Utils/Algorithm.h" +#include "wlr_begin.hpp" +#include "wlr/types/wlr_pointer_constraints_v1.h" +#include "wlr_end.hpp" + #if HAVE_AVIF #include "avif/avif.h" #endif @@ -1383,8 +1387,19 @@ MouseCursor::MouseCursor(xwayland_ctx_t *ctx) void MouseCursor::UpdatePosition() { wlserver_lock(); - m_x = wlserver.mouse_surface_cursorx; - m_y = wlserver.mouse_surface_cursory; + struct wlr_pointer_constraint_v1 *pConstraint = wlserver.GetCursorConstraint(); + if ( pConstraint && pConstraint->current.cursor_hint.enabled ) + { + m_x = pConstraint->current.cursor_hint.x; + m_y = pConstraint->current.cursor_hint.y; + m_bConstrained = true; + } + else + { + m_x = wlserver.mouse_surface_cursorx; + m_y = wlserver.mouse_surface_cursory; + m_bConstrained = false; + } wlserver_unlock(); } @@ -7600,7 +7615,7 @@ steamcompmgr_main(int argc, char **argv) ( global_focus.cursor && global_focus.cursor->imageEmpty() ) && ( !window_is_steam( global_focus.inputFocusWindow ) ); - const bool bHasPointerConstraint = wlserver.HasMouseConstraint(); // atomic, no lock needed + const bool bHasPointerConstraint = global_focus.cursor->IsConstrained(); uint32_t uAppId = global_focus.inputFocusWindow ? global_focus.inputFocusWindow->appID diff --git a/src/steamcompmgr.hpp b/src/steamcompmgr.hpp index 7c2da8c..b009ac7 100644 --- a/src/steamcompmgr.hpp +++ b/src/steamcompmgr.hpp @@ -95,6 +95,8 @@ public: void GetDesiredSize( int& nWidth, int &nHeight ); void checkSuspension(); + + bool IsConstrained() const { return m_bConstrained; } private: bool getTexture(); @@ -102,6 +104,7 @@ private: void updateCursorFeedback( bool bForce = false ); int m_x = 0, m_y = 0; + bool m_bConstrained = false; int m_hotspotX = 0, m_hotspotY = 0; gamescope::OwningRc m_texture; diff --git a/src/wlserver.cpp b/src/wlserver.cpp index 1852be9..bdacff4 100644 --- a/src/wlserver.cpp +++ b/src/wlserver.cpp @@ -2243,7 +2243,9 @@ static void wlserver_warp_to_constraint_hint() double sx = pConstraint->current.cursor_hint.x; double sy = pConstraint->current.cursor_hint.y; - wlserver_mousewarp( sx, sy, 0, true ); + wlserver.mouse_surface_cursorx = sx; + wlserver.mouse_surface_cursory = sy; + wlr_seat_pointer_warp( wlserver.wlr.seat, sx, sy ); } } @@ -2256,6 +2258,8 @@ static void wlserver_update_cursor_constraint() { wlserver.mouse_constraint_requires_warp = false; + wlserver_warp_to_constraint_hint(); + if (!pixman_region32_contains_point(pRegion, floor(wlserver.mouse_surface_cursorx), floor(wlserver.mouse_surface_cursory), NULL)) { int nboxes; @@ -2265,8 +2269,7 @@ static void wlserver_update_cursor_constraint() wlserver.mouse_surface_cursorx = std::clamp( wlserver.mouse_surface_cursorx, boxes[0].x1, boxes[0].x2); wlserver.mouse_surface_cursory = std::clamp( wlserver.mouse_surface_cursory, boxes[0].y1, boxes[0].y2); - wlr_seat_pointer_notify_motion( wlserver.wlr.seat, 0, wlserver.mouse_surface_cursorx, wlserver.mouse_surface_cursory ); - wlr_seat_pointer_notify_frame( wlserver.wlr.seat ); + wlr_seat_pointer_warp( wlserver.wlr.seat, wlserver.mouse_surface_cursorx, wlserver.mouse_surface_cursory ); } } } -- 2.45.2 From 1f15714bc37a246c7c7d175970bef5070b7d5649 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Wed, 24 Jul 2024 18:30:11 +0100 Subject: [PATCH 16/19] steamcompmgr: Fix return value of avifImageRGBToYUV being ignored --- src/steamcompmgr.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index 9c98f9c..1861f9d 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -2712,7 +2712,11 @@ paint_all(bool async) rgbAvifImage.pixels = (uint8_t *)imageData.data(); rgbAvifImage.rowBytes = g_nOutputWidth * kCompCnt * sizeof( uint16_t ); - avifImageRGBToYUV( pAvifImage, &rgbAvifImage ); // Not really! See Matrix Coefficients IDENTITY above. + if ( ( avifResult = avifImageRGBToYUV( pAvifImage, &rgbAvifImage ) ) != AVIF_RESULT_OK ) // Not really! See Matrix Coefficients IDENTITY above. + { + xwm_log.errorf( "Failed to convert RGB to YUV: %u", avifResult ); + return; + } avifEncoder *pEncoder = avifEncoderCreate(); defer( avifEncoderDestroy( pEncoder ) ); -- 2.45.2 From bfebd15dd6b3141e1ed5ee4bf768dc0d50c9660f Mon Sep 17 00:00:00 2001 From: psykose Date: Sat, 6 Jul 2024 20:52:50 +0200 Subject: [PATCH 17/19] utils: include limits.h for PATH_MAX --- src/Utils/Process.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Utils/Process.cpp b/src/Utils/Process.cpp index 32c52f1..9e3f758 100644 --- a/src/Utils/Process.cpp +++ b/src/Utils/Process.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include -- 2.45.2 From 7fe73df4bcf71579203332baa2f3cf47506bc560 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Wed, 24 Jul 2024 12:40:01 +0200 Subject: [PATCH 18/19] wayland_backend: round surface size towards infinity Otherwise, if the scale is larger than the texture size, the surface size would be 0. Signed-off-by: Julian Orth --- src/Backends/WaylandBackend.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Backends/WaylandBackend.cpp b/src/Backends/WaylandBackend.cpp index 1a09acc..cc76f62 100644 --- a/src/Backends/WaylandBackend.cpp +++ b/src/Backends/WaylandBackend.cpp @@ -1083,8 +1083,8 @@ namespace gamescope .flSrcY = 0.0, .flSrcWidth = double( pLayer->tex->width() ), .flSrcHeight = double( pLayer->tex->height() ), - .nDstWidth = int32_t( pLayer->tex->width() / double( pLayer->scale.x ) ), - .nDstHeight = int32_t( pLayer->tex->height() / double( pLayer->scale.y ) ), + .nDstWidth = int32_t( ceil( pLayer->tex->width() / double( pLayer->scale.x ) ) ), + .nDstHeight = int32_t( ceil( pLayer->tex->height() / double( pLayer->scale.y ) ) ), .eColorspace = pLayer->colorspace, .bOpaque = pLayer->zpos == g_zposBase, .uFractionalScale = GetScale(), -- 2.45.2 From 7ae5e0d2a75de06e267c47ca3cd3cddedd1d7416 Mon Sep 17 00:00:00 2001 From: sharkautarch <128002472+sharkautarch@users.noreply.github.com> Date: Wed, 24 Jul 2024 13:42:21 -0400 Subject: [PATCH 19/19] color_helpers: Clang warning fixes --- src/color_bench.cpp | 8 ++++---- src/color_helpers.cpp | 11 +++++++++-- src/color_helpers.h | 11 ++++++++++- src/color_helpers_impl.h | 20 ++++++++++++++++++++ src/color_tests.cpp | 6 +++--- src/rendervulkan.hpp | 9 +++++---- src/steamcompmgr.cpp | 6 +++++- 7 files changed, 56 insertions(+), 15 deletions(-) create mode 100644 src/color_helpers_impl.h diff --git a/src/color_bench.cpp b/src/color_bench.cpp index 9c9d986..1058986 100644 --- a/src/color_bench.cpp +++ b/src/color_bench.cpp @@ -4,10 +4,10 @@ #include #include "Utils/Algorithm.h" -#include "color_helpers.h" +#include "color_helpers_impl.h" -const uint32_t nLutSize1d = 4096; -const uint32_t nLutEdgeSize3d = 17; +using color_bench::nLutEdgeSize3d; +using color_bench::nLutSize1d; uint16_t lut1d[nLutSize1d*4]; uint16_t lut3d[nLutEdgeSize3d*nLutEdgeSize3d*nLutEdgeSize3d*4]; @@ -38,7 +38,7 @@ static void BenchmarkCalcColorTransform(EOTF inputEOTF, benchmark::State &state) float flGain = 1.0f; for (auto _ : state) { - calcColorTransform( &lut1d_float, nLutSize1d, &lut3d_float, nLutEdgeSize3d, inputColorimetry, inputEOTF, + calcColorTransform( &lut1d_float, nLutSize1d, &lut3d_float, inputColorimetry, inputEOTF, outputEncodingColorimetry, EOTF_Gamma22, destVirtualWhite, k_EChromaticAdapatationMethod_XYZ, colorMapping, nightmode, tonemapping, nullptr, flGain ); diff --git a/src/color_helpers.cpp b/src/color_helpers.cpp index 18d3ee6..2075eca 100644 --- a/src/color_helpers.cpp +++ b/src/color_helpers.cpp @@ -1,4 +1,5 @@ -#include "color_helpers.h" +#define COLOR_HELPERS_CPP +#include "color_helpers_impl.h" #include #include @@ -213,7 +214,11 @@ inline void lerp_rgb(float* out, const float* a, const float* b, const float* c, inline float ClampAndSanitize( float a, float min, float max ) { +#ifndef __FAST_MATH__ return std::isfinite( a ) ? std::min(std::max(min, a), max) : min; +#else + return std::min(std::max(min, a), max); +#endif } // Adapted from: @@ -665,8 +670,9 @@ inline T applyShaper( const T & input, EOTF source, EOTF dest, const tonemapping bool g_bHuePreservationWhenClipping = false; +template void calcColorTransform( lut1d_t * pShaper, int nLutSize1d, - lut3d_t * pLut3d, int nLutEdgeSize3d, + lut3d_t * pLut3d, const displaycolorimetry_t & source, EOTF sourceEOTF, const displaycolorimetry_t & dest, EOTF destEOTF, const glm::vec2 & destVirtualWhite, EChromaticAdaptationMethod eMethod, @@ -679,6 +685,7 @@ void calcColorTransform( lut1d_t * pShaper, int nLutSize1d, // The 3d lut should be considered a 'matched' pair where the transform is only complete // when applying both. I.e., you can put ANY transform in here, and it should work. + static constexpr int32_t nLutEdgeSize3d = static_cast(lutEdgeSize3d); if ( pShaper ) { float flScale = 1.f / ( (float) nLutSize1d - 1.f ); diff --git a/src/color_helpers.h b/src/color_helpers.h index 319bfc7..66213e1 100644 --- a/src/color_helpers.h +++ b/src/color_helpers.h @@ -363,14 +363,23 @@ bool LoadCubeLut( lut3d_t * lut3d, const char * filename ); // If the white points differ, this performs an absolute colorimetric match // Look luts are optional, but if specified applied in the sourceEOTF space +template void calcColorTransform( lut1d_t * pShaper, int nLutSize1d, - lut3d_t * pLut3d, int nLutEdgeSize3d, + lut3d_t * pLut3d, const displaycolorimetry_t & source, EOTF sourceEOTF, const displaycolorimetry_t & dest, EOTF destEOTF, const glm::vec2 & destVirtualWhite, EChromaticAdaptationMethod eMethod, const colormapping_t & mapping, const nightmode_t & nightmode, const tonemapping_t & tonemapping, const lut3d_t * pLook, float flGain ); +#define REGISTER_LUT_EDGE_SIZE(size) template void calcColorTransform<(size)>( lut1d_t * pShaper, int nLutSize1d, \ + lut3d_t * pLut3d, \ + const displaycolorimetry_t & source, EOTF sourceEOTF, \ + const displaycolorimetry_t & dest, EOTF destEOTF, \ + const glm::vec2 & destVirtualWhite, EChromaticAdaptationMethod eMethod, \ + const colormapping_t & mapping, const nightmode_t & nightmode, const tonemapping_t & tonemapping, \ + const lut3d_t * pLook, float flGain ) + // Build colorimetry and a gamut mapping for the given SDR configuration // Note: the output colorimetry will use the native display's white point // Only the color gamut will change diff --git a/src/color_helpers_impl.h b/src/color_helpers_impl.h new file mode 100644 index 0000000..3e8c2d5 --- /dev/null +++ b/src/color_helpers_impl.h @@ -0,0 +1,20 @@ +#pragma once +#include "color_helpers.h" + +namespace rendervulkan { + static constexpr uint32_t s_nLutEdgeSize3d = 17; + static constexpr uint32_t s_nLutSize1d = 4096; +} + +namespace color_bench { + static constexpr uint32_t nLutEdgeSize3d = 17; + static constexpr uint32_t nLutSize1d = 4096; +} + +namespace ns_color_tests { + [[maybe_unused]] static constexpr uint32_t nLutEdgeSize3d = 17; +} + +#ifdef COLOR_HELPERS_CPP +REGISTER_LUT_EDGE_SIZE(rendervulkan::s_nLutEdgeSize3d); +#endif \ No newline at end of file diff --git a/src/color_tests.cpp b/src/color_tests.cpp index 66aae90..2d682bf 100644 --- a/src/color_tests.cpp +++ b/src/color_tests.cpp @@ -1,12 +1,12 @@ #include "color_helpers.h" #include -#include +//#include #include /* +using ns_color_tests::nLutEdgeSize3d; const uint32_t nLutSize1d = 4096; -const uint32_t nLutEdgeSize3d = 17; uint16_t lut1d[nLutSize1d*4]; uint16_t lut3d[nLutEdgeSize3d*nLutEdgeSize3d*nLutEdgeSize3d*4]; @@ -36,7 +36,7 @@ static void BenchmarkCalcColorTransform(EOTF inputEOTF, benchmark::State &state) float flGain = 1.0f; for (auto _ : state) { - calcColorTransform( &lut1d_float, nLutSize1d, &lut3d_float, nLutEdgeSize3d, inputColorimetry, inputEOTF, + calcColorTransform( &lut1d_float, nLutSize1d, &lut3d_float, inputColorimetry, inputEOTF, outputEncodingColorimetry, EOTF_Gamma22, colorMapping, nightmode, tonemapping, nullptr, flGain ); for ( size_t i=0, end = lut1d_float.dataR.size(); i #include "main.hpp" -#include "color_helpers.h" + #include "gamescope_shared.h" #include "backend.h" @@ -415,7 +415,7 @@ struct wlr_renderer *vulkan_renderer_create( void ); using mat3x4 = std::array, 3>; -#include "color_helpers.h" +#include "color_helpers_impl.h" struct gamescope_color_mgmt_t { @@ -453,8 +453,9 @@ struct gamescope_color_mgmt_t bool operator != (const gamescope_color_mgmt_t&) const = default; }; -static constexpr uint32_t s_nLutEdgeSize3d = 17; -static constexpr uint32_t s_nLutSize1d = 4096; +//namespace members from "color_helpers_impl.h": +using rendervulkan::s_nLutEdgeSize3d; +using rendervulkan::s_nLutSize1d; struct gamescope_color_mgmt_luts { diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index 1861f9d..4a9d567 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -315,7 +315,7 @@ create_color_mgmt_luts(const gamescope_color_mgmt_t& newColorMgmt, gamescope_col buildPQColorimetry( &inputColorimetry, &colorMapping, displayColorimetry ); } - calcColorTransform( &g_tmpLut1d, s_nLutSize1d, &g_tmpLut3d, s_nLutEdgeSize3d, inputColorimetry, inputEOTF, + calcColorTransform( &g_tmpLut1d, s_nLutSize1d, &g_tmpLut3d, inputColorimetry, inputEOTF, outputEncodingColorimetry, newColorMgmt.outputEncodingEOTF, newColorMgmt.outputVirtualWhite, newColorMgmt.chromaticAdaptationMode, colorMapping, newColorMgmt.nightmode, tonemapping, pLook, flGain ); @@ -5029,7 +5029,11 @@ steamcompmgr_latch_frame_done( steamcompmgr_win_t *w, uint64_t vblank_idx ) static inline float santitize_float( float f ) { +#ifndef __FAST_MATH__ return ( std::isfinite( f ) ? f : 0.f ); +#else + return f; +#endif } static void -- 2.45.2