From 4c260d886f44d2d473882963162c9df80ec686eb Mon Sep 17 00:00:00 2001 From: Richie McIlroy <33632126+richiemcilroy@users.noreply.github.com> Date: Wed, 10 Dec 2025 21:52:12 +0000 Subject: [PATCH 01/31] feat: Massive performance overhaul for editor playback --- .claude/settings.local.json | 3 +- Cargo.lock | 16 + apps/desktop/package.json | 2 + apps/desktop/src-tauri/Cargo.toml | 1 + apps/desktop/src-tauri/src/camera_legacy.rs | 3 + apps/desktop/src-tauri/src/editor_window.rs | 4 +- apps/desktop/src-tauri/src/frame_ws.rs | 176 +++++- .../src-tauri/src/screenshot_editor.rs | 2 + apps/desktop/src/utils/socket.ts | 108 +++- crates/editor/src/editor.rs | 143 ++++- crates/editor/src/playback.rs | 532 ++++++++++++++++-- crates/rendering/src/composite_frame.rs | 62 +- crates/rendering/src/decoder/avassetreader.rs | 168 ++++-- crates/rendering/src/frame_pipeline.rs | 462 +++++++++++++-- crates/rendering/src/layers/camera.rs | 56 +- crates/rendering/src/layers/display.rs | 57 +- crates/rendering/src/lib.rs | 291 +++++----- pnpm-lock.yaml | 60 +- 18 files changed, 1726 insertions(+), 420 deletions(-) diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 7f820b11da..5b6343dd8d 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -30,7 +30,8 @@ "Bash(pnpm exec tsc:*)", "Bash(pnpm biome check:*)", "Bash(pnpm --dir apps/desktop exec tsc:*)", - "Bash(xxd:*)" + "Bash(xxd:*)", + "Bash(git checkout:*)" ], "deny": [], "ask": [] diff --git a/Cargo.lock b/Cargo.lock index 8deccdc263..facd9ce2fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1212,6 +1212,7 @@ dependencies = [ "keyed_priority_queue", "lazy_static", "log", + "lz4_flex", "md5", "nix 0.29.0", "objc", @@ -4956,6 +4957,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" +[[package]] +name = "lz4_flex" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08ab2867e3eeeca90e844d1940eab391c9dc5228783db2ed999acbc0a9ed375a" +dependencies = [ + "twox-hash", +] + [[package]] name = "mac" version = "0.1.1" @@ -10234,6 +10244,12 @@ dependencies = [ "utf-8", ] +[[package]] +name = "twox-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c" + [[package]] name = "typeid" version = "1.0.3" diff --git a/apps/desktop/package.json b/apps/desktop/package.json index b5f4e3d96d..b83a4b3547 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -56,6 +56,7 @@ "@types/react-tooltip": "^4.2.4", "cva": "npm:class-variance-authority@^0.7.0", "effect": "^3.18.4", + "lz4js": "^0.2.0", "mp4box": "^0.5.2", "posthog-js": "^1.215.3", "solid-js": "^1.9.3", @@ -76,6 +77,7 @@ "@tauri-apps/cli": ">=2.1.0", "@total-typescript/ts-reset": "^0.6.1", "@types/dom-webcodecs": "^0.1.11", + "@types/lz4js": "^0.2.1", "@types/uuid": "^9.0.8", "cross-env": "^7.0.3", "typescript": "^5.8.3", diff --git a/apps/desktop/src-tauri/Cargo.toml b/apps/desktop/src-tauri/Cargo.toml index df574378e5..184bb073d1 100644 --- a/apps/desktop/src-tauri/Cargo.toml +++ b/apps/desktop/src-tauri/Cargo.toml @@ -107,6 +107,7 @@ tauri-plugin-sentry = "0.5.0" thiserror.workspace = true bytes = "1.10.1" async-stream = "0.3.6" +lz4_flex = "0.11" sanitize-filename = "0.6.0" tracing-futures = { version = "0.2.5", features = ["futures-03"] } tracing-opentelemetry = "0.32.0" diff --git a/apps/desktop/src-tauri/src/camera_legacy.rs b/apps/desktop/src-tauri/src/camera_legacy.rs index 444d0f24d4..ee99b50fb1 100644 --- a/apps/desktop/src-tauri/src/camera_legacy.rs +++ b/apps/desktop/src-tauri/src/camera_legacy.rs @@ -1,3 +1,5 @@ +use std::time::Instant; + use cap_recording::FFmpegVideoFrame; use flume::Sender; use tokio_util::sync::CancellationToken; @@ -62,6 +64,7 @@ pub async fn create_camera_preview_ws() -> (Sender, u16, Cance width: frame.width(), height: frame.height(), stride: frame.stride(0) as u32, + created_at: Instant::now(), }) .ok(); } diff --git a/apps/desktop/src-tauri/src/editor_window.rs b/apps/desktop/src-tauri/src/editor_window.rs index 2d637a8a7b..587b8426a5 100644 --- a/apps/desktop/src-tauri/src/editor_window.rs +++ b/apps/desktop/src-tauri/src/editor_window.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, ops::Deref, path::PathBuf, sync::Arc}; +use std::{collections::HashMap, ops::Deref, path::PathBuf, sync::Arc, time::Instant}; use tauri::{AppHandle, Manager, Runtime, Window, ipc::CommandArg}; use tokio::sync::{RwLock, broadcast}; use tokio_util::sync::CancellationToken; @@ -33,6 +33,7 @@ async fn do_prewarm(app: AppHandle, path: PathBuf) -> PendingResult { width: frame.width, height: frame.height, stride: frame.padded_bytes_per_row, + created_at: Instant::now(), }); }), ) @@ -188,6 +189,7 @@ impl EditorInstances { width: frame.width, height: frame.height, stride: frame.padded_bytes_per_row, + created_at: Instant::now(), }); }), ) diff --git a/apps/desktop/src-tauri/src/frame_ws.rs b/apps/desktop/src-tauri/src/frame_ws.rs index 16fa506e39..4ad9bc4e35 100644 --- a/apps/desktop/src-tauri/src/frame_ws.rs +++ b/apps/desktop/src-tauri/src/frame_ws.rs @@ -1,12 +1,26 @@ +use std::time::Instant; use tokio::sync::{broadcast, watch}; use tokio_util::sync::CancellationToken; +fn compress_frame_data(mut data: Vec, stride: u32, height: u32, width: u32) -> Vec { + data.extend_from_slice(&stride.to_le_bytes()); + data.extend_from_slice(&height.to_le_bytes()); + data.extend_from_slice(&width.to_le_bytes()); + + let data_len = data.len(); + let mut result = Vec::with_capacity(data_len + 4); + result.extend_from_slice(&(data_len as u32).to_le_bytes()); + result.extend_from_slice(&data); + result +} + #[derive(Clone)] pub struct WSFrame { pub data: Vec, pub width: u32, pub height: u32, pub stride: u32, + pub created_at: Instant, } pub async fn create_watch_frame_ws( @@ -36,19 +50,36 @@ pub async fn create_watch_frame_ws( tracing::info!("Socket connection established"); let now = std::time::Instant::now(); - // Send the current frame immediately upon connection (if one exists) - // This ensures the client doesn't wait for the next config change to see the image + let mut frames_sent = 0u64; + let mut total_latency_us = 0u64; + let mut max_latency_us = 0u64; + { let frame_opt = camera_rx.borrow().clone(); - if let Some(mut frame) = frame_opt { - frame.data.extend_from_slice(&frame.stride.to_le_bytes()); - frame.data.extend_from_slice(&frame.height.to_le_bytes()); - frame.data.extend_from_slice(&frame.width.to_le_bytes()); + if let Some(frame) = frame_opt { + let frame_latency = frame.created_at.elapsed(); + let latency_us = frame_latency.as_micros() as u64; + total_latency_us += latency_us; + max_latency_us = max_latency_us.max(latency_us); + frames_sent += 1; - if let Err(e) = socket.send(Message::Binary(frame.data)).await { + let original_size = frame.data.len(); + let packed = + compress_frame_data(frame.data, frame.stride, frame.height, frame.width); + let compressed_size = packed.len(); + + if let Err(e) = socket.send(Message::Binary(packed)).await { tracing::error!("Failed to send initial frame to socket: {:?}", e); return; } + + tracing::debug!( + frame_latency_us = latency_us, + original_size_bytes = original_size, + compressed_size_bytes = compressed_size, + compression_ratio = %format!("{:.1}%", (compressed_size as f64 / original_size as f64) * 100.0), + "[PERF:WS_WATCH] initial frame sent (compressed)" + ); } } @@ -75,20 +106,58 @@ pub async fn create_watch_frame_ws( break; } let frame_opt = camera_rx.borrow().clone(); - if let Some(mut frame) = frame_opt { - frame.data.extend_from_slice(&frame.stride.to_le_bytes()); - frame.data.extend_from_slice(&frame.height.to_le_bytes()); - frame.data.extend_from_slice(&frame.width.to_le_bytes()); + if let Some(frame) = frame_opt { + let frame_latency = frame.created_at.elapsed(); + let latency_us = frame_latency.as_micros() as u64; + total_latency_us += latency_us; + max_latency_us = max_latency_us.max(latency_us); + frames_sent += 1; + + let send_start = Instant::now(); + let original_size = frame.data.len(); + let compress_start = Instant::now(); + let packed = compress_frame_data(frame.data, frame.stride, frame.height, frame.width); + let compress_time = compress_start.elapsed(); + let compressed_size = packed.len(); - if let Err(e) = socket.send(Message::Binary(frame.data)).await { + let ws_send_start = Instant::now(); + if let Err(e) = socket.send(Message::Binary(packed)).await { tracing::error!("Failed to send frame to socket: {:?}", e); break; } + let ws_send_time = ws_send_start.elapsed(); + let send_time = send_start.elapsed(); + + // #region agent log + use std::io::Write; + if let Ok(mut f) = std::fs::OpenOptions::new().create(true).append(true).open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") { + let _ = writeln!(f, r#"{{"hypothesisId":"D","location":"frame_ws.rs:ws_send","message":"WebSocket frame sent","data":{{"frame_latency_us":{},"compress_time_us":{},"ws_send_time_us":{},"total_send_time_us":{},"original_bytes":{},"compressed_bytes":{}}},"timestamp":{}}}"#, + latency_us, compress_time.as_micros(), ws_send_time.as_micros(), send_time.as_micros(), original_size, compressed_size, std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_millis()); + } + // #endregion + + tracing::debug!( + frame_latency_us = latency_us, + send_time_us = send_time.as_micros() as u64, + original_size_bytes = original_size, + compressed_size_bytes = compressed_size, + "[PERF:WS_WATCH] frame sent (compressed)" + ); } } } } + if frames_sent > 0 { + let avg_latency = total_latency_us / frames_sent; + tracing::info!( + total_frames_sent = frames_sent, + avg_latency_us = avg_latency, + max_latency_us = max_latency_us, + "[PERF:WS_WATCH] session ended - final metrics" + ); + } + let elapsed = now.elapsed(); println!("Websocket closing after {elapsed:.2?}"); tracing::info!("Websocket closing after {elapsed:.2?}"); @@ -143,6 +212,12 @@ pub async fn create_frame_ws(frame_tx: broadcast::Sender) -> (u16, Canc tracing::info!("Socket connection established"); let now = std::time::Instant::now(); + let mut frames_sent = 0u64; + let mut frames_lagged = 0u64; + let mut total_latency_us = 0u64; + let mut max_latency_us = 0u64; + let mut last_metrics_log = Instant::now(); + loop { tokio::select! { msg = socket.recv() => { @@ -162,15 +237,52 @@ pub async fn create_frame_ws(frame_tx: broadcast::Sender) -> (u16, Canc }, incoming_frame = camera_rx.recv() => { match incoming_frame { - Ok(mut frame) => { - frame.data.extend_from_slice(&frame.stride.to_le_bytes()); - frame.data.extend_from_slice(&frame.height.to_le_bytes()); - frame.data.extend_from_slice(&frame.width.to_le_bytes()); + Ok(frame) => { + let frame_latency = frame.created_at.elapsed(); + let latency_us = frame_latency.as_micros() as u64; + total_latency_us += latency_us; + max_latency_us = max_latency_us.max(latency_us); + frames_sent += 1; - if let Err(e) = socket.send(Message::Binary(frame.data)).await { + let send_start = Instant::now(); + let original_size = frame.data.len(); + let compress_start = Instant::now(); + let packed = compress_frame_data(frame.data, frame.stride, frame.height, frame.width); + let compress_time = compress_start.elapsed(); + let compressed_size = packed.len(); + + let ws_send_start = Instant::now(); + if let Err(e) = socket.send(Message::Binary(packed)).await { tracing::error!("Failed to send frame to socket: {:?}", e); break; } + let ws_send_time = ws_send_start.elapsed(); + let send_time = send_start.elapsed(); + + // #region agent log + use std::io::Write; + if let Ok(mut f) = std::fs::OpenOptions::new().create(true).append(true).open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") { + let _ = writeln!(f, r#"{{"hypothesisId":"WS_BROADCAST","location":"frame_ws.rs:broadcast_send","message":"WebSocket broadcast frame sent","data":{{"frame_latency_us":{},"compress_time_us":{},"ws_send_time_us":{},"total_send_time_us":{},"width":{},"height":{}}},"timestamp":{}}}"#, + latency_us, compress_time.as_micros(), ws_send_time.as_micros(), send_time.as_micros(), frame.width, frame.height, std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_millis()); + } + // #endregion + + tracing::debug!( + frame_latency_us = latency_us, + send_time_us = send_time.as_micros() as u64, + original_size_bytes = original_size, + compressed_size_bytes = compressed_size, + width = frame.width, + height = frame.height, + "[PERF:WS] frame sent (compressed)" + ); + + if frame_latency.as_millis() > 50 { + tracing::warn!( + frame_latency_ms = frame_latency.as_millis() as u64, + "[PERF:WS] high frame latency detected" + ); + } } Err(broadcast::error::RecvError::Closed) => { tracing::error!( @@ -179,12 +291,40 @@ pub async fn create_frame_ws(frame_tx: broadcast::Sender) -> (u16, Canc break; } Err(broadcast::error::RecvError::Lagged(skipped)) => { - tracing::warn!("Missed {skipped} frames on websocket receiver"); + frames_lagged += skipped; + tracing::warn!( + skipped = skipped, + total_lagged = frames_lagged, + "[PERF:WS] frames lagged/dropped" + ); continue; } } } } + + if last_metrics_log.elapsed().as_secs() >= 2 && frames_sent > 0 { + let avg_latency = total_latency_us / frames_sent; + tracing::info!( + frames_sent = frames_sent, + frames_lagged = frames_lagged, + avg_latency_us = avg_latency, + max_latency_us = max_latency_us, + "[PERF:WS] periodic metrics" + ); + last_metrics_log = Instant::now(); + } + } + + if frames_sent > 0 { + let avg_latency = total_latency_us / frames_sent; + tracing::info!( + total_frames_sent = frames_sent, + total_frames_lagged = frames_lagged, + avg_latency_us = avg_latency, + max_latency_us = max_latency_us, + "[PERF:WS] session ended - final metrics" + ); } let elapsed = now.elapsed(); diff --git a/apps/desktop/src-tauri/src/screenshot_editor.rs b/apps/desktop/src-tauri/src/screenshot_editor.rs index e52510b7dd..208b44405c 100644 --- a/apps/desktop/src-tauri/src/screenshot_editor.rs +++ b/apps/desktop/src-tauri/src/screenshot_editor.rs @@ -15,6 +15,7 @@ use relative_path::RelativePathBuf; use serde::Serialize; use specta::Type; use std::str::FromStr; +use std::time::Instant; use std::{collections::HashMap, ops::Deref, path::PathBuf, sync::Arc}; use tauri::{ Manager, Runtime, Window, @@ -353,6 +354,7 @@ impl ScreenshotEditorInstances { width: frame.width, height: frame.height, stride: frame.padded_bytes_per_row, + created_at: Instant::now(), })); } Err(e) => { diff --git a/apps/desktop/src/utils/socket.ts b/apps/desktop/src/utils/socket.ts index cd5cbc06c4..d4e61b7a6e 100644 --- a/apps/desktop/src/utils/socket.ts +++ b/apps/desktop/src/utils/socket.ts @@ -1,6 +1,33 @@ import { createWS } from "@solid-primitives/websocket"; +import * as lz4 from "lz4js"; import { createResource, createSignal } from "solid-js"; +interface SocketMetrics { + framesReceived: number; + totalParseTimeMs: number; + totalDecompressTimeMs: number; + totalImageDataTimeMs: number; + maxParseTimeMs: number; + maxDecompressTimeMs: number; + maxImageDataTimeMs: number; + lastLogTime: number; +} + +function decompressLz4(compressedBuffer: ArrayBuffer): Uint8Array { + const view = new DataView(compressedBuffer); + const uncompressedSize = view.getUint32(0, true); + const dataAfterSize = compressedBuffer.byteLength - 4; + + if (dataAfterSize === uncompressedSize) { + return new Uint8Array(compressedBuffer.slice(4)); + } + + const compressedData = new Uint8Array(compressedBuffer, 4); + const output = new Uint8Array(uncompressedSize); + lz4.decompressBlock(compressedData, output, 0, compressedData.length, 0); + return output; +} + export function createImageDataWS( url: string, onmessage: (data: { width: number; data: ImageData }) => void, @@ -8,13 +35,35 @@ export function createImageDataWS( const [isConnected, setIsConnected] = createSignal(false); const ws = createWS(url); + const metrics: SocketMetrics = { + framesReceived: 0, + totalParseTimeMs: 0, + totalDecompressTimeMs: 0, + totalImageDataTimeMs: 0, + maxParseTimeMs: 0, + maxDecompressTimeMs: 0, + maxImageDataTimeMs: 0, + lastLogTime: performance.now(), + }; + ws.addEventListener("open", () => { console.log("WebSocket connected"); setIsConnected(true); + metrics.lastLogTime = performance.now(); }); ws.addEventListener("close", () => { console.log("WebSocket disconnected"); + if (metrics.framesReceived > 0) { + const avgParseTime = metrics.totalParseTimeMs / metrics.framesReceived; + const avgDecompressTime = + metrics.totalDecompressTimeMs / metrics.framesReceived; + const avgImageDataTime = + metrics.totalImageDataTimeMs / metrics.framesReceived; + console.log( + `[PERF:FRONTEND_WS] session ended - frames: ${metrics.framesReceived}, avg decompress: ${avgDecompressTime.toFixed(2)}ms, avg parse: ${avgParseTime.toFixed(2)}ms, avg imageData: ${avgImageDataTime.toFixed(2)}ms, max decompress: ${metrics.maxDecompressTimeMs.toFixed(2)}ms, max parse: ${metrics.maxParseTimeMs.toFixed(2)}ms, max imageData: ${metrics.maxImageDataTimeMs.toFixed(2)}ms`, + ); + } setIsConnected(false); }); @@ -25,8 +74,22 @@ export function createImageDataWS( ws.binaryType = "arraybuffer"; ws.onmessage = (event) => { - const buffer = event.data as ArrayBuffer; - const clamped = new Uint8ClampedArray(buffer); + const frameStart = performance.now(); + const compressedBuffer = event.data as ArrayBuffer; + const compressedSize = compressedBuffer.byteLength; + + const decompressStart = performance.now(); + let decompressed: Uint8Array; + try { + decompressed = decompressLz4(compressedBuffer); + } catch (e) { + console.error("Failed to decompress frame:", e); + return; + } + const decompressTime = performance.now() - decompressStart; + + const buffer = decompressed.buffer; + const clamped = new Uint8ClampedArray(decompressed); if (clamped.length < 12) { console.error("Received frame too small to contain metadata"); return; @@ -64,6 +127,7 @@ export function createImageDataWS( let pixels: Uint8ClampedArray; + const strideConvertStart = performance.now(); if (strideBytes === expectedRowBytes) { pixels = source.subarray(0, expectedLength); } else { @@ -77,9 +141,49 @@ export function createImageDataWS( ); } } + const parseTime = performance.now() - frameStart; + const imageDataStart = performance.now(); const imageData = new ImageData(pixels, width, height); onmessage({ width, data: imageData }); + const imageDataTime = performance.now() - imageDataStart; + + metrics.framesReceived++; + metrics.totalDecompressTimeMs += decompressTime; + metrics.totalParseTimeMs += parseTime; + metrics.totalImageDataTimeMs += imageDataTime; + metrics.maxDecompressTimeMs = Math.max( + metrics.maxDecompressTimeMs, + decompressTime, + ); + metrics.maxParseTimeMs = Math.max(metrics.maxParseTimeMs, parseTime); + metrics.maxImageDataTimeMs = Math.max( + metrics.maxImageDataTimeMs, + imageDataTime, + ); + + const now = performance.now(); + if (now - metrics.lastLogTime >= 2000 && metrics.framesReceived > 0) { + const avgDecompressTime = + metrics.totalDecompressTimeMs / metrics.framesReceived; + const avgParseTime = metrics.totalParseTimeMs / metrics.framesReceived; + const avgImageDataTime = + metrics.totalImageDataTimeMs / metrics.framesReceived; + const compressionRatio = ( + (compressedSize / decompressed.length) * + 100 + ).toFixed(1); + console.log( + `[PERF:FRONTEND_WS] periodic - frames: ${metrics.framesReceived}, compressed: ${compressedSize} bytes (${compressionRatio}%), decompressed: ${decompressed.length} bytes, avg decompress: ${avgDecompressTime.toFixed(2)}ms, avg parse: ${avgParseTime.toFixed(2)}ms, avg imageData: ${avgImageDataTime.toFixed(2)}ms, dimensions: ${width}x${height}`, + ); + metrics.lastLogTime = now; + } + + if (parseTime > 10) { + console.warn( + `[PERF:FRONTEND_WS] high parse time: ${parseTime.toFixed(2)}ms for ${width}x${height} frame`, + ); + } }; return [ws, isConnected]; diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 28171fa991..e40261e0b6 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1,4 +1,5 @@ use std::sync::Arc; +use std::time::Instant; use cap_project::{CursorEvents, RecordingMeta, StudioRecordingMeta}; use cap_rendering::{ @@ -6,6 +7,7 @@ use cap_rendering::{ RenderVideoConstants, RenderedFrame, RendererLayers, }; use tokio::sync::{mpsc, oneshot}; +use tracing::{debug, info}; #[allow(clippy::large_enum_variant)] pub enum RendererMessage { @@ -56,7 +58,7 @@ impl Renderer { let total_frames = (30_f64 * max_duration).ceil() as u32; - let (tx, rx) = mpsc::channel(4); + let (tx, rx) = mpsc::channel(8); let this = Self { rx, @@ -81,12 +83,22 @@ impl Renderer { uniforms: ProjectUniforms, finished: oneshot::Sender<()>, cursor: Arc, - #[allow(dead_code)] frame_number: u32, } let mut pending_frame: Option = None; + let mut frames_rendered = 0u64; + let mut frames_dropped = 0u64; + let mut total_render_time_us = 0u64; + let mut total_callback_time_us = 0u64; + let mut max_render_time_us = 0u64; + let mut max_callback_time_us = 0u64; + let mut last_metrics_log = Instant::now(); + let start_time = Instant::now(); + + info!("[PERF:EDITOR_RENDER] renderer loop started"); + loop { let frame_to_render = if let Some(pending) = pending_frame.take() { Some(pending) @@ -107,6 +119,27 @@ impl Renderer { }), Some(RendererMessage::Stop { finished }) => { let _ = finished.send(()); + let elapsed = start_time.elapsed(); + let avg_render_time = if frames_rendered > 0 { + total_render_time_us / frames_rendered + } else { + 0 + }; + let avg_callback_time = if frames_rendered > 0 { + total_callback_time_us / frames_rendered + } else { + 0 + }; + info!( + elapsed_ms = elapsed.as_millis() as u64, + frames_rendered = frames_rendered, + frames_dropped = frames_dropped, + avg_render_time_us = avg_render_time, + avg_callback_time_us = avg_callback_time, + max_render_time_us = max_render_time_us, + max_callback_time_us = max_callback_time_us, + "[PERF:EDITOR_RENDER] renderer stopped - final metrics" + ); return; } None => return, @@ -117,6 +150,7 @@ impl Renderer { continue; }; + let mut dropped_in_batch = 0u32; while let Ok(msg) = self.rx.try_recv() { match msg { RendererMessage::RenderFrame { @@ -126,6 +160,7 @@ impl Renderer { cursor, frame_number, } => { + dropped_in_batch += 1; let _ = current.finished.send(()); current = PendingFrame { segment_frames, @@ -143,6 +178,56 @@ impl Renderer { } } + if dropped_in_batch > 0 { + frames_dropped += dropped_in_batch as u64; + debug!( + dropped_frames = dropped_in_batch, + total_dropped = frames_dropped, + "[PERF:EDITOR_RENDER] dropped frames to catch up" + ); + + // #region agent log + use std::io::Write; + if let Ok(mut f) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") + { + let _ = writeln!( + f, + r#"{{"hypothesisId":"A","location":"editor.rs:frames_dropped","message":"Renderer dropped frames due to backpressure","data":{{"dropped_in_batch":{},"total_dropped":{},"rendering_frame":{}}},"timestamp":{}}}"#, + dropped_in_batch, + frames_dropped, + current.frame_number, + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() + ); + } + // #endregion + } + + // #region agent log + use std::io::Write; + if let Ok(mut f) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") + { + let _ = writeln!( + f, + r#"{{"hypothesisId":"A","location":"editor.rs:render_start","message":"Starting GPU render","data":{{"frame_number":{}}},"timestamp":{}}}"#, + current.frame_number, + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() + ); + } + // #endregion + + let render_start = Instant::now(); let frame = frame_renderer .render( current.segment_frames, @@ -152,8 +237,60 @@ impl Renderer { ) .await .unwrap(); + let render_time = render_start.elapsed(); + + // #region agent log + if let Ok(mut f) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") + { + let _ = writeln!( + f, + r#"{{"hypothesisId":"A","location":"editor.rs:render_complete","message":"GPU render complete","data":{{"frame_number":{},"render_time_us":{}}},"timestamp":{}}}"#, + current.frame_number, + render_time.as_micros(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() + ); + } + // #endregion + let callback_start = Instant::now(); (self.frame_cb)(frame); + let callback_time = callback_start.elapsed(); + + frames_rendered += 1; + let render_time_us = render_time.as_micros() as u64; + let callback_time_us = callback_time.as_micros() as u64; + total_render_time_us += render_time_us; + total_callback_time_us += callback_time_us; + max_render_time_us = max_render_time_us.max(render_time_us); + max_callback_time_us = max_callback_time_us.max(callback_time_us); + + debug!( + frame_number = current.frame_number, + render_time_us = render_time_us, + callback_time_us = callback_time_us, + "[PERF:EDITOR_RENDER] frame rendered" + ); + + if last_metrics_log.elapsed().as_secs() >= 2 && frames_rendered > 0 { + let avg_render_time = total_render_time_us / frames_rendered; + let avg_callback_time = total_callback_time_us / frames_rendered; + info!( + frames_rendered = frames_rendered, + frames_dropped = frames_dropped, + avg_render_time_us = avg_render_time, + avg_callback_time_us = avg_callback_time, + max_render_time_us = max_render_time_us, + max_callback_time_us = max_callback_time_us, + "[PERF:EDITOR_RENDER] periodic metrics" + ); + last_metrics_log = Instant::now(); + } let _ = current.finished.send(()); } @@ -183,7 +320,7 @@ impl RendererHandle { }) .await; - finished_rx.await.ok(); + let _ = finished_rx.await; } pub async fn stop(&self) { diff --git a/crates/editor/src/playback.rs b/crates/editor/src/playback.rs index ab66f429a6..04a0455298 100644 --- a/crates/editor/src/playback.rs +++ b/crates/editor/src/playback.rs @@ -12,14 +12,14 @@ use cpal::{ use futures::stream::{FuturesUnordered, StreamExt}; use std::{ collections::{HashSet, VecDeque}, - sync::Arc, + sync::{Arc, RwLock}, time::Duration, }; use tokio::{ sync::{mpsc as tokio_mpsc, watch}, time::Instant, }; -use tracing::{error, info, trace, warn}; +use tracing::{debug, error, info, warn}; use crate::{ audio::{AudioPlaybackBuffer, AudioSegment}, @@ -28,8 +28,9 @@ use crate::{ segments::get_audio_segments, }; -const PREFETCH_BUFFER_SIZE: usize = 16; -const PARALLEL_DECODE_TASKS: usize = 4; +const PREFETCH_BUFFER_SIZE: usize = 64; +const PARALLEL_DECODE_TASKS: usize = 8; +const MAX_PREFETCH_AHEAD: u32 = 90; #[derive(Debug)] pub enum PlaybackStartError { @@ -90,6 +91,11 @@ impl Playback { let (prefetch_tx, mut prefetch_rx) = tokio_mpsc::channel::(PREFETCH_BUFFER_SIZE * 2); let (frame_request_tx, mut frame_request_rx) = watch::channel(self.start_frame_number); + let (playback_position_tx, playback_position_rx) = watch::channel(self.start_frame_number); + + let in_flight_frames: Arc>> = Arc::new(RwLock::new(HashSet::new())); + let prefetch_in_flight = in_flight_frames.clone(); + let main_in_flight = in_flight_frames; let prefetch_stop_rx = stop_rx.clone(); let prefetch_project = self.project.clone(); @@ -103,7 +109,6 @@ impl Playback { tokio::spawn(async move { let mut next_prefetch_frame = *frame_request_rx.borrow(); let mut in_flight: FuturesUnordered<_> = FuturesUnordered::new(); - let mut in_flight_frames: HashSet = HashSet::new(); loop { if *prefetch_stop_rx.borrow() { @@ -114,19 +119,33 @@ impl Playback { let requested = *frame_request_rx.borrow_and_update(); if requested > next_prefetch_frame { next_prefetch_frame = requested; - in_flight_frames.retain(|&f| f >= requested); + if let Ok(mut in_flight_guard) = prefetch_in_flight.write() { + in_flight_guard.retain(|&f| f >= requested); + } } } + let current_playback_frame = *playback_position_rx.borrow(); + let max_prefetch_frame = current_playback_frame + MAX_PREFETCH_AHEAD; + while in_flight.len() < PARALLEL_DECODE_TASKS { let frame_num = next_prefetch_frame; + + if frame_num > max_prefetch_frame { + break; + } + let prefetch_time = frame_num as f64 / fps_f64; if prefetch_time >= prefetch_duration { break; } - if in_flight_frames.contains(&frame_num) { + let already_in_flight = prefetch_in_flight + .read() + .map(|guard| guard.contains(&frame_num)) + .unwrap_or(false); + if already_in_flight { next_prefetch_frame += 1; continue; } @@ -148,12 +167,25 @@ impl Playback { let hide_camera = project.camera.hide; let segment_index = segment.recording_clip; - in_flight_frames.insert(frame_num); + if let Ok(mut in_flight_guard) = prefetch_in_flight.write() { + in_flight_guard.insert(frame_num); + } in_flight.push(async move { + let decode_start = Instant::now(); let result = decoders .get_frames(segment_time as f32, !hide_camera, clip_offsets) .await; + let decode_time = decode_start.elapsed(); + + // #region agent log + use std::io::Write; + if let Ok(mut f) = std::fs::OpenOptions::new().create(true).append(true).open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") { + let _ = writeln!(f, r#"{{"hypothesisId":"DECODE","location":"playback.rs:prefetch_decode","message":"Frame decoded by prefetch","data":{{"frame_number":{},"decode_time_ms":{}}},"timestamp":{}}}"#, + frame_num, decode_time.as_millis(), std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_millis()); + } + // #endregion + (frame_num, segment_index, result) }); } @@ -165,7 +197,9 @@ impl Playback { biased; Some((frame_num, segment_index, result)) = in_flight.next() => { - in_flight_frames.remove(&frame_num); + if let Ok(mut in_flight_guard) = prefetch_in_flight.write() { + in_flight_guard.remove(&frame_num); + } if let Some(segment_frames) = result { let _ = prefetch_tx.send(PrefetchedFrame { frame_number: frame_num, @@ -181,8 +215,6 @@ impl Playback { }); tokio::spawn(async move { - let start = Instant::now(); - let duration = if let Some(timeline) = &self.project.borrow().timeline { timeline.duration() } else { @@ -204,12 +236,122 @@ impl Playback { VecDeque::with_capacity(PREFETCH_BUFFER_SIZE); let max_frame_skip = 3u32; + let mut total_frames_rendered = 0u64; + let mut total_frames_skipped = 0u64; + let mut prefetch_hits = 0u64; + let mut prefetch_misses = 0u64; + let mut total_render_time_us = 0u64; + let mut max_render_time_us = 0u64; + let mut last_metrics_log = Instant::now(); + + info!( + start_frame = self.start_frame_number, + fps = fps, + duration_secs = duration, + "[PERF:PLAYBACK] starting playback" + ); + + let warmup_target_frames = 10usize; + let warmup_first_frame_timeout = Duration::from_millis(3000); + let warmup_additional_timeout = Duration::from_millis(2000); + let warmup_start = Instant::now(); + let mut first_frame_time: Option = None; + + while !*stop_rx.borrow() { + let timed_out = if let Some(first_time) = first_frame_time { + first_time.elapsed() > warmup_additional_timeout + } else { + warmup_start.elapsed() > warmup_first_frame_timeout + }; + + if timed_out { + if prefetch_buffer.is_empty() { + warn!( + elapsed_ms = warmup_start.elapsed().as_millis() as u64, + "[PERF:PLAYBACK] warmup timeout waiting for first frame" + ); + } + break; + } + + if first_frame_time.is_some() && prefetch_buffer.len() >= warmup_target_frames { + break; + } + + tokio::select! { + Some(prefetched) = prefetch_rx.recv() => { + if prefetched.frame_number >= frame_number { + let received_frame = prefetched.frame_number; + prefetch_buffer.push_back(prefetched); + if first_frame_time.is_none() { + first_frame_time = Some(Instant::now()); + debug!( + frame = received_frame, + wait_ms = warmup_start.elapsed().as_millis() as u64, + "[PERF:PLAYBACK] first frame received" + ); + } + } + } + _ = tokio::time::sleep(Duration::from_millis(10)) => {} + } + } + + prefetch_buffer + .make_contiguous() + .sort_by_key(|p| p.frame_number); + + let warmup_time = warmup_start.elapsed(); + info!( + warmup_frames = prefetch_buffer.len(), + warmup_time_ms = warmup_time.as_millis() as u64, + first_frame_ready = first_frame_time.is_some(), + "[PERF:PLAYBACK] warmup complete" + ); + + // #region agent log + { + use std::io::Write; + if let Ok(mut f) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") + { + let _ = writeln!( + f, + r#"{{"hypothesisId":"WARMUP","location":"playback.rs:warmup","message":"Warmup phase completed","data":{{"warmup_frames":{},"warmup_time_ms":{},"first_frame_ready":{}}},"timestamp":{}}}"#, + prefetch_buffer.len(), + warmup_time.as_millis(), + first_frame_time.is_some(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() + ); + } + } + // #endregion + + let start = Instant::now(); + 'playback: loop { while let Ok(prefetched) = prefetch_rx.try_recv() { if prefetched.frame_number >= frame_number { prefetch_buffer.push_back(prefetched); - if prefetch_buffer.len() > PREFETCH_BUFFER_SIZE { - prefetch_buffer.pop_front(); + while prefetch_buffer.len() > PREFETCH_BUFFER_SIZE { + if let Some(idx) = prefetch_buffer + .iter() + .enumerate() + .filter(|(_, p)| { + p.frame_number > frame_number + PREFETCH_BUFFER_SIZE as u32 + }) + .max_by_key(|(_, p)| p.frame_number) + .map(|(i, _)| i) + { + prefetch_buffer.remove(idx); + } else { + prefetch_buffer.pop_front(); + } } } } @@ -217,11 +359,41 @@ impl Playback { let frame_offset = frame_number.saturating_sub(self.start_frame_number) as f64; let next_deadline = start + frame_duration.mul_f64(frame_offset); + // #region agent log + let sleep_start = Instant::now(); + let time_until_deadline = next_deadline.saturating_duration_since(Instant::now()); + // #endregion + tokio::select! { _ = stop_rx.changed() => break 'playback, _ = tokio::time::sleep_until(next_deadline) => {} } + // #region agent log + let sleep_time = sleep_start.elapsed(); + use std::io::Write; + if sleep_time.as_millis() > 50 || time_until_deadline.as_millis() > 50 { + if let Ok(mut f) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") + { + let _ = writeln!( + f, + r#"{{"hypothesisId":"SLEEP","location":"playback.rs:sleep_until","message":"Frame deadline sleep","data":{{"frame_number":{},"time_until_deadline_ms":{},"actual_sleep_ms":{},"elapsed_since_start_ms":{}}},"timestamp":{}}}"#, + frame_number, + time_until_deadline.as_millis(), + sleep_time.as_millis(), + start.elapsed().as_millis(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() + ); + } + } + // #endregion + if *stop_rx.borrow() { break; } @@ -237,39 +409,173 @@ impl Playback { .iter() .position(|p| p.frame_number == frame_number); + let frame_fetch_start = Instant::now(); + let was_prefetched = prefetched_idx.is_some(); + let segment_frames_opt = if let Some(idx) = prefetched_idx { + prefetch_hits += 1; let prefetched = prefetch_buffer.remove(idx).unwrap(); Some((prefetched.segment_frames, prefetched.segment_index)) } else { - let Some((segment_time, segment)) = project.get_segment_time(playback_time) - else { - break; - }; + let is_in_flight = main_in_flight + .read() + .map(|guard| guard.contains(&frame_number)) + .unwrap_or(false); + + if is_in_flight { + let wait_start = Instant::now(); + let max_wait = Duration::from_millis(100); + let mut found_frame = None; + + // #region agent log + use std::io::Write; + if let Ok(mut f) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") + { + let _ = writeln!( + f, + r#"{{"hypothesisId":"INFLIGHT_WAIT","location":"playback.rs:in_flight_wait_start","message":"Frame in flight - waiting on channel","data":{{"frame_number":{},"elapsed_since_start_ms":{}}},"timestamp":{}}}"#, + frame_number, + start.elapsed().as_millis(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() + ); + } + // #endregion + + while wait_start.elapsed() < max_wait { + tokio::select! { + _ = stop_rx.changed() => break 'playback, + Some(prefetched) = prefetch_rx.recv() => { + if prefetched.frame_number == frame_number { + found_frame = Some(prefetched); + break; + } else if prefetched.frame_number >= self.start_frame_number { + prefetch_buffer.push_back(prefetched); + } + } + _ = tokio::time::sleep(Duration::from_millis(5)) => { + let still_in_flight = main_in_flight + .read() + .map(|guard| guard.contains(&frame_number)) + .unwrap_or(false); + if !still_in_flight { + break; + } + } + } + } - let Some(segment_media) = - self.segment_medias.get(segment.recording_clip as usize) - else { - frame_number = frame_number.saturating_add(1); - continue; - }; + // #region agent log + let wait_time = wait_start.elapsed(); + if let Ok(mut f) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") + { + let _ = writeln!( + f, + r#"{{"hypothesisId":"INFLIGHT_WAIT","location":"playback.rs:in_flight_wait_done","message":"In-flight wait completed","data":{{"frame_number":{},"wait_ms":{},"found":{}}},"timestamp":{}}}"#, + frame_number, + wait_time.as_millis(), + found_frame.is_some(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() + ); + } + // #endregion + + if let Some(prefetched) = found_frame { + prefetch_hits += 1; + Some((prefetched.segment_frames, prefetched.segment_index)) + } else { + let prefetched_idx = prefetch_buffer + .iter() + .position(|p| p.frame_number == frame_number); + if let Some(idx) = prefetched_idx { + prefetch_hits += 1; + let prefetched = prefetch_buffer.remove(idx).unwrap(); + Some((prefetched.segment_frames, prefetched.segment_index)) + } else { + frame_number = frame_number.saturating_add(1); + total_frames_skipped += 1; + continue; + } + } + } else { + prefetch_misses += 1; + + // #region agent log + use std::io::Write; + if let Ok(mut f) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") + { + let _ = writeln!( + f, + r#"{{"hypothesisId":"C","location":"playback.rs:prefetch_miss","message":"Prefetch miss - decoding on main loop","data":{{"frame_number":{},"prefetch_buffer_size":{}}},"timestamp":{}}}"#, + frame_number, + prefetch_buffer.len(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() + ); + } + // #endregion + let Some((segment_time, segment)) = project.get_segment_time(playback_time) + else { + break; + }; + + let Some(segment_media) = + self.segment_medias.get(segment.recording_clip as usize) + else { + frame_number = frame_number.saturating_add(1); + continue; + }; - let clip_offsets = project - .clips - .iter() - .find(|v| v.index == segment.recording_clip) - .map(|v| v.offsets) - .unwrap_or_default(); - - let data = tokio::select! { - _ = stop_rx.changed() => break 'playback, - data = segment_media - .decoders - .get_frames(segment_time as f32, !project.camera.hide, clip_offsets) => data, - }; + let clip_offsets = project + .clips + .iter() + .find(|v| v.index == segment.recording_clip) + .map(|v| v.offsets) + .unwrap_or_default(); - data.map(|frames| (frames, segment.recording_clip)) + if let Ok(mut guard) = main_in_flight.write() { + guard.insert(frame_number); + } + + let data = tokio::select! { + _ = stop_rx.changed() => { + if let Ok(mut guard) = main_in_flight.write() { + guard.remove(&frame_number); + } + break 'playback + }, + data = segment_media + .decoders + .get_frames(segment_time as f32, !project.camera.hide, clip_offsets) => { + if let Ok(mut guard) = main_in_flight.write() { + guard.remove(&frame_number); + } + data + }, + }; + + data.map(|frames| (frames, segment.recording_clip)) + } }; + let frame_fetch_time = frame_fetch_start.elapsed(); + if let Some((segment_frames, segment_index)) = segment_frames_opt { let Some(segment_media) = self.segment_medias.get(segment_index as usize) else { @@ -277,6 +583,7 @@ impl Playback { continue; }; + let uniforms_start = Instant::now(); let uniforms = ProjectUniforms::new( &self.render_constants, &project, @@ -286,7 +593,31 @@ impl Playback { &segment_media.cursor, &segment_frames, ); + let uniforms_time = uniforms_start.elapsed(); + + // #region agent log + use std::io::Write; + if let Ok(mut f) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") + { + let _ = writeln!( + f, + r#"{{"hypothesisId":"B","location":"playback.rs:render_frame_start","message":"About to call render_frame","data":{{"frame_number":{},"prefetch_hit":{},"prefetch_buffer_size":{},"frame_fetch_us":{}}},"timestamp":{}}}"#, + frame_number, + was_prefetched, + prefetch_buffer.len(), + frame_fetch_time.as_micros(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() + ); + } + // #endregion + let render_start = Instant::now(); self.renderer .render_frame( segment_frames, @@ -295,33 +626,150 @@ impl Playback { frame_number, ) .await; + let render_time = render_start.elapsed(); + + // #region agent log + if let Ok(mut f) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") + { + let _ = writeln!( + f, + r#"{{"hypothesisId":"B","location":"playback.rs:render_frame_done","message":"render_frame returned (channel send complete)","data":{{"frame_number":{},"render_send_time_us":{}}},"timestamp":{}}}"#, + frame_number, + render_time.as_micros(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() + ); + } + // #endregion + + total_frames_rendered += 1; + let render_time_us = render_time.as_micros() as u64; + total_render_time_us += render_time_us; + max_render_time_us = max_render_time_us.max(render_time_us); + + debug!( + frame = frame_number, + prefetch_hit = was_prefetched, + prefetch_buffer_size = prefetch_buffer.len(), + frame_fetch_us = frame_fetch_time.as_micros() as u64, + uniforms_us = uniforms_time.as_micros() as u64, + render_us = render_time_us, + "[PERF:PLAYBACK] frame rendered" + ); } event_tx.send(PlaybackEvent::Frame(frame_number)).ok(); frame_number = frame_number.saturating_add(1); + let _ = playback_position_tx.send(frame_number); let expected_frame = self.start_frame_number + (start.elapsed().as_secs_f64() * fps_f64).floor() as u32; if frame_number < expected_frame { let frames_behind = expected_frame - frame_number; - if frames_behind <= max_frame_skip { + let skipped = if frames_behind <= max_frame_skip { frame_number = expected_frame; - trace!("Skipping {} frames to catch up", frames_behind); + frames_behind } else { frame_number += max_frame_skip; - trace!( - "Limiting frame skip to {} (was {} behind)", - max_frame_skip, frames_behind + max_frame_skip + }; + + total_frames_skipped += skipped as u64; + + // #region agent log + use std::io::Write; + if let Ok(mut f) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") + { + let _ = writeln!( + f, + r#"{{"hypothesisId":"B","location":"playback.rs:frame_skip","message":"Playback loop skipping frames","data":{{"frames_behind":{},"frames_skipped":{},"new_frame_number":{},"total_skipped":{},"elapsed_since_start_ms":{}}},"timestamp":{}}}"#, + frames_behind, + skipped, + frame_number, + total_frames_skipped, + start.elapsed().as_millis(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() ); } + // #endregion + + info!( + frames_behind = frames_behind, + frames_skipped = skipped, + current_frame = frame_number, + total_skipped = total_frames_skipped, + "[PERF:PLAYBACK] skipping frames to catch up" + ); prefetch_buffer.retain(|p| p.frame_number >= frame_number); let _ = frame_request_tx.send(frame_number); + let _ = playback_position_tx.send(frame_number); + } + + if last_metrics_log.elapsed().as_secs() >= 2 { + let avg_render_time = if total_frames_rendered > 0 { + total_render_time_us / total_frames_rendered + } else { + 0 + }; + + info!( + total_rendered = total_frames_rendered, + total_skipped = total_frames_skipped, + prefetch_hit_rate = format!( + "{:.1}%", + (prefetch_hits as f64 + / (prefetch_hits + prefetch_misses).max(1) as f64) + * 100.0 + ), + avg_render_time_us = avg_render_time, + max_render_time_us = max_render_time_us, + prefetch_buffer_size = prefetch_buffer.len(), + "[PERF:PLAYBACK] periodic metrics" + ); + + last_metrics_log = Instant::now(); } } + let total_elapsed = start.elapsed(); + let avg_render_time = if total_frames_rendered > 0 { + total_render_time_us / total_frames_rendered + } else { + 0 + }; + + info!( + total_elapsed_ms = total_elapsed.as_millis() as u64, + total_rendered = total_frames_rendered, + total_skipped = total_frames_skipped, + prefetch_hit_rate = format!( + "{:.1}%", + (prefetch_hits as f64 / (prefetch_hits + prefetch_misses).max(1) as f64) + * 100.0 + ), + avg_render_time_us = avg_render_time, + max_render_time_us = max_render_time_us, + effective_fps = format!( + "{:.1}", + total_frames_rendered as f64 / total_elapsed.as_secs_f64() + ), + "[PERF:PLAYBACK] playback ended - final metrics" + ); + stop_tx.send(true).ok(); event_tx.send(PlaybackEvent::Stop).ok(); diff --git a/crates/rendering/src/composite_frame.rs b/crates/rendering/src/composite_frame.rs index 8d2eaae2fd..e4742b1159 100644 --- a/crates/rendering/src/composite_frame.rs +++ b/crates/rendering/src/composite_frame.rs @@ -6,6 +6,7 @@ use crate::create_shader_render_pipeline; pub struct CompositeVideoFramePipeline { pub bind_group_layout: wgpu::BindGroupLayout, pub render_pipeline: wgpu::RenderPipeline, + sampler: wgpu::Sampler, } #[derive(Debug, Clone, Copy, Pod, Zeroable)] @@ -98,9 +99,20 @@ impl CompositeVideoFramePipeline { include_wgsl!("shaders/composite-video-frame.wgsl"), ); + let sampler = device.create_sampler(&wgpu::SamplerDescriptor { + address_mode_u: wgpu::AddressMode::ClampToEdge, + address_mode_v: wgpu::AddressMode::ClampToEdge, + address_mode_w: wgpu::AddressMode::ClampToEdge, + mag_filter: wgpu::FilterMode::Linear, + min_filter: wgpu::FilterMode::Linear, + mipmap_filter: wgpu::FilterMode::Linear, + ..Default::default() + }); + Self { bind_group_layout, render_pipeline, + sampler, } } @@ -144,38 +156,24 @@ impl CompositeVideoFramePipeline { uniforms: &wgpu::Buffer, frame: &wgpu::TextureView, ) -> wgpu::BindGroup { - let sampler = device.create_sampler( - &(wgpu::SamplerDescriptor { - address_mode_u: wgpu::AddressMode::ClampToEdge, - address_mode_v: wgpu::AddressMode::ClampToEdge, - address_mode_w: wgpu::AddressMode::ClampToEdge, - mag_filter: wgpu::FilterMode::Linear, - min_filter: wgpu::FilterMode::Linear, - mipmap_filter: wgpu::FilterMode::Linear, - ..Default::default() - }), - ); - - device.create_bind_group( - &(wgpu::BindGroupDescriptor { - layout: &self.bind_group_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: uniforms.as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::TextureView(frame), - }, - wgpu::BindGroupEntry { - binding: 2, - resource: wgpu::BindingResource::Sampler(&sampler), - }, - ], - label: Some("bind_group"), - }), - ) + device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &self.bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: uniforms.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::TextureView(frame), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: wgpu::BindingResource::Sampler(&self.sampler), + }, + ], + label: Some("bind_group"), + }) } pub fn create_frame_texture(device: &wgpu::Device, width: u32, height: u32) -> wgpu::Texture { diff --git a/crates/rendering/src/decoder/avassetreader.rs b/crates/rendering/src/decoder/avassetreader.rs index a36dd4e086..b8a5fe269d 100644 --- a/crates/rendering/src/decoder/avassetreader.rs +++ b/crates/rendering/src/decoder/avassetreader.rs @@ -4,6 +4,7 @@ use std::{ path::PathBuf, rc::Rc, sync::{Arc, mpsc}, + time::Instant, }; use cidre::{ @@ -27,13 +28,7 @@ struct ProcessedFrame { } #[derive(Clone)] -enum CachedFrame { - Raw { - image_buf: R, - number: u32, - }, - Processed(ProcessedFrame), -} +struct CachedFrame(ProcessedFrame); struct ImageBufProcessor { converter: FrameConverter, @@ -148,23 +143,19 @@ impl ImageBufProcessor { } impl CachedFrame { - fn process(&mut self, processor: &mut ImageBufProcessor) -> ProcessedFrame { - match self { - CachedFrame::Raw { image_buf, number } => { - let frame_buffer = processor.convert(image_buf); - let data = ProcessedFrame { - number: *number, - data: Arc::new(frame_buffer), - width: image_buf.width() as u32, - height: image_buf.height() as u32, - }; - - *self = Self::Processed(data.clone()); - - data - } - CachedFrame::Processed(data) => data.clone(), - } + fn new(processor: &mut ImageBufProcessor, mut image_buf: R, number: u32) -> Self { + let frame_buffer = processor.convert(&mut image_buf); + let data = ProcessedFrame { + number, + data: Arc::new(frame_buffer), + width: image_buf.width() as u32, + height: image_buf.height() as u32, + }; + Self(data) + } + + fn data(&self) -> &ProcessedFrame { + &self.0 } } @@ -198,7 +189,7 @@ impl AVAssetReaderDecoder { } fn run( - _name: &'static str, + name: &'static str, path: PathBuf, fps: u32, rx: mpsc::Receiver, @@ -228,13 +219,34 @@ impl AVAssetReaderDecoder { let mut frames = this.inner.frames(); let mut processor = ImageBufProcessor::new(); + let mut cache_hits = 0u64; + let mut cache_misses = 0u64; + let mut total_requests = 0u64; + let mut total_decode_time_us = 0u64; + let mut total_reset_count = 0u64; + let mut total_reset_time_us = 0u64; + let last_metrics_log = Rc::new(RefCell::new(Instant::now())); + while let Ok(r) = rx.recv() { match r { VideoDecoderMessage::GetFrame(requested_time, sender) => { + let request_start = Instant::now(); + total_requests += 1; let requested_frame = (requested_time * fps as f32).floor() as u32; - let mut sender = if let Some(cached) = cache.get_mut(&requested_frame) { - let data = cached.process(&mut processor); + let mut sender = if let Some(cached) = cache.get(&requested_frame) { + cache_hits += 1; + let data = cached.data().clone(); + let total_time = request_start.elapsed(); + + tracing::debug!( + decoder = name, + frame = requested_frame, + cache_hit = true, + total_time_us = total_time.as_micros() as u64, + cache_size = cache.len(), + "[PERF:DECODER] cache hit" + ); let _ = sender.send(DecodedFrame { data: data.data.clone(), @@ -244,34 +256,78 @@ impl AVAssetReaderDecoder { *last_sent_frame.borrow_mut() = Some(data); continue; } else { + cache_misses += 1; let last_sent_frame = last_sent_frame.clone(); + let request_start_clone = request_start; + let last_metrics_log_clone = last_metrics_log.clone(); + let decoder_name = name; Some(move |data: ProcessedFrame| { + let total_time = request_start_clone.elapsed(); + tracing::debug!( + decoder = decoder_name, + frame = data.number, + cache_hit = false, + total_time_us = total_time.as_micros() as u64, + "[PERF:DECODER] cache miss - frame decoded" + ); *last_sent_frame.borrow_mut() = Some(data.clone()); let _ = sender.send(DecodedFrame { data: data.data.clone(), width: data.width, height: data.height, }); + + let mut last_log = last_metrics_log_clone.borrow_mut(); + if last_log.elapsed().as_secs() >= 2 { + *last_log = Instant::now(); + } }) }; let cache_min = requested_frame.saturating_sub(FRAME_CACHE_SIZE as u32 / 2); let cache_max = requested_frame + FRAME_CACHE_SIZE as u32 / 2; - if requested_frame == 0 - || last_sent_frame - .borrow() - .as_ref() - .map(|last| { - requested_frame < last.number - || requested_frame - last.number > FRAME_CACHE_SIZE as u32 - }) - .unwrap_or(true) - { + let cache_frame_min = cache.keys().next().copied(); + let cache_frame_max = cache.keys().next_back().copied(); + + let needs_reset = + if let (Some(c_min), Some(c_max)) = (cache_frame_min, cache_frame_max) { + let is_backward_seek_beyond_cache = requested_frame < c_min; + let is_forward_seek_beyond_cache = + requested_frame > c_max + FRAME_CACHE_SIZE as u32 / 4; + is_backward_seek_beyond_cache || is_forward_seek_beyond_cache + } else { + true + }; + + if needs_reset { + let reset_start = Instant::now(); + total_reset_count += 1; this.reset(requested_time); frames = this.inner.frames(); *last_sent_frame.borrow_mut() = None; - cache.clear(); + + let old_cache_size = cache.len(); + let retained = cache + .keys() + .filter(|&&f| f >= cache_min && f <= cache_max) + .count(); + cache.retain(|&f, _| f >= cache_min && f <= cache_max); + let cleared = old_cache_size - retained; + + let reset_time = reset_start.elapsed(); + total_reset_time_us += reset_time.as_micros() as u64; + + tracing::info!( + decoder = name, + requested_frame = requested_frame, + requested_time = requested_time, + reset_time_ms = reset_time.as_millis() as u64, + cleared_cache_entries = cleared, + retained_cache_entries = retained, + total_resets = total_reset_count, + "[PERF:DECODER] decoder reset/seek" + ); } last_active_frame = Some(requested_frame); @@ -293,37 +349,22 @@ impl AVAssetReaderDecoder { continue; }; - let mut cache_frame = CachedFrame::Raw { - image_buf: frame.retained(), - number: current_frame, - }; + let cache_frame = + CachedFrame::new(&mut processor, frame.retained(), current_frame); this.is_done = false; - // Handles frame skips. - // We use the cache instead of last_sent_frame as newer non-matching frames could have been decoded. if let Some(most_recent_prev_frame) = - cache.iter_mut().rev().find(|v| *v.0 < requested_frame) + cache.iter().rev().find(|v| *v.0 < requested_frame) && let Some(sender) = sender.take() { - (sender)(most_recent_prev_frame.1.process(&mut processor)); + (sender)(most_recent_prev_frame.1.data().clone()); } let exceeds_cache_bounds = current_frame > cache_max; let too_small_for_cache_bounds = current_frame < cache_min; if !too_small_for_cache_bounds { - if current_frame == requested_frame - && let Some(sender) = sender.take() - { - let data = cache_frame.process(&mut processor); - // info!("sending frame {requested_frame}"); - - (sender)(data); - - break; - } - if cache.len() >= FRAME_CACHE_SIZE { if let Some(last_active_frame) = &last_active_frame { let frame = if requested_frame > *last_active_frame { @@ -344,6 +385,13 @@ impl AVAssetReaderDecoder { } cache.insert(current_frame, cache_frame.clone()); + + if current_frame == requested_frame + && let Some(sender) = sender.take() + { + (sender)(cache_frame.data().clone()); + break; + } } if current_frame > requested_frame && sender.is_some() { @@ -360,11 +408,7 @@ impl AVAssetReaderDecoder { (sender)(last_sent_frame); } else if let Some(sender) = sender.take() { - // info!( - // "sending forward frame {current_frame} for {requested_frame}", - // ); - - (sender)(cache_frame.process(&mut processor)); + (sender)(cache_frame.data().clone()); } } diff --git a/crates/rendering/src/frame_pipeline.rs b/crates/rendering/src/frame_pipeline.rs index 2c17424e23..755cbfa638 100644 --- a/crates/rendering/src/frame_pipeline.rs +++ b/crates/rendering/src/frame_pipeline.rs @@ -1,6 +1,321 @@ +use std::{sync::Arc, time::Instant}; +use tokio::sync::oneshot; use wgpu::COPY_BYTES_PER_ROW_ALIGNMENT; -use crate::{ProjectUniforms, RenderSession, RenderingError}; +use crate::{ProjectUniforms, RenderingError}; + +pub struct PendingReadback { + rx: oneshot::Receiver>, + buffer: Arc, + padded_bytes_per_row: u32, + width: u32, + height: u32, + submit_time: Instant, +} + +impl PendingReadback { + pub async fn wait(mut self, device: &wgpu::Device) -> Result { + let poll_start = Instant::now(); + let mut poll_count = 0u32; + + loop { + match self.rx.try_recv() { + Ok(result) => { + result?; + break; + } + Err(oneshot::error::TryRecvError::Empty) => { + match device.poll(wgpu::PollType::Poll) { + Ok(maintained) => { + if maintained.is_queue_empty() { + break; + } + } + Err(e) => return Err(e.into()), + } + poll_count += 1; + if poll_count % 10 == 0 { + tokio::task::yield_now().await; + } + } + Err(oneshot::error::TryRecvError::Closed) => { + return Err(RenderingError::BufferMapWaitingFailed); + } + } + } + + let poll_time = poll_start.elapsed(); + let total_time = self.submit_time.elapsed(); + + let data_copy_start = Instant::now(); + let buffer_slice = self.buffer.slice(..); + let data = buffer_slice.get_mapped_range(); + let data_vec = data.to_vec(); + let data_copy_time = data_copy_start.elapsed(); + + drop(data); + self.buffer.unmap(); + + tracing::debug!( + poll_us = poll_time.as_micros() as u64, + data_copy_us = data_copy_time.as_micros() as u64, + total_pipeline_us = total_time.as_micros() as u64, + "[PERF:GPU_BUFFER] pipelined readback wait completed" + ); + + if poll_time.as_millis() > 10 { + tracing::warn!( + poll_time_ms = poll_time.as_millis() as u64, + "[PERF:GPU_BUFFER] GPU poll took longer than 10ms - potential bottleneck" + ); + } + + Ok(RenderedFrame { + data: data_vec, + padded_bytes_per_row: self.padded_bytes_per_row, + width: self.width, + height: self.height, + }) + } +} + +pub struct PipelinedGpuReadback { + buffers: [Arc; 3], + buffer_size: u64, + current_index: usize, + pending: Option, +} + +impl PipelinedGpuReadback { + pub fn new(device: &wgpu::Device, initial_size: u64) -> Self { + let make_buffer = || { + Arc::new(device.create_buffer(&wgpu::BufferDescriptor { + label: Some("Pipelined Readback Buffer"), + size: initial_size, + usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ, + mapped_at_creation: false, + })) + }; + + Self { + buffers: [make_buffer(), make_buffer(), make_buffer()], + buffer_size: initial_size, + current_index: 0, + pending: None, + } + } + + pub fn ensure_size(&mut self, device: &wgpu::Device, required_size: u64) { + if self.buffer_size < required_size { + let make_buffer = || { + Arc::new(device.create_buffer(&wgpu::BufferDescriptor { + label: Some("Pipelined Readback Buffer"), + size: required_size, + usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ, + mapped_at_creation: false, + })) + }; + + self.buffers = [make_buffer(), make_buffer(), make_buffer()]; + self.buffer_size = required_size; + self.current_index = 0; + } + } + + fn next_buffer(&mut self) -> Arc { + let buffer = self.buffers[self.current_index].clone(); + self.current_index = (self.current_index + 1) % self.buffers.len(); + buffer + } + + pub fn submit_readback( + &mut self, + device: &wgpu::Device, + queue: &wgpu::Queue, + texture: &wgpu::Texture, + uniforms: &ProjectUniforms, + render_encoder: wgpu::CommandEncoder, + ) -> Result<(), RenderingError> { + let submit_start = Instant::now(); + + let padded_bytes_per_row = padded_bytes_per_row(uniforms.output_size); + let output_buffer_size = (padded_bytes_per_row * uniforms.output_size.1) as u64; + + self.ensure_size(device, output_buffer_size); + let buffer = self.next_buffer(); + + let submit1_start = Instant::now(); + queue.submit(std::iter::once(render_encoder.finish())); + let submit1_time = submit1_start.elapsed(); + + let output_texture_size = wgpu::Extent3d { + width: uniforms.output_size.0, + height: uniforms.output_size.1, + depth_or_array_layers: 1, + }; + + let copy_encoder_start = Instant::now(); + let mut copy_encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("Copy Encoder"), + }); + + copy_encoder.copy_texture_to_buffer( + wgpu::TexelCopyTextureInfo { + texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + wgpu::TexelCopyBufferInfo { + buffer: &buffer, + layout: wgpu::TexelCopyBufferLayout { + offset: 0, + bytes_per_row: Some(padded_bytes_per_row), + rows_per_image: Some(uniforms.output_size.1), + }, + }, + output_texture_size, + ); + let copy_encoder_time = copy_encoder_start.elapsed(); + + let submit2_start = Instant::now(); + queue.submit(std::iter::once(copy_encoder.finish())); + let submit2_time = submit2_start.elapsed(); + + let (tx, rx) = oneshot::channel(); + buffer + .slice(..) + .map_async(wgpu::MapMode::Read, move |result| { + let _ = tx.send(result); + }); + + self.pending = Some(PendingReadback { + rx, + buffer, + padded_bytes_per_row, + width: uniforms.output_size.0, + height: uniforms.output_size.1, + submit_time: submit_start, + }); + + tracing::debug!( + buffer_size_bytes = output_buffer_size, + submit1_us = submit1_time.as_micros() as u64, + copy_encoder_us = copy_encoder_time.as_micros() as u64, + submit2_us = submit2_time.as_micros() as u64, + "[PERF:GPU_BUFFER] pipelined readback submitted" + ); + + Ok(()) + } + + pub fn take_pending(&mut self) -> Option { + self.pending.take() + } + + pub fn has_pending(&self) -> bool { + self.pending.is_some() + } +} + +pub struct RenderSession { + pub textures: (wgpu::Texture, wgpu::Texture), + texture_views: (wgpu::TextureView, wgpu::TextureView), + pub current_is_left: bool, + pub pipelined_readback: PipelinedGpuReadback, +} + +impl RenderSession { + pub fn new(device: &wgpu::Device, width: u32, height: u32) -> Self { + let make_texture = || { + device.create_texture(&wgpu::TextureDescriptor { + size: wgpu::Extent3d { + width, + height, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8UnormSrgb, + usage: wgpu::TextureUsages::TEXTURE_BINDING + | wgpu::TextureUsages::RENDER_ATTACHMENT + | wgpu::TextureUsages::COPY_SRC, + label: Some("Intermediate Texture"), + view_formats: &[], + }) + }; + + let textures = (make_texture(), make_texture()); + let padded = padded_bytes_per_row((width, height)); + let initial_buffer_size = (padded * height) as u64; + + Self { + current_is_left: true, + texture_views: ( + textures.0.create_view(&Default::default()), + textures.1.create_view(&Default::default()), + ), + textures, + pipelined_readback: PipelinedGpuReadback::new(device, initial_buffer_size), + } + } + + pub fn update_texture_size(&mut self, device: &wgpu::Device, width: u32, height: u32) { + let make_texture = || { + device.create_texture(&wgpu::TextureDescriptor { + size: wgpu::Extent3d { + width, + height, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8UnormSrgb, + usage: wgpu::TextureUsages::TEXTURE_BINDING + | wgpu::TextureUsages::RENDER_ATTACHMENT + | wgpu::TextureUsages::COPY_SRC, + label: Some("Intermediate Texture"), + view_formats: &[], + }) + }; + + self.textures = (make_texture(), make_texture()); + self.texture_views = ( + self.textures.0.create_view(&Default::default()), + self.textures.1.create_view(&Default::default()), + ); + } + + pub fn current_texture(&self) -> &wgpu::Texture { + if self.current_is_left { + &self.textures.0 + } else { + &self.textures.1 + } + } + + pub fn current_texture_view(&self) -> &wgpu::TextureView { + if self.current_is_left { + &self.texture_views.0 + } else { + &self.texture_views.1 + } + } + + pub fn other_texture_view(&self) -> &wgpu::TextureView { + if self.current_is_left { + &self.texture_views.1 + } else { + &self.texture_views.0 + } + } + + pub fn swap_textures(&mut self) { + self.current_is_left = !self.current_is_left; + } +} // pub struct FramePipelineState<'a> { // pub constants: &'a RenderVideoConstants, @@ -68,69 +383,110 @@ pub async fn finish_encoder( uniforms: &ProjectUniforms, encoder: wgpu::CommandEncoder, ) -> Result { - let padded_bytes_per_row = padded_bytes_per_row(uniforms.output_size); + let total_start = Instant::now(); - queue.submit(std::iter::once(encoder.finish())); + let previous_pending = session.pipelined_readback.take_pending(); + let has_previous = previous_pending.is_some(); - let output_texture_size = wgpu::Extent3d { - width: uniforms.output_size.0, - height: uniforms.output_size.1, - depth_or_array_layers: 1, + let texture = if session.current_is_left { + &session.textures.0 + } else { + &session.textures.1 }; - let output_buffer_size = (padded_bytes_per_row * uniforms.output_size.1) as u64; - session.ensure_readback_buffers(device, output_buffer_size); - let output_buffer = session.current_readback_buffer(); + let submit_start = Instant::now(); + session + .pipelined_readback + .submit_readback(device, queue, texture, uniforms, encoder)?; + let submit_time = submit_start.elapsed(); - let mut encoder = device.create_command_encoder( - &(wgpu::CommandEncoderDescriptor { - label: Some("Copy Encoder"), - }), - ); - - encoder.copy_texture_to_buffer( - wgpu::TexelCopyTextureInfo { - texture: session.current_texture(), - mip_level: 0, - origin: wgpu::Origin3d::ZERO, - aspect: wgpu::TextureAspect::All, - }, - wgpu::TexelCopyBufferInfo { - buffer: output_buffer, - layout: wgpu::TexelCopyBufferLayout { - offset: 0, - bytes_per_row: Some(padded_bytes_per_row), - rows_per_image: Some(uniforms.output_size.1), - }, - }, - output_texture_size, - ); + let result = if let Some(pending) = previous_pending { + let wait_start = Instant::now(); + let frame = pending.wait(device).await?; + let wait_time = wait_start.elapsed(); + + // #region agent log + use std::io::Write; + if let Ok(mut f) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") + { + let _ = writeln!( + f, + r#"{{"hypothesisId":"GPU_PIPELINE","location":"frame_pipeline.rs:finish_encoder","message":"Pipelined finish (waited for previous)","data":{{"submit_us":{},"wait_us":{},"total_us":{},"has_previous":true}},"timestamp":{}}}"#, + submit_time.as_micros(), + wait_time.as_micros(), + total_start.elapsed().as_micros(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() + ); + } + // #endregion - queue.submit(std::iter::once(encoder.finish())); + tracing::debug!( + wait_us = wait_time.as_micros() as u64, + total_us = total_start.elapsed().as_micros() as u64, + "[PERF:GPU_BUFFER] pipelined finish_encoder (pipelined, waited for previous)" + ); - let buffer_slice = output_buffer.slice(..); - let (tx, rx) = tokio::sync::oneshot::channel(); - buffer_slice.map_async(wgpu::MapMode::Read, move |result| { - let _ = tx.send(result); - }); + frame + } else { + let wait_start = Instant::now(); - device.poll(wgpu::PollType::Wait)?; + let pending = session + .pipelined_readback + .take_pending() + .expect("just submitted a readback"); + let frame = pending.wait(device).await?; + let wait_time = wait_start.elapsed(); - rx.await - .map_err(|_| RenderingError::BufferMapWaitingFailed)??; + let prime_start = Instant::now(); + let prime_encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("Pipeline Priming Encoder"), + }); + session.pipelined_readback.submit_readback( + device, + queue, + texture, + uniforms, + prime_encoder, + )?; + let prime_time = prime_start.elapsed(); - let data = buffer_slice.get_mapped_range(); - let data_vec = data.to_vec(); + // #region agent log + use std::io::Write; + if let Ok(mut f) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") + { + let _ = writeln!( + f, + r#"{{"hypothesisId":"GPU_PIPELINE","location":"frame_pipeline.rs:finish_encoder","message":"First frame (primed pipeline)","data":{{"submit_us":{},"wait_us":{},"prime_us":{},"total_us":{},"has_previous":false}},"timestamp":{}}}"#, + submit_time.as_micros(), + wait_time.as_micros(), + prime_time.as_micros(), + total_start.elapsed().as_micros(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() + ); + } + // #endregion - drop(data); - output_buffer.unmap(); + tracing::debug!( + wait_us = wait_time.as_micros() as u64, + prime_us = prime_time.as_micros() as u64, + total_us = total_start.elapsed().as_micros() as u64, + "[PERF:GPU_BUFFER] pipelined finish_encoder (first frame, primed pipeline)" + ); - session.swap_readback_buffers(); + frame + }; - Ok(RenderedFrame { - data: data_vec, - padded_bytes_per_row, - width: uniforms.output_size.0, - height: uniforms.output_size.1, - }) + Ok(result) } diff --git a/crates/rendering/src/layers/camera.rs b/crates/rendering/src/layers/camera.rs index ce5f99b550..d4f325e3f4 100644 --- a/crates/rendering/src/layers/camera.rs +++ b/crates/rendering/src/layers/camera.rs @@ -6,18 +6,21 @@ use crate::{ }; pub struct CameraLayer { - frame_texture: wgpu::Texture, - frame_texture_view: wgpu::TextureView, + frame_textures: [wgpu::Texture; 2], + frame_texture_views: [wgpu::TextureView; 2], + current_texture: usize, uniforms_buffer: wgpu::Buffer, - bind_group: Option, + bind_groups: [Option; 2], pipeline: CompositeVideoFramePipeline, hidden: bool, } impl CameraLayer { pub fn new(device: &wgpu::Device) -> Self { - let frame_texture = CompositeVideoFramePipeline::create_frame_texture(device, 1920, 1080); - let frame_texture_view = frame_texture.create_view(&Default::default()); + let frame_texture_0 = CompositeVideoFramePipeline::create_frame_texture(device, 1920, 1080); + let frame_texture_1 = CompositeVideoFramePipeline::create_frame_texture(device, 1920, 1080); + let frame_texture_view_0 = frame_texture_0.create_view(&Default::default()); + let frame_texture_view_1 = frame_texture_1.create_view(&Default::default()); let pipeline = CompositeVideoFramePipeline::new(device); @@ -29,13 +32,17 @@ impl CameraLayer { }), ); - let bind_group = Some(pipeline.bind_group(device, &uniforms_buffer, &frame_texture_view)); + let bind_group_0 = + Some(pipeline.bind_group(device, &uniforms_buffer, &frame_texture_view_0)); + let bind_group_1 = + Some(pipeline.bind_group(device, &uniforms_buffer, &frame_texture_view_1)); Self { - frame_texture, - frame_texture_view, + frame_textures: [frame_texture_0, frame_texture_1], + frame_texture_views: [frame_texture_view_0, frame_texture_view_1], + current_texture: 0, uniforms_buffer, - bind_group, + bind_groups: [bind_group_0, bind_group_1], pipeline, hidden: false, } @@ -53,34 +60,41 @@ impl CameraLayer { return; }; - if self.frame_texture.width() != frame_size.x || self.frame_texture.height() != frame_size.y + let next_texture = 1 - self.current_texture; + + if self.frame_textures[next_texture].width() != frame_size.x + || self.frame_textures[next_texture].height() != frame_size.y { - self.frame_texture = CompositeVideoFramePipeline::create_frame_texture( + self.frame_textures[next_texture] = CompositeVideoFramePipeline::create_frame_texture( device, frame_size.x, frame_size.y, ); - self.frame_texture_view = self.frame_texture.create_view(&Default::default()); + self.frame_texture_views[next_texture] = + self.frame_textures[next_texture].create_view(&Default::default()); - self.bind_group = Some(self.pipeline.bind_group( + self.bind_groups[next_texture] = Some(self.pipeline.bind_group( device, &self.uniforms_buffer, - &self.frame_texture_view, + &self.frame_texture_views[next_texture], )); } + let frame_data = camera_frame.data(); + let src_bytes_per_row = frame_size.x * 4; + queue.write_texture( wgpu::TexelCopyTextureInfo { - texture: &self.frame_texture, + texture: &self.frame_textures[next_texture], mip_level: 0, origin: wgpu::Origin3d::ZERO, aspect: wgpu::TextureAspect::All, }, - camera_frame.data(), + frame_data, wgpu::TexelCopyBufferLayout { offset: 0, - bytes_per_row: Some(frame_size.x * 4), - rows_per_image: None, + bytes_per_row: Some(src_bytes_per_row), + rows_per_image: Some(frame_size.y), }, wgpu::Extent3d { width: frame_size.x, @@ -90,11 +104,15 @@ impl CameraLayer { ); queue.write_buffer(&self.uniforms_buffer, 0, bytemuck::cast_slice(&[uniforms])); + + self.current_texture = next_texture; } + pub fn copy_to_texture(&mut self, _encoder: &mut wgpu::CommandEncoder) {} + pub fn render(&self, pass: &mut wgpu::RenderPass<'_>) { if !self.hidden - && let Some(bind_group) = &self.bind_group + && let Some(bind_group) = &self.bind_groups[self.current_texture] { pass.set_pipeline(&self.pipeline.render_pipeline); pass.set_bind_group(0, bind_group, &[]); diff --git a/crates/rendering/src/layers/display.rs b/crates/rendering/src/layers/display.rs index 6ad8f7f2ac..954f6e541c 100644 --- a/crates/rendering/src/layers/display.rs +++ b/crates/rendering/src/layers/display.rs @@ -6,28 +6,35 @@ use crate::{ }; pub struct DisplayLayer { - frame_texture: wgpu::Texture, - frame_texture_view: wgpu::TextureView, + frame_textures: [wgpu::Texture; 2], + frame_texture_views: [wgpu::TextureView; 2], + current_texture: usize, uniforms_buffer: wgpu::Buffer, pipeline: CompositeVideoFramePipeline, - bind_group: Option, + bind_groups: [Option; 2], } impl DisplayLayer { pub fn new(device: &wgpu::Device) -> Self { - let frame_texture = CompositeVideoFramePipeline::create_frame_texture(device, 1920, 1080); - let frame_texture_view = frame_texture.create_view(&Default::default()); + let frame_texture_0 = CompositeVideoFramePipeline::create_frame_texture(device, 1920, 1080); + let frame_texture_1 = CompositeVideoFramePipeline::create_frame_texture(device, 1920, 1080); + let frame_texture_view_0 = frame_texture_0.create_view(&Default::default()); + let frame_texture_view_1 = frame_texture_1.create_view(&Default::default()); let uniforms_buffer = CompositeVideoFrameUniforms::default().to_buffer(device); let pipeline = CompositeVideoFramePipeline::new(device); - let bind_group = Some(pipeline.bind_group(device, &uniforms_buffer, &frame_texture_view)); + let bind_group_0 = + Some(pipeline.bind_group(device, &uniforms_buffer, &frame_texture_view_0)); + let bind_group_1 = + Some(pipeline.bind_group(device, &uniforms_buffer, &frame_texture_view_1)); Self { - frame_texture_view, - frame_texture, + frame_textures: [frame_texture_0, frame_texture_1], + frame_texture_views: [frame_texture_view_0, frame_texture_view_1], + current_texture: 0, uniforms_buffer, pipeline, - bind_group, + bind_groups: [bind_group_0, bind_group_1], } } @@ -39,34 +46,41 @@ impl DisplayLayer { frame_size: XY, uniforms: CompositeVideoFrameUniforms, ) { - if self.frame_texture.width() != frame_size.x || self.frame_texture.height() != frame_size.y + let next_texture = 1 - self.current_texture; + + if self.frame_textures[next_texture].width() != frame_size.x + || self.frame_textures[next_texture].height() != frame_size.y { - self.frame_texture = CompositeVideoFramePipeline::create_frame_texture( + self.frame_textures[next_texture] = CompositeVideoFramePipeline::create_frame_texture( device, frame_size.x, frame_size.y, ); - self.frame_texture_view = self.frame_texture.create_view(&Default::default()); + self.frame_texture_views[next_texture] = + self.frame_textures[next_texture].create_view(&Default::default()); - self.bind_group = Some(self.pipeline.bind_group( + self.bind_groups[next_texture] = Some(self.pipeline.bind_group( device, &self.uniforms_buffer, - &self.frame_texture_view, + &self.frame_texture_views[next_texture], )); } + let frame_data = segment_frames.screen_frame.data(); + let src_bytes_per_row = frame_size.x * 4; + queue.write_texture( wgpu::TexelCopyTextureInfo { - texture: &self.frame_texture, + texture: &self.frame_textures[next_texture], mip_level: 0, origin: wgpu::Origin3d::ZERO, aspect: wgpu::TextureAspect::All, }, - segment_frames.screen_frame.data(), + frame_data, wgpu::TexelCopyBufferLayout { offset: 0, - bytes_per_row: Some(frame_size.x * 4), - rows_per_image: None, + bytes_per_row: Some(src_bytes_per_row), + rows_per_image: Some(frame_size.y), }, wgpu::Extent3d { width: frame_size.x, @@ -75,12 +89,15 @@ impl DisplayLayer { }, ); - // Update existing uniform buffer in place; bind group remains valid. uniforms.write_to_buffer(queue, &self.uniforms_buffer); + + self.current_texture = next_texture; } + pub fn copy_to_texture(&mut self, _encoder: &mut wgpu::CommandEncoder) {} + pub fn render(&self, pass: &mut wgpu::RenderPass<'_>) { - if let Some(bind_group) = &self.bind_group { + if let Some(bind_group) = &self.bind_groups[self.current_texture] { pass.set_pipeline(&self.pipeline.render_pipeline); pass.set_bind_group(0, bind_group, &[]); pass.draw(0..4, 0..1); diff --git a/crates/rendering/src/lib.rs b/crates/rendering/src/lib.rs index f95158a90b..cbae3accae 100644 --- a/crates/rendering/src/lib.rs +++ b/crates/rendering/src/lib.rs @@ -7,7 +7,7 @@ use composite_frame::CompositeVideoFrameUniforms; use core::f64; use cursor_interpolation::{InterpolatedCursorPosition, interpolate_cursor}; use decoder::{AsyncVideoDecoderHandle, spawn_decoder}; -use frame_pipeline::finish_encoder; +use frame_pipeline::{RenderSession, finish_encoder}; use futures::FutureExt; use futures::future::OptionFuture; use layers::{ @@ -1553,12 +1553,40 @@ impl<'a> FrameRenderer<'a> { ) }); + // #region agent log + use std::io::Write; + let texture_update_start = std::time::Instant::now(); + // #endregion + session.update_texture_size( &self.constants.device, uniforms.output_size.0, uniforms.output_size.1, ); + // #region agent log + let texture_update_time = texture_update_start.elapsed(); + if texture_update_time.as_micros() > 100 { + if let Ok(mut f) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") + { + let _ = writeln!( + f, + r#"{{"hypothesisId":"E","location":"lib.rs:update_texture_size","message":"Texture size update took significant time","data":{{"time_us":{},"width":{},"height":{}}},"timestamp":{}}}"#, + texture_update_time.as_micros(), + uniforms.output_size.0, + uniforms.output_size.1, + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() + ); + } + } + // #endregion + produce_frame( self.constants, segment_frames, @@ -1605,6 +1633,9 @@ impl RendererLayers { segment_frames: &DecodedSegmentFrames, cursor: &CursorEvents, ) -> Result<(), RenderingError> { + let prepare_start = Instant::now(); + + let bg_start = Instant::now(); self.background .prepare( constants, @@ -1612,11 +1643,15 @@ impl RendererLayers { Background::from(uniforms.project.background.source.clone()), ) .await?; + let bg_time = bg_start.elapsed(); + let blur_start = Instant::now(); if uniforms.project.background.blur > 0.0 { self.background_blur.prepare(&constants.queue, uniforms); } + let blur_time = blur_start.elapsed(); + let display_start = Instant::now(); self.display.prepare( &constants.device, &constants.queue, @@ -1624,7 +1659,9 @@ impl RendererLayers { constants.options.screen_size, uniforms.display, ); + let display_time = display_start.elapsed(); + let cursor_start = Instant::now(); self.cursor.prepare( segment_frames, uniforms.resolution_base, @@ -1633,7 +1670,9 @@ impl RendererLayers { uniforms, constants, ); + let cursor_time = cursor_start.elapsed(); + let camera_start = Instant::now(); self.camera.prepare( &constants.device, &constants.queue, @@ -1645,7 +1684,9 @@ impl RendererLayers { )) })(), ); + let camera_time = camera_start.elapsed(); + let camera_only_start = Instant::now(); self.camera_only.prepare( &constants.device, &constants.queue, @@ -1657,26 +1698,75 @@ impl RendererLayers { )) })(), ); + let camera_only_time = camera_only_start.elapsed(); + let text_start = Instant::now(); self.text.prepare( &constants.device, &constants.queue, uniforms.output_size, &uniforms.texts, ); + let text_time = text_start.elapsed(); + let captions_start = Instant::now(); self.captions.prepare( uniforms, segment_frames, XY::new(uniforms.output_size.0, uniforms.output_size.1), constants, ); + let captions_time = captions_start.elapsed(); + + let total_time = prepare_start.elapsed(); + + // #region agent log + use std::io::Write; + if let Ok(mut f) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") + { + let _ = writeln!( + f, + r#"{{"hypothesisId":"PREPARE_BREAKDOWN","location":"lib.rs:prepare","message":"Layer prepare breakdown","data":{{"bg_us":{},"blur_us":{},"display_us":{},"cursor_us":{},"camera_us":{},"camera_only_us":{},"text_us":{},"captions_us":{},"total_us":{}}},"timestamp":{}}}"#, + bg_time.as_micros(), + blur_time.as_micros(), + display_time.as_micros(), + cursor_time.as_micros(), + camera_time.as_micros(), + camera_only_time.as_micros(), + text_time.as_micros(), + captions_time.as_micros(), + total_time.as_micros(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() + ); + } + // #endregion + + if total_time.as_millis() > 5 { + tracing::debug!( + total_us = total_time.as_micros() as u64, + bg_us = bg_time.as_micros() as u64, + blur_us = blur_time.as_micros() as u64, + display_us = display_time.as_micros() as u64, + cursor_us = cursor_time.as_micros() as u64, + camera_us = camera_time.as_micros() as u64, + camera_only_us = camera_only_time.as_micros() as u64, + text_us = text_time.as_micros() as u64, + captions_us = captions_time.as_micros() as u64, + "[PERF:PREPARE] layer prepare breakdown" + ); + } Ok(()) } pub fn render( - &self, + &mut self, device: &wgpu::Device, queue: &wgpu::Queue, encoder: &mut wgpu::CommandEncoder, @@ -1702,6 +1792,10 @@ impl RendererLayers { }; } + self.display.copy_to_texture(encoder); + self.camera.copy_to_texture(encoder); + self.camera_only.copy_to_texture(encoder); + { let mut pass = render_pass!( session.current_texture_view(), @@ -1760,147 +1854,6 @@ impl RendererLayers { } } -pub struct RenderSession { - textures: (wgpu::Texture, wgpu::Texture), - texture_views: (wgpu::TextureView, wgpu::TextureView), - current_is_left: bool, - readback_buffers: (Option, Option), - readback_buffer_size: u64, - current_readback_is_left: bool, -} - -impl RenderSession { - pub fn new(device: &wgpu::Device, width: u32, height: u32) -> Self { - let make_texture = || { - device.create_texture(&wgpu::TextureDescriptor { - size: wgpu::Extent3d { - width, - height, - depth_or_array_layers: 1, - }, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Rgba8UnormSrgb, - usage: wgpu::TextureUsages::TEXTURE_BINDING - | wgpu::TextureUsages::RENDER_ATTACHMENT - | wgpu::TextureUsages::COPY_SRC, - label: Some("Intermediate Texture"), - view_formats: &[], - }) - }; - - let textures = (make_texture(), make_texture()); - - Self { - current_is_left: true, - texture_views: ( - textures.0.create_view(&Default::default()), - textures.1.create_view(&Default::default()), - ), - textures, - readback_buffers: (None, None), - readback_buffer_size: 0, - current_readback_is_left: true, - } - } - - pub fn update_texture_size(&mut self, device: &wgpu::Device, width: u32, height: u32) { - let make_texture = || { - device.create_texture(&wgpu::TextureDescriptor { - size: wgpu::Extent3d { - width, - height, - depth_or_array_layers: 1, - }, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Rgba8UnormSrgb, - usage: wgpu::TextureUsages::TEXTURE_BINDING - | wgpu::TextureUsages::RENDER_ATTACHMENT - | wgpu::TextureUsages::COPY_SRC, - label: Some("Intermediate Texture"), - view_formats: &[], - }) - }; - - self.textures = (make_texture(), make_texture()); - self.texture_views = ( - self.textures.0.create_view(&Default::default()), - self.textures.1.create_view(&Default::default()), - ); - } - - pub fn current_texture(&self) -> &wgpu::Texture { - if self.current_is_left { - &self.textures.0 - } else { - &self.textures.1 - } - } - - pub fn current_texture_view(&self) -> &wgpu::TextureView { - if self.current_is_left { - &self.texture_views.0 - } else { - &self.texture_views.1 - } - } - - pub fn other_texture_view(&self) -> &wgpu::TextureView { - if self.current_is_left { - &self.texture_views.1 - } else { - &self.texture_views.0 - } - } - - pub fn swap_textures(&mut self) { - self.current_is_left = !self.current_is_left; - } - - pub(crate) fn ensure_readback_buffers(&mut self, device: &wgpu::Device, size: u64) { - let needs_new = self - .readback_buffers - .0 - .as_ref() - .is_none_or(|_| self.readback_buffer_size < size); - - if needs_new { - let make_buffer = || { - device.create_buffer(&wgpu::BufferDescriptor { - label: Some("RenderSession Readback Buffer"), - size, - usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ, - mapped_at_creation: false, - }) - }; - - self.readback_buffers = (Some(make_buffer()), Some(make_buffer())); - self.readback_buffer_size = size; - } - } - - pub(crate) fn current_readback_buffer(&self) -> &wgpu::Buffer { - if self.current_readback_is_left { - self.readback_buffers - .0 - .as_ref() - .expect("readback buffer should be initialised") - } else { - self.readback_buffers - .1 - .as_ref() - .expect("readback buffer should be initialised") - } - } - - pub(crate) fn swap_readback_buffers(&mut self) { - self.current_readback_is_left = !self.current_readback_is_left; - } -} - async fn produce_frame( constants: &RenderVideoConstants, segment_frames: DecodedSegmentFrames, @@ -1909,16 +1862,23 @@ async fn produce_frame( layers: &mut RendererLayers, session: &mut RenderSession, ) -> Result { + let total_start = Instant::now(); + + let prepare_start = Instant::now(); layers .prepare(constants, &uniforms, &segment_frames, cursor) .await?; + let prepare_time = prepare_start.elapsed(); + let encoder_start = Instant::now(); let mut encoder = constants.device.create_command_encoder( &(wgpu::CommandEncoderDescriptor { label: Some("Render Encoder"), }), ); + let encoder_create_time = encoder_start.elapsed(); + let render_start = Instant::now(); layers.render( &constants.device, &constants.queue, @@ -1926,15 +1886,56 @@ async fn produce_frame( session, &uniforms, ); + let render_time = render_start.elapsed(); - finish_encoder( + let finish_start = Instant::now(); + let result = finish_encoder( session, &constants.device, &constants.queue, &uniforms, encoder, ) - .await + .await; + let finish_time = finish_start.elapsed(); + + let total_time = total_start.elapsed(); + + // #region agent log + use std::io::Write; + if let Ok(mut f) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") + { + let _ = writeln!( + f, + r#"{{"hypothesisId":"GPU_BREAKDOWN","location":"lib.rs:produce_frame","message":"GPU render breakdown","data":{{"prepare_us":{},"encoder_create_us":{},"render_pass_us":{},"finish_encoder_us":{},"total_us":{}}},"timestamp":{}}}"#, + prepare_time.as_micros(), + encoder_create_time.as_micros(), + render_time.as_micros(), + finish_time.as_micros(), + total_time.as_micros(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() + ); + } + // #endregion + + tracing::debug!( + output_width = uniforms.output_size.0, + output_height = uniforms.output_size.1, + prepare_us = prepare_time.as_micros() as u64, + encoder_create_us = encoder_create_time.as_micros() as u64, + render_pass_us = render_time.as_micros() as u64, + finish_encoder_us = finish_time.as_micros() as u64, + total_us = total_time.as_micros() as u64, + "[PERF:GPU] produce_frame timing breakdown" + ); + + result } fn parse_color_component(hex_color: &str, index: usize) -> f32 { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 96c04f15a2..f0b06c2172 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -173,6 +173,9 @@ importers: effect: specifier: ^3.18.4 version: 3.18.4 + lz4js: + specifier: ^0.2.0 + version: 0.2.0 mp4box: specifier: ^0.5.2 version: 0.5.4 @@ -228,6 +231,9 @@ importers: '@types/dom-webcodecs': specifier: ^0.1.11 version: 0.1.14 + '@types/lz4js': + specifier: ^0.2.1 + version: 0.2.2 '@types/uuid': specifier: ^9.0.8 version: 9.0.8 @@ -6299,10 +6305,10 @@ packages: react-dom: optional: true - '@storybook/builder-vite@10.2.0-alpha.3': - resolution: {integrity: sha512-ix9mF8UvsCykGQ/9+9JgvLgrP91hcuwsoAQ35+ilUGsV2WVcQ8pqhvQklwsyjJPGm1lWqM0/et2doucITl/W6A==} + '@storybook/builder-vite@10.2.0-alpha.5': + resolution: {integrity: sha512-bYHUVT+V6mUebzHBIuJ1epynkpd9ILKNs1SIbP5qxe9yxmwhMtFngys2xJMElS6c6ctJwoug8V40kT59M7MLgw==} peerDependencies: - storybook: ^10.2.0-alpha.3 + storybook: ^10.2.0-alpha.5 vite: ^5.0.0 || ^6.0.0 || ^7.0.0 '@storybook/core@8.6.12': @@ -6313,12 +6319,12 @@ packages: prettier: optional: true - '@storybook/csf-plugin@10.2.0-alpha.3': - resolution: {integrity: sha512-t+wMFO2H/sfpQtN6ismI2uqY3wuk9FSkO85un7UprANoNg2F0pLdE1v08NzhgPAjh3rCNXWX6s88iGUtW+qFxQ==} + '@storybook/csf-plugin@10.2.0-alpha.5': + resolution: {integrity: sha512-fgCpTDxU5pXV+YvQduAMq5Ki63VEKP/QbHfEAbfGvYvsoMXh8KXnJ3pfK+V/BB3BQdhhOCZFxLuJnNtDe9l4wQ==} peerDependencies: esbuild: '*' rollup: '*' - storybook: ^10.2.0-alpha.3 + storybook: ^10.2.0-alpha.5 vite: '*' webpack: '*' peerDependenciesMeta: @@ -6930,6 +6936,9 @@ packages: '@types/lodash@4.17.16': resolution: {integrity: sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g==} + '@types/lz4js@0.2.2': + resolution: {integrity: sha512-pqXfoJ2AwllcLTf1Nia0+HT1KHj8z4wWo3bBP6vn7Aen5ySywBqrh/u1TyBwWxuKDS+mPzVHkPbHivqZEfk6pA==} + '@types/mdast@4.0.4': resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} @@ -10664,6 +10673,9 @@ packages: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true + lz4js@0.2.0: + resolution: {integrity: sha512-gY2Ia9Lm7Ep8qMiuGRhvUq0Q7qUereeldZPP1PMEJxPtEWHJLqw9pgX68oHajBH0nzJK4MaZEA/YNV3jT8u8Bg==} + magic-string@0.25.9: resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} @@ -20094,9 +20106,9 @@ snapshots: react: 19.1.1 react-dom: 19.1.1(react@19.1.1) - '@storybook/builder-vite@10.2.0-alpha.3(esbuild@0.25.5)(rollup@4.40.2)(storybook@8.6.12(prettier@3.5.3))(vite@6.3.5(@types/node@22.15.17)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1))(webpack@5.101.3(esbuild@0.25.5))': + '@storybook/builder-vite@10.2.0-alpha.5(esbuild@0.25.5)(rollup@4.40.2)(storybook@8.6.12(prettier@3.5.3))(vite@6.3.5(@types/node@22.15.17)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1))(webpack@5.101.3(esbuild@0.25.5))': dependencies: - '@storybook/csf-plugin': 10.2.0-alpha.3(esbuild@0.25.5)(rollup@4.40.2)(storybook@8.6.12(prettier@3.5.3))(vite@6.3.5(@types/node@22.15.17)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1))(webpack@5.101.3(esbuild@0.25.5)) + '@storybook/csf-plugin': 10.2.0-alpha.5(esbuild@0.25.5)(rollup@4.40.2)(storybook@8.6.12(prettier@3.5.3))(vite@6.3.5(@types/node@22.15.17)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1))(webpack@5.101.3(esbuild@0.25.5)) '@vitest/mocker': 3.2.4(vite@6.3.5(@types/node@22.15.17)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1)) storybook: 8.6.12(prettier@3.5.3) ts-dedent: 2.2.0 @@ -20128,7 +20140,7 @@ snapshots: - supports-color - utf-8-validate - '@storybook/csf-plugin@10.2.0-alpha.3(esbuild@0.25.5)(rollup@4.40.2)(storybook@8.6.12(prettier@3.5.3))(vite@6.3.5(@types/node@22.15.17)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1))(webpack@5.101.3(esbuild@0.25.5))': + '@storybook/csf-plugin@10.2.0-alpha.5(esbuild@0.25.5)(rollup@4.40.2)(storybook@8.6.12(prettier@3.5.3))(vite@6.3.5(@types/node@22.15.17)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1))(webpack@5.101.3(esbuild@0.25.5))': dependencies: storybook: 8.6.12(prettier@3.5.3) unplugin: 2.3.10 @@ -20803,6 +20815,8 @@ snapshots: '@types/lodash@4.17.16': {} + '@types/lz4js@0.2.2': {} + '@types/mdast@4.0.4': dependencies: '@types/unist': 3.0.3 @@ -23218,8 +23232,8 @@ snapshots: '@typescript-eslint/parser': 5.62.0(eslint@9.30.1(jiti@2.6.1))(typescript@5.8.3) eslint: 9.30.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@9.30.1(jiti@2.6.1)) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@5.62.0(eslint@9.30.1(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.30.1(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@9.30.1(jiti@2.6.1))(typescript@5.8.3))(eslint@9.30.1(jiti@2.6.1)))(eslint@9.30.1(jiti@2.6.1)) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@5.62.0(eslint@9.30.1(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@9.30.1(jiti@2.6.1))(typescript@5.8.3))(eslint@9.30.1(jiti@2.6.1)))(eslint@9.30.1(jiti@2.6.1)))(eslint@9.30.1(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.30.1(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.30.1(jiti@2.6.1)) eslint-plugin-react-hooks: 5.2.0(eslint@9.30.1(jiti@2.6.1)) @@ -23247,33 +23261,33 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0)(eslint@8.57.1): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@9.30.1(jiti@2.6.1))(typescript@5.8.3))(eslint@9.30.1(jiti@2.6.1)))(eslint@9.30.1(jiti@2.6.1)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 - eslint: 8.57.1 + eslint: 9.30.1(jiti@2.6.1) get-tsconfig: 4.11.0 is-bun-module: 2.0.0 stable-hash: 0.0.5 tinyglobby: 0.2.15 unrs-resolver: 1.7.2 optionalDependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@5.62.0(eslint@9.30.1(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@9.30.1(jiti@2.6.1))(typescript@5.8.3))(eslint@9.30.1(jiti@2.6.1)))(eslint@9.30.1(jiti@2.6.1)))(eslint@9.30.1(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0)(eslint@9.30.1(jiti@2.6.1)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0)(eslint@8.57.1): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 - eslint: 9.30.1(jiti@2.6.1) + eslint: 8.57.1 get-tsconfig: 4.11.0 is-bun-module: 2.0.0 stable-hash: 0.0.5 tinyglobby: 0.2.15 unrs-resolver: 1.7.2 optionalDependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@5.62.0(eslint@9.30.1(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.30.1(jiti@2.6.1)) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) transitivePeerDependencies: - supports-color @@ -23288,14 +23302,14 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@5.62.0(eslint@9.30.1(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.30.1(jiti@2.6.1)): + eslint-module-utils@2.12.0(@typescript-eslint/parser@5.62.0(eslint@9.30.1(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@9.30.1(jiti@2.6.1))(typescript@5.8.3))(eslint@9.30.1(jiti@2.6.1)))(eslint@9.30.1(jiti@2.6.1)))(eslint@9.30.1(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 5.62.0(eslint@9.30.1(jiti@2.6.1))(typescript@5.8.3) eslint: 9.30.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@9.30.1(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@9.30.1(jiti@2.6.1))(typescript@5.8.3))(eslint@9.30.1(jiti@2.6.1)))(eslint@9.30.1(jiti@2.6.1)) transitivePeerDependencies: - supports-color @@ -23328,7 +23342,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@9.30.1(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.30.1(jiti@2.6.1)): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@9.30.1(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@9.30.1(jiti@2.6.1))(typescript@5.8.3))(eslint@9.30.1(jiti@2.6.1)))(eslint@9.30.1(jiti@2.6.1)))(eslint@9.30.1(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -23339,7 +23353,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.30.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@5.62.0(eslint@9.30.1(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.30.1(jiti@2.6.1)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@5.62.0(eslint@9.30.1(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@9.30.1(jiti@2.6.1))(typescript@5.8.3))(eslint@9.30.1(jiti@2.6.1)))(eslint@9.30.1(jiti@2.6.1)))(eslint@9.30.1(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -25156,6 +25170,8 @@ snapshots: lz-string@1.5.0: {} + lz4js@0.2.0: {} + magic-string@0.25.9: dependencies: sourcemap-codec: 1.4.8 @@ -27940,7 +27956,7 @@ snapshots: storybook-solidjs-vite@1.0.0-beta.7(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(esbuild@0.25.5)(rollup@4.40.2)(solid-js@1.9.6)(storybook@8.6.12(prettier@3.5.3))(vite-plugin-solid@2.11.6(@testing-library/jest-dom@6.5.0)(solid-js@1.9.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1)))(vite@6.3.5(@types/node@22.15.17)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1))(webpack@5.101.3(esbuild@0.25.5)): dependencies: - '@storybook/builder-vite': 10.2.0-alpha.3(esbuild@0.25.5)(rollup@4.40.2)(storybook@8.6.12(prettier@3.5.3))(vite@6.3.5(@types/node@22.15.17)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1))(webpack@5.101.3(esbuild@0.25.5)) + '@storybook/builder-vite': 10.2.0-alpha.5(esbuild@0.25.5)(rollup@4.40.2)(storybook@8.6.12(prettier@3.5.3))(vite@6.3.5(@types/node@22.15.17)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1))(webpack@5.101.3(esbuild@0.25.5)) '@storybook/types': 9.0.0-alpha.1(storybook@8.6.12(prettier@3.5.3)) magic-string: 0.30.17 solid-js: 1.9.6 From e62cccac277339f444b80b79885e4f41f93a38bb Mon Sep 17 00:00:00 2001 From: Richie McIlroy <33632126+richiemcilroy@users.noreply.github.com> Date: Wed, 10 Dec 2025 22:03:27 +0000 Subject: [PATCH 02/31] clippy bits --- apps/desktop/src-tauri/src/captions.rs | 149 +++++++----------- apps/desktop/src-tauri/src/lib.rs | 3 +- .../src-tauri/src/screenshot_editor.rs | 11 +- .../src-tauri/src/update_project_names.rs | 16 +- apps/desktop/src-tauri/src/upload.rs | 2 +- crates/recording/src/recovery.rs | 30 ++-- crates/rendering/src/decoder/avassetreader.rs | 24 +-- crates/rendering/src/frame_pipeline.rs | 2 +- crates/utils/src/lib.rs | 4 +- 9 files changed, 96 insertions(+), 145 deletions(-) diff --git a/apps/desktop/src-tauri/src/captions.rs b/apps/desktop/src-tauri/src/captions.rs index 0dde354181..f4fb8ac49b 100644 --- a/apps/desktop/src-tauri/src/captions.rs +++ b/apps/desktop/src-tauri/src/captions.rs @@ -61,7 +61,7 @@ pub async fn save_model_file(path: String, data: Vec) -> Result<(), String> async fn extract_audio_from_video(video_path: &str, output_path: &PathBuf) -> Result<(), String> { log::info!("=== EXTRACT AUDIO START ==="); log::info!("Attempting to extract audio from: {video_path}"); - log::info!("Output path: {:?}", output_path); + log::info!("Output path: {output_path:?}"); if video_path.ends_with(".cap") { log::info!("Detected .cap project directory"); @@ -160,12 +160,11 @@ async fn extract_audio_from_video(video_path: &str, output_path: &PathBuf) -> Re log::info!("Final mixed audio: {} samples", mixed_samples.len()); let mix_rms = (mixed_samples.iter().map(|&s| s * s).sum::() / mixed_samples.len() as f32).sqrt(); - log::info!("Mixed audio RMS: {:.4}", mix_rms); + log::info!("Mixed audio RMS: {mix_rms:.4}"); if mix_rms < 0.001 { log::warn!( - "WARNING: Mixed audio RMS is very low ({:.6}) - audio may be nearly silent!", - mix_rms + "WARNING: Mixed audio RMS is very low ({mix_rms:.6}) - audio may be nearly silent!" ); } @@ -509,7 +508,7 @@ fn is_special_token(token_text: &str) -> bool { || trimmed.contains("<|"); if is_special { - log::debug!("Filtering special token: {:?}", token_text); + log::debug!("Filtering special token: {token_text:?}"); } is_special @@ -522,7 +521,7 @@ fn process_with_whisper( ) -> Result { log::info!("=== WHISPER TRANSCRIPTION START ==="); log::info!("Processing audio file: {audio_path:?}"); - log::info!("Language setting: {}", language); + log::info!("Language setting: {language}"); let mut params = FullParams::new(SamplingStrategy::Greedy { best_of: 1 }); @@ -569,18 +568,11 @@ fn process_with_whisper( / audio_data_f32.len() as f32) .sqrt(); log::info!( - "Audio samples - min: {:.4}, max: {:.4}, avg: {:.6}, RMS: {:.4}", - min_sample, - max_sample, - avg_sample, - rms + "Audio samples - min: {min_sample:.4}, max: {max_sample:.4}, avg: {avg_sample:.6}, RMS: {rms:.4}" ); if rms < 0.001 { - log::warn!( - "WARNING: Audio RMS is very low ({:.6}) - audio may be nearly silent!", - rms - ); + log::warn!("WARNING: Audio RMS is very low ({rms:.6}) - audio may be nearly silent!"); } log::info!("First 20 audio samples:"); @@ -646,10 +638,7 @@ fn process_with_whisper( if is_special_token(&token_text) { log::debug!( - " Token[{}]: id={}, text={:?} -> SKIPPED (special)", - t, - token_id, - token_text + " Token[{t}]: id={token_id}, text={token_text:?} -> SKIPPED (special)" ); continue; } @@ -661,13 +650,7 @@ fn process_with_whisper( let token_end = (data.t1 as f32) / 100.0; log::info!( - " Token[{}]: id={}, text={:?}, t0={:.2}s, t1={:.2}s, prob={:.4}", - t, - token_id, - token_text, - token_start, - token_end, - token_prob + " Token[{t}]: id={token_id}, text={token_text:?}, t0={token_start:.2}s, t1={token_end:.2}s, prob={token_prob:.4}" ); if token_text.starts_with(' ') || token_text.starts_with('\n') { @@ -688,27 +671,18 @@ fn process_with_whisper( } current_word = token_text.trim().to_string(); word_start = Some(token_start); - log::debug!( - " -> Starting new word: '{}' at {:.2}s", - current_word, - token_start - ); + log::debug!(" -> Starting new word: '{current_word}' at {token_start:.2}s"); } else { if word_start.is_none() { word_start = Some(token_start); - log::debug!(" -> Word start set to {:.2}s", token_start); + log::debug!(" -> Word start set to {token_start:.2}s"); } current_word.push_str(&token_text); - log::debug!(" -> Appending to word: '{}'", current_word); + log::debug!(" -> Appending to word: '{current_word}'"); } word_end = token_end; } else { - log::warn!( - " Token[{}]: id={}, text={:?} -> NO TIMING DATA", - t, - token_id, - token_text - ); + log::warn!(" Token[{t}]: id={token_id}, text={token_text:?} -> NO TIMING DATA"); } } @@ -740,7 +714,7 @@ fn process_with_whisper( } if words.is_empty() { - log::warn!(" Segment {} has no words, skipping", i); + log::warn!(" Segment {i} has no words, skipping"); continue; } @@ -778,7 +752,7 @@ fn process_with_whisper( log::info!("Total segments: {}", segments.len()); let total_words: usize = segments.iter().map(|s| s.words.len()).sum(); - log::info!("Total words: {}", total_words); + log::info!("Total words: {total_words}"); log::info!("=== FINAL TRANSCRIPTION SUMMARY ==="); for segment in &segments { @@ -813,7 +787,7 @@ fn find_python() -> Option { if version.contains("Python 3") || String::from_utf8_lossy(&output.stderr).contains("Python 3") { - log::info!("Found Python 3 at: {}", cmd); + log::info!("Found Python 3 at: {cmd}"); return Some(cmd.to_string()); } } @@ -961,8 +935,8 @@ fn ensure_server_script_exists() -> Result { if !script_path.exists() { std::fs::write(&script_path, get_whisperx_server_script()) - .map_err(|e| format!("Failed to write server script: {}", e))?; - log::info!("Created WhisperX server script at {:?}", script_path); + .map_err(|e| format!("Failed to write server script: {e}"))?; + log::info!("Created WhisperX server script at {script_path:?}"); } Ok(script_path) @@ -978,7 +952,7 @@ fn start_whisperx_server( let script_path = ensure_server_script_exists()?; - log::info!("Starting WhisperX server with model size: {}", model_size); + log::info!("Starting WhisperX server with model size: {model_size}"); let mut child = Command::new(venv_python) .arg(&script_path) @@ -989,7 +963,7 @@ fn start_whisperx_server( .stdout(std::process::Stdio::piped()) .stderr(std::process::Stdio::piped()) .spawn() - .map_err(|e| format!("Failed to start WhisperX server: {}", e))?; + .map_err(|e| format!("Failed to start WhisperX server: {e}"))?; let stdin = child .stdin @@ -1012,9 +986,9 @@ fn start_whisperx_server( let reader = std::io::BufReader::new(stderr); for line in reader.lines().map_while(Result::ok) { if let Some(stripped) = line.strip_prefix("STDERR:") { - log::info!("[WhisperX] {}", stripped); + log::info!("[WhisperX] {stripped}"); } else { - log::info!("[WhisperX stderr] {}", line); + log::info!("[WhisperX stderr] {line}"); } } }); @@ -1047,7 +1021,7 @@ fn start_whisperx_server( } Err(e) => { let _ = child.kill(); - return Err(format!("Error reading from WhisperX server: {}", e)); + return Err(format!("Error reading from WhisperX server: {e}")); } } } @@ -1092,24 +1066,24 @@ fn transcribe_with_server( server .stdin .flush() - .map_err(|e| format!("Failed to flush request: {}", e))?; + .map_err(|e| format!("Failed to flush request: {e}"))?; let mut response_line = String::new(); server .stdout .read_line(&mut response_line) - .map_err(|e| format!("Failed to read response from server: {}", e))?; + .map_err(|e| format!("Failed to read response from server: {e}"))?; let response: serde_json::Value = serde_json::from_str(&response_line) - .map_err(|e| format!("Failed to parse server response: {}", e))?; + .map_err(|e| format!("Failed to parse server response: {e}"))?; if !response["success"].as_bool().unwrap_or(false) { let error = response["error"].as_str().unwrap_or("Unknown error"); - return Err(format!("WhisperX server error: {}", error)); + return Err(format!("WhisperX server error: {error}")); } let whisperx_result: WhisperXOutput = serde_json::from_value(response["result"].clone()) - .map_err(|e| format!("Failed to parse WhisperX output: {}", e))?; + .map_err(|e| format!("Failed to parse WhisperX output: {e}"))?; log::info!( "WhisperX server produced {} segments", @@ -1188,7 +1162,7 @@ fn transcribe_with_server( .unwrap_or(whisperx_seg.end as f32); segments.push(CaptionSegment { - id: format!("segment-{}-{}", seg_idx, chunk_idx), + id: format!("segment-{seg_idx}-{chunk_idx}"), start: segment_start, end: segment_end, text: segment_text, @@ -1209,7 +1183,7 @@ fn get_whisperx_cache_dir() -> Result { .ok_or_else(|| "Could not determine cache directory".to_string())?; let whisperx_dir = cache_dir.join("cap").join("whisperx"); std::fs::create_dir_all(&whisperx_dir) - .map_err(|e| format!("Failed to create whisperx cache directory: {}", e))?; + .map_err(|e| format!("Failed to create whisperx cache directory: {e}"))?; Ok(whisperx_dir) } @@ -1217,7 +1191,7 @@ fn get_whisperx_models_cache_dir() -> Result { let cache_dir = get_whisperx_cache_dir()?; let models_dir = cache_dir.join("models"); std::fs::create_dir_all(&models_dir) - .map_err(|e| format!("Failed to create whisperx models cache directory: {}", e))?; + .map_err(|e| format!("Failed to create whisperx models cache directory: {e}"))?; Ok(models_dir) } @@ -1225,7 +1199,7 @@ fn get_huggingface_cache_dir() -> Result { let cache_dir = get_whisperx_cache_dir()?; let hf_dir = cache_dir.join("huggingface"); std::fs::create_dir_all(&hf_dir) - .map_err(|e| format!("Failed to create huggingface cache directory: {}", e))?; + .map_err(|e| format!("Failed to create huggingface cache directory: {e}"))?; Ok(hf_dir) } @@ -1233,7 +1207,7 @@ fn get_torch_cache_dir() -> Result { let cache_dir = get_whisperx_cache_dir()?; let torch_dir = cache_dir.join("torch"); std::fs::create_dir_all(&torch_dir) - .map_err(|e| format!("Failed to create torch cache directory: {}", e))?; + .map_err(|e| format!("Failed to create torch cache directory: {e}"))?; Ok(torch_dir) } @@ -1254,24 +1228,20 @@ fn create_venv_if_needed(system_python: &str) -> Result { let venv_python = get_venv_python()?; if venv_python.exists() { - log::info!("Virtual environment already exists at: {:?}", venv_dir); + log::info!("Virtual environment already exists at: {venv_dir:?}"); return Ok(venv_python); } - log::info!( - "Creating virtual environment at: {:?} using {}", - venv_dir, - system_python - ); + log::info!("Creating virtual environment at: {venv_dir:?} using {system_python}"); let output = Command::new(system_python) .args(["-m", "venv", venv_dir.to_str().unwrap()]) .output() - .map_err(|e| format!("Failed to create venv: {}", e))?; + .map_err(|e| format!("Failed to create venv: {e}"))?; if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); - return Err(format!("Failed to create virtual environment: {}", stderr)); + return Err(format!("Failed to create virtual environment: {stderr}")); } if !venv_python.exists() { @@ -1286,7 +1256,7 @@ fn create_venv_if_needed(system_python: &str) -> Result { .output(); if let Err(e) = pip_upgrade { - log::warn!("Failed to upgrade pip in venv: {}", e); + log::warn!("Failed to upgrade pip in venv: {e}"); } Ok(venv_python) @@ -1297,11 +1267,11 @@ fn download_whisperx_whl() -> Result { let whl_path = cache_dir.join(WHISPERX_WHL_NAME); if whl_path.exists() { - log::info!("WhisperX wheel already cached at: {:?}", whl_path); + log::info!("WhisperX wheel already cached at: {whl_path:?}"); return Ok(whl_path); } - log::info!("Downloading WhisperX wheel from: {}", WHISPERX_WHL_URL); + log::info!("Downloading WhisperX wheel from: {WHISPERX_WHL_URL}"); let output = Command::new("curl") .args([ @@ -1312,18 +1282,18 @@ fn download_whisperx_whl() -> Result { WHISPERX_WHL_URL, ]) .output() - .map_err(|e| format!("Failed to run curl: {}", e))?; + .map_err(|e| format!("Failed to run curl: {e}"))?; if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); - return Err(format!("Failed to download WhisperX wheel: {}", stderr)); + return Err(format!("Failed to download WhisperX wheel: {stderr}")); } if !whl_path.exists() { return Err("WhisperX wheel was not downloaded".to_string()); } - log::info!("Successfully downloaded WhisperX wheel to: {:?}", whl_path); + log::info!("Successfully downloaded WhisperX wheel to: {whl_path:?}"); Ok(whl_path) } @@ -1331,16 +1301,16 @@ fn install_whisperx_in_venv(venv_python: &PathBuf) -> Result<(), String> { log::info!("Installing whisperx in virtual environment..."); let whl_path = download_whisperx_whl()?; - log::info!("Installing WhisperX from: {:?}", whl_path); + log::info!("Installing WhisperX from: {whl_path:?}"); let install_result = Command::new(venv_python) .args(["-m", "pip", "install", whl_path.to_str().unwrap()]) .output() - .map_err(|e| format!("Failed to run pip install: {}", e))?; + .map_err(|e| format!("Failed to run pip install: {e}"))?; if !install_result.status.success() { let stderr = String::from_utf8_lossy(&install_result.stderr); - return Err(format!("Failed to install whisperx: {}", stderr)); + return Err(format!("Failed to install whisperx: {stderr}")); } log::info!("Successfully installed whisperx in virtual environment"); @@ -1422,7 +1392,7 @@ pub async fn prewarm_whisperx(model_path: String) -> Result { let venv_python = match setup_whisperx_environment(&system_python) { Ok(p) => p, Err(e) => { - log::info!("WhisperX environment not ready: {}, skipping pre-warm", e); + log::info!("WhisperX environment not ready: {e}, skipping pre-warm"); return Ok(false); } }; @@ -1450,7 +1420,7 @@ pub async fn prewarm_whisperx(model_path: String) -> Result { *server_guard = Some(server); } Err(e) => { - log::warn!("Failed to pre-warm WhisperX server: {}", e); + log::warn!("Failed to pre-warm WhisperX server: {e}"); } } }); @@ -1472,12 +1442,12 @@ pub async fn transcribe_audio( log::info!("Language: {}", language); if !std::path::Path::new(&video_path).exists() { - log::error!("Video file not found at path: {}", video_path); + log::error!("Video file not found at path: {video_path}"); return Err(format!("Video file not found at path: {video_path}")); } if !std::path::Path::new(&model_path).exists() { - log::error!("Model file not found at path: {}", model_path); + log::error!("Model file not found at path: {model_path}"); return Err(format!("Model file not found at path: {model_path}")); } @@ -1494,7 +1464,7 @@ pub async fn transcribe_audio( } if !audio_path.exists() { - log::error!("Audio file was not created at {:?}", audio_path); + log::error!("Audio file was not created at {audio_path:?}"); return Err("Failed to create audio file for transcription".to_string()); } @@ -1511,7 +1481,7 @@ pub async fn transcribe_audio( log::info!("Detected model size: {}", model_size); if let Some(system_python) = find_python() { - log::info!("Found system Python at: {}", system_python); + log::info!("Found system Python at: {system_python}"); match setup_whisperx_environment(&system_python) { Ok(venv_python) => { @@ -1549,7 +1519,7 @@ pub async fn transcribe_audio( *server_guard = Some(server); } Err(e) => { - log::warn!("Failed to start WhisperX server: {}", e); + log::warn!("Failed to start WhisperX server: {e}"); return Err(e); } } @@ -1572,8 +1542,7 @@ pub async fn transcribe_audio( && is_server_communication_error(e) { log::warn!( - "Server communication error detected, clearing dead server: {}", - e + "Server communication error detected, clearing dead server: {e}" ); *server_guard = None; } @@ -1608,20 +1577,16 @@ pub async fn transcribe_audio( } } Ok(Err(e)) => { - log::warn!("WhisperX failed: {}. Falling back to built-in Whisper.", e); + log::warn!("WhisperX failed: {e}. Falling back to built-in Whisper."); } Err(e) => { - log::warn!( - "WhisperX task error: {}. Falling back to built-in Whisper.", - e - ); + log::warn!("WhisperX task error: {e}. Falling back to built-in Whisper."); } } } Err(e) => { log::warn!( - "Failed to setup WhisperX environment: {}. Falling back to built-in Whisper.", - e + "Failed to setup WhisperX environment: {e}. Falling back to built-in Whisper." ); } } diff --git a/apps/desktop/src-tauri/src/lib.rs b/apps/desktop/src-tauri/src/lib.rs index 3bab3baefa..85630e45c3 100644 --- a/apps/desktop/src-tauri/src/lib.rs +++ b/apps/desktop/src-tauri/src/lib.rs @@ -484,8 +484,7 @@ async fn set_camera_input( Err(e) => { if attempts >= 3 { break Err(format!( - "Failed to initialize camera after {} attempts: {}", - attempts, e + "Failed to initialize camera after {attempts} attempts: {e}" )); } warn!( diff --git a/apps/desktop/src-tauri/src/screenshot_editor.rs b/apps/desktop/src-tauri/src/screenshot_editor.rs index 208b44405c..4b0e41d914 100644 --- a/apps/desktop/src-tauri/src/screenshot_editor.rs +++ b/apps/desktop/src-tauri/src/screenshot_editor.rs @@ -158,7 +158,7 @@ impl ScreenshotEditorInstances { .map(|e| e.path()) }) .ok_or_else(|| { - format!("No PNG file found in directory: {:?}", path) + format!("No PNG file found in directory: {path:?}") })? } } else { @@ -465,15 +465,12 @@ pub async fn update_screenshot_config( if parent.extension().and_then(|s| s.to_str()) == Some("cap") { let path = parent.to_path_buf(); if let Err(e) = config.write(&path) { - eprintln!("Failed to save screenshot config: {}", e); + eprintln!("Failed to save screenshot config: {e}"); } else { - println!("Saved screenshot config to {:?}", path); + println!("Saved screenshot config to {path:?}"); } } else { - println!( - "Not saving config: parent {:?} is not a .cap directory", - parent - ); + println!("Not saving config: parent {parent:?} is not a .cap directory"); } Ok(()) } diff --git a/apps/desktop/src-tauri/src/update_project_names.rs b/apps/desktop/src-tauri/src/update_project_names.rs index 723ad80b87..38e9f4542b 100644 --- a/apps/desktop/src-tauri/src/update_project_names.rs +++ b/apps/desktop/src-tauri/src/update_project_names.rs @@ -17,7 +17,7 @@ const STORE_KEY: &str = "uuid_projects_migrated"; pub fn migrate_if_needed(app: &AppHandle) -> Result<(), String> { let store = app .store("store") - .map_err(|e| format!("Failed to access store: {}", e))?; + .map_err(|e| format!("Failed to access store: {e}"))?; if store .get(STORE_KEY) @@ -51,7 +51,7 @@ pub async fn migrate(app: &AppHandle) -> Result<(), String> { let recordings_dir = recordings_path(app); if !fs::try_exists(&recordings_dir) .await - .map_err(|e| format!("Failed to check recordings directory: {}", e))? + .map_err(|e| format!("Failed to check recordings directory: {e}"))? { return Ok(()); } @@ -154,12 +154,12 @@ async fn collect_uuid_projects(recordings_dir: &Path) -> Result, St let mut uuid_projects = Vec::new(); let mut entries = fs::read_dir(recordings_dir) .await - .map_err(|e| format!("Failed to read recordings directory: {}", e))?; + .map_err(|e| format!("Failed to read recordings directory: {e}"))?; while let Some(entry) = entries .next_entry() .await - .map_err(|e| format!("Failed to read directory entry: {}", e))? + .map_err(|e| format!("Failed to read directory entry: {e}"))? { let path = entry.path(); if !path.is_dir() { @@ -197,7 +197,7 @@ async fn migrate_single_project( Ok(meta) => meta, Err(e) => { tracing::warn!("Failed to load metadata for {}: {}", filename, e); - return Err(format!("Failed to load metadata: {}", e)); + return Err(format!("Failed to load metadata: {e}")); } }; @@ -260,7 +260,7 @@ async fn migrate_project_filename_async( let filename = if sanitized.ends_with(".cap") { sanitized } else { - format!("{}.cap", sanitized) + format!("{sanitized}.cap") }; let parent_dir = project_path @@ -268,13 +268,13 @@ async fn migrate_project_filename_async( .ok_or("Project path has no parent directory")?; let unique_filename = cap_utils::ensure_unique_filename(&filename, parent_dir) - .map_err(|e| format!("Failed to ensure unique filename: {}", e))?; + .map_err(|e| format!("Failed to ensure unique filename: {e}"))?; let final_path = parent_dir.join(&unique_filename); fs::rename(project_path, &final_path) .await - .map_err(|e| format!("Failed to rename project directory: {}", e))?; + .map_err(|e| format!("Failed to rename project directory: {e}"))?; Ok(final_path) } diff --git a/apps/desktop/src-tauri/src/upload.rs b/apps/desktop/src-tauri/src/upload.rs index f426ea2d9a..5ba1b0bd7e 100644 --- a/apps/desktop/src-tauri/src/upload.rs +++ b/apps/desktop/src-tauri/src/upload.rs @@ -230,7 +230,7 @@ pub async fn create_or_get_video( } if let Some(org_id) = organization_id { - s3_config_url.push_str(&format!("&orgId={}", org_id)); + s3_config_url.push_str(&format!("&orgId={org_id}")); } let response = app diff --git a/crates/recording/src/recovery.rs b/crates/recording/src/recovery.rs index dac11d84a3..79f7669803 100644 --- a/crates/recording/src/recovery.rs +++ b/crates/recording/src/recovery.rs @@ -455,14 +455,12 @@ impl RecoveryManager { } Ok(false) => { return Err(RecoveryError::UnplayableVideo(format!( - "Display video has no decodable frames: {:?}", - display_output + "Display video has no decodable frames: {display_output:?}" ))); } Err(e) => { return Err(RecoveryError::UnplayableVideo(format!( - "Display video validation failed for {:?}: {}", - display_output, e + "Display video validation failed for {display_output:?}: {e}" ))); } } @@ -521,7 +519,8 @@ impl RecoveryManager { .recoverable_segments .iter() .map(|seg| { - let segment_base = format!("content/segments/segment-{}", seg.index); + let segment_index = seg.index; + let segment_base = format!("content/segments/segment-{segment_index}"); let segment_dir = recording.project_path.join(&segment_base); let display_path = segment_dir.join("display.mp4"); @@ -534,13 +533,13 @@ impl RecoveryManager { MultipleSegment { display: VideoMeta { - path: RelativePathBuf::from(format!("{}/display.mp4", segment_base)), + path: RelativePathBuf::from(format!("{segment_base}/display.mp4")), fps, start_time: None, }, camera: if camera_path.exists() { Some(VideoMeta { - path: RelativePathBuf::from(format!("{}/camera.mp4", segment_base)), + path: RelativePathBuf::from(format!("{segment_base}/camera.mp4")), fps: 30, start_time: None, }) @@ -549,10 +548,7 @@ impl RecoveryManager { }, mic: if mic_path.exists() { Some(AudioMeta { - path: RelativePathBuf::from(format!( - "{}/audio-input.ogg", - segment_base - )), + path: RelativePathBuf::from(format!("{segment_base}/audio-input.ogg")), start_time: None, }) } else { @@ -560,20 +556,14 @@ impl RecoveryManager { }, system_audio: if system_audio_path.exists() { Some(AudioMeta { - path: RelativePathBuf::from(format!( - "{}/system_audio.ogg", - segment_base - )), + path: RelativePathBuf::from(format!("{segment_base}/system_audio.ogg")), start_time: None, }) } else { None }, cursor: if cursor_path.exists() { - Some(RelativePathBuf::from(format!( - "{}/cursor.json", - segment_base - ))) + Some(RelativePathBuf::from(format!("{segment_base}/cursor.json"))) } else { None }, @@ -605,7 +595,7 @@ impl RecoveryManager { .iter() .enumerate() .filter_map(|(i, segment)| { - let segment_base = format!("content/segments/segment-{}", i); + let segment_base = format!("content/segments/segment-{i}"); let display_path = recording .project_path .join(&segment_base) diff --git a/crates/rendering/src/decoder/avassetreader.rs b/crates/rendering/src/decoder/avassetreader.rs index b8a5fe269d..b53a43e3ab 100644 --- a/crates/rendering/src/decoder/avassetreader.rs +++ b/crates/rendering/src/decoder/avassetreader.rs @@ -219,23 +219,23 @@ impl AVAssetReaderDecoder { let mut frames = this.inner.frames(); let mut processor = ImageBufProcessor::new(); - let mut cache_hits = 0u64; - let mut cache_misses = 0u64; - let mut total_requests = 0u64; - let mut total_decode_time_us = 0u64; - let mut total_reset_count = 0u64; - let mut total_reset_time_us = 0u64; + let mut _cache_hits = 0u64; + let mut _cache_misses = 0u64; + let mut _total_requests = 0u64; + let _total_decode_time_us = 0u64; + let mut _total_reset_count = 0u64; + let mut _total_reset_time_us = 0u64; let last_metrics_log = Rc::new(RefCell::new(Instant::now())); while let Ok(r) = rx.recv() { match r { VideoDecoderMessage::GetFrame(requested_time, sender) => { let request_start = Instant::now(); - total_requests += 1; + _total_requests += 1; let requested_frame = (requested_time * fps as f32).floor() as u32; let mut sender = if let Some(cached) = cache.get(&requested_frame) { - cache_hits += 1; + _cache_hits += 1; let data = cached.data().clone(); let total_time = request_start.elapsed(); @@ -256,7 +256,7 @@ impl AVAssetReaderDecoder { *last_sent_frame.borrow_mut() = Some(data); continue; } else { - cache_misses += 1; + _cache_misses += 1; let last_sent_frame = last_sent_frame.clone(); let request_start_clone = request_start; let last_metrics_log_clone = last_metrics_log.clone(); @@ -302,7 +302,7 @@ impl AVAssetReaderDecoder { if needs_reset { let reset_start = Instant::now(); - total_reset_count += 1; + _total_reset_count += 1; this.reset(requested_time); frames = this.inner.frames(); *last_sent_frame.borrow_mut() = None; @@ -316,7 +316,7 @@ impl AVAssetReaderDecoder { let cleared = old_cache_size - retained; let reset_time = reset_start.elapsed(); - total_reset_time_us += reset_time.as_micros() as u64; + _total_reset_time_us += reset_time.as_micros() as u64; tracing::info!( decoder = name, @@ -325,7 +325,7 @@ impl AVAssetReaderDecoder { reset_time_ms = reset_time.as_millis() as u64, cleared_cache_entries = cleared, retained_cache_entries = retained, - total_resets = total_reset_count, + total_resets = _total_reset_count, "[PERF:DECODER] decoder reset/seek" ); } diff --git a/crates/rendering/src/frame_pipeline.rs b/crates/rendering/src/frame_pipeline.rs index 755cbfa638..1144135011 100644 --- a/crates/rendering/src/frame_pipeline.rs +++ b/crates/rendering/src/frame_pipeline.rs @@ -386,7 +386,7 @@ pub async fn finish_encoder( let total_start = Instant::now(); let previous_pending = session.pipelined_readback.take_pending(); - let has_previous = previous_pending.is_some(); + let _has_previous = previous_pending.is_some(); let texture = if session.current_is_left { &session.textures.0 diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs index 7b0ed42f28..39eca69fa4 100644 --- a/crates/utils/src/lib.rs +++ b/crates/utils/src/lib.rs @@ -89,9 +89,9 @@ pub fn ensure_unique_filename_with_attempts( loop { let numbered_filename = if extension.is_empty() { - format!("{} ({})", name_without_ext, counter) + format!("{name_without_ext} ({counter})") } else { - format!("{} ({}){}", name_without_ext, counter, &extension) + format!("{name_without_ext} ({counter}){extension}") }; let test_path = parent_dir.join(&numbered_filename); From 5a4a18bf383e99677c258928714e66adece37f85 Mon Sep 17 00:00:00 2001 From: Richie McIlroy <33632126+richiemcilroy@users.noreply.github.com> Date: Fri, 12 Dec 2025 15:26:17 +0000 Subject: [PATCH 03/31] Add performance logging and playback optimizations --- apps/desktop/src-tauri/src/frame_ws.rs | 51 +- apps/desktop/src/routes/editor/context.ts | 2 +- apps/desktop/src/utils/socket.ts | 29 + crates/editor/src/editor.rs | 87 +- crates/editor/src/editor_instance.rs | 104 ++- crates/editor/src/playback.rs | 786 +++++++++++------- crates/rendering/src/decoder/avassetreader.rs | 318 ++++--- crates/rendering/src/decoder/ffmpeg.rs | 49 +- crates/rendering/src/decoder/mod.rs | 144 +++- crates/rendering/src/frame_pipeline.rs | 69 +- crates/rendering/src/layers/camera.rs | 182 +++- crates/rendering/src/layers/display.rs | 190 +++-- crates/rendering/src/lib.rs | 167 ++-- .../rendering/src/shaders/nv12_to_rgba.wgsl | 34 + crates/rendering/src/yuv_converter.rs | 585 +++++++++++++ 15 files changed, 2046 insertions(+), 751 deletions(-) create mode 100644 crates/rendering/src/shaders/nv12_to_rgba.wgsl create mode 100644 crates/rendering/src/yuv_converter.rs diff --git a/apps/desktop/src-tauri/src/frame_ws.rs b/apps/desktop/src-tauri/src/frame_ws.rs index 4ad9bc4e35..3c3f81ecd2 100644 --- a/apps/desktop/src-tauri/src/frame_ws.rs +++ b/apps/desktop/src-tauri/src/frame_ws.rs @@ -7,11 +7,7 @@ fn compress_frame_data(mut data: Vec, stride: u32, height: u32, width: u32) data.extend_from_slice(&height.to_le_bytes()); data.extend_from_slice(&width.to_le_bytes()); - let data_len = data.len(); - let mut result = Vec::with_capacity(data_len + 4); - result.extend_from_slice(&(data_len as u32).to_le_bytes()); - result.extend_from_slice(&data); - result + lz4_flex::compress_prepend_size(&data) } #[derive(Clone)] @@ -115,27 +111,15 @@ pub async fn create_watch_frame_ws( let send_start = Instant::now(); let original_size = frame.data.len(); - let compress_start = Instant::now(); let packed = compress_frame_data(frame.data, frame.stride, frame.height, frame.width); - let compress_time = compress_start.elapsed(); let compressed_size = packed.len(); - let ws_send_start = Instant::now(); if let Err(e) = socket.send(Message::Binary(packed)).await { tracing::error!("Failed to send frame to socket: {:?}", e); break; } - let ws_send_time = ws_send_start.elapsed(); let send_time = send_start.elapsed(); - // #region agent log - use std::io::Write; - if let Ok(mut f) = std::fs::OpenOptions::new().create(true).append(true).open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") { - let _ = writeln!(f, r#"{{"hypothesisId":"D","location":"frame_ws.rs:ws_send","message":"WebSocket frame sent","data":{{"frame_latency_us":{},"compress_time_us":{},"ws_send_time_us":{},"total_send_time_us":{},"original_bytes":{},"compressed_bytes":{}}},"timestamp":{}}}"#, - latency_us, compress_time.as_micros(), ws_send_time.as_micros(), send_time.as_micros(), original_size, compressed_size, std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_millis()); - } - // #endregion - tracing::debug!( frame_latency_us = latency_us, send_time_us = send_time.as_micros() as u64, @@ -143,6 +127,27 @@ pub async fn create_watch_frame_ws( compressed_size_bytes = compressed_size, "[PERF:WS_WATCH] frame sent (compressed)" ); + + // #region agent log + use std::io::Write; + if let Ok(mut file) = std::fs::OpenOptions::new().create(true).append(true).open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") { + let log_entry = serde_json::json!({ + "location": "frame_ws.rs:ws_send", + "message": "websocket frame sent", + "data": { + "frame_latency_us": latency_us, + "send_time_us": send_time.as_micros() as u64, + "original_size_bytes": original_size, + "compressed_size_bytes": compressed_size, + "compression_ratio_pct": format!("{:.1}", (compressed_size as f64 / original_size as f64) * 100.0) + }, + "timestamp": std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_millis() as u64, + "sessionId": "debug-session", + "hypothesisId": "A" + }); + writeln!(file, "{}", log_entry).ok(); + } + // #endregion } } } @@ -246,27 +251,15 @@ pub async fn create_frame_ws(frame_tx: broadcast::Sender) -> (u16, Canc let send_start = Instant::now(); let original_size = frame.data.len(); - let compress_start = Instant::now(); let packed = compress_frame_data(frame.data, frame.stride, frame.height, frame.width); - let compress_time = compress_start.elapsed(); let compressed_size = packed.len(); - let ws_send_start = Instant::now(); if let Err(e) = socket.send(Message::Binary(packed)).await { tracing::error!("Failed to send frame to socket: {:?}", e); break; } - let ws_send_time = ws_send_start.elapsed(); let send_time = send_start.elapsed(); - // #region agent log - use std::io::Write; - if let Ok(mut f) = std::fs::OpenOptions::new().create(true).append(true).open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") { - let _ = writeln!(f, r#"{{"hypothesisId":"WS_BROADCAST","location":"frame_ws.rs:broadcast_send","message":"WebSocket broadcast frame sent","data":{{"frame_latency_us":{},"compress_time_us":{},"ws_send_time_us":{},"total_send_time_us":{},"width":{},"height":{}}},"timestamp":{}}}"#, - latency_us, compress_time.as_micros(), ws_send_time.as_micros(), send_time.as_micros(), frame.width, frame.height, std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_millis()); - } - // #endregion - tracing::debug!( frame_latency_us = latency_us, send_time_us = send_time.as_micros() as u64, diff --git a/apps/desktop/src/routes/editor/context.ts b/apps/desktop/src/routes/editor/context.ts index f7b9bef38b..f98d905448 100644 --- a/apps/desktop/src/routes/editor/context.ts +++ b/apps/desktop/src/routes/editor/context.ts @@ -47,7 +47,7 @@ export type CurrentDialog = export type DialogState = { open: false } | ({ open: boolean } & CurrentDialog); -export const FPS = 60; +export const FPS = 30; export const OUTPUT_SIZE = { x: 1920, diff --git a/apps/desktop/src/utils/socket.ts b/apps/desktop/src/utils/socket.ts index d4e61b7a6e..361e934d13 100644 --- a/apps/desktop/src/utils/socket.ts +++ b/apps/desktop/src/utils/socket.ts @@ -176,6 +176,35 @@ export function createImageDataWS( console.log( `[PERF:FRONTEND_WS] periodic - frames: ${metrics.framesReceived}, compressed: ${compressedSize} bytes (${compressionRatio}%), decompressed: ${decompressed.length} bytes, avg decompress: ${avgDecompressTime.toFixed(2)}ms, avg parse: ${avgParseTime.toFixed(2)}ms, avg imageData: ${avgImageDataTime.toFixed(2)}ms, dimensions: ${width}x${height}`, ); + // #region agent log + fetch( + "http://127.0.0.1:7242/ingest/966647b7-72f6-4ab7-b76e-6b773ac020d7", + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + location: "socket.ts:ws_metrics", + message: "frontend WS metrics", + data: { + framesReceived: metrics.framesReceived, + avgDecompressMs: avgDecompressTime.toFixed(2), + avgParseMs: avgParseTime.toFixed(2), + avgImageDataMs: avgImageDataTime.toFixed(2), + maxDecompressMs: metrics.maxDecompressTimeMs.toFixed(2), + maxParseMs: metrics.maxParseTimeMs.toFixed(2), + compressedBytes: compressedSize, + decompressedBytes: decompressed.length, + compressionRatio, + width, + height, + }, + timestamp: Date.now(), + sessionId: "debug-session", + hypothesisId: "B", + }), + }, + ).catch(() => {}); + // #endregion metrics.lastLogTime = now; } diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index e40261e0b6..9610b4e119 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -151,6 +151,7 @@ impl Renderer { }; let mut dropped_in_batch = 0u32; + let queue_drain_start = Instant::now(); while let Ok(msg) = self.rx.try_recv() { match msg { RendererMessage::RenderFrame { @@ -176,6 +177,9 @@ impl Renderer { return; } } + if queue_drain_start.elapsed().as_millis() > 5 { + break; + } } if dropped_in_batch > 0 { @@ -185,48 +189,8 @@ impl Renderer { total_dropped = frames_dropped, "[PERF:EDITOR_RENDER] dropped frames to catch up" ); - - // #region agent log - use std::io::Write; - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") - { - let _ = writeln!( - f, - r#"{{"hypothesisId":"A","location":"editor.rs:frames_dropped","message":"Renderer dropped frames due to backpressure","data":{{"dropped_in_batch":{},"total_dropped":{},"rendering_frame":{}}},"timestamp":{}}}"#, - dropped_in_batch, - frames_dropped, - current.frame_number, - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_millis() - ); - } - // #endregion } - // #region agent log - use std::io::Write; - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") - { - let _ = writeln!( - f, - r#"{{"hypothesisId":"A","location":"editor.rs:render_start","message":"Starting GPU render","data":{{"frame_number":{}}},"timestamp":{}}}"#, - current.frame_number, - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_millis() - ); - } - // #endregion - let render_start = Instant::now(); let frame = frame_renderer .render( @@ -239,25 +203,6 @@ impl Renderer { .unwrap(); let render_time = render_start.elapsed(); - // #region agent log - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") - { - let _ = writeln!( - f, - r#"{{"hypothesisId":"A","location":"editor.rs:render_complete","message":"GPU render complete","data":{{"frame_number":{},"render_time_us":{}}},"timestamp":{}}}"#, - current.frame_number, - render_time.as_micros(), - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_millis() - ); - } - // #endregion - let callback_start = Instant::now(); (self.frame_cb)(frame); let callback_time = callback_start.elapsed(); @@ -277,6 +222,26 @@ impl Renderer { "[PERF:EDITOR_RENDER] frame rendered" ); + // #region agent log + use std::io::Write; + if let Ok(mut file) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") + { + let ts = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() as u64; + writeln!( + file, + r#"{{"location":"editor.rs:frame_rendered","message":"editor render timing","data":{{"frame_number":{},"render_time_us":{},"callback_time_us":{}}},"timestamp":{},"sessionId":"debug-session","hypothesisId":"C"}}"#, + current.frame_number, render_time_us, callback_time_us, ts + ) + .ok(); + } + // #endregion + if last_metrics_log.elapsed().as_secs() >= 2 && frames_rendered > 0 { let avg_render_time = total_render_time_us / frames_rendered; let avg_callback_time = total_callback_time_us / frames_rendered; @@ -309,7 +274,7 @@ impl RendererHandle { cursor: Arc, frame_number: u32, ) { - let (finished_tx, finished_rx) = oneshot::channel(); + let (finished_tx, _finished_rx) = oneshot::channel(); self.send(RendererMessage::RenderFrame { segment_frames, @@ -319,8 +284,6 @@ impl RendererHandle { frame_number, }) .await; - - let _ = finished_rx.await; } pub async fn stop(&self) { diff --git a/crates/editor/src/editor_instance.rs b/crates/editor/src/editor_instance.rs index ac627b2430..e20c2355d8 100644 --- a/crates/editor/src/editor_instance.rs +++ b/crates/editor/src/editor_instance.rs @@ -12,14 +12,16 @@ use cap_rendering::{ }; use std::{path::PathBuf, sync::Arc}; use tokio::sync::{Mutex, watch}; +use tokio_util::sync::CancellationToken; use tracing::{trace, warn}; pub struct EditorInstance { pub project_path: PathBuf, - // pub ws_port: u16, pub recordings: Arc, pub renderer: Arc, pub render_constants: Arc, + playback_active: watch::Sender, + playback_active_rx: watch::Receiver, pub state: Arc>, on_state_change: Box, pub preview_tx: watch::Sender>, @@ -163,6 +165,7 @@ impl EditorInstance { )?); let (preview_tx, preview_rx) = watch::channel(None); + let (playback_active_tx, playback_active_rx) = watch::channel(false); let this = Arc::new(Self { project_path, @@ -179,6 +182,8 @@ impl EditorInstance { project_config: watch::channel(project), segment_medias: Arc::new(segments), meta: recording_meta, + playback_active: playback_active_tx, + playback_active_rx, }); this.state.lock().await.preview_task = @@ -235,6 +240,8 @@ impl EditorInstance { } pub async fn start_playback(self: &Arc, fps: u32, resolution_base: XY) { + let _ = self.playback_active.send(true); + let (mut handle, prev) = { let Ok(mut state) = self.state.try_lock() else { return; @@ -278,7 +285,7 @@ impl EditorInstance { .await; } playback::PlaybackEvent::Stop => { - // ! This editor instance (self) gets dropped here + let _ = this.playback_active.send(false); return; } } @@ -297,6 +304,8 @@ impl EditorInstance { trace!("Starting preview renderer task"); tokio::spawn(async move { trace!("Preview renderer task running"); + let mut prefetch_cancel_token: Option = None; + loop { trace!("Preview renderer: waiting for frame request"); preview_rx.changed().await.unwrap(); @@ -309,8 +318,36 @@ impl EditorInstance { break; }; + if let Some(token) = prefetch_cancel_token.take() { + token.cancel(); + } + + if *self.playback_active_rx.borrow() { + break; + } + trace!("Preview renderer: processing frame {}", frame_number); + // #region agent log + use std::io::Write; + if let Ok(mut file) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") + { + let ts = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() as u64; + writeln!( + file, + r#"{{"location":"editor_instance.rs:preview_request","message":"preview renderer requesting frame","data":{{"frame_number":{},"fps":{}}},"timestamp":{},"sessionId":"debug-session","hypothesisId":"C"}}"#, + frame_number, fps, ts + ) + .ok(); + } + // #endregion + let project = self.project_config.1.borrow().clone(); let Some((segment_time, segment)) = @@ -330,6 +367,69 @@ impl EditorInstance { .find(|v| v.index == segment.recording_clip); let clip_offsets = clip_config.map(|v| v.offsets).unwrap_or_default(); + let new_cancel_token = CancellationToken::new(); + prefetch_cancel_token = Some(new_cancel_token.clone()); + + let playback_is_active = *self.playback_active_rx.borrow(); + if !playback_is_active { + // #region agent log + use std::io::Write; + if let Ok(mut file) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") + { + let ts = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() as u64; + writeln!( + file, + r#"{{"location":"editor_instance.rs:preview_prefetch","message":"preview spawning prefetch tasks","data":{{"frame_number":{},"prefetch_count":5}},"timestamp":{},"sessionId":"debug-session","hypothesisId":"C"}}"#, + frame_number, ts + ) + .ok(); + } + // #endregion + + let prefetch_frames_count = 5u32; + let hide_camera = project.camera.hide; + let playback_rx = self.playback_active_rx.clone(); + for offset in 1..=prefetch_frames_count { + let prefetch_frame = frame_number + offset; + if let Some((prefetch_segment_time, prefetch_segment)) = + project.get_segment_time(prefetch_frame as f64 / fps as f64) + { + if let Some(prefetch_segment_media) = self + .segment_medias + .get(prefetch_segment.recording_clip as usize) + { + let prefetch_clip_offsets = project + .clips + .iter() + .find(|v| v.index == prefetch_segment.recording_clip) + .map(|v| v.offsets) + .unwrap_or_default(); + let decoders = prefetch_segment_media.decoders.clone(); + let cancel_token = new_cancel_token.clone(); + let playback_rx = playback_rx.clone(); + tokio::spawn(async move { + if cancel_token.is_cancelled() || *playback_rx.borrow() { + return; + } + let _ = decoders + .get_frames( + prefetch_segment_time as f32, + !hide_camera, + prefetch_clip_offsets, + ) + .await; + }); + } + } + } + } + let get_frames_future = segment_medias.decoders.get_frames( segment_time as f32, !project.camera.hide, diff --git a/crates/editor/src/playback.rs b/crates/editor/src/playback.rs index 04a0455298..8606f8669f 100644 --- a/crates/editor/src/playback.rs +++ b/crates/editor/src/playback.rs @@ -11,7 +11,7 @@ use cpal::{ }; use futures::stream::{FuturesUnordered, StreamExt}; use std::{ - collections::{HashSet, VecDeque}, + collections::{HashMap, HashSet, VecDeque}, sync::{Arc, RwLock}, time::Duration, }; @@ -28,9 +28,11 @@ use crate::{ segments::get_audio_segments, }; -const PREFETCH_BUFFER_SIZE: usize = 64; -const PARALLEL_DECODE_TASKS: usize = 8; -const MAX_PREFETCH_AHEAD: u32 = 90; +const PREFETCH_BUFFER_SIZE: usize = 120; +const PARALLEL_DECODE_TASKS: usize = 16; +const MAX_PREFETCH_AHEAD: u32 = 150; +const PREFETCH_BEHIND: u32 = 30; +const FRAME_CACHE_SIZE: usize = 90; #[derive(Debug)] pub enum PlaybackStartError { @@ -64,6 +66,58 @@ struct PrefetchedFrame { segment_index: u32, } +struct FrameCache { + frames: HashMap, + order: VecDeque, + capacity: usize, +} + +impl FrameCache { + fn new(capacity: usize) -> Self { + Self { + frames: HashMap::with_capacity(capacity), + order: VecDeque::with_capacity(capacity), + capacity, + } + } + + fn get(&mut self, frame_number: u32) -> Option<(DecodedSegmentFrames, u32)> { + if let Some(data) = self.frames.get(&frame_number) { + if let Some(pos) = self.order.iter().position(|&f| f == frame_number) { + self.order.remove(pos); + self.order.push_back(frame_number); + } + Some(data.clone()) + } else { + None + } + } + + fn insert( + &mut self, + frame_number: u32, + segment_frames: DecodedSegmentFrames, + segment_index: u32, + ) { + if self.frames.contains_key(&frame_number) { + if let Some(pos) = self.order.iter().position(|&f| f == frame_number) { + self.order.remove(pos); + } + } else if self.frames.len() >= self.capacity { + if let Some(oldest) = self.order.pop_front() { + self.frames.remove(&oldest); + } + } + self.frames + .insert(frame_number, (segment_frames, segment_index)); + self.order.push_back(frame_number); + } + + fn len(&self) -> usize { + self.frames.len() + } +} + impl Playback { pub async fn start( self, @@ -107,8 +161,18 @@ impl Playback { }; tokio::spawn(async move { + type PrefetchFuture = std::pin::Pin< + Box< + dyn std::future::Future)> + + Send, + >, + >; let mut next_prefetch_frame = *frame_request_rx.borrow(); - let mut in_flight: FuturesUnordered<_> = FuturesUnordered::new(); + let mut in_flight: FuturesUnordered = FuturesUnordered::new(); + let mut frames_decoded: u32 = 0; + let mut prefetched_behind: HashSet = HashSet::new(); + const INITIAL_PARALLEL_TASKS: usize = 8; + const RAMP_UP_AFTER_FRAMES: u32 = 5; loop { if *prefetch_stop_rx.borrow() { @@ -117,18 +181,67 @@ impl Playback { if let Ok(true) = frame_request_rx.has_changed() { let requested = *frame_request_rx.borrow_and_update(); - if requested > next_prefetch_frame { + if requested != next_prefetch_frame { + let old_frame = next_prefetch_frame; + let is_backward_seek = requested < old_frame; + let seek_distance = if is_backward_seek { + old_frame - requested + } else { + requested - old_frame + }; + next_prefetch_frame = requested; + frames_decoded = 0; + prefetched_behind.clear(); + + let in_flight_before = + prefetch_in_flight.read().map(|g| g.len()).unwrap_or(0); + let futures_before = in_flight.len(); + if let Ok(mut in_flight_guard) = prefetch_in_flight.write() { - in_flight_guard.retain(|&f| f >= requested); + in_flight_guard.clear(); + } + + if is_backward_seek || seek_distance > MAX_PREFETCH_AHEAD / 2 { + in_flight = FuturesUnordered::new(); + } + + let in_flight_after = + prefetch_in_flight.read().map(|g| g.len()).unwrap_or(0); + let futures_after = in_flight.len(); + + // #region agent log + use std::io::Write; + if let Ok(mut file) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") + { + let ts = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() as u64; + writeln!( + file, + r#"{{"location":"playback.rs:prefetch_seek","message":"prefetch task received seek","data":{{"old_frame":{},"new_frame":{},"is_backward":{},"seek_distance":{},"in_flight_before":{},"in_flight_after":{},"futures_before":{},"futures_after":{}}},"timestamp":{},"sessionId":"debug-session","hypothesisId":"B"}}"#, + old_frame, requested, is_backward_seek, seek_distance, in_flight_before, in_flight_after, futures_before, futures_after, ts + ) + .ok(); } + // #endregion } } let current_playback_frame = *playback_position_rx.borrow(); let max_prefetch_frame = current_playback_frame + MAX_PREFETCH_AHEAD; - while in_flight.len() < PARALLEL_DECODE_TASKS { + let effective_parallel = if frames_decoded < RAMP_UP_AFTER_FRAMES { + INITIAL_PARALLEL_TASKS + } else { + PARALLEL_DECODE_TASKS + }; + + while in_flight.len() < effective_parallel { let frame_num = next_prefetch_frame; if frame_num > max_prefetch_frame { @@ -171,28 +284,73 @@ impl Playback { in_flight_guard.insert(frame_num); } - in_flight.push(async move { - let decode_start = Instant::now(); + in_flight.push(Box::pin(async move { let result = decoders .get_frames(segment_time as f32, !hide_camera, clip_offsets) .await; - let decode_time = decode_start.elapsed(); - - // #region agent log - use std::io::Write; - if let Ok(mut f) = std::fs::OpenOptions::new().create(true).append(true).open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") { - let _ = writeln!(f, r#"{{"hypothesisId":"DECODE","location":"playback.rs:prefetch_decode","message":"Frame decoded by prefetch","data":{{"frame_number":{},"decode_time_ms":{}}},"timestamp":{}}}"#, - frame_num, decode_time.as_millis(), std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_millis()); - } - // #endregion - (frame_num, segment_index, result) - }); + })); } next_prefetch_frame += 1; } + if in_flight.len() < effective_parallel { + for behind_offset in 1..=PREFETCH_BEHIND { + if in_flight.len() >= effective_parallel { + break; + } + let behind_frame = current_playback_frame.saturating_sub(behind_offset); + if behind_frame == 0 || prefetched_behind.contains(&behind_frame) { + continue; + } + + let prefetch_time = behind_frame as f64 / fps_f64; + if prefetch_time >= prefetch_duration || prefetch_time < 0.0 { + continue; + } + + let already_in_flight = prefetch_in_flight + .read() + .map(|guard| guard.contains(&behind_frame)) + .unwrap_or(false); + if already_in_flight { + continue; + } + + let project = prefetch_project.borrow().clone(); + + if let Some((segment_time, segment)) = + project.get_segment_time(prefetch_time) + && let Some(segment_media) = + prefetch_segment_medias.get(segment.recording_clip as usize) + { + let clip_offsets = project + .clips + .iter() + .find(|v| v.index == segment.recording_clip) + .map(|v| v.offsets) + .unwrap_or_default(); + + let decoders = segment_media.decoders.clone(); + let hide_camera = project.camera.hide; + let segment_index = segment.recording_clip; + + if let Ok(mut in_flight_guard) = prefetch_in_flight.write() { + in_flight_guard.insert(behind_frame); + } + + prefetched_behind.insert(behind_frame); + in_flight.push(Box::pin(async move { + let result = decoders + .get_frames(segment_time as f32, !hide_camera, clip_offsets) + .await; + (behind_frame, segment_index, result) + })); + } + } + } + tokio::select! { biased; @@ -200,6 +358,7 @@ impl Playback { if let Ok(mut in_flight_guard) = prefetch_in_flight.write() { in_flight_guard.remove(&frame_num); } + frames_decoded = frames_decoded.saturating_add(1); if let Some(segment_frames) = result { let _ = prefetch_tx.send(PrefetchedFrame { frame_number: frame_num, @@ -234,12 +393,15 @@ impl Playback { let mut frame_number = self.start_frame_number; let mut prefetch_buffer: VecDeque = VecDeque::with_capacity(PREFETCH_BUFFER_SIZE); - let max_frame_skip = 3u32; + let mut frame_cache = FrameCache::new(FRAME_CACHE_SIZE); + let base_max_frame_skip = 2u32; + let aggressive_skip_threshold = 15u32; let mut total_frames_rendered = 0u64; let mut total_frames_skipped = 0u64; let mut prefetch_hits = 0u64; let mut prefetch_misses = 0u64; + let mut cache_hits = 0u64; let mut total_render_time_us = 0u64; let mut max_render_time_us = 0u64; let mut last_metrics_log = Instant::now(); @@ -251,49 +413,57 @@ impl Playback { "[PERF:PLAYBACK] starting playback" ); - let warmup_target_frames = 10usize; - let warmup_first_frame_timeout = Duration::from_millis(3000); - let warmup_additional_timeout = Duration::from_millis(2000); + // #region agent log + use std::io::Write as _; + if let Ok(mut file) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") + { + let ts = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() as u64; + writeln!( + file, + r#"{{"location":"playback.rs:start","message":"playback starting","data":{{"start_frame":{},"fps":{},"duration":{}}},"timestamp":{},"sessionId":"debug-session","hypothesisId":"E"}}"#, + self.start_frame_number, fps, duration, ts + ) + .ok(); + } + // #endregion + let warmup_start = Instant::now(); + let warmup_target_frames = 8usize; + let warmup_after_first_timeout = Duration::from_millis(200); let mut first_frame_time: Option = None; while !*stop_rx.borrow() { - let timed_out = if let Some(first_time) = first_frame_time { - first_time.elapsed() > warmup_additional_timeout + let should_start = if let Some(first_time) = first_frame_time { + prefetch_buffer.len() >= warmup_target_frames + || first_time.elapsed() > warmup_after_first_timeout } else { - warmup_start.elapsed() > warmup_first_frame_timeout + false }; - if timed_out { - if prefetch_buffer.is_empty() { - warn!( - elapsed_ms = warmup_start.elapsed().as_millis() as u64, - "[PERF:PLAYBACK] warmup timeout waiting for first frame" - ); - } - break; - } - - if first_frame_time.is_some() && prefetch_buffer.len() >= warmup_target_frames { + if should_start { break; } tokio::select! { Some(prefetched) = prefetch_rx.recv() => { if prefetched.frame_number >= frame_number { - let received_frame = prefetched.frame_number; prefetch_buffer.push_back(prefetched); if first_frame_time.is_none() { first_frame_time = Some(Instant::now()); - debug!( - frame = received_frame, - wait_ms = warmup_start.elapsed().as_millis() as u64, - "[PERF:PLAYBACK] first frame received" - ); } } } - _ = tokio::time::sleep(Duration::from_millis(10)) => {} + _ = stop_rx.changed() => { + if *stop_rx.borrow() { + break; + } + } } } @@ -302,36 +472,14 @@ impl Playback { .sort_by_key(|p| p.frame_number); let warmup_time = warmup_start.elapsed(); + let first_frame_ready = first_frame_time.is_some(); info!( warmup_frames = prefetch_buffer.len(), warmup_time_ms = warmup_time.as_millis() as u64, - first_frame_ready = first_frame_time.is_some(), - "[PERF:PLAYBACK] warmup complete" + first_frame_ready = first_frame_ready, + "[PERF:PLAYBACK] warmup complete (adaptive mode)" ); - // #region agent log - { - use std::io::Write; - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") - { - let _ = writeln!( - f, - r#"{{"hypothesisId":"WARMUP","location":"playback.rs:warmup","message":"Warmup phase completed","data":{{"warmup_frames":{},"warmup_time_ms":{},"first_frame_ready":{}}},"timestamp":{}}}"#, - prefetch_buffer.len(), - warmup_time.as_millis(), - first_frame_time.is_some(), - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_millis() - ); - } - } - // #endregion - let start = Instant::now(); 'playback: loop { @@ -359,41 +507,11 @@ impl Playback { let frame_offset = frame_number.saturating_sub(self.start_frame_number) as f64; let next_deadline = start + frame_duration.mul_f64(frame_offset); - // #region agent log - let sleep_start = Instant::now(); - let time_until_deadline = next_deadline.saturating_duration_since(Instant::now()); - // #endregion - tokio::select! { _ = stop_rx.changed() => break 'playback, _ = tokio::time::sleep_until(next_deadline) => {} } - // #region agent log - let sleep_time = sleep_start.elapsed(); - use std::io::Write; - if sleep_time.as_millis() > 50 || time_until_deadline.as_millis() > 50 { - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") - { - let _ = writeln!( - f, - r#"{{"hypothesisId":"SLEEP","location":"playback.rs:sleep_until","message":"Frame deadline sleep","data":{{"frame_number":{},"time_until_deadline_ms":{},"actual_sleep_ms":{},"elapsed_since_start_ms":{}}},"timestamp":{}}}"#, - frame_number, - time_until_deadline.as_millis(), - sleep_time.as_millis(), - start.elapsed().as_millis(), - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_millis() - ); - } - } - // #endregion - if *stop_rx.borrow() { break; } @@ -405,177 +523,226 @@ impl Playback { let project = self.project.borrow().clone(); - let prefetched_idx = prefetch_buffer - .iter() - .position(|p| p.frame_number == frame_number); - let frame_fetch_start = Instant::now(); - let was_prefetched = prefetched_idx.is_some(); + let mut was_prefetched = false; + let mut was_cached = false; - let segment_frames_opt = if let Some(idx) = prefetched_idx { - prefetch_hits += 1; - let prefetched = prefetch_buffer.remove(idx).unwrap(); - Some((prefetched.segment_frames, prefetched.segment_index)) + let segment_frames_opt = if let Some(cached) = frame_cache.get(frame_number) { + cache_hits += 1; + was_cached = true; + Some(cached) } else { - let is_in_flight = main_in_flight - .read() - .map(|guard| guard.contains(&frame_number)) - .unwrap_or(false); - - if is_in_flight { - let wait_start = Instant::now(); - let max_wait = Duration::from_millis(100); - let mut found_frame = None; - - // #region agent log - use std::io::Write; - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") - { - let _ = writeln!( - f, - r#"{{"hypothesisId":"INFLIGHT_WAIT","location":"playback.rs:in_flight_wait_start","message":"Frame in flight - waiting on channel","data":{{"frame_number":{},"elapsed_since_start_ms":{}}},"timestamp":{}}}"#, - frame_number, - start.elapsed().as_millis(), - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_millis() - ); - } - // #endregion - - while wait_start.elapsed() < max_wait { - tokio::select! { - _ = stop_rx.changed() => break 'playback, - Some(prefetched) = prefetch_rx.recv() => { - if prefetched.frame_number == frame_number { - found_frame = Some(prefetched); - break; - } else if prefetched.frame_number >= self.start_frame_number { - prefetch_buffer.push_back(prefetched); + let prefetched_idx = prefetch_buffer + .iter() + .position(|p| p.frame_number == frame_number); + + if let Some(idx) = prefetched_idx { + prefetch_hits += 1; + was_prefetched = true; + let prefetched = prefetch_buffer.remove(idx).unwrap(); + Some((prefetched.segment_frames, prefetched.segment_index)) + } else { + let is_in_flight = main_in_flight + .read() + .map(|guard| guard.contains(&frame_number)) + .unwrap_or(false); + + if is_in_flight { + let wait_start = Instant::now(); + let max_wait = Duration::from_millis(150); + let mut found_frame = None; + + while wait_start.elapsed() < max_wait { + tokio::select! { + _ = stop_rx.changed() => break 'playback, + Some(prefetched) = prefetch_rx.recv() => { + if prefetched.frame_number == frame_number { + found_frame = Some(prefetched); + break; + } else if prefetched.frame_number >= self.start_frame_number { + prefetch_buffer.push_back(prefetched); + } } - } - _ = tokio::time::sleep(Duration::from_millis(5)) => { - let still_in_flight = main_in_flight - .read() - .map(|guard| guard.contains(&frame_number)) - .unwrap_or(false); - if !still_in_flight { - break; + _ = tokio::time::sleep(Duration::from_millis(5)) => { + let still_in_flight = main_in_flight + .read() + .map(|guard| guard.contains(&frame_number)) + .unwrap_or(false); + if !still_in_flight { + break; + } } } } - } - - // #region agent log - let wait_time = wait_start.elapsed(); - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") - { - let _ = writeln!( - f, - r#"{{"hypothesisId":"INFLIGHT_WAIT","location":"playback.rs:in_flight_wait_done","message":"In-flight wait completed","data":{{"frame_number":{},"wait_ms":{},"found":{}}},"timestamp":{}}}"#, - frame_number, - wait_time.as_millis(), - found_frame.is_some(), - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_millis() - ); - } - // #endregion - if let Some(prefetched) = found_frame { - prefetch_hits += 1; - Some((prefetched.segment_frames, prefetched.segment_index)) - } else { - let prefetched_idx = prefetch_buffer - .iter() - .position(|p| p.frame_number == frame_number); - if let Some(idx) = prefetched_idx { + if let Some(prefetched) = found_frame { prefetch_hits += 1; - let prefetched = prefetch_buffer.remove(idx).unwrap(); + was_prefetched = true; Some((prefetched.segment_frames, prefetched.segment_index)) } else { - frame_number = frame_number.saturating_add(1); - total_frames_skipped += 1; - continue; + let prefetched_idx = prefetch_buffer + .iter() + .position(|p| p.frame_number == frame_number); + if let Some(idx) = prefetched_idx { + prefetch_hits += 1; + was_prefetched = true; + let prefetched = prefetch_buffer.remove(idx).unwrap(); + Some((prefetched.segment_frames, prefetched.segment_index)) + } else { + frame_number = frame_number.saturating_add(1); + total_frames_skipped += 1; + continue; + } } - } - } else { - prefetch_misses += 1; - - // #region agent log - use std::io::Write; - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") - { - let _ = writeln!( - f, - r#"{{"hypothesisId":"C","location":"playback.rs:prefetch_miss","message":"Prefetch miss - decoding on main loop","data":{{"frame_number":{},"prefetch_buffer_size":{}}},"timestamp":{}}}"#, - frame_number, - prefetch_buffer.len(), - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_millis() - ); - } - // #endregion - let Some((segment_time, segment)) = project.get_segment_time(playback_time) - else { - break; - }; - - let Some(segment_media) = - self.segment_medias.get(segment.recording_clip as usize) - else { - frame_number = frame_number.saturating_add(1); - continue; - }; - - let clip_offsets = project - .clips - .iter() - .find(|v| v.index == segment.recording_clip) - .map(|v| v.offsets) - .unwrap_or_default(); + } else { + prefetch_misses += 1; + + if prefetch_buffer.is_empty() && total_frames_rendered < 15 { + let _ = frame_request_tx.send(frame_number); + + // #region agent log + use std::io::Write; + if let Ok(mut file) = + std::fs::OpenOptions::new().create(true).append(true).open( + "/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log", + ) + { + let ts = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() + as u64; + writeln!( + file, + r#"{{"location":"playback.rs:wait_for_prefetch","message":"waiting for prefetch to catch up","data":{{"frame":{},"total_rendered":{}}},"timestamp":{},"sessionId":"debug-session","hypothesisId":"H"}}"#, + frame_number, total_frames_rendered, ts + ) + .ok(); + } + // #endregion - if let Ok(mut guard) = main_in_flight.write() { - guard.insert(frame_number); - } + let wait_result = tokio::time::timeout( + Duration::from_millis(100), + prefetch_rx.recv(), + ) + .await; - let data = tokio::select! { - _ = stop_rx.changed() => { - if let Ok(mut guard) = main_in_flight.write() { - guard.remove(&frame_number); + if let Ok(Some(prefetched)) = wait_result { + if prefetched.frame_number == frame_number { + prefetch_hits += 1; + was_prefetched = true; + Some((prefetched.segment_frames, prefetched.segment_index)) + } else { + prefetch_buffer.push_back(prefetched); + frame_number = frame_number.saturating_add(1); + total_frames_skipped += 1; + continue; + } + } else { + frame_number = frame_number.saturating_add(1); + total_frames_skipped += 1; + continue; } - break 'playback - }, - data = segment_media - .decoders - .get_frames(segment_time as f32, !project.camera.hide, clip_offsets) => { + } else { + let Some((segment_time, segment)) = + project.get_segment_time(playback_time) + else { + break; + }; + + let Some(segment_media) = + self.segment_medias.get(segment.recording_clip as usize) + else { + frame_number = frame_number.saturating_add(1); + continue; + }; + + let clip_offsets = project + .clips + .iter() + .find(|v| v.index == segment.recording_clip) + .map(|v| v.offsets) + .unwrap_or_default(); + if let Ok(mut guard) = main_in_flight.write() { - guard.remove(&frame_number); + guard.insert(frame_number); } - data - }, - }; - data.map(|frames| (frames, segment.recording_clip)) + let max_wait = Duration::from_millis(150); + let data = tokio::select! { + _ = stop_rx.changed() => { + if let Ok(mut guard) = main_in_flight.write() { + guard.remove(&frame_number); + } + break 'playback + }, + _ = tokio::time::sleep(max_wait) => { + if let Ok(mut guard) = main_in_flight.write() { + guard.remove(&frame_number); + } + // #region agent log + use std::io::Write; + if let Ok(mut file) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") + { + let ts = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() as u64; + writeln!( + file, + r#"{{"location":"playback.rs:decoder_timeout","message":"direct decoder call timed out","data":{{"frame":{},"timeout_ms":100}},"timestamp":{},"sessionId":"debug-session","hypothesisId":"M"}}"#, + frame_number, ts + ) + .ok(); + } + // #endregion + frame_number = frame_number.saturating_add(1); + total_frames_skipped += 1; + continue; + }, + data = segment_media + .decoders + .get_frames(segment_time as f32, !project.camera.hide, clip_offsets) => { + if let Ok(mut guard) = main_in_flight.write() { + guard.remove(&frame_number); + } + data + }, + }; + + data.map(|frames| (frames, segment.recording_clip)) + } + } } }; let frame_fetch_time = frame_fetch_start.elapsed(); + // #region agent log + if frame_fetch_time.as_millis() > 200 { + use std::io::Write; + if let Ok(mut file) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") + { + let ts = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() as u64; + writeln!( + file, + r#"{{"location":"playback.rs:long_frame_fetch","message":"frame fetch took >200ms","data":{{"frame":{},"fetch_ms":{},"was_cached":{},"was_prefetched":{},"prefetch_buffer_size":{}}},"timestamp":{},"sessionId":"debug-session","hypothesisId":"N"}}"#, + frame_number, frame_fetch_time.as_millis(), was_cached, was_prefetched, prefetch_buffer.len(), ts + ) + .ok(); + } + } + // #endregion + if let Some((segment_frames, segment_index)) = segment_frames_opt { let Some(segment_media) = self.segment_medias.get(segment_index as usize) else { @@ -583,6 +750,10 @@ impl Playback { continue; }; + if !was_cached { + frame_cache.insert(frame_number, segment_frames.clone(), segment_index); + } + let uniforms_start = Instant::now(); let uniforms = ProjectUniforms::new( &self.render_constants, @@ -595,28 +766,6 @@ impl Playback { ); let uniforms_time = uniforms_start.elapsed(); - // #region agent log - use std::io::Write; - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") - { - let _ = writeln!( - f, - r#"{{"hypothesisId":"B","location":"playback.rs:render_frame_start","message":"About to call render_frame","data":{{"frame_number":{},"prefetch_hit":{},"prefetch_buffer_size":{},"frame_fetch_us":{}}},"timestamp":{}}}"#, - frame_number, - was_prefetched, - prefetch_buffer.len(), - frame_fetch_time.as_micros(), - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_millis() - ); - } - // #endregion - let render_start = Instant::now(); self.renderer .render_frame( @@ -628,25 +777,6 @@ impl Playback { .await; let render_time = render_start.elapsed(); - // #region agent log - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") - { - let _ = writeln!( - f, - r#"{{"hypothesisId":"B","location":"playback.rs:render_frame_done","message":"render_frame returned (channel send complete)","data":{{"frame_number":{},"render_send_time_us":{}}},"timestamp":{}}}"#, - frame_number, - render_time.as_micros(), - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_millis() - ); - } - // #endregion - total_frames_rendered += 1; let render_time_us = render_time.as_micros() as u64; total_render_time_us += render_time_us; @@ -654,13 +784,43 @@ impl Playback { debug!( frame = frame_number, + cache_hit = was_cached, prefetch_hit = was_prefetched, prefetch_buffer_size = prefetch_buffer.len(), + cache_size = frame_cache.len(), frame_fetch_us = frame_fetch_time.as_micros() as u64, uniforms_us = uniforms_time.as_micros() as u64, render_us = render_time_us, "[PERF:PLAYBACK] frame rendered" ); + + // #region agent log + use std::io::Write; + if let Ok(mut file) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") + { + let ts = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() as u64; + writeln!( + file, + r#"{{"location":"playback.rs:frame_rendered","message":"playback frame timing","data":{{"frame":{},"cache_hit":{},"prefetch_hit":{},"prefetch_buffer_size":{},"cache_size":{},"frame_fetch_us":{},"uniforms_us":{},"render_us":{}}},"timestamp":{},"sessionId":"debug-session","hypothesisId":"D"}}"#, + frame_number, + was_cached, + was_prefetched, + prefetch_buffer.len(), + frame_cache.len(), + frame_fetch_time.as_micros() as u64, + uniforms_time.as_micros() as u64, + render_time_us, + ts + ) + .ok(); + } + // #endregion } event_tx.send(PlaybackEvent::Frame(frame_number)).ok(); @@ -673,6 +833,11 @@ impl Playback { if frame_number < expected_frame { let frames_behind = expected_frame - frame_number; + let max_frame_skip = if frames_behind > aggressive_skip_threshold { + frames_behind.min(fps / 2) + } else { + base_max_frame_skip + }; let skipped = if frames_behind <= max_frame_skip { frame_number = expected_frame; frames_behind @@ -683,37 +848,34 @@ impl Playback { total_frames_skipped += skipped as u64; + info!( + frames_behind = frames_behind, + frames_skipped = skipped, + current_frame = frame_number, + total_skipped = total_frames_skipped, + "[PERF:PLAYBACK] skipping frames to catch up" + ); + // #region agent log use std::io::Write; - if let Ok(mut f) = std::fs::OpenOptions::new() + if let Ok(mut file) = std::fs::OpenOptions::new() .create(true) .append(true) .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") { - let _ = writeln!( - f, - r#"{{"hypothesisId":"B","location":"playback.rs:frame_skip","message":"Playback loop skipping frames","data":{{"frames_behind":{},"frames_skipped":{},"new_frame_number":{},"total_skipped":{},"elapsed_since_start_ms":{}}},"timestamp":{}}}"#, - frames_behind, - skipped, - frame_number, - total_frames_skipped, - start.elapsed().as_millis(), - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_millis() - ); + let ts = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() as u64; + writeln!( + file, + r#"{{"location":"playback.rs:frame_skip","message":"frames skipped to catch up","data":{{"frames_behind":{},"frames_skipped":{},"current_frame":{},"total_skipped":{}}},"timestamp":{},"sessionId":"debug-session","hypothesisId":"D"}}"#, + frames_behind, skipped, frame_number, total_frames_skipped, ts + ) + .ok(); } // #endregion - info!( - frames_behind = frames_behind, - frames_skipped = skipped, - current_frame = frame_number, - total_skipped = total_frames_skipped, - "[PERF:PLAYBACK] skipping frames to catch up" - ); - prefetch_buffer.retain(|p| p.frame_number >= frame_number); let _ = frame_request_tx.send(frame_number); let _ = playback_position_tx.send(frame_number); diff --git a/crates/rendering/src/decoder/avassetreader.rs b/crates/rendering/src/decoder/avassetreader.rs index b53a43e3ab..8780cbe946 100644 --- a/crates/rendering/src/decoder/avassetreader.rs +++ b/crates/rendering/src/decoder/avassetreader.rs @@ -11,12 +11,12 @@ use cidre::{ arc::R, cv::{self, pixel_buffer::LockFlags}, }; -use ffmpeg::{Rational, format, frame}; +use ffmpeg::{Rational, format}; use tokio::{runtime::Handle as TokioHandle, sync::oneshot}; -use crate::DecodedFrame; +use crate::{DecodedFrame, PixelFormat}; -use super::frame_converter::{FrameConverter, copy_rgba_plane}; +use super::frame_converter::copy_rgba_plane; use super::{FRAME_CACHE_SIZE, VideoDecoderMessage, pts_to_frame}; #[derive(Clone)] @@ -25,38 +25,47 @@ struct ProcessedFrame { data: Arc>, width: u32, height: u32, + format: PixelFormat, + y_stride: u32, + uv_stride: u32, +} + +impl ProcessedFrame { + fn to_decoded_frame(&self) -> DecodedFrame { + match self.format { + PixelFormat::Rgba => DecodedFrame::new((*self.data).clone(), self.width, self.height), + PixelFormat::Nv12 => DecodedFrame::new_nv12( + (*self.data).clone(), + self.width, + self.height, + self.y_stride, + self.uv_stride, + ), + PixelFormat::Yuv420p => DecodedFrame::new_yuv420p( + (*self.data).clone(), + self.width, + self.height, + self.y_stride, + self.uv_stride, + ), + } + } } #[derive(Clone)] struct CachedFrame(ProcessedFrame); -struct ImageBufProcessor { - converter: FrameConverter, - scratch_frame: frame::Video, - scratch_spec: Option<(format::Pixel, u32, u32)>, -} +struct ImageBufProcessor; impl ImageBufProcessor { fn new() -> Self { - Self { - converter: FrameConverter::new(), - scratch_frame: frame::Video::empty(), - scratch_spec: None, - } + Self } - fn convert(&mut self, image_buf: &mut R) -> Vec { - let format = + fn extract_raw(&self, image_buf: &mut R) -> (Vec, PixelFormat, u32, u32) { + let pixel_format = cap_video_decode::avassetreader::pixel_format_to_pixel(image_buf.pixel_format()); - if matches!(format, format::Pixel::RGBA) { - return self.copy_rgba(image_buf); - } - - let width = image_buf.width() as u32; - let height = image_buf.height() as u32; - self.ensure_scratch(format, width, height); - unsafe { image_buf .lock_base_addr(LockFlags::READ_ONLY) @@ -64,94 +73,139 @@ impl ImageBufProcessor { .unwrap(); } - self.copy_planes(image_buf); - - unsafe { image_buf.unlock_lock_base_addr(LockFlags::READ_ONLY) }; - - self.converter.convert(&mut self.scratch_frame) - } - - fn ensure_scratch(&mut self, format: format::Pixel, width: u32, height: u32) { - let needs_new = - self.scratch_spec - .is_none_or(|(current_format, current_width, current_height)| { - current_format != format || current_width != width || current_height != height - }); - - if needs_new { - self.scratch_frame = frame::Video::new(format, width, height); - self.scratch_spec = Some((format, width, height)); - } - } - - fn copy_rgba(&mut self, image_buf: &mut R) -> Vec { - unsafe { - image_buf - .lock_base_addr(LockFlags::READ_ONLY) - .result() - .unwrap(); - } + let result = match pixel_format { + format::Pixel::RGBA => { + let bytes_per_row = image_buf.plane_bytes_per_row(0); + let width = image_buf.width(); + let height = image_buf.height(); + + let slice = unsafe { + std::slice::from_raw_parts::<'static, _>( + image_buf.plane_base_address(0), + bytes_per_row * height, + ) + }; + + let bytes = copy_rgba_plane(slice, bytes_per_row, width, height); + (bytes, PixelFormat::Rgba, width as u32 * 4, 0) + } + format::Pixel::NV12 => { + let width = image_buf.width(); + let height = image_buf.height(); + let y_stride = image_buf.plane_bytes_per_row(0); + let uv_stride = image_buf.plane_bytes_per_row(1); + let y_height = image_buf.plane_height(0); + let uv_height = image_buf.plane_height(1); + + let y_slice = unsafe { + std::slice::from_raw_parts::<'static, _>( + image_buf.plane_base_address(0), + y_stride * y_height, + ) + }; + + let uv_slice = unsafe { + std::slice::from_raw_parts::<'static, _>( + image_buf.plane_base_address(1), + uv_stride * uv_height, + ) + }; + + let mut data = Vec::with_capacity(width * height + width * height / 2); + for row in 0..y_height { + let start = row * y_stride; + let end = start + width; + data.extend_from_slice(&y_slice[start..end]); + } + for row in 0..uv_height { + let start = row * uv_stride; + let end = start + width; + data.extend_from_slice(&uv_slice[start..end]); + } - let bytes_per_row = image_buf.plane_bytes_per_row(0); - let width = image_buf.width(); - let height = image_buf.height(); + (data, PixelFormat::Nv12, width as u32, width as u32) + } + format::Pixel::YUV420P => { + let width = image_buf.width(); + let height = image_buf.height(); + let y_stride = image_buf.plane_bytes_per_row(0); + let u_stride = image_buf.plane_bytes_per_row(1); + let v_stride = image_buf.plane_bytes_per_row(2); + let y_height = image_buf.plane_height(0); + let uv_height = image_buf.plane_height(1); + + let y_slice = unsafe { + std::slice::from_raw_parts::<'static, _>( + image_buf.plane_base_address(0), + y_stride * y_height, + ) + }; + + let u_slice = unsafe { + std::slice::from_raw_parts::<'static, _>( + image_buf.plane_base_address(1), + u_stride * uv_height, + ) + }; + + let v_slice = unsafe { + std::slice::from_raw_parts::<'static, _>( + image_buf.plane_base_address(2), + v_stride * uv_height, + ) + }; + + let half_width = width / 2; + let mut data = Vec::with_capacity(width * height + half_width * uv_height * 2); + + for row in 0..y_height { + let start = row * y_stride; + let end = start + width; + data.extend_from_slice(&y_slice[start..end]); + } + for row in 0..uv_height { + let start = row * u_stride; + let end = start + half_width; + data.extend_from_slice(&u_slice[start..end]); + } + for row in 0..uv_height { + let start = row * v_stride; + let end = start + half_width; + data.extend_from_slice(&v_slice[start..end]); + } - let slice = unsafe { - std::slice::from_raw_parts::<'static, _>( - image_buf.plane_base_address(0), - bytes_per_row * height, - ) + (data, PixelFormat::Yuv420p, width as u32, half_width as u32) + } + _ => { + let width = image_buf.width(); + let height = image_buf.height(); + let black_frame = vec![0u8; width * height * 4]; + (black_frame, PixelFormat::Rgba, width as u32 * 4, 0) + } }; - let bytes = copy_rgba_plane(slice, bytes_per_row, width, height); - unsafe { image_buf.unlock_lock_base_addr(LockFlags::READ_ONLY) }; - bytes - } - - fn copy_planes(&mut self, image_buf: &mut R) { - match self.scratch_frame.format() { - format::Pixel::NV12 | format::Pixel::YUV420P => { - let scratch = &mut self.scratch_frame; - for plane_i in 0..image_buf.plane_count() { - let bytes_per_row = image_buf.plane_bytes_per_row(plane_i); - let height = image_buf.plane_height(plane_i); - - let ffmpeg_stride = scratch.stride(plane_i); - let row_length = bytes_per_row.min(ffmpeg_stride); - - let slice = unsafe { - std::slice::from_raw_parts::<'static, _>( - image_buf.plane_base_address(plane_i), - bytes_per_row * height, - ) - }; - - for i in 0..height { - scratch.data_mut(plane_i) - [i * ffmpeg_stride..(i * ffmpeg_stride + row_length)] - .copy_from_slice( - &slice[i * bytes_per_row..(i * bytes_per_row + row_length)], - ); - } - } - } - format => todo!("implement {:?}", format), - } + result } } impl CachedFrame { - fn new(processor: &mut ImageBufProcessor, mut image_buf: R, number: u32) -> Self { - let frame_buffer = processor.convert(&mut image_buf); - let data = ProcessedFrame { + fn new(processor: &ImageBufProcessor, mut image_buf: R, number: u32) -> Self { + let width = image_buf.width() as u32; + let height = image_buf.height() as u32; + let (data, format, y_stride, uv_stride) = processor.extract_raw(&mut image_buf); + + let frame = ProcessedFrame { number, - data: Arc::new(frame_buffer), - width: image_buf.width() as u32, - height: image_buf.height() as u32, + data: Arc::new(data), + width, + height, + format, + y_stride, + uv_stride, }; - Self(data) + Self(frame) } fn data(&self) -> &ProcessedFrame { @@ -229,11 +283,34 @@ impl AVAssetReaderDecoder { while let Ok(r) = rx.recv() { match r { - VideoDecoderMessage::GetFrame(requested_time, sender) => { + VideoDecoderMessage::GetFrame(mut requested_time, mut sender) => { + if sender.is_closed() { + continue; + } + let request_start = Instant::now(); _total_requests += 1; let requested_frame = (requested_time * fps as f32).floor() as u32; + const BACKWARD_SEEK_TOLERANCE: u32 = 120; + let cache_frame_min_early = cache.keys().next().copied(); + let cache_frame_max_early = cache.keys().next_back().copied(); + + if let (Some(c_min), Some(_c_max)) = + (cache_frame_min_early, cache_frame_max_early) + { + let is_backward_within_tolerance = requested_frame < c_min + && requested_frame + BACKWARD_SEEK_TOLERANCE >= c_min; + if is_backward_within_tolerance { + if let Some(closest_frame) = cache.get(&c_min) { + let data = closest_frame.data().clone(); + let _ = sender.send(data.to_decoded_frame()); + *last_sent_frame.borrow_mut() = Some(data); + continue; + } + } + } + let mut sender = if let Some(cached) = cache.get(&requested_frame) { _cache_hits += 1; let data = cached.data().clone(); @@ -248,11 +325,7 @@ impl AVAssetReaderDecoder { "[PERF:DECODER] cache hit" ); - let _ = sender.send(DecodedFrame { - data: data.data.clone(), - width: data.width, - height: data.height, - }); + let _ = sender.send(data.to_decoded_frame()); *last_sent_frame.borrow_mut() = Some(data); continue; } else { @@ -271,11 +344,7 @@ impl AVAssetReaderDecoder { "[PERF:DECODER] cache miss - frame decoded" ); *last_sent_frame.borrow_mut() = Some(data.clone()); - let _ = sender.send(DecodedFrame { - data: data.data.clone(), - width: data.width, - height: data.height, - }); + let _ = sender.send(data.to_decoded_frame()); let mut last_log = last_metrics_log_clone.borrow_mut(); if last_log.elapsed().as_secs() >= 2 { @@ -292,10 +361,11 @@ impl AVAssetReaderDecoder { let needs_reset = if let (Some(c_min), Some(c_max)) = (cache_frame_min, cache_frame_max) { - let is_backward_seek_beyond_cache = requested_frame < c_min; + let is_backward_seek_beyond_tolerance = + requested_frame + BACKWARD_SEEK_TOLERANCE < c_min; let is_forward_seek_beyond_cache = requested_frame > c_max + FRAME_CACHE_SIZE as u32 / 4; - is_backward_seek_beyond_cache || is_forward_seek_beyond_cache + is_backward_seek_beyond_tolerance || is_forward_seek_beyond_cache } else { true }; @@ -303,6 +373,7 @@ impl AVAssetReaderDecoder { if needs_reset { let reset_start = Instant::now(); _total_reset_count += 1; + this.reset(requested_time); frames = this.inner.frames(); *last_sent_frame.borrow_mut() = None; @@ -328,6 +399,26 @@ impl AVAssetReaderDecoder { total_resets = _total_reset_count, "[PERF:DECODER] decoder reset/seek" ); + + // #region agent log + use std::io::Write; + if let Ok(mut file) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") + { + let ts = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() as u64; + writeln!( + file, + r#"{{"location":"avassetreader.rs:decoder_reset","message":"decoder performed reset/seek","data":{{"decoder":"{}","requested_frame":{},"reset_time_ms":{},"cleared_cache":{},"retained_cache":{},"total_resets":{}}},"timestamp":{},"sessionId":"debug-session","hypothesisId":"D"}}"#, + name, requested_frame, reset_time.as_millis() as u64, cleared, retained, _total_reset_count, ts + ) + .ok(); + } + // #endregion } last_active_frame = Some(requested_frame); @@ -436,6 +527,9 @@ impl AVAssetReaderDecoder { data: Arc::new(black_frame_data), width: video_width, height: video_height, + format: PixelFormat::Rgba, + y_stride: video_width * 4, + uv_stride: 0, }; (sender)(black_frame); } diff --git a/crates/rendering/src/decoder/ffmpeg.rs b/crates/rendering/src/decoder/ffmpeg.rs index 52cc65af5c..66f8be63ff 100644 --- a/crates/rendering/src/decoder/ffmpeg.rs +++ b/crates/rendering/src/decoder/ffmpeg.rs @@ -9,7 +9,7 @@ use std::{ }; use tokio::sync::oneshot; -use crate::DecodedFrame; +use crate::{DecodedFrame, PixelFormat}; use super::{FRAME_CACHE_SIZE, VideoDecoderMessage, frame_converter::FrameConverter, pts_to_frame}; @@ -19,6 +19,31 @@ struct ProcessedFrame { data: Arc>, width: u32, height: u32, + format: PixelFormat, + y_stride: u32, + uv_stride: u32, +} + +impl ProcessedFrame { + fn to_decoded_frame(&self) -> DecodedFrame { + match self.format { + PixelFormat::Rgba => DecodedFrame::new((*self.data).clone(), self.width, self.height), + PixelFormat::Nv12 => DecodedFrame::new_nv12( + (*self.data).clone(), + self.width, + self.height, + self.y_stride, + self.uv_stride, + ), + PixelFormat::Yuv420p => DecodedFrame::new_yuv420p( + (*self.data).clone(), + self.width, + self.height, + self.y_stride, + self.uv_stride, + ), + } + } } impl CachedFrame { @@ -31,6 +56,9 @@ impl CachedFrame { number: *number, width: frame.width(), height: frame.height(), + format: PixelFormat::Rgba, + y_stride: frame.width() * 4, + uv_stride: 0, }; *self = Self::Processed(data.clone()); @@ -100,6 +128,10 @@ impl FfmpegDecoder { while let Ok(r) = rx.recv() { match r { VideoDecoderMessage::GetFrame(requested_time, sender) => { + if sender.is_closed() { + continue; + } + let requested_frame = (requested_time * fps as f32).floor() as u32; // sender.send(black_frame.clone()).ok(); // continue; @@ -107,22 +139,14 @@ impl FfmpegDecoder { let mut sender = if let Some(cached) = cache.get_mut(&requested_frame) { let data = cached.process(&mut converter); - let _ = sender.send(DecodedFrame { - data: data.data.clone(), - width: data.width, - height: data.height, - }); + let _ = sender.send(data.to_decoded_frame()); *last_sent_frame.borrow_mut() = Some(data); continue; } else { let last_sent_frame = last_sent_frame.clone(); Some(move |data: ProcessedFrame| { *last_sent_frame.borrow_mut() = Some(data.clone()); - let _ = sender.send(DecodedFrame { - data: data.data.clone(), - width: data.width, - height: data.height, - }); + let _ = sender.send(data.to_decoded_frame()); }) }; @@ -257,6 +281,9 @@ impl FfmpegDecoder { data: Arc::new(black_frame_data), width: video_width, height: video_height, + format: PixelFormat::Rgba, + y_stride: video_width * 4, + uv_stride: 0, }; (sender)(black_frame); } diff --git a/crates/rendering/src/decoder/mod.rs b/crates/rendering/src/decoder/mod.rs index 61f48e5968..d1fd6b97c0 100644 --- a/crates/rendering/src/decoder/mod.rs +++ b/crates/rendering/src/decoder/mod.rs @@ -10,10 +10,21 @@ mod avassetreader; mod ffmpeg; mod frame_converter; +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum PixelFormat { + Rgba, + Nv12, + Yuv420p, +} + +#[derive(Clone)] pub struct DecodedFrame { data: Arc>, width: u32, height: u32, + format: PixelFormat, + y_stride: u32, + uv_stride: u32, } impl DecodedFrame { @@ -22,6 +33,37 @@ impl DecodedFrame { data: Arc::new(data), width, height, + format: PixelFormat::Rgba, + y_stride: width * 4, + uv_stride: 0, + } + } + + pub fn new_nv12(data: Vec, width: u32, height: u32, y_stride: u32, uv_stride: u32) -> Self { + Self { + data: Arc::new(data), + width, + height, + format: PixelFormat::Nv12, + y_stride, + uv_stride, + } + } + + pub fn new_yuv420p( + data: Vec, + width: u32, + height: u32, + y_stride: u32, + uv_stride: u32, + ) -> Self { + Self { + data: Arc::new(data), + width, + height, + format: PixelFormat::Yuv420p, + y_stride, + uv_stride, } } @@ -36,6 +78,61 @@ impl DecodedFrame { pub fn height(&self) -> u32 { self.height } + + pub fn format(&self) -> PixelFormat { + self.format + } + + pub fn y_plane(&self) -> Option<&[u8]> { + match self.format { + PixelFormat::Nv12 | PixelFormat::Yuv420p => { + let y_size = (self.y_stride * self.height) as usize; + Some(&self.data[..y_size]) + } + PixelFormat::Rgba => None, + } + } + + pub fn uv_plane(&self) -> Option<&[u8]> { + match self.format { + PixelFormat::Nv12 => { + let y_size = (self.y_stride * self.height) as usize; + Some(&self.data[y_size..]) + } + PixelFormat::Yuv420p => None, + PixelFormat::Rgba => None, + } + } + + pub fn u_plane(&self) -> Option<&[u8]> { + match self.format { + PixelFormat::Yuv420p => { + let y_size = (self.y_stride * self.height) as usize; + let u_size = (self.uv_stride * self.height / 2) as usize; + Some(&self.data[y_size..y_size + u_size]) + } + _ => None, + } + } + + pub fn v_plane(&self) -> Option<&[u8]> { + match self.format { + PixelFormat::Yuv420p => { + let y_size = (self.y_stride * self.height) as usize; + let u_size = (self.uv_stride * self.height / 2) as usize; + Some(&self.data[y_size + u_size..]) + } + _ => None, + } + } + + pub fn y_stride(&self) -> u32 { + self.y_stride + } + + pub fn uv_stride(&self) -> u32 { + self.uv_stride + } } pub enum VideoDecoderMessage { @@ -47,7 +144,7 @@ pub fn pts_to_frame(pts: i64, time_base: Rational, fps: u32) -> u32 { .round() as u32 } -pub const FRAME_CACHE_SIZE: usize = 500; +pub const FRAME_CACHE_SIZE: usize = 750; #[derive(Clone)] pub struct AsyncVideoDecoderHandle { @@ -55,13 +152,54 @@ pub struct AsyncVideoDecoderHandle { offset: f64, } +trait MpscSenderLen { + fn len(&self) -> Option; +} + +impl MpscSenderLen for mpsc::Sender { + fn len(&self) -> Option { + None + } +} + impl AsyncVideoDecoderHandle { pub async fn get_frame(&self, time: f32) -> Option { let (tx, rx) = tokio::sync::oneshot::channel(); + let adjusted_time = self.get_time(time); + self.sender - .send(VideoDecoderMessage::GetFrame(self.get_time(time), tx)) + .send(VideoDecoderMessage::GetFrame(adjusted_time, tx)) .unwrap(); - rx.await.ok() + + let start = std::time::Instant::now(); + let result = rx.await; + let wait_ms = start.elapsed().as_millis(); + + // #region agent log + use std::io::Write; + let success = result.is_ok(); + let was_cancelled = result.is_err(); + if was_cancelled || wait_ms > 50 { + if let Ok(mut file) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") + { + let ts = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() as u64; + writeln!( + file, + r#"{{"location":"decoder/mod.rs:get_frame_exit","message":"get_frame completed","data":{{"time":{},"wait_ms":{},"success":{},"cancelled":{}}},"timestamp":{},"sessionId":"debug-session","hypothesisId":"A"}}"#, + time, wait_ms, success, was_cancelled, ts + ) + .ok(); + } + } + // #endregion + + result.ok() } pub fn get_time(&self, time: f32) -> f32 { diff --git a/crates/rendering/src/frame_pipeline.rs b/crates/rendering/src/frame_pipeline.rs index 1144135011..43d87b8ee0 100644 --- a/crates/rendering/src/frame_pipeline.rs +++ b/crates/rendering/src/frame_pipeline.rs @@ -63,6 +63,30 @@ impl PendingReadback { "[PERF:GPU_BUFFER] pipelined readback wait completed" ); + // #region agent log + use std::io::Write; + if let Ok(mut file) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") + { + let log_entry = serde_json::json!({ + "location": "frame_pipeline.rs:readback_wait", + "message": "GPU readback timing", + "data": { + "poll_us": poll_time.as_micros() as u64, + "data_copy_us": data_copy_time.as_micros() as u64, + "total_pipeline_us": total_time.as_micros() as u64, + "poll_count": poll_count + }, + "timestamp": std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_millis() as u64, + "sessionId": "debug-session", + "hypothesisId": "C" + }); + writeln!(file, "{}", log_entry).ok(); + } + // #endregion + if poll_time.as_millis() > 10 { tracing::warn!( poll_time_ms = poll_time.as_millis() as u64, @@ -394,38 +418,15 @@ pub async fn finish_encoder( &session.textures.1 }; - let submit_start = Instant::now(); session .pipelined_readback .submit_readback(device, queue, texture, uniforms, encoder)?; - let submit_time = submit_start.elapsed(); let result = if let Some(pending) = previous_pending { let wait_start = Instant::now(); let frame = pending.wait(device).await?; let wait_time = wait_start.elapsed(); - // #region agent log - use std::io::Write; - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") - { - let _ = writeln!( - f, - r#"{{"hypothesisId":"GPU_PIPELINE","location":"frame_pipeline.rs:finish_encoder","message":"Pipelined finish (waited for previous)","data":{{"submit_us":{},"wait_us":{},"total_us":{},"has_previous":true}},"timestamp":{}}}"#, - submit_time.as_micros(), - wait_time.as_micros(), - total_start.elapsed().as_micros(), - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_millis() - ); - } - // #endregion - tracing::debug!( wait_us = wait_time.as_micros() as u64, total_us = total_start.elapsed().as_micros() as u64, @@ -456,28 +457,6 @@ pub async fn finish_encoder( )?; let prime_time = prime_start.elapsed(); - // #region agent log - use std::io::Write; - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") - { - let _ = writeln!( - f, - r#"{{"hypothesisId":"GPU_PIPELINE","location":"frame_pipeline.rs:finish_encoder","message":"First frame (primed pipeline)","data":{{"submit_us":{},"wait_us":{},"prime_us":{},"total_us":{},"has_previous":false}},"timestamp":{}}}"#, - submit_time.as_micros(), - wait_time.as_micros(), - prime_time.as_micros(), - total_start.elapsed().as_micros(), - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_millis() - ); - } - // #endregion - tracing::debug!( wait_us = wait_time.as_micros() as u64, prime_us = prime_time.as_micros() as u64, diff --git a/crates/rendering/src/layers/camera.rs b/crates/rendering/src/layers/camera.rs index d4f325e3f4..1a5f4f31e1 100644 --- a/crates/rendering/src/layers/camera.rs +++ b/crates/rendering/src/layers/camera.rs @@ -2,9 +2,34 @@ use cap_project::XY; use wgpu::util::DeviceExt; use crate::{ - CompositeVideoFrameUniforms, DecodedFrame, composite_frame::CompositeVideoFramePipeline, + CompositeVideoFrameUniforms, DecodedFrame, PixelFormat, + composite_frame::CompositeVideoFramePipeline, yuv_converter::YuvToRgbaConverter, }; +fn create_frame_texture_with_storage( + device: &wgpu::Device, + width: u32, + height: u32, +) -> wgpu::Texture { + device.create_texture(&wgpu::TextureDescriptor { + size: wgpu::Extent3d { + width, + height, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8UnormSrgb, + usage: wgpu::TextureUsages::TEXTURE_BINDING + | wgpu::TextureUsages::RENDER_ATTACHMENT + | wgpu::TextureUsages::COPY_DST + | wgpu::TextureUsages::STORAGE_BINDING, + label: Some("Camera Frame texture with storage"), + view_formats: &[wgpu::TextureFormat::Rgba8Unorm], + }) +} + pub struct CameraLayer { frame_textures: [wgpu::Texture; 2], frame_texture_views: [wgpu::TextureView; 2], @@ -13,12 +38,14 @@ pub struct CameraLayer { bind_groups: [Option; 2], pipeline: CompositeVideoFramePipeline, hidden: bool, + last_frame_ptr: usize, + yuv_converter: YuvToRgbaConverter, } impl CameraLayer { pub fn new(device: &wgpu::Device) -> Self { - let frame_texture_0 = CompositeVideoFramePipeline::create_frame_texture(device, 1920, 1080); - let frame_texture_1 = CompositeVideoFramePipeline::create_frame_texture(device, 1920, 1080); + let frame_texture_0 = create_frame_texture_with_storage(device, 1920, 1080); + let frame_texture_1 = create_frame_texture_with_storage(device, 1920, 1080); let frame_texture_view_0 = frame_texture_0.create_view(&Default::default()); let frame_texture_view_1 = frame_texture_1.create_view(&Default::default()); @@ -37,6 +64,8 @@ impl CameraLayer { let bind_group_1 = Some(pipeline.bind_group(device, &uniforms_buffer, &frame_texture_view_1)); + let yuv_converter = YuvToRgbaConverter::new(device); + Self { frame_textures: [frame_texture_0, frame_texture_1], frame_texture_views: [frame_texture_view_0, frame_texture_view_1], @@ -45,6 +74,8 @@ impl CameraLayer { bind_groups: [bind_group_0, bind_group_1], pipeline, hidden: false, + last_frame_ptr: 0, + yuv_converter, } } @@ -60,52 +91,111 @@ impl CameraLayer { return; }; - let next_texture = 1 - self.current_texture; - - if self.frame_textures[next_texture].width() != frame_size.x - || self.frame_textures[next_texture].height() != frame_size.y - { - self.frame_textures[next_texture] = CompositeVideoFramePipeline::create_frame_texture( - device, - frame_size.x, - frame_size.y, - ); - self.frame_texture_views[next_texture] = - self.frame_textures[next_texture].create_view(&Default::default()); - - self.bind_groups[next_texture] = Some(self.pipeline.bind_group( - device, - &self.uniforms_buffer, - &self.frame_texture_views[next_texture], - )); - } - let frame_data = camera_frame.data(); - let src_bytes_per_row = frame_size.x * 4; - - queue.write_texture( - wgpu::TexelCopyTextureInfo { - texture: &self.frame_textures[next_texture], - mip_level: 0, - origin: wgpu::Origin3d::ZERO, - aspect: wgpu::TextureAspect::All, - }, - frame_data, - wgpu::TexelCopyBufferLayout { - offset: 0, - bytes_per_row: Some(src_bytes_per_row), - rows_per_image: Some(frame_size.y), - }, - wgpu::Extent3d { - width: frame_size.x, - height: frame_size.y, - depth_or_array_layers: 1, - }, - ); + let frame_ptr = frame_data.as_ptr() as usize; + let format = camera_frame.format(); + + if frame_ptr != self.last_frame_ptr { + let next_texture = 1 - self.current_texture; + + if self.frame_textures[next_texture].width() != frame_size.x + || self.frame_textures[next_texture].height() != frame_size.y + { + self.frame_textures[next_texture] = + create_frame_texture_with_storage(device, frame_size.x, frame_size.y); + self.frame_texture_views[next_texture] = + self.frame_textures[next_texture].create_view(&Default::default()); + + self.bind_groups[next_texture] = Some(self.pipeline.bind_group( + device, + &self.uniforms_buffer, + &self.frame_texture_views[next_texture], + )); + } + + match format { + PixelFormat::Rgba => { + let src_bytes_per_row = frame_size.x * 4; + + queue.write_texture( + wgpu::TexelCopyTextureInfo { + texture: &self.frame_textures[next_texture], + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + frame_data, + wgpu::TexelCopyBufferLayout { + offset: 0, + bytes_per_row: Some(src_bytes_per_row), + rows_per_image: Some(frame_size.y), + }, + wgpu::Extent3d { + width: frame_size.x, + height: frame_size.y, + depth_or_array_layers: 1, + }, + ); + } + PixelFormat::Nv12 => { + if let (Some(y_data), Some(uv_data)) = + (camera_frame.y_plane(), camera_frame.uv_plane()) + { + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("Camera NV12 Conversion Encoder"), + }); + + self.yuv_converter.convert_nv12_to_texture( + device, + queue, + &mut encoder, + y_data, + uv_data, + frame_size.x, + frame_size.y, + camera_frame.y_stride(), + &self.frame_textures[next_texture], + ); + + queue.submit(std::iter::once(encoder.finish())); + } + } + PixelFormat::Yuv420p => { + if let (Some(y_data), Some(u_data), Some(v_data)) = ( + camera_frame.y_plane(), + camera_frame.u_plane(), + camera_frame.v_plane(), + ) { + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("Camera YUV420P Conversion Encoder"), + }); + + self.yuv_converter.convert_yuv420p_to_texture( + device, + queue, + &mut encoder, + y_data, + u_data, + v_data, + frame_size.x, + frame_size.y, + camera_frame.y_stride(), + camera_frame.uv_stride(), + &self.frame_textures[next_texture], + ); + + queue.submit(std::iter::once(encoder.finish())); + } + } + } + + self.last_frame_ptr = frame_ptr; + self.current_texture = next_texture; + } queue.write_buffer(&self.uniforms_buffer, 0, bytemuck::cast_slice(&[uniforms])); - - self.current_texture = next_texture; } pub fn copy_to_texture(&mut self, _encoder: &mut wgpu::CommandEncoder) {} diff --git a/crates/rendering/src/layers/display.rs b/crates/rendering/src/layers/display.rs index 954f6e541c..0789ef71af 100644 --- a/crates/rendering/src/layers/display.rs +++ b/crates/rendering/src/layers/display.rs @@ -1,8 +1,9 @@ use cap_project::XY; use crate::{ - DecodedSegmentFrames, + DecodedSegmentFrames, PixelFormat, composite_frame::{CompositeVideoFramePipeline, CompositeVideoFrameUniforms}, + yuv_converter::YuvToRgbaConverter, }; pub struct DisplayLayer { @@ -12,12 +13,38 @@ pub struct DisplayLayer { uniforms_buffer: wgpu::Buffer, pipeline: CompositeVideoFramePipeline, bind_groups: [Option; 2], + last_frame_ptr: usize, + yuv_converter: YuvToRgbaConverter, +} + +fn create_frame_texture_with_storage( + device: &wgpu::Device, + width: u32, + height: u32, +) -> wgpu::Texture { + device.create_texture(&wgpu::TextureDescriptor { + size: wgpu::Extent3d { + width, + height, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8UnormSrgb, + usage: wgpu::TextureUsages::TEXTURE_BINDING + | wgpu::TextureUsages::RENDER_ATTACHMENT + | wgpu::TextureUsages::COPY_DST + | wgpu::TextureUsages::STORAGE_BINDING, + label: Some("Frame Composite texture with storage"), + view_formats: &[wgpu::TextureFormat::Rgba8Unorm], + }) } impl DisplayLayer { pub fn new(device: &wgpu::Device) -> Self { - let frame_texture_0 = CompositeVideoFramePipeline::create_frame_texture(device, 1920, 1080); - let frame_texture_1 = CompositeVideoFramePipeline::create_frame_texture(device, 1920, 1080); + let frame_texture_0 = create_frame_texture_with_storage(device, 1920, 1080); + let frame_texture_1 = create_frame_texture_with_storage(device, 1920, 1080); let frame_texture_view_0 = frame_texture_0.create_view(&Default::default()); let frame_texture_view_1 = frame_texture_1.create_view(&Default::default()); @@ -28,6 +55,8 @@ impl DisplayLayer { let bind_group_1 = Some(pipeline.bind_group(device, &uniforms_buffer, &frame_texture_view_1)); + let yuv_converter = YuvToRgbaConverter::new(device); + Self { frame_textures: [frame_texture_0, frame_texture_1], frame_texture_views: [frame_texture_view_0, frame_texture_view_1], @@ -35,6 +64,8 @@ impl DisplayLayer { uniforms_buffer, pipeline, bind_groups: [bind_group_0, bind_group_1], + last_frame_ptr: 0, + yuv_converter, } } @@ -45,53 +76,118 @@ impl DisplayLayer { segment_frames: &DecodedSegmentFrames, frame_size: XY, uniforms: CompositeVideoFrameUniforms, - ) { - let next_texture = 1 - self.current_texture; - - if self.frame_textures[next_texture].width() != frame_size.x - || self.frame_textures[next_texture].height() != frame_size.y - { - self.frame_textures[next_texture] = CompositeVideoFramePipeline::create_frame_texture( - device, - frame_size.x, - frame_size.y, - ); - self.frame_texture_views[next_texture] = - self.frame_textures[next_texture].create_view(&Default::default()); - - self.bind_groups[next_texture] = Some(self.pipeline.bind_group( - device, - &self.uniforms_buffer, - &self.frame_texture_views[next_texture], - )); - } - + ) -> (bool, u32, u32) { let frame_data = segment_frames.screen_frame.data(); - let src_bytes_per_row = frame_size.x * 4; - - queue.write_texture( - wgpu::TexelCopyTextureInfo { - texture: &self.frame_textures[next_texture], - mip_level: 0, - origin: wgpu::Origin3d::ZERO, - aspect: wgpu::TextureAspect::All, - }, - frame_data, - wgpu::TexelCopyBufferLayout { - offset: 0, - bytes_per_row: Some(src_bytes_per_row), - rows_per_image: Some(frame_size.y), - }, - wgpu::Extent3d { - width: frame_size.x, - height: frame_size.y, - depth_or_array_layers: 1, - }, - ); + let frame_ptr = frame_data.as_ptr() as usize; + let actual_width = segment_frames.screen_frame.width(); + let actual_height = segment_frames.screen_frame.height(); + let format = segment_frames.screen_frame.format(); - uniforms.write_to_buffer(queue, &self.uniforms_buffer); + let skipped = frame_ptr == self.last_frame_ptr; + if !skipped { + let next_texture = 1 - self.current_texture; + + if self.frame_textures[next_texture].width() != frame_size.x + || self.frame_textures[next_texture].height() != frame_size.y + { + self.frame_textures[next_texture] = + create_frame_texture_with_storage(device, frame_size.x, frame_size.y); + self.frame_texture_views[next_texture] = + self.frame_textures[next_texture].create_view(&Default::default()); + + self.bind_groups[next_texture] = Some(self.pipeline.bind_group( + device, + &self.uniforms_buffer, + &self.frame_texture_views[next_texture], + )); + } + + match format { + PixelFormat::Rgba => { + let src_bytes_per_row = frame_size.x * 4; - self.current_texture = next_texture; + queue.write_texture( + wgpu::TexelCopyTextureInfo { + texture: &self.frame_textures[next_texture], + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + frame_data, + wgpu::TexelCopyBufferLayout { + offset: 0, + bytes_per_row: Some(src_bytes_per_row), + rows_per_image: Some(frame_size.y), + }, + wgpu::Extent3d { + width: frame_size.x, + height: frame_size.y, + depth_or_array_layers: 1, + }, + ); + } + PixelFormat::Nv12 => { + let screen_frame = &segment_frames.screen_frame; + if let (Some(y_data), Some(uv_data)) = + (screen_frame.y_plane(), screen_frame.uv_plane()) + { + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("NV12 Conversion Encoder"), + }); + + self.yuv_converter.convert_nv12_to_texture( + device, + queue, + &mut encoder, + y_data, + uv_data, + frame_size.x, + frame_size.y, + screen_frame.y_stride(), + &self.frame_textures[next_texture], + ); + + queue.submit(std::iter::once(encoder.finish())); + } + } + PixelFormat::Yuv420p => { + let screen_frame = &segment_frames.screen_frame; + if let (Some(y_data), Some(u_data), Some(v_data)) = ( + screen_frame.y_plane(), + screen_frame.u_plane(), + screen_frame.v_plane(), + ) { + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("YUV420P Conversion Encoder"), + }); + + self.yuv_converter.convert_yuv420p_to_texture( + device, + queue, + &mut encoder, + y_data, + u_data, + v_data, + frame_size.x, + frame_size.y, + screen_frame.y_stride(), + screen_frame.uv_stride(), + &self.frame_textures[next_texture], + ); + + queue.submit(std::iter::once(encoder.finish())); + } + } + } + + self.last_frame_ptr = frame_ptr; + self.current_texture = next_texture; + } + + uniforms.write_to_buffer(queue, &self.uniforms_buffer); + (skipped, actual_width, actual_height) } pub fn copy_to_texture(&mut self, _encoder: &mut wgpu::CommandEncoder) {} diff --git a/crates/rendering/src/lib.rs b/crates/rendering/src/lib.rs index cbae3accae..a7cb0fbdf5 100644 --- a/crates/rendering/src/lib.rs +++ b/crates/rendering/src/lib.rs @@ -32,10 +32,11 @@ mod project_recordings; mod scene; mod spring_mass_damper; mod text; +pub mod yuv_converter; mod zoom; pub use coord::*; -pub use decoder::DecodedFrame; +pub use decoder::{DecodedFrame, PixelFormat}; pub use frame_pipeline::RenderedFrame; pub use project_recordings::{ProjectRecordingsMeta, SegmentRecordings, Video}; @@ -198,9 +199,33 @@ impl RecordingSegmentDecoders { ) ); + let camera_frame = camera.flatten(); + + // #region agent log + if needs_camera && camera_frame.is_none() { + use std::io::Write; + if let Ok(mut file) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") + { + let ts = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() as u64; + writeln!( + file, + r#"{{"location":"lib.rs:get_frames","message":"camera frame missing","data":{{"segment_time":{},"has_camera_decoder":{}}},"timestamp":{},"sessionId":"debug-session","hypothesisId":"O"}}"#, + segment_time, self.camera.is_some(), ts + ) + .ok(); + } + } + // #endregion + Some(DecodedSegmentFrames { screen_frame: screen?, - camera_frame: camera.flatten(), + camera_frame, segment_time, recording_time: segment_time + self.segment_offset as f32, }) @@ -1518,6 +1543,7 @@ impl ProjectUniforms { } } +#[derive(Clone)] pub struct DecodedSegmentFrames { pub screen_frame: DecodedFrame, pub camera_frame: Option, @@ -1553,40 +1579,12 @@ impl<'a> FrameRenderer<'a> { ) }); - // #region agent log - use std::io::Write; - let texture_update_start = std::time::Instant::now(); - // #endregion - session.update_texture_size( &self.constants.device, uniforms.output_size.0, uniforms.output_size.1, ); - // #region agent log - let texture_update_time = texture_update_start.elapsed(); - if texture_update_time.as_micros() > 100 { - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") - { - let _ = writeln!( - f, - r#"{{"hypothesisId":"E","location":"lib.rs:update_texture_size","message":"Texture size update took significant time","data":{{"time_us":{},"width":{},"height":{}}},"timestamp":{}}}"#, - texture_update_time.as_micros(), - uniforms.output_size.0, - uniforms.output_size.1, - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_millis() - ); - } - } - // #endregion - produce_frame( self.constants, segment_frames, @@ -1652,7 +1650,7 @@ impl RendererLayers { let blur_time = blur_start.elapsed(); let display_start = Instant::now(); - self.display.prepare( + let (display_skipped, frame_width, frame_height) = self.display.prepare( &constants.device, &constants.queue, segment_frames, @@ -1720,33 +1718,6 @@ impl RendererLayers { let total_time = prepare_start.elapsed(); - // #region agent log - use std::io::Write; - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") - { - let _ = writeln!( - f, - r#"{{"hypothesisId":"PREPARE_BREAKDOWN","location":"lib.rs:prepare","message":"Layer prepare breakdown","data":{{"bg_us":{},"blur_us":{},"display_us":{},"cursor_us":{},"camera_us":{},"camera_only_us":{},"text_us":{},"captions_us":{},"total_us":{}}},"timestamp":{}}}"#, - bg_time.as_micros(), - blur_time.as_micros(), - display_time.as_micros(), - cursor_time.as_micros(), - camera_time.as_micros(), - camera_only_time.as_micros(), - text_time.as_micros(), - captions_time.as_micros(), - total_time.as_micros(), - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_millis() - ); - } - // #endregion - if total_time.as_millis() > 5 { tracing::debug!( total_us = total_time.as_micros() as u64, @@ -1762,6 +1733,36 @@ impl RendererLayers { ); } + // #region agent log + use std::io::Write; + if let Ok(mut file) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") + { + let ts = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() as u64; + let frame_bytes = frame_width * frame_height * 4; + writeln!( + file, + r#"{{"location":"lib.rs:prepare_breakdown","message":"layer prepare timing breakdown","data":{{"total_us":{},"display_us":{},"display_skipped":{},"frame_width":{},"frame_height":{},"frame_bytes":{},"camera_us":{},"bg_us":{},"cursor_us":{}}},"timestamp":{},"sessionId":"debug-session","hypothesisId":"F"}}"#, + total_time.as_micros() as u64, + display_time.as_micros() as u64, + display_skipped, + frame_width, + frame_height, + frame_bytes, + camera_time.as_micros() as u64, + bg_time.as_micros() as u64, + cursor_time.as_micros() as u64, + ts + ) + .ok(); + } + // #endregion + Ok(()) } @@ -1901,29 +1902,6 @@ async fn produce_frame( let total_time = total_start.elapsed(); - // #region agent log - use std::io::Write; - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") - { - let _ = writeln!( - f, - r#"{{"hypothesisId":"GPU_BREAKDOWN","location":"lib.rs:produce_frame","message":"GPU render breakdown","data":{{"prepare_us":{},"encoder_create_us":{},"render_pass_us":{},"finish_encoder_us":{},"total_us":{}}},"timestamp":{}}}"#, - prepare_time.as_micros(), - encoder_create_time.as_micros(), - render_time.as_micros(), - finish_time.as_micros(), - total_time.as_micros(), - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_millis() - ); - } - // #endregion - tracing::debug!( output_width = uniforms.output_size.0, output_height = uniforms.output_size.1, @@ -1935,6 +1913,33 @@ async fn produce_frame( "[PERF:GPU] produce_frame timing breakdown" ); + // #region agent log + use std::io::Write; + if let Ok(mut file) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open("/Users/macbookuser/Documents/GitHub/cap/.cursor/debug.log") + { + let log_entry = serde_json::json!({ + "location": "lib.rs:produce_frame", + "message": "GPU produce_frame timing", + "data": { + "output_width": uniforms.output_size.0, + "output_height": uniforms.output_size.1, + "prepare_us": prepare_time.as_micros() as u64, + "encoder_create_us": encoder_create_time.as_micros() as u64, + "render_pass_us": render_time.as_micros() as u64, + "finish_encoder_us": finish_time.as_micros() as u64, + "total_us": total_time.as_micros() as u64 + }, + "timestamp": std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_millis() as u64, + "sessionId": "debug-session", + "hypothesisId": "C" + }); + writeln!(file, "{}", log_entry).ok(); + } + // #endregion + result } diff --git a/crates/rendering/src/shaders/nv12_to_rgba.wgsl b/crates/rendering/src/shaders/nv12_to_rgba.wgsl new file mode 100644 index 0000000000..ea08269919 --- /dev/null +++ b/crates/rendering/src/shaders/nv12_to_rgba.wgsl @@ -0,0 +1,34 @@ +@group(0) @binding(0) var y_plane: texture_2d; +@group(0) @binding(1) var uv_plane: texture_2d; +@group(0) @binding(2) var output: texture_storage_2d; + +@compute @workgroup_size(8, 8) +fn main(@builtin(global_invocation_id) global_id: vec3) { + let coords = global_id.xy; + let dims = textureDimensions(output); + + if (coords.x >= dims.x || coords.y >= dims.y) { + return; + } + + let y_raw = textureLoad(y_plane, coords, 0).r; + let uv_coords = coords / 2; + let uv_raw = textureLoad(uv_plane, uv_coords, 0).rg; + + let y = (y_raw - 0.0625) * 1.164; + let u = uv_raw.r - 0.5; + let v = uv_raw.g - 0.5; + + let r = y + 1.596 * v; + let g = y - 0.391 * u - 0.813 * v; + let b = y + 2.018 * u; + + let color = vec4( + clamp(r, 0.0, 1.0), + clamp(g, 0.0, 1.0), + clamp(b, 0.0, 1.0), + 1.0 + ); + + textureStore(output, coords, color); +} diff --git a/crates/rendering/src/yuv_converter.rs b/crates/rendering/src/yuv_converter.rs new file mode 100644 index 0000000000..586a286f0a --- /dev/null +++ b/crates/rendering/src/yuv_converter.rs @@ -0,0 +1,585 @@ +use crate::decoder::PixelFormat; + +pub struct YuvToRgbaConverter { + nv12_pipeline: wgpu::ComputePipeline, + yuv420p_pipeline: wgpu::ComputePipeline, + nv12_bind_group_layout: wgpu::BindGroupLayout, + yuv420p_bind_group_layout: wgpu::BindGroupLayout, + y_texture: Option, + uv_texture: Option, + u_texture: Option, + v_texture: Option, + cached_width: u32, + cached_height: u32, + cached_format: Option, +} + +impl YuvToRgbaConverter { + pub fn new(device: &wgpu::Device) -> Self { + let nv12_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("NV12 to RGBA Converter"), + source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(include_str!( + "shaders/nv12_to_rgba.wgsl" + ))), + }); + + let yuv420p_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("YUV420P to RGBA Converter"), + source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(include_str!( + "shaders/yuv420p_to_rgba.wgsl" + ))), + }); + + let nv12_bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("NV12 Converter Bind Group Layout"), + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Float { filterable: false }, + view_dimension: wgpu::TextureViewDimension::D2, + multisampled: false, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Float { filterable: false }, + view_dimension: wgpu::TextureViewDimension::D2, + multisampled: false, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 2, + visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::StorageTexture { + access: wgpu::StorageTextureAccess::WriteOnly, + format: wgpu::TextureFormat::Rgba8Unorm, + view_dimension: wgpu::TextureViewDimension::D2, + }, + count: None, + }, + ], + }); + + let yuv420p_bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("YUV420P Converter Bind Group Layout"), + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Float { filterable: false }, + view_dimension: wgpu::TextureViewDimension::D2, + multisampled: false, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Float { filterable: false }, + view_dimension: wgpu::TextureViewDimension::D2, + multisampled: false, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 2, + visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Float { filterable: false }, + view_dimension: wgpu::TextureViewDimension::D2, + multisampled: false, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 3, + visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::StorageTexture { + access: wgpu::StorageTextureAccess::WriteOnly, + format: wgpu::TextureFormat::Rgba8Unorm, + view_dimension: wgpu::TextureViewDimension::D2, + }, + count: None, + }, + ], + }); + + let nv12_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("NV12 Converter Pipeline Layout"), + bind_group_layouts: &[&nv12_bind_group_layout], + push_constant_ranges: &[], + }); + + let yuv420p_pipeline_layout = + device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("YUV420P Converter Pipeline Layout"), + bind_group_layouts: &[&yuv420p_bind_group_layout], + push_constant_ranges: &[], + }); + + let nv12_pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor { + label: Some("NV12 Converter Pipeline"), + layout: Some(&nv12_pipeline_layout), + module: &nv12_shader, + entry_point: Some("main"), + compilation_options: Default::default(), + cache: None, + }); + + let yuv420p_pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor { + label: Some("YUV420P Converter Pipeline"), + layout: Some(&yuv420p_pipeline_layout), + module: &yuv420p_shader, + entry_point: Some("main"), + compilation_options: Default::default(), + cache: None, + }); + + Self { + nv12_pipeline, + yuv420p_pipeline, + nv12_bind_group_layout, + yuv420p_bind_group_layout, + y_texture: None, + uv_texture: None, + u_texture: None, + v_texture: None, + cached_width: 0, + cached_height: 0, + cached_format: None, + } + } + + fn ensure_textures( + &mut self, + device: &wgpu::Device, + width: u32, + height: u32, + format: PixelFormat, + ) { + if self.cached_width == width + && self.cached_height == height + && self.cached_format == Some(format) + { + return; + } + + self.y_texture = Some(device.create_texture(&wgpu::TextureDescriptor { + label: Some("Y Plane Texture"), + size: wgpu::Extent3d { + width, + height, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::R8Unorm, + usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, + view_formats: &[], + })); + + match format { + PixelFormat::Nv12 => { + self.uv_texture = Some(device.create_texture(&wgpu::TextureDescriptor { + label: Some("UV Plane Texture"), + size: wgpu::Extent3d { + width: width / 2, + height: height / 2, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rg8Unorm, + usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, + view_formats: &[], + })); + self.u_texture = None; + self.v_texture = None; + } + PixelFormat::Yuv420p => { + self.u_texture = Some(device.create_texture(&wgpu::TextureDescriptor { + label: Some("U Plane Texture"), + size: wgpu::Extent3d { + width: width / 2, + height: height / 2, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::R8Unorm, + usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, + view_formats: &[], + })); + self.v_texture = Some(device.create_texture(&wgpu::TextureDescriptor { + label: Some("V Plane Texture"), + size: wgpu::Extent3d { + width: width / 2, + height: height / 2, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::R8Unorm, + usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, + view_formats: &[], + })); + self.uv_texture = None; + } + PixelFormat::Rgba => {} + } + + self.cached_width = width; + self.cached_height = height; + self.cached_format = Some(format); + } + + pub fn convert_nv12_to_texture( + &mut self, + device: &wgpu::Device, + queue: &wgpu::Queue, + encoder: &mut wgpu::CommandEncoder, + y_data: &[u8], + uv_data: &[u8], + width: u32, + height: u32, + y_stride: u32, + output_texture: &wgpu::Texture, + ) { + self.ensure_textures(device, width, height, PixelFormat::Nv12); + + let y_texture = self.y_texture.as_ref().unwrap(); + let uv_texture = self.uv_texture.as_ref().unwrap(); + + if y_stride == width { + queue.write_texture( + wgpu::TexelCopyTextureInfo { + texture: y_texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + y_data, + wgpu::TexelCopyBufferLayout { + offset: 0, + bytes_per_row: Some(width), + rows_per_image: Some(height), + }, + wgpu::Extent3d { + width, + height, + depth_or_array_layers: 1, + }, + ); + } else { + let mut packed_y = Vec::with_capacity((width * height) as usize); + for row in 0..height as usize { + let start = row * y_stride as usize; + let end = start + width as usize; + if end <= y_data.len() { + packed_y.extend_from_slice(&y_data[start..end]); + } + } + queue.write_texture( + wgpu::TexelCopyTextureInfo { + texture: y_texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + &packed_y, + wgpu::TexelCopyBufferLayout { + offset: 0, + bytes_per_row: Some(width), + rows_per_image: Some(height), + }, + wgpu::Extent3d { + width, + height, + depth_or_array_layers: 1, + }, + ); + } + + queue.write_texture( + wgpu::TexelCopyTextureInfo { + texture: uv_texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + uv_data, + wgpu::TexelCopyBufferLayout { + offset: 0, + bytes_per_row: Some(width), + rows_per_image: Some(height / 2), + }, + wgpu::Extent3d { + width: width / 2, + height: height / 2, + depth_or_array_layers: 1, + }, + ); + + let output_view = output_texture.create_view(&wgpu::TextureViewDescriptor { + format: Some(wgpu::TextureFormat::Rgba8Unorm), + ..Default::default() + }); + + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("NV12 Converter Bind Group"), + layout: &self.nv12_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView( + &y_texture.create_view(&Default::default()), + ), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::TextureView( + &uv_texture.create_view(&Default::default()), + ), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: wgpu::BindingResource::TextureView(&output_view), + }, + ], + }); + + { + let mut compute_pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor { + label: Some("NV12 Conversion Pass"), + ..Default::default() + }); + compute_pass.set_pipeline(&self.nv12_pipeline); + compute_pass.set_bind_group(0, &bind_group, &[]); + compute_pass.dispatch_workgroups(width.div_ceil(8), height.div_ceil(8), 1); + } + } + + pub fn convert_yuv420p_to_texture( + &mut self, + device: &wgpu::Device, + queue: &wgpu::Queue, + encoder: &mut wgpu::CommandEncoder, + y_data: &[u8], + u_data: &[u8], + v_data: &[u8], + width: u32, + height: u32, + y_stride: u32, + uv_stride: u32, + output_texture: &wgpu::Texture, + ) { + self.ensure_textures(device, width, height, PixelFormat::Yuv420p); + + let y_texture = self.y_texture.as_ref().unwrap(); + let u_texture = self.u_texture.as_ref().unwrap(); + let v_texture = self.v_texture.as_ref().unwrap(); + + if y_stride == width { + queue.write_texture( + wgpu::TexelCopyTextureInfo { + texture: y_texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + y_data, + wgpu::TexelCopyBufferLayout { + offset: 0, + bytes_per_row: Some(width), + rows_per_image: Some(height), + }, + wgpu::Extent3d { + width, + height, + depth_or_array_layers: 1, + }, + ); + } else { + let mut packed_y = Vec::with_capacity((width * height) as usize); + for row in 0..height as usize { + let start = row * y_stride as usize; + let end = start + width as usize; + if end <= y_data.len() { + packed_y.extend_from_slice(&y_data[start..end]); + } + } + queue.write_texture( + wgpu::TexelCopyTextureInfo { + texture: y_texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + &packed_y, + wgpu::TexelCopyBufferLayout { + offset: 0, + bytes_per_row: Some(width), + rows_per_image: Some(height), + }, + wgpu::Extent3d { + width, + height, + depth_or_array_layers: 1, + }, + ); + } + + let half_width = width / 2; + let half_height = height / 2; + + if uv_stride == half_width { + queue.write_texture( + wgpu::TexelCopyTextureInfo { + texture: u_texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + u_data, + wgpu::TexelCopyBufferLayout { + offset: 0, + bytes_per_row: Some(half_width), + rows_per_image: Some(half_height), + }, + wgpu::Extent3d { + width: half_width, + height: half_height, + depth_or_array_layers: 1, + }, + ); + queue.write_texture( + wgpu::TexelCopyTextureInfo { + texture: v_texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + v_data, + wgpu::TexelCopyBufferLayout { + offset: 0, + bytes_per_row: Some(half_width), + rows_per_image: Some(half_height), + }, + wgpu::Extent3d { + width: half_width, + height: half_height, + depth_or_array_layers: 1, + }, + ); + } else { + let mut packed_u = Vec::with_capacity((half_width * half_height) as usize); + let mut packed_v = Vec::with_capacity((half_width * half_height) as usize); + for row in 0..half_height as usize { + let start = row * uv_stride as usize; + let end = start + half_width as usize; + if end <= u_data.len() { + packed_u.extend_from_slice(&u_data[start..end]); + } + if end <= v_data.len() { + packed_v.extend_from_slice(&v_data[start..end]); + } + } + queue.write_texture( + wgpu::TexelCopyTextureInfo { + texture: u_texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + &packed_u, + wgpu::TexelCopyBufferLayout { + offset: 0, + bytes_per_row: Some(half_width), + rows_per_image: Some(half_height), + }, + wgpu::Extent3d { + width: half_width, + height: half_height, + depth_or_array_layers: 1, + }, + ); + queue.write_texture( + wgpu::TexelCopyTextureInfo { + texture: v_texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + &packed_v, + wgpu::TexelCopyBufferLayout { + offset: 0, + bytes_per_row: Some(half_width), + rows_per_image: Some(half_height), + }, + wgpu::Extent3d { + width: half_width, + height: half_height, + depth_or_array_layers: 1, + }, + ); + } + + let output_view = output_texture.create_view(&wgpu::TextureViewDescriptor { + format: Some(wgpu::TextureFormat::Rgba8Unorm), + ..Default::default() + }); + + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("YUV420P Converter Bind Group"), + layout: &self.yuv420p_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView( + &y_texture.create_view(&Default::default()), + ), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::TextureView( + &u_texture.create_view(&Default::default()), + ), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: wgpu::BindingResource::TextureView( + &v_texture.create_view(&Default::default()), + ), + }, + wgpu::BindGroupEntry { + binding: 3, + resource: wgpu::BindingResource::TextureView(&output_view), + }, + ], + }); + + { + let mut compute_pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor { + label: Some("YUV420P Conversion Pass"), + ..Default::default() + }); + compute_pass.set_pipeline(&self.yuv420p_pipeline); + compute_pass.set_bind_group(0, &bind_group, &[]); + compute_pass.dispatch_workgroups(width.div_ceil(8), height.div_ceil(8), 1); + } + } +} From 38b171c3ccc3daf73f82e3755871115384ee5a6b Mon Sep 17 00:00:00 2001 From: Richie McIlroy <33632126+richiemcilroy@users.noreply.github.com> Date: Fri, 12 Dec 2025 15:26:30 +0000 Subject: [PATCH 04/31] Add YUV420P to RGBA shader and canvas render metrics --- apps/desktop/src/routes/editor/Player.tsx | 53 +++++++++++++++++-- .../src/shaders/yuv420p_to_rgba.wgsl | 36 +++++++++++++ 2 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 crates/rendering/src/shaders/yuv420p_to_rgba.wgsl diff --git a/apps/desktop/src/routes/editor/Player.tsx b/apps/desktop/src/routes/editor/Player.tsx index d596f06791..2f93d97358 100644 --- a/apps/desktop/src/routes/editor/Player.tsx +++ b/apps/desktop/src/routes/editor/Player.tsx @@ -184,7 +184,6 @@ export function PlayerContent() { await commands.stopPlayback(); setEditorState("playing", false); } else { - // Ensure we seek to the current playback time before starting playback await commands.seekTo(Math.floor(editorState.playbackTime * FPS)); await commands.startPlayback(FPS, previewResolutionBase()); setEditorState("playing", true); @@ -463,12 +462,60 @@ function PreviewCanvas() { createSignal(); const containerBounds = createElementBounds(canvasContainerRef); + // #region agent log + let canvasRenderCount = 0; + let totalCanvasRenderTime = 0; + let maxCanvasRenderTime = 0; + let lastCanvasMetricsLog = performance.now(); + // #endregion + createEffect(() => { const frame = latestFrame(); if (!frame) return; if (!canvasRef) return; - const ctx = canvasRef.getContext("2d"); - ctx?.putImageData(frame.data, 0, 0); + const ctx = canvasRef.getContext("2d", { alpha: false }); + if (!ctx) return; + // #region agent log + const renderStart = performance.now(); + // #endregion + createImageBitmap(frame.data).then((bitmap) => { + ctx.drawImage(bitmap, 0, 0); + bitmap.close(); + // #region agent log + const renderTime = performance.now() - renderStart; + canvasRenderCount++; + totalCanvasRenderTime += renderTime; + maxCanvasRenderTime = Math.max(maxCanvasRenderTime, renderTime); + if ( + performance.now() - lastCanvasMetricsLog >= 2000 && + canvasRenderCount > 0 + ) { + const avgTime = totalCanvasRenderTime / canvasRenderCount; + fetch( + "http://127.0.0.1:7242/ingest/966647b7-72f6-4ab7-b76e-6b773ac020d7", + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + location: "Player.tsx:canvas_render", + message: "canvas render metrics", + data: { + canvasRenderCount, + avgRenderTimeMs: avgTime.toFixed(2), + maxRenderTimeMs: maxCanvasRenderTime.toFixed(2), + frameWidth: frame.width, + frameHeight: frame.data.height, + }, + timestamp: Date.now(), + sessionId: "debug-session", + hypothesisId: "E", + }), + }, + ).catch(() => {}); + lastCanvasMetricsLog = performance.now(); + } + // #endregion + }); }); return ( diff --git a/crates/rendering/src/shaders/yuv420p_to_rgba.wgsl b/crates/rendering/src/shaders/yuv420p_to_rgba.wgsl new file mode 100644 index 0000000000..b4262d31fc --- /dev/null +++ b/crates/rendering/src/shaders/yuv420p_to_rgba.wgsl @@ -0,0 +1,36 @@ +@group(0) @binding(0) var y_plane: texture_2d; +@group(0) @binding(1) var u_plane: texture_2d; +@group(0) @binding(2) var v_plane: texture_2d; +@group(0) @binding(3) var output: texture_storage_2d; + +@compute @workgroup_size(8, 8) +fn main(@builtin(global_invocation_id) global_id: vec3) { + let coords = global_id.xy; + let dims = textureDimensions(output); + + if (coords.x >= dims.x || coords.y >= dims.y) { + return; + } + + let y_raw = textureLoad(y_plane, coords, 0).r; + let uv_coords = coords / 2; + let u_raw = textureLoad(u_plane, uv_coords, 0).r; + let v_raw = textureLoad(v_plane, uv_coords, 0).r; + + let y = (y_raw - 0.0625) * 1.164; + let u = u_raw - 0.5; + let v = v_raw - 0.5; + + let r = y + 1.596 * v; + let g = y - 0.391 * u - 0.813 * v; + let b = y + 2.018 * u; + + let color = vec4( + clamp(r, 0.0, 1.0), + clamp(g, 0.0, 1.0), + clamp(b, 0.0, 1.0), + 1.0 + ); + + textureStore(output, coords, color); +} From 1a2ffd83c6ce3eae733a84fe732afbca3358ff84 Mon Sep 17 00:00:00 2001 From: Richie McIlroy <33632126+richiemcilroy@users.noreply.github.com> Date: Fri, 12 Dec 2025 15:29:51 +0000 Subject: [PATCH 05/31] Refactor YUV conversion pipeline and shaders --- crates/rendering/src/layers/camera.rs | 110 +++++++++++------- crates/rendering/src/layers/display.rs | 110 +++++++++++------- .../rendering/src/shaders/nv12_to_rgba.wgsl | 1 + .../src/shaders/yuv420p_to_rgba.wgsl | 18 ++- crates/rendering/src/yuv_converter.rs | 80 +++++++++---- 5 files changed, 199 insertions(+), 120 deletions(-) diff --git a/crates/rendering/src/layers/camera.rs b/crates/rendering/src/layers/camera.rs index 1a5f4f31e1..b3c9bb16b9 100644 --- a/crates/rendering/src/layers/camera.rs +++ b/crates/rendering/src/layers/camera.rs @@ -6,30 +6,6 @@ use crate::{ composite_frame::CompositeVideoFramePipeline, yuv_converter::YuvToRgbaConverter, }; -fn create_frame_texture_with_storage( - device: &wgpu::Device, - width: u32, - height: u32, -) -> wgpu::Texture { - device.create_texture(&wgpu::TextureDescriptor { - size: wgpu::Extent3d { - width, - height, - depth_or_array_layers: 1, - }, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Rgba8UnormSrgb, - usage: wgpu::TextureUsages::TEXTURE_BINDING - | wgpu::TextureUsages::RENDER_ATTACHMENT - | wgpu::TextureUsages::COPY_DST - | wgpu::TextureUsages::STORAGE_BINDING, - label: Some("Camera Frame texture with storage"), - view_formats: &[wgpu::TextureFormat::Rgba8Unorm], - }) -} - pub struct CameraLayer { frame_textures: [wgpu::Texture; 2], frame_texture_views: [wgpu::TextureView; 2], @@ -44,8 +20,8 @@ pub struct CameraLayer { impl CameraLayer { pub fn new(device: &wgpu::Device) -> Self { - let frame_texture_0 = create_frame_texture_with_storage(device, 1920, 1080); - let frame_texture_1 = create_frame_texture_with_storage(device, 1920, 1080); + let frame_texture_0 = CompositeVideoFramePipeline::create_frame_texture(device, 1920, 1080); + let frame_texture_1 = CompositeVideoFramePipeline::create_frame_texture(device, 1920, 1080); let frame_texture_view_0 = frame_texture_0.create_view(&Default::default()); let frame_texture_view_1 = frame_texture_1.create_view(&Default::default()); @@ -102,7 +78,11 @@ impl CameraLayer { || self.frame_textures[next_texture].height() != frame_size.y { self.frame_textures[next_texture] = - create_frame_texture_with_storage(device, frame_size.x, frame_size.y); + CompositeVideoFramePipeline::create_frame_texture( + device, + frame_size.x, + frame_size.y, + ); self.frame_texture_views[next_texture] = self.frame_textures[next_texture].create_view(&Default::default()); @@ -141,24 +121,44 @@ impl CameraLayer { if let (Some(y_data), Some(uv_data)) = (camera_frame.y_plane(), camera_frame.uv_plane()) { - let mut encoder = - device.create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("Camera NV12 Conversion Encoder"), - }); - - self.yuv_converter.convert_nv12_to_texture( + self.yuv_converter.convert_nv12( device, queue, - &mut encoder, y_data, uv_data, frame_size.x, frame_size.y, camera_frame.y_stride(), - &self.frame_textures[next_texture], ); - queue.submit(std::iter::once(encoder.finish())); + if let Some(output_texture) = self.yuv_converter.output_texture() { + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("Camera YUV Copy Encoder"), + }); + + encoder.copy_texture_to_texture( + wgpu::TexelCopyTextureInfo { + texture: output_texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + wgpu::TexelCopyTextureInfo { + texture: &self.frame_textures[next_texture], + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + wgpu::Extent3d { + width: frame_size.x, + height: frame_size.y, + depth_or_array_layers: 1, + }, + ); + + queue.submit(std::iter::once(encoder.finish())); + } } } PixelFormat::Yuv420p => { @@ -167,15 +167,9 @@ impl CameraLayer { camera_frame.u_plane(), camera_frame.v_plane(), ) { - let mut encoder = - device.create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("Camera YUV420P Conversion Encoder"), - }); - - self.yuv_converter.convert_yuv420p_to_texture( + self.yuv_converter.convert_yuv420p( device, queue, - &mut encoder, y_data, u_data, v_data, @@ -183,10 +177,36 @@ impl CameraLayer { frame_size.y, camera_frame.y_stride(), camera_frame.uv_stride(), - &self.frame_textures[next_texture], ); - queue.submit(std::iter::once(encoder.finish())); + if let Some(output_texture) = self.yuv_converter.output_texture() { + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("Camera YUV Copy Encoder"), + }); + + encoder.copy_texture_to_texture( + wgpu::TexelCopyTextureInfo { + texture: output_texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + wgpu::TexelCopyTextureInfo { + texture: &self.frame_textures[next_texture], + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + wgpu::Extent3d { + width: frame_size.x, + height: frame_size.y, + depth_or_array_layers: 1, + }, + ); + + queue.submit(std::iter::once(encoder.finish())); + } } } } diff --git a/crates/rendering/src/layers/display.rs b/crates/rendering/src/layers/display.rs index 0789ef71af..f80193cabc 100644 --- a/crates/rendering/src/layers/display.rs +++ b/crates/rendering/src/layers/display.rs @@ -17,34 +17,10 @@ pub struct DisplayLayer { yuv_converter: YuvToRgbaConverter, } -fn create_frame_texture_with_storage( - device: &wgpu::Device, - width: u32, - height: u32, -) -> wgpu::Texture { - device.create_texture(&wgpu::TextureDescriptor { - size: wgpu::Extent3d { - width, - height, - depth_or_array_layers: 1, - }, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Rgba8UnormSrgb, - usage: wgpu::TextureUsages::TEXTURE_BINDING - | wgpu::TextureUsages::RENDER_ATTACHMENT - | wgpu::TextureUsages::COPY_DST - | wgpu::TextureUsages::STORAGE_BINDING, - label: Some("Frame Composite texture with storage"), - view_formats: &[wgpu::TextureFormat::Rgba8Unorm], - }) -} - impl DisplayLayer { pub fn new(device: &wgpu::Device) -> Self { - let frame_texture_0 = create_frame_texture_with_storage(device, 1920, 1080); - let frame_texture_1 = create_frame_texture_with_storage(device, 1920, 1080); + let frame_texture_0 = CompositeVideoFramePipeline::create_frame_texture(device, 1920, 1080); + let frame_texture_1 = CompositeVideoFramePipeline::create_frame_texture(device, 1920, 1080); let frame_texture_view_0 = frame_texture_0.create_view(&Default::default()); let frame_texture_view_1 = frame_texture_1.create_view(&Default::default()); @@ -91,7 +67,11 @@ impl DisplayLayer { || self.frame_textures[next_texture].height() != frame_size.y { self.frame_textures[next_texture] = - create_frame_texture_with_storage(device, frame_size.x, frame_size.y); + CompositeVideoFramePipeline::create_frame_texture( + device, + frame_size.x, + frame_size.y, + ); self.frame_texture_views[next_texture] = self.frame_textures[next_texture].create_view(&Default::default()); @@ -131,24 +111,44 @@ impl DisplayLayer { if let (Some(y_data), Some(uv_data)) = (screen_frame.y_plane(), screen_frame.uv_plane()) { - let mut encoder = - device.create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("NV12 Conversion Encoder"), - }); - - self.yuv_converter.convert_nv12_to_texture( + self.yuv_converter.convert_nv12( device, queue, - &mut encoder, y_data, uv_data, frame_size.x, frame_size.y, screen_frame.y_stride(), - &self.frame_textures[next_texture], ); - queue.submit(std::iter::once(encoder.finish())); + if let Some(output_texture) = self.yuv_converter.output_texture() { + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("YUV Copy Encoder"), + }); + + encoder.copy_texture_to_texture( + wgpu::TexelCopyTextureInfo { + texture: output_texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + wgpu::TexelCopyTextureInfo { + texture: &self.frame_textures[next_texture], + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + wgpu::Extent3d { + width: frame_size.x, + height: frame_size.y, + depth_or_array_layers: 1, + }, + ); + + queue.submit(std::iter::once(encoder.finish())); + } } } PixelFormat::Yuv420p => { @@ -158,15 +158,9 @@ impl DisplayLayer { screen_frame.u_plane(), screen_frame.v_plane(), ) { - let mut encoder = - device.create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("YUV420P Conversion Encoder"), - }); - - self.yuv_converter.convert_yuv420p_to_texture( + self.yuv_converter.convert_yuv420p( device, queue, - &mut encoder, y_data, u_data, v_data, @@ -174,10 +168,36 @@ impl DisplayLayer { frame_size.y, screen_frame.y_stride(), screen_frame.uv_stride(), - &self.frame_textures[next_texture], ); - queue.submit(std::iter::once(encoder.finish())); + if let Some(output_texture) = self.yuv_converter.output_texture() { + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("YUV Copy Encoder"), + }); + + encoder.copy_texture_to_texture( + wgpu::TexelCopyTextureInfo { + texture: output_texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + wgpu::TexelCopyTextureInfo { + texture: &self.frame_textures[next_texture], + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + wgpu::Extent3d { + width: frame_size.x, + height: frame_size.y, + depth_or_array_layers: 1, + }, + ); + + queue.submit(std::iter::once(encoder.finish())); + } } } } diff --git a/crates/rendering/src/shaders/nv12_to_rgba.wgsl b/crates/rendering/src/shaders/nv12_to_rgba.wgsl index ea08269919..f816bd02f1 100644 --- a/crates/rendering/src/shaders/nv12_to_rgba.wgsl +++ b/crates/rendering/src/shaders/nv12_to_rgba.wgsl @@ -12,6 +12,7 @@ fn main(@builtin(global_invocation_id) global_id: vec3) { } let y_raw = textureLoad(y_plane, coords, 0).r; + let uv_coords = coords / 2; let uv_raw = textureLoad(uv_plane, uv_coords, 0).rg; diff --git a/crates/rendering/src/shaders/yuv420p_to_rgba.wgsl b/crates/rendering/src/shaders/yuv420p_to_rgba.wgsl index b4262d31fc..ed25f466ed 100644 --- a/crates/rendering/src/shaders/yuv420p_to_rgba.wgsl +++ b/crates/rendering/src/shaders/yuv420p_to_rgba.wgsl @@ -12,18 +12,15 @@ fn main(@builtin(global_invocation_id) global_id: vec3) { return; } - let y_raw = textureLoad(y_plane, coords, 0).r; - let uv_coords = coords / 2; - let u_raw = textureLoad(u_plane, uv_coords, 0).r; - let v_raw = textureLoad(v_plane, uv_coords, 0).r; + let y = textureLoad(y_plane, coords, 0).r; - let y = (y_raw - 0.0625) * 1.164; - let u = u_raw - 0.5; - let v = v_raw - 0.5; + let uv_coords = coords / 2; + let u = textureLoad(u_plane, uv_coords, 0).r - 0.5; + let v = textureLoad(v_plane, uv_coords, 0).r - 0.5; - let r = y + 1.596 * v; - let g = y - 0.391 * u - 0.813 * v; - let b = y + 2.018 * u; + let r = y + 1.402 * v; + let g = y - 0.344 * u - 0.714 * v; + let b = y + 1.772 * u; let color = vec4( clamp(r, 0.0, 1.0), @@ -34,3 +31,4 @@ fn main(@builtin(global_invocation_id) global_id: vec3) { textureStore(output, coords, color); } + diff --git a/crates/rendering/src/yuv_converter.rs b/crates/rendering/src/yuv_converter.rs index 586a286f0a..81f6637e62 100644 --- a/crates/rendering/src/yuv_converter.rs +++ b/crates/rendering/src/yuv_converter.rs @@ -9,6 +9,8 @@ pub struct YuvToRgbaConverter { uv_texture: Option, u_texture: Option, v_texture: Option, + output_texture: Option, + output_view: Option, cached_width: u32, cached_height: u32, cached_format: Option, @@ -154,6 +156,8 @@ impl YuvToRgbaConverter { uv_texture: None, u_texture: None, v_texture: None, + output_texture: None, + output_view: None, cached_width: 0, cached_height: 0, cached_format: None, @@ -242,27 +246,50 @@ impl YuvToRgbaConverter { PixelFormat::Rgba => {} } + self.output_texture = Some(device.create_texture(&wgpu::TextureDescriptor { + label: Some("RGBA Output Texture"), + size: wgpu::Extent3d { + width, + height, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8Unorm, + usage: wgpu::TextureUsages::STORAGE_BINDING + | wgpu::TextureUsages::TEXTURE_BINDING + | wgpu::TextureUsages::COPY_SRC, + view_formats: &[], + })); + + self.output_view = Some( + self.output_texture + .as_ref() + .unwrap() + .create_view(&Default::default()), + ); + self.cached_width = width; self.cached_height = height; self.cached_format = Some(format); } - pub fn convert_nv12_to_texture( + pub fn convert_nv12( &mut self, device: &wgpu::Device, queue: &wgpu::Queue, - encoder: &mut wgpu::CommandEncoder, y_data: &[u8], uv_data: &[u8], width: u32, height: u32, y_stride: u32, - output_texture: &wgpu::Texture, - ) { + ) -> &wgpu::TextureView { self.ensure_textures(device, width, height, PixelFormat::Nv12); let y_texture = self.y_texture.as_ref().unwrap(); let uv_texture = self.uv_texture.as_ref().unwrap(); + let output_texture = self.output_texture.as_ref().unwrap(); if y_stride == width { queue.write_texture( @@ -334,11 +361,6 @@ impl YuvToRgbaConverter { }, ); - let output_view = output_texture.create_view(&wgpu::TextureViewDescriptor { - format: Some(wgpu::TextureFormat::Rgba8Unorm), - ..Default::default() - }); - let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { label: Some("NV12 Converter Bind Group"), layout: &self.nv12_bind_group_layout, @@ -357,11 +379,17 @@ impl YuvToRgbaConverter { }, wgpu::BindGroupEntry { binding: 2, - resource: wgpu::BindingResource::TextureView(&output_view), + resource: wgpu::BindingResource::TextureView( + &output_texture.create_view(&Default::default()), + ), }, ], }); + let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("NV12 Conversion Encoder"), + }); + { let mut compute_pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor { label: Some("NV12 Conversion Pass"), @@ -371,13 +399,16 @@ impl YuvToRgbaConverter { compute_pass.set_bind_group(0, &bind_group, &[]); compute_pass.dispatch_workgroups(width.div_ceil(8), height.div_ceil(8), 1); } + + queue.submit(std::iter::once(encoder.finish())); + + self.output_view.as_ref().unwrap() } - pub fn convert_yuv420p_to_texture( + pub fn convert_yuv420p( &mut self, device: &wgpu::Device, queue: &wgpu::Queue, - encoder: &mut wgpu::CommandEncoder, y_data: &[u8], u_data: &[u8], v_data: &[u8], @@ -385,13 +416,13 @@ impl YuvToRgbaConverter { height: u32, y_stride: u32, uv_stride: u32, - output_texture: &wgpu::Texture, - ) { + ) -> &wgpu::TextureView { self.ensure_textures(device, width, height, PixelFormat::Yuv420p); let y_texture = self.y_texture.as_ref().unwrap(); let u_texture = self.u_texture.as_ref().unwrap(); let v_texture = self.v_texture.as_ref().unwrap(); + let output_texture = self.output_texture.as_ref().unwrap(); if y_stride == width { queue.write_texture( @@ -538,11 +569,6 @@ impl YuvToRgbaConverter { ); } - let output_view = output_texture.create_view(&wgpu::TextureViewDescriptor { - format: Some(wgpu::TextureFormat::Rgba8Unorm), - ..Default::default() - }); - let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { label: Some("YUV420P Converter Bind Group"), layout: &self.yuv420p_bind_group_layout, @@ -567,11 +593,17 @@ impl YuvToRgbaConverter { }, wgpu::BindGroupEntry { binding: 3, - resource: wgpu::BindingResource::TextureView(&output_view), + resource: wgpu::BindingResource::TextureView( + &output_texture.create_view(&Default::default()), + ), }, ], }); + let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("YUV420P Conversion Encoder"), + }); + { let mut compute_pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor { label: Some("YUV420P Conversion Pass"), @@ -581,5 +613,13 @@ impl YuvToRgbaConverter { compute_pass.set_bind_group(0, &bind_group, &[]); compute_pass.dispatch_workgroups(width.div_ceil(8), height.div_ceil(8), 1); } + + queue.submit(std::iter::once(encoder.finish())); + + self.output_view.as_ref().unwrap() + } + + pub fn output_texture(&self) -> Option<&wgpu::Texture> { + self.output_texture.as_ref() } } From 169582bcaaf3d6d41f21535776721d4fde723206 Mon Sep 17 00:00:00 2001 From: Richie McIlroy <33632126+richiemcilroy@users.noreply.github.com> Date: Mon, 15 Dec 2025 11:27:24 +0000 Subject: [PATCH 06/31] wip: Editor playback optimisations --- apps/desktop/core | Bin 57499648 -> 0 bytes apps/desktop/src-tauri/src/editor_window.rs | 36 +- apps/desktop/src-tauri/src/frame_ws.rs | 17 + apps/desktop/src/routes/editor/Player.tsx | 103 +++-- core | Bin 70086656 -> 0 bytes crates/editor/src/playback.rs | 97 +++-- crates/rendering/src/frame_pipeline.rs | 26 +- .../src/shaders/yuv420p_to_rgba.wgsl | 17 +- docs/PLAYBACK_PERFORMANCE_OPTIMIZATIONS.md | 387 ++++++++++++++++++ 9 files changed, 542 insertions(+), 141 deletions(-) delete mode 100644 apps/desktop/core delete mode 100644 core create mode 100644 docs/PLAYBACK_PERFORMANCE_OPTIMIZATIONS.md diff --git a/apps/desktop/core b/apps/desktop/core deleted file mode 100644 index 5a292175000a17900002ce251dec2173e031d31b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57499648 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 z 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

>JKw$Jj)w809Zq6yfiJv1 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 ucEManaU+r4eKk6#dp$> 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 z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+0D%X*z(h5+$HI#q@Q3LD2@oJafB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly 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

6#x@$1$<|614I&FO;8f|5ovzY9(+STXxhy-|Oq zj)N8Ay5=uM?N2>cYxJS|_2$>3{3)pGgX&Ifz7}3d<1f^wX}34hHbEHUz4rPut@jq552^F3hC@ZE zUk@$)s^T17^-J^QryzBC!3rT}EvrAeF8P%9e2wP&y7Z%KJprlnQ|s;Fs~I0i=KWbS zU!(QY1g+zU{;A(-;KlqefcpdaAGHs49IOyeI|LPAq6;Z!TE&W>H9FI@p z_G^F!T9ooi&T8tz2Ir*d$dBflV*VFrtMM0VAKLyx?W3^&QNKZf`nD&af9tPuweqL& zQdfUPb4@Y*(zMQ}_MokwCF0)SS^EYn1hxNpBUTiD4$!h+Yn-F2K8E91QT#cqvRR$hmi zn(~?;jdppd{JNg!QC}hEfB*J8FMwYUrwc({A9O7-|111FFRqZjx}N7zUs;s;NYl!X z>Q`5N(AX@t{`YUsr?ZD@=7-4Q&-hFIeNpsF_^p{A(fWig{b(#G()`{|Q{QAGUGsam zlBmC=)nLzysNU%QK-*u6BOcE42P*`1{nB_O=6{)5`q25>`O!T~a(&J7DauftAJvsu zeh#%Y<&zO8v9cjwAZuAXB|}H;~VwQMXBEaEq$qt z>Z*^1I3}iF+IdZXTn}lq=g$ogPviN66@uD+i&xzg?+U2A^!;1+RsTorfa{2 zM`kHu-NGY_p?bZE{G5XJa#KiH$k>p9BfLgNj0x;*BOLzoqt=i57qR;NH-FJpzjEX! zR$iMr8h;^OwaZKQkfzez!ut(ig`n1t>Q_v^Y_0srN45Qr?5V3i)ph^zZ|iFwa;Cqa z9DdaL*@GABPYbVyNxrlBEuBaAtGebd>~G{m&y$Gv5~0 zW3}RKklusq3swl~@~%UyDC1?W0jp0skM5au%@0l&F~3Z$@}}Y(UE?p^>xs4BID3u% zQU9X-e3kq~!uJ~zOF>=UqTU|`w9w42s^WZI^)VP3i`9qZj;20}Ax(@{yb&pruQ{%* zA2yDNKEJe~LHl}&99{mGhGSy$m$YV@@tnq6?e#F41DKW&@BdEXSW)_e0DeBX$_bDz|NC9U z{E{1J>`DE(uKe6EfKVG&_b+sP3WfKh$J}N0NylArj;{Ua)SvDHsq+IXgoK7n9|Oc_ zexZ)jL5l0zFVzf(_JGv+(fJfQF!q5e~QKaCCI*?)`QUy^=e^YJuZUK|$u z!+}2!CH<)Uy5@hn=HuSu*?gAHC(@p8kgZ~7iO+Y*H^s`E{crE%=*Umk_-0dD<1bVO z?eUH3Bb*-xD+DS71?r2$^!vB+IyBUMzaZw%re!qx(KWU8BO7HqviD=b3PG)(Jz`@1 zoTgP?vPqoQ{w8Wu0sMMGX82L(*A9G9`cp}ProFh~oR|yZ{uhm7V*QnRza;qv`KUS$ zmc5_C^QSlkwSM!!7p1@Q(DHBUA3PR{>qqllvHD2h>FK(ppLTtiAud@Yo*(r+|0O?< z=B)pwtSP9=+X%c^{pK2J{DsP(-Ct4v;8B(x2P*`1e&h>c{gsDGGoPUQN$viM)>C!$ zS7ZyZ_VI81y{`RmbnhdUpM$ZcymU?N{)Ofp%S~jfuL- z`wSTurF}SX{~$e{;z!r~;3tlWwU2PE@tpc&bsQ{v{-&ir$>=&1WN1)DX&;5(FALD& z&vfmgyziWFR5Sk*<{LWRXQ#eRY&QBvGrTH$z6+Hc|g8)=2~qw7-8HNW~d`{`Qmq`AA8zr?K2 z_zSgHZTnIAv-xqbLQvN)jV)sSl389;A5;eI^-h|v*_exuZ?vBH9;BY%tFKeIe+lR3 z({UOTb@ea0?Ducw4PPnle>HI}vGS&A=|g2O)jFTXL{kg#@;1aVG5^ce@;|CGbsVgO z;*wJ0{?{6Z{)_)5Xz5GY|C|45_fO;>+WtcQRpI%wIzJkZ#Qg8y^79xh{`|ud`H9ts zcZL7$e+m3JP8WjO&uDI46#vWB%Ae|6*Ze@2zc{SYv^T2H?2(%LE3%P?rMSO1;Fy@d zWb*nWJCo0=<6znPflSkNu*u z@X;fO1r8qDYVg>Qkzr$43ihkckH#Z0e@WA_ALXs>e>5iM>d=qIKC$(yY_0FTP~W4y zekJ(#YVrPn#+IVYH*Bml{z&7KE`O&+mn)!wd=jd8*pNC^bnQurQ zY34(7$@qolFKT_LykhgeG$W0Fk&Pv$mVf5s6xY>1)BTp1Kdav_Ab+O*L>&h!#68Mt zu17LbnF6g36~&(=TIZ8Lhp!W_UmB~#^vmSwNk3}8>Nr>-o~C0zKh0~4qFhh}NNY2J16Y=_>_FI(l#u#hPr#VHOj`Gr2FV-J; z?ANph@;B}NfZCg`{e-&KC$p_J?Uk;nJ%1q^>9QZyubBPR?^BTdXnv)RgB9YEn98E} zAC5R)6#J!V*`NF+SBL#{jhChyHRBPTA22~P9#Q#q?Vlw3iIq2P6U&dvOMQnr4wl8I zikG(mju)l89$M#~-XMFIiZ2kC04`m!X|6J`WP0CoDs1;S6+4bz& zrMuht-B}gI-@iL^_Q|_arzAh(`TLt^#z}9rp1Z4(lE1%udV%BQ(VNrXr}F(Crw-H~ zli1enVg|S0k8J{?Z|5MMjI3 zxo)x{%#!S~~Be>u;`Ytrm&r4uXP z)<+4~ym!CalH|nn+rD|q7W+@vCMh5^??okIBcl{N=2@0)SyhFw-FJKxPR(z)%=?r$^pvVL{b zqhlub=3FuLJIIc=FgZ8jz`E^*IsEz1hDRr}+bx%`l2vB?U4qHJ%kMv|dXrk4>7TH+ z(T0M$t%KtE_g51Jq;0i6IPtKb2h(S9$*m*0&ri0r;`@yjzASa3uXTK%q%h_Wt6v7Y zzI)-YR^h|qD<8eAntZ3XLXpJEzw)Kmkj6d<>oXcM`>Zf2^*ea!pn*BhnZGUFd}n&A zqgm<7;p~2VdF>-kE4|N{MDg$cEZjU>zOu*O5;-lIzDthZTXnHchno5P`w)vP&!2iY zr{Pp9KX(1aBRx0GP5b;wZqKf_=y*hfA6NJLDf#yz7pnT*x_)Ereyj1UKN#3me)stM zfsc~-^OFS)D_QKASihAE|9;tmaYk$Qj(b!!YB;k;|GYl3&h`HGQ}XwLV;6L6uDaLX zHFX3#-sJs~4@p-lmr$-?{?m8ltyLX+nSO}cz}okmE6WNx_GtYfiN}>w+8^A%KBB2S zkd=S7@2idVrmS{ROlJDUtUcVPX6^XT3jY19nQLEG^z?7}RyK#_KkbTzv_mOhNehwb!A->-CJ{m-h$fRS;-zEl!8ZH^@dCSFzm3Ls*zII{R%eUraGXIKn ze%J9)?CM$asw_U*WUkZ73tc+P`2GF_U&pB%THG+t;qR|R-F9D{^!WFMRKA`SaoZtg z;+vD(Gq$tqkGo>s7f$_F2_CqcxH?!*v z&uib#@cNYCochckhndXjS#SC2$b5dikcKrLY%%aZph#owcc|*qrVefq_dz*PiRP`LBhTMVlPWy z{lllqop)KRR_tLt%k5Uu z_Fy%ui;eReu=BeGTMbb(ifAI+#^N5qKaLdNcu_kXUw3uk`y+1NaZ_n%FipPmi< zku@>vE9<{HkL=g>Sj(IBTo$tOc5eUrf#2yJ{W6ZS{;$&l*;C1d%NLXQ_r(;)rx>}u zy=AN5-}`Jg@@TQ!<2%ku9nAFYuz6a^f-OUvXYlVKw!e}*=h%dBhm0_mzvtT11Jg=b zSt{IEdu|u()a~ktr{hz(KenCH&^vU!QBRo}yZ>!td40yr8pXU*`Fq!`eOJGUSbKA` z+?&;BE0d3LA9ml7$4_S0Z#k}@!n>IkCQ&ok^;%vju{W_q4YMr1{_e2ZHD+?w@Ru2U zJ-OcTVCj*oSB}Z}``^tE)cG{RAbW7mQg*)U+P7J!S1P1Q{n_|xz zWkHMEp*7n#l-*_abTWDK>)_w}UuQgK>qpLOEqWVW|7NFX#>(qtxu;yW5kq8I{QcL) zFBi?4vp(cp$U}C1qXqrk-!3lED~GSYIUZ=|+4}Xvi6IM^K8+p~gm&vWeP*gHyM7~I z=ad?a(#I?K`?C&tlm2!sKCx9k{~mX}ar=4}i@e#ukB?6cESpwJdH2y-$=jR#heNBD zWH^rZTgCFXSIsl1oa^S1)0f$^?t#(iuNw4h9K!w4{%o_V)o)$!&*$%p*RhnZl~_op zUS+MRmSh@YOVd{R4Xf_V}>JZA2kQmbv*ddqKwsi zcKw>ZBfUztm)v#X->b20zsk$r+@-$@pHEbuRs6sQn246xWmLW$MDG6*-W2Jy#Fsg(qWTNqixq* z`1jt5@47XlpV=~h*=<%|h9gaqpWJQ=^l%uaxg>wL*n60(lfcV5u&nX=;s15@j=_Ww0mA(sbjjjEMmt$?tCy})f?C9 ziZjeVKKQ!^b$k@CBtDe&S0BQjj>!Gl**%GWf9$PY%wc6|{|uRu>G$r~q;bW1kIIPW zOn zg~qp>Zy!09e}L(8J}h+T_Xqt0a`^Mor#rJxoEh}+K?cA6^KbdZ-dt?8UzWh?<5`0# zFP~L%@bTmKH&0ShcQ{6bZIo|e`aU@}#?G~Zw?oEGmjCUU=ZfY zwWu{LzZ>&M?90FGo@SNA(%%hBF$=3<`8tPxzx77?gm)+MmsFFvvi|tav2DGS75i3+ z;@>O2?SAfY&eGmLlzhMAtz%t(e(M|K;P*GHznkvg_x>l3pXOm9ZZjmGWO&7 zT`v(c<&;~FM8@a$DW08^(nkFCGN13a-!rqMK^M!7?Ok%&_;JO!#p`DIhmKnP&Gfy} zAhyc-duPXG@$U~`p6{BGx9ItD7yf?fX`lA9Yu>wWrr_T%zxblUHP5BZVl!&7@?UuP zz-MuZ!Fe*?-yaKFk`}r(X?(^WmVZXdn~3nICrqOF_aM(2ySJQv&Gk&yeWuTurJt^h zI@P_ojL#3#jq@yq?z!GnxtG=VnVo}=ZZ`frT(*+UH%_`|#~n3vzLNEZoqwc3{1%ns z*A1CFYdzG^dP^yeJq z9+v;H#KF0Lm%Vd7gMS}3_2HRuQmM^VFA zc@JG)mRj%7#{7JKe|2#F;SEo!Y?H{c*?528#a?&wg-cFk++*cS>9XPF>*#qw@!a3` z&F^He@!=W!9RB{=9{=W38$AzgoyGlS-^1cJUN%XemXXZ#-8(b=^@|Pdwp;P<*YCL; zy|$Y{yqB^iv&U}F6~@~x+>$8x`pC}rlir`*XMZ)GzyH7E{h4m(H$G|}^^w_YTZK{w znpo9pl**s4?kKpwxXIZkPZV+NdfP5f+k5=M3FnLlJbmJ$0i$eguT1UF+WVG=d2iwy zzVY=N%ld=F3L}^PuAW~-v6cDDmZh`qH+g$yj)L#!-&A6Zo6Mkchoqg%9*JxGEW@mS z8Ov6){%MoD)w*|s9)FAC_q!Wkv}|=eb=Q}CzTUMieVONC4@*mzzU=y{)n8N(*UtFq z!uyw1iA~*JE==+DmI9FR<&a4C`xmc>I8vEZ)A>%#U1VqI?#SRGqcg z)wlXx4ta9OC5!LR zoAZ8c^=me>Ev)$Wg64cH@cmdn$2Ma>v*(hH!+54z&Q`)}pt(`O3$pR>v_Wd1nY zeQCK~rKSW%@%5ltcIFi>zhB!s<0$hFJO80&+saE0PvZUajQNjWIex2KS~iUN!}OUQ zeFi@EpPJ9V_x;!VD$jcVFtShm#r$zvRV(>#kKeETrnB}q_1N7HbvpMlisJL>nDm8; zJs)@WQ#@w(`vC=&uG$yWIi1hvBcl^-yX-qRDmr5pv)6VHq$>8fn<0r=$8t(sl zcvKMc*S?ppn%6jP*;|po+E=7q%j4YxERSXJ^@<3e;D_xE54q=0V(lrU%iV6q`DG5s z{Mq>}A2uB9qHr<{G3DzKp1E(j_fC$M@%dEm^ri<#*}v-{>&VjgcJF22RL8E0jK9wn z*2Tj*Wb&pbIcu1G!@g~JaJSJ5cLjg{Ys5^&{X4&sCoB2w*9$7%I5`1_JWQ%;Ph|tkItqnkFsI*9=zs@zpc5& z0_9oeKZBNT95DRo$LJ_NpAJ~M&Mr*({!PvZ<_`f6j~tJx-!I&|i(BHQ@Pd!MByOG?d|>E^=M z$NPlMdg8fY?|udMH@~X))9U(tv9tv9V@JTUiHYwsY%;dc{>MPS+s3V`9)TIzTENKobF)->$~Rj`D91q>`Iet zr~k_6&dS$e>805g+htAUy#IB4UoUk3t6{DEc>Q`tPHg?(yN>lgDsjVH?*^P>jtutO1qUCl1egvl0O^}mfy#_K*{%KR4dq! zdZTKoS~+~Y`+RABLE^ZY2Q&D7z*5F{+U%cZ8kh8*`D-QjoB2~OyqOcfnbk+l^fO+N!mnQ=(y8h}ixG7c{CkVm1=-s++}i6enwcUe#Mqtn2t>cJSlrtxBxBabo(MK1%+74%R8H4wh@u?M^(O z-&rlao4(6(ZDq%It43|J7NGvBURd@%H((|Nc4O>J8iI$KS^@3me%| z-lW@iC4c^AVq7hG^V3^ZT^=+0m5zM(;@pgb3ze_f{Ihh4?v zXudi=iQjLO=~BtM)85=X`8Hs_8>aH_#h13bH*rXc zZ*a~^Hhz>|^EK%Djes7Ci>$vW{yy*Cf#yM7bNKVRVoP7e-B8v__T$grN*+5L_}90d zN=!=~89K6Bqofrtmx;^mm;=G7{QJFMZz{UgZt-;T00V*<+mRcPA`vU^Pd|xJwm=#nYA=)Gix9BR+nq+R(RSKRh^smUl||6?DOz< zUX>CTOa}Pz_wgS5uCieE*_7Z6{=P`o@5lRGD+GIH++hBFFZ4l9^Ay<)C4YW*_tLId z>t~A&X7Kq*)`K}UI?A7#=kxyT&g$%Jv;KbdGCHvF=yor|f?2f!95Pn2@#xC_x@G6j zmCwyNz~-knBNT^Sd=szb@cHJAT3y$7-+ObsUoWQrwRTs3oV)h(RmM6tK3%gouz$cu zN7twbX0I#nrkcsFm)VzZ!rJepvS-Zp--z3hTAkVJikC%n!rNjaWPCmU@{H)(o(qb1 z%(%n)k4rn2w{F|NJ8jwgM~LFbGu{;p-v9N%N!fU&&-opx;|<=Q9-`dD?3ocN37$9U zVT~yMJpF9+q#wNoNkilL|I3|y{$t>x_~9Mo!R-9>O>0(6E$kNqw$dwp`^O$ERII&tQ3WX0H!&$1L|&!eFy?uS~~i{qbWUS!?X&vTcpcQ;|TvpI$eM->5;)X{CVfv(2JAb z9WV$8Nn-7JO`um;_zC6U!j&m+!p7@W&kZh@%C*nR8OQu%rN!|nN8e11mB%puUGaR# z;sq0Co|p0V86Q!nb@}L}8GaL3`WZV$xV}nzT0$Ae+WX8}5!MDdiyuYt`SPs5#uxez zn`@NM*DvS)-s9KMCbo7K_s_)->V^6GSj|)L{%29svlTb=tMk)^@0VHd?#Y|4`;Y99 z@$&o~G2v4}|6!gP3z&X!feAwvHCOgbvLD zZ~Nh^lCK|3tF_L^%id*msy*}9zXD(NP>gwx8>wq}fC z724(+lz^H9x8Aw5|62S0E0>%im= zw=?+vfrWg1y7c+lwl(B@|5fnsMKyM|yZGCRKd%lh-lXoRhvAE4{QJ;B7B*$;P276K zWgwfs1orwl=2|^TW)^=QGO)gVbBmTwjw{x)@(zmb@-Fu9fVQ|mr8FD&(|xvSp>K4Wa#-OY7J`-y}mkBk63TlLdN$)b*UBI>qFUJiTQkA zVvktC; z?8nP)vc1gS9V3!04%Dico5jD+rP%RamF`+_DT&X=JG9$r?Bm>{jEwJZYQJA*@K>%u za0qXop6#BV=-+6`#ln@N;Oi%Cqh(Dl*uPg~wPyb2dh}^R^ru}%KY0|F#mM~oMELRdk=!RB0l zKX(g@uEW~?ej!ot|I=}K_w8t(JZj~xcDFL$$oslg>#E$lYQ43msD(F_H$Z2A@O12$o~0!zg^RI z0T=wwF20q|=SxjO2hW@RLG@8_nfX`a(0WE*t)^BC;r|!lXi;zD>Mco@Nqqh%5A^@E z(Qj$@5N;p)8GTEp-#Qtu;NKgld$e)TRr5C`Wi8nJuz}ZkgLdg*bL08`jJi8Eo-a9k z!ffS0=8yGapOpFdA+U4?PhWROqRYzhhvsGQ=X3V4=K{JuX|gP22}@t6O8>>FHX~MN zaeLIUNUK&Lk7}x1&iW6#;u9=>)oFS%gWnI=irt|&P&&OshCi#XTC2}2>6`4cAeH~0 zPR;0q(-#Lf_{)X=Uw8GufLCwZ> z@eD&YU$fd&X49O#1N&;lii8 zDINUy`jC13N5x9DUO`*_|4%`2l9k`I-P%Vx2JKy+x|7+<zykldh zkbJW|t3j-Nm0fc4(qEsZ*u?Yslu`79y4II>kMZOCuS$D4P5N&2EKkvh`A_Lush+CFd@A41R^mZMl_s8FRPHFFI?&jjsnU&}FMfpKui*|DOPjQ7O56OUC%;|piOGq{k>`2e>;1kza$naC_hffxXJ==3 zXJ==31qTWKT)*oTS9s2#a})T_T{Ey8V)!EHA-M*^y((*8u|1D5ml5#Y>Ar0C(z}Pa z;GcVUT;H$r{8zz=!pidzMqXR*x{8BmjHf-IJ5g4qoXL#ewtS zuZ_ER;F*4{ZWG8u)3uVTep3c}P@{?dtI6w|cgu>u1~VLp{I4eP|N1 zU<31%iv#N}ac9ncwHPj!-QAEh4q@F z16Q6YF#cg8O-~)@vxi?b&d734iR6I4)}ceiXW|Yn=tBkn==wCDN$0X=S8!Jm`PGkm z?4_Hs=a=$Qp}#$lR^z4b?CUI@pETUYB>Jm8{Tx#0+OKa>7ZdWcr(dSMUw4)x2kg7M z4@H#B%GlE^o&I+)3)UBQ<<4c+Q2$)SgZkx2K)2&FjM0IyU&SDFyG$x zYUTrnsaHO;V18euf#0c3^%nf%zb1{Z4c4P8PtMzvRh&JOoJ`2`TGw}D(pLoIZEF8`n4nLr9(T}W zt>+~zKFoLK^jv=WkdEGkwt6D}<^9yFjE+|F7>PvvbMLawdp4@s()ttp;D+6%cmF=S zkOSk-!ViZ(PsmF8#@SA!pObshqSaK#ll7XI?=F5d^1{YHP402QzihD&O{Ka=Jc1^hK42i`l7er4qWD(DN$#eKd%8_s#n zSwPJ1BCZWKj1JuO+y0*q`pzg<$Nj_Jt%+s8d@1?z^ZdaxkSWAaX z!+aQTN1S|NR<99sg8}=={nNG>v_AS?+y?%Mj1Q}?XWv$?=D~cx@3F-QYUdGerSY+h z34PJ0A0v2g@u9n9=wDq|dEEKyO=|-W_A3lKR2p2Jl@eSB`wLyScX5n9sWX?)BP z_tQEZ{80z~T*JJ2&T2{geuxbHoom=birv1`*-ZFzwxsmQ69>fj(>9Gg^IE*r+*7@DH{A z9-CR%ZM+(9Eiv9&Ytd6aDO1&1n~DCzYL2zJ?cGjjn=>JwR;xnJ9Ix3PAy`kyx5X>w zhVmC&H!|FJYcc0#^!2bMqgY@cusFGIZOW@fXK8RgzWKPNtA&%#L^EOjXLkP06(NO* zkBIp{eaK*6p}1O=xI|r1C$A>#9Y))g>wPYNHekIY`fsC+mxIR5YGeP7I11R~4J=JP zc5PC5&j$Of{^AD)Gn&tQebT5c7+7CxVXK)Xh+5g8r&8ZrzgCn@+6ZfPGN;!-RqA_ggBP{vhhR z)8p51r31G1SVz0QL1Pf2wXqhajp;{x#adN<;3){!p-L41%W;iB_fxYP^90`Q;sHe!*cVQTyZ z7Wfmse|cH>xNh%G&TGPd;%j{12&LMsyXrtbzkX}>`#Q6HIveK8U*EqZEuCqq&SMkh zXduW-DXb*~{N+x@yKE-1zry{TZ|~%;`6_txmID3R$E$X>F*Cm(=D_^;qspvV z6^&i}xlsOh6(hAn_AJ@Wfc**YbM#&fFK-YsVE*wgXT*%SE-r3d=s(_$RlPpTA=sV) z_OQ1no~%tZc&N&`O7Qnh*UhBlyL>$=tdGBO(Y_L?c0pMH^S{^cZwJQ=+t!r{`s#Df z(DE@g*^fA2e|GiHmSf3Td8yz$VUZKqff0O5nqI5>= z-ecNCCG_KG#XDcvY?;2VZUZrXJ>IZ+)OXSwBQE&cJy{w0Vef|H_c@?XKen~5`Lf5o z6BX9a@46U1EZB8*bQ}1yHGdn|U)M|bArtOre{iSZrtw*e4Q!aN-rrqTGH=VB@f6s< zeeZoYtzCb3new6kxIb;k%X9P3EorJG>g(P%c2IDn^;|NHpLgForDfIC{9-Sx1N+Hs z+tr5-Pa%!11An2Wm5h*QCTT&^?VHhwus7TsYZ@BSX2M{?eDB;McDEnlt#LeXO|`}6YyaEerZwkQM0zP1$@}Q*ci(Bqu|k3 zvH<+U8l4!o3|pTRFjf%dyJUM%3*(su=qvF<`y? z+?_pDCpX9Q+F(8D+}JKVH)QSnWjw5FBHAneee%4CmzZy)%P$R6V1MZ;r%&B%Gw01< z0{{Lzv3h0H<8y7~g#^7P7Y%8$c>a_lot_5%)yKD~ys9s*@aKX3?%4Zje6tUqZ%D@{ zL;rAe(Uh$Pl_iOcHAMe>uyfx2B`^KQllK$yc;MiM;KR*^!vv!F{Wm^B)MN3AK!QW|LPRu}h zpxQh>tat4T&0Xfd^?NHD?0>tj8gau;-8)7Bd&Hj3y6^dKMtBHdKk)8vCOsa;_irmXu?^HGg=g6J=o zS6Jn)Vb{E(fq&&n+XV}@w7z{sgZ+xj?%bW4y!=NzV+f&Na=UtH&1~{(Yy){)KJ6~) zOx*s_!M=*e1RDy;X-2@MNrYwPuZ3-*&a6;qT9 zM`-ThLU}7J{$AaudFV`O{J(=~V9%K4X;s(aUbBV+`|T&4=tSN0(r7pz_5;n>oi}Ey znNe61>>nxXJpJ#mMcQj=AdhA74S$=dd@>+||Jl?DTEdk22Xo0_Kbf+P^K|mgVcj|4 z&sx^wf%}WGBlq&aKYG%g8J9lIF{mL!|37I0i~ZyN(3c!o-zwc!UcSCh4+9?D2Vas? zqd%bKjk|RDn6N%obnr9Dg&Mt1I$RoNPzm`cu>E$Y)P3n1GWdh!cV10)pXmNeeNn)_ z$!o;w?JG@ZKa|EN4U?M*{flmKub;c1s23%IXwNPiiZvIE|1g#_meAib<27g7cP;C} z1^hb z+wGuJi{Em=p390~GHR)#>1rmNH<0o^(`~)m7hewe<0pR$PCwOr!Ga6pcT#-ugyBp4 zH~vzd0;2y<`1WCpyYc5Ud^jH>)~V@9m!x-{X|TQ-lVj){yVQrl|48(A<5ed4O)lJ4 z!C6DJx2TsMH@1wUR#9QRjt+hJil&@H6VNlelHi;8dS zO0B|+>khztz4P8bbq1~V;B_J7VU&|r`mi$#?8%^ShNw{Q`RK6%1XGCehaJ2_xBK+j zjt%2?$jU*U1_O>-G*V71iuH3#~)Q77_@|A@Ti!T3b*BiObqTd>6D0|)l2 z`gLWEw@UXJBA89!4=nDX=hAP*M?Q?V0iN}fg0%aF)3y=xx_;XlW|$C_&w};W5fg4? z+m4u|!G`wfl5?tHUCO5LCMXZ%#EunQ8=F85m%#6LaHcJF|Fv!GZ$$s?RlND+2gbH) z4uc5y2`zcF^yOfVU>q?&@t#&?wJOY^5C0^QzekV2Szd!29P8jb1w-e<*?Lj;^-+JKHjU{H| zPqL)j$M4`e*q`nYYB*R@#ca)VzY=jzm)f+vJK`t)QZKa8<*4Oa=`v){Zf~Ga93`*0QN_lExJte>(usw z4eLEdT|at+AKPWj$RYUEX98`oWq4C9c^aXgnIC*-`fSd*rY5lOS!_7@aw@BMTGK#+ z-)5fSY|_Ey{%r6kH63fA-gZ8_gaY$jQ;UbCwb@pFoL+>z$#_Kh)i($KRAQ?Uuu0C$ zy2Vqd7u#UJMHkPT9}RcrZxXx}E2R&5Li0WPyK2mq!Kx-5&ho0I;GzHFp}#_)Q?8z83$f&F&WBZ z5U_gu%pG@@6nJuBKa}#c&xUgu<3q`!{VylJUkb6Dq{0UOOVtUQYYuHu-$Vs_g~~yO zvd+;V8a$YPE055%9<_fBK63J3pC<6%5w4)#U^_IVv*3KAr|Xw~^Rw!2O9B1&hd=#W z)r?h~I`9X3R5UdCkWscW5A@w{Mf2m--KJ?#z@GlKwz_yH{a7*+<_n)fy4>%+%}$wr zp6I{6_^0`K?D-bYg7wGGRaWG;i64y_$%MRrI)39_btmo|4(yNlIMwEhbFB3kGMs1d z>g;o^&IZmYeDGI)AF_Mb%gQ5vb6|h(Utjy1Uv&PfH*FL#KDDNfH_6{~s-_9#tz|v4 zr8r~W7(VEuZ;RG2`(GTggb(AxFSpf#a0AJu3&^;=y?qkIwo%9bs{8T^pQd z@vtbMIy%001ry-M^`+fcX@?|nz`paODAi|D%IM2HXup3M@9UX0*yk!`5Me)fG<(+) z3!5qX$#9;~L+4!8=obgH1r|j3gDYd^jaK$hsRMa!_75HKrq`74rZA$v{%gHTBQt(NuT&kyI%aKL_Y)%pCUscXYySukI{?8x^W zKE&Fn3GNTMY<`Qp{bf!d{{YcHHHK_HdiT}&4;*OEm*QS~7n$z&kfx_@2qCW*>c4h* z68kYtI$SV~&^OMdD!po|eZP|e=hK|^->~t~l_*aRoHxL)n%cJ9>{&Du^r17#jN7SK z?)Q_%$Nz)q-%t6kR-fYg_%sLhqyAa+_J#4}@^b0;|5KO({%J?_^ID8_hE~(SKk~?n z#&O@~HGkxTK5;l?$hqbDPsaW(zdG2TQop|P>N&RUFZR>lrEi1z%D$@C*Q-9o?xF+| z?RW3-I;*LdKi}X0{uNhl=Q4Mv8*^cQcU6e$l~1107H#0~vTLe)G5hr2Hf`YFTOD#+ zxqf{73(j7m{;TWzRQ8U`jBNt@$gbOtDNnn8<}%=Xf}N>Bj~DNmf0zgB3)@p$md@|{ z{9GN_Z@1Ua__$IjnaUkS)c-b*!m?w9V@o+)qW%^e8wY&6Ww4?R^l{$V10O29YF_d| zpWQn3OvGuuN7ZCl&#H_(y!F1ts+WA|-xjRTnMZPa)W!h&$3o4}g!g}59L<3#sd+7O?z?l>d*jE?5*ypCf zr=I76zwW{+x^~fq>7_I~BER{^qr0p)^*Wo~3*g8nQ^zdXe3juw!1LB;^`R#xFQ&nI z${$sCZc$D@8pi8QgwIv38=vghmCol9^*v|d43`7pr-#%*doNGDmDBQ;Mq-2hHM?qc z;7Ik;i}~P>%JI;CXWM6!2?PAW%Z*1&TT2~l!PrcUce6s=U%K4ns#Cz9X-3?8zfX?A z$_(%qo{>FKb;A{>t97tnaQeel^o=aA7fp@}5Z-QW9Kwh5FD7|ZRvnnPRkaQFLzd3o z8Wr>S;~@_0zbj5nyVaW5Bb^HRf3e5tIdw&o)G09ED7xLC@qGCQeB|-JJ{(vdD#$)< zU|l`u*uO?bfee`cC;MOW)4xA+DGScqNUDmRGups$IvLKxh^x9#+dW5jbQ_#^l%S9N zqZzdYZQySl6Bp;RtwPuT63+>O~KWEGL71Nz%i7`ZI%1A1L6@pBUMoGtBXLXPRjKuRQa;;T2^| zGT49O97ipoCs@_8VEz(q?(e(mOvnOOEunwMYR>yR$Km8N$_he$qs?DUT^6v^l?&&G zMrV(2?q+{$0T<5qi86oNoORwIMY=vHFy9#W@W74N{%&>B;nFYz{3n>%2S;2OQhbC3 z^NH}Hd&%y#%%dDQ?`Vu>qrughto>Xr(cZ?)etLgd+*v~g=)+;P`H#Y0+^6zkz8n_$ zVxkXqf;J8MyAb_^bv^@qr?$cQKOv5{d-opdSHcAUw^5oeSC&}nlNoT{VX%!`$??C; zqME=zJTNtN;i(aYr@5eS2iArb+s+%lNV+@%un&#Ur$1Ug z0ed4O`|{-@pGSwWVEkc(9Ew>Qrlm< z<*l!BqUM@W6CdLk6J{@O(y?E&HjVg0?^;u8{@;AhJ+qgnCmYto z`ql5>(69fRNnF@pL7lyO_l13f!r0IsSWoqSn~>lULxb}ms5WybqhG%bB!fO>oxQln z(JJ-{GW6Fvv#<7fkj9U!gYzp4{CWGU21f^Rq5tXg!eq>GYqLNBoQG}kReMI@hGW~w zupiN4eOyKD6)P7eod0OHzVDX<7oU6;!1+;T{`tWPm;3oJ;QSlo$m36L6~bO;q-?gp z?s4PyI@o_miF_8bGo^PigGPi?&Ys!fS8^tk4Ev>xu8eIt7SS5QgY|Bskm2F0_y%Sy z7@u^VKb-z{$?td_tX~@XTh#hdrzx?(f7d{>h*xv(L_!;!zoKum&dBiW*I;TWF`v<2 zF@d^p9qDBg%tyNv6@2}6DZ+~j{MU8N5!AK5InxIIJK77^4w!Y$I)f8Vw8t*SPW#yN zeiXC8Ur>9h?w}aY024tu(SK?=-Z^T#`A9wk^gXiUk+Yto2U3`DeuSpx;2ECLefTsu z?}BVYd(nTWn>Q8oH;onZn`%}p>mx01(y-tNAvK31>3*HJF`Pb)=|&hyu4ed2(9mgM}}`>M~2d_Jsy3*C2B zjd*#jj{x@H{%EUR*rywF4YQCK&%RAr+~1zfen5uy_?=yI?|Yp7FwPdDKl^r>soUp# zUOolv2cHG~W}bQ-;=qCV*B9pGbEBuB^;2j+pE&``=BoF(O9B6~&*TJQ=NBy>rOW?2 zm;?KB-*OJe@37O!=fimVj&kJo?cOt!*>HYBD>L5v#WSZL6gUsG#q7{IH_8ec2ll7D z;m_$5(RQ(g3+JW1VKsD*UVNkQclk?)*MWUsKz={@x#`OXP3Z*x1l+XJTu04c{hvUe zc`7(pxUYLlG_5g7t)#|C}Ac~p+-qQkoeaUK!&jK2gK-V#Sn-HA8GyqdT%vklfO@5V0NxAfJWHfjJdKWj33#*SWFWW#qL+WYN^=f|w? z9Bj^n`f0j+vwF?jiz^tgf9MwT*3!jw2O=1-esPmQWiQWfxWR+=dYv=y>bm_VLulZS zb&cv5UfO@rDQ-_fzOT6_xfMAS`Z3{rtShv>Pc4uASiy($TCcEg7zzsJ@ELG^|K-*r z+dpdFV)9)G`WiXMchAijJe3dol`k@&1K1MJ^t*qc;hD`&irj!#}h#6Q_q zTpeg=p;HI?^~u(+p*|9F^C!6m#*ND{$;5??|WXEg6Z(PDxiu~)thx1~N zQeWGt>AipcuaP?t$OZkgp5t`lRbrly3jTz3HE%T;8(w^6!Fu=sJyOJhs_Q0AV1M4v z8b5f|h(7D3v*5%1th+h+mS)wl`Mm7}zjxQQ?Q3C^lsJ9>PrUl@ZO!oeZLpuQc8YP= ze#wsE()ifm-&{={W2ZZfe}lh@(BHPQw;ib5n0%ZJ`oXp-szZ-=+QlFPzAEbV((@+P zW2ta{OI6L(aoQ=IA`1A^Y@;5%7TRS{e=_(d^UM-9osBlU$$|RYqQpD<{`>WX_X<{fa|5~}A>5u%sJu2DZME|tnfnIfwyb+58;IGBiTVb78 z-tFoy=~IaDbeZ0lrv5L-nMsEWU_bp5=4y8bqrhp>;nFY_{DBry!jul1oa@qLPV|Qh z)>Xd=YRjkamk{;MnKH|K`2Ka-Jn%P~r{}+|BvSob9oW<7QV*@&(9QNwzUcfv@+-l% z#a_Lsupgj8pl4h0GU+{kBO!kkWIbp7sW&=vln8##rd;TDNyp8D4Cilg+{pr_hIy1U zz0xor>_Iax7cPqWYv6L`Wnw&-#$3H_$GKhZEI1!}I#Kl`8s{Gw)4RsnLTmm>Dq%o4lux9I*)5~aOwez?&Pb4{IT1TinR1Y z^|+ufvB@89D;-(p$%6TI?z(OCHIwS|1<*bU_>1M5He>}BukSc4<^V4uhv zHF>70NmOkc=)<{9fhiw%Mm{A&`^{#!pGu`%f6Ipb0NHJ~v_HSuvxrkmw6BTWk!6`H zvPY7^-zS}~#tl+VR%xmr>MxUNc0ljbw176S|75Urt@^yO8O;HESvq;p4EIxwCQWdj zUs8>apV`Tw792IgA15_-)>aEn^kW{JC!RWGXOPavgW8;BME# zI#{2LXU_~7TtBOj2m52=+)GbSI&kZk^-5`ZlZGjoQFAV%I3FE7CCvdV1KZ8-Rh6i%Tklr;4c!^I;nwd*!+V5_P$U- z3Y}!!wUj!SsLzlolk5Lnt+a#>^LgL8F*j!zvf>!fUytBks@3=2+e8NcoglOQO3fjE zsPR@2{a0XJ=)o?!y!GaYtZz_@Ap1XV4+v983GT{8F{_fLuFK^YlE*+l%?Y*B; zLYBwDRjO?8zwJ9}z^HCEJ6@APU+qiwcsVQk35D~D=ntuag{tAkk1w-9-|Rae_4>(h`!nM=j$Ny-QNe@r*36n5uXaCswSWiuj8SZw!H3bO0;tem_T~m` zi8<7*jREJOcHs?8EKlE{#D(+vd+{#R&|-ydWY~|@MQ`&Jluq*MG|>`E0{4``=mMZ>UCPXr6jABajC3X%%;uJTIDSG#T_Y74G(+ z)zeOT(SUzS)LM7HANKFsz#mZQL8|FekBVRHgVOZ;r!c=0(Z75vV}E+NcfnZ>?2r30 zKd95XuetV26QcdMRlYX<>#9yY8P2czGL&IszzX_hzE6Yo{7((Fp#xlx-KD^Jz8@R+ zuCj8p*uV$>t9O2R9@uOIsj-D2x`_s)M>Urk^?ZEogK+FBgW zN~b69Bj&gFTbyq@XZ0D%8%^-*UgNbcdwBiK_;4QgotEKuYnE&}Nd|xIJLNSk_f#W) znU9j8|GQ=VntVZ3=LQ4r=eng4xBu?Q`ZG+}zj(8OGj-I0~Bt6Tlks!PA^q``di%7NThGi=6grGkCnO3SDUgY{VlSztfE{5(A3)Li;6`#)K5 z|H-AH<(>)Mjn^{3AM4=!uA3_Ls+}1J3IEXxx0=_l=(akT4f@UbtUJmolnMq1&Wk*^ zwI#m#v(r2V*k_N%eA+!MwjhZD{(vXGTsAbG%lJWt@$C$~(Y;r}x&#jRN1vWQP=9J< z*JdW{4?I=objW-5s_zW&xBIi1Kjx|PgvVUye@<>4Rk2i6ePh!aqJ15Uh_2dTDdE8dGx8~PV*Yc)bnxawPschj;Jo|>l96X5=ip*CjDJTo9Hu_*?mC?Z_Oc`N zjW$z5XsH4?U+Zw$;bskJrLIYt=s)&Z-)XpCb=`~)_nYi3JNi_AV(@M% z=ofqG=f5~%e^9u8VNc~+_p;P|>&SPB@pbo?_g$u(ANq_B`}JzKw!VFT`tE2Z*sp7b zE>Kz4+RK##=XqCGY7}-Fw6#|q_ycbb9(-wbbMay}*eiBA?DD_%^z5EC@OR#RWT-Uwy_8P?0T4c%>b_2cC4((?MB!d%gLh_^OXES@nhhzaXimDLSl zGaoGu=7W7B z-!vlsRbw6%+QW*%Q(2`GpE26NzP@7U6yr{PCo=?K|Kmo?nk{S`c~-E1kk_RLwvPWi z>Uu2?_Qx(wS(~u-Xr=*w6Vd-H&2pX+FroIh`xSYxe!i&uPsV{pOK%3;f3dLakEkuY z@L&9S_~3sypB{NEW!sG3+7o_Pe@yU4nNhj6yFbG*h6nb&+2x}zhZKG5PlfZ#IP`=x zn~{Ibqrv%6<@0wjlOxN#$Y6h%={LuAeAmO9so;M+lazanRJW^~0rssKmD=jA6$Rbd zVE>w4_UGTndc{@Of&IVi%&x9UYFJ_}Y>O{auM4$99mz0tOGx3ukK#efv$b^brI46ZWkGS4Lm| zyr*d>;a^tV@+J7p{kjSP=!-=$$Ac#oWf?PJKE@uZb@=XN-Qg@~Z#mtgPbYoL@55#j z@>tM)b@;_m>)A}0ujg04_q|&G^db-ZDe}I2wd&HXdOHXFIdiw_dwv~zWi}7!$?o0| z@FUPGhGj;`SN502ULRDwVwm7>H&Nqsw{@}4f6?bzAPz<&D%&N=ZrdE=B3^U1jRr$5=QDETOW^LgW~H$48tpO)1I z^Uv6<4KX>F*Dhgz{vS*KzA>TCa$5%YAH_5mJlXufZMbxPO|bu@Z==-$4MR^KD%8jL zh@r|GcC1~;hx@si&!@Pq)ZBH84g48PitViH5zL+WdUGHt1 zwTA=!PejV$hx^y;aw5ZdN8uWF&x-Y9pGwms4c9?=!@9>^&V9Noma~k|ugvM+QZmbg z32mUSgpk6+3UA#Bkxu`g!hFyV0-igo%{;WPg#qWckBCtEyx6Nqk6cgmS0irCeQBJN zHINDCPx+UdPs-`(V#psytdF?TPf^L8S9Gp}{r!G^QDtYg?%%?K^(x;8RPvFH3oy(eQop+CE{*sSn1+*;2D|4iqYMK`}?P0eG# zez*SflPw#joO#Ir|1P`2;PvnR?zNl>_KG2Xep~k*P6+3(g7OtAb9~YrcXI)z&cEk4 z$$rU4?ifPf7^r)D_?2L%D{bH}Vq;ny^GE#(0|wZW22{s=>3gK+hXCep159V-pKjRd z#DMeC`VT!|yV$Z%?>6wyvX3!&yLW7pH5vMY{>>JrR|fV}6@dM&-z}zk_=|Nf*x>(S z>o@#VUjG^AIJb%N+b;K=^hd&xL3MC`qgCOqPf---uY8#AP|wh#N6tABPKNU@ET5ar zDGt4*(FXd1<(V7yCEBBpHGzMxRrlx7vqqOsbVuty_^{q?ZdyNn)!gnc7%;yu)6INRH*Mk(0i1W)n>0M5`W{=K%_I7s z-VNT->$93#$+ZM*5@U3!-b{ImbbFz}`h1Tsi8pBH*BR4b{oHu@tpmL-e`sOBe3at1 zdx_V4%L824A46H5e%(UIl-n=F}rIU*bBA%m@ZM%3YW0KUaB$w&KoP^2lwmX zK0VFC{kDe-4fE^ZJR^;UjdR*KQ*N+x2>xmWXP1#%>hj5OU!ywRgc+Y-F_#bRLDhP* z>637#fC1;#t2zADt)fMH78&NpstsdCvrEH%nLl%2|BZ5r-NOgY+YXSyKQ?CntIRaV zE=f&6#C$-w*2S$pC^X96X`HagGuOvuf=i}9Icl6sjxbv&>^e{>Y+RwXFtm^yo#)$= z?Hi+NTWXw`r);1@nWV`oV%m<(a~|KNTd;jk<yD-(k!Fx=ytA1~7@5qUk`c6I?qzEmqP;*}+luU>s zn3)TOYMRVMOH1eeIhuN@s>5Qf`{JNUjy{q8T}*6pf{mEE3CN_gS{3$M^knBlW zj+2~xEvB`Zcj4%05t#Y{tM+?Y5YWy5X-YNjKFLVGhcwoY6rO-mg!L08CP1Pk-i zHG5DfG@($(v0K6fqhWTIu@i*0nIsqCD4o*D2)8g-C{z);k5w1u843MNh58fR`y~We z#`l=eFF?<*WJr2nW1+KwX2=+Op;;D+?poM$l9LPiD9Ui^?qZS^r0-H3o#{al3iZts zv|MzC4M{N1vvwrK>QaXco0OD2Ksni>wAYxveeGiVI@k>FA`}`4>4_ub?VKn2#SU>T zaWoNn3fX?HX{Ei@Yywk-jCgxhH}_5knq#A*g~D;dfqo`AeoTF}BCj6g)RIBPL2xN-BCf{-hqJCjKm%TauQ=pnepyNpW$ispB0c$C-Av zDanh-3ve??j2h{XXQ-z=AsiJUOenM~ia;Mip;x@Ho4r}MaIny#6XQFa4`!d^bE^g>IcLNiD2exoV+ zUJ;}F+N!$P7WF7H%V%}7GBO)RN{ARcaiqa$Z%efVVThhZU_olWE=k=E@o=Ir8p%^hOGq@irodor_6RDsn%6_4=nq+xwyQ~k4Yx&#NQ1X=h9!_o|tEK5-tT{Tq@ z8Te7NPqgmcZ=8yEPeeg?A(I~WSEt@8iplx5~_O&g{W{iZnjymqYndpW>L}*exZ}MzMW7ff-y3_ zpM$PY=+q-7IV%Y5_Yryz%gpMY(8El_et2%LsF;ar%DqcXMrP%P=XnjD7#|*??_(Yp{(Cc-QL;`j3MVTU({zi+j6xYOEr%gXiclD#EA-bJHNwH&*D}^PaDH{6fkhPip>e8)UT&RAY%Io<`gHA- zsgjWBlO`PCFu>3^c4T1yJ7iFafC2KD0IxQ?-l0n*V8k{)-hzXlg7w|e(Zvh z)Zq@D(&Ey`xJLA)IvM#CkMv{cl!P#yCz%HKADm`qnGuj=Z!pr^Z=C+*QRsrSQC|5a zVV1*{NZOslI(rT7#3~r?Vx~v2j%Aq{PwLB{C-7arZR6UxdoNGjr2gH8^-3_NxU+KtySOI!jP7ZoY7sv^P)#|- zNG*M=i&LV3QyklIydJ%Kdd8SUO)JmQ6Nb3v8$?n_`Rty?1qCX-`stWvM)qUV^;Oj4 z9Y$FO*>xW8lozP1k>9n48B5bGEUTLkwKAbFO&IQM5mQ=dW}+3tOe)YW4b-Q)y11EG z4C)oiWV(#d9gSlJm_&`rW~z4SA@myLRx&s~)F4XN%wR~8kA7gf+MsU!UVVkZ(&N9h z{-l5v98hpT!2tya6dX`+K*0e82NWDoa6rKU1qT!yP;fxO0R;yX98hpT!2tya6dX`+ zK*0e82NWDoa6rKU1qT!yP;fxO0R;yX98hpT!2tya6dX`+;QxOdPy&*S(SLmSmzorb zblAUx>t8#K8D%k_WMOJyb0lKb|>~*VHiIu#J~1@@a8}1{9@p~d5A`e0eY1oZbxBJ zdQpDb=!cHv*9hm!;QVl!j>4k+MEo>DKU_nvbomD#K&Wpie8dBk! z2o8qxr_g8nBgzl|?@0M7tBL(wxO_NGN8$F&APxTRXnrKv&nC*Rqp);-n4j;^E6q5FXo>JMxX6)*&<@UCyx67;cNgHhvP?t7xP+Hesnnh z4add!vta)g&d*Q+MxX643C>%?c-zC_{$Cu9A5nhzpgvjoNtcfV=ab|7aQ`WS@h67M z;QUpL*95&;a`M9m7k4DTU{_+l7tRm!UIe4hc32nox8eNk&?_%LQxw*b{6yuETRymt z80Uw1CNID79n4Qu9(mc~Cp+kM(Z#;gM%_8=DV|XBXCBwZD+!!Ms?-CGP4f2CN+aJtl{B%X{|3>}Ihy8>Y zuReO^m9M;m`Tef`S|swD^)LBRI+`EcUxe!q*Lg6&=(8QJ1{jwQAATpVe7LQ2grC1F zUnQLXi}S;m!N|)GUpymAUK$}i3U03tQn^@OhM?ah!x;!JlaLpDnTw=6A@FB^_7Q!{ z%1<&rmd}_tvHT4}uw=Lof=Th>@FxiTH~IYE7;j@l=U<_`uzboJZ*ko|N3V4I%bG-- zCy(m`>qnCM5{@gW?_zyTG8~U5^7P%?2%#)}w*l@SLP<%M=%vH?n-~s5Z-WF}is2%$ zzPlCStV|d47pK8=a z-X+3m2yQ46hgTt-!82vo-)hv^ZbA4&@`>rkPYUsmBn`vQq%t_KLj;qYRomycST0r} zoXt}CiQ+B$-^y=?=Fj|=O62FJN%qOi7Uf1FVO=8#5hbsDqL?iuVtF5pe$0aN7jSqa z{sZ^rVt!zpBKV)V;eL=f=S%l6shz&HI4*kH#~AcqlWcEq8pp~^PqbqtXJlq4nx?Tb za_!?Yv$GT9b2Acia_loQM`z^aBqlJ@vof=DbDT|G6AQD_Vl!fMS(zEqb zclKme=>Npm1HmM^_v^~6YKius8_5qEt2@pwZMIhIK2USCFKv-Z`mx_<;Ak~8NDL<&}aM8 z*j0SrA->SP7J_yAi|+%$2}JxuSAUS+pjU)X^n2NKLOyU@eBVl=hxon=DuSiU zD@yNz!er5B>M2f-^K8x((}(YzYN+h~v%MhjCw<}QTNb`D33@P(O?gCn#%ZydPMO^68C@N z`VN+$S2AAZI&nGh@1pxaaJ}P4QXJ@9Ot3Zh0pMOR9Hec8J2Va*!jGy=< zv60EhH!q*^yuf2Uq^!K=b2;TlrzKihT`?O@?tA_ZPFPI)l z`Y`^O46%G8@MnLI`wm(7ba#l$i|?M1#D~YQEC@%R?T@HFFnvPwisX+7Me^(;Zf`hG z$@{!;dv4hI&+>}!;rnv`Mz08;==YWgasP~eYnH%|Whr>dKg%mh7vDjA4f}ew%PCG5 zir%uB;{FGLKiko3^xYBq8VZQ}#4x`5=xva~C#qk3HYv>i#2N8s_=)8Q%cH#VYM?M#<&A;+YH_~!l`JpjpS;)w>MesgMLuVFU&5<_JPME`eNDrJ=TR~ zl{bq`#K&zP^H>C<&vv*>PI;YCct^@xCUKv7`}q4mEbsg!;`04BdHWT;((+6S68EP# z&t?gHSU+kQE5?t&pZ$R;`j#axrh5r_#qxsNg9t{Sgg!2Ze}ho?ehJ`{$ZV>651pBY=1=bQ4u7If6Ws7!);syjzigD4 zpJ^!m|8&0Ja#gHv{o8!s8|5XdK1>ft$uqzxIw8-0$gU423jc5FBUl1I&I9-1`1x<= zAJtO$aJ$40?hjtxHH4GNQ0 zAHSRL)91;qkKqVkN9sd*Jj#-yPsBfY{mbw8C$E3T`l&4bRfdV{1LrBpKg>^g{Ifz~ zviR2s<-z5^{fP)hpY3pi9R785JeTfYNcFPo7tdv7(boX+F@1Ra7QyJV9j4EhUB4D6 zyd(A7AYsqJ{A`g!-+$ZQvJS~EFP>}5s^8{=M10&I;_*^IlBF!(#d(P#Ssmh$0l$5qm_l1U@!a&g9r5aDTrCz0&ehHj$7oJig)N zA{c!V@M78HTR#+Di(U~vksczwUO`{o<>$Hq9^Z7cWy>?}dt~)5EjdJdEH5}ON&OGg zo3&DQ|ANPJneu!@%zxbHN{(-MJPFQ~O&^xkj?_o>cyWJ&#}9e+frBKUa!G*mm(~*&{LAl$D#9o&x|x6!ljLDBT+bi0VUxo1P~w52mMVrg;4^48hfM z%8Th~mVvMOIx&C2_;DMrj1u=J3lSW1l;AH8$B&4<{ped3eUka(I%|=@w-v#aa^&Sc z3X?@2U4lM52cSob>AQj8208R$-uzecQXu9pLh-Y_Sk^ltFU>$7<`b4NX?YQ~D;)M# zqCP}?m7f2aHi+f>-{cSTMi&1_H^udf_1ze@xO{kxs0{F<&-O<|$4vyu!Z-hh7~gml zCrbkVWt>J1zE3EuBl2PzE3RKWKFE_76GW&i`b;J0GezmMB=B`ZY3Rpf%QNOpN9tGC zny`OiKB=Ksx_(9cvqV9%=xewo<{$3E>BZvuz;m=pIrO2Re>|f8#SDHmZ|`4{W3%iA z{G(ej%}p%KwM$PB^+O{1)}nN>=yPZi(`Sd`R7${HoY!&L{F{al%fc5dQQmTtT=%vZ zKVA=rk%MnN3hRix1jmczWf4jvPhJkop|4p&esLSGl)$$arD>2uUsp6($trK-HF5c{ ze%1{72KppvqBN!_Wb@A)g}p&>MEyO=y8V$}Z=7F3$O{fPKxxWW%O1~%qOg-Fjx;{f z5^;Pyr|W0J-4i}RGUUt#@E-g*?CAIaih zBly|i^5Z%a!RWIcCasmtKYJA35&lWr|AOW454TNO^u??v!W zzbcbMAGS5fqR&D9pYkVyF@0Db8@7w*5BN8cyx{ui2>%*%#QHAoudC(IhxylvUXeVb z&-RDDSKQvPd`Px8EMqZIV*3eBAfgZ3i)G25?na_MaJ=^Ukc52=)6mQk_csXqiBI1A ztznb6elhJL7^lT>S+nf^r9TSm2><9)#PW;j)14%{{mSdFe&-)6ul-_P%i^Eu1abRq zZ*LOzD{P+vg}QC;5W_}(<*6C#Quu9X|m;I1Pa@QUXeVD=$4)@&>x89 z8~a?!^WWXkL40Kr_|{;2a@OB4?Xv7S(*C-V@nkVl^@60n^pT)KWq55?nGHVQA3fb$TM z4H7U4{waS`kn+$G`bcNQ^^uFx$g{`d`9eqL`!N#umY_89`l}B}pqml>(&aTBE}maR z;6PWx{*24^6s2hZ{20a$t|$BmQP>;wUxZKeyYzgpMWTK`;zspY%wLR0lui=~dq?J5 zq&edHCF2Gn$Nt;{g~^(4RWBBo7mw#H+2Zl6D@qewCcFK*qcB)95Ulz5&SLQI@;}OVCF`eNdJJJ{y#Vepa@; z;PpIN<2n5>vHt|uryhDG=L`A>rcaSA&)9Y*3!iR2Q9g_h=P8K~+yAoU;PXdeU(qX) zXY|?rG(daD_y(ePzLr>D9)jTJ9kTb&;IXPB{a>{Ny;uh&rHI!bF^KcU5B80grC&++ zR}NDNe&KW)=#{5mbw*(wp|29^6XU})$ZH?tP>=vclI9=%saPJ&Q5^a+G3ye8syN2Wu+tieGAkFicWfl{xnXNEzjRkSV#J2(p>R)`3)nL(?6@BzDO4Te(C>; z#N|~&Y2@{P6H%C~`la6$*KZ;sCrbifDoSH|Ubg&Acb1LMbbz?OKZgu#%@Xomh|-kF z!B>vLI#M5HGsXSE43wrCU_^TRgIS87MJP<>_`6?Rf4Kd}6pQ=Ug(!`@@wdWh~b zOZNvccL{rfC|~*}vAv3n@zcfn8>UP2q@#}!68J>v9YVzRB;0--n#J?WGz9;oPa+=1 z8S~cU=Q=xvMbG?K|NI?QY5ODkT?8}GVA|dvLO7mZj#5fX6O~s)pS1me)TYv&FAfhu zZw%0tnc)_nnURw#!k+?oMR3E7Xq&;4;YakZ><+=lpTF$T{D~L;)(rKGRcicHULfRB z1QWj!Hbc?3rl<)xGNyNJF@%Kr|1 zB03cR6&z4-K*0e82NWDoa6rKU1qT!yP;fxO0R;yX98hpT!2tya6dX`+;QxCLu+pb4Qq!dD%yd(8dQN55}KZz2>vCqlP%ubYy z8K0S+9-EPn^E2us`@CFMT8@1?TO?!VOtcxRF!yJQ{{8KdsT=~WK zNRu8AdyC~-P&C-P!3ru6d)J7)WA7TVVL=hgf6ti>Bpa^h^1Szb-~Z?PysMktot>SX znVp%P-NSVk4D=i5;_c_@(%0Q(pqJlZ!El!We%=CSFE?&CZWr6Gf6>mvkv8m z2ggSSGMq;v&&vyMf-q$0YJTxr2W6jEcv4N`aK z#)<-C2a93`i<3m*?YQDt%tph+vEqPmT2L5J1aoQvOMsR%v5{bk14Xf@OSwc_STJOv zi2&r#e6SI<#00QWzYE#JMyo-O9$ep%i2k@U9?X#&86OcKit!zZAV7U^R1DW5TolI@ zqn~WMqQ9KDnviV5MUf$Kp{T;jidh^r$XHPb>Y>@xSImq^iG#ToBta}tbDP*W{}?1; z^gj`k^DsCHBWDvG9~){RM)XEcUn03+{vRWXi;s!q;wGpnRbxGT8FC@9C?mc<&874R zaeZj@u2Y5PP1A%?{h%E!g8jo|MH)bXWBbzk0peMoazUoo$3IpQ8OSYPnz$GV&9G{k zXi|f2o#-!)@%udz`vJ#1p>pAq*U^?|Tq)>5;B6W7kR44xLU z0Kf#HN`pXu(+vR#y_m3~+(3HoLGm9H6BVPuPEp|^8|F6)6IXw6xG0Dl7sXZ6igr<5r+_+GG#3KHY+yD_* z92qN$i9?e|KgETL>QD$ACO%pxQG!^MNGlYiZh{e4A*DfUvRH1kCP@QEO#wHZ)PBEe&W^3RYck2dBX;MKK4P?39P{YXh z@Nk+$G-!}$ddS+60ID#~~xt z!U9cOltiOWQO;jO3M%Q3fZ19s!FRLmppT zK#WI^I})iun*Uss41(cBm4{hyX_6CShaV^9M`{t2T002IA% zr~&l>myq_gyQN?|OvJ1}3XL=)hsNNN+eDz951E(=HqI`?%GsM~CbFRy1KB^$qG12{ z@HlLC;k}4<*3sqyh8sPU4HA6|7=RKZ#$zUD9Y_0t)=`Y39&3VGj(JSPk?YG0Ls7UB zLX7UFwq<+}ZLr=d>qt?wuZtR~jZQ5c84Cf4K*mT^k3~`bjMw7G{cCd-qeZ3Se_*kr z4QQHP^i*x4qoU)ZF$q#NSIjvoF%m5pNnnvpKzv*ricZkmKe7YWnuCD|=4F6?nV*g* zS*|&;HKL|BtzwKd+LEvs6PvC&b;MyK6MOjX)p!J86e=p&n{8<(-{)_hyCw`>Rc5JKNm7*lNzp0fK14^?1 zem{(W-4_ul+6-%8xkW8g%O_CA#%k?H^E2a5maC7rn0OKH%O8O!x}xk-sCJ z#su(j<0@80NQqi^(2?HXVv`EQR3na{mYBZ~(h4+Y*l@{bXVV)4@a zXNwPVf&2O=sfX^Sg*dV^%MgI3f#LoU(W0R8=CG{dNXF@;fmXQA<+Cl9Fa#WfQF9Dh zeLN>-uh$j{4NZzTmJvdoV4Vdmk!l{I=wJv4D;*nA@BUn5iv-gF0EQ!wO@rbfm>3Y` z11Y!Ao@p7-RIUiw%;oxv#E zts#w_;n(D)eeqy!WK*5Q8*g~l+;wMn1TQggiD@^_nsJF@ts|q* z7p5SnguFsDYRoA*)bZ_L8;t@*3^jD9(!QfDHv-Morc>Ei zl!a#mGWJ8mB`}CMl9p_6=lmn%xc-Q#VtMM(G=|ndgIPGh$O)`{k$+%lw-nuz77Jb~ z#ymwG=wdo(2_&dUF%nd)x|k-~dLP%63G?4$Heay?Z=^P$6f{U2N&B~M@hNx-8meK9+$Y|MiQg%=Y<_cp-(EciY z_T%K$y=?D76s5HXs97(yR-ybrg_Ea{?YQVT(~AyqYFH6k4j6r;sfMDlf2rdrNV`S; z+J*t5YecupqO&%P6e6GA9of?mTaOiq#+axVr)=)XZcz_|+?1&3!RUn6KZH5b)Zh=b z;fm+NEFYqURtwrgrqwU$IV09!jsrM_NHo!mFMEj#HLJMisAUf7Y_b}R`>{y+iQuz^jxeY>5Wo&~E z4aDfAkvM}+DZ>o~GBwcB5E(2q4AnFk08vcz%f(PnSACEs3P+K7baaDeTp=^WsvD?f zh~D`@<~3Cj>W&nNZX0o|I|^)xJsBwU7&d`%G2#715_D373UqJ`6)NiIL7(hv^otD@ z2gfmO%ZbA%4sonzQ*(3mF%|ShQ|n>F@X#5bi=7 zEgj*Z>)LXJ?Q@>!}UY zmnf^P1#51HGCj`p#EV1+wh`gWtm(30n8R<{N@WJsT08Z0kQJuxi4H0&(GzX+AtFo# zwcgS==*Kfda!_{SA4JE)75oE;#U^P*t0h1EV#`buTX?GH@6j}iiNQL~WW)GS% z=_6eY5U71%czlp3R@;`*Vt3pfd2Z$7GEA-ZJMlXA&;lGR4oAizI&MTOO-Em8CZQw0 znp#M0lwklU0B&Prqk)|^%Oi2oq#>hB%8^pbl}6422K%3~6N;@vx>W@Xh15AH2KuB( zO&2YOqGL$fWW-FR&9iiD^lz~`bDlu})FelR&=G?on-D1!ge1wMnw|eP76kIYAW#{7 z)F;4*dC>W)MI2 zpYoT7Su}Z|3FAKeDriDC)d-wuozKyA=#P5&HBdo(qA9Xvah1Q_ol=|3T0kj{P!xqRAqWaJ(}9(Rrf>I za9JBLSId){)EuC7sHkCt(}oicEtb=SkqPuO*;&oCYQ&j?2S$}5FxclQ!<$Uhp;Mfg zwq<@Tg|n7$A^^j+Xw#`+Jn<+w;(|l+ICqFu1R8407@#dPK-fU z6VP;F*6O*LXpo7bP%Z+lW`c*&DEbx|r!^8}WndDDBT=$!ALOc_*k}yW#nvcG(7KZ* zNW600QqVsvwEE%<5og_e0}PzawPvKcLIt1|Q(-(&`ph&A1ywXQLYgg@bovrIDC(X>z~6p_ILwbS;x)ewSFunxQR7G-G!=jJtazOwwb&QuTsRau>Ce5Kyh6WR3?qwM!{@)*miWL8$)LF zFEzo-_I~K1X)T~%EKqVYlg>%nRT{ZJc&u`d)|2s$x@psqV!+@%(Tb?k5>G9=l*c6x zgh`e_nt3qf)@rkD#n~``>|Y$W{fn?3q&bj7UWVg z)YCzL{Goh<#z_D3WI(zL5`-WpTU$q?QIWZWZthuF__yN*FdM!ie{o?ClbdU$3Pj9yY|Bh~qiV>rb zJ94YLp*vZCT>v`z^a5NchdJ$*_|CQ?R_Ofbml{LbVx z5WoJ@N-V}d(V5O{A)?Tel5{@Vc!mlQPS~a3@Dhy8O`m*jWOhlUK zOz)Rg8eO%iOmC4)8U-4)9K3cL+W#m+&Iu!@UIlIA6({SIO+noX(S$&Y;R|9SOb7~( zM$ZM^O{6aj(D#5)k!2*NjqW&T`lMiKC`65fD>(h|1a|~w{L&{jWXKz-HkzFsE1V3X zGDAs%10j2lm^~~?Ma?ywZ$>z@w_Pp8r>XalcU6%!2CNYnM;{-c6FNM*K9HcTBZ;I( z2LkQ{*N(MDLp4rli$fR0z~4h7*f6FtLc)pL(N@Fl10YO_f;uaMO$C?d8sN@oGy$5* zfcQXh5SoO43>p}#bNULKwY^OzNMK+RF*-mN29wbMKiHXYwRQ;xtVTXU*)Wt-duCdd zm~02zzcbsv`rKSnETSOFM~{v64-}!K4%+8qnF@&;D{(5CJmrK&jO?C5+o|Q53CQ7d zYR^7*tnzzt_eV{l_V(nmS(w+9r{(?5Ty!}_JjXo}@yL#-rUEu;!x@$)+TE(kY@vjxH zrBnNggkRe>OXX?D2~ZzUqKuk}jMJR)-|3WbbugZF*FzxR2 zF|#hn@^3^TPZtL_N!)(VEk(orx13TJnk}@6h`ltLVF>d6mYt_zco5@h*db(%W+Xu? zut%%miX2jewNAT#!yQF*BYjVNzqD+Fz?X8%%N5>Df$IO11pm9vXqh6UgWKg=u-t}2 z4NwNGx<;*4aYWClJ#&`8)M_*&lV*WkjFEB2{8Y@#6-J%yZEe+R9y!2trcJjLJYd;= zq9%=cRJ^08_h+5>Jc;0TF6=i_x(vQ2 zuy0?wY(;G@p7lgt0A2PR`C|Z8z>p0H1*33Ko^8R{jsMoc2lH-fh zfgYP?2SzM(>6S{?lVv6TzSH{IDZ}5cygOSt5x$?fHvf6kA8*!g;N$OotnHtfcJ}n3 zEz34YUxvqtf8}5-Z0z*lZsc z+g|#R!{EQ7bkdqHS)Xd~zwT^Ge^+5yX+rd}t{Z#v+q7irmsu@Xu&Lsj5N$I`k(E z|4;uEWWst*tUL9GS6cm5lnJcI zk>l^hj=w$aLh&5^EXowtXZ#qk`rW z8n*TV9>7nuD!rZBD}5z3ANV<;?h2~c*j@+O`v2SY%1n@li2P&Cg9T|^w($xcf_#Ko z*`M+5pLvyRUoZglSIF$%oF5-EUrEDZeL_kHN1S|c&XjCVmZ?N&A5{8rSTCc`r^))t zmZd8}{zet{zP@P$FH?!(kGg#;HaBaMjD*J@0s3r&)onALNntKoPnMFZb$}Up7XSoD}#ji*x@KTbFTXJ(83He>taYJpNIz zWv9Xs%&9OvdRp5?uKX>~-l=r6mqE~| z1WrqkM+e{R6G21fiiCPl?)Jxn^fiM#+w=c`GJkfMpiXC>B1vsrUY4sEwm>N0MS}c0 z7N%`DV7`2kq7Bf;q3+8Dn|rxRBs*aKT}nm8JuHsS<%L3fhufZ6({J`dUr74j`Xg%s z_J#Fq(}u~0Hf9`I|GQ;rOQ4rg z|Cv|b>^o6ac@Oq~PX5S!%^tTH{JZeU{_<#`Pt&foyS29;pGej#pMtzM>e{7+b;FPI zcv$`#)t%=xFYuxd*`9~(*#`R(g7#l~K!4J%Qhx+dK;Js07dJfI`1vl`S=JZip?2MA zd4-!=bs_7?vJ&%Cwfq}5vl3ful5_-l*Beu3<^D~zHt;$Fyz2V|)fyghs4ZUr^r+Uq zk;RbQmGozbfBC0i1+@Q}!MiT8Jscs%-*5eqaboqpnDbs7XVAaj`}Hi%oVHv_0+VBV zw={WvK&|dm&==W${YQBg^v#!k;cmTmmj;UG!1$jt&8fGdPe#A)UHMx65Wv4EY-Fp1 z-6yk12+D0R{@di0LYwmri^+PjOr?W-y*l%vUR`}h^=HS*nibpt{ru8L?9%1V=2As6 z=6CR2lKz;cy`0&Ro?z&``;}q9ALiZ_I_>f z)c+Ixi9D@8swuEu`C(OG6=Z+d&bbHcd#2yI^^Y%)>LYyzWo72%lBLGa{j@q)P*DB} z>vgiL-3G$iu;x7e|!Nd%$Y{R0?$h`nui+8GFi+_okf)t{%~rP#i1IJ6 z0R6e+mR;zxrk`bk2e4mk4;%Dk(eC*N;)jPs^%?pf3Xd z`cKJP;2-&!ypOY;vkHIRy7IO1AmG1sM#~+W^v#rzIB|Zcgb(t)*r)CO zx<)pxst172qFG(ejvhFjBb@>Kn)WiMdu=7}tjq!0FU;&&KY86$op_3*CBUEW^K|mC z#T#|>9e)(a+n<@YZ*opOu5^4#(h=x0D|5xb0kuvoQeb^BGwf#UySMx*fagkLr;>vWLdHh`cJu~m+!Y>CaZl)_E%bhJvt#Qs(Ck`n^Vd9 zk_EtDsk1hZ8vW$cezIQL8v3V%h2Ja^{Sm=g4EQA9I$Zn3(vOwiA4vHB*JbfS;ID`} zJxs1POe`kxDQN=p4@-`*v}$N^LYM~a!+hos*S`>?KV1XwSxfrcOm^Q zxHY*qJzYE!^i6Q4|D2)vc2haFz|SI|!wtLH9i=}*{mVa8EZ7?Xndh>b$2J-&&W8Qg zzs|fLv9`yb|FZLcp;EjY_`^H-Zb+|e;UQ@Tl!w*1sDC9ZI#(vc!_W*x&?bnkxF==Oscs{J3 zV_4DjBl)#Nil#7rN5@Bz9;U@d6&Zl9gX5O>U0y^z5(q$EI%PJVJ>_;_oWc+EZ)eL+ zS7oAfpOSgFKDk@hNv|VM^5da@$K;$K?+EXuB>u_-SUC2KbKF|SGLT<@hj^y z1~&0W{dX61Mip3DH|%bT@p*SVZdUi=7{56^NAXXxv<1IhMmWf|5# zW-nU}J8EI9(_b|d^o8ly`qKukJF=C8r`iSUW85!caL}c8=quCOf8;r^Uae*|%x_Y< zq=sZP;M>YlFX3h1fQ{DvkvB`gM}!S$B^G zm2g3RnpmzeG&u8hFwYb4Z;~;Fw{T6BLMjyK({R=mvxnhIA8}iN*U0hXsnQE>M+?lM z+#oseYw3mI&QfoH*TBd=M>6j_YkX3$3g%zOabs-TSznxl1EGJNI-RHW@iSBM3&CEY zEcq{Ox^#EpaUecYJ7a!9R>l%DlAdg3)j?RV>P9VUS@5>mOO`>on&p_hl9ct+IPYP+ zDwegIJQ@{$U&aG`s`^wpt6WpBSlS8NS8;spa>ey!0_%8jupZw_vKN{)6RxDNJbsg8 z-TfBOM8`hjgaf@ltJ+OJ`P#!)+5qPJMK~dU=oGhL0hXW7QuifuYnk2Q^nm^CGZpl+ z?#p+-?QbaY`R9Ai!CSXlPY;od1AZwI2D-d_ZvR~>hxxyi9thlK-7rC_XT+Rey;07o zIlScjJK0O1-y8Aa=6;K=C$Z6otXH@Iy;anQ2`{*X4+|n-e3dLbHO{V1AF_P`-v6Je z@{@9!zw_fa2YNkK7Ipr1t;aSB>(|GM*l+;2ZFl0sO~;Q$t0U zidp>;a(wAyut)E!B3;J5t}##|0Q~P!tADBn54%OS|J|}63Ha%z@MiiVMc#11ERdfY z0-j`P!r|*Ad^wiSYtoKa*5tNw;8P&4S9xyUsWxdRgMrM z(^C7pPh7j$2yp!Sv}8kd|JBo8vcad)p#Slbl$`?&OdBfKgFSFurf+o7C2%s?UwR1i z{ZY#9YLDv|YLWG1S(yRw4)I=F*J%9V1zBI&vaBJ{cdyj`*eg-|PqzLAI9{A99@%+$ zf3vl0?Ug%$zPqFeZH#yNC&;$~|L;p7+{ z-z~gDS3MhavZ0!R{ATj5rJikN;j3H@{JmMSC2#fmkfX|Jz;By*t*;;Jz1&)00{CpA zWYO6VQ$-~F-!04VdF~p?D#aOx&h~=(fZs~~yNUPY3uAa3z<*VNxkas>HS}fGp}bNu zbA5}GxBqeZuk3tOgF)Xd7Ov=JYdCNUTYugO;Qxgjuc`-H?JHQVxc=)`|`Q$XKNl=W&|+CF%U0{fE_`Ni%3 z$nKyg#`gFG-q+T%TR6^P!%u+E=f^4h<~M8`tz&OeS71MplmsW&YvS2hHWT<=LVdhd z_23en{ZG{!=$WXT@L^$oqcc)`UXvhM@?5cJ`*Xa^aNp!S)8-}E9*!#*82V|u z&oc_kSB#+7$uQ2fw~{NcAH)Djq@58PjN57@t;UZBXhG?mP4g?0A2*uXhpd+_f$_y9#+*BOic=oO^k&nx}eElYvVX9FeEJv#55QE7XH zgwKfq`izpaUjAVCJe~6+*$<${2-V__2KlW=ll_&$VZD8%mPhlg_unMz3#LN-aB8nZ zdr9LpWPN4J0vzx2RE74eV$@V8-d9<8^5vj!1}Z%FHE1N)v5!^QKN+B0{fskks=WZ~ z|NgQI*-c#b6o^*>KlKxCm2~*DStoyj#7DUq_@l34*o|oh;sEghD7z?De408jESNe7 z`pkurTz_WrbRhpH(BE19huN^)to0*uMBL&MqKbXso?aF%C_Zm4qF9GDogm33~?b?d=%8kGurkpR$RBPk& zRHLEZSb5QRRg>DY<$=J@#szj)nxDC%b3V?)`m(jmebeau%}U7eWD5aaOZmAhzW>ke zWPN4J0tes^L;eU)%OERn{wAQmp}08Ltg%GrJVJ=~F9Ti|ev|n8-#!mw!&mSH)~kWM zi)d!Vx~jZKAaC_mdOd?WK4pzJlJKRI!TzI^hcoX#ud|4(XDf?wzF`fXpw7uR({%O+ z3fpVdWm^ZWm~z~igvZD6sH(i|UT)vpzbEVe+huAU@b{N40!w}Ipi2AuN}R9s-YuRQ z5xj6dAIBp;B-KCBY)k*Vk}%-+qSMO#-Q65N@#X`671e$^=-4a28q^%%=Xb+r3UWMO z>73t_^blNv^?&X5pyHF5e)5fMPnM;5(Ef>AmrYiE?%XEp|J!BCALxC1X4bpAdOkY-D24UkO^esmIlYG0 z1&=_!Zq^RSy)$6%X%b(767<=13u=Gyu(Qp`da}$L1M#LSoBM_DOfB8c$NuN#ytr4> zEPXeN+k?Kj^ul}i$+3X%+b8!Ceu`ibljOGfylj9Yv1bgDhaKFrTR;=!L z6$#!C57xGw{G_?lR1%(IG04yU7oF<&s2QOX|0CO1wp>sR_WvA<+lTLEUNdIvPu&52 z+V$l_gGpz*JXiJt`ebh|di&wjouNV;zs~Bip!$-cmQHN&*y>AEpbxg5UQjZq>ifA= z7Z`6#mtEFZK22biUpDw;y-W=Jz2WAD*^8#l8${MCje%Zk-5##+YqkCuuL$s4n^ZE< zl@nQ;Y_Hl5`fzpa!F5N6UNzyh1^idd{2md*KdW=zR)XWj%kuV{KC0vXT!GI+me1VK zKjGEIcpkR*Gm=h5r;dFtD2W638C@nB)oeRKpu+K1+3*>&eqJ1KMhBk-z@O=Pn@4^g zaxGil74&C%=&GPqhobb!_GDSU5ynfKc|wqPvCaSa{SP^g@6DPy^pZz%sSSxQTbUBV zdQW$oV>PnrfsJhK$$ABj|4%%9YijF}JFD`s{+o33%bnBIwMI&uUo-K`wP*QZNv!@p z315!&;W#&`RVUj80=Wy|E2-V(ZE2m<$AX@~Pm-H?mxo^ayjwUI)<@E%-hn$48ucLI zNhDC8*nEZe`5|j1;^6=BI`*`9DU2U=)3Q$cFn`wg5{W-q zrrv=3h26aWc47Ozo5*^yEX{)PLo5ci%jr1h7+KF&Rs{e(0xi}(DOOI2W@}&BdLE9S zj2zyj+PZD4bo_PY2B5d_#U!U?b#~m8=tDe8I9!_1CT*w*e-Pj&49#`AyJg01Db_#3 zLk~R4UAe=a@`L@vySDZ7lqUYq*zi-}`Fb@ExD@wnhd*2U->p|-`@sE$V~y$ibKVK+ zLp-Yg@T#8|IVU%!_JTdu|K{96quA(~17m0`zflGxqY>vFI(^e8e~(p!b)Yp{dEXZOQhk zF|ZyzH$P||w5XGgei3Yf`EEZ(8IyaZS$T3 zLEgU$ud@9G4Pia4lLoJS|D)wn!4jCiO_w~YMQzMlt8o0T-A!RF@0V*{O4`BtTDkQ& z8E-Q!UwRAdN2{fM$Nw30u#-3ofW#e`%XkP3jt9 zw@XnM=5L;~qj(g@@oNFj$2D*6ckKGR8h=s?0G~E5jOV0y-mG1M?Tt34ueTppf9R3Q z&i8lQ^YQulA74b*`DfP{@#_ISjF#TaYkBG8J2Cc0Ic~caJI`;EC&Tw+I7=h0nf{c? z*~aJK^V(*o_u7_D?XAPVWPcgHKiTxlf^)$;?4OeLig?hkjh7bmv%aDCoUE5l0QqWo zGx$~Q7A^|1o-CK(`-}D5gk48XNm@+SD+I8A)k(^=I*??b6F(%|SGKIg@}g(4k^9tJ zs3QAI@%@`>q1KNcI&R4$>&Y_bD(qK3LzcUzzG|nV-^uo5nZox^OC3fse8l~p)@ z;qDS@AJt@3b^*=@x*KBO_w~KLPBMI6aVPhbY+bN^#y^ceh3_j|pAjz{Wqrw5hVz@Q z-YAHV*>P?OAK$OI`ng%ry!PjG;+Z5q1^9gU(vtUQ`>*xcNSybS2yK zv3@@PxpmqC6O~TBlyVO6!`aXFJ-;RH9Y^+;)dqcj#zCN0ZD>m!`Iq8&)akYtI@UEf zqLcq4X$<=G#HmNGYagrfRfh8ePb5~o-h1{ABZ`9k>bS!Mzh1UeofH<({#dSc>_%s& zvm`!jWhJKf{!RP7SgcRXrf@#_!NkPm^{ub;rmSE;JCLZ@=&?&yqEG{*Pwk~-y^B6HlyRD_`Y~vh(mMXfcWiX|9`qn-G}kBPDQ3p z9&37|vhbDoykhI~Im6t?&3qv54F2iXO%pa-zQ3%q{sq|o$UN2i^@^fso%^Tc_+odU z*QSuAzRGh~&B=PUGKKTc*5!J?Ja_;0KW?AN@qMEe&qKPterBs1ACrTATlU;N$bQ&r z*8B}Awm+A)-QRiH;1dnW`Kg}5dZll=Ikne;f|J6Yu%9lfl^O2w=T1XTRgkZR&ubs? zZ6Wh1!TD`zhG&l5o7H}j1n2iI+%%K7%yU;w9r!q>&iBr_5U(1Nr@c{__&+t=yG( zztZQsBz(3q5Bsk(y>ztvy+*lo$j)e=S%9`fK8;J(t72EtZh*D_fQyg!pJmNQB< zi+}#(oZ!YGTLFIWBkuW0v%^6*Csr0;w)Pah-yGT3%xau(Ki2aS=_c4u#(eJUqIYFN z2oKXgY}5CT^&4F}F2?*C>OH(=lbUZ$WLTes)cQ03tNtC47~j_mITf;RT%%Pw`TfE{ z!2cre>l0K@>a=IWpRJyf!1?~@+*eWIb`63#`2NYL=RU=6L{;{QG5ts7X8p+bzHv%| zticN1nNc8J@ky_}2@-UO??}R{ zY?+VqPX_dTc~^LR+lb2cR|JB+>9%Qdtsk319ohN|oM3-<3F$oK(vinW1vp>3 U} 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 zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7e?|MvtwQFQ6kX}ilmI(T>S?1WU* zlAV%uNhK#0Atx8*U9)bfd{I$;da->bm9}JeW`o1CqVONvmTcE-NQ!73e|S6bN7Tw& z!cix0w|{#!J+*gc(XV%B%WC!I@msP^+4;5dEUV7%;*{U|{4P!9?VsNjwf4&6l-t

CX5{k%Dqx8KjV)Y_|#vupO&l-GJ3KG075 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 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ 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