fix: memory leak for Metal renderer when repeatedly connect/disconnect stream or toggling HDR on host

This commit is contained in:
Acaki committed 2025-08-24 20:54:51 +08:00
1 parent b64ad64524
commit a2c4d440db
2 files changed
+82 -11

No files matched your search

+71 -9
View File
@@ -163,6 +163,11 @@ CFStringRef __currentColorSpace;
_renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear; _renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear;
_renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0, 0, 0, 0); _renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0, 0, 0, 0);
_renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore; _renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore;
// Initialize texture array to NULL
for (int i = 0; i < MAX_VIDEO_PLANES; i++) {
_cvMetalTextures[i] = NULL;
}
} }
return self; return self;
} }
@@ -179,20 +184,36 @@ CFStringRef __currentColorSpace;
if (_VideoVertexBuffer) { if (_VideoVertexBuffer) {
_VideoVertexBuffer = nil; _VideoVertexBuffer = nil;
} }
// Clean up pipeline states
for (int i = 0; i < MAX_VIDEO_PLANES; i++) { for (int i = 0; i < MAX_VIDEO_PLANES; i++) {
if (_videoPipelineState[i]) { if (_videoPipelineState[i]) {
_videoPipelineState[i] = nil; _videoPipelineState[i] = nil;
} }
_videoPipelinePixelFormat[i] = MTLPixelFormatInvalid; _videoPipelinePixelFormat[i] = MTLPixelFormatInvalid;
} }
// Clean up any remaining Metal textures
for (int i = 0; i < MAX_VIDEO_PLANES; i++) {
if (_cvMetalTextures[i]) {
CFRelease(_cvMetalTextures[i]);
_cvMetalTextures[i] = NULL;
}
}
// Properly release texture cache
if (_textureCache) {
CFRelease(_textureCache);
_textureCache = NULL;
}
if (_renderPassDescriptor) { if (_renderPassDescriptor) {
_renderPassDescriptor = nil; _renderPassDescriptor = nil;
} }
if (_textureCache) {
_textureCache = nil;
}
if (__currentColorSpace) { if (__currentColorSpace) {
CFRelease(__currentColorSpace); CFRelease(__currentColorSpace);
__currentColorSpace = NULL;
} }
} }
@@ -345,6 +366,17 @@ CFStringRef __currentColorSpace;
BOOL fullRange = NO; BOOL fullRange = NO;
int colorspace = [self getFrameColorspaceAndRange:frame isFullRange:&fullRange]; int colorspace = [self getFrameColorspaceAndRange:frame isFullRange:&fullRange];
if (colorspace != _lastColorSpace || fullRange != _lastFullRange) { if (colorspace != _lastColorSpace || fullRange != _lastFullRange) {
// Clean up any pending textures before colorspace change
for (int i = 0; i < MAX_VIDEO_PLANES; i++) {
if (_cvMetalTextures[i]) {
CFRelease(_cvMetalTextures[i]);
_cvMetalTextures[i] = NULL;
}
}
// Flush texture cache to avoid memory accumulation
if (_textureCache) {
CVMetalTextureCacheFlush(_textureCache, 0);
}
CGColorSpaceRef newColorSpace = nil; CGColorSpaceRef newColorSpace = nil;
MTLPixelFormat newPixelFormat = layer.pixelFormat; MTLPixelFormat newPixelFormat = layer.pixelFormat;
BOOL isHDR = NO; BOOL isHDR = NO;
@@ -436,6 +468,7 @@ CFStringRef __currentColorSpace;
#endif #endif
if (__currentColorSpace) { if (__currentColorSpace) {
CFRelease(__currentColorSpace); CFRelease(__currentColorSpace);
__currentColorSpace = NULL;
} }
__currentColorSpace = CGColorSpaceCopyName(newColorSpace); __currentColorSpace = CGColorSpaceCopyName(newColorSpace);
CGColorSpaceRelease(newColorSpace); CGColorSpaceRelease(newColorSpace);
@@ -443,12 +476,15 @@ CFStringRef __currentColorSpace;
// Create the new colorspace parameter buffer for our fragment shader // Create the new colorspace parameter buffer for our fragment shader
MTLResourceOptions bufferOptions = MTLResourceStorageModeShared; MTLResourceOptions bufferOptions = MTLResourceStorageModeShared;
_CscParamsBuffer = [_device newBufferWithBytes:(void *)&paramBuffer length:sizeof(paramBuffer) options:bufferOptions]; id<MTLBuffer> newCscParamsBuffer = [_device newBufferWithBytes:(void *)&paramBuffer length:sizeof(paramBuffer) options:bufferOptions];
if (!_CscParamsBuffer) { if (!newCscParamsBuffer) {
Log(LOG_E, @"Failed to create CSC parameters buffer"); Log(LOG_E, @"Failed to create CSC parameters buffer");
return NO; return NO;
} }
// Replace old buffer with new one
_CscParamsBuffer = newCscParamsBuffer;
_lastColorSpace = colorspace; _lastColorSpace = colorspace;
_lastFullRange = fullRange; _lastFullRange = fullRange;
} }
@@ -504,12 +540,15 @@ CFStringRef __currentColorSpace;
}; };
MTLResourceOptions bufferOptions = MTLResourceStorageModeShared; MTLResourceOptions bufferOptions = MTLResourceStorageModeShared;
_VideoVertexBuffer = [_device newBufferWithBytes:verts length:sizeof(verts) options:bufferOptions]; id<MTLBuffer> newVideoVertexBuffer = [_device newBufferWithBytes:verts length:sizeof(verts) options:bufferOptions];
if (!_VideoVertexBuffer) { if (!newVideoVertexBuffer) {
Log(LOG_E, @"Failed to create video vertex buffer"); Log(LOG_E, @"Failed to create video vertex buffer");
return NO; return NO;
} }
// Replace old buffer with new one
_VideoVertexBuffer = newVideoVertexBuffer;
_lastFrameWidth = [frame width]; _lastFrameWidth = [frame width];
_lastFrameHeight = [frame height]; _lastFrameHeight = [frame height];
_lastDrawableWidth = drawableWidth; _lastDrawableWidth = drawableWidth;
@@ -550,7 +589,9 @@ CFStringRef __currentColorSpace;
Log(LOG_I, @"Metal frame changed layer's colorspace and/or pixel format"); Log(LOG_I, @"Metal frame changed layer's colorspace and/or pixel format");
// Invalidate all pipeline states since pixel format affects all of them // Invalidate all pipeline states since pixel format affects all of them
for (int i = 0; i < MAX_VIDEO_PLANES; i++) { for (int i = 0; i < MAX_VIDEO_PLANES; i++) {
_videoPipelineState[i] = nil; if (_videoPipelineState[i]) {
_videoPipelineState[i] = nil;
}
_videoPipelinePixelFormat[i] = MTLPixelFormatInvalid; _videoPipelinePixelFormat[i] = MTLPixelFormatInvalid;
} }
} }
@@ -700,7 +741,7 @@ CFStringRef __currentColorSpace;
for (size_t i = 0; i < planes; i++) { for (size_t i = 0; i < planes; i++) {
if (self->_cvMetalTextures[i]) { if (self->_cvMetalTextures[i]) {
CFRelease(self->_cvMetalTextures[i]); CFRelease(self->_cvMetalTextures[i]);
self->_cvMetalTextures[i] = nil; self->_cvMetalTextures[i] = NULL;
} }
} }
@@ -737,6 +778,27 @@ CFStringRef __currentColorSpace;
for (NSUInteger i = 0; i < MaxFramesInFlight; i++) { for (NSUInteger i = 0; i < MaxFramesInFlight; i++) {
dispatch_semaphore_signal(_inFlightSemaphore); dispatch_semaphore_signal(_inFlightSemaphore);
} }
// Clean up any pending Metal textures
for (int i = 0; i < MAX_VIDEO_PLANES; i++) {
if (_cvMetalTextures[i]) {
CFRelease(_cvMetalTextures[i]);
_cvMetalTextures[i] = NULL;
}
}
// Flush texture cache to free memory
if (_textureCache) {
CVMetalTextureCacheFlush(_textureCache, 0);
}
// Clear pipeline states
for (int i = 0; i < MAX_VIDEO_PLANES; i++) {
if (_videoPipelineState[i]) {
_videoPipelineState[i] = nil;
}
_videoPipelinePixelFormat[i] = MTLPixelFormatInvalid;
}
} }
} }
+11 -2
View File
@@ -124,10 +124,19 @@
if (_displayLink) { if (_displayLink) {
[_displayLink invalidate]; [_displayLink invalidate];
_displayLink = nil;
} }
[_renderer shutdown]; if (_renderer) {
_renderer = nil; [_renderer shutdown];
_renderer = nil;
}
if (_metalView) {
_metalView.delegate = nil;
[_metalView shutdown];
_metalView = nil;
}
} }
#if TARGET_OS_IOS #if TARGET_OS_IOS