Skip to content

Commit cbcc80a

Browse files
jslokclaude
andauthored
fix: Cap the AHardwareBuffer import cache at maximum 12 images (#4129)
Importing an AHardwareBuffer into Vulkan acquires a reference on it, so the unbounded import cache pinned every camera buffer the Resizer had ever seen. Cap it at 12 entries, evicting oldest-inserted first. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 8b13c0b commit cbcc80a

2 files changed

Lines changed: 17 additions & 0 deletions

File tree

packages/react-native-vision-camera-resizer/android/src/main/cpp/vulkan/VulkanHardwareBufferInterop.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,14 @@ const VulkanHardwareBufferInterop::ImportedImage& VulkanHardwareBufferInterop::i
7373
iterator = _cachedImages.erase(iterator);
7474
}
7575

76+
// Evict the oldest entries before inserting so old camera sessions' buffers get released.
77+
// Safe to destroy here: run() holds _stateMutex across import -> dispatch -> submit-and-wait,
78+
// so no imported image is in flight on the GPU at this point.
79+
while (_cachedImages.size() >= kMaxCachedImages) {
80+
destroyImportedImage(_cachedImages.front().importedImage);
81+
_cachedImages.erase(_cachedImages.begin());
82+
}
83+
7684
// No suitable ImportedImage was found in our cache - we have to create a new one.
7785
CachedImage cachedImage{
7886
.hardwareBuffer = hardwareBuffer,

packages/react-native-vision-camera-resizer/android/src/main/cpp/vulkan/VulkanHardwareBufferInterop.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,15 @@ class VulkanHardwareBufferInterop final {
7070
ImportedImage importedImage{};
7171
};
7272

73+
/**
74+
* Upper bound on cached imported images (oldest-inserted evicted first).
75+
* Importing an AHardwareBuffer into Vulkan acquires a reference on it, so every cached entry
76+
* pins a whole camera buffer - each camera session restart allocates a fresh buffer set, and
77+
* an unbounded cache pins the old sets forever. A streaming session cycles through ~4-6
78+
* buffers, so 12 leaves plenty of slack.
79+
*/
80+
static inline constexpr size_t kMaxCachedImages = 12;
81+
7382
static inline constexpr VkFormatFeatureFlags kRequiredExternalFormatFeatures = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT;
7483
static inline constexpr VkFormatFeatureFlags kLinearFilterFeatureMask =
7584
VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT;

0 commit comments

Comments
 (0)