This commit is contained in:
Ali 2022-07-13 11:23:09 +02:00
parent 74b5d4998c
commit c5d3e5e294
6 changed files with 206 additions and 79 deletions

View File

@ -6,18 +6,34 @@ import ManagedFile
import Compression
public final class AnimationCacheItemFrame {
public enum Format {
case rgba(width: Int, height: Int, bytesPerRow: Int)
public enum RequestedFormat {
case rgba
case yuva
}
public final class Plane {
public let data: Data
public let width: Int
public let height: Int
public let bytesPerRow: Int
public init(data: Data, width: Int, height: Int, bytesPerRow: Int) {
self.data = data
self.width = width
self.height = height
self.bytesPerRow = bytesPerRow
}
}
public enum Format {
case rgba(data: Data, width: Int, height: Int, bytesPerRow: Int)
case yuva(y: Plane, u: Plane, v: Plane, a: Plane)
}
public let data: Data
public let range: Range<Int>
public let format: Format
public let duration: Double
public init(data: Data, range: Range<Int>, format: Format, duration: Double) {
self.data = data
self.range = range
public init(format: Format, duration: Double) {
self.format = format
self.duration = duration
}
@ -25,22 +41,22 @@ public final class AnimationCacheItemFrame {
public final class AnimationCacheItem {
public let numFrames: Int
private let getFrameImpl: (Int) -> AnimationCacheItemFrame?
private let getFrameImpl: (Int, AnimationCacheItemFrame.RequestedFormat) -> AnimationCacheItemFrame?
private let getFrameIndexImpl: (Double) -> Int
public init(numFrames: Int, getFrame: @escaping (Int) -> AnimationCacheItemFrame?, getFrameIndexImpl: @escaping (Double) -> Int) {
public init(numFrames: Int, getFrame: @escaping (Int, AnimationCacheItemFrame.RequestedFormat) -> AnimationCacheItemFrame?, getFrameIndexImpl: @escaping (Double) -> Int) {
self.numFrames = numFrames
self.getFrameImpl = getFrame
self.getFrameIndexImpl = getFrameIndexImpl
}
public func getFrame(index: Int) -> AnimationCacheItemFrame? {
return self.getFrameImpl(index)
public func getFrame(index: Int, requestedFormat: AnimationCacheItemFrame.RequestedFormat) -> AnimationCacheItemFrame? {
return self.getFrameImpl(index, requestedFormat)
}
public func getFrame(at duration: Double) -> AnimationCacheItemFrame? {
public func getFrame(at duration: Double, requestedFormat: AnimationCacheItemFrame.RequestedFormat) -> AnimationCacheItemFrame? {
let index = self.getFrameIndexImpl(duration)
return self.getFrameImpl(index)
return self.getFrameImpl(index, requestedFormat)
}
}
@ -495,13 +511,11 @@ private final class AnimationCacheItemAccessor {
self.currentDctCoefficients = DctCoefficientsYUVA420(width: width, height: height)
}
func getFrame(index: Int) -> AnimationCacheItemFrame? {
func getFrame(index: Int, requestedFormat: AnimationCacheItemFrame.RequestedFormat) -> AnimationCacheItemFrame? {
guard let frameInfo = self.frameMapping[index] else {
return nil
}
let currentSurface = ImageARGB(width: self.currentYUVASurface.yPlane.width, height: self.currentYUVASurface.yPlane.height)
var frameDataOffset = 0
let frameLength = frameInfo.range.upperBound - frameInfo.range.lowerBound
for i in 0 ..< 4 {
@ -531,9 +545,47 @@ private final class AnimationCacheItemAccessor {
}
self.currentDctCoefficients.idct(dctData: self.currentDctData, target: self.currentYUVASurface)
self.currentYUVASurface.toARGB(target: currentSurface)
return AnimationCacheItemFrame(data: currentSurface.argbPlane.data, range: 0 ..< currentSurface.argbPlane.data.count, format: .rgba(width: currentSurface.argbPlane.width, height: currentSurface.argbPlane.height, bytesPerRow: currentSurface.argbPlane.bytesPerRow), duration: frameInfo.duration)
switch requestedFormat {
case .rgba:
let currentSurface = ImageARGB(width: self.currentYUVASurface.yPlane.width, height: self.currentYUVASurface.yPlane.height)
self.currentYUVASurface.toARGB(target: currentSurface)
return AnimationCacheItemFrame(format: .rgba(data: currentSurface.argbPlane.data, width: currentSurface.argbPlane.width, height: currentSurface.argbPlane.height, bytesPerRow: currentSurface.argbPlane.bytesPerRow), duration: frameInfo.duration)
case .yuva:
let currentYUVASurface = self.currentYUVASurface
self.currentYUVASurface = ImageYUVA420(width: currentYUVASurface.yPlane.width, height: currentYUVASurface.yPlane.height)
return AnimationCacheItemFrame(
format: .yuva(
y: AnimationCacheItemFrame.Plane(
data: currentYUVASurface.yPlane.data,
width: currentYUVASurface.yPlane.width,
height: currentYUVASurface.yPlane.height,
bytesPerRow: currentYUVASurface.yPlane.bytesPerRow
),
u: AnimationCacheItemFrame.Plane(
data: currentYUVASurface.uPlane.data,
width: currentYUVASurface.uPlane.width,
height: currentYUVASurface.uPlane.height,
bytesPerRow: currentYUVASurface.uPlane.bytesPerRow
),
v: AnimationCacheItemFrame.Plane(
data: currentYUVASurface.vPlane.data,
width: currentYUVASurface.vPlane.width,
height: currentYUVASurface.vPlane.height,
bytesPerRow: currentYUVASurface.vPlane.bytesPerRow
),
a: AnimationCacheItemFrame.Plane(
data: currentYUVASurface.aPlane.data,
width: currentYUVASurface.aPlane.width,
height: currentYUVASurface.aPlane.height,
bytesPerRow: currentYUVASurface.aPlane.bytesPerRow
)
),
duration: frameInfo.duration
)
}
}
func getFrameIndex(duration: Double) -> Int {
@ -675,8 +727,8 @@ private func loadItem(path: String) -> AnimationCacheItem? {
let itemAccessor = AnimationCacheItemAccessor(data: data, frameMapping: frameMapping, width: Int(width), height: Int(height), dctQuality: Int(dctQuality))
return AnimationCacheItem(numFrames: Int(numFrames), getFrame: { index in
return itemAccessor.getFrame(index: index)
return AnimationCacheItem(numFrames: Int(numFrames), getFrame: { index, requestedFormat in
return itemAccessor.getFrame(index: index, requestedFormat: requestedFormat)
}, getFrameIndexImpl: { duration in
return itemAccessor.getFrameIndex(duration: duration)
})

View File

@ -101,10 +101,11 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget {
override public func action(forKey event: String) -> CAAction? {
if event == kCAOnOrderIn {
self.isInHierarchyValue = true
self.updatePlayback()
} else if event == kCAOnOrderOut {
self.isInHierarchyValue = false
self.updatePlayback()
}
self.updatePlayback()
return nullAction
}

View File

@ -31,7 +31,10 @@ vertex Varyings multiAnimationVertex(
fragment half4 multiAnimationFragment(
Varyings in[[stage_in]],
texture2d<float, access::sample> texture[[texture(0)]]
texture2d<float, access::sample> textureY[[texture(0)]],
texture2d<float, access::sample> textureU[[texture(1)]],
texture2d<float, access::sample> textureV[[texture(2)]],
texture2d<float, access::sample> textureA[[texture(3)]]
) {
constexpr sampler s(address::clamp_to_edge, filter::linear);
return half4(texture.sample(s, in.texCoord));

View File

@ -64,12 +64,14 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
private final class TextureStoragePool {
let width: Int
let height: Int
let format: TextureStorage.Content.Format
private var items: [TextureStorage.Content] = []
init(width: Int, height: Int) {
init(width: Int, height: Int, format: TextureStorage.Content.Format) {
self.width = width
self.height = height
self.format = format
}
func recycle(content: TextureStorage.Content) {
@ -82,7 +84,7 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
func take(device: MTLDevice) -> TextureStorage.Content? {
if self.items.isEmpty {
guard let content = TextureStorage.Content(device: device, width: self.width, height: self.height) else {
guard let content = TextureStorage.Content(device: device, width: self.width, height: self.height, format: format) else {
return nil
}
return content
@ -93,6 +95,11 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
private final class TextureStorage {
final class Content {
enum Format {
case bgra
case r
}
let buffer: MTLBuffer?
let width: Int
@ -100,9 +107,18 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
let bytesPerRow: Int
let texture: MTLTexture
init?(device: MTLDevice, width: Int, height: Int) {
let bytesPerPixel = 4
let pixelRowAlignment = device.minimumLinearTextureAlignment(for: .bgra8Unorm)
init?(device: MTLDevice, width: Int, height: Int, format: Format) {
let bytesPerPixel: Int
let pixelFormat: MTLPixelFormat
switch format {
case .bgra:
bytesPerPixel = 4
pixelFormat = .bgra8Unorm
case .r:
bytesPerPixel = 1
pixelFormat = .r8Unorm
}
let pixelRowAlignment = device.minimumLinearTextureAlignment(for: pixelFormat)
let bytesPerRow = alignUp(size: width * bytesPerPixel, align: pixelRowAlignment)
self.width = width
@ -112,7 +128,7 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
#if targetEnvironment(simulator)
let textureDescriptor = MTLTextureDescriptor()
textureDescriptor.textureType = .type2D
textureDescriptor.pixelFormat = .bgra8Unorm
textureDescriptor.pixelFormat = pixelFormat
textureDescriptor.width = width
textureDescriptor.height = height
textureDescriptor.usage = [.renderTarget]
@ -130,7 +146,7 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
let textureDescriptor = MTLTextureDescriptor()
textureDescriptor.textureType = .type2D
textureDescriptor.pixelFormat = .bgra8Unorm
textureDescriptor.pixelFormat = pixelFormat
textureDescriptor.width = width
textureDescriptor.height = height
textureDescriptor.usage = [.renderTarget]
@ -144,7 +160,7 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
self.texture = texture
}
func replace(rgbaData: Data, range: Range<Int>, width: Int, height: Int, bytesPerRow: Int) {
func replace(rgbaData: Data, width: Int, height: Int, bytesPerRow: Int) {
if width != self.width || height != self.height {
assert(false, "Image size does not match")
return
@ -153,11 +169,11 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
if let buffer = self.buffer, self.bytesPerRow == bytesPerRow {
rgbaData.withUnsafeBytes { bytes in
let _ = memcpy(buffer.contents(), bytes.baseAddress!.advanced(by: range.lowerBound), bytesPerRow * height)
let _ = memcpy(buffer.contents(), bytes.baseAddress!, bytesPerRow * height)
}
} else {
rgbaData.withUnsafeBytes { bytes in
self.texture.replace(region: region, mipmapLevel: 0, withBytes: bytes.baseAddress!.advanced(by: range.lowerBound), bytesPerRow: bytesPerRow)
self.texture.replace(region: region, mipmapLevel: 0, withBytes: bytes.baseAddress!, bytesPerRow: bytesPerRow)
}
}
}
@ -229,15 +245,26 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
private final class Frame {
let timestamp: Double
let texture: TextureStorage.Content
let textureY: TextureStorage.Content
let textureU: TextureStorage.Content
let textureV: TextureStorage.Content
let textureA: TextureStorage.Content
init(device: MTLDevice, texture: TextureStorage.Content, data: AnimationCacheItemFrame, timestamp: Double) {
init?(device: MTLDevice, textureY: TextureStorage.Content, textureU: TextureStorage.Content, textureV: TextureStorage.Content, textureA: TextureStorage.Content, data: AnimationCacheItemFrame, timestamp: Double) {
self.timestamp = timestamp
self.texture = texture
self.textureY = textureY
self.textureU = textureU
self.textureV = textureV
self.textureA = textureA
switch data.format {
case let .rgba(width, height, bytesPerRow):
texture.replace(rgbaData: data.data, range: data.range, width: width, height: height, bytesPerRow: bytesPerRow)
case .rgba:
return nil
case let .yuva(y, u, v, a):
self.textureY.replace(rgbaData: y.data, width: y.width, height: y.height, bytesPerRow: y.bytesPerRow)
self.textureU.replace(rgbaData: u.data, width: u.width, height: u.height, bytesPerRow: u.bytesPerRow)
self.textureV.replace(rgbaData: v.data, width: v.width, height: v.height, bytesPerRow: v.bytesPerRow)
self.textureA.replace(rgbaData: a.data, width: a.width, height: a.height, bytesPerRow: a.bytesPerRow)
}
}
}
@ -316,11 +343,11 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
self.isPlaying = isPlaying
}
func animationTick(device: MTLDevice, texturePool: TextureStoragePool, advanceTimestamp: Double) -> LoadFrameTask? {
return self.update(device: device, texturePool: texturePool, advanceTimestamp: advanceTimestamp)
func animationTick(device: MTLDevice, texturePoolFullPlane: TextureStoragePool, texturePoolHalfPlane: TextureStoragePool, advanceTimestamp: Double) -> LoadFrameTask? {
return self.update(device: device, texturePoolFullPlane: texturePoolFullPlane, texturePoolHalfPlane: texturePoolHalfPlane, advanceTimestamp: advanceTimestamp)
}
private func update(device: MTLDevice, texturePool: TextureStoragePool, advanceTimestamp: Double?) -> LoadFrameTask? {
private func update(device: MTLDevice, texturePoolFullPlane: TextureStoragePool, texturePoolHalfPlane: TextureStoragePool, advanceTimestamp: Double?) -> LoadFrameTask? {
guard let item = self.item else {
return nil
}
@ -335,7 +362,7 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
self.isLoadingFrame = true
return LoadFrameTask(task: { [weak self] in
let frame = item.getFrame(at: timestamp)
let frame = item.getFrame(at: timestamp, requestedFormat: .rgba)
return {
guard let strongSelf = self else {
@ -343,9 +370,12 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
}
var currentFrame: Frame?
let texture = texturePool.take(device: device)
if let frame = frame, let texture = texture {
currentFrame = Frame(device: device, texture: texture, data: frame, timestamp: timestamp)
let textureY = texturePoolFullPlane.take(device: device)
let textureU = texturePoolHalfPlane.take(device: device)
let textureV = texturePoolHalfPlane.take(device: device)
let textureA = texturePoolFullPlane.take(device: device)
if let frame = frame, let textureY = textureY, let textureU = textureU, let textureV = textureV, let textureA = textureA {
currentFrame = Frame(device: device, textureY: textureY, textureU: textureU, textureV: textureV, textureA: textureA, data: frame, timestamp: timestamp)
}
strongSelf.isLoadingFrame = false
@ -369,7 +399,8 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
private let commandQueue: MTLCommandQueue
private let renderPipelineState: MTLRenderPipelineState
private let texturePool: TextureStoragePool
private let texturePoolFullPlane: TextureStoragePool
private let texturePoolHalfPlane: TextureStoragePool
private let slotCount: Int
private let slotsX: Int
@ -389,8 +420,10 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
self.cellSize = cellSize
self.stateUpdated = stateUpdated
self.slotsX = 16
self.slotsY = 16
let resolutionX = max(1, (1024 / Int(cellSize.width))) * Int(cellSize.width)
let resolutionY = max(1, (1024 / Int(cellSize.height))) * Int(cellSize.height)
self.slotsX = resolutionX / Int(cellSize.width)
self.slotsY = resolutionY / Int(cellSize.height)
let drawableSize = CGSize(width: cellSize.width * CGFloat(self.slotsX), height: cellSize.height * CGFloat(self.slotsY))
self.slotCount = (Int(drawableSize.width) / Int(cellSize.width)) * (Int(drawableSize.height) / Int(cellSize.height))
@ -413,7 +446,8 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
self.renderPipelineState = makePipelineState(device: self.metalDevice, library: defaultLibrary, vertexProgram: "multiAnimationVertex", fragmentProgram: "multiAnimationFragment")!
self.texturePool = TextureStoragePool(width: Int(self.cellSize.width), height: Int(self.cellSize.height))
self.texturePoolFullPlane = TextureStoragePool(width: Int(self.cellSize.width), height: Int(self.cellSize.height), format: .r)
self.texturePoolHalfPlane = TextureStoragePool(width: Int(self.cellSize.width) / 2, height: Int(self.cellSize.height) / 2, format: .r)
super.init()
@ -427,6 +461,7 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
self.pixelFormat = .bgra8Unorm
self.framebufferOnly = true
self.allowsNextDrawableTimeout = true
self.isOpaque = false
}
override public init(layer: Any) {
@ -465,6 +500,23 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
if let itemContext = self.itemContexts[itemId] {
itemContext.targets.append(TargetReference(target))
let deinitIndex = target.deinitCallbacks.add { [weak self, weak itemContext] in
Queue.mainQueue().async {
guard let strongSelf = self, let currentItemContext = strongSelf.itemContexts[itemId], currentItemContext === itemContext else {
return
}
strongSelf.removeTargetFromItemContext(itemId: itemId, itemContext: currentItemContext, targetId: targetId)
}
}
let updateStateIndex = target.updateStateCallbacks.add { [weak itemContext] in
guard let itemContext = itemContext else {
return
}
itemContext.updateIsPlaying()
}
target.contents = self.contents
let slotX = itemContext.slotIndex % self.slotsX
@ -476,22 +528,18 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
self.isPlaying = true
return ActionDisposable { [weak self, weak itemContext] in
return ActionDisposable { [weak self, weak target, weak itemContext] in
Queue.mainQueue().async {
guard let strongSelf = self, let currentItemContext = strongSelf.itemContexts[itemId], currentItemContext === itemContext else {
return
}
if let index = currentItemContext.targets.firstIndex(where: { $0.id == targetId }) {
currentItemContext.targets.remove(at: index)
if currentItemContext.targets.isEmpty {
strongSelf.slotToItemId[currentItemContext.slotIndex] = nil
strongSelf.itemContexts.removeValue(forKey: itemId)
if strongSelf.itemContexts.isEmpty {
strongSelf.isPlaying = false
}
}
if let target = target {
target.deinitCallbacks.remove(deinitIndex)
target.updateStateCallbacks.remove(updateStateIndex)
}
strongSelf.removeTargetFromItemContext(itemId: itemId, itemContext: currentItemContext, targetId: targetId)
}
}
} else {
@ -499,6 +547,21 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
}
}
private func removeTargetFromItemContext(itemId: String, itemContext: ItemContext, targetId: Int64) {
if let index = itemContext.targets.firstIndex(where: { $0.id == targetId }) {
itemContext.targets.remove(at: index)
if itemContext.targets.isEmpty {
self.slotToItemId[itemContext.slotIndex] = nil
self.itemContexts.removeValue(forKey: itemId)
if self.itemContexts.isEmpty {
self.isPlaying = false
}
}
}
}
private func updateIsPlaying() {
var isPlaying = false
for (_, itemContext) in self.itemContexts {
@ -515,7 +578,7 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
var tasks: [LoadFrameTask] = []
for (_, itemContext) in self.itemContexts {
if itemContext.isPlaying {
if let task = itemContext.animationTick(device: self.metalDevice, texturePool: self.texturePool, advanceTimestamp: advanceTimestamp) {
if let task = itemContext.animationTick(device: self.metalDevice, texturePoolFullPlane: self.texturePoolFullPlane, texturePoolHalfPlane: self.texturePoolHalfPlane, advanceTimestamp: advanceTimestamp) {
tasks.append(task)
}
}
@ -580,25 +643,31 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
let contentsRect = CGRect(origin: CGPoint(x: (CGFloat(slotX) * self.cellSize.width) / totalX, y: (CGFloat(slotY) * self.cellSize.height) / totalY), size: CGSize(width: self.cellSize.width / totalX, height: self.cellSize.height / totalY))
vertices[4 * 0 + 0] = Float(contentsRect.minX).remap(fromLow: 0.0, fromHigh: 1.0, toLow: -1.0, toHigh: 1.0)
vertices[4 * 0 + 1] = Float(contentsRect.minY).remap(fromLow: 0.0, fromHigh: 1.0, toLow: -1.0, toHigh: 1.0)
vertices[4 * 1 + 0] = Float(contentsRect.maxX).remap(fromLow: 0.0, fromHigh: 1.0, toLow: -1.0, toHigh: 1.0)
vertices[4 * 1 + 1] = Float(contentsRect.minY).remap(fromLow: 0.0, fromHigh: 1.0, toLow: -1.0, toHigh: 1.0)
vertices[4 * 2 + 0] = Float(contentsRect.minX).remap(fromLow: 0.0, fromHigh: 1.0, toLow: -1.0, toHigh: 1.0)
vertices[4 * 2 + 1] = Float(contentsRect.maxY).remap(fromLow: 0.0, fromHigh: 1.0, toLow: -1.0, toHigh: 1.0)
vertices[4 * 2 + 1] = Float(contentsRect.minY).remap(fromLow: 0.0, fromHigh: 1.0, toLow: -1.0, toHigh: 1.0)
vertices[4 * 3 + 0] = Float(contentsRect.maxX).remap(fromLow: 0.0, fromHigh: 1.0, toLow: -1.0, toHigh: 1.0)
vertices[4 * 3 + 1] = Float(contentsRect.maxY).remap(fromLow: 0.0, fromHigh: 1.0, toLow: -1.0, toHigh: 1.0)
vertices[4 * 3 + 1] = Float(contentsRect.minY).remap(fromLow: 0.0, fromHigh: 1.0, toLow: -1.0, toHigh: 1.0)
vertices[4 * 0 + 0] = Float(contentsRect.minX).remap(fromLow: 0.0, fromHigh: 1.0, toLow: -1.0, toHigh: 1.0)
vertices[4 * 0 + 1] = Float(contentsRect.maxY).remap(fromLow: 0.0, fromHigh: 1.0, toLow: -1.0, toHigh: 1.0)
vertices[4 * 1 + 0] = Float(contentsRect.maxX).remap(fromLow: 0.0, fromHigh: 1.0, toLow: -1.0, toHigh: 1.0)
vertices[4 * 1 + 1] = Float(contentsRect.maxY).remap(fromLow: 0.0, fromHigh: 1.0, toLow: -1.0, toHigh: 1.0)
renderEncoder.setVertexBytes(&vertices, length: 4 * vertices.count, index: 0)
var slotPosition = simd_uint2(UInt32(itemContext.slotIndex % self.slotsX), UInt32(itemContext.slotIndex % self.slotsY))
renderEncoder.setVertexBytes(&slotPosition, length: MemoryLayout<simd_uint2>.size * 2, index: 3)
usedTextures.append(frame.texture)
renderEncoder.setFragmentTexture(frame.texture.texture, index: 0)
usedTextures.append(frame.textureY)
usedTextures.append(frame.textureU)
usedTextures.append(frame.textureV)
usedTextures.append(frame.textureA)
renderEncoder.setFragmentTexture(frame.textureY.texture, index: 0)
renderEncoder.setFragmentTexture(frame.textureU.texture, index: 1)
renderEncoder.setFragmentTexture(frame.textureV.texture, index: 2)
renderEncoder.setFragmentTexture(frame.textureA.texture, index: 3)
renderEncoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4, instanceCount: 1)
}

View File

@ -16,8 +16,8 @@ private var nextRenderTargetId: Int64 = 1
open class MultiAnimationRenderTarget: SimpleLayer {
public let id: Int64
fileprivate let deinitCallbacks = Bag<() -> Void>()
fileprivate let updateStateCallbacks = Bag<() -> Void>()
let deinitCallbacks = Bag<() -> Void>()
let updateStateCallbacks = Bag<() -> Void>()
public final var shouldBeAnimating: Bool = false {
didSet {
@ -69,16 +69,16 @@ private final class FrameGroup {
let timestamp: Double
init?(item: AnimationCacheItem, timestamp: Double) {
guard let firstFrame = item.getFrame(at: timestamp) else {
guard let firstFrame = item.getFrame(at: timestamp, requestedFormat: .rgba) else {
return nil
}
switch firstFrame.format {
case let .rgba(width, height, bytesPerRow):
case let .rgba(data, width, height, bytesPerRow):
let context = DrawingContext(size: CGSize(width: CGFloat(width), height: CGFloat(height)), scale: 1.0, opaque: false, bytesPerRow: bytesPerRow)
firstFrame.data.withUnsafeBytes { bytes -> Void in
memcpy(context.bytes, bytes.baseAddress!.advanced(by: firstFrame.range.lowerBound), height * bytesPerRow)
data.withUnsafeBytes { bytes -> Void in
memcpy(context.bytes, bytes.baseAddress!, height * bytesPerRow)
/*var sourceBuffer = vImage_Buffer()
sourceBuffer.width = UInt(width)
@ -110,6 +110,8 @@ private final class FrameGroup {
self.size = CGSize(width: CGFloat(width), height: CGFloat(height))
self.timestamp = timestamp
self.badgeImage = nil
default:
return nil
}
}
}

View File

@ -303,11 +303,11 @@ class ChatPresentationContext {
})
let animationRenderer: MultiAnimationRenderer
/*if #available(iOS 13.0, *) {
if #available(iOS 13.0, *) {
animationRenderer = MultiAnimationMetalRendererImpl()
} else {*/
} else {
animationRenderer = MultiAnimationRendererImpl()
//}
}
self.animationRenderer = animationRenderer
}