diff --git a/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift b/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift index ae82a4ca04..3b517ba006 100644 --- a/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift +++ b/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift @@ -172,7 +172,7 @@ public final class AnimatedStickerNode: ASDisplayNode { private var cachedData: (Data, Bool, EmojiFitzModifier?)? private let useMetalCache: Bool - private var renderer: (AnimationRenderer & ASDisplayNode)? + private var renderer: AnimationRendererPool.Holder? public var isPlaying: Bool = false private var currentLoopCount: Int = 0 @@ -232,30 +232,42 @@ public final class AnimatedStickerNode: ASDisplayNode { self.timer.swap(nil)?.invalidate() } + private static let hardwareRendererPool = AnimationRendererPool(generate: { + if #available(iOS 10.0, *) { + return CompressedAnimationRenderer() + } else { + return SoftwareAnimationRenderer() + } + }) + + private static let softwareRendererPool = AnimationRendererPool(generate: { + return SoftwareAnimationRenderer() + }) + private weak var nodeToCopyFrameFrom: AnimatedStickerNode? override public func didLoad() { super.didLoad() if #available(iOS 10.0, *), self.useMetalCache { - self.renderer = CompressedAnimationRenderer() + self.renderer = AnimatedStickerNode.hardwareRendererPool.take() } else { - self.renderer = SoftwareAnimationRenderer() + self.renderer = AnimatedStickerNode.softwareRendererPool.take() + if let contents = self.nodeToCopyFrameFrom?.renderer?.renderer.contents { + self.renderer?.renderer.contents = contents + } } - self.renderer?.frame = CGRect(origin: CGPoint(), size: self.size ?? self.bounds.size) - if let contents = self.nodeToCopyFrameFrom?.renderer?.contents { - self.renderer?.contents = contents - } + self.renderer?.renderer.frame = CGRect(origin: CGPoint(), size: self.size ?? self.bounds.size) if let (overlayColor, replace) = self.overlayColor { - self.renderer?.setOverlayColor(overlayColor, replace: replace, animated: false) + self.renderer?.renderer.setOverlayColor(overlayColor, replace: replace, animated: false) } self.nodeToCopyFrameFrom = nil - self.addSubnode(self.renderer!) + self.addSubnode(self.renderer!.renderer) } public func cloneCurrentFrame(from otherNode: AnimatedStickerNode?) { - if let renderer = self.renderer { - if let contents = otherNode?.renderer?.contents { + if let renderer = self.renderer?.renderer as? SoftwareAnimationRenderer, let otherRenderer = otherNode?.renderer?.renderer as? SoftwareAnimationRenderer { + if let contents = otherRenderer.contents { renderer.contents = contents } } else { @@ -428,7 +440,7 @@ public final class AnimatedStickerNode: ASDisplayNode { return } - strongSelf.renderer?.render(queue: strongSelf.queue, width: frame.width, height: frame.height, bytesPerRow: frame.bytesPerRow, data: frame.data, type: frame.type, mulAlpha: frame.multiplyAlpha, completion: { + strongSelf.renderer?.renderer.render(queue: strongSelf.queue, width: frame.width, height: frame.height, bytesPerRow: frame.bytesPerRow, data: frame.data, type: frame.type, mulAlpha: frame.multiplyAlpha, completion: { guard let strongSelf = self else { return } @@ -532,7 +544,7 @@ public final class AnimatedStickerNode: ASDisplayNode { return } - strongSelf.renderer?.render(queue: strongSelf.queue, width: frame.width, height: frame.height, bytesPerRow: frame.bytesPerRow, data: frame.data, type: frame.type, mulAlpha: frame.multiplyAlpha, completion: { + strongSelf.renderer?.renderer.render(queue: strongSelf.queue, width: frame.width, height: frame.height, bytesPerRow: frame.bytesPerRow, data: frame.data, type: frame.type, mulAlpha: frame.multiplyAlpha, completion: { guard let strongSelf = self else { return } @@ -685,7 +697,7 @@ public final class AnimatedStickerNode: ASDisplayNode { return } - strongSelf.renderer?.render(queue: strongSelf.queue, width: frame.width, height: frame.height, bytesPerRow: frame.bytesPerRow, data: frame.data, type: frame.type, mulAlpha: frame.multiplyAlpha, completion: { + strongSelf.renderer?.renderer.render(queue: strongSelf.queue, width: frame.width, height: frame.height, bytesPerRow: frame.bytesPerRow, data: frame.data, type: frame.type, mulAlpha: frame.multiplyAlpha, completion: { guard let strongSelf = self else { return } @@ -715,11 +727,11 @@ public final class AnimatedStickerNode: ASDisplayNode { public func updateLayout(size: CGSize) { self.size = size - self.renderer?.frame = CGRect(origin: CGPoint(), size: size) + self.renderer?.renderer.frame = CGRect(origin: CGPoint(), size: size) } public func setOverlayColor(_ color: UIColor?, replace: Bool, animated: Bool) { self.overlayColor = (color, replace) - self.renderer?.setOverlayColor(color, replace: replace, animated: animated) + self.renderer?.renderer.setOverlayColor(color, replace: replace, animated: animated) } } diff --git a/submodules/AnimatedStickerNode/Sources/AnimationRenderer.swift b/submodules/AnimatedStickerNode/Sources/AnimationRenderer.swift index accd2ac924..915c2e4532 100644 --- a/submodules/AnimatedStickerNode/Sources/AnimationRenderer.swift +++ b/submodules/AnimatedStickerNode/Sources/AnimationRenderer.swift @@ -8,7 +8,47 @@ public enum AnimationRendererFrameType { case dct } -protocol AnimationRenderer { +final class AnimationRendererPool { + final class Holder { + let pool: AnimationRendererPool + let renderer: AnimationRenderer + + init(pool: AnimationRendererPool, renderer: AnimationRenderer) { + self.pool = pool + self.renderer = renderer + } + + deinit { + self.renderer.removeFromSupernode() + self.pool.putBack(renderer: self.renderer) + } + } + + private let generate: () -> AnimationRenderer + + private var items: [AnimationRenderer] = [] + + init(generate: @escaping () -> AnimationRenderer) { + self.generate = generate + } + + func take() -> Holder { + if !self.items.isEmpty { + let item = self.items.removeLast() + return Holder(pool: self, renderer: item) + } else { + return Holder(pool: self, renderer: self.generate()) + } + } + + private func putBack(renderer: AnimationRenderer) { + #if DEBUG + self.items.append(renderer) + #endif + } +} + +protocol AnimationRenderer: ASDisplayNode { func render(queue: Queue, width: Int, height: Int, bytesPerRow: Int, data: Data, type: AnimationRendererFrameType, mulAlpha: Bool, completion: @escaping () -> Void) func setOverlayColor(_ color: UIColor?, replace: Bool, animated: Bool) diff --git a/submodules/AnimatedStickerNode/Sources/CompressedAnimationRenderer.swift b/submodules/AnimatedStickerNode/Sources/CompressedAnimationRenderer.swift index d924b51f86..47dd85d689 100644 --- a/submodules/AnimatedStickerNode/Sources/CompressedAnimationRenderer.swift +++ b/submodules/AnimatedStickerNode/Sources/CompressedAnimationRenderer.swift @@ -88,66 +88,6 @@ final class CompressedAnimationRenderer: ASDisplayNode, AnimationRenderer { case .yuva: self.renderer.renderYuva(metalLayer: self.layer, width: width, height: height, data: data, completion: completion) } - - /*assert(bytesPerRow > 0) - queue.async { [weak self] in - switch type { - case .dct: - break - default: - return - } - - var image: UIImage? - - autoreleasepool { - image = generateImagePixel(CGSize(width: CGFloat(width), height: CGFloat(height)), scale: 1.0, pixelGenerator: { _, pixelData, contextBytesPerRow in - switch type { - case .yuva: - data.withUnsafeBytes { bytes -> Void in - guard let baseAddress = bytes.baseAddress else { - return - } - if bytesPerRow <= 0 || height <= 0 || width <= 0 || bytesPerRow * height > bytes.count { - assert(false) - return - } - decodeYUVAToRGBA(baseAddress.assumingMemoryBound(to: UInt8.self), pixelData, Int32(width), Int32(height), Int32(contextBytesPerRow)) - } - case .argb: - var data = data - data.withUnsafeMutableBytes { bytes -> Void in - guard let baseAddress = bytes.baseAddress else { - return - } - if mulAlpha { - var srcData = vImage_Buffer(data: baseAddress.assumingMemoryBound(to: UInt8.self), height: vImagePixelCount(height), width: vImagePixelCount(width), rowBytes: bytesPerRow) - var destData = vImage_Buffer(data: pixelData, height: vImagePixelCount(height), width: vImagePixelCount(width), rowBytes: bytesPerRow) - - let permuteMap: [UInt8] = [3, 2, 1, 0] - vImagePermuteChannels_ARGB8888(&srcData, &destData, permuteMap, vImage_Flags(kvImageDoNotTile)) - vImagePremultiplyData_ARGB8888(&destData, &destData, vImage_Flags(kvImageDoNotTile)) - vImagePermuteChannels_ARGB8888(&destData, &destData, permuteMap, vImage_Flags(kvImageDoNotTile)) - } else { - memcpy(pixelData, baseAddress.assumingMemoryBound(to: UInt8.self), bytes.count) - } - } - } - }) - } - - Queue.mainQueue().async { - guard let strongSelf = self else { - return - } - strongSelf.contents = image?.cgImage - strongSelf.updateHighlightedContentNode() - if strongSelf.highlightedContentNode?.frame != strongSelf.bounds { - strongSelf.highlightedContentNode?.frame = strongSelf.bounds - } - completion() - } - }*/ } private func updateHighlightedContentNode() { diff --git a/submodules/AnimationCompression/Resources/DCT.metal b/submodules/AnimationCompression/Resources/DCT.metal index d1b8b8f619..f0f3e78c92 100644 --- a/submodules/AnimationCompression/Resources/DCT.metal +++ b/submodules/AnimationCompression/Resources/DCT.metal @@ -75,7 +75,9 @@ fragment float4 samplingIdctShader( const half4 yuva = half4(color0, color1, color2, color3); - return float4(rgb(yuva)); + const half4 color = rgb(yuva); + + return float4(color.r * color.a, color.g * color.a, color.b * color.a, color.a); } fragment float4 samplingRgbShader( @@ -84,7 +86,11 @@ fragment float4 samplingRgbShader( ) { constexpr sampler textureSampler(mag_filter::linear, min_filter::linear); - const half4 color = colorTexture.sample(textureSampler, in.textureCoordinate); + half4 color = colorTexture.sample(textureSampler, in.textureCoordinate); + + color.r *= color.a; + color.g *= color.a; + color.b *= color.a; return float4(color.r, color.g, color.b, color.a); } @@ -104,27 +110,33 @@ fragment float4 samplingYuvaShader( RasterizerData in [[stage_in]], texture2d yTexture [[texture(0)]], texture2d cbcrTexture [[texture(1)]], - texture2d alphaTexture [[texture(2)]], - constant int &alphaWidth [[buffer(3)]] + texture2d alphaTexture [[texture(2)]], + constant uint2 &alphaSize [[buffer(3)]] ) { constexpr sampler textureSampler(mag_filter::linear, min_filter::linear); - constexpr sampler nearestSampler(mag_filter::nearest, min_filter::nearest); half4 color = samplePoint(yTexture, cbcrTexture, textureSampler, in.textureCoordinate); - int horizontalPixel = (int)(in.textureCoordinate.x * alphaWidth); - uint8_t alpha2 = (uint8_t)alphaTexture.sample(nearestSampler, in.textureCoordinate.x, in.textureCoordinate.y).r; + int alphaX = (int)(in.textureCoordinate.x * alphaSize.x); + int alphaY = (int)(in.textureCoordinate.y * alphaSize.y); - uint8_t a1 = (alpha2 & (0xf0U)); - uint8_t a2 = ((alpha2 & (0x0fU)) << 4); + uint32_t packedAlpha = alphaTexture.read(uint2(alphaX / 2, alphaY)).r; + uint32_t a1 = (packedAlpha & (0xf0U)); + uint32_t a2 = (packedAlpha & (0x0fU)) << 4; - uint8_t alphas[2]; - alphas[0] = a1 | (a1 >> 4); - alphas[1] = a2 | (a2 >> 4); - int alphaIndex = horizontalPixel % 2; - uint8_t resolvedAlpha = alphas[alphaIndex]; + uint32_t left = (a1 >> 4) | a1; + uint32_t right = (a2 >> 4) | a2; - color.a = ((half)resolvedAlpha) / 255.0f; + uint32_t chooseLeft = alphaX % 2 == 0; + uint32_t resolvedAlpha = chooseLeft * left + (1 - chooseLeft) * right; + + float alpha = resolvedAlpha / 255.0f; + + color.r *= alpha; + color.g *= alpha; + color.b *= alpha; + + color.a = alpha; return float4(color); } diff --git a/submodules/AnimationCompression/Sources/AnimationCompressor.swift b/submodules/AnimationCompression/Sources/AnimationCompressor.swift index b3e1f7f569..46bf366c3e 100644 --- a/submodules/AnimationCompression/Sources/AnimationCompressor.swift +++ b/submodules/AnimationCompression/Sources/AnimationCompressor.swift @@ -131,17 +131,12 @@ final class Texture { } } - func readDirect(width: Int, height: Int, bytesPerRow: Int, read: (UnsafeMutableRawPointer, Int) -> Void) { + func readDirect(width: Int, height: Int, bytesPerRow: Int, read: (UnsafeMutableRawPointer?) -> UnsafeRawPointer) { if let directBuffer = self.directBuffer, width == self.width, height == self.height, bytesPerRow == directBuffer.bytesPerRow { - read(directBuffer.buffer.contents(), directBuffer.buffer.length) + let _ = read(directBuffer.buffer.contents()) } else { - var tempData = Data(count: height * bytesPerRow) - tempData.withUnsafeMutableBytes { bytes in - read(bytes.baseAddress!, height * bytesPerRow) - - let region = MTLRegion(origin: MTLOrigin(x: 0, y: 0, z: 0), size: MTLSize(width: width, height: height, depth: 1)) - self.texture.replace(region: region, mipmapLevel: 0, withBytes: bytes.baseAddress!, bytesPerRow: bytesPerRow) - } + let region = MTLRegion(origin: MTLOrigin(x: 0, y: 0, z: 0), size: MTLSize(width: width, height: height, depth: 1)) + self.texture.replace(region: region, mipmapLevel: 0, withBytes: read(nil), bytesPerRow: bytesPerRow) } } } diff --git a/submodules/AnimationCompression/Sources/CompressedImageRenderer.swift b/submodules/AnimationCompression/Sources/CompressedImageRenderer.swift index 901cc727a8..064993f610 100644 --- a/submodules/AnimationCompression/Sources/CompressedImageRenderer.swift +++ b/submodules/AnimationCompression/Sources/CompressedImageRenderer.swift @@ -22,7 +22,6 @@ public final class CompressedImageRenderer { let renderIdctPipelineState: MTLRenderPipelineState let renderRgbPipelineState: MTLRenderPipelineState let renderYuvaPipelineState: MTLRenderPipelineState - let commandQueue: MTLCommandQueue init?(sharedContext: AnimationCompressor.SharedContext) { self.sharedContext = sharedContext @@ -81,11 +80,6 @@ public final class CompressedImageRenderer { return nil } self.renderYuvaPipelineState = renderYuvaPipelineState - - guard let commandQueue = self.sharedContext.device.makeCommandQueue() else { - return nil - } - self.commandQueue = commandQueue } } @@ -98,10 +92,61 @@ public final class CompressedImageRenderer { private var rgbTexture: Texture? private var yuvaTextures: TextureSet? + + private let commandQueue: MTLCommandQueue public init?(sharedContext: AnimationCompressor.SharedContext) { self.sharedContext = sharedContext self.shared = Shared.shared + + guard let commandQueue = self.sharedContext.device.makeCommandQueue() else { + return nil + } + self.commandQueue = commandQueue + } + + 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 + } + return metalLayer.nextDrawable() + } 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 updateIdctTextures(compressedImage: AnimationCompressor.CompressedImageData) { @@ -162,8 +207,18 @@ public final class CompressedImageRenderer { let planeSize = Int(readBuffer.readInt32()) let planeData = readBuffer.readDataNoCopy(length: planeSize) - compressedTextures.textures[i].readDirect(width: planeWidth, height: planeHeight, bytesPerRow: bytesPerRow, read: { destination, maxLength in - readDCTBlocks(Int32(planeWidth), Int32(planeHeight), planeData, destination.assumingMemoryBound(to: Float32.self), Int32(bytesPerRow / 4)) + var tempData: Data? + compressedTextures.textures[i].readDirect(width: planeWidth, height: planeHeight, bytesPerRow: bytesPerRow, read: { destinationBytes in + if let destinationBytes = destinationBytes { + readDCTBlocks(Int32(planeWidth), Int32(planeHeight), planeData, destinationBytes.assumingMemoryBound(to: Float32.self), Int32(bytesPerRow / 4)) + return UnsafeRawPointer(destinationBytes) + } else { + tempData = Data(count: bytesPerRow * planeHeight) + return tempData!.withUnsafeMutableBytes { bytes -> UnsafeRawPointer in + readDCTBlocks(Int32(planeWidth), Int32(planeHeight), planeData, bytes.baseAddress!.assumingMemoryBound(to: Float32.self), Int32(bytesPerRow / 4)) + return UnsafeRawPointer(bytes.baseAddress!) + } + } }) } } @@ -177,7 +232,7 @@ public final class CompressedImageRenderer { return } - guard let commandBuffer = self.shared.commandQueue.makeCommandBuffer() else { + guard let commandBuffer = self.commandQueue.makeCommandBuffer() else { return } commandBuffer.label = "MyCommand" @@ -240,28 +295,7 @@ public final class CompressedImageRenderer { let drawableSize = CGSize(width: CGFloat(outputTextures.textures[0].width), height: CGFloat(outputTextures.textures[0].height)) - var maybeDrawable: CAMetalDrawable? - #if targetEnvironment(simulator) - if #available(iOS 13.0, *) { - if let metalLayer = metalLayer as? CAMetalLayer { - if metalLayer.drawableSize != drawableSize { - metalLayer.drawableSize = drawableSize - } - maybeDrawable = metalLayer.nextDrawable() - } - } else { - preconditionFailure() - } - #else - if let metalLayer = metalLayer as? CAMetalLayer { - if metalLayer.drawableSize != drawableSize { - metalLayer.drawableSize = drawableSize - } - maybeDrawable = metalLayer.nextDrawable() - } - #endif - - guard let drawable = maybeDrawable else { + guard let drawable = self.getNextDrawable(metalLayer: metalLayer, drawableSize: drawableSize) else { commandBuffer.commit() completion() return @@ -287,13 +321,34 @@ public final class CompressedImageRenderer { renderEncoder.endEncoding() - commandBuffer.present(drawable) + var storedDrawable: MTLDrawable? = drawable + commandBuffer.addScheduledHandler { _ in + storedDrawable?.present() + storedDrawable = nil + } + +#if targetEnvironment(simulator) 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() + } + } + } +#endif commandBuffer.commit() } @@ -316,8 +371,15 @@ public final class CompressedImageRenderer { rgbTexture = texture } - rgbTexture.readDirect(width: width, height: height, bytesPerRow: bytesPerRow, read: { destination, maxLength in - data.copyBytes(to: destination.assumingMemoryBound(to: UInt8.self), from: 0 ..< min(maxLength, data.count)) + rgbTexture.readDirect(width: width, height: height, bytesPerRow: bytesPerRow, read: { destinationBytes in + return data.withUnsafeBytes { bytes -> UnsafeRawPointer in + if let destinationBytes = destinationBytes { + memcpy(destinationBytes, bytes.baseAddress!, bytes.count) + return UnsafeRawPointer(destinationBytes) + } else { + return bytes.baseAddress! + } + } }) } @@ -328,35 +390,14 @@ public final class CompressedImageRenderer { return } - guard let commandBuffer = self.shared.commandQueue.makeCommandBuffer() else { + guard let commandBuffer = self.commandQueue.makeCommandBuffer() else { return } commandBuffer.label = "MyCommand" let drawableSize = CGSize(width: CGFloat(rgbTexture.width), height: CGFloat(rgbTexture.height)) - var maybeDrawable: CAMetalDrawable? -#if targetEnvironment(simulator) - if #available(iOS 13.0, *) { - if let metalLayer = metalLayer as? CAMetalLayer { - if metalLayer.drawableSize != drawableSize { - metalLayer.drawableSize = drawableSize - } - maybeDrawable = metalLayer.nextDrawable() - } - } else { - preconditionFailure() - } -#else - if let metalLayer = metalLayer as? CAMetalLayer { - if metalLayer.drawableSize != drawableSize { - metalLayer.drawableSize = drawableSize - } - maybeDrawable = metalLayer.nextDrawable() - } -#endif - - guard let drawable = maybeDrawable else { + guard let drawable = self.getNextDrawable(metalLayer: metalLayer, drawableSize: drawableSize) else { commandBuffer.commit() completion() return @@ -413,7 +454,7 @@ public final class CompressedImageRenderer { pixelFormat: .rg8Unorm ), TextureSet.Description( - fractionWidth: 1, fractionHeight: 1, + fractionWidth: 2, fractionHeight: 1, pixelFormat: .r8Uint ) ], @@ -431,40 +472,32 @@ public final class CompressedImageRenderer { return } - yuvaTextures.textures[0].readDirect(width: width, height: height, bytesPerRow: width, read: { destination, maxLength in - memcpy(destination, yuva.advanced(by: 0), min(width * height, maxLength)) - }) - - yuvaTextures.textures[1].readDirect(width: width / 2, height: height / 2, bytesPerRow: width, read: { destination, maxLength in - memcpy(destination, yuva.advanced(by: width * height), min(width * height, maxLength)) - }) - - var unpackedAlpha = Data(count: width * height) - unpackedAlpha.withUnsafeMutableBytes { alphaBuffer in - let alphaBytes = alphaBuffer.baseAddress!.assumingMemoryBound(to: UInt8.self) - let alpha = yuva.advanced(by: width * height * 2) - - var i = 0 - for y in 0 ..< height { - let alphaRow = alphaBytes.advanced(by: y * width) - - var x = 0 - while x < width { - let a = alpha[i / 2] - let a1 = (a & (0xf0)) - let a2 = ((a & (0x0f)) << 4) - alphaRow[x + 0] = a1 | (a1 >> 4); - alphaRow[x + 1] = a2 | (a2 >> 4); - - x += 2 - i += 2 - } + yuvaTextures.textures[0].readDirect(width: width, height: height, bytesPerRow: width, read: { destinationBytes in + if let destinationBytes = destinationBytes { + memcpy(destinationBytes, yuva.advanced(by: 0), width * height) + return UnsafeRawPointer(destinationBytes) + } else { + return UnsafeRawPointer(yuva.advanced(by: 0)) } - - yuvaTextures.textures[2].readDirect(width: width, height: height, bytesPerRow: width, read: { destination, maxLength in - memcpy(destination, alphaBytes, min(maxLength, width * height)) - }) - } + }) + + yuvaTextures.textures[1].readDirect(width: width / 2, height: height / 2, bytesPerRow: width, read: { destinationBytes in + if let destinationBytes = destinationBytes { + memcpy(destinationBytes, yuva.advanced(by: width * height), width * height / 2) + return UnsafeRawPointer(destinationBytes) + } else { + return UnsafeRawPointer(yuva.advanced(by: width * height)) + } + }) + + yuvaTextures.textures[2].readDirect(width: width / 2, height: height, bytesPerRow: width / 2, read: { destinationBytes in + if let destinationBytes = destinationBytes { + memcpy(destinationBytes, yuva.advanced(by: width * height * 2), width / 2 * height) + return UnsafeRawPointer(destinationBytes) + } else { + return UnsafeRawPointer(yuva.advanced(by: width * height * 2)) + } + }) } } @@ -475,35 +508,14 @@ public final class CompressedImageRenderer { return } - guard let commandBuffer = self.shared.commandQueue.makeCommandBuffer() else { + guard let commandBuffer = self.commandQueue.makeCommandBuffer() else { return } commandBuffer.label = "MyCommand" let drawableSize = CGSize(width: CGFloat(yuvaTextures.width), height: CGFloat(yuvaTextures.height)) - var maybeDrawable: CAMetalDrawable? -#if targetEnvironment(simulator) - if #available(iOS 13.0, *) { - if let metalLayer = metalLayer as? CAMetalLayer { - if metalLayer.drawableSize != drawableSize { - metalLayer.drawableSize = drawableSize - } - maybeDrawable = metalLayer.nextDrawable() - } - } else { - preconditionFailure() - } -#else - if let metalLayer = metalLayer as? CAMetalLayer { - if metalLayer.drawableSize != drawableSize { - metalLayer.drawableSize = drawableSize - } - maybeDrawable = metalLayer.nextDrawable() - } -#endif - - guard let drawable = maybeDrawable else { + guard let drawable = self.getNextDrawable(metalLayer: metalLayer, drawableSize: drawableSize) else { commandBuffer.commit() completion() return @@ -524,8 +536,8 @@ public final class CompressedImageRenderer { renderEncoder.setFragmentTexture(yuvaTextures.textures[1].texture, index: 1) renderEncoder.setFragmentTexture(yuvaTextures.textures[2].texture, index: 2) - var alphaWidth: Int32 = Int32(yuvaTextures.textures[2].texture.width) - renderEncoder.setFragmentBytes(&alphaWidth, length: 4, index: 3) + var alphaSize = simd_uint2(UInt32(yuvaTextures.textures[0].texture.width), UInt32(yuvaTextures.textures[0].texture.height)) + renderEncoder.setFragmentBytes(&alphaSize, length: 8, index: 3) renderEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 6)