github Coder787-source/KytyPlus v3.8

57 minutes ago

KytyPlus v3.8 Release Notes

Tag: v3.8 | License: GPL-2.0 | Platforms: Windows, macOS, Linux


Overview

Two releases' worth of work on one title, and it pays off: Crash Bandicoot 4 now plays its
cutscenes.
The intro video chain runs start to finish instead of stranding the game on the
fourth bumper, and the black window that used to follow the logo is gone.

For everyone else this is a batch of correctness fixes — a fence wait that could never be
re-evaluated, geometry draws being silently skipped, three atomics misclassified as plain
reads, a depth-stencil aspect being demoted while still in use — plus the retirement of a
crashy default and some hang diagnostics that land in your log for any title.

Crash Bandicoot 4: the cutscene chain completes

The v3.7 retest log showed the real deadlock in video 4 (BumperUnreal): the demuxer hit EOF
and both decoders finished, but Active() stayed true for 116 seconds because decoded frames
were still sitting in the queue. The game stops polling GetVideoData near the end of a video,
so the leftovers never drained and the state machine never reached the branch that fires
STATE_STOP. Videos 1-3 escaped only because the game happened to consume their queues fully
before it stopped polling.

End-of-stream is now "demuxer at EOF and both decoders finished" — queued leftovers no
longer keep a player alive. The moment a drained video's workers finish, the natural-EOF gate
fires STATE_STOP exactly once, whether or not the game keeps consuming. That is what unsticks
the fourth bumper, and the whole chain (Intro_delay → BumperAtviNextGen →
BumperTfbNextGen → BumperUnreal) now runs to completion.

The black screen after the logo

Two independent bugs, both fixed.

A fence wait that could never be re-evaluated

CB4 wedged on a black screen while the present loop kept flipping empty frames at ~3 fps and
zero draws reached the GPU. The UE4 RHI issues a release_mem + wait_reg_mem fence
ping-pong on the compute queue forever. A submission blocked on wait_reg_mem sits at the
front of its queue, and the GPU thread only cleared blocked flags once every queue was idle
— so the graphics queue front was never re-polled while the compute queue kept producing work.
The wait was evaluated once, failed once, and was never retried.

  • Blocked queue fronts are now re-pollable every 10 ms even while other queues have work, so
    blocked waits actually get re-evaluated.
  • New per-address stalled-wait watchdog: after 10 s of consecutive unsatisfied polls the wait is
    released and the awaited value written, so guest bookkeeping stays consistent and the run
    self-heals instead of wedging forever. Ten seconds is comfortably longer than an iGPU
    shader-compile burst; normal fences resolve in microseconds and never trip it.
  • write32/write64 and CpOpReleaseMem now guard a null destination — an unpatched
    release_mem label address used to fall through to a memcpy to nullptr.

This one change alone moved CB4 past the splash screen — further than the previously documented
results for this title (upstream v0.0.5.5 crashes after splash, #88).

FSR upscaling on integrated GPUs

The 0xc0 fault — an access violation inside the AMD driver at CB4's first menu present, right
when the display-mode switch fires resize churn — took four commits to pin down honestly, and
ended in "don't do this by default":

  • A surface rebuild swaps the Vulkan device handle, so FSR pipelines and descriptor sets built
    on the old handle were dispatched to a dead one. Dispatch now binds to the creating device
    handle and survives context rebuilds.
  • Destroy() never took the mutex its own header documents as serialising Dispatch against
    Destroy, so a resize could free pipelines and UBOs out from under a recording thread. Now
    locked.
  • The first round of guards was wrong in an instructive way: it required resources to exist at
    Dispatch() entry, but those are created lazily inside Dispatch(). The guard could never
    pass — FSR became impossible, every present silently fell back to blit, and reset() churned
    the upscaler four times per run. The guard now only checks what Create() actually produces.

That correction is what exposed the real answer: FSR 1.0 faults deterministically in the
current AMD driver, independent of our lifetime handling.
The plain blit path displays the
same content without the compute pass (verified: the splash renders through blit on the same
machine), so the iGPU performance floor now keeps only the texture LOD bias. FSR remains
available for explicit opt-in from the launcher; it is no longer auto-enabled on integrated GPUs.

If you have been running with FSR on an AMD iGPU and seeing a driver reset, this release
removes that path from your defaults.

Presentation fixes on integrated GPUs

  • Present mode now floors to FIFO on iGPUs. The launcher's default is Immediate, and
    Immediate on the Radeon 840M presents nothing at all — the only launcher-vs-command-line
    difference the logs turned up was exactly this (presentMode=Fifo on the run that worked,
    Immediate on every run that didn't). The floor is applied in both the config defaults and the
    swapchain, so a config still asking for Immediate or Mailbox can't reintroduce black output.
  • Zero-extent swapchains are refused. The window could self-minimise mid-run; SDL reported a
    0×0 surface, the swapchain was rebuilt at 0×0, and it stayed broken for tens of millions of log
    lines before the next successful resize healed it. A zero extent now keeps the last known size
    and falls back to the configured screen size.
  • The launcher no longer wraps the emulator in cmd /K with CREATE_NEW_CONSOLE. It spawns
    the emulator directly, which also stops the exit code being swallowed.

Rendering correctness fixes

Three bugs that were quietly dropping or corrupting work, all found while chasing the above:

  • Geometry-stage draws were being skipped. CB4 transitions from ES+GS to NGG primgen
    passthrough without clearing the old GS address — disabled stages keep their base registers.
    The skip test rejected any draw carrying a non-zero GS address alongside a non-zero ES address,
    which is precisely the sequence CB4 emits, so its vertex draws all landed in
    Skipping unsupported GE shader draw. Now gated on the GS stage-enable bit, which is what
    actually determines whether a shader address names a live shader.
  • IsAtomic() dropped three atomics. It enumerated AtomicUMin..Or and then fell into
    default: return false, so AtomicXorU32, AtomicFMinF32 and AtomicFMaxF32 were classified
    as plain reads and the IR never marked those image resources atomic. Because of that, the
    compute watchdog had to compensate bluntly and dropped every storage image whose format lacks
    STORAGE_IMAGE_ATOMIC — silently discarding CB4's color clears on RGBA8_UNORM and friends. With
    the flag fixed, the guard can ask the question it actually meant to ask.
  • Depth aspect demotion. Sampling a depth image that had been acquired
    read-only-stencil transited both aspects to read-only, demoting the aspect that was still
    the writable stencil attachment. The transit now adds shader-read access to the existing state
    instead of re-selecting a read-only layout for the whole image.

Movie playback

  • file:// URIs are stripped before the path reaches the filesystem. UE movies use URI form while
    startup movies use plain paths, and the URI form was failing to open silently — the only
    log line named the path without saying the path was the problem. All three early-return
    failures (open, find_stream_info, codec init) now log the reason.

Hang diagnostics

Both land in the log, and both work for any title, not just CB4:

  • Infinite KernelWaitEventFlag / KernelWaitSema waits (timeout == nullptr) now log the object
    pointer, bit pattern / need count, and wait mode.
  • Any single PthreadCondWait exceeding 10 seconds reports the cond/mutex/thread pointers, once
    per second per stuck wait.

These exist because CB4's wall was a silent block — shaders compiled, then no log activity, two
threads pegged, black screen. They turn "it hung" into a named object in the log.

Compatibility table

  • Crash Bandicoot 4: It's About Time (PPSA02433) — the Native (PS5) row still reads
    "Boots / logos only" at v3.6, which this release supersedes. The new verified depth is the
    full intro cutscene chain playing to completion. The row updates through the normal

For testers — please retest

The FSR retirement is a behaviour change on integrated GPUs, and the GPU command-path changes
(watchdog, anti-starvation re-poll) touch every title. If you can, run a build from this release
on your usual title and report anything that changes — a log via Share Log is ideal. The stalls
that previously just looked like "it hangs" should now name themselves in the log, so if you
still see a hang, that log line tells us exactly which object to look at.

Don't miss a new KytyPlus release

NewReleases is sending notifications on new releases.