Skip to content

Commit 6bb498c

Browse files
authored
Fix iOS Camera Texture memory leak on dispose (#1135)
* Fix issue where camera texture delegate was stopping reset from being called on camera texture dispose. * Remove implData reference from CameraTextureDelegate, and just pass down textureCache. * PR Feedback
1 parent a4fcade commit 6bb498c

File tree

2 files changed

+33
-15
lines changed

2 files changed

+33
-15
lines changed

Plugins/NativeCamera/Source/Apple/NativeCameraImpl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ namespace Babylon::Plugins
2929
Graphics::DeviceContext* m_deviceContext;
3030
Napi::Env m_env;
3131

32-
std::shared_ptr<ImplData> m_implData;
32+
std::unique_ptr<ImplData> m_implData;
3333

3434
bool m_overrideCameraTexture{};
3535

Plugins/NativeCamera/Source/Apple/NativeCameraImpl.mm

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,14 @@ @interface CameraTextureDelegate : NSObject <AVCaptureVideoDataOutputSampleBuffe
2525
{
2626
@public AVCaptureVideoOrientation videoOrientation;
2727

28-
std::shared_ptr<Babylon::Plugins::Camera::Impl::ImplData> implData;
28+
CVMetalTextureCacheRef textureCache;
2929
bool orientationUpdated;
3030
}
3131

32-
- (id)init:(std::shared_ptr<Babylon::Plugins::Camera::Impl::ImplData>)implData;
32+
- (id)init:(CVMetalTextureCacheRef)textureCache;
3333
- (id<MTLTexture>)getCameraTextureY;
3434
- (id<MTLTexture>)getCameraTextureCbCr;
35+
- (void)reset;
3536

3637
@end
3738

@@ -140,7 +141,9 @@ fragment float4 fragmentShader(RasterizerData in [[stage_in]],
140141
[currentCommandBuffer waitUntilCompleted];
141142
}
142143

143-
[avCaptureSession stopRunning];
144+
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
145+
[avCaptureSession stopRunning];
146+
});
144147

145148
if (textureCache)
146149
{
@@ -152,7 +155,7 @@ fragment float4 fragmentShader(RasterizerData in [[stage_in]],
152155
CameraTextureDelegate* cameraTextureDelegate{};
153156
AVCaptureSession* avCaptureSession{};
154157
CVMetalTextureCacheRef textureCache{};
155-
id <MTLTexture> textureRGBA{};
158+
id<MTLTexture> textureRGBA{};
156159
id<MTLRenderPipelineState> cameraPipelineState{};
157160
id<MTLDevice> metalDevice{};
158161
id<MTLCommandQueue> commandQueue{};
@@ -191,7 +194,7 @@ fragment float4 fragmentShader(RasterizerData in [[stage_in]],
191194

192195
dispatch_async(dispatch_get_main_queue(), ^{
193196
CVMetalTextureCacheCreate(nullptr, nullptr, m_implData->metalDevice, nullptr, &m_implData->textureCache);
194-
m_implData->cameraTextureDelegate = [[CameraTextureDelegate alloc]init:m_implData];
197+
m_implData->cameraTextureDelegate = [[CameraTextureDelegate alloc]init:m_implData->textureCache];
195198
m_implData->avCaptureSession = [[AVCaptureSession alloc] init];
196199
m_implData->textureRGBA = nil;
197200

@@ -347,7 +350,9 @@ fragment float4 fragmentShader(RasterizerData in [[stage_in]],
347350
// Actually start the camera session.
348351
[m_implData->avCaptureSession addOutput:dataOutput];
349352
[m_implData->avCaptureSession commitConfiguration];
350-
[m_implData->avCaptureSession startRunning];
353+
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
354+
[m_implData->avCaptureSession startRunning];
355+
});
351356

352357
// Create a pipeline state for converting the camera output to RGBA.
353358
id<MTLLibrary> lib = CompileShader(m_implData->metalDevice, shaderSource);
@@ -463,7 +468,9 @@ fragment float4 fragmentShader(RasterizerData in [[stage_in]],
463468

464469
void Camera::Impl::Close()
465470
{
466-
m_implData.reset(new ImplData);
471+
[m_implData->cameraTextureDelegate reset];
472+
m_implData.reset();
473+
m_implData = std::make_unique<ImplData>();
467474
}
468475
}
469476

@@ -472,10 +479,10 @@ @implementation CameraTextureDelegate {
472479
CVMetalTextureRef cameraTextureCbCr;
473480
}
474481

475-
- (id)init:(std::shared_ptr<Babylon::Plugins::Camera::Impl::ImplData>)implData
482+
- (id)init:(CVMetalTextureCacheRef)textureCache
476483
{
477484
self = [super init];
478-
self->implData = implData;
485+
self->textureCache = textureCache;
479486
#if (TARGET_OS_IPHONE)
480487
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(OrientationDidChange:) name:UIDeviceOrientationDidChangeNotification object:nil];
481488
[self updateOrientation];
@@ -513,6 +520,12 @@ - (id)init:(std::shared_ptr<Babylon::Plugins::Camera::Impl::ImplData>)implData
513520
return nil;
514521
}
515522

523+
- (void) reset {
524+
@synchronized (self) {
525+
self->textureCache = nil;
526+
}
527+
}
528+
516529
#if (TARGET_OS_IPHONE)
517530
/**
518531
Updates target video orientation.
@@ -603,11 +616,16 @@ - (CVMetalTextureRef)getCameraTexture:(CVPixelBufferRef)pixelBuffer plane:(int)p
603616
auto pixelFormat = planeIndex ? MTLPixelFormatRG8Unorm : MTLPixelFormatR8Unorm;
604617
CVMetalTextureRef textureRef;
605618

606-
// Create a texture from the corresponding plane.
607-
auto status = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, implData->textureCache, pixelBuffer, nil, pixelFormat, planeWidth, planeHeight, planeIndex, &textureRef);
608-
if (status != kCVReturnSuccess) {
609-
CVBufferRelease(textureRef);
610-
textureRef = nil;
619+
@synchronized (self) {
620+
if (self->textureCache == nil) {
621+
return{};
622+
}
623+
624+
auto status = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, textureCache, pixelBuffer, nil, pixelFormat, planeWidth, planeHeight, planeIndex, &textureRef);
625+
if (status != kCVReturnSuccess) {
626+
CVBufferRelease(textureRef);
627+
textureRef = nil;
628+
}
611629
}
612630

613631
return textureRef;

0 commit comments

Comments
 (0)