[Android] Reduce RAM hogging and OOM crashes#1354
Open
waxm134 wants to merge 5 commits into
Open
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Android is much more resource constraint than Desktop. These fixes ensure that memory is freed up whenever possible on Android. All changes only take effect on Android platform. Export pipeline is untouched, all fixes target the preview path
Type of Change
Changes Made
1. Downscale-first preview path (
lib.rs:process_preview_job)Android goes through a different code path than desktop. Instead of transforming at full resolution then downscaling, it:
preview_dimfirstpreview_dimRAM impact: The largest intermediate buffer is ~
preview_dim^2 × 3 × 4bytes instead oforiginal_res^2 × 3 × 4. For a 24MP image at 1920px preview: ~42 MB vs ~288 MB.Preview quality: Identical — color/exposure/crop transformations produce the same result regardless of whether they run at 1920px or 6000px, because they're per-pixel operations.
Export quality: Untouched — export uses
get_cached_full_warped_image(full-res, no downscale-first).2. Preview cache at
max(editorPreviewResolution, 2560)(image_loader.rs)When an image is loaded, a downscaled copy of the pristine image is created at
max(editorPreviewResolution, 2560)(default: 2560) and cached instate.preview_cache. The full-resolutionDynamicImageinoriginal_imagecan be freed by the Rust allocator.RAM impact: The full original image (e.g. 24 MP, ~280 MB as
Rgb32F) can be deallocated, leaving only the ~30 MB 2560px cache.Preview quality: At normal zoom levels (preview_dim ≤ 2560), the 2560 cache is used directly — no quality loss. At high zoom (preview_dim > 2560), the cache is bypassed and the full original is used.
Export quality: Unaffected — export always reads the full original.
3.
ImageRgba32F→ImageRgb8conversion on Android (image_loader.rs)After decoding a raw file that produces a 32-bit float image on Android, it's immediately converted to 8-bit RGB.
RAM impact: 4× less memory per pixel (1 byte per channel instead of 4).
Preview quality: The conversion happens before the preview pipeline. Since the preview pipeline works in
f32internally anyway (the downscale producesRgb32F), the quality loss is just the initial precision loss from the raw developer → in practice invisible.Export quality: Unaffected — export re-reads the raw file at full precision.
4. Throttled flush pipeline (
useImageProcessing.ts)The
flushPipeline(which sends theapply_adjustmentsinvoke to the backend) is throttled to at most once per 33ms on Android, with a 200ms backup timer.RAM impact: Prevents job queue buildup. Without this, multiple high-resolution renders could be in-flight simultaneously, causing 2-3× peak memory.
Preview quality: No change — throttling delays the 2nd+ frame by at most 200ms (shorter than a single render at high zoom).
Export quality: Unaffected.
5. Ref-based hi-fi zoom dispatch (
useImageProcessing.ts)On Android,
requestHiFiZoomandrequestHiFiOriginalZoomstore the target resolution in a ref and call themselves debounced without arguments. Non-Android passes the target directly.RAM impact: Ensures only the highest pending resolution is actually rendered — no unnecessary intermediate renders at non-peak zoom.
Preview quality: No change — you get the final resolution, not intermediate ones.
6.
isSliderDraggingguard on crop preview (useImageProcessing.ts)The uncropped (crop menu) preview generation is blocked while the slider is being dragged.
RAM impact: Prevents concurrent heavy renders during slider changes.
7. Sequence guard + work mutex on uncropped preview (
lib.rs:generate_uncropped_preview)Uses
uncropped_preview_seq(atomic counter) anduncropped_preview_work_mutexto ensure only the latest uncropped preview job runs. A 500ms emit throttle prevents flooding the frontend with JPEG base64 data.RAM impact: Prevents multiple uncropped preview jobs stacking up on the thread pool.
Preview quality: No change.
8. GPU processor + image cache cleared on image switch (
image_loader.rs)state.gpu_processorandstate.gpu_image_cacheare set toNonewhen a new image is loaded.RAM impact: Drops all GPU textures (output, working, blur, tonal, clarity, tile, geometry staging) — can be hundreds of MB on a large image.
9. Resolution refs reset on image switch (
useImageProcessing.ts)currentResRef.current = 0andcurrentOriginalResRef.current = 0whenselectedImage?.pathchanges.RAM impact: Ensures the new image renders at base resolution immediately rather than comparing against the old image's high-res target.
10. Zoom-out memory freeing (
useImageProcessing.ts)When
finalRes < currentResRef.current * 0.7, a new render is forced at the lower resolution.RAM impact: Overwrites the backend's
cached_preview(192+ MB for a 4000px RGBA f32 blob) with the lower-resolution version, freeing that RAM. Also replaces the frontend blob URL.Screenshots/Videos
Testing
Test Configuration:
Checklist
Additional Notes
AI Disclaimer:
Please state the involvement of AI in this PR: