mirror of
https://github.com/obsproject/obs-studio.git
synced 2026-09-27 17:55:42 -04:00
libobs-metal: Wrap blitSwapChains in autoreleasepool to fix mach port leak
The CVDisplayLink callback thread driving blitSwapChains() has no implicit autorelease pool. CAMetalDrawable objects returned by nextDrawable() are autoreleased and each holds an IOSurface backed by an IOSurfaceSharedEventReference mach port. Without an autoreleasepool, these drawables are never drained on the display link thread, causing a new IOSurface (and port) to leak every frame when a render target is set (e.g. cross-process preview via CAContext/CALayerHost). Fix by wrapping the full frame cycle (nextDrawable → blit → present → commit) in an autoreleasepool, so autoreleased drawables are reclaimed each frame and returned to the maximumDrawableCount pool.
This commit is contained in:
@@ -149,38 +149,43 @@ class MetalDevice {
|
||||
func blitSwapChains() {
|
||||
guard swapChains.count > 0 else { return }
|
||||
|
||||
guard let commandBuffer = commandQueue.makeCommandBuffer(),
|
||||
let encoder = commandBuffer.makeBlitCommandEncoder()
|
||||
else {
|
||||
return
|
||||
}
|
||||
|
||||
self.swapChainQueue.sync {
|
||||
swapChains = swapChains.filter { $0.discard == false }
|
||||
}
|
||||
|
||||
for swapChain in swapChains {
|
||||
guard let renderTarget = swapChain.renderTarget, let drawable = swapChain.layer.nextDrawable() else {
|
||||
continue
|
||||
}
|
||||
|
||||
guard renderTarget.texture.width == drawable.texture.width,
|
||||
renderTarget.texture.height == drawable.texture.height,
|
||||
renderTarget.texture.pixelFormat == drawable.texture.pixelFormat
|
||||
autoreleasepool {
|
||||
guard let commandBuffer = commandQueue.makeCommandBuffer(),
|
||||
let encoder = commandBuffer.makeBlitCommandEncoder()
|
||||
else {
|
||||
continue
|
||||
return
|
||||
}
|
||||
|
||||
autoreleasepool {
|
||||
var drawablesToPresent: [CAMetalDrawable] = []
|
||||
for swapChain in swapChains {
|
||||
guard let renderTarget = swapChain.renderTarget, let drawable = swapChain.layer.nextDrawable() else {
|
||||
continue
|
||||
}
|
||||
|
||||
guard renderTarget.texture.width == drawable.texture.width,
|
||||
renderTarget.texture.height == drawable.texture.height,
|
||||
renderTarget.texture.pixelFormat == drawable.texture.pixelFormat
|
||||
else {
|
||||
continue
|
||||
}
|
||||
|
||||
encoder.waitForFence(swapChain.fence)
|
||||
encoder.copy(from: renderTarget.texture, to: drawable.texture)
|
||||
drawablesToPresent.append(drawable)
|
||||
}
|
||||
|
||||
encoder.endEncoding()
|
||||
|
||||
for drawable in drawablesToPresent {
|
||||
commandBuffer.present(drawable)
|
||||
}
|
||||
}
|
||||
|
||||
encoder.endEncoding()
|
||||
commandBuffer.commit()
|
||||
commandBuffer.commit()
|
||||
}
|
||||
}
|
||||
|
||||
/// Simulates an explicit "clear" command commonly used in OpenGL or Direct3D11 implementations.
|
||||
|
||||
Reference in New Issue
Block a user