From 1cda4437cc25437d1e09969e6280fcf2c47275e1 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 18 Feb 2022 20:07:42 +0400 Subject: [PATCH] Metal rendering improvements --- .../Sources/AnimatedStickerFrameSource.swift | 10 +- .../Sources/AnimatedStickerNode.swift | 2 +- .../Sources/AnimationRenderer.swift | 4 +- .../Sources/CompressedAnimationRenderer.swift | 54 ++--- .../Sources/CompressedImageRenderer.swift | 185 +++--------------- 5 files changed, 53 insertions(+), 202 deletions(-) diff --git a/submodules/AnimatedStickerNode/Sources/AnimatedStickerFrameSource.swift b/submodules/AnimatedStickerNode/Sources/AnimatedStickerFrameSource.swift index f90c0769c9..2858b52f01 100644 --- a/submodules/AnimatedStickerNode/Sources/AnimatedStickerFrameSource.swift +++ b/submodules/AnimatedStickerNode/Sources/AnimatedStickerFrameSource.swift @@ -245,6 +245,12 @@ public final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource } } +private func alignUp(size: Int, align: Int) -> Int { + precondition(((align - 1) & align) == 0, "Align must be a power of two") + + let alignmentMask = align - 1 + return (size + alignmentMask) & ~alignmentMask +} private final class AnimatedStickerDirectFrameSourceCache { private enum FrameRangeResult { @@ -274,8 +280,8 @@ private final class AnimatedStickerDirectFrameSourceCache { self.storeQueue = sharedStoreQueue self.frameCount = frameCount - self.width = width - self.height = height + self.width = alignUp(size: width, align: 8) + self.height = alignUp(size: width, align: 8) self.useHardware = useHardware let suffix : String diff --git a/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift b/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift index 3b517ba006..9f7fb2ec8f 100644 --- a/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift +++ b/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift @@ -248,7 +248,7 @@ public final class AnimatedStickerNode: ASDisplayNode { override public func didLoad() { super.didLoad() - if #available(iOS 10.0, *), self.useMetalCache { + if #available(iOS 10.0, *), (self.useMetalCache/* || "".isEmpty*/) { self.renderer = AnimatedStickerNode.hardwareRendererPool.take() } else { self.renderer = AnimatedStickerNode.softwareRendererPool.take() diff --git a/submodules/AnimatedStickerNode/Sources/AnimationRenderer.swift b/submodules/AnimatedStickerNode/Sources/AnimationRenderer.swift index 915c2e4532..e4e2264b15 100644 --- a/submodules/AnimatedStickerNode/Sources/AnimationRenderer.swift +++ b/submodules/AnimatedStickerNode/Sources/AnimationRenderer.swift @@ -42,9 +42,9 @@ final class AnimationRendererPool { } private func putBack(renderer: AnimationRenderer) { - #if DEBUG + /*#if DEBUG self.items.append(renderer) - #endif + #endif*/ } } diff --git a/submodules/AnimatedStickerNode/Sources/CompressedAnimationRenderer.swift b/submodules/AnimatedStickerNode/Sources/CompressedAnimationRenderer.swift index 47dd85d689..3446907812 100644 --- a/submodules/AnimatedStickerNode/Sources/CompressedAnimationRenderer.swift +++ b/submodules/AnimatedStickerNode/Sources/CompressedAnimationRenderer.swift @@ -8,44 +8,19 @@ import Accelerate import AnimationCompression import Metal import MetalKit +import MetalImageView @available(iOS 10.0, *) final class CompressedAnimationRenderer: ASDisplayNode, AnimationRenderer { private final class View: UIView { static override var layerClass: AnyClass { -#if targetEnvironment(simulator) - if #available(iOS 13.0, *) { - return CAMetalLayer.self - } else { - preconditionFailure() - } -#else - return CAMetalLayer.self -#endif + return MetalImageLayer.self } init(device: MTLDevice) { super.init(frame: CGRect()) -#if targetEnvironment(simulator) - if #available(iOS 13.0, *) { - let metalLayer = self.layer as! CAMetalLayer - - metalLayer.device = MTLCreateSystemDefaultDevice() - metalLayer.pixelFormat = .bgra8Unorm - metalLayer.framebufferOnly = true - metalLayer.allowsNextDrawableTimeout = true - } -#else - let metalLayer = self.layer as! CAMetalLayer - - metalLayer.device = MTLCreateSystemDefaultDevice() - metalLayer.pixelFormat = .bgra8Unorm - metalLayer.framebufferOnly = true - if #available(iOS 11.0, *) { - metalLayer.allowsNextDrawableTimeout = true - } -#endif + (self.layer as! MetalImageLayer).renderer.device = device } required init?(coder: NSCoder) { @@ -82,16 +57,25 @@ final class CompressedAnimationRenderer: ASDisplayNode, AnimationRenderer { func render(queue: Queue, width: Int, height: Int, bytesPerRow: Int, data: Data, type: AnimationRendererFrameType, mulAlpha: Bool, completion: @escaping () -> Void) { switch type { case .dct: - self.renderer.renderIdct(metalLayer: self.layer, compressedImage: AnimationCompressor.CompressedImageData(data: data), completion: completion) + self.renderer.renderIdct(layer: self.layer as! MetalImageLayer, compressedImage: AnimationCompressor.CompressedImageData(data: data), completion: { [weak self] in + self?.updateHighlightedContentNode() + completion() + }) case .argb: - self.renderer.renderRgb(metalLayer: self.layer, width: width, height: height, bytesPerRow: bytesPerRow, data: data, completion: completion) + self.renderer.renderRgb(layer: self.layer as! MetalImageLayer, width: width, height: height, bytesPerRow: bytesPerRow, data: data, completion: { [weak self] in + self?.updateHighlightedContentNode() + completion() + }) case .yuva: - self.renderer.renderYuva(metalLayer: self.layer, width: width, height: height, data: data, completion: completion) + self.renderer.renderYuva(layer: self.layer as! MetalImageLayer, width: width, height: height, data: data, completion: { [weak self] in + self?.updateHighlightedContentNode() + completion() + }) } } private func updateHighlightedContentNode() { - /*guard let highlightedContentNode = self.highlightedContentNode, let highlightedColor = self.highlightedColor else { + guard let highlightedContentNode = self.highlightedContentNode, let highlightedColor = self.highlightedColor else { return } if let contents = self.contents, CFGetTypeID(contents as CFTypeRef) == CGImage.typeID { @@ -100,11 +84,11 @@ final class CompressedAnimationRenderer: ASDisplayNode, AnimationRenderer { highlightedContentNode.tintColor = highlightedColor if self.highlightReplacesContent { self.contents = nil - }*/ + } } func setOverlayColor(_ color: UIColor?, replace: Bool, animated: Bool) { - /*self.highlightReplacesContent = replace + self.highlightReplacesContent = replace var updated = false if let current = self.highlightedColor, let color = color { updated = !current.isEqual(color) @@ -141,6 +125,6 @@ final class CompressedAnimationRenderer: ASDisplayNode, AnimationRenderer { strongSelf.highlightedContentNode?.removeFromSupernode() strongSelf.highlightedContentNode = nil }) - }*/ + } } } diff --git a/submodules/AnimationCompression/Sources/CompressedImageRenderer.swift b/submodules/AnimationCompression/Sources/CompressedImageRenderer.swift index 8336fb5f71..fe835aa6db 100644 --- a/submodules/AnimationCompression/Sources/CompressedImageRenderer.swift +++ b/submodules/AnimationCompression/Sources/CompressedImageRenderer.swift @@ -4,6 +4,7 @@ import Metal import MetalKit import simd import DctHuffman +import MetalImageView private struct Vertex { var position: vector_float2 @@ -109,62 +110,9 @@ public final class CompressedImageRenderer { private var drawableRequestTimestamp: Double? - private func getNextDrawable(metalLayer: CALayer, drawableSize: CGSize) -> CAMetalDrawable? { -#if targetEnvironment(simulator) - if #available(iOS 13.0, *) { - if let metalLayer = metalLayer as? CAMetalLayer { - if metalLayer.drawableSize != drawableSize { - metalLayer.drawableSize = drawableSize - } - let beginTime = CFAbsoluteTimeGetCurrent() - let drawableRequestDuration: Double - if let drawableRequestTimestamp = self.drawableRequestTimestamp { - drawableRequestDuration = beginTime - drawableRequestTimestamp - if drawableRequestDuration < 1.0 / 60.0 { - return nil - } - } else { - drawableRequestDuration = 0.0 - } - self.drawableRequestTimestamp = beginTime - let result = metalLayer.nextDrawable() - let duration = CFAbsoluteTimeGetCurrent() - beginTime - if duration > 1.0 / 60.0 { - print("lag \(duration * 1000.0) ms (\(drawableRequestDuration * 1000.0) ms)") - } - return result - } else { - return nil - } - } else { - return nil - } -#else - if let metalLayer = metalLayer as? CAMetalLayer { - if metalLayer.drawableSize != drawableSize { - metalLayer.drawableSize = drawableSize - } - let beginTime = CFAbsoluteTimeGetCurrent() - let drawableRequestDuration: Double - if let drawableRequestTimestamp = self.drawableRequestTimestamp { - drawableRequestDuration = beginTime - drawableRequestTimestamp - if drawableRequestDuration < 1.0 / 60.0 { - return nil - } - } else { - drawableRequestDuration = 0.0 - } - self.drawableRequestTimestamp = beginTime - let result = metalLayer.nextDrawable() - let duration = CFAbsoluteTimeGetCurrent() - beginTime - if duration > 1.0 / 200.0 { - print("lag \(duration * 1000.0) ms (\(drawableRequestDuration * 1000.0) ms)") - } - return result - } else { - return nil - } -#endif + private func getNextDrawable(layer: MetalImageLayer, drawableSize: CGSize) -> MetalImageLayer.Drawable? { + layer.renderer.drawableSize = drawableSize + return layer.renderer.nextDrawable() } private func updateIdctTextures(compressedImage: AnimationCompressor.CompressedImageData) { @@ -241,7 +189,7 @@ public final class CompressedImageRenderer { } } - public func renderIdct(metalLayer: CALayer, compressedImage: AnimationCompressor.CompressedImageData, completion: @escaping () -> Void) { + public func renderIdct(layer: MetalImageLayer, compressedImage: AnimationCompressor.CompressedImageData, completion: @escaping () -> Void) { DispatchQueue.global().async { self.updateIdctTextures(compressedImage: compressedImage) @@ -313,7 +261,7 @@ public final class CompressedImageRenderer { let drawableSize = CGSize(width: CGFloat(outputTextures.textures[0].width), height: CGFloat(outputTextures.textures[0].height)) - guard let drawable = self.getNextDrawable(metalLayer: metalLayer, drawableSize: drawableSize) else { + guard let drawable = self.getNextDrawable(layer: layer, drawableSize: drawableSize) else { commandBuffer.commit() completion() return @@ -339,35 +287,15 @@ public final class CompressedImageRenderer { renderEncoder.endEncoding() - var storedDrawable: MTLDrawable? = drawable - commandBuffer.addScheduledHandler { _ in - autoreleasepool { - storedDrawable?.present() - storedDrawable = nil - } - } - -#if targetEnvironment(simulator) + var storedDrawable: MetalImageLayer.Drawable? = drawable commandBuffer.addCompletedHandler { _ in DispatchQueue.main.async { - completion() - } - } -#else - if #available(iOS 10.3, *) { - drawable.addPresentedHandler { _ in - DispatchQueue.main.async { - completion() - } - } - } else { - commandBuffer.addCompletedHandler { _ in - DispatchQueue.main.async { - completion() + autoreleasepool { + storedDrawable?.present(completion: completion) + storedDrawable = nil } } } -#endif commandBuffer.commit() } @@ -402,14 +330,7 @@ public final class CompressedImageRenderer { }) } - public func renderRgb(metalLayer: CALayer, width: Int, height: Int, bytesPerRow: Int, data: Data, completion: @escaping () -> Void) { - if "".isEmpty { - DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2.0 / 60.0, execute: { - completion() - }) - return - } - + public func renderRgb(layer: MetalImageLayer, width: Int, height: Int, bytesPerRow: Int, data: Data, completion: @escaping () -> Void) { self.updateRgbTexture(width: width, height: height, bytesPerRow: bytesPerRow, data: data) guard let rgbTexture = self.rgbTexture else { @@ -423,7 +344,7 @@ public final class CompressedImageRenderer { let drawableSize = CGSize(width: CGFloat(rgbTexture.width), height: CGFloat(rgbTexture.height)) - guard let drawable = self.getNextDrawable(metalLayer: metalLayer, drawableSize: drawableSize) else { + guard let drawable = self.getNextDrawable(layer: layer, drawableSize: drawableSize) else { commandBuffer.commit() completion() return @@ -446,35 +367,15 @@ public final class CompressedImageRenderer { renderEncoder.endEncoding() - var storedDrawable: MTLDrawable? = drawable - commandBuffer.addScheduledHandler { _ in - autoreleasepool { - storedDrawable?.present() - storedDrawable = nil - } - } - -#if targetEnvironment(simulator) + var storedDrawable: MetalImageLayer.Drawable? = drawable commandBuffer.addCompletedHandler { _ in DispatchQueue.main.async { - completion() - } - } -#else - if #available(iOS 10.3, *) { - drawable.addPresentedHandler { _ in - DispatchQueue.main.async { - completion() - } - } - } else { - commandBuffer.addCompletedHandler { _ in - DispatchQueue.main.async { - completion() + autoreleasepool { + storedDrawable?.present(completion: completion) + storedDrawable = nil } } } -#endif commandBuffer.commit() } @@ -553,17 +454,10 @@ public final class CompressedImageRenderer { } } - public func renderYuva(metalLayer: CALayer, width: Int, height: Int, data: Data, completion: @escaping () -> Void) { - if self.isRendering { - DispatchQueue.main.async { - completion() - } - return - } - self.isRendering = true + public func renderYuva(layer: MetalImageLayer, width: Int, height: Int, data: Data, completion: @escaping () -> Void) { DispatchQueue.global().async { autoreleasepool { - let renderStartTime = CFAbsoluteTimeGetCurrent() + //let renderStartTime = CFAbsoluteTimeGetCurrent() var beginTime: Double = 0.0 var duration: Double = 0.0 @@ -578,7 +472,6 @@ public final class CompressedImageRenderer { guard let yuvaTextures = self.yuvaTextures else { DispatchQueue.main.async { - self.isRendering = false completion() } return @@ -588,7 +481,6 @@ public final class CompressedImageRenderer { guard let commandBuffer = self.commandQueue.makeCommandBuffer() else { DispatchQueue.main.async { - self.isRendering = false completion() } return @@ -598,10 +490,9 @@ public final class CompressedImageRenderer { let drawableSize = CGSize(width: CGFloat(yuvaTextures.width), height: CGFloat(yuvaTextures.height)) - guard let drawable = self.getNextDrawable(metalLayer: metalLayer, drawableSize: drawableSize) else { + guard let drawable = self.getNextDrawable(layer: layer, drawableSize: drawableSize) else { commandBuffer.commit() DispatchQueue.main.async { - self.isRendering = false completion() } return @@ -614,7 +505,6 @@ public final class CompressedImageRenderer { guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) else { DispatchQueue.main.async { - self.isRendering = false completion() } return @@ -633,44 +523,15 @@ public final class CompressedImageRenderer { renderEncoder.endEncoding() - var storedDrawable: MTLDrawable? = drawable - commandBuffer.addScheduledHandler { _ in - autoreleasepool { - storedDrawable?.present() - storedDrawable = nil - } - } - -#if targetEnvironment(simulator) + var storedDrawable: MetalImageLayer.Drawable? = drawable commandBuffer.addCompletedHandler { _ in DispatchQueue.main.async { - self.isRendering = false - completion() - } - } -#else - if #available(iOS 10.3, *) { - commandBuffer.addCompletedHandler { _ in - DispatchQueue.main.async { - self.isRendering = false - } - let renderDuration = CFAbsoluteTimeGetCurrent() - renderStartTime - print("render duration \(renderDuration * 1000.0) ms") - } - drawable.addPresentedHandler { _ in - DispatchQueue.main.async { - completion() - } - } - } else { - commandBuffer.addCompletedHandler { _ in - DispatchQueue.main.async { - self.isRendering = false - completion() + autoreleasepool { + storedDrawable?.present(completion: completion) + storedDrawable = nil } } } -#endif commandBuffer.commit()