diff --git a/src/drm.cpp b/src/drm.cpp index 6b6bd65..116477c 100644 --- a/src/drm.cpp +++ b/src/drm.cpp @@ -41,6 +41,7 @@ extern "C" { struct drm_t g_DRM = {}; uint32_t g_nDRMFormat = DRM_FORMAT_INVALID; +uint32_t g_nDRMFormatOverlay = DRM_FORMAT_INVALID; // for partial composition, we may have more limited formats than base planes + alpha. bool g_bRotated = false; bool g_bUseLayers = true; bool g_bDebugLayers = false; @@ -1357,6 +1358,12 @@ bool init_drm(struct drm_t *drm, int width, int height, int refresh, bool wants_ } } + // ARGB8888 is the Xformat and AFormat here in this function as we want transparent overlay + g_nDRMFormatOverlay = pick_plane_format(&drm->primary_formats, DRM_FORMAT_ARGB8888, DRM_FORMAT_ARGB8888); + if ( g_nDRMFormatOverlay == DRM_FORMAT_INVALID ) { + drm_log.errorf("Overlay plane doesn't support any formats >= 8888"); + } + drm->kms_in_fence_fd = -1; std::thread flip_handler_thread( flip_handler_thread_run ); @@ -2284,11 +2291,6 @@ drm_prepare_liftoff( struct drm_t *drm, const struct FrameInfo_t *frameInfo, boo liftoff_layer_set_property( drm->lo_layers[ i ], "VALVE1_PLANE_SHAPER_TF", 0 ); liftoff_layer_set_property( drm->lo_layers[ i ], "VALVE1_PLANE_LUT3D", 0 ); } - - if (!g_bDisableBlendTF && !bSinglePlane) - liftoff_layer_set_property( drm->lo_layers[ i ], "VALVE1_PLANE_BLEND_TF", drm->pending.output_tf ); - else - liftoff_layer_set_property( drm->lo_layers[ i ], "VALVE1_PLANE_BLEND_TF", 0 ); } } else @@ -2299,6 +2301,23 @@ drm_prepare_liftoff( struct drm_t *drm, const struct FrameInfo_t *frameInfo, boo liftoff_layer_set_property( drm->lo_layers[ i ], "VALVE1_PLANE_SHAPER_LUT", 0 ); liftoff_layer_set_property( drm->lo_layers[ i ], "VALVE1_PLANE_SHAPER_TF", 0 ); liftoff_layer_set_property( drm->lo_layers[ i ], "VALVE1_PLANE_LUT3D", 0 ); + } + } + + if ( frameInfo->layers[i].allowBlending ) + { + if ( drm_supports_color_mgmt( drm ) ) + { + if (!g_bDisableBlendTF && !bSinglePlane) + liftoff_layer_set_property( drm->lo_layers[ i ], "VALVE1_PLANE_BLEND_TF", drm->pending.output_tf ); + else + liftoff_layer_set_property( drm->lo_layers[ i ], "VALVE1_PLANE_BLEND_TF", 0 ); + } + } + else + { + if ( drm_supports_color_mgmt( drm ) ) + { liftoff_layer_set_property( drm->lo_layers[ i ], "VALVE1_PLANE_BLEND_TF", DRM_VALVE1_TRANSFER_FUNCTION_DEFAULT ); } } diff --git a/src/drm.hpp b/src/drm.hpp index 2216e5a..f7fc276 100644 --- a/src/drm.hpp +++ b/src/drm.hpp @@ -269,6 +269,7 @@ struct drm_t { extern struct drm_t g_DRM; extern uint32_t g_nDRMFormat; +extern uint32_t g_nDRMFormatOverlay; extern bool g_bUseLayers; extern bool g_bRotated; diff --git a/src/rendervulkan.cpp b/src/rendervulkan.cpp index e9b4911..d2cb038 100644 --- a/src/rendervulkan.cpp +++ b/src/rendervulkan.cpp @@ -103,9 +103,13 @@ struct VulkanOutput_t VkFence acquireFence; uint32_t nOutImage; // swapchain index in nested mode, or ping/pong between two RTs + uint32_t nOutImagePartial; + uint32_t nOutPresentImagePartial; std::vector> outputImages; + std::vector> outputImagesPartialOverlay; VkFormat outputFormat = VK_FORMAT_UNDEFINED; + VkFormat outputFormatOverlay = VK_FORMAT_UNDEFINED; std::array, 8> pScreenshotImages; @@ -2861,7 +2865,7 @@ std::shared_ptr vulkan_create_debug_white_texture() void vulkan_present_to_openvr( void ) { //static auto texture = vulkan_create_debug_white_texture(); - auto texture = vulkan_get_last_output_image(); + auto texture = vulkan_get_last_output_image( false ); vr::VRVulkanTextureData_t data = { @@ -2985,9 +2989,13 @@ static bool vulkan_make_output_images( VulkanOutput_t *pOutput ) outputImageflags.bSwapchain = BIsVRSession(); pOutput->outputImages.resize(2); + pOutput->outputImagesPartialOverlay.resize(3); pOutput->outputImages[0] = nullptr; pOutput->outputImages[1] = nullptr; + pOutput->outputImagesPartialOverlay[0] = nullptr; + pOutput->outputImagesPartialOverlay[1] = nullptr; + pOutput->outputImagesPartialOverlay[2] = nullptr; VkFormat format = pOutput->outputFormat; @@ -3007,6 +3015,35 @@ static bool vulkan_make_output_images( VulkanOutput_t *pOutput ) return false; } + if ( pOutput->outputFormatOverlay != VK_FORMAT_UNDEFINED ) + { + VkFormat partialFormat = pOutput->outputFormatOverlay; + + pOutput->outputImagesPartialOverlay[0] = std::make_shared(); + bool bSuccess = pOutput->outputImagesPartialOverlay[0]->BInit( g_nOutputWidth, g_nOutputHeight, 1u, VulkanFormatToDRM(partialFormat), outputImageflags ); + if ( bSuccess != true ) + { + vk_log.errorf( "failed to allocate buffer for KMS" ); + return false; + } + + pOutput->outputImagesPartialOverlay[1] = std::make_shared(); + bSuccess = pOutput->outputImagesPartialOverlay[1]->BInit( g_nOutputWidth, g_nOutputHeight, 1u, VulkanFormatToDRM(partialFormat), outputImageflags ); + if ( bSuccess != true ) + { + vk_log.errorf( "failed to allocate buffer for KMS" ); + return false; + } + + pOutput->outputImagesPartialOverlay[2] = std::make_shared(); + bSuccess = pOutput->outputImagesPartialOverlay[2]->BInit( g_nOutputWidth, g_nOutputHeight, 1u, VulkanFormatToDRM(partialFormat), outputImageflags ); + if ( bSuccess != true ) + { + vk_log.errorf( "failed to allocate buffer for KMS" ); + return false; + } + } + return true; } @@ -3016,6 +3053,8 @@ bool vulkan_remake_output_images() g_device.waitIdle(); pOutput->nOutImage = 0; + pOutput->nOutImagePartial = 0; + pOutput->nOutPresentImagePartial = 2; // Delete screenshot image to be remade if needed for (auto& pScreenshotImage : pOutput->pScreenshotImages) @@ -3094,6 +3133,7 @@ bool vulkan_make_output( VkSurfaceKHR surface ) else { pOutput->outputFormat = DRMFormatToVulkan( g_nDRMFormat, false ); + pOutput->outputFormatOverlay = DRMFormatToVulkan( g_nDRMFormatOverlay, false ); if ( pOutput->outputFormat == VK_FORMAT_UNDEFINED ) { @@ -3101,6 +3141,12 @@ bool vulkan_make_output( VkSurfaceKHR surface ) return false; } + if ( pOutput->outputFormatOverlay == VK_FORMAT_UNDEFINED ) + { + vk_log.errorf( "failed to find Vulkan format suitable for KMS partial overlays" ); + return false; + } + if ( !vulkan_make_output_images( pOutput ) ) return false; } @@ -3497,9 +3543,23 @@ bool vulkan_screenshot( const struct FrameInfo_t *frameInfo, std::shared_ptr pPipewireTexture ) +std::unique_ptr partial_wait_thread; + +bool vulkan_composite( const struct FrameInfo_t *frameInfo, std::shared_ptr pPipewireTexture, bool partial ) { - auto compositeImage = g_output.outputImages[ g_output.nOutImage ]; + if ( partial_wait_thread ) + { + partial_wait_thread->join(); + partial_wait_thread = nullptr; + } + + if ( partial ) + { + g_output.nOutPresentImagePartial = g_output.nOutImagePartial; + g_output.nOutImagePartial = ( g_output.nOutImagePartial + 1 ) % 3; + } + + auto compositeImage = partial ? g_output.outputImagesPartialOverlay[ g_output.nOutImagePartial ] : g_output.outputImages[ g_output.nOutImage ]; auto cmdBuffer = g_device.commandBuffer(); @@ -3682,18 +3742,36 @@ bool vulkan_composite( const struct FrameInfo_t *frameInfo, std::shared_ptr([sequence] + { + g_device.wait(sequence); + }); + } + else + { + g_device.wait(sequence); + + if ( !BIsSDLSession() ) + { + g_output.nOutImage = !g_output.nOutImage; + } } return true; } -std::shared_ptr vulkan_get_last_output_image( void ) +std::shared_ptr vulkan_get_last_output_image( bool partial ) { + if ( partial ) + { + uint32_t nOutImage = g_output.nOutPresentImagePartial; + //vk_log.infof( "Partial overlay frame: %d", nOutImage ); + return g_output.outputImagesPartialOverlay[ nOutImage ]; + } + return g_output.outputImages[ !g_output.nOutImage ]; } diff --git a/src/rendervulkan.hpp b/src/rendervulkan.hpp index 8b39637..c334471 100644 --- a/src/rendervulkan.hpp +++ b/src/rendervulkan.hpp @@ -240,7 +240,7 @@ struct FrameInfo_t std::shared_ptr shaperLut[EOTF_Count]; std::shared_ptr lut3D[EOTF_Count]; - bool applyOutputColorMgmt; + bool applyOutputColorMgmt; // drm only int layerCount; struct Layer_t @@ -256,7 +256,8 @@ struct FrameInfo_t bool blackBorder; bool linearFilter; - bool applyColorMgmt; + bool applyColorMgmt; // drm only + bool allowBlending; // drm only GamescopeAppTextureColorspace colorspace; @@ -315,6 +316,7 @@ namespace CompositeDebugFlag static constexpr uint32_t Heatmap = 1u << 2; static constexpr uint32_t Heatmap_MSWCG = 1u << 3; static constexpr uint32_t Heatmap_Hard = 1u << 4; + static constexpr uint32_t Markers_Partial = 1u << 5; static constexpr uint32_t Tonemap_Reinhard = 1u << 7; }; @@ -327,8 +329,8 @@ std::shared_ptr vulkan_create_texture_from_dmabuf( struct wlr_dm std::shared_ptr vulkan_create_texture_from_bits( uint32_t width, uint32_t height, uint32_t contentWidth, uint32_t contentHeight, uint32_t drmFormat, CVulkanTexture::createFlags texCreateFlags, void *bits ); std::shared_ptr vulkan_create_texture_from_wlr_buffer( struct wlr_buffer *buf ); -bool vulkan_composite( const struct FrameInfo_t *frameInfo, std::shared_ptr pScreenshotTexture ); -std::shared_ptr vulkan_get_last_output_image( void ); +bool vulkan_composite( const struct FrameInfo_t *frameInfo, std::shared_ptr pScreenshotTexture, bool partial ); +std::shared_ptr vulkan_get_last_output_image( bool partial ); std::shared_ptr vulkan_acquire_screenshot_texture(uint32_t width, uint32_t height, bool exportable, uint32_t drmFormat, EStreamColorspace colorspace = k_EStreamColorspace_Unknown); void vulkan_present_to_window( void ); diff --git a/src/shaders/composite.h b/src/shaders/composite.h index 575c7d3..03e5ba3 100644 --- a/src/shaders/composite.h +++ b/src/shaders/composite.h @@ -14,6 +14,9 @@ void compositing_debug(uvec2 coord) { if (pos.x >= 40 && pos.x < 120 && pos.y >= 40 && pos.y < 120) { vec4 value = vec4(1.0f, 1.0f, 1.0f, 1.0f); + if (checkDebugFlag(compositedebug_Markers_Partial)) { + value = vec4(0.0f, 1.0f, 1.0f, 1.0f); + } if (pos.x >= 48 && pos.x < 112 && pos.y >= 48 && pos.y < 112) { uint random = pseudo_random(u_frameId.x + (pos.x & ~0x7) + (pos.y & ~0x7) * 50); vec4 time = round(unpackUnorm4x8(random)).xyzw; diff --git a/src/shaders/cs_composite_blit.comp b/src/shaders/cs_composite_blit.comp index bb4498f..03830c4 100644 --- a/src/shaders/cs_composite_blit.comp +++ b/src/shaders/cs_composite_blit.comp @@ -26,13 +26,14 @@ void main() { return; vec2 uv = vec2(coord); - vec3 outputValue = vec3(0.0f); + vec4 outputValue = vec4(0.0f); if (checkDebugFlag(compositedebug_PlaneBorders)) - outputValue = vec3(1.0f, 0.0f, 0.0f); + outputValue = vec4(1.0f, 0.0f, 0.0f, 0.0f); - if (c_layerCount > 0) - outputValue = sampleLayer(0, uv).rgb * u_opacity[0]; + if (c_layerCount > 0) { + outputValue = sampleLayer(0, uv) * u_opacity[0]; + } for (int i = 1; i < c_layerCount; i++) { vec4 layerColor = sampleLayer(i, uv); @@ -43,11 +44,11 @@ void main() { // For the other side of things, we need to multiply by (1.0f - (layerColor.a * opacity)) float opacity = u_opacity[i]; float layerAlpha = opacity * layerColor.a; - outputValue = layerColor.rgb * opacity + outputValue * (1.0f - layerAlpha); + outputValue = layerColor * opacity + outputValue * (1.0f - layerAlpha); } - outputValue = encodeOutputColor(outputValue); - imageStore(dst, ivec2(coord), vec4(outputValue, 0)); + outputValue.rgb = encodeOutputColor(outputValue.rgb); + imageStore(dst, ivec2(coord), outputValue); // Indicator to quickly tell if we're in the compositing path or not. if (checkDebugFlag(compositedebug_Markers)) diff --git a/src/shaders/descriptor_set.h b/src/shaders/descriptor_set.h index 1e17bcb..87d7919 100644 --- a/src/shaders/descriptor_set.h +++ b/src/shaders/descriptor_set.h @@ -26,6 +26,7 @@ const uint compositedebug_PlaneBorders = 1u << 1; const uint compositedebug_Heatmap = 1u << 2; const uint compositedebug_Heatmap_MSWCG = 1u << 3; // If compositedebug_Heatmap is set, use the MS WCG heatmap instead of Lilium const uint compositedebug_Heatmap_Hard = 1u << 4; // If compositedebug_Heatmap is set, use a heatmap with specialized hard flagging +const uint compositedebug_Markers_Partial = 1u << 5; // If compositedebug_Heatmap is set, use a heatmap with specialized hard flagging //const uint compositedebug_Tonemap_Reinhard = 1u << 7; // Use Reinhard tonemapping instead of Uncharted. bool checkDebugFlag(uint flag) { diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index 4109f58..7a97c77 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -1858,6 +1858,7 @@ void MouseCursor::paint(steamcompmgr_win_t *window, steamcompmgr_win_t *fit, str layer->zpos = g_zposCursor; // cursor, on top of both bottom layers layer->applyColorMgmt = false; + layer->allowBlending = true; layer->tex = m_texture; layer->fbid = BIsNested() ? 0 : m_texture->fbid(); @@ -2093,6 +2094,7 @@ paint_window(steamcompmgr_win_t *w, steamcompmgr_win_t *scaleW, struct FrameInfo layer->blackBorder = flags & PaintWindowFlag::DrawBorders; layer->applyColorMgmt = true; + layer->allowBlending = true; layer->zpos = g_zposBase; if ( w != scaleW ) @@ -2401,17 +2403,18 @@ paint_all(bool async) bool bNeedsNearest = g_upscaleFilter == GamescopeUpscaleFilter::NEAREST && frameInfo.layers[0].scale.x != 1.0f && frameInfo.layers[0].scale.y != 1.0f; + bool bWantsPartialComposite = frameInfo.layerCount >= 3; - bool bNeedsComposite = BIsNested(); - bNeedsComposite |= alwaysComposite; - bNeedsComposite |= pw_buffer != nullptr; - bNeedsComposite |= bWasFirstFrame; - bNeedsComposite |= frameInfo.useFSRLayer0; - bNeedsComposite |= frameInfo.useNISLayer0; - bNeedsComposite |= frameInfo.blurLayer0; - bNeedsComposite |= bNeedsNearest; - bNeedsComposite |= bDrewCursor; - bNeedsComposite |= g_bColorSliderInUse; + bool bNeedsFullComposite = BIsNested(); + bNeedsFullComposite |= alwaysComposite; + bNeedsFullComposite |= pw_buffer != nullptr; + bNeedsFullComposite |= bWasFirstFrame; + bNeedsFullComposite |= frameInfo.useFSRLayer0; + bNeedsFullComposite |= frameInfo.useNISLayer0; + bNeedsFullComposite |= frameInfo.blurLayer0; + bNeedsFullComposite |= bNeedsNearest; + bNeedsFullComposite |= bDrewCursor; + bNeedsFullComposite |= g_bColorSliderInUse; for (uint32_t i = 0; i < EOTF_Count; i++) { @@ -2424,17 +2427,25 @@ paint_all(bool async) if ( !BIsNested() && g_bOutputHDREnabled ) { - bNeedsComposite |= g_bHDRItmEnable; + bNeedsFullComposite |= g_bHDRItmEnable; if ( !drm_supports_color_mgmt(&g_DRM) ) - bNeedsComposite |= ( frameInfo.layerCount > 1 || frameInfo.layers[0].colorspace != GAMESCOPE_APP_TEXTURE_COLORSPACE_HDR10_PQ ); + bNeedsFullComposite |= ( frameInfo.layerCount > 1 || frameInfo.layers[0].colorspace != GAMESCOPE_APP_TEXTURE_COLORSPACE_HDR10_PQ ); } - bNeedsComposite |= !!(g_uCompositeDebug & CompositeDebugFlag::Heatmap); + bNeedsFullComposite |= !!(g_uCompositeDebug & CompositeDebugFlag::Heatmap); - if ( !bNeedsComposite ) + static bool g_bWasPartialComposite = false; + static int g_nLastSingleOverlayZPos = 0; + + if ( !bNeedsFullComposite && !bWantsPartialComposite ) { int ret = drm_prepare( &g_DRM, async, &frameInfo ); if ( ret == 0 ) + { bDoComposite = false; + g_bWasPartialComposite = false; + if ( frameInfo.layerCount == 2 ) + g_nLastSingleOverlayZPos = frameInfo.layers[1].zpos; + } else if ( ret == -EACCES ) return; } @@ -2451,7 +2462,65 @@ paint_all(bool async) pPipewireTexture = pw_buffer->texture; } #endif - bool bResult = vulkan_composite( &frameInfo, pPipewireTexture ); + + struct FrameInfo_t compositeFrameInfo = frameInfo; + + if ( compositeFrameInfo.layerCount == 1 ) + { + // If we failed to flip a single plane then + // we definitely need to composite for some reason... + bNeedsFullComposite = true; + } + + if ( !bNeedsFullComposite ) + { + // If we want to partial composite, fallback to full + // composite if we have mismatching colorspaces in our overlays. + // This is 2, and we do i-1 so 1...layerCount. So AFTER we have removed baseplane. + // Overlays only. + // + // Josh: + // We could handle mismatching colorspaces for partial composition + // but I want to keep overlay -> partial composition promotion as simple + // as possible, using the same 3D + SHAPER LUTs + BLEND in DRM + // as changing them is incredibly expensive!! It takes forever. + // We can't just point it to random BDA or whatever, it has to be uploaded slowly + // thru registers which is SUPER SLOW. + // This avoids stutter. + for ( int i = 2; i < compositeFrameInfo.layerCount; i++ ) + { + if ( frameInfo.layers[i - 1].colorspace != frameInfo.layers[i].colorspace ) + { + bNeedsFullComposite = true; + break; + } + } + } + + // If doing a partial composition then remove the baseplane + // from our frameinfo to composite. + if ( !bNeedsFullComposite ) + { + for ( int i = 1; i < compositeFrameInfo.layerCount; i++ ) + compositeFrameInfo.layers[i - 1] = compositeFrameInfo.layers[i]; + compositeFrameInfo.layerCount -= 1; + + // When doing partial composition, apply the shaper + 3D LUT stuff + // at scanout. + for ( uint32_t nEOTF = 0; nEOTF < EOTF_Count; nEOTF++ ) { + compositeFrameInfo.shaperLut[ nEOTF ] = nullptr; + compositeFrameInfo.lut3D[ nEOTF ] = nullptr; + } + + // If using composite debug markers, make sure we mark them as partial + // so we know! + if ( g_uCompositeDebug & CompositeDebugFlag::Markers ) + g_uCompositeDebug |= CompositeDebugFlag::Markers_Partial; + } + + bool bResult = vulkan_composite( &compositeFrameInfo, pPipewireTexture, !bNeedsFullComposite ); + + g_uCompositeDebug &= ~CompositeDebugFlag::Markers_Partial; if ( bResult != true ) { @@ -2477,23 +2546,89 @@ paint_all(bool async) } else { - struct FrameInfo_t compositeFrameInfo = {}; - compositeFrameInfo = {}; - compositeFrameInfo.applyOutputColorMgmt = false; - compositeFrameInfo.layerCount = 1; - FrameInfo_t::Layer_t *layer = &compositeFrameInfo.layers[ 0 ]; - layer->scale.x = 1.0; - layer->scale.y = 1.0; - layer->opacity = 1.0; + struct FrameInfo_t presentCompFrameInfo = {}; - layer->tex = vulkan_get_last_output_image(); - layer->fbid = layer->tex->fbid(); - layer->applyColorMgmt = false; + if ( bNeedsFullComposite ) + { + presentCompFrameInfo.applyOutputColorMxgmt = false; + presentCompFrameInfo.layerCount = 1; - layer->linearFilter = false; - layer->colorspace = g_bOutputHDREnabled ? GAMESCOPE_APP_TEXTURE_COLORSPACE_HDR10_PQ : GAMESCOPE_APP_TEXTURE_COLORSPACE_SRGB; + FrameInfo_t::Layer_t *baseLayer = &presentCompFrameInfo.layers[ 0 ]; + baseLayer->scale.x = 1.0; + baseLayer->scale.y = 1.0; + baseLayer->opacity = 1.0; + baseLayer->zpos = g_zposBase; - int ret = drm_prepare( &g_DRM, async, &compositeFrameInfo ); + baseLayer->tex = vulkan_get_last_output_image( false ); + baseLayer->fbid = baseLayer->tex->fbid(); + baseLayer->applyColorMgmt = false; + baseLayer->allowBlending = false; + + baseLayer->linearFilter = false; + baseLayer->colorspace = g_bOutputHDREnabled ? GAMESCOPE_APP_TEXTURE_COLORSPACE_HDR10_PQ : GAMESCOPE_APP_TEXTURE_COLORSPACE_SRGB; + + g_bWasPartialComposite = false; + } + else + { + if ( g_bWasPartialComposite ) + { + presentCompFrameInfo.applyOutputColorMgmt = true; + presentCompFrameInfo.layerCount = 2; + + presentCompFrameInfo.layers[ 0 ] = frameInfo.layers[ 0 ]; + presentCompFrameInfo.layers[ 0 ].zpos = g_zposBase; + + FrameInfo_t::Layer_t *overlayLayer = &presentCompFrameInfo.layers[ 1 ]; + overlayLayer->scale.x = 1.0; + overlayLayer->scale.y = 1.0; + overlayLayer->opacity = 1.0; + overlayLayer->zpos = g_zposOverlay; + + overlayLayer->tex = vulkan_get_last_output_image( true ); + overlayLayer->fbid = overlayLayer->tex->fbid(); + overlayLayer->applyColorMgmt = true; + overlayLayer->allowBlending = true; + + overlayLayer->linearFilter = false; + // Partial composition stuff has the same colorspace. + // So read that from the composite frame info + overlayLayer->colorspace = compositeFrameInfo.layers[0].colorspace; + } + else + { + // Use whatever overlay we had last while waiting for the + // partial composition to have anything queued. + presentCompFrameInfo.applyOutputColorMgmt = true; + presentCompFrameInfo.layerCount = 1; + + presentCompFrameInfo.layers[ 0 ] = frameInfo.layers[ 0 ]; + presentCompFrameInfo.layers[ 0 ].zpos = g_zposBase; + + FrameInfo_t::Layer_t *lastPresentedOverlayLayer = nullptr; + for (int i = 0; i < frameInfo.layerCount; i++) + { + if (frameInfo.layers[i].zpos == g_nLastSingleOverlayZPos) + { + lastPresentedOverlayLayer = &frameInfo.layers[i]; + break; + } + } + + if (lastPresentedOverlayLayer) + { + FrameInfo_t::Layer_t *overlayLayer = &presentCompFrameInfo.layers[ 1 ]; + *overlayLayer = *lastPresentedOverlayLayer; + overlayLayer->zpos = g_zposOverlay; + + presentCompFrameInfo.layerCount = 2; + } + } + + g_bWasPartialComposite = true; + } + + int ret = drm_prepare( &g_DRM, async, &presentCompFrameInfo ); // Happens when we're VT-switched away if ( ret == -EACCES )