From f02f111d1cde80cbb9b09ff5803f220f8a04e02d Mon Sep 17 00:00:00 2001 From: Naveen Kumar Date: Wed, 24 Jun 2026 08:01:13 +0000 Subject: [PATCH] DRMBackend: Add support for the IN_FORMATS_ASYNC drm property Assisted-by: Claude:claude-sonnet-4.6 Signed-off-by: Naveen Kumar --- src/Backends/DRMBackend.cpp | 77 +++++++++++++++++++++++++++++-------- 1 file changed, 62 insertions(+), 15 deletions(-) diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp index 134e19ac5f..607554b04a 100644 --- a/src/Backends/DRMBackend.cpp +++ b/src/Backends/DRMBackend.cpp @@ -110,6 +110,7 @@ struct drm_t { uint64_t cursor_width, cursor_height; bool allow_modifiers; struct wlr_drm_format_set formats; + struct wlr_drm_format_set formats_async; std::vector< std::unique_ptr< gamescope::CDRMPlane > > planes; std::vector< std::unique_ptr< gamescope::CDRMCRTC > > crtcs; @@ -120,6 +121,7 @@ struct drm_t { gamescope::CDRMConnector *pConnector; struct wlr_drm_format_set primary_formats; + struct wlr_drm_format_set primary_formats_async; drmModeAtomicReq *req; uint32_t flags; @@ -300,6 +302,7 @@ namespace gamescope std::optional type; // Immutable std::optional IN_FORMATS; // Immutable + std::optional IN_FORMATS_ASYNC; // Immutable std::optional FB_ID; std::optional IN_FENCE_FD; @@ -659,7 +662,7 @@ static gamescope::CDRMCRTC *find_crtc_for_connector( struct drm_t *drm, gamescop return nullptr; } -static bool get_plane_formats( struct drm_t *drm, gamescope::CDRMPlane *pPlane, struct wlr_drm_format_set *pFormatSet ) +static bool get_plane_formats( struct drm_t *drm, gamescope::CDRMPlane *pPlane, struct wlr_drm_format_set *pFormatSet, bool bAsync ) { for ( uint32_t i = 0; i < pPlane->GetModePlane()->count_formats; i++ ) { @@ -667,14 +670,22 @@ static bool get_plane_formats( struct drm_t *drm, gamescope::CDRMPlane *pPlane, wlr_drm_format_set_add( pFormatSet, uFormat, DRM_FORMAT_MOD_INVALID ); } - if ( pPlane->GetProperties().IN_FORMATS ) + const std::optional *pInFormats = &pPlane->GetProperties().IN_FORMATS; + const char *pszInFormatsName = "IN_FORMATS"; + if ( bAsync && pPlane->GetProperties().IN_FORMATS_ASYNC ) { - const uint64_t ulBlobId = pPlane->GetProperties().IN_FORMATS->GetCurrentValue(); + pInFormats = &pPlane->GetProperties().IN_FORMATS_ASYNC; + pszInFormatsName = "IN_FORMATS_ASYNC"; + } + + if ( *pInFormats ) + { + const uint64_t ulBlobId = ( *pInFormats )->GetCurrentValue(); drmModePropertyBlobRes *pBlob = drmModeGetPropertyBlob( drm->fd, ulBlobId ); if ( !pBlob ) { - drm_log.errorf_errno("drmModeGetPropertyBlob(IN_FORMATS) failed"); + drm_log.errorf_errno("drmModeGetPropertyBlob(%s) failed", pszInFormatsName); return false; } defer( drmModeFreePropertyBlob( pBlob ) ); @@ -712,6 +723,14 @@ static uint32_t pick_plane_format( const struct wlr_drm_format_set *formats, uin return result; } +static uint32_t pick_preferred_plane_format( const struct wlr_drm_format_set *pPreferredFormats, const struct wlr_drm_format_set *pFallbackFormats, uint32_t Xformat, uint32_t Aformat ) +{ + uint32_t fmt = pick_plane_format( pPreferredFormats, Xformat, Aformat ); + if ( fmt == DRM_FORMAT_INVALID && pFallbackFormats != pPreferredFormats ) + fmt = pick_plane_format( pFallbackFormats, Xformat, Aformat ); + return fmt; +} + /* Pick a primary plane that can be connected to the chosen CRTC. */ static gamescope::CDRMPlane *find_primary_plane(struct drm_t *drm) { @@ -1356,7 +1375,10 @@ bool init_drm(struct drm_t *drm, int width, int height, int refresh) // Fetch formats which can be scanned out for ( std::unique_ptr< gamescope::CDRMPlane > &pPlane : drm->planes ) { - if ( !get_plane_formats( drm, pPlane.get(), &drm->formats ) ) + if ( !get_plane_formats( drm, pPlane.get(), &drm->formats, false ) ) + return false; + + if ( !get_plane_formats( drm, pPlane.get(), &drm->formats_async, true ) ) return false; } @@ -1370,7 +1392,12 @@ bool init_drm(struct drm_t *drm, int width, int height, int refresh) return false; } - if ( !get_plane_formats( drm, drm->pPrimaryPlane, &drm->primary_formats ) ) + if ( !get_plane_formats( drm, drm->pPrimaryPlane, &drm->primary_formats, false ) ) + { + return false; + } + + if ( !get_plane_formats( drm, drm->pPrimaryPlane, &drm->primary_formats_async, true ) ) { return false; } @@ -1384,11 +1411,15 @@ bool init_drm(struct drm_t *drm, int width, int height, int refresh) // 2. When compositing HDR content as a fallback when we undock, it avoids introducing // a bunch of horrible banding when going to G2.2 curve. // It ensures that we can dither that. - g_nDRMFormat = pick_plane_format(&drm->primary_formats, DRM_FORMAT_XRGB2101010, DRM_FORMAT_ARGB2101010); + const wlr_drm_format_set *pPrimaryPreferredFormats = &drm->primary_formats; + if ( g_bSupportsAsyncFlips && drm->primary_formats_async.len > 0 ) + pPrimaryPreferredFormats = &drm->primary_formats_async; + + g_nDRMFormat = pick_preferred_plane_format( pPrimaryPreferredFormats, &drm->primary_formats, DRM_FORMAT_XRGB2101010, DRM_FORMAT_ARGB2101010 ); if ( g_nDRMFormat == DRM_FORMAT_INVALID ) { - g_nDRMFormat = pick_plane_format(&drm->primary_formats, DRM_FORMAT_XBGR2101010, DRM_FORMAT_ABGR2101010); + g_nDRMFormat = pick_preferred_plane_format( pPrimaryPreferredFormats, &drm->primary_formats, DRM_FORMAT_XBGR2101010, DRM_FORMAT_ABGR2101010 ); if ( g_nDRMFormat == DRM_FORMAT_INVALID ) { - g_nDRMFormat = pick_plane_format(&drm->primary_formats, DRM_FORMAT_XRGB8888, DRM_FORMAT_ARGB8888); + g_nDRMFormat = pick_preferred_plane_format( pPrimaryPreferredFormats, &drm->primary_formats, DRM_FORMAT_XRGB8888, DRM_FORMAT_ARGB8888 ); if ( g_nDRMFormat == DRM_FORMAT_INVALID ) { drm_log.errorf("Primary plane doesn't support any formats >= 8888"); return false; @@ -1397,12 +1428,16 @@ bool init_drm(struct drm_t *drm, int width, int height, int refresh) } if (have_overlay_planes(drm)) { + const wlr_drm_format_set *pOverlayPreferredFormats = &drm->formats; + if ( g_bSupportsAsyncFlips && drm->formats_async.len > 0 ) + pOverlayPreferredFormats = &drm->formats_async; + // ARGB8888 is the Xformat and AFormat here in this function as we want transparent overlay - g_nDRMFormatOverlay = pick_plane_format(&drm->formats, DRM_FORMAT_ARGB2101010, DRM_FORMAT_ARGB2101010); + g_nDRMFormatOverlay = pick_preferred_plane_format( pOverlayPreferredFormats, &drm->formats, DRM_FORMAT_ARGB2101010, DRM_FORMAT_ARGB2101010 ); if ( g_nDRMFormatOverlay == DRM_FORMAT_INVALID ) { - g_nDRMFormatOverlay = pick_plane_format(&drm->formats, DRM_FORMAT_ABGR2101010, DRM_FORMAT_ABGR2101010); + g_nDRMFormatOverlay = pick_preferred_plane_format( pOverlayPreferredFormats, &drm->formats, DRM_FORMAT_ABGR2101010, DRM_FORMAT_ABGR2101010 ); if ( g_nDRMFormatOverlay == DRM_FORMAT_INVALID ) { - g_nDRMFormatOverlay = pick_plane_format(&drm->formats, DRM_FORMAT_ARGB8888, DRM_FORMAT_ARGB8888); + g_nDRMFormatOverlay = pick_preferred_plane_format( pOverlayPreferredFormats, &drm->formats, DRM_FORMAT_ARGB8888, DRM_FORMAT_ARGB8888 ); if ( g_nDRMFormatOverlay == DRM_FORMAT_INVALID ) { drm_log.errorf("Overlay plane doesn't support any formats >= 8888"); return false; @@ -1585,7 +1620,9 @@ void finish_drm(struct drm_t *drm) free(drm->device_name); wlr_drm_format_set_finish( &drm->formats ); + wlr_drm_format_set_finish( &drm->formats_async ); wlr_drm_format_set_finish( &drm->primary_formats ); + wlr_drm_format_set_finish( &drm->primary_formats_async ); drm->m_FbIdsInRequest.clear(); { std::unique_lock lock( drm->m_QueuedFbIdsMutex ); @@ -1627,9 +1664,12 @@ gamescope::OwningRc drm_fbid_from_dmabuf( struct drm_t *d gamescope::OwningRc pBackendFb; uint32_t fb_id = 0; - if ( !wlr_drm_format_set_has( &drm->formats, dma_buf->format, dma_buf->modifier ) ) + const bool bAsync = drm->flags & DRM_MODE_PAGE_FLIP_ASYNC; + const wlr_drm_format_set *pFormatSet = bAsync ? &drm->formats_async : &drm->formats; + + if ( !wlr_drm_format_set_has( pFormatSet, dma_buf->format, dma_buf->modifier ) ) { - drm_log.errorf( "Cannot import FB to DRM: format 0x%" PRIX32 " and modifier 0x%" PRIX64 " not supported for scan-out", dma_buf->format, dma_buf->modifier ); + drm_log.errorf( "Cannot import FB to DRM: format 0x%" PRIX32 " and modifier 0x%" PRIX64 " not supported for %s scan-out", dma_buf->format, dma_buf->modifier, bAsync ? "async" : "sync" ); return nullptr; } @@ -2087,6 +2127,7 @@ namespace gamescope { m_Props.type = CDRMAtomicProperty::Instantiate( "type", this, *rawProperties ); m_Props.IN_FORMATS = CDRMAtomicProperty::Instantiate( "IN_FORMATS", this, *rawProperties ); + m_Props.IN_FORMATS_ASYNC = CDRMAtomicProperty::Instantiate( "IN_FORMATS_ASYNC", this, *rawProperties ); m_Props.FB_ID = CDRMAtomicProperty::Instantiate( "FB_ID", this, *rawProperties ); m_Props.IN_FENCE_FD = CDRMAtomicProperty::Instantiate( "IN_FENCE_FD", this, *rawProperties ); @@ -3848,7 +3889,13 @@ namespace gamescope } virtual std::span GetSupportedModifiers( uint32_t uDrmFormat ) const override { - const wlr_drm_format *pFormat = wlr_drm_format_set_get( &g_DRM.formats, uDrmFormat ); + const wlr_drm_format_set *pFormatSet = &g_DRM.formats; + if ( g_bSupportsAsyncFlips && g_DRM.formats_async.len > 0 ) + pFormatSet = &g_DRM.formats_async; + + const wlr_drm_format *pFormat = wlr_drm_format_set_get( pFormatSet, uDrmFormat ); + if ( !pFormat && pFormatSet != &g_DRM.formats ) + pFormat = wlr_drm_format_set_get( &g_DRM.formats, uDrmFormat ); if ( !pFormat ) return std::span{};