Skip to content

Commit e3f5b8b

Browse files
Merge pull request #1616 from CapSoftware/windows-playback-perf
Windows playback perf
2 parents 1e84be2 + 103feb9 commit e3f5b8b

File tree

137 files changed

+40085
-234
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

137 files changed

+40085
-234
lines changed

Cargo.lock

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ cidre = { git = "https://github.com/CapSoftware/cidre", rev = "bf84b67079a8" }
108108
# https://github.com/gfx-rs/wgpu/pull/7550
109109
# wgpu = { git = "https://github.com/gfx-rs/wgpu", rev = "cd41a6e32a6239b65d1cecbeccde6a43a100914a" }
110110
wgpu-hal = { path = "vendor/wgpu-hal" }
111+
tao = { path = "vendor/tao" }
111112

112113
# https://github.com/CapSoftware/posthog-rs/commit/c7e9712be2f9a9122b1df685d5a067afa5415288
113114
posthog-rs = { git = "https://github.com/CapSoftware/posthog-rs", rev = "c7e9712be2f9a9122b1df685d5a067afa5415288" }

apps/desktop/src-tauri/src/editor_window.rs

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ use tauri::{AppHandle, Manager, Runtime, Window, ipc::CommandArg};
33
use tokio::sync::{RwLock, watch};
44
use tokio_util::sync::CancellationToken;
55

6+
use cap_rendering::GpuOutputFormat;
7+
68
use crate::{
79
create_editor_instance_impl,
810
frame_ws::{WSFrame, WSFrameFormat, create_watch_frame_ws},
@@ -29,16 +31,22 @@ async fn do_prewarm(app: AppHandle, path: PathBuf) -> PendingResult {
2931
path,
3032
Box::new(move |output| {
3133
let ws_frame = match output {
32-
cap_editor::EditorFrameOutput::Nv12(frame) => WSFrame {
33-
data: frame.data,
34-
width: frame.width,
35-
height: frame.height,
36-
stride: frame.y_stride,
37-
frame_number: frame.frame_number,
38-
target_time_ns: frame.target_time_ns,
39-
format: WSFrameFormat::Nv12,
40-
created_at: Instant::now(),
41-
},
34+
cap_editor::EditorFrameOutput::Nv12(frame) => {
35+
let ws_format = match frame.format {
36+
GpuOutputFormat::Nv12 => WSFrameFormat::Nv12,
37+
GpuOutputFormat::Rgba => WSFrameFormat::Rgba,
38+
};
39+
WSFrame {
40+
data: frame.data,
41+
width: frame.width,
42+
height: frame.height,
43+
stride: frame.y_stride,
44+
frame_number: frame.frame_number,
45+
target_time_ns: frame.target_time_ns,
46+
format: ws_format,
47+
created_at: Instant::now(),
48+
}
49+
}
4250
cap_editor::EditorFrameOutput::Rgba(frame) => WSFrame {
4351
data: frame.data,
4452
width: frame.width,
@@ -234,16 +242,22 @@ impl EditorInstances {
234242
path,
235243
Box::new(move |output| {
236244
let ws_frame = match output {
237-
cap_editor::EditorFrameOutput::Nv12(frame) => WSFrame {
238-
data: frame.data,
239-
width: frame.width,
240-
height: frame.height,
241-
stride: frame.y_stride,
242-
frame_number: frame.frame_number,
243-
target_time_ns: frame.target_time_ns,
244-
format: WSFrameFormat::Nv12,
245-
created_at: Instant::now(),
246-
},
245+
cap_editor::EditorFrameOutput::Nv12(frame) => {
246+
let ws_format = match frame.format {
247+
GpuOutputFormat::Nv12 => WSFrameFormat::Nv12,
248+
GpuOutputFormat::Rgba => WSFrameFormat::Rgba,
249+
};
250+
WSFrame {
251+
data: frame.data,
252+
width: frame.width,
253+
height: frame.height,
254+
stride: frame.y_stride,
255+
frame_number: frame.frame_number,
256+
target_time_ns: frame.target_time_ns,
257+
format: ws_format,
258+
created_at: Instant::now(),
259+
}
260+
}
247261
cap_editor::EditorFrameOutput::Rgba(frame) => WSFrame {
248262
data: frame.data,
249263
width: frame.width,

apps/desktop/src-tauri/src/frame_ws.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ fn pack_nv12_frame_ref(
3030
data: &[u8],
3131
width: u32,
3232
height: u32,
33+
y_stride: u32,
3334
frame_number: u32,
3435
target_time_ns: u64,
3536
) -> Vec<u8> {
36-
let y_stride = width;
3737
let metadata_size = 28;
3838
let mut output = Vec::with_capacity(data.len() + metadata_size);
3939
output.extend_from_slice(data);
@@ -90,6 +90,7 @@ fn pack_ws_frame_ref(frame: &WSFrame) -> Vec<u8> {
9090
&frame.data,
9191
frame.width,
9292
frame.height,
93+
frame.stride,
9394
frame.frame_number,
9495
frame.target_time_ns,
9596
),

apps/desktop/src/routes/editor/context.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export const getPreviewResolution = (
8080
quality: EditorPreviewQuality,
8181
): XY<number> => {
8282
const scale = previewQualityScale[quality];
83-
const width = (Math.max(2, Math.round(OUTPUT_SIZE.x * scale)) + 1) & ~1;
83+
const width = (Math.max(4, Math.round(OUTPUT_SIZE.x * scale)) + 3) & ~3;
8484
const height = (Math.max(2, Math.round(OUTPUT_SIZE.y * scale)) + 1) & ~1;
8585

8686
return { x: width, y: height };

apps/desktop/src/routes/screenshot-editor/context.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ function convertNv12ToRgba(
3838
const ySize = yStride * height;
3939
const yPlane = nv12Data;
4040
const uvPlane = nv12Data.subarray(ySize);
41-
const uvStride = width;
41+
const uvStride = yStride;
4242

4343
for (let row = 0; row < height; row++) {
4444
const yRowOffset = row * yStride;
@@ -285,7 +285,7 @@ function createScreenshotEditorContext() {
285285
if (!width || !height) return;
286286

287287
const ySize = yStride * height;
288-
const uvSize = width * (height / 2);
288+
const uvSize = yStride * (height / 2);
289289
const totalSize = ySize + uvSize;
290290

291291
const nv12Data = new Uint8ClampedArray(buffer, 0, totalSize);

apps/desktop/src/utils/frame-worker.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ function parseFrameMetadata(bytes: Uint8Array): FrameMetadata | null {
231231
if (!width || !height) return null;
232232

233233
const ySize = yStride * height;
234-
const uvSize = width * (height / 2);
234+
const uvSize = yStride * (height / 2);
235235
const totalSize = ySize + uvSize;
236236

237237
if (bytes.byteLength - 28 < totalSize) {
@@ -303,7 +303,7 @@ function convertNv12ToRgba(
303303
const ySize = yStride * height;
304304
const yPlane = nv12Data;
305305
const uvPlane = nv12Data.subarray(ySize);
306-
const uvStride = width;
306+
const uvStride = yStride;
307307

308308
for (let row = 0; row < height; row++) {
309309
const yRowOffset = row * yStride;

apps/desktop/src/utils/socket.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ function convertNv12ToRgbaMainThread(
6262
const ySize = yStride * height;
6363
const yPlane = nv12Data;
6464
const uvPlane = nv12Data.subarray(ySize);
65-
const uvStride = width;
65+
const uvStride = yStride;
6666

6767
for (let row = 0; row < height; row++) {
6868
const yRowOffset = row * yStride;
@@ -308,7 +308,7 @@ export function createImageDataWS(
308308

309309
if (width > 0 && height > 0) {
310310
const ySize = yStride * height;
311-
const uvSize = width * (height / 2);
311+
const uvSize = yStride * (height / 2);
312312
const totalSize = ySize + uvSize;
313313

314314
const frameData = new Uint8ClampedArray(buffer, 0, totalSize);
@@ -351,7 +351,7 @@ export function createImageDataWS(
351351

352352
if (width > 0 && height > 0) {
353353
const ySize = yStride * height;
354-
const uvSize = width * (height / 2);
354+
const uvSize = yStride * (height / 2);
355355
const totalSize = ySize + uvSize;
356356

357357
const frameData = new Uint8ClampedArray(buffer, 0, totalSize);
@@ -669,7 +669,7 @@ export function createImageDataWS(
669669

670670
if (width > 0 && height > 0) {
671671
const ySize = yStride * height;
672-
const uvSize = width * (height / 2);
672+
const uvSize = yStride * (height / 2);
673673
const totalSize = ySize + uvSize;
674674

675675
const frameData = new Uint8ClampedArray(buffer, 0, totalSize);
@@ -735,7 +735,7 @@ export function createImageDataWS(
735735

736736
if (width > 0 && height > 0) {
737737
const ySize = yStride * height;
738-
const uvSize = width * (height / 2);
738+
const uvSize = yStride * (height / 2);
739739
const totalSize = ySize + uvSize;
740740

741741
const nv12Data = new Uint8ClampedArray(buffer, 0, totalSize);

apps/desktop/src/utils/tauri.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,7 @@ videoImportProgress: "video-import-progress"
403403

404404
/** user-defined types **/
405405

406+
export type AllGpusInfo = { gpus: GpuInfoDiag[]; primaryGpuIndex: number | null; isMultiGpuSystem: boolean; hasDiscreteGpu: boolean }
406407
export type Annotation = { id: string; type: AnnotationType; x: number; y: number; width: number; height: number; strokeColor: string; strokeWidth: number; fillColor: string; opacity: number; rotation: number; text: string | null; maskType?: MaskType | null; maskLevel?: number | null }
407408
export type AnnotationType = "arrow" | "circle" | "rectangle" | "text" | "mask"
408409
export type AppTheme = "system" | "light" | "dark"
@@ -476,6 +477,7 @@ quality: number | null;
476477
*/
477478
fast: boolean | null }
478479
export type GlideDirection = "none" | "left" | "right" | "up" | "down"
480+
export type GpuInfoDiag = { vendor: string; description: string; dedicatedVideoMemoryMb: number; adapterIndex: number; isSoftwareAdapter: boolean; isBasicRenderDriver: boolean; supportsHardwareEncoding: boolean }
479481
export type HapticPattern = "alignment" | "levelChange" | "generic"
480482
export type HapticPerformanceTime = "default" | "now" | "drawCompleted"
481483
export type Hotkey = { code: string; meta: boolean; ctrl: boolean; alt: boolean; shift: boolean }
@@ -489,7 +491,6 @@ export type JsonValue<T> = [T]
489491
export type LogicalBounds = { position: LogicalPosition; size: LogicalSize }
490492
export type LogicalPosition = { x: number; y: number }
491493
export type LogicalSize = { width: number; height: number }
492-
export type MacOSVersionInfo = { major: number; minor: number; patch: number; displayName: string; buildNumber: string; isAppleSilicon: boolean }
493494
export type MainWindowRecordingStartBehaviour = "close" | "minimise"
494495
export type MaskKeyframes = { position?: MaskVectorKeyframe[]; size?: MaskVectorKeyframe[]; intensity?: MaskScalarKeyframe[] }
495496
export type MaskKind = "sensitive" | "highlight"
@@ -533,6 +534,7 @@ export type RecordingStatus = "pending" | "recording"
533534
export type RecordingStopped = null
534535
export type RecordingTargetMode = "display" | "window" | "area" | "camera"
535536
export type RenderFrameEvent = { frame_number: number; fps: number; resolution_base: XY<number> }
537+
export type RenderingStatus = { isUsingSoftwareRendering: boolean; isUsingBasicRenderDriver: boolean; hardwareEncodingAvailable: boolean; warningMessage: string | null }
536538
export type RequestOpenRecordingPicker = { target_mode: RecordingTargetMode | null }
537539
export type RequestOpenSettings = { page: string }
538540
export type RequestScreenCapturePrewarm = { force?: boolean }
@@ -555,7 +557,7 @@ export type StartRecordingInputs = { capture_target: ScreenCaptureTarget; captur
555557
export type StereoMode = "stereo" | "monoL" | "monoR"
556558
export type StudioRecordingMeta = { segment: SingleSegment } | { inner: MultipleSegments }
557559
export type StudioRecordingStatus = { status: "InProgress" } | { status: "NeedsRemux" } | { status: "Failed"; error: string } | { status: "Complete" }
558-
export type SystemDiagnostics = { macosVersion: MacOSVersionInfo | null; availableEncoders: string[]; screenCaptureSupported: boolean; metalSupported: boolean; gpuName: string | null }
560+
export type SystemDiagnostics = { windowsVersion: WindowsVersionInfo | null; gpuInfo: GpuInfoDiag | null; allGpus: AllGpusInfo | null; renderingStatus: RenderingStatus; availableEncoders: string[]; graphicsCaptureSupported: boolean; d3D11VideoProcessorAvailable: boolean }
559561
export type TargetUnderCursor = { display_id: DisplayId | null; window: WindowUnderCursor | null }
560562
export type TextSegment = { start: number; end: number; enabled?: boolean; content?: string; center?: XY<number>; size?: XY<number>; fontFamily?: string; fontSize?: number; fontWeight?: number; italic?: boolean; color?: string; fadeDuration?: number }
561563
export type TimelineConfiguration = { segments: TimelineSegment[]; zoomSegments: ZoomSegment[]; sceneSegments?: SceneSegment[]; maskSegments?: MaskSegment[]; textSegments?: TextSegment[] }
@@ -574,6 +576,7 @@ export type WindowExclusion = { bundleIdentifier?: string | null; ownerName?: st
574576
export type WindowId = string
575577
export type WindowPosition = { x: number; y: number; displayId?: DisplayId | null }
576578
export type WindowUnderCursor = { id: WindowId; app_name: string; bounds: LogicalBounds }
579+
export type WindowsVersionInfo = { major: number; minor: number; build: number; displayName: string; meetsRequirements: boolean; isWindows11: boolean }
577580
export type XY<T> = { x: T; y: T }
578581
export type ZoomMode = "auto" | { manual: { x: number; y: number } }
579582
export type ZoomSegment = { start: number; end: number; amount: number; mode: ZoomMode; glideDirection?: GlideDirection; glideSpeed?: number; instantAnimation?: boolean; edgeSnapRatio?: number }

apps/desktop/src/utils/webgpu-renderer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@ export function renderNv12FrameWebGPU(
380380
const ySize = yStride * height;
381381
const uvWidth = width / 2;
382382
const uvHeight = height / 2;
383-
const uvStride = width;
383+
const uvStride = yStride;
384384
const uvSize = uvStride * uvHeight;
385385

386386
if (data.byteLength < ySize + uvSize) {

0 commit comments

Comments
 (0)