Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ jobs:
env:
# Lets Gradle resolve the private mcef-nova artifact from GitHub Packages. Works once the
# package grants read access to this repo; until then the vendor/maven fallback covers it.
# mcef-nova v1.0.2 (tag v1.0.2, published to GitHub Packages). The vendor/maven copy has the
# same classes as a build of that tag.
GITHUB_ACTOR: ${{ github.actor }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION_NAME: ${{ needs.decide.outputs.version_name }}
Expand Down
4 changes: 2 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ dependencies {
}

// Version-agnostic CEF fork: a plain library (no net.minecraft), so no Loom remapping.
implementation("com.github.FPSMasterTeam:mcef-nova:1.0.1")
implementation("com.github.FPSMasterTeam:mcef-nova:1.0.2")

// modRuntimeOnly(group = "maven.modrinth", name = "ImmediatelyFast", version = "1.14.2+1.21.11-fabric")
// modApi(group = "maven.modrinth", name = "sodium", version = "mc1.21.11-0.8.12-fabric")
Expand Down Expand Up @@ -319,7 +319,7 @@ dependencies {
bundledRuntime("org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.4.0")
bundledRuntime("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.11.0")
bundledRuntime("org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.11.0")
bundledRuntime("com.github.FPSMasterTeam:mcef-nova:1.0.1")
bundledRuntime("com.github.FPSMasterTeam:mcef-nova:1.0.2")
bundledRuntime("io.github.vlouboos:standaloneevent-common:1.6")
// 只打 Cadence 自己的类:它的两个传递依赖 kotlin-stdlib(上面已 bundle)与 gson(MC 自带)
// 都已在运行时就位,transitive 打进来只会重复/覆盖。
Expand Down
13 changes: 8 additions & 5 deletions src/main/java/top/fpsmaster/mixin/impl/MixinGameRenderer.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,14 @@ public void hookGameRender(CallbackInfo callbackInfo) {
top.fpsmaster.ui.kit.NovaBlur.INSTANCE.beginFrame();
if (MCEF.INSTANCE.isInitialized()) {
try {
// On Windows/Linux CEF runs on its own message-loop thread (mcef-nova's
// CefMessageLoopThread) and this only uploads the frames that thread buffered —
// the render thread never blocks in CefDoMessageLoopWork again (that block, ~13ms
// per frame while the browser was idle, was the "40 fps until the ClickGUI opens"
// Windows bug). On macOS this still pumps CEF on this thread, then uploads.
// With hardware acceleration OFF (default), Windows/Linux CEF runs on its own
// message-loop thread (mcef-nova's CefMessageLoopThread) and this only uploads the
// frames that thread buffered — the render thread never blocks in
// CefDoMessageLoopWork (that block, ~13ms per frame while the browser was idle, was
// the "40 fps until the ClickGUI opens" Windows bug). With acceleration ON
// (mcef.pumpOnRenderThread) — and always on macOS — this pumps CEF on this thread
// first (CEF's accelerated OSR only delivers frames continuously in that mode),
// then adopts/uploads.
MCEF.INSTANCE.update();
} catch (Exception e) {
LogUtil.logger.error("Failed to update browser frames", e);
Expand Down
8 changes: 8 additions & 0 deletions src/main/kotlin/top/fpsmaster/Client.kt
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ class Client : ModInitializer {
return
}
hostConfigured = true
// mcef-nova 1.0.2 only enables CEF's shared-texture (zero-copy) paint when its message loop is
// pumped on the render thread: on its dedicated CEF thread, accelerated OSR stalls after one
// frame. The pump mode is fixed at CEF init, so decide it here from the persisted toggle AND
// actual platform/GPU support — a stale enabled setting can travel to an unsupported machine,
// where pumping on the render thread would only bring back CEF's message-loop cost per frame.
if (ClientSettings.hardwareAcceleration.getValue() && BasicBrowser.isAccelerationAvailable()) {
System.setProperty("mcef.pumpOnRenderThread", "true")
}
// Legacy immediate-mode (<1.21.5) draws the imported zero-copy accel texture through a plain
// position_tex shader, so have mcef R/B-swizzle imported accel textures (CEF ships BGRA imported
// as RGBA8) at import time. 1.21.5+ swaps R/B in a dedicated shader instead and leaves this off.
Expand Down
8 changes: 0 additions & 8 deletions src/main/kotlin/top/fpsmaster/module/impl/auxiliary/Replay.kt
Original file line number Diff line number Diff line change
Expand Up @@ -188,14 +188,6 @@ class Replay : Module("replay", Category.AUXILIARY) {
project.ensureDuration(playback.durationMillis)
session = DirectorSession(playback, store, project)
logger.info("[replay] playing ${file.name}")
if (playback.truncated) {
NotificationManager.add(
Language.get("replay.title"),
Language.get("replay.status.truncated").format(playback.durationMillis / 60_000),
5f,
NotificationCenter.Type.WARNING
)
}
return null
}

Expand Down
25 changes: 20 additions & 5 deletions src/main/kotlin/top/fpsmaster/replay/DirectorSession.kt
Original file line number Diff line number Diff line change
Expand Up @@ -76,19 +76,27 @@ class DirectorSession(
applyCamera(source)
}

/** Client thread, once per frame. */
/** Client thread, once per tick. */
fun tick() {
// The length arrives from a background index a moment after opening.
project.ensureDuration(sourceDuration())
// Whether the frame drawn since the last tick showed a fully loaded world.
val settled = !playback.isCatchingUp
playback.tick()
val running = export
if (running != null) {
running.step()
running.step(settled)
if (running.isFinished) {
export = null
}
return
}
pollRollKeys()
playback.tick()
advanceOutputClock()
// While the world is still loading towards the playhead, positionMillis is where it is
// going, not where it is; following clip edges off it would seek again mid-load.
if (!playback.isCatchingUp) {
advanceOutputClock()
}
applyCamera(playback.positionMillis)
}

Expand Down Expand Up @@ -275,10 +283,17 @@ class DirectorSession(
scrubTo(0L)
}

fun step() {
/**
* [settled]: the world had caught up before this tick, so the frame drawn since shows the
* position set last time. A seek that has not finished loading skips capture until then.
*/
fun step(settled: Boolean) {
if (isFinished) {
return
}
if (!settled || playback.isCatchingUp) {
return
}
if (frameIndex >= plan.frameCount) {
finish()
return
Expand Down
Loading
Loading