Skip to content

Commit 5419836

Browse files
committed
fix PSD tools flicker
1 parent f696fbe commit 5419836

4 files changed

Lines changed: 337 additions & 30 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "frame-script",
33
"private": true,
4-
"version": "0.0.9",
4+
"version": "0.0.10",
55
"type": "module",
66
"scripts": {
77
"dev:vite": "vite --config vite.studio.config.ts",

render/src/main.rs

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,49 @@ async fn wait_for_images_ready(page: &Page) {
163163
page.evaluate(script).await.unwrap();
164164
}
165165

166+
async fn wait_for_audio_waveforms_ready(page: &Page) {
167+
let script = r#"
168+
(async () => {
169+
const api = window.__frameScript;
170+
if (api && typeof api.waitAudioWaveformsReady === "function") {
171+
await api.waitAudioWaveformsReady();
172+
}
173+
})()
174+
"#;
175+
page.evaluate(script).await.unwrap();
176+
}
177+
178+
async fn wait_for_psd_ready(page: &Page) {
179+
let script = r#"
180+
(async () => {
181+
const api = window.__frameScript;
182+
if (api && typeof api.waitPsdReady === "function") {
183+
await api.waitPsdReady();
184+
}
185+
})()
186+
"#;
187+
page.evaluate(script).await.unwrap();
188+
}
189+
190+
async fn wait_for_psd_frame(page: &Page, frame: usize) {
191+
let script = format!(
192+
r#"
193+
(async () => {{
194+
const api = window.__frameScript;
195+
if (api && typeof api.waitPsdFrame === "function") {{
196+
try {{
197+
await api.waitPsdFrame({});
198+
}} catch (_e) {{
199+
// ignore
200+
}}
201+
}}
202+
}})()
203+
"#,
204+
frame
205+
);
206+
page.evaluate(script).await.unwrap();
207+
}
208+
166209
async fn wait_for_webgl_ready(page: &Page) {
167210
let script = r#"
168211
(async () => {
@@ -337,8 +380,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
337380
let completed_clone = completed.clone();
338381
let is_canceled_clone = is_canceled.clone();
339382
tasks.push(tokio::spawn(async move {
340-
let profile_dir =
341-
PathBuf::from(format!("{}/profiles/profile-{:03}", FRAME_DIRECTORY, worker_id));
383+
let profile_dir = PathBuf::from(format!(
384+
"{}/profiles/profile-{:03}",
385+
FRAME_DIRECTORY, worker_id
386+
));
342387

343388
let (mut browser, mut handler) = spawn_browser_instance(profile_dir, width, height)
344389
.await
@@ -361,7 +406,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
361406
ffmpeg_low_memory_clone,
362407
)
363408
.await
364-
.map_err(|error| format!("worker {worker_id}: failed to create ffmpeg writer: {error}"))?;
409+
.map_err(|error| {
410+
format!("worker {worker_id}: failed to create ffmpeg writer: {error}")
411+
})?;
365412

366413
let page = browser
367414
.new_page(page_url)
@@ -373,6 +420,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
373420
wait_for_frame_api(&page).await;
374421
wait_for_animation_ready(&page).await;
375422
wait_for_draw_text_ready(&page).await;
423+
wait_for_audio_waveforms_ready(&page).await;
424+
wait_for_psd_ready(&page).await;
376425
wait_for_webgl_ready(&page).await;
377426

378427
for frame in start..end {
@@ -389,9 +438,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
389438
"#,
390439
frame
391440
);
392-
page.evaluate(js)
393-
.await
394-
.map_err(|error| format!("worker {worker_id}: setFrame eval failed: {error}"))?;
441+
page.evaluate(js).await.map_err(|error| {
442+
format!("worker {worker_id}: setFrame eval failed: {error}")
443+
})?;
395444

396445
wait_for_next_frame(&page).await;
397446

@@ -410,10 +459,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
410459
"#,
411460
frame
412461
);
413-
page.evaluate(script)
414-
.await
415-
.map_err(|error| format!("worker {worker_id}: waitCanvasFrame eval failed: {error}"))?;
462+
page.evaluate(script).await.map_err(|error| {
463+
format!("worker {worker_id}: waitCanvasFrame eval failed: {error}")
464+
})?;
416465

466+
wait_for_audio_waveforms_ready(&page).await;
467+
wait_for_psd_frame(&page, frame).await;
417468
wait_for_images_ready(&page).await;
418469
wait_for_webgl_frame(&page, frame).await;
419470

src/lib/audio-waveform.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,94 @@ export type WaveformData = {
1515
durationSec: number
1616
}
1717

18+
type AudioWaveformTracker = {
19+
pending: number
20+
start: () => () => void
21+
wait: () => Promise<void>
22+
}
23+
24+
const AUDIO_WAVEFORM_TRACKER_KEY = "__frameScript_AudioWaveformTracker"
25+
26+
const getAudioWaveformTracker = (): AudioWaveformTracker => {
27+
const g = globalThis as unknown as Record<string, unknown>
28+
const existing = g[AUDIO_WAVEFORM_TRACKER_KEY] as
29+
| AudioWaveformTracker
30+
| undefined
31+
if (existing) return existing
32+
33+
let pending = 0
34+
const waiters = new Set<() => void>()
35+
36+
const notifyIfReady = () => {
37+
if (pending !== 0) return
38+
for (const resolve of Array.from(waiters)) {
39+
resolve()
40+
}
41+
waiters.clear()
42+
}
43+
44+
const tracker: AudioWaveformTracker = {
45+
get pending() {
46+
return pending
47+
},
48+
start: () => {
49+
pending += 1
50+
let done = false
51+
return () => {
52+
if (done) return
53+
done = true
54+
pending = Math.max(0, pending - 1)
55+
notifyIfReady()
56+
}
57+
},
58+
wait: () => {
59+
if (pending === 0) return Promise.resolve()
60+
return new Promise<void>((resolve) => {
61+
waiters.add(resolve)
62+
})
63+
},
64+
}
65+
66+
g[AUDIO_WAVEFORM_TRACKER_KEY] = tracker
67+
return tracker
68+
}
69+
70+
const waitForAnimationTick = () =>
71+
new Promise<void>((resolve) => {
72+
if (
73+
typeof window === "undefined" ||
74+
typeof window.requestAnimationFrame !== "function"
75+
) {
76+
setTimeout(resolve, 0)
77+
return
78+
}
79+
window.requestAnimationFrame(() => resolve())
80+
})
81+
82+
const installAudioWaveformApi = () => {
83+
if (typeof window === "undefined") return
84+
const tracker = getAudioWaveformTracker()
85+
const waitAudioWaveformsReady = async () => {
86+
while (true) {
87+
if (tracker.pending === 0) {
88+
await waitForAnimationTick()
89+
if (tracker.pending === 0) return
90+
}
91+
await tracker.wait()
92+
}
93+
}
94+
95+
;(window as any).__frameScript = {
96+
...(window as any).__frameScript,
97+
waitAudioWaveformsReady,
98+
getAudioWaveformsPending: () => tracker.pending,
99+
}
100+
}
101+
102+
if (typeof window !== "undefined") {
103+
installAudioWaveformApi()
104+
}
105+
18106
const waveformCache = new Map<string, WaveformData | null>()
19107
const waveformPromises = new Map<string, Promise<WaveformData | null>>()
20108
let waveformAudioContext: AudioContext | null = null
@@ -76,6 +164,7 @@ export const loadWaveformData = async (
76164
const existing = waveformPromises.get(path)
77165
if (existing) return existing
78166

167+
const finishPending = getAudioWaveformTracker().start()
79168
const promise = (async () => {
80169
try {
81170
const ctx = getAudioContext()
@@ -95,6 +184,7 @@ export const loadWaveformData = async (
95184
return null
96185
} finally {
97186
waveformPromises.delete(path)
187+
finishPending()
98188
}
99189
})()
100190

0 commit comments

Comments
 (0)