mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 03:20:48 +00:00
Temp
This commit is contained in:
parent
74b5d4998c
commit
c5d3e5e294
@ -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)
|
||||
})
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user