Skip to content

Test-Branch (no testing needed)#380

Draft
maxjivi05 wants to merge 5 commits into
WinNative-Emu:mainfrom
maxjivi05:test-branch
Draft

Test-Branch (no testing needed)#380
maxjivi05 wants to merge 5 commits into
WinNative-Emu:mainfrom
maxjivi05:test-branch

Conversation

@maxjivi05

Copy link
Copy Markdown
Contributor

…aceControl

Adds an opt-in per-container "Direct Composition" toggle that, when active on API 29+, routes fullscreen direct-scanout drawables to a child ASurfaceControl layer bound to the XServerView's SurfaceView. SurfaceFlinger

  • HWC promote the layer to a hardware overlay plane (Snapdragon DPU scanout), bypassing in-process GLRenderer composition entirely for qualifying frames. Verified end-to-end on a OnePlus 15 (Android 16, Adreno X2-class DPU) running a vkd3d-proton title — SurfaceFlinger dumpsys shows composition type=DEVICE on the winnative-direct-composition layer with continuous buffer updates and HWC scaling the native game frame to display resolution.

Phase 1 — toggle plumbing:

  • Container.EXTRA_DIRECT_COMPOSITION + isDirectCompositionEnabled / setDirectCompositionEnabled accessors backed by existing extraData JSON; default off, no migration needed.
  • ContainerSettingsComposeDialog reads/writes the setting; new container creation also sets it on the freshly-built Container.
  • GameSettingsStateHolder.directComposition + a SettingCheckbox in the container-edit UI (gated on isContainerEditMode so shortcut sheets don't show it).
  • Two new strings: session_display_direct_composition + session_display_direct_composition_summary.

Phase 2 — native + lifecycle + push path:

  • cpp/winlator/surface_compositor.c — dlopen-resolved JNI wrappers around ASurfaceControl + ASurfaceTransaction (libandroid.so, API 29+). Probe (nativeIsAvailable), attach/detach (createFromWindow + reparent-to-null + release), setColor (proof-of-life), pushBuffer (setBuffer + modern setPosition/setScale/setCrop or fallback setGeometry, with framework-owned fence FD), allocateTestBuffer + releaseBuffer (256x256 magenta smoke test, RGBA_8888 + GPU_SAMPLED_IMAGE + COMPOSER_OVERLAY usage, with retry-without- OVERLAY for grallocs that reject the combo).
  • SurfaceCompositor.java — static isAvailable() probe with API-29 short-circuit + cached-availability + UnsatisfiedLinkError guard.
  • DirectCompositionLayer.java — instance class wrapping a single ASurfaceControl, all public methods synchronized so UI-thread release() and GLThread pushBuffer() can't race the native pointer.
  • Drawable.scanoutSource + directScanout fields made volatile for cross-thread visibility between the X-server worker and GLThread.
  • Drawable.acquireFenceFd as AtomicInteger with takeAcquireFenceFd (read-and-clear) + setAcquireFenceFd (closes prior FD on overwrite) — forward-compatible API for future X11 Present wait_fence plumbing; today always -1, documented as empirical observation rather than a Mesa contract.
  • GPUImage.getHardwareBufferPtr() — public accessor for the underlying AHardwareBuffer*, used by the renderer hook.
  • GLRenderer.directCompositionTarget (volatile) + setDirectCompositionTarget setter; per-frame maybePushDirectComposition under Drawable.renderLock with last-pushed cache to avoid per-frame transaction storm + a consecutive-failure counter that self-detaches the target after DC_FAIL_LIMIT to stop wasting JNI calls on permanent failure; maybeHideDirectComposition on direct->fallback transition, idempotent and cache-invalidating.
  • XServerDisplayActivity.installDirectCompositionLifecycle — SurfaceHolder.Callback registered against XServerView's holder; if the surface is already valid by the time we register (GLSurfaceView's GLThread can fire surfaceCreated before setupUI returns), synthesize the initial dispatch so we don't miss the first attach; on surfaceDestroyed clear the renderer pointer BEFORE releasing the layer; onDestroy removes the callback, clears the renderer pointer, releases layer + test buffer, in that order.
  • Magnifier-UI overlay forces fallback (and immediate hide) so the GL-rendered overlay isn't hidden under the SC layer at z=1.

Phase 3 — flicker / fence / overlays:

  • Reverted Phase 2.5's GL-skip optimization. The actual perf win is HWC overlay-plane scanout (DPU instead of GPU), not skipping the GL render. Always-render keeps the GLSurfaceView backbuffer in lockstep with the SC layer, so a stale-frame reveal during direct->fallback transition is impossible — whatever SF reveals when SC hides is the same frame the user is already seeing.
  • Cursor handling: cursor is hidden by the activity for fullscreen contexts (renderer.setCursorVisible(false) at setupUI), and direct mode requires fullscreen, so cursor-under-SC is moot in practice.

Containment:

  • Toggle off -> zero behavior change vs. pre-DC. Same code path as today.
  • NDK unavailable (API < 29 or stripped libandroid) -> silent fallback to GL path, no crashes. dlopen + dlsym means the shared library still loads on minSdk 26.
  • SurfaceControl.isAvailable cached after first probe.

…aceControl

Adds an opt-in per-container "Direct Composition" toggle that, when active
on API 29+, routes fullscreen direct-scanout drawables to a child
ASurfaceControl layer bound to the XServerView's SurfaceView. SurfaceFlinger
+ HWC promote the layer to a hardware overlay plane (Snapdragon DPU
scanout), bypassing in-process GLRenderer composition entirely for
qualifying frames. Verified end-to-end on a OnePlus 15 (Android 16,
Adreno X2-class DPU) running a vkd3d-proton title — SurfaceFlinger
dumpsys shows composition type=DEVICE on the winnative-direct-composition
layer with continuous buffer updates and HWC scaling the native game
frame to display resolution.

Phase 1 — toggle plumbing:
  * Container.EXTRA_DIRECT_COMPOSITION + isDirectCompositionEnabled /
    setDirectCompositionEnabled accessors backed by existing extraData
    JSON; default off, no migration needed.
  * ContainerSettingsComposeDialog reads/writes the setting; new
    container creation also sets it on the freshly-built Container.
  * GameSettingsStateHolder.directComposition + a SettingCheckbox in
    the container-edit UI (gated on isContainerEditMode so shortcut
    sheets don't show it).
  * Two new strings: session_display_direct_composition +
    session_display_direct_composition_summary.

Phase 2 — native + lifecycle + push path:
  * cpp/winlator/surface_compositor.c — dlopen-resolved JNI wrappers
    around ASurfaceControl + ASurfaceTransaction (libandroid.so, API
    29+). Probe (nativeIsAvailable), attach/detach (createFromWindow +
    reparent-to-null + release), setColor (proof-of-life), pushBuffer
    (setBuffer + modern setPosition/setScale/setCrop or fallback
    setGeometry, with framework-owned fence FD), allocateTestBuffer +
    releaseBuffer (256x256 magenta smoke test, RGBA_8888 +
    GPU_SAMPLED_IMAGE + COMPOSER_OVERLAY usage, with retry-without-
    OVERLAY for grallocs that reject the combo).
  * SurfaceCompositor.java — static isAvailable() probe with API-29
    short-circuit + cached-availability + UnsatisfiedLinkError guard.
  * DirectCompositionLayer.java — instance class wrapping a single
    ASurfaceControl, all public methods synchronized so UI-thread
    release() and GLThread pushBuffer() can't race the native pointer.
  * Drawable.scanoutSource + directScanout fields made volatile for
    cross-thread visibility between the X-server worker and GLThread.
  * Drawable.acquireFenceFd as AtomicInteger with takeAcquireFenceFd
    (read-and-clear) + setAcquireFenceFd (closes prior FD on overwrite)
    — forward-compatible API for future X11 Present wait_fence
    plumbing; today always -1, documented as empirical observation
    rather than a Mesa contract.
  * GPUImage.getHardwareBufferPtr() — public accessor for the
    underlying AHardwareBuffer*, used by the renderer hook.
  * GLRenderer.directCompositionTarget (volatile) +
    setDirectCompositionTarget setter; per-frame
    maybePushDirectComposition under Drawable.renderLock with
    last-pushed cache to avoid per-frame transaction storm + a
    consecutive-failure counter that self-detaches the target after
    DC_FAIL_LIMIT to stop wasting JNI calls on permanent failure;
    maybeHideDirectComposition on direct->fallback transition,
    idempotent and cache-invalidating.
  * XServerDisplayActivity.installDirectCompositionLifecycle —
    SurfaceHolder.Callback registered against XServerView's holder;
    if the surface is already valid by the time we register
    (GLSurfaceView's GLThread can fire surfaceCreated before setupUI
    returns), synthesize the initial dispatch so we don't miss the
    first attach; on surfaceDestroyed clear the renderer pointer
    BEFORE releasing the layer; onDestroy removes the callback,
    clears the renderer pointer, releases layer + test buffer, in
    that order.
  * Magnifier-UI overlay forces fallback (and immediate hide) so the
    GL-rendered overlay isn't hidden under the SC layer at z=1.

Phase 3 — flicker / fence / overlays:
  * Reverted Phase 2.5's GL-skip optimization. The actual perf win is
    HWC overlay-plane scanout (DPU instead of GPU), not skipping the
    GL render. Always-render keeps the GLSurfaceView backbuffer in
    lockstep with the SC layer, so a stale-frame reveal during
    direct->fallback transition is impossible — whatever SF reveals
    when SC hides is the same frame the user is already seeing.
  * Cursor handling: cursor is hidden by the activity for fullscreen
    contexts (renderer.setCursorVisible(false) at setupUI), and
    direct mode requires fullscreen, so cursor-under-SC is moot in
    practice.

Containment:
  * Toggle off -> zero behavior change vs. pre-DC. Same code path as
    today.
  * NDK unavailable (API < 29 or stripped libandroid) -> silent
    fallback to GL path, no crashes. dlopen + dlsym means the shared
    library still loads on minSdk 26.
  * SurfaceControl.isAvailable cached after first probe.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6f375f16a4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +4670 to +4672
if (xServerView != null) {
xServerView.post(XServerDisplayActivity.this::pushDirectCompositionSmokeTest);
} else {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove unconditional Direct Composition smoke-test push

Calling pushDirectCompositionSmokeTest() on every surfaceCreated makes a visible magenta test buffer part of the normal runtime path whenever Direct Composition is enabled. For sessions that never hit the fullscreen direct-scanout path (or where maybePushDirectComposition returns false), that buffer is never explicitly transitioned out because GLRenderer.maybeHideDirectComposition() only hides when dcLayerActive was set by a successful direct push, so users can end up with a persistent overlay artifact in production gameplay/UI.

Useful? React with 👍 / 👎.

maxjivi05 added 4 commits May 7, 2026 16:57
…ition layer

Some games rendered visibly brighter through the new SurfaceControl path
than via the legacy GLRenderer composition. Root cause is Snapdragon DPU's
mixed-SDR/HDR composition pipeline: SDR layers without explicit
dataspace / opacity / extended-range metadata are routed through an
HDR-aware path that applies a midtone boost via vendor-tuned LUTs. The GL
composition path bypasses that pipeline; the SC path engages it.

Fix: assert three previously-implicit metadata properties on every
pushBuffer transaction so HWC takes the legacy SDR composition path.

surface_compositor.c
  * Resolve three new optional symbols via dlopen/dlsym:
      ASurfaceTransaction_setBufferDataSpace      (API 29)
      ASurfaceTransaction_setBufferTransparency   (API 29)
      ASurfaceTransaction_setExtendedRangeBrightness (API 34)
    Each is independent of the mandatory availability gate — missing
    symbols produce a one-shot init log and skip the call rather than
    failing the whole DC path.
  * nativePushBuffer gains a `jboolean opaque` parameter and, after
    setBuffer, calls (when each symbol resolved):
      setBufferDataSpace(SRGB)            — explicit sRGB tag, no
                                              speculative re-encode
      setBufferTransparency(OPAQUE/TRANSL) — opaque skips alpha-blend
                                              stage, OPAQUE only when
                                              caller asserts alpha=1.0
      setExtendedRangeBrightness(1.0, 1.0)— assertively opts the layer
                                              out of the SDR-on-HDR
                                              boost path
  * One-shot diagnostic log at init prints which colour symbols
    resolved so a post-deploy logcat can distinguish "fix didn't apply"
    from "fix applied, vendor pipeline still boosting" without rebuild.

DirectCompositionLayer.java
  * pushBuffer signature gains `boolean opaque`. Javadoc explains the
    Snapdragon DPU rationale and warns against passing true on
    translucent buffers.

GLRenderer.maybePushDirectComposition
  * Passes opaque=true — DXVK / vkd3d-proton swap-chain frames are
    RGBA8888 with alpha=1.0 throughout by Vulkan WSI convention.

XServerDisplayActivity
  * pushDirectCompositionSmokeTest: passes opaque=true (magenta swatch
    is fully opaque) and shrinks the swatch from 256x256 to 128x128
    (user feedback — less obtrusive while DC is still a developer
    toggle).

Sharpness side-effect: the same HDR-aware pipeline is responsible for
some of the vendor's content-adaptive enhancement (perceived "too sharp"
look). Neutralising it via the three calls above is expected to reduce
both the brightness mismatch and the residual sharpness simultaneously.

Containment: zero impact when the toggle is off; on devices missing any
of the three new symbols the path degrades to the prior (brighter)
behaviour with a single one-shot log line. JNI signatures match;
reviewer pass cleared.
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