Feature/perf capture recording optimizations - #134
Conversation
Implements a Capture Library feature with: - `CaptureLibraryService` and `ICaptureLibraryService` for managing and searching captures by file name or OCR text, with date filtering and progress reporting. - `CaptureTextIndex` and `ICaptureTextIndex` for OCR text extraction and LRU caching. - `LibraryViewModel` for MVVM state, search/filter logic, and commands. - New `LibraryWindow` (XAML/UI) for browsing, searching, and opening captures. - Tray menu integration, DI registration, and automation launch support. - `CaptureItem` and `CaptureSearchProgress` records for modeling. - `IOcrService` and `WindowsOcrService` updated for cancellation support. - Comprehensive unit tests for all new components.
- Add IDebounceService with async debounce implementation and DI registration - Debounce LibraryViewModel search input for better UX - Add date range presets and sync logic to LibraryViewModel - Optimize CaptureLibraryService to skip OCR for short queries - Centralize shared UI styles in Controls.xaml - Refactor Library and Settings windows to use shared styles - Improve Library filter bar with range presets and layout tweaks
Replaced MouseBinding for LeftDoubleClick with a MouseDoubleClick event handler in LibraryWindow.xaml. The new handler sets SelectedItem and invokes OpenCommand when a capture is double-clicked. Added FindAncestor<T> helper to locate the ListBoxItem in the visual tree.
Replaces `CaptureTextIndex`/`ICaptureTextIndex` with `CaptureTextLookupService`/`ICaptureTextLookupService` throughout the codebase. The new service uses `IServiceScopeFactory` for proper DbContext lifetime management. Updates all DI registrations and usages. Migrates and renames all related tests. Adds `SQLitePCLRaw.bundle_e_sqlite3` to support SQLite.
LibraryViewModel now tracks search telemetry events ("started", "completed", "short_query", "cancelled") with relevant properties via ITelemetryService. Constructors updated to accept an ITelemetryService, with NullTelemetryService as default. Improved cancellation logic for Refresh command. Added unit tests for telemetry. CaptureTextLookupService now only updates LastAccessedAt if 10+ minutes have passed.
… and improve error resilience
# Conflicts: # Pointframe.Tests/Services/CaptureLibraryOcrSearchTests.cs # Pointframe/Services/Capture/CaptureLibraryService.cs
There was a problem hiding this comment.
Pull request overview
This PR focuses on reducing hot-path allocations and improving responsiveness in two performance-sensitive areas of Pointframe: screen recording frame capture and capture-library searching/previewing.
Changes:
- Reuse capture resources during recording sessions (e.g., screen DC) and avoid extra buffer copies for stop-time padding.
- Refactor capture library enumeration into a shared helper and introduce file-system pattern prefiltering for faster searches, with results explicitly sorted newest-first for OCR searches.
- Reduce per-call allocations in the annotation color picker by reusing a scratch pixel buffer.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| Pointframe/Services/Recording/ScreenRecordingService.cs | Reuses screen DC and avoids redundant frame buffer copying during stop-time padding. |
| Pointframe/Services/Capture/CaptureLibraryService.cs | Refactors and optimizes capture enumeration/search (including optional filename search patterns) and enforces newest-first sorting for OCR results. |
| Pointframe/Services/Annotation/AnnotationCanvasRenderer.cs | Removes per-sample allocation by reusing a 4-byte scratch buffer for pixel sampling. |
| Pointframe.Tests/Services/CaptureLibraryOcrSearchTests.cs | Adds coverage ensuring OCR search results are sorted newest-first for long queries. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…alidate file search patterns in CaptureLibraryService
| _cts = new CancellationTokenSource(); | ||
| IsRecording = true; | ||
| _captureLoop = Task.Run(() => CaptureLoop(_cts.Token)); | ||
| _encodeLoop = Task.Run(EncodeLoop); | ||
| _logger.LogInformation("Recording started: {W}x{H} @ {Fps}fps (MP4) → {Path}", width, height, fps, outputPath); | ||
| } | ||
| catch | ||
| { | ||
| _screenDc?.Dispose(); | ||
| _screenDc = null; | ||
| _captureGraphics?.Dispose(); | ||
| _captureGraphics = null; | ||
| _captureBitmap?.Dispose(); | ||
| _captureBitmap = null; | ||
| _writer?.Dispose(); | ||
| _writer = null; | ||
| _captureLoop = null; | ||
| _encodeLoop = null; | ||
| _encodeChannel = null; | ||
| _cts?.Dispose(); | ||
| _cts = null; | ||
| throw; | ||
| } |
| _logger.LogInformation("Padding recording with {FrameCount} duplicate frames to match elapsed duration", framesToPad); | ||
|
|
||
| for (var index = 0; index < framesToPad; index++) | ||
| { | ||
| var frameCopy = new byte[paddingSource.Length]; | ||
| Buffer.BlockCopy(paddingSource, 0, frameCopy, 0, frameCopy.Length); | ||
| Interlocked.Increment(ref _attemptedFrameCount); | ||
| try | ||
| { | ||
| _writer?.WriteFrame(frameCopy); | ||
| _writer?.WriteFrame(paddingSource); | ||
| } |
|
@copilot Fix the code for all comments in this review thread. When a review comment includes a suggested change, apply the suggestion exactly. Do not make changes beyond what is described in the linked review thread. |
Addressed in |
No description provided.