feat: Replace GLES display renderer with native Vulkan compositor#343
Merged
22 commits merged intoMay 13, 2026
Merged
Conversation
added 6 commits
May 1, 2026 13:18
This reverts commit cf38113.
added 13 commits
May 2, 2026 23:18
# Conflicts: # app/src/main/runtime/display/XServerDisplayActivity.java # app/src/main/runtime/display/renderer/GLRenderer.java # app/src/main/runtime/display/winhandler/WinHandler.java # app/src/main/runtime/display/xserver/extensions/MITSHMExtension.java # app/src/main/runtime/input/ui/TouchpadView.kt
# Conflicts: # app/src/main/runtime/display/winhandler/WinHandler.java # app/src/main/runtime/input/ui/InputControlsView.java
maxjivi05
added a commit
to maxjivi05/WinNative
that referenced
this pull request
May 14, 2026
Resolves two conflicts from WinNative-Emu#343 (Vulkan compositor rewrite) + WinNative-Emu#413 (store game detail UI): - UnifiedActivity Epic/GOG popups: kept main's new StoreGameDetailScreen fullscreen UI; added showBestConfigs / onBestConfigs parameters so the Best Configs entry surfaces inside that screen instead of as a standalone CompactActionButton. - StoreGameDetailScreen: new optional Best Configs action button slot between Custom Path and Cloud Sync. - FrameRating: kept main's recordGameFrame(primarySource, serial) refactor and rolling FPS buffer; restored the FrameObserver callback from best-settings so perf-recording / leaderboard stats still fire per present (notification runs before the visibility gate so it works with HUD hidden). Follow-up UX tweaks: - LibraryGameLaunchScreen: removed the Best Configs action-row button. Discovery happens inside the per-shortcut Settings dialog's Import flow, which is the right place for "load a saved config." - BestConfigsImportSheet: relabelled the two options to "Device" / "Community" with cleaner supporting copy, and reordered Device on top to match the wording. Same M3 picker pattern the Export sheet uses.
This pull request was closed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR replaces the upstream
GLSurfaceView/GLES display renderer with a native Vulkan compositor backed by an AndroidSurface/ANativeWindow.The new path keeps rendering compositor-first: windows, cursor, and post-processing effects are composed into a Vulkan swapchain image and presented with
vkQueuePresentKHR. It also supports zero-copyAHardwareBufferimport for suitable DRI3/PRESENT pixmaps, but those imported buffers are still sampled by the Vulkan compositor rather than routed through a separateSurfaceControlscanout path.Vulkan Setup
The compositor creates a Vulkan
1.1instance with:VK_KHR_surfaceVK_KHR_android_surfaceIt enumerates physical devices through the active Android Vulkan loader/ICD, selects a graphics queue, checks presentation support for the Android surface, and requires
VK_KHR_swapchainbefore creating the swapchain path.AHardwareBufferimport is enabled only when the required external-memory extensions are all available:VK_ANDROID_external_memory_android_hardware_bufferVK_KHR_external_memoryVK_KHR_dedicated_allocationVK_KHR_get_memory_requirements2Optional helpers such as
VK_EXT_queue_family_foreignandVK_KHR_sampler_ycbcr_conversionare enabled only when exposed by the driver.Swapchain And Presentation
Swapchain creation queries surface capabilities, formats, transforms, present modes, and image-count limits.
FIFO is the default present mode because it is guaranteed by Vulkan. MAILBOX or IMMEDIATE can be requested, but the renderer validates support and falls back to FIFO when unavailable.
The swapchain image count is clamped to the surface limits, and each swapchain image gets its own image view, framebuffer, and render-finished semaphore.
Scene Submission
The Java side no longer issues GLES draw calls per window. Instead, it serializes the current display state into a compact direct
ByteBuffercontaining:Java builds the scene while holding the relevant X/render locks, releases them, then native code snapshots the scene under
scene_mutexbefore acquire, command recording, queue submit, and present under renderer-side synchronization.Texture Uploads
Texture ownership moved from GLES texture IDs to native Vulkan texture objects.
Software drawable updates are tracked with dirty rectangles. When possible, only the dirty bounds are copied into staging memory and uploaded with
vkCmdCopyBufferToImage. Multiple dirty drawable updates can be batched into a single staging upload pass before frame rendering, reducing the cost of small X11 damage updates.Pending uploads are submitted before the frame render, and same-queue ordering plus transfer-to-shader-read barriers make the updated texture contents visible to the compositor frame.
DRI3 / PRESENT AHardwareBuffer Path
The DRI3/PRESENT path can import suitable
AHardwareBufferpixmaps directly into Vulkan. When a PRESENT pixmap covers the target drawable, the window can use that pixmap as its compositor source instead of copying pixels into the window backing buffer.Native code imports the buffer with
VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID, creates a Vulkan image/image view backed by the Android buffer, and samples it as a compositor texture.External-format buffers are rejected unless the current descriptor/sampler path can represent them correctly, avoiding invalid Vulkan usage on stricter drivers.
Frame Lifecycle
Frame rendering is explicit and ordered:
vkQueuePresentKHR.Texture destruction is deferred through a small graveyard so native texture resources are not freed while still reachable by in-flight GPU work.
Scheduling And Frame Pacing
X/window/PRESENT events now use a coalesced render request that posts to Android
Choreographer, so multiple updates before the next display tick collapse into one render.Renderer-side FPS limiting is performed natively after submit/present. The limiter uses the same absolute-heartbeat pacing model as the X client path:
100 msresync window0.5 msearly-sleep threshold4 msfinal precision windowThis avoids catch-up bursts after long stalls while keeping renderer pacing aligned with the existing X client timing logic.
Comparison With Ludashi
Compared with Ludashi’s more aggressive
SurfaceControlscanout renderer, this implementation is a compositor-first Vulkan path.It does not try to bypass composition in fullscreen/native-mode cases. Instead, it prioritizes:
AHardwareBufferimport semanticsThanks to Ludashi and StevenMXZ for the inspiration and for proving that a Vulkan-based Winlator display renderer is practical on Android. This implementation takes a more conservative compositor-first approach, but their work helped validate the direction.