Skip to content

fix(android): treat VK_SUBOPTIMAL_KHR as success in the presenter - #1

Open
KLCLAU wants to merge 1 commit into
fadi-labib:generalsx-android-v2.6from
KLCLAU:android-suboptimal-no-recreate
Open

fix(android): treat VK_SUBOPTIMAL_KHR as success in the presenter#1
KLCLAU wants to merge 1 commit into
fadi-labib:generalsx-android-v2.6from
KLCLAU:android-suboptimal-no-recreate

Conversation

@KLCLAU

@KLCLAU KLCLAU commented Sep 1, 2026

Copy link
Copy Markdown

Problem

On Android the display surface is portrait-native while GeneralsX renders landscape, so surfaceCapabilities.currentTransform is a 90/270° rotation the swapchain's preTransform never matches. Every vkAcquireNextImageKHR / vkQueuePresentKHR returns VK_SUBOPTIMAL_KHR, and Presenter treats that like VK_ERROR_OUT_OF_DATE_KHR — recreating the swapchain every single frame:

info:  Presenter: Got VK_SUBOPTIMAL_KHR, recreating swapchain
info:  Presenter: Actual swapchain properties: ...
info:  Presenter: Got VK_SUBOPTIMAL_KHR, recreating swapchain
...   (761 times in 45 seconds on a Galaxy S26+)

Fix

The acquired image is fully usable and the Android compositor does the rotation for free, so on __ANDROID__ treat VK_SUBOPTIMAL_KHR as success for the recreate decision (presenterResultOk()), at all three sites: the post-acquire check, the pre-acquire-after-present optimization, and the post-present dirty flag. Desktop behaviour is unchanged (presenterResultOk is r == VK_SUCCESS off Android).

Result: recreate-per-frame loop 761 → 0; steady frame rate in menus and in-game on Adreno 840.

A proper pre-rotation implementation (render with preTransform == currentTransform, apply the inverse in the final blit) would be better still, but this stops the pathological loop with a one-liner and no behavioural risk.

Companion to fadi-labib/Generals-Android#24.

On Android the display surface is portrait-native while the game renders
landscape, so surfaceCapabilities.currentTransform is a 90/270-degree
rotation that the swapchain's preTransform never matches. Every
vkAcquireNextImageKHR / vkQueuePresentKHR then returns VK_SUBOPTIMAL_KHR
and Presenter treats it like OUT_OF_DATE, recreating the swapchain on
*every single frame* (observed: 761 'recreating swapchain' log lines in
45 seconds, plus the wasted GPU work).

The acquired image is fully usable and the Android compositor performs the
rotation for free, so on __ANDROID__ treat VK_SUBOPTIMAL_KHR as success
for the recreate decision (presenterResultOk). Non-Android behaviour is
unchanged.

With this, the recreate-per-frame loop drops from 761 to 0 on a Galaxy
S26+ (Adreno 840) and the game holds a steady frame rate in menus and
in-game.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant