mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Temp
This commit is contained in:
parent
d0b2c85d70
commit
42293bcf05
@ -20,6 +20,11 @@ public final class QueueLocalObject<T: AnyObject> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func unsafeGet() -> T? {
|
||||||
|
assert(self.queue.isCurrent())
|
||||||
|
return self.valueRef?.takeUnretainedValue()
|
||||||
|
}
|
||||||
|
|
||||||
public func with(_ f: @escaping (T) -> Void) {
|
public func with(_ f: @escaping (T) -> Void) {
|
||||||
self.queue.async {
|
self.queue.async {
|
||||||
if let valueRef = self.valueRef {
|
if let valueRef = self.valueRef {
|
||||||
|
@ -5,10 +5,17 @@ import CryptoUtils
|
|||||||
import ManagedFile
|
import ManagedFile
|
||||||
import Compression
|
import Compression
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
public final class AnimationCacheItemFrame {
|
public final class AnimationCacheItemFrame {
|
||||||
public enum RequestedFormat {
|
public enum RequestedFormat {
|
||||||
case rgba
|
case rgba
|
||||||
case yuva
|
case yuva(bytesPerRow: Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class Plane {
|
public final class Plane {
|
||||||
@ -279,7 +286,7 @@ private final class AnimationCacheItemWriterImpl: AnimationCacheItemWriter {
|
|||||||
} else {
|
} else {
|
||||||
isFirstFrame = true
|
isFirstFrame = true
|
||||||
|
|
||||||
surface = ImageARGB(width: width, height: height)
|
surface = ImageARGB(width: width, height: height, bytesPerRow: alignUp(size: width * 4, align: 32))
|
||||||
self.currentSurface = surface
|
self.currentSurface = surface
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,7 +299,7 @@ private final class AnimationCacheItemWriterImpl: AnimationCacheItemWriter {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
yuvaSurface = ImageYUVA420(width: width, height: height)
|
yuvaSurface = ImageYUVA420(width: width, height: height, bytesPerRow: nil)
|
||||||
self.currentYUVASurface = yuvaSurface
|
self.currentYUVASurface = yuvaSurface
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -484,7 +491,7 @@ private final class AnimationCacheItemAccessor {
|
|||||||
private let durationMapping: [Double]
|
private let durationMapping: [Double]
|
||||||
private let totalDuration: Double
|
private let totalDuration: Double
|
||||||
|
|
||||||
private var currentYUVASurface: ImageYUVA420
|
private var currentYUVASurface: ImageYUVA420?
|
||||||
private var currentDctData: DctData
|
private var currentDctData: DctData
|
||||||
private var currentDctCoefficients: DctCoefficientsYUVA420
|
private var currentDctCoefficients: DctCoefficientsYUVA420
|
||||||
|
|
||||||
@ -506,7 +513,6 @@ private final class AnimationCacheItemAccessor {
|
|||||||
self.durationMapping = durationMapping
|
self.durationMapping = durationMapping
|
||||||
self.totalDuration = totalDuration
|
self.totalDuration = totalDuration
|
||||||
|
|
||||||
self.currentYUVASurface = ImageYUVA420(width: width, height: height)
|
|
||||||
self.currentDctData = DctData(quality: dctQuality)
|
self.currentDctData = DctData(quality: dctQuality)
|
||||||
self.currentDctCoefficients = DctCoefficientsYUVA420(width: width, height: height)
|
self.currentDctCoefficients = DctCoefficientsYUVA420(width: width, height: height)
|
||||||
}
|
}
|
||||||
@ -544,43 +550,53 @@ private final class AnimationCacheItemAccessor {
|
|||||||
frameDataOffset += dctPlane.data.count
|
frameDataOffset += dctPlane.data.count
|
||||||
}
|
}
|
||||||
|
|
||||||
self.currentDctCoefficients.idct(dctData: self.currentDctData, target: self.currentYUVASurface)
|
let yuvaSurface: ImageYUVA420
|
||||||
|
switch requestedFormat {
|
||||||
|
case .rgba:
|
||||||
|
if let currentYUVASurface = self.currentYUVASurface {
|
||||||
|
yuvaSurface = currentYUVASurface
|
||||||
|
} else {
|
||||||
|
yuvaSurface = ImageYUVA420(width: self.currentDctCoefficients.yPlane.width, height: self.currentDctCoefficients.yPlane.height, bytesPerRow: nil)
|
||||||
|
}
|
||||||
|
case let .yuva(preferredBytesPerRow):
|
||||||
|
yuvaSurface = ImageYUVA420(width: self.currentDctCoefficients.yPlane.width, height: self.currentDctCoefficients.yPlane.height, bytesPerRow: preferredBytesPerRow)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.currentDctCoefficients.idct(dctData: self.currentDctData, target: yuvaSurface)
|
||||||
|
|
||||||
switch requestedFormat {
|
switch requestedFormat {
|
||||||
case .rgba:
|
case .rgba:
|
||||||
let currentSurface = ImageARGB(width: self.currentYUVASurface.yPlane.width, height: self.currentYUVASurface.yPlane.height)
|
let currentSurface = ImageARGB(width: yuvaSurface.yPlane.width, height: yuvaSurface.yPlane.height, bytesPerRow: alignUp(size: yuvaSurface.yPlane.width * 4, align: 32))
|
||||||
self.currentYUVASurface.toARGB(target: currentSurface)
|
yuvaSurface.toARGB(target: currentSurface)
|
||||||
|
self.currentYUVASurface = yuvaSurface
|
||||||
|
|
||||||
return AnimationCacheItemFrame(format: .rgba(data: currentSurface.argbPlane.data, width: currentSurface.argbPlane.width, height: currentSurface.argbPlane.height, bytesPerRow: currentSurface.argbPlane.bytesPerRow), duration: frameInfo.duration)
|
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:
|
case .yuva:
|
||||||
let currentYUVASurface = self.currentYUVASurface
|
|
||||||
self.currentYUVASurface = ImageYUVA420(width: currentYUVASurface.yPlane.width, height: currentYUVASurface.yPlane.height)
|
|
||||||
|
|
||||||
return AnimationCacheItemFrame(
|
return AnimationCacheItemFrame(
|
||||||
format: .yuva(
|
format: .yuva(
|
||||||
y: AnimationCacheItemFrame.Plane(
|
y: AnimationCacheItemFrame.Plane(
|
||||||
data: currentYUVASurface.yPlane.data,
|
data: yuvaSurface.yPlane.data,
|
||||||
width: currentYUVASurface.yPlane.width,
|
width: yuvaSurface.yPlane.width,
|
||||||
height: currentYUVASurface.yPlane.height,
|
height: yuvaSurface.yPlane.height,
|
||||||
bytesPerRow: currentYUVASurface.yPlane.bytesPerRow
|
bytesPerRow: yuvaSurface.yPlane.bytesPerRow
|
||||||
),
|
),
|
||||||
u: AnimationCacheItemFrame.Plane(
|
u: AnimationCacheItemFrame.Plane(
|
||||||
data: currentYUVASurface.uPlane.data,
|
data: yuvaSurface.uPlane.data,
|
||||||
width: currentYUVASurface.uPlane.width,
|
width: yuvaSurface.uPlane.width,
|
||||||
height: currentYUVASurface.uPlane.height,
|
height: yuvaSurface.uPlane.height,
|
||||||
bytesPerRow: currentYUVASurface.uPlane.bytesPerRow
|
bytesPerRow: yuvaSurface.uPlane.bytesPerRow
|
||||||
),
|
),
|
||||||
v: AnimationCacheItemFrame.Plane(
|
v: AnimationCacheItemFrame.Plane(
|
||||||
data: currentYUVASurface.vPlane.data,
|
data: yuvaSurface.vPlane.data,
|
||||||
width: currentYUVASurface.vPlane.width,
|
width: yuvaSurface.vPlane.width,
|
||||||
height: currentYUVASurface.vPlane.height,
|
height: yuvaSurface.vPlane.height,
|
||||||
bytesPerRow: currentYUVASurface.vPlane.bytesPerRow
|
bytesPerRow: yuvaSurface.vPlane.bytesPerRow
|
||||||
),
|
),
|
||||||
a: AnimationCacheItemFrame.Plane(
|
a: AnimationCacheItemFrame.Plane(
|
||||||
data: currentYUVASurface.aPlane.data,
|
data: yuvaSurface.aPlane.data,
|
||||||
width: currentYUVASurface.aPlane.width,
|
width: yuvaSurface.aPlane.width,
|
||||||
height: currentYUVASurface.aPlane.height,
|
height: yuvaSurface.aPlane.height,
|
||||||
bytesPerRow: currentYUVASurface.aPlane.bytesPerRow
|
bytesPerRow: yuvaSurface.aPlane.bytesPerRow
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
duration: frameInfo.duration
|
duration: frameInfo.duration
|
||||||
|
@ -9,20 +9,20 @@ final class ImagePlane {
|
|||||||
let components: Int
|
let components: Int
|
||||||
var data: Data
|
var data: Data
|
||||||
|
|
||||||
init(width: Int, height: Int, components: Int) {
|
init(width: Int, height: Int, components: Int, bytesPerRow: Int?) {
|
||||||
self.width = width
|
self.width = width
|
||||||
self.height = height
|
self.height = height
|
||||||
self.bytesPerRow = width * components
|
self.bytesPerRow = bytesPerRow ?? (width * components)
|
||||||
self.components = components
|
self.components = components
|
||||||
self.data = Data(count: width * components * height)
|
self.data = Data(count: self.bytesPerRow * height)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class ImageARGB {
|
final class ImageARGB {
|
||||||
let argbPlane: ImagePlane
|
let argbPlane: ImagePlane
|
||||||
|
|
||||||
init(width: Int, height: Int) {
|
init(width: Int, height: Int, bytesPerRow: Int?) {
|
||||||
self.argbPlane = ImagePlane(width: width, height: height, components: 4)
|
self.argbPlane = ImagePlane(width: width, height: height, components: 4, bytesPerRow: bytesPerRow)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,11 +32,11 @@ final class ImageYUVA420 {
|
|||||||
let vPlane: ImagePlane
|
let vPlane: ImagePlane
|
||||||
let aPlane: ImagePlane
|
let aPlane: ImagePlane
|
||||||
|
|
||||||
init(width: Int, height: Int) {
|
init(width: Int, height: Int, bytesPerRow: Int?) {
|
||||||
self.yPlane = ImagePlane(width: width, height: height, components: 1)
|
self.yPlane = ImagePlane(width: width, height: height, components: 1, bytesPerRow: bytesPerRow)
|
||||||
self.uPlane = ImagePlane(width: width / 2, height: height / 2, components: 1)
|
self.uPlane = ImagePlane(width: width / 2, height: height / 2, components: 1, bytesPerRow: bytesPerRow)
|
||||||
self.vPlane = ImagePlane(width: width / 2, height: height / 2, components: 1)
|
self.vPlane = ImagePlane(width: width / 2, height: height / 2, components: 1, bytesPerRow: bytesPerRow)
|
||||||
self.aPlane = ImagePlane(width: width, height: height, components: 1)
|
self.aPlane = ImagePlane(width: width, height: height, components: 1, bytesPerRow: bytesPerRow)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,8 +92,8 @@ extension ImageARGB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func toYUVA420() -> ImageYUVA420 {
|
func toYUVA420(bytesPerRow: Int?) -> ImageYUVA420 {
|
||||||
let resultImage = ImageYUVA420(width: self.argbPlane.width, height: self.argbPlane.height)
|
let resultImage = ImageYUVA420(width: self.argbPlane.width, height: self.argbPlane.height, bytesPerRow: bytesPerRow)
|
||||||
self.toYUVA420(target: resultImage)
|
self.toYUVA420(target: resultImage)
|
||||||
return resultImage
|
return resultImage
|
||||||
}
|
}
|
||||||
@ -125,8 +125,8 @@ extension ImageYUVA420 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func toARGB() -> ImageARGB {
|
func toARGB(bytesPerRow: Int?) -> ImageARGB {
|
||||||
let resultImage = ImageARGB(width: self.yPlane.width, height: self.yPlane.height)
|
let resultImage = ImageARGB(width: self.yPlane.width, height: self.yPlane.height, bytesPerRow: bytesPerRow)
|
||||||
self.toARGB(target: resultImage)
|
self.toARGB(target: resultImage)
|
||||||
return resultImage
|
return resultImage
|
||||||
}
|
}
|
||||||
@ -215,14 +215,14 @@ extension DctCoefficientsYUVA420 {
|
|||||||
targetPlane.data.withUnsafeMutableBytes { bytes in
|
targetPlane.data.withUnsafeMutableBytes { bytes in
|
||||||
let pixels = bytes.baseAddress!.assumingMemoryBound(to: UInt8.self)
|
let pixels = bytes.baseAddress!.assumingMemoryBound(to: UInt8.self)
|
||||||
|
|
||||||
dctData.dct.inverse(withCoefficients: coefficients, pixels: pixels, width: sourcePlane.width, height: sourcePlane.height, coefficientsPerRow: targetPlane.width, bytesPerRow: targetPlane.width)
|
dctData.dct.inverse(withCoefficients: coefficients, pixels: pixels, width: sourcePlane.width, height: sourcePlane.height, coefficientsPerRow: targetPlane.width, bytesPerRow: targetPlane.bytesPerRow)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func idct(dctData: DctData) -> ImageYUVA420 {
|
func idct(dctData: DctData, bytesPerRow: Int?) -> ImageYUVA420 {
|
||||||
let resultImage = ImageYUVA420(width: self.yPlane.width, height: self.yPlane.height)
|
let resultImage = ImageYUVA420(width: self.yPlane.width, height: self.yPlane.height, bytesPerRow: bytesPerRow)
|
||||||
self.idct(dctData: dctData, target: resultImage)
|
self.idct(dctData: dctData, target: resultImage)
|
||||||
return resultImage
|
return resultImage
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,6 @@ vertex Varyings multiAnimationVertex(
|
|||||||
Varyings out;
|
Varyings out;
|
||||||
constant Vertex &v = verticies[vid];
|
constant Vertex &v = verticies[vid];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
out.position = float4(float2(v.position), 0.0, 1.0);
|
out.position = float4(float2(v.position), 0.0, 1.0);
|
||||||
out.texCoord = v.texCoord;
|
out.texCoord = v.texCoord;
|
||||||
|
|
||||||
@ -37,5 +35,12 @@ fragment half4 multiAnimationFragment(
|
|||||||
texture2d<float, access::sample> textureA[[texture(3)]]
|
texture2d<float, access::sample> textureA[[texture(3)]]
|
||||||
) {
|
) {
|
||||||
constexpr sampler s(address::clamp_to_edge, filter::linear);
|
constexpr sampler s(address::clamp_to_edge, filter::linear);
|
||||||
return half4(texture.sample(s, in.texCoord));
|
|
||||||
|
half y = textureY.sample(s, in.texCoord).r;
|
||||||
|
half u = textureU.sample(s, in.texCoord).r - 0.5;
|
||||||
|
half v = textureV.sample(s, in.texCoord).r - 0.5;
|
||||||
|
half a = textureA.sample(s, in.texCoord).r;
|
||||||
|
|
||||||
|
half4 out = half4(1.5748 * v + y, -0.1873 * v + y, 1.8556 * u + y, a);
|
||||||
|
return half4(out.b, out.g, out.r, out.a);
|
||||||
}
|
}
|
||||||
|
@ -62,34 +62,74 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final class TextureStoragePool {
|
private final class TextureStoragePool {
|
||||||
|
struct Parameters {
|
||||||
let width: Int
|
let width: Int
|
||||||
let height: Int
|
let height: Int
|
||||||
let format: TextureStorage.Content.Format
|
let format: TextureStorage.Content.Format
|
||||||
|
}
|
||||||
|
|
||||||
|
let parameters: Parameters
|
||||||
private var items: [TextureStorage.Content] = []
|
private var items: [TextureStorage.Content] = []
|
||||||
|
private var cleanupTimer: Foundation.Timer?
|
||||||
|
private var lastTakeTimestamp: Double = 0.0
|
||||||
|
|
||||||
init(width: Int, height: Int, format: TextureStorage.Content.Format) {
|
init(width: Int, height: Int, format: TextureStorage.Content.Format) {
|
||||||
self.width = width
|
self.parameters = Parameters(width: width, height: height, format: format)
|
||||||
self.height = height
|
|
||||||
self.format = format
|
let cleanupTimer = Foundation.Timer(timeInterval: 2.0, repeats: true, block: { [weak self] _ in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.collect()
|
||||||
|
})
|
||||||
|
self.cleanupTimer = cleanupTimer
|
||||||
|
RunLoop.main.add(cleanupTimer, forMode: .common)
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.cleanupTimer?.invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func collect() {
|
||||||
|
let timestamp = CFAbsoluteTimeGetCurrent()
|
||||||
|
if timestamp - self.lastTakeTimestamp < 1.0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if self.items.count > 32 {
|
||||||
|
autoreleasepool {
|
||||||
|
var remainingItems: [Unmanaged<TextureStorage.Content>] = []
|
||||||
|
while self.items.count > 32 {
|
||||||
|
let item = self.items.removeLast()
|
||||||
|
remainingItems.append(Unmanaged.passRetained(item))
|
||||||
|
}
|
||||||
|
DispatchQueue.global().async {
|
||||||
|
autoreleasepool {
|
||||||
|
for item in remainingItems {
|
||||||
|
item.release()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func recycle(content: TextureStorage.Content) {
|
func recycle(content: TextureStorage.Content) {
|
||||||
if self.items.count < 4 {
|
|
||||||
self.items.append(content)
|
self.items.append(content)
|
||||||
} else {
|
|
||||||
print("Warning: over-recycling texture storage")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func take(device: MTLDevice) -> TextureStorage.Content? {
|
func take() -> TextureStorage? {
|
||||||
if self.items.isEmpty {
|
if self.items.isEmpty {
|
||||||
guard let content = TextureStorage.Content(device: device, width: self.width, height: self.height, format: format) else {
|
self.lastTakeTimestamp = CFAbsoluteTimeGetCurrent()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return content
|
return TextureStorage(pool: self, content: self.items.removeLast())
|
||||||
}
|
}
|
||||||
return self.items.removeLast()
|
|
||||||
|
static func takeNew(device: MTLDevice, parameters: Parameters, pool: TextureStoragePool) -> TextureStorage? {
|
||||||
|
guard let content = TextureStorage.Content(device: device, width: parameters.width, height: parameters.height, format: parameters.format) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return TextureStorage(pool: pool, content: content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,6 +147,17 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
|
|||||||
let bytesPerRow: Int
|
let bytesPerRow: Int
|
||||||
let texture: MTLTexture
|
let texture: MTLTexture
|
||||||
|
|
||||||
|
static func rowAlignment(device: MTLDevice, format: Format) -> Int {
|
||||||
|
let pixelFormat: MTLPixelFormat
|
||||||
|
switch format {
|
||||||
|
case .bgra:
|
||||||
|
pixelFormat = .bgra8Unorm
|
||||||
|
case .r:
|
||||||
|
pixelFormat = .r8Unorm
|
||||||
|
}
|
||||||
|
return device.minimumLinearTextureAlignment(for: pixelFormat)
|
||||||
|
}
|
||||||
|
|
||||||
init?(device: MTLDevice, width: Int, height: Int, format: Format) {
|
init?(device: MTLDevice, width: Int, height: Int, format: Format) {
|
||||||
let bytesPerPixel: Int
|
let bytesPerPixel: Int
|
||||||
let pixelFormat: MTLPixelFormat
|
let pixelFormat: MTLPixelFormat
|
||||||
@ -118,7 +169,7 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
|
|||||||
bytesPerPixel = 1
|
bytesPerPixel = 1
|
||||||
pixelFormat = .r8Unorm
|
pixelFormat = .r8Unorm
|
||||||
}
|
}
|
||||||
let pixelRowAlignment = device.minimumLinearTextureAlignment(for: pixelFormat)
|
let pixelRowAlignment = Content.rowAlignment(device: device, format: format)
|
||||||
let bytesPerRow = alignUp(size: width * bytesPerPixel, align: pixelRowAlignment)
|
let bytesPerRow = alignUp(size: width * bytesPerPixel, align: pixelRowAlignment)
|
||||||
|
|
||||||
self.width = width
|
self.width = width
|
||||||
@ -131,7 +182,7 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
|
|||||||
textureDescriptor.pixelFormat = pixelFormat
|
textureDescriptor.pixelFormat = pixelFormat
|
||||||
textureDescriptor.width = width
|
textureDescriptor.width = width
|
||||||
textureDescriptor.height = height
|
textureDescriptor.height = height
|
||||||
textureDescriptor.usage = [.renderTarget]
|
textureDescriptor.usage = [.shaderRead]
|
||||||
textureDescriptor.storageMode = .shared
|
textureDescriptor.storageMode = .shared
|
||||||
|
|
||||||
guard let texture = device.makeTexture(descriptor: textureDescriptor) else {
|
guard let texture = device.makeTexture(descriptor: textureDescriptor) else {
|
||||||
@ -149,7 +200,7 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
|
|||||||
textureDescriptor.pixelFormat = pixelFormat
|
textureDescriptor.pixelFormat = pixelFormat
|
||||||
textureDescriptor.width = width
|
textureDescriptor.width = width
|
||||||
textureDescriptor.height = height
|
textureDescriptor.height = height
|
||||||
textureDescriptor.usage = [.renderTarget]
|
textureDescriptor.usage = [.shaderRead]
|
||||||
textureDescriptor.storageMode = buffer.storageMode
|
textureDescriptor.storageMode = buffer.storageMode
|
||||||
|
|
||||||
guard let texture = buffer.makeTexture(descriptor: textureDescriptor, offset: 0, bytesPerRow: bytesPerRow) else {
|
guard let texture = buffer.makeTexture(descriptor: textureDescriptor, offset: 0, bytesPerRow: bytesPerRow) else {
|
||||||
@ -168,6 +219,8 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
|
|||||||
let region = MTLRegion(origin: MTLOrigin(x: 0, y: 0, z: 0), size: MTLSize(width: width, height: height, depth: 1))
|
let region = MTLRegion(origin: MTLOrigin(x: 0, y: 0, z: 0), size: MTLSize(width: width, height: height, depth: 1))
|
||||||
|
|
||||||
if let buffer = self.buffer, self.bytesPerRow == bytesPerRow {
|
if let buffer = self.buffer, self.bytesPerRow == bytesPerRow {
|
||||||
|
assert(bytesPerRow * height <= rgbaData.count)
|
||||||
|
|
||||||
rgbaData.withUnsafeBytes { bytes in
|
rgbaData.withUnsafeBytes { bytes in
|
||||||
let _ = memcpy(buffer.contents(), bytes.baseAddress!, bytesPerRow * height)
|
let _ = memcpy(buffer.contents(), bytes.baseAddress!, bytesPerRow * height)
|
||||||
}
|
}
|
||||||
@ -193,64 +246,16 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
|
|||||||
self.pool?.recycle(content: self.content)
|
self.pool?.recycle(content: self.content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*func createCGImage() -> CGImage? {
|
|
||||||
if self.isInvalidated {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
self.isInvalidated = true
|
|
||||||
|
|
||||||
#if targetEnvironment(simulator)
|
|
||||||
guard let data = NSMutableData(capacity: self.content.bytesPerRow * self.content.height) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
data.length = self.content.bytesPerRow * self.content.height
|
|
||||||
self.content.texture.getBytes(data.mutableBytes, bytesPerRow: self.content.bytesPerRow, bytesPerImage: self.content.bytesPerRow * self.content.height, from: MTLRegion(origin: MTLOrigin(), size: MTLSize(width: self.content.width, height: self.content.height, depth: 1)), mipmapLevel: 0, slice: 0)
|
|
||||||
|
|
||||||
guard let dataProvider = CGDataProvider(data: data as CFData) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
let content = self.content
|
|
||||||
let pool = self.pool
|
|
||||||
guard let dataProvider = CGDataProvider(data: Data(bytesNoCopy: self.content.buffer.contents(), count: self.content.buffer.length, deallocator: .custom { [weak pool] _, _ in
|
|
||||||
guard let pool = pool else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pool.recycle(content: content)
|
|
||||||
}) as CFData) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
guard let image = CGImage(
|
|
||||||
width: Int(self.content.width),
|
|
||||||
height: Int(self.content.height),
|
|
||||||
bitsPerComponent: 8,
|
|
||||||
bitsPerPixel: 8 * 4,
|
|
||||||
bytesPerRow: self.content.bytesPerRow,
|
|
||||||
space: DeviceGraphicsContextSettings.shared.colorSpace,
|
|
||||||
bitmapInfo: DeviceGraphicsContextSettings.shared.transparentBitmapInfo,
|
|
||||||
provider: dataProvider,
|
|
||||||
decode: nil,
|
|
||||||
shouldInterpolate: true,
|
|
||||||
intent: .defaultIntent
|
|
||||||
) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return image
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class Frame {
|
private final class Frame {
|
||||||
let timestamp: Double
|
let timestamp: Double
|
||||||
let textureY: TextureStorage.Content
|
let textureY: TextureStorage
|
||||||
let textureU: TextureStorage.Content
|
let textureU: TextureStorage
|
||||||
let textureV: TextureStorage.Content
|
let textureV: TextureStorage
|
||||||
let textureA: TextureStorage.Content
|
let textureA: TextureStorage
|
||||||
|
|
||||||
init?(device: MTLDevice, textureY: TextureStorage.Content, textureU: TextureStorage.Content, textureV: TextureStorage.Content, textureA: TextureStorage.Content, data: AnimationCacheItemFrame, timestamp: Double) {
|
init?(device: MTLDevice, textureY: TextureStorage, textureU: TextureStorage, textureV: TextureStorage, textureA: TextureStorage, data: AnimationCacheItemFrame, timestamp: Double) {
|
||||||
self.timestamp = timestamp
|
self.timestamp = timestamp
|
||||||
self.textureY = textureY
|
self.textureY = textureY
|
||||||
self.textureU = textureU
|
self.textureU = textureU
|
||||||
@ -261,10 +266,10 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
|
|||||||
case .rgba:
|
case .rgba:
|
||||||
return nil
|
return nil
|
||||||
case let .yuva(y, u, v, a):
|
case let .yuva(y, u, v, a):
|
||||||
self.textureY.replace(rgbaData: y.data, width: y.width, height: y.height, bytesPerRow: y.bytesPerRow)
|
self.textureY.content.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.textureU.content.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.textureV.content.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)
|
self.textureA.content.replace(rgbaData: a.data, width: a.width, height: a.height, bytesPerRow: a.bytesPerRow)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -292,9 +297,11 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
|
|||||||
|
|
||||||
var targets: [TargetReference] = []
|
var targets: [TargetReference] = []
|
||||||
var slotIndex: Int
|
var slotIndex: Int
|
||||||
|
private let preferredBytesPerRow: Int
|
||||||
|
|
||||||
init(slotIndex: Int, cache: AnimationCache, itemId: String, size: CGSize, fetch: @escaping (CGSize, AnimationCacheItemWriter) -> Disposable, stateUpdated: @escaping () -> Void) {
|
init(slotIndex: Int, preferredBytesPerRow: Int, cache: AnimationCache, itemId: String, size: CGSize, fetch: @escaping (CGSize, AnimationCacheItemWriter) -> Disposable, stateUpdated: @escaping () -> Void) {
|
||||||
self.slotIndex = slotIndex
|
self.slotIndex = slotIndex
|
||||||
|
self.preferredBytesPerRow = preferredBytesPerRow
|
||||||
self.cache = cache
|
self.cache = cache
|
||||||
self.stateUpdated = stateUpdated
|
self.stateUpdated = stateUpdated
|
||||||
|
|
||||||
@ -361,23 +368,33 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
|
|||||||
} else if !self.isLoadingFrame {
|
} else if !self.isLoadingFrame {
|
||||||
self.isLoadingFrame = true
|
self.isLoadingFrame = true
|
||||||
|
|
||||||
|
let fullParameters = texturePoolFullPlane.parameters
|
||||||
|
let halfParameters = texturePoolHalfPlane.parameters
|
||||||
|
|
||||||
|
let readyTextureY = texturePoolFullPlane.take()
|
||||||
|
let readyTextureU = texturePoolHalfPlane.take()
|
||||||
|
let readyTextureV = texturePoolHalfPlane.take()
|
||||||
|
let readyTextureA = texturePoolFullPlane.take()
|
||||||
|
let preferredBytesPerRow = self.preferredBytesPerRow
|
||||||
|
|
||||||
return LoadFrameTask(task: { [weak self] in
|
return LoadFrameTask(task: { [weak self] in
|
||||||
let frame = item.getFrame(at: timestamp, requestedFormat: .rgba)
|
let frame = item.getFrame(at: timestamp, requestedFormat: .yuva(bytesPerRow: preferredBytesPerRow))
|
||||||
|
|
||||||
|
let textureY = readyTextureY ?? TextureStoragePool.takeNew(device: device, parameters: fullParameters, pool: texturePoolFullPlane)
|
||||||
|
let textureU = readyTextureU ?? TextureStoragePool.takeNew(device: device, parameters: halfParameters, pool: texturePoolHalfPlane)
|
||||||
|
let textureV = readyTextureV ?? TextureStoragePool.takeNew(device: device, parameters: halfParameters, pool: texturePoolHalfPlane)
|
||||||
|
let textureA = readyTextureA ?? TextureStoragePool.takeNew(device: device, parameters: fullParameters, pool: texturePoolFullPlane)
|
||||||
|
|
||||||
|
var currentFrame: Frame?
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentFrame: Frame?
|
|
||||||
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
|
strongSelf.isLoadingFrame = false
|
||||||
|
|
||||||
if let currentFrame = currentFrame {
|
if let currentFrame = currentFrame {
|
||||||
@ -402,6 +419,8 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
|
|||||||
private let texturePoolFullPlane: TextureStoragePool
|
private let texturePoolFullPlane: TextureStoragePool
|
||||||
private let texturePoolHalfPlane: TextureStoragePool
|
private let texturePoolHalfPlane: TextureStoragePool
|
||||||
|
|
||||||
|
private let preferredBytesPerRow: Int
|
||||||
|
|
||||||
private let slotCount: Int
|
private let slotCount: Int
|
||||||
private let slotsX: Int
|
private let slotsX: Int
|
||||||
private let slotsY: Int
|
private let slotsY: Int
|
||||||
@ -449,6 +468,8 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
|
|||||||
self.texturePoolFullPlane = TextureStoragePool(width: Int(self.cellSize.width), height: Int(self.cellSize.height), format: .r)
|
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)
|
self.texturePoolHalfPlane = TextureStoragePool(width: Int(self.cellSize.width) / 2, height: Int(self.cellSize.height) / 2, format: .r)
|
||||||
|
|
||||||
|
self.preferredBytesPerRow = alignUp(size: Int(self.cellSize.width), align: TextureStorage.Content.rowAlignment(device: self.metalDevice, format: .r))
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.device = self.metalDevice
|
self.device = self.metalDevice
|
||||||
@ -487,7 +508,7 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
|
|||||||
for i in 0 ..< self.slotCount {
|
for i in 0 ..< self.slotCount {
|
||||||
if self.slotToItemId[i] == nil {
|
if self.slotToItemId[i] == nil {
|
||||||
self.slotToItemId[i] = itemId
|
self.slotToItemId[i] = itemId
|
||||||
self.itemContexts[itemId] = ItemContext(slotIndex: i, cache: cache, itemId: itemId, size: size, fetch: fetch, stateUpdated: { [weak self] in
|
self.itemContexts[itemId] = ItemContext(slotIndex: i, preferredBytesPerRow: self.preferredBytesPerRow, cache: cache, itemId: itemId, size: size, fetch: fetch, stateUpdated: { [weak self] in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -588,10 +609,15 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func redraw() {
|
func redraw() {
|
||||||
guard let commandBuffer = self.commandQueue.makeCommandBuffer() else {
|
guard let drawable = self.nextDrawable() else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard let drawable = self.nextDrawable() else {
|
|
||||||
|
let commandQueue = self.commandQueue
|
||||||
|
let renderPipelineState = self.renderPipelineState
|
||||||
|
let cellSize = self.cellSize
|
||||||
|
|
||||||
|
guard let commandBuffer = commandQueue.makeCommandBuffer() else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -614,7 +640,7 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var usedTextures: [MultiAnimationMetalRendererImpl.TextureStorage.Content] = []
|
var usedTextures: [Unmanaged<MultiAnimationMetalRendererImpl.TextureStorage>] = []
|
||||||
|
|
||||||
var vertices: [Float] = [
|
var vertices: [Float] = [
|
||||||
-1.0, -1.0, 0.0, 0.0,
|
-1.0, -1.0, 0.0, 0.0,
|
||||||
@ -623,12 +649,12 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
|
|||||||
1.0, 1.0, 1.0, 1.0
|
1.0, 1.0, 1.0, 1.0
|
||||||
]
|
]
|
||||||
|
|
||||||
renderEncoder.setRenderPipelineState(self.renderPipelineState)
|
renderEncoder.setRenderPipelineState(renderPipelineState)
|
||||||
|
|
||||||
var resolution = simd_uint2(UInt32(drawable.texture.width), UInt32(drawable.texture.height))
|
var resolution = simd_uint2(UInt32(drawable.texture.width), UInt32(drawable.texture.height))
|
||||||
renderEncoder.setVertexBytes(&resolution, length: MemoryLayout<simd_uint2>.size * 2, index: 1)
|
renderEncoder.setVertexBytes(&resolution, length: MemoryLayout<simd_uint2>.size * 2, index: 1)
|
||||||
|
|
||||||
var slotSize = simd_uint2(UInt32(self.cellSize.width), UInt32(self.cellSize.height))
|
var slotSize = simd_uint2(UInt32(cellSize.width), UInt32(cellSize.height))
|
||||||
renderEncoder.setVertexBytes(&slotSize, length: MemoryLayout<simd_uint2>.size * 2, index: 2)
|
renderEncoder.setVertexBytes(&slotSize, length: MemoryLayout<simd_uint2>.size * 2, index: 2)
|
||||||
|
|
||||||
for (_, itemContext) in self.itemContexts {
|
for (_, itemContext) in self.itemContexts {
|
||||||
@ -660,14 +686,14 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
|
|||||||
var slotPosition = simd_uint2(UInt32(itemContext.slotIndex % self.slotsX), UInt32(itemContext.slotIndex % self.slotsY))
|
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)
|
renderEncoder.setVertexBytes(&slotPosition, length: MemoryLayout<simd_uint2>.size * 2, index: 3)
|
||||||
|
|
||||||
usedTextures.append(frame.textureY)
|
usedTextures.append(Unmanaged.passRetained(frame.textureY))
|
||||||
usedTextures.append(frame.textureU)
|
usedTextures.append(Unmanaged.passRetained(frame.textureU))
|
||||||
usedTextures.append(frame.textureV)
|
usedTextures.append(Unmanaged.passRetained(frame.textureV))
|
||||||
usedTextures.append(frame.textureA)
|
usedTextures.append(Unmanaged.passRetained(frame.textureA))
|
||||||
renderEncoder.setFragmentTexture(frame.textureY.texture, index: 0)
|
renderEncoder.setFragmentTexture(frame.textureY.content.texture, index: 0)
|
||||||
renderEncoder.setFragmentTexture(frame.textureU.texture, index: 1)
|
renderEncoder.setFragmentTexture(frame.textureU.content.texture, index: 1)
|
||||||
renderEncoder.setFragmentTexture(frame.textureV.texture, index: 2)
|
renderEncoder.setFragmentTexture(frame.textureV.content.texture, index: 2)
|
||||||
renderEncoder.setFragmentTexture(frame.textureA.texture, index: 3)
|
renderEncoder.setFragmentTexture(frame.textureA.content.texture, index: 3)
|
||||||
|
|
||||||
renderEncoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4, instanceCount: 1)
|
renderEncoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4, instanceCount: 1)
|
||||||
}
|
}
|
||||||
@ -692,7 +718,8 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
|
|||||||
}
|
}
|
||||||
commandBuffer.addCompletedHandler { _ in
|
commandBuffer.addCompletedHandler { _ in
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
for _ in usedTextures {
|
for texture in usedTextures {
|
||||||
|
texture.release()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -820,8 +847,6 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
|
|||||||
for completion in completions {
|
for completion in completions {
|
||||||
completion()
|
completion()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
for index in surfaceLayersWithTasks {
|
for index in surfaceLayersWithTasks {
|
||||||
@ -834,3 +859,5 @@ public final class MultiAnimationMetalRendererImpl: MultiAnimationRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user