diff --git a/Tests/CallUITest/Resources/test.png b/Tests/CallUITest/Resources/test.png
new file mode 100644
index 0000000000..59ef68aabd
Binary files /dev/null and b/Tests/CallUITest/Resources/test.png differ
diff --git a/Tests/CallUITest/Resources/test2.mp4 b/Tests/CallUITest/Resources/test2.mp4
new file mode 100644
index 0000000000..c0327e91e3
Binary files /dev/null and b/Tests/CallUITest/Resources/test2.mp4 differ
diff --git a/submodules/MetalEngine/BUILD b/submodules/MetalEngine/BUILD
new file mode 100644
index 0000000000..2460b6126b
--- /dev/null
+++ b/submodules/MetalEngine/BUILD
@@ -0,0 +1,63 @@
+load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
+
+load(
+ "@build_bazel_rules_apple//apple:resources.bzl",
+ "apple_resource_bundle",
+ "apple_resource_group",
+)
+load("//build-system/bazel-utils:plist_fragment.bzl",
+ "plist_fragment",
+)
+
+filegroup(
+ name = "MetalSources",
+ srcs = glob([
+ "Metal/**/*.metal",
+ ]),
+ visibility = ["//visibility:public"],
+)
+
+plist_fragment(
+ name = "MetalEngineMetalSourcesBundleInfoPlist",
+ extension = "plist",
+ template =
+ """
+ CFBundleIdentifier
+ org.telegram.MetalEngineMetalSources
+ CFBundleDevelopmentRegion
+ en
+ CFBundleName
+ MetalEngine
+ """
+)
+
+apple_resource_bundle(
+ name = "MetalEngineMetalSourcesBundle",
+ infoplists = [
+ ":MetalEngineMetalSourcesBundleInfoPlist",
+ ],
+ resources = [
+ ":MetalSources",
+ ],
+)
+
+swift_library(
+ name = "MetalEngine",
+ module_name = "MetalEngine",
+ srcs = glob([
+ "Sources/**/*.swift",
+ ]),
+ copts = [
+ "-warnings-as-errors",
+ ],
+ data = [
+ ":MetalEngineMetalSourcesBundle",
+ ],
+ deps = [
+ "//submodules/Display",
+ "//submodules/Utils/ShelfPack",
+ ],
+ visibility = [
+ "//visibility:public",
+ ],
+)
diff --git a/submodules/MetalEngine/Metal/MetalEngineShaders.metal b/submodules/MetalEngine/Metal/MetalEngineShaders.metal
new file mode 100644
index 0000000000..0e751f4fa4
--- /dev/null
+++ b/submodules/MetalEngine/Metal/MetalEngineShaders.metal
@@ -0,0 +1,11 @@
+#include
+
+using namespace metal;
+
+vertex float4 clearVertex(const device float2* vertexArray [[ buffer(0) ]], unsigned int vid [[ vertex_id ]]) {
+ return float4(vertexArray[vid], 0.0, 1.0);
+}
+
+fragment half4 clearFragment(const device float4 &color [[ buffer(0) ]]) {
+ return half4(color);
+}
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Utils/AlignUp.swift b/submodules/MetalEngine/Sources/AlignUp.swift
similarity index 100%
rename from submodules/TelegramUI/Components/Calls/CallScreen/Sources/Utils/AlignUp.swift
rename to submodules/MetalEngine/Sources/AlignUp.swift
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Rendering/MetalLayerContext.swift b/submodules/MetalEngine/Sources/MetalEngine.swift
similarity index 94%
rename from submodules/TelegramUI/Components/Calls/CallScreen/Sources/Rendering/MetalLayerContext.swift
rename to submodules/MetalEngine/Sources/MetalEngine.swift
index 03b45b95a9..2b8f1348e2 100644
--- a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Rendering/MetalLayerContext.swift
+++ b/submodules/MetalEngine/Sources/MetalEngine.swift
@@ -2,15 +2,9 @@ import Foundation
import Metal
import UIKit
import IOSurface
+import Display
import ShelfPack
-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 Placeholder {
var contents: Resolved?
}
@@ -110,16 +104,16 @@ public struct RenderLayerPlacement: Equatable {
public protocol RenderToLayerState: AnyObject {
var pipelineState: MTLRenderPipelineState { get }
- init?(device: MTLDevice, library: MTLLibrary)
+ init?(device: MTLDevice)
}
public protocol ComputeState: AnyObject {
- init?(device: MTLDevice, library: MTLLibrary)
+ init?(device: MTLDevice)
}
-open class MetalSubjectLayer: SimpleLayer {
+open class MetalEngineSubjectLayer: SimpleLayer {
fileprivate var internalId: Int = -1
- fileprivate var surfaceAllocation: MetalContext.SurfaceAllocation?
+ fileprivate var surfaceAllocation: MetalEngine.SurfaceAllocation?
public override init() {
super.init()
@@ -136,7 +130,7 @@ open class MetalSubjectLayer: SimpleLayer {
}
override public func setNeedsDisplay() {
- if let subject = self as? MetalSubject {
+ if let subject = self as? MetalEngineSubject {
subject.setNeedsUpdate()
}
}
@@ -171,7 +165,7 @@ public final class PooledTexture {
}
}
- public func get(context: MetalSubjectContext) -> TexturePlaceholder? {
+ public func get(context: MetalEngineSubjectContext) -> TexturePlaceholder? {
#if DEBUG
if context.freeResourcesOnCompletion.contains(where: { $0 === self }) {
assertionFailure("Trying to get PooledTexture more than once per update cycle")
@@ -193,7 +187,7 @@ public final class PooledTexture {
}
}
-public final class MetalSubjectContext {
+public final class MetalEngineSubjectContext {
fileprivate final class ComputeOperation {
let commands: (MTLCommandBuffer) -> Void
@@ -205,13 +199,13 @@ public final class MetalSubjectContext {
fileprivate final class RenderToLayerOperation {
let spec: RenderLayerSpec
let state: RenderToLayerState
- weak var layer: MetalSubjectLayer?
+ weak var layer: MetalEngineSubjectLayer?
let commands: (MTLRenderCommandEncoder, RenderLayerPlacement) -> Void
init(
spec: RenderLayerSpec,
state: RenderToLayerState,
- layer: MetalSubjectLayer,
+ layer: MetalEngineSubjectLayer,
commands: @escaping (MTLRenderCommandEncoder, RenderLayerPlacement) -> Void
) {
self.spec = spec
@@ -222,13 +216,13 @@ public final class MetalSubjectContext {
}
private let device: MTLDevice
- private let impl: MetalContext.Impl
+ private let impl: MetalEngine.Impl
fileprivate var computeOperations: [ComputeOperation] = []
fileprivate var renderToLayerOperationsGroupedByState: [ObjectIdentifier: [RenderToLayerOperation]] = [:]
fileprivate var freeResourcesOnCompletion: [PooledTexture.Texture] = []
- fileprivate init(device: MTLDevice, impl: MetalContext.Impl) {
+ fileprivate init(device: MTLDevice, impl: MetalEngine.Impl) {
self.device = device
self.impl = impl
}
@@ -236,7 +230,7 @@ public final class MetalSubjectContext {
public func renderToLayer(
spec: RenderLayerSpec,
state: RenderToLayerStateType.Type,
- layer: MetalSubjectLayer,
+ layer: MetalEngineSubjectLayer,
inputs: repeat Placeholder,
commands: @escaping (MTLRenderCommandEncoder, RenderLayerPlacement, repeat each Resolved) -> Void
) {
@@ -245,7 +239,7 @@ public final class MetalSubjectContext {
if let current = self.impl.renderStates[stateTypeId] as? RenderToLayerStateType {
resolvedState = current
} else {
- guard let value = RenderToLayerStateType(device: self.device, library: self.impl.library) else {
+ guard let value = RenderToLayerStateType(device: self.device) else {
assertionFailure("Could not initialize render state \(state)")
return
}
@@ -278,7 +272,7 @@ public final class MetalSubjectContext {
public func renderToLayer(
spec: RenderLayerSpec,
state: RenderToLayerStateType.Type,
- layer: MetalSubjectLayer,
+ layer: MetalEngineSubjectLayer,
commands: @escaping (MTLRenderCommandEncoder, RenderLayerPlacement) -> Void
) {
self.renderToLayer(spec: spec, state: state, layer: layer, inputs: noInputPlaceholder, commands: { encoder, placement, _ in
@@ -296,7 +290,7 @@ public final class MetalSubjectContext {
if let current = self.impl.computeStates[stateTypeId] as? ComputeStateType {
resolvedState = current
} else {
- guard let value = ComputeStateType(device: self.device, library: self.impl.library) else {
+ guard let value = ComputeStateType(device: self.device) else {
assertionFailure("Could not initialize compute state \(state)")
return Placeholder()
}
@@ -338,24 +332,24 @@ public final class MetalSubjectContext {
}
}
-public final class MetalSubjectInternalData {
+public final class MetalEngineSubjectInternalData {
var internalId: Int = -1
- var renderSurfaceAllocation: MetalContext.SurfaceAllocation?
+ var renderSurfaceAllocation: MetalEngine.SurfaceAllocation?
init() {
}
}
-public protocol MetalSubject: AnyObject {
- var internalData: MetalSubjectInternalData? { get set }
+public protocol MetalEngineSubject: AnyObject {
+ var internalData: MetalEngineSubjectInternalData? { get set }
func setNeedsUpdate()
- func update(context: MetalSubjectContext)
+ func update(context: MetalEngineSubjectContext)
}
-public extension MetalSubject {
+public extension MetalEngineSubject {
func setNeedsUpdate() {
- MetalContext.shared.impl.addSubjectNeedsUpdate(subject: self)
+ MetalEngine.shared.impl.addSubjectNeedsUpdate(subject: self)
}
}
@@ -370,7 +364,7 @@ private final class MetalEventLayer: CAMetalLayer {
}
}
-public final class MetalContext {
+public final class MetalEngine {
struct SurfaceAllocation {
struct Phase {
let subRect: CGRect
@@ -509,9 +503,9 @@ public final class MetalContext {
}
private final class SubjectReference {
- weak var subject: MetalSubject?
+ weak var subject: MetalEngineSubject?
- init(subject: MetalSubject) {
+ init(subject: MetalEngineSubject) {
self.subject = subject
}
}
@@ -548,7 +542,7 @@ public final class MetalContext {
init?(device: MTLDevice) {
let mainBundle = Bundle(for: Impl.self)
- guard let path = mainBundle.path(forResource: "MetalSourcesBundle", ofType: "bundle") else {
+ guard let path = mainBundle.path(forResource: "MetalEngineMetalSourcesBundle", ofType: "bundle") else {
return nil
}
guard let bundle = Bundle(path: path) else {
@@ -624,15 +618,15 @@ public final class MetalContext {
let surfaceId = self.nextSurfaceId
self.nextSurfaceId += 1
- let surfaceWidth = max(1024, alignUp(size: Int(minSize.width), align: 64))
- let surfaceHeight = max(512, alignUp(size: Int(minSize.height), align: 64))
+ let surfaceWidth = max(1024, alignUp(Int(minSize.width), alignment: 64))
+ let surfaceHeight = max(512, alignUp(Int(minSize.height), alignment: 64))
let surface = Surface(id: surfaceId, device: self.device, width: surfaceWidth, height: surfaceHeight)
self.surfaces[surfaceId] = surface
return surface
}
- private func refreshLayerAllocation(layer: MetalSubjectLayer, renderSpec: RenderLayerSpec) {
+ private func refreshLayerAllocation(layer: MetalEngineSubjectLayer, renderSpec: RenderLayerSpec) {
var previousSurfaceId: Int?
var updatedSurfaceId: Int?
@@ -689,12 +683,12 @@ public final class MetalContext {
}
}
- func addSubjectNeedsUpdate(subject: MetalSubject) {
- let internalData: MetalSubjectInternalData
+ func addSubjectNeedsUpdate(subject: MetalEngineSubject) {
+ let internalData: MetalEngineSubjectInternalData
if let current = subject.internalData {
internalData = current
} else {
- internalData = MetalSubjectInternalData()
+ internalData = MetalEngineSubjectInternalData()
subject.internalData = internalData
}
@@ -751,7 +745,7 @@ public final class MetalContext {
return
}
- let subjectContext = MetalSubjectContext(device: device, impl: self)
+ let subjectContext = MetalEngineSubjectContext(device: device, impl: self)
for subjectReference in self.updatedSubjects {
guard let subject = subjectReference.subject else {
@@ -899,7 +893,7 @@ public final class MetalContext {
}
}
- public static let shared = MetalContext()
+ public static let shared = MetalEngine()
fileprivate let impl: Impl
diff --git a/submodules/TelegramCallsUI/BUILD b/submodules/TelegramCallsUI/BUILD
index dbd5bc8794..6179f39b69 100644
--- a/submodules/TelegramCallsUI/BUILD
+++ b/submodules/TelegramCallsUI/BUILD
@@ -105,6 +105,7 @@ swift_library(
"//submodules/Components/HierarchyTrackingLayer:HierarchyTrackingLayer",
"//submodules/PeerInfoUI/CreateExternalMediaStreamScreen:CreateExternalMediaStreamScreen",
"//submodules/PhoneNumberFormat:PhoneNumberFormat",
+ "//submodules/TelegramUI/Components/Calls/CallScreen",
],
visibility = [
"//visibility:public",
diff --git a/submodules/TelegramCallsUI/Sources/CallController.swift b/submodules/TelegramCallsUI/Sources/CallController.swift
index 5845fc5d36..2e0db9a12c 100644
--- a/submodules/TelegramCallsUI/Sources/CallController.swift
+++ b/submodules/TelegramCallsUI/Sources/CallController.swift
@@ -135,11 +135,11 @@ public final class CallController: ViewController {
}
override public func loadDisplayNode() {
- if self.call.isVideoPossible {
- self.displayNode = CallControllerNode(sharedContext: self.sharedContext, account: self.account, presentationData: self.presentationData, statusBar: self.statusBar, debugInfo: self.call.debugInfo(), shouldStayHiddenUntilConnection: !self.call.isOutgoing && self.call.isIntegratedWithCallKit, easyDebugAccess: self.easyDebugAccess, call: self.call)
- } else {
- self.displayNode = LegacyCallControllerNode(sharedContext: self.sharedContext, account: self.account, presentationData: self.presentationData, statusBar: self.statusBar, debugInfo: self.call.debugInfo(), shouldStayHiddenUntilConnection: !self.call.isOutgoing && self.call.isIntegratedWithCallKit, easyDebugAccess: self.easyDebugAccess, call: self.call)
- }
+ #if DEBUG
+ self.displayNode = CallControllerNodeV2(sharedContext: self.sharedContext, account: self.account, presentationData: self.presentationData, statusBar: self.statusBar, debugInfo: self.call.debugInfo(), shouldStayHiddenUntilConnection: !self.call.isOutgoing && self.call.isIntegratedWithCallKit, easyDebugAccess: self.easyDebugAccess, call: self.call)
+ #else
+ self.displayNode = CallControllerNode(sharedContext: self.sharedContext, account: self.account, presentationData: self.presentationData, statusBar: self.statusBar, debugInfo: self.call.debugInfo(), shouldStayHiddenUntilConnection: !self.call.isOutgoing && self.call.isIntegratedWithCallKit, easyDebugAccess: self.easyDebugAccess, call: self.call)
+ #endif
self.displayNodeDidLoad()
self.controllerNode.toggleMute = { [weak self] in
diff --git a/submodules/TelegramCallsUI/Sources/CallControllerNodeV2.swift b/submodules/TelegramCallsUI/Sources/CallControllerNodeV2.swift
new file mode 100644
index 0000000000..0749b43341
--- /dev/null
+++ b/submodules/TelegramCallsUI/Sources/CallControllerNodeV2.swift
@@ -0,0 +1,91 @@
+import Foundation
+import AsyncDisplayKit
+import Display
+import TelegramCore
+import Postbox
+import TelegramAudio
+import AccountContext
+import TelegramPresentationData
+import SwiftSignalKit
+import CallScreen
+
+final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeProtocol {
+ private let sharedContext: SharedAccountContext
+ private let account: Account
+ private let presentationData: PresentationData
+ private let statusBar: StatusBar
+ private let call: PresentationCall
+
+ private let callScreen: PrivateCallScreen
+
+ var isMuted: Bool = false
+
+ var toggleMute: (() -> Void)?
+ var setCurrentAudioOutput: ((AudioSessionOutput) -> Void)?
+ var beginAudioOuputSelection: ((Bool) -> Void)?
+ var acceptCall: (() -> Void)?
+ var endCall: (() -> Void)?
+ var back: (() -> Void)?
+ var presentCallRating: ((CallId, Bool) -> Void)?
+ var present: ((ViewController) -> Void)?
+ var callEnded: ((Bool) -> Void)?
+ var dismissedInteractively: (() -> Void)?
+ var dismissAllTooltips: (() -> Void)?
+
+ init(
+ sharedContext: SharedAccountContext,
+ account: Account,
+ presentationData: PresentationData,
+ statusBar: StatusBar,
+ debugInfo: Signal<(String, String), NoError>,
+ shouldStayHiddenUntilConnection: Bool = false,
+ easyDebugAccess: Bool,
+ call: PresentationCall
+ ) {
+ self.sharedContext = sharedContext
+ self.account = account
+ self.presentationData = presentationData
+ self.statusBar = statusBar
+ self.call = call
+
+ self.callScreen = PrivateCallScreen()
+
+ super.init()
+
+ self.view.addSubview(self.callScreen)
+ }
+
+ func updateAudioOutputs(availableOutputs: [AudioSessionOutput], currentOutput: AudioSessionOutput?) {
+
+ }
+
+ func updateCallState(_ callState: PresentationCallState) {
+ if case let .terminated(id, _, reportRating) = callState.state, let callId = id {
+ if reportRating {
+ self.presentCallRating?(callId, self.call.isVideo)
+ }
+ self.callEnded?(reportRating)
+ }
+ }
+
+ func updatePeer(accountPeer: Peer, peer: Peer, hasOther: Bool) {
+
+ }
+
+ func animateIn() {
+
+ }
+
+ func animateOut(completion: @escaping () -> Void) {
+
+ }
+
+ func expandFromPipIfPossible() {
+
+ }
+
+ func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
+ transition.updateFrame(view: self.callScreen, frame: CGRect(origin: CGPoint(), size: layout.size))
+ self.callScreen.update(size: layout.size, insets: layout.insets(options: [.statusBar]))
+ }
+}
diff --git a/submodules/TelegramUI/BUILD b/submodules/TelegramUI/BUILD
index bf1fae9910..e58329bdd9 100644
--- a/submodules/TelegramUI/BUILD
+++ b/submodules/TelegramUI/BUILD
@@ -410,6 +410,7 @@ swift_library(
"//submodules/TelegramUI/Components/Settings/PeerNameColorScreen",
"//submodules/TelegramUI/Components/ContextMenuScreen",
"//submodules/TelegramUI/Components/PeerAllowedReactionsScreen",
+ "//submodules/MetalEngine",
] + select({
"@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets,
"//build-system:ios_sim_arm64": [],
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/BUILD b/submodules/TelegramUI/Components/Calls/CallScreen/BUILD
index 059d137551..7fa37262e1 100644
--- a/submodules/TelegramUI/Components/Calls/CallScreen/BUILD
+++ b/submodules/TelegramUI/Components/Calls/CallScreen/BUILD
@@ -10,7 +10,7 @@ load("//build-system/bazel-utils:plist_fragment.bzl",
)
filegroup(
- name = "MetalSources",
+ name = "CallScreenMetalSources",
srcs = glob([
"Metal/**/*.metal",
]),
@@ -18,7 +18,7 @@ filegroup(
)
plist_fragment(
- name = "MetalSourcesBundleInfoPlist",
+ name = "CallScreenMetalSourcesBundleInfoPlist",
extension = "plist",
template =
"""
@@ -27,20 +27,26 @@ plist_fragment(
CFBundleDevelopmentRegion
en
CFBundleName
- AnimationCompression
+ CallScreen
"""
)
apple_resource_bundle(
- name = "MetalSourcesBundle",
+ name = "CallScreenMetalSourcesBundle",
infoplists = [
- ":MetalSourcesBundleInfoPlist",
+ ":CallScreenMetalSourcesBundleInfoPlist",
],
resources = [
- ":MetalSources",
+ ":CallScreenMetalSources",
],
)
+filegroup(
+ name = "Assets",
+ srcs = glob(["CallScreenAssets.xcassets/**"]),
+ visibility = ["//visibility:public"],
+)
+
swift_library(
name = "CallScreen",
module_name = "CallScreen",
@@ -51,13 +57,14 @@ swift_library(
"-warnings-as-errors",
],
data = [
- ":MetalSourcesBundle",
+ ":CallScreenMetalSourcesBundle",
+ ":Assets",
],
deps = [
"//submodules/Display",
+ "//submodules/MetalEngine",
"//submodules/ComponentFlow",
"//submodules/TelegramUI/Components/AnimatedTextComponent",
- "//submodules/Utils/ShelfPack",
],
visibility = [
"//visibility:public",
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Airpods.imageset/Contents.json b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Airpods.imageset/Contents.json
new file mode 100644
index 0000000000..0b0b845832
--- /dev/null
+++ b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Airpods.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "ic_call_audioairpods.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Airpods.imageset/ic_call_audioairpods.pdf b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Airpods.imageset/ic_call_audioairpods.pdf
new file mode 100644
index 0000000000..2f31d498cc
Binary files /dev/null and b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Airpods.imageset/ic_call_audioairpods.pdf differ
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/AirpodsPro.imageset/Contents.json b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/AirpodsPro.imageset/Contents.json
new file mode 100644
index 0000000000..370377d8ee
--- /dev/null
+++ b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/AirpodsPro.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "ic_call_audioairpodspro.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/AirpodsPro.imageset/ic_call_audioairpodspro.pdf b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/AirpodsPro.imageset/ic_call_audioairpodspro.pdf
new file mode 100644
index 0000000000..305f6ba52b
Binary files /dev/null and b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/AirpodsPro.imageset/ic_call_audioairpodspro.pdf differ
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/AirpodsProMax.imageset/Contents.json b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/AirpodsProMax.imageset/Contents.json
new file mode 100644
index 0000000000..795a18ba91
--- /dev/null
+++ b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/AirpodsProMax.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "ic_call_airpodsmax.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/AirpodsProMax.imageset/ic_call_airpodsmax.pdf b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/AirpodsProMax.imageset/ic_call_airpodsmax.pdf
new file mode 100644
index 0000000000..3661b17240
Binary files /dev/null and b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/AirpodsProMax.imageset/ic_call_airpodsmax.pdf differ
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Bluetooth.imageset/Contents.json b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Bluetooth.imageset/Contents.json
new file mode 100644
index 0000000000..a448d215b7
--- /dev/null
+++ b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Bluetooth.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "ic_call_audiobt.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Bluetooth.imageset/ic_call_audiobt.pdf b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Bluetooth.imageset/ic_call_audiobt.pdf
new file mode 100644
index 0000000000..a2f8cad6cf
Binary files /dev/null and b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Bluetooth.imageset/ic_call_audiobt.pdf differ
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Contents.json b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Contents.json
new file mode 100644
index 0000000000..6e965652df
--- /dev/null
+++ b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Contents.json
@@ -0,0 +1,9 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "provides-namespace" : true
+ }
+}
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/End.imageset/CallCancelIcon@2x.png b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/End.imageset/CallCancelIcon@2x.png
new file mode 100644
index 0000000000..1d51eb1725
Binary files /dev/null and b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/End.imageset/CallCancelIcon@2x.png differ
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/End.imageset/CallCancelIcon@3x.png b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/End.imageset/CallCancelIcon@3x.png
new file mode 100644
index 0000000000..95e99b4db2
Binary files /dev/null and b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/End.imageset/CallCancelIcon@3x.png differ
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/End.imageset/Contents.json b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/End.imageset/Contents.json
new file mode 100644
index 0000000000..b9550f6ca9
--- /dev/null
+++ b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/End.imageset/Contents.json
@@ -0,0 +1,22 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "filename" : "CallCancelIcon@2x.png",
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "filename" : "CallCancelIcon@3x.png",
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Flip.imageset/Contents.json b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Flip.imageset/Contents.json
new file mode 100644
index 0000000000..5c38ab0966
--- /dev/null
+++ b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Flip.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "ic_flip (1).pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Flip.imageset/ic_flip (1).pdf b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Flip.imageset/ic_flip (1).pdf
new file mode 100644
index 0000000000..da09156412
Binary files /dev/null and b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Flip.imageset/ic_flip (1).pdf differ
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Mute.imageset/Contents.json b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Mute.imageset/Contents.json
new file mode 100644
index 0000000000..be885df064
--- /dev/null
+++ b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Mute.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "ic_call_microphone.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Mute.imageset/ic_call_microphone.pdf b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Mute.imageset/ic_call_microphone.pdf
new file mode 100644
index 0000000000..99ad802193
Binary files /dev/null and b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Mute.imageset/ic_call_microphone.pdf differ
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Speaker.imageset/Contents.json b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Speaker.imageset/Contents.json
new file mode 100644
index 0000000000..8876671fab
--- /dev/null
+++ b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Speaker.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "ic_call_speaker.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Speaker.imageset/ic_call_speaker.pdf b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Speaker.imageset/ic_call_speaker.pdf
new file mode 100644
index 0000000000..104b537f82
Binary files /dev/null and b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Speaker.imageset/ic_call_speaker.pdf differ
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Video.imageset/Contents.json b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Video.imageset/Contents.json
new file mode 100644
index 0000000000..324f180936
--- /dev/null
+++ b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Video.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "ic_call_camera.pdf",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Video.imageset/ic_call_camera.pdf b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Video.imageset/ic_call_camera.pdf
new file mode 100644
index 0000000000..92a55e486d
Binary files /dev/null and b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Call/Video.imageset/ic_call_camera.pdf differ
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Contents.json b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Contents.json
new file mode 100644
index 0000000000..73c00596a7
--- /dev/null
+++ b/submodules/TelegramUI/Components/Calls/CallScreen/CallScreenAssets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Metal/Shaders.metal b/submodules/TelegramUI/Components/Calls/CallScreen/Metal/CallScreenShaders.metal
similarity index 97%
rename from submodules/TelegramUI/Components/Calls/CallScreen/Metal/Shaders.metal
rename to submodules/TelegramUI/Components/Calls/CallScreen/Metal/CallScreenShaders.metal
index 7bf649b0fc..1306472e92 100644
--- a/submodules/TelegramUI/Components/Calls/CallScreen/Metal/Shaders.metal
+++ b/submodules/TelegramUI/Components/Calls/CallScreen/Metal/CallScreenShaders.metal
@@ -21,14 +21,6 @@ struct QuadVertexOut {
float2 uv;
};
-vertex float4 clearVertex(const device float2* vertexArray [[ buffer(0) ]], unsigned int vid [[ vertex_id ]]) {
- return float4(vertexArray[vid], 0.0, 1.0);
-}
-
-fragment half4 clearFragment(const device float4 &color [[ buffer(0) ]]) {
- return half4(color);
-}
-
vertex QuadVertexOut callBackgroundVertex(
const device Rectangle &rect [[ buffer(0) ]],
unsigned int vid [[ vertex_id ]]
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Animation/Animation.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Animation/Animation.swift
index 531f7de1bf..daa9a69fdc 100644
--- a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Animation/Animation.swift
+++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Animation/Animation.swift
@@ -147,6 +147,18 @@ public final class AnimatedProperty: AnyAnimatedProp
self.didStartAnimation?()
}
+ public func animate(from: T, to: T, duration: Double, curve: AnimationCurve) {
+ self.value = from
+ self.animation = Animation(from: from, to: to, duration: duration, curve: curve)
+ self.animation?.start()
+ self.didStartAnimation?()
+ }
+
+ public func set(to: T) {
+ self.animation = nil
+ self.value = to
+ }
+
override public func update() {
if let animation = self.animation {
self.value = animation.update(at: CACurrentMediaTime())
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/CallScreen.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/CallScreen.swift
index 2cc37ab552..6566d2eb64 100644
--- a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/CallScreen.swift
+++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/CallScreen.swift
@@ -1,4 +1,28 @@
import Foundation
import UIKit
import Display
+import MetalKit
+private final class BundleMarker: NSObject {
+}
+
+private var metalLibraryValue: MTLLibrary?
+func metalLibrary(device: MTLDevice) -> MTLLibrary? {
+ if let metalLibraryValue {
+ return metalLibraryValue
+ }
+
+ let mainBundle = Bundle(for: BundleMarker.self)
+ guard let path = mainBundle.path(forResource: "CallScreenMetalSourcesBundle", ofType: "bundle") else {
+ return nil
+ }
+ guard let bundle = Bundle(path: path) else {
+ return nil
+ }
+ guard let library = try? device.makeDefaultLibrary(bundle: bundle) else {
+ return nil
+ }
+
+ metalLibraryValue = library
+ return library
+}
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/AvatarLayer.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/AvatarLayer.swift
index b0a316f966..f57ddb8ec4 100644
--- a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/AvatarLayer.swift
+++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/AvatarLayer.swift
@@ -1,5 +1,6 @@
import Foundation
import UIKit
+import Display
final class AvatarLayer: SimpleLayer {
var image: UIImage? {
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/ButtonGroupView.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/ButtonGroupView.swift
index 0d4b159d91..9d13c09cc2 100644
--- a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/ButtonGroupView.swift
+++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/ButtonGroupView.swift
@@ -1,5 +1,6 @@
import Foundation
import UIKit
+import Display
final class ButtonGroupView: UIView, ContentOverlayView {
enum Key: Hashable {
@@ -12,6 +13,7 @@ final class ButtonGroupView: UIView, ContentOverlayView {
let overlayMaskLayer: CALayer
private var buttons: [Key: ContentOverlayButton] = [:]
+ var audioPressed: (() -> Void)?
var toggleVideo: (() -> Void)?
override init(frame: CGRect) {
@@ -89,9 +91,15 @@ final class ButtonGroupView: UIView, ContentOverlayView {
let image: UIImage?
switch key {
case .audio:
- image = UIImage(named: "Call/ButtonSpeaker")
+ image = UIImage(named: "Call/Speaker")
+ button.action = { [weak self] in
+ guard let self else {
+ return
+ }
+ self.audioPressed?()
+ }
case .video:
- image = UIImage(named: "Call/ButtonVideo")
+ image = UIImage(named: "Call/Video")
button.action = { [weak self] in
guard let self else {
return
@@ -99,9 +107,9 @@ final class ButtonGroupView: UIView, ContentOverlayView {
self.toggleVideo?()
}
case .mic:
- image = UIImage(named: "Call/ButtonMicMuted")
+ image = UIImage(named: "Call/Mute")
case .close:
- image = UIImage(named: "Call/ButtonEnd")
+ image = UIImage(named: "Call/End")
}
button.setImage(image?.withRenderingMode(.alwaysTemplate), for: .normal)
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/CallBackgroundLayer.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/CallBackgroundLayer.swift
index c6c1b5f497..e265111e22 100644
--- a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/CallBackgroundLayer.swift
+++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/CallBackgroundLayer.swift
@@ -1,6 +1,7 @@
import Foundation
import MetalKit
import UIKit
+import MetalEngine
private func shiftArray(array: [SIMD2], offset: Int) -> [SIMD2] {
var newArray = array
@@ -28,13 +29,28 @@ private func hexToFloat(_ hex: Int) -> SIMD4 {
return SIMD4(x: red, y: green, z: blue, w: 1.0)
}
-final class CallBackgroundLayer: MetalSubjectLayer, MetalSubject {
- var internalData: MetalSubjectInternalData?
+private struct ColorSet: Equatable, AnimationInterpolatable {
+ static let animationInterpolator = AnimationInterpolator { from, to, fraction in
+ var result: [SIMD4] = []
+ for i in 0 ..< min(from.colors.count, to.colors.count) {
+ result.append(from.colors[i] * Float(1.0 - fraction) + to.colors[i] * Float(fraction))
+ }
+ return ColorSet(colors: result)
+ }
+
+ var colors: [SIMD4]
+}
+
+final class CallBackgroundLayer: MetalEngineSubjectLayer, MetalEngineSubject {
+ var internalData: MetalEngineSubjectInternalData?
final class RenderState: RenderToLayerState {
let pipelineState: MTLRenderPipelineState
- init?(device: MTLDevice, library: MTLLibrary) {
+ init?(device: MTLDevice) {
+ guard let library = metalLibrary(device: device) else {
+ return nil
+ }
guard let vertexFunction = library.makeFunction(name: "callBackgroundVertex"), let fragmentFunction = library.makeFunction(name: "callBackgroundFragment") else {
return nil
}
@@ -66,11 +82,44 @@ final class CallBackgroundLayer: MetalSubjectLayer, MetalSubject {
private var displayLinkSubscription: SharedDisplayLink.Subscription?
- var renderSpec: RenderLayerSpec?
+ var renderSpec: RenderLayerSpec? {
+ didSet {
+ if self.renderSpec != oldValue {
+ self.setNeedsUpdate()
+ }
+ }
+ }
+
+ private let colorSets: [ColorSet]
+ private let colorTransition: AnimatedProperty
+ private var stateIndex: Int = 0
+ private let phaseAcceleration = AnimatedProperty(0.0)
init(isBlur: Bool) {
self.isBlur = isBlur
+ self.colorSets = [
+ ColorSet(colors: [
+ hexToFloat(0x568FD6),
+ hexToFloat(0x626ED5),
+ hexToFloat(0xA667D5),
+ hexToFloat(0x7664DA)
+ ]),
+ ColorSet(colors: [
+ hexToFloat(0xACBD65),
+ hexToFloat(0x459F8D),
+ hexToFloat(0x53A4D1),
+ hexToFloat(0x3E917A)
+ ]),
+ ColorSet(colors: [
+ hexToFloat(0xC0508D),
+ hexToFloat(0xF09536),
+ hexToFloat(0xCE5081),
+ hexToFloat(0xFC7C4C)
+ ])
+ ]
+ self.colorTransition = AnimatedProperty(colorSets[0])
+
super.init()
self.didEnterHierarchy = { [weak self] in
@@ -81,8 +130,14 @@ final class CallBackgroundLayer: MetalSubjectLayer, MetalSubject {
guard let self else {
return
}
+ self.colorTransition.update()
+ self.phaseAcceleration.update()
+
let stepCount = 8
- self.phase = (self.phase + 1.0 / 60.0).truncatingRemainder(dividingBy: Float(stepCount))
+ var phaseStep: CGFloat = 0.5 / 30.0
+ phaseStep += phaseStep * self.phaseAcceleration.value * 0.5
+ self.phase = (self.phase + Float(phaseStep)).truncatingRemainder(dividingBy: Float(stepCount))
+
self.setNeedsUpdate()
})
}
@@ -95,6 +150,9 @@ final class CallBackgroundLayer: MetalSubjectLayer, MetalSubject {
}
override init(layer: Any) {
+ self.colorSets = []
+ self.colorTransition = AnimatedProperty(ColorSet(colors: []))
+
super.init(layer: layer)
}
@@ -102,7 +160,19 @@ final class CallBackgroundLayer: MetalSubjectLayer, MetalSubject {
fatalError("init(coder:) has not been implemented")
}
- func update(context: MetalSubjectContext) {
+ func update(stateIndex: Int, animated: Bool) {
+ if self.stateIndex != stateIndex {
+ self.stateIndex = stateIndex
+ if animated {
+ self.phaseAcceleration.animate(from: 1.0, to: 0.0, duration: 2.0, curve: .easeInOut)
+ self.colorTransition.animate(to: self.colorSets[stateIndex % self.colorSets.count], duration: 0.3, curve: .easeInOut)
+ } else {
+ self.colorTransition.set(to: self.colorSets[stateIndex % self.colorSets.count])
+ }
+ }
+ }
+
+ func update(context: MetalEngineSubjectContext) {
guard let renderSpec = self.renderSpec else {
return
}
@@ -127,22 +197,9 @@ final class CallBackgroundLayer: MetalSubjectLayer, MetalSubject {
}
encoder.setFragmentBytes(&positions, length: 4 * MemoryLayout>.size, index: 0)
- var colors: [[SIMD4]] = [
- [
- hexToFloat(0x568FD6),
- hexToFloat(0x626ED5),
- hexToFloat(0xA667D5),
- hexToFloat(0x7664DA)
- ],
- [
- hexToFloat(0xACBD65),
- hexToFloat(0x459F8D),
- hexToFloat(0x53A4D1),
- hexToFloat(0x3E917A)
- ]
- ]
+ var colors: [SIMD4] = self.colorTransition.value.colors
- encoder.setFragmentBytes(&colors[0], length: 4 * MemoryLayout>.size, index: 1)
+ encoder.setFragmentBytes(&colors, length: 4 * MemoryLayout>.size, index: 1)
var brightness: Float = isBlur ? 1.1 : 1.0
var saturation: Float = isBlur ? 1.2 : 1.0
encoder.setFragmentBytes(&brightness, length: 4, index: 2)
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/CallBlobsLayer.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/CallBlobsLayer.swift
index 0d6732c4f2..cd988cbee6 100644
--- a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/CallBlobsLayer.swift
+++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/CallBlobsLayer.swift
@@ -1,8 +1,9 @@
import Foundation
import MetalKit
+import MetalEngine
-final class CallBlobsLayer: MetalSubjectLayer, MetalSubject {
- var internalData: MetalSubjectInternalData?
+final class CallBlobsLayer: MetalEngineSubjectLayer, MetalEngineSubject {
+ var internalData: MetalEngineSubjectInternalData?
struct Blob {
var points: [Float]
@@ -48,10 +49,10 @@ final class CallBlobsLayer: MetalSubjectLayer, MetalSubject {
let pipelineState: MTLRenderPipelineState
- required init?(
- device: MTLDevice,
- library: MTLLibrary
- ) {
+ required init?(device: MTLDevice) {
+ guard let library = metalLibrary(device: device) else {
+ return nil
+ }
guard let vertexFunction = library.makeFunction(name: "callBlobVertex"), let fragmentFunction = library.makeFunction(name: "callBlobFragment") else {
return nil
}
@@ -123,7 +124,7 @@ final class CallBlobsLayer: MetalSubjectLayer, MetalSubject {
fatalError("init(coder:) has not been implemented")
}
- func update(context: MetalSubjectContext) {
+ func update(context: MetalEngineSubjectContext) {
if self.bounds.isEmpty {
return
}
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/ContentOverlayButton.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/ContentOverlayButton.swift
index 880a6a41fd..e2ceaf3889 100644
--- a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/ContentOverlayButton.swift
+++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/ContentOverlayButton.swift
@@ -1,5 +1,6 @@
import Foundation
import UIKit
+import Display
final class ContentOverlayButton: UIButton, ContentOverlayView {
var overlayMaskLayer: CALayer {
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/ContentView.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/ContentView.swift
index b735b03a43..822e5d9211 100644
--- a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/ContentView.swift
+++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/ContentView.swift
@@ -1,7 +1,21 @@
import Foundation
import UIKit
+import Display
+import MetalEngine
final class ContentView: UIView {
+ private struct Params: Equatable {
+ var size: CGSize
+ var insets: UIEdgeInsets
+ var state: PrivateCallScreen.State
+
+ init(size: CGSize, insets: UIEdgeInsets, state: PrivateCallScreen.State) {
+ self.size = size
+ self.insets = insets
+ self.state = state
+ }
+ }
+
private let blobLayer: CallBlobsLayer
private let avatarLayer: AvatarLayer
private let titleView: TextView
@@ -14,8 +28,7 @@ final class ContentView: UIView {
private var videoLayerMask: SimpleShapeLayer?
private var blurredVideoLayerMask: SimpleShapeLayer?
- private var currentLayout: (size: CGSize, insets: UIEdgeInsets)?
- private var phase: CGFloat = 0.0
+ private var params: Params?
private var isDisplayingVideo: Bool = false
private var videoDisplayFraction = AnimatedProperty(0.0)
@@ -54,8 +67,8 @@ final class ContentView: UIView {
guard let self else {
return
}
- if let currentLayout = self.currentLayout {
- self.update(size: currentLayout.size, insets: currentLayout.insets)
+ if let params = self.params {
+ self.updateInternal(params: params)
}
}
}
@@ -64,41 +77,59 @@ final class ContentView: UIView {
fatalError("init(coder:) has not been implemented")
}
- func update(size: CGSize, insets: UIEdgeInsets) {
- self.currentLayout = (size, insets)
-
+ func update(size: CGSize, insets: UIEdgeInsets, state: PrivateCallScreen.State) {
+ let params = Params(size: size, insets: insets, state: state)
+ if self.params == params {
+ return
+ }
+ self.params = params
+ self.updateInternal(params: params)
+ }
+
+ private func updateInternal(params: Params) {
if self.emojiView == nil {
let emojiView = KeyEmojiView(emoji: ["🐱", "🚂", "❄️", "🎨"])
self.emojiView = emojiView
self.addSubview(emojiView)
}
if let emojiView = self.emojiView {
- emojiView.frame = CGRect(origin: CGPoint(x: size.width - 12.0 - emojiView.size.width, y: insets.top + 27.0), size: emojiView.size)
+ emojiView.frame = CGRect(origin: CGPoint(x: params.size.width - 12.0 - emojiView.size.width, y: params.insets.top + 27.0), size: emojiView.size)
}
if self.videoInput == nil, let url = Bundle.main.url(forResource: "test2", withExtension: "mp4") {
- self.videoInput = VideoInput(device: MetalContext.shared.device, url: url)
+ self.videoInput = VideoInput(device: MetalEngine.shared.device, url: url)
self.videoLayer.video = self.videoInput
}
- self.phase += 3.0 / 60.0
- self.phase = self.phase.truncatingRemainder(dividingBy: 1.0)
- var avatarScale: CGFloat = 0.05 * sin(CGFloat(self.phase) * CGFloat.pi)
+ //self.phase += 3.0 / 60.0
+ //self.phase = self.phase.truncatingRemainder(dividingBy: 1.0)
+ var avatarScale: CGFloat = 0.05 * sin(CGFloat(0.0) * CGFloat.pi)
avatarScale *= 1.0 - self.videoDisplayFraction.value
let avatarSize: CGFloat = 136.0
let blobSize: CGFloat = 176.0
- let expandedVideoRadius: CGFloat = sqrt(pow(size.width * 0.5, 2.0) + pow(size.height * 0.5, 2.0))
+ let expandedVideoRadius: CGFloat = sqrt(pow(params.size.width * 0.5, 2.0) + pow(params.size.height * 0.5, 2.0))
- let avatarFrame = CGRect(origin: CGPoint(x: floor((size.width - avatarSize) * 0.5), y: CGFloat.animationInterpolator.interpolate(from: 222.0, to: floor((size.height - avatarSize) * 0.5), fraction: self.videoDisplayFraction.value)), size: CGSize(width: avatarSize, height: avatarSize))
+ let avatarFrame = CGRect(origin: CGPoint(x: floor((params.size.width - avatarSize) * 0.5), y: CGFloat.animationInterpolator.interpolate(from: 222.0, to: floor((params.size.height - avatarSize) * 0.5), fraction: self.videoDisplayFraction.value)), size: CGSize(width: avatarSize, height: avatarSize))
- let titleSize = self.titleView.update(string: "Emma Walters", fontSize: CGFloat.animationInterpolator.interpolate(from: 28.0, to: 17.0, fraction: self.videoDisplayFraction.value), fontWeight: CGFloat.animationInterpolator.interpolate(from: 0.0, to: 0.25, fraction: self.videoDisplayFraction.value), constrainedWidth: size.width - 16.0 * 2.0)
- let titleFrame = CGRect(origin: CGPoint(x: (size.width - titleSize.width) * 0.5, y: CGFloat.animationInterpolator.interpolate(from: avatarFrame.maxY + 39.0, to: insets.top + 17.0, fraction: self.videoDisplayFraction.value)), size: titleSize)
+ let titleSize = self.titleView.update(string: "Emma Walters", fontSize: CGFloat.animationInterpolator.interpolate(from: 28.0, to: 17.0, fraction: self.videoDisplayFraction.value), fontWeight: CGFloat.animationInterpolator.interpolate(from: 0.0, to: 0.25, fraction: self.videoDisplayFraction.value), constrainedWidth: params.size.width - 16.0 * 2.0)
+ let titleFrame = CGRect(origin: CGPoint(x: (params.size.width - titleSize.width) * 0.5, y: CGFloat.animationInterpolator.interpolate(from: avatarFrame.maxY + 39.0, to: params.insets.top + 17.0, fraction: self.videoDisplayFraction.value)), size: titleSize)
self.titleView.frame = titleFrame
- let statusSize = self.statusView.update(state: .waiting(.requesting))
- self.statusView.frame = CGRect(origin: CGPoint(x: (size.width - statusSize.width) * 0.5, y: titleFrame.maxY + CGFloat.animationInterpolator.interpolate(from: 4.0, to: 0.0, fraction: self.videoDisplayFraction.value)), size: statusSize)
+ let statusState: StatusView.State
+ switch params.state.lifecycleState {
+ case .connecting:
+ statusState = .waiting(.requesting)
+ case .ringing:
+ statusState = .waiting(.ringing)
+ case .exchangingKeys:
+ statusState = .waiting(.generatingKeys)
+ case let .active(_, signalInfo):
+ statusState = .active(StatusView.ActiveState(signalStrength: signalInfo.quality))
+ }
+ let statusSize = self.statusView.update(state: statusState)
+ self.statusView.frame = CGRect(origin: CGPoint(x: (params.size.width - statusSize.width) * 0.5, y: titleFrame.maxY + CGFloat.animationInterpolator.interpolate(from: 4.0, to: 0.0, fraction: self.videoDisplayFraction.value)), size: statusSize)
let blobFrame = CGRect(origin: CGPoint(x: floor(avatarFrame.midX - blobSize * 0.5), y: floor(avatarFrame.midY - blobSize * 0.5)), size: CGSize(width: blobSize, height: blobSize))
@@ -114,7 +145,7 @@ final class ContentView: UIView {
self.blobLayer.transform = CATransform3DMakeScale(1.0 + avatarScale * 2.0, 1.0 + avatarScale * 2.0, 1.0)
let videoResolution = CGSize(width: 400.0, height: 400.0)
- let videoFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: size)
+ let videoFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: params.size)
let videoRenderingSize = CGSize(width: videoResolution.width * 2.0, height: videoResolution.height * 2.0)
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/MainVideoLayer.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/MainVideoLayer.swift
index c360549739..0be79faf8d 100644
--- a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/MainVideoLayer.swift
+++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/MainVideoLayer.swift
@@ -3,6 +3,7 @@ import UIKit
import MetalKit
import MetalPerformanceShaders
import Accelerate
+import MetalEngine
func imageToCVPixelBuffer(image: UIImage) -> CVPixelBuffer? {
guard let cgImage = image.cgImage, let data = cgImage.dataProvider?.data, let bytes = CFDataGetBytePtr(data), let colorSpace = cgImage.colorSpace, case .rgb = colorSpace.model, cgImage.bitsPerPixel / cgImage.bitsPerComponent == 4 else {
@@ -36,10 +37,10 @@ func imageToCVPixelBuffer(image: UIImage) -> CVPixelBuffer? {
return pixelBuffer
}
-final class MainVideoLayer: MetalSubjectLayer, MetalSubject {
- var internalData: MetalSubjectInternalData?
+final class MainVideoLayer: MetalEngineSubjectLayer, MetalEngineSubject {
+ var internalData: MetalEngineSubjectInternalData?
- let blurredLayer: MetalSubjectLayer
+ let blurredLayer: MetalEngineSubjectLayer
final class BlurState: ComputeState {
let computePipelineStateYUVToRGBA: MTLComputePipelineState
@@ -47,10 +48,10 @@ final class MainVideoLayer: MetalSubjectLayer, MetalSubject {
let computePipelineStateVertical: MTLComputePipelineState
let downscaleKernel: MPSImageBilinearScale
- required init?(
- device: MTLDevice,
- library: MTLLibrary
- ) {
+ required init?(device: MTLDevice) {
+ guard let library = metalLibrary(device: device) else {
+ return nil
+ }
guard let functionVideoYUVToRGBA = library.makeFunction(name: "videoYUVToRGBA") else {
return nil
}
@@ -79,10 +80,10 @@ final class MainVideoLayer: MetalSubjectLayer, MetalSubject {
final class RenderState: RenderToLayerState {
let pipelineState: MTLRenderPipelineState
- required init?(
- device: MTLDevice,
- library: MTLLibrary
- ) {
+ required init?(device: MTLDevice) {
+ guard let library = metalLibrary(device: device) else {
+ return nil
+ }
guard let vertexFunction = library.makeFunction(name: "mainVideoVertex"), let fragmentFunction = library.makeFunction(name: "mainVideoFragment") else {
return nil
}
@@ -115,13 +116,13 @@ final class MainVideoLayer: MetalSubjectLayer, MetalSubject {
private var blurredVerticalTexture: PooledTexture?
override init() {
- self.blurredLayer = MetalSubjectLayer()
+ self.blurredLayer = MetalEngineSubjectLayer()
super.init()
}
override init(layer: Any) {
- self.blurredLayer = MetalSubjectLayer()
+ self.blurredLayer = MetalEngineSubjectLayer()
super.init(layer: layer)
}
@@ -130,7 +131,7 @@ final class MainVideoLayer: MetalSubjectLayer, MetalSubject {
fatalError("init(coder:) has not been implemented")
}
- func update(context: MetalSubjectContext) {
+ func update(context: MetalEngineSubjectContext) {
if self.isHidden {
return
}
@@ -143,16 +144,16 @@ final class MainVideoLayer: MetalSubjectLayer, MetalSubject {
let rgbaTextureSpec = TextureSpec(width: videoTextures.y.width, height: videoTextures.y.height, pixelFormat: .rgba8UnsignedNormalized)
if self.rgbaTexture == nil || self.rgbaTexture?.spec != rgbaTextureSpec {
- self.rgbaTexture = MetalContext.shared.pooledTexture(spec: rgbaTextureSpec)
+ self.rgbaTexture = MetalEngine.shared.pooledTexture(spec: rgbaTextureSpec)
}
if self.downscaledTexture == nil {
- self.downscaledTexture = MetalContext.shared.pooledTexture(spec: TextureSpec(width: 128, height: 128, pixelFormat: .rgba8UnsignedNormalized))
+ self.downscaledTexture = MetalEngine.shared.pooledTexture(spec: TextureSpec(width: 128, height: 128, pixelFormat: .rgba8UnsignedNormalized))
}
if self.blurredHorizontalTexture == nil {
- self.blurredHorizontalTexture = MetalContext.shared.pooledTexture(spec: TextureSpec(width: 128, height: 128, pixelFormat: .rgba8UnsignedNormalized))
+ self.blurredHorizontalTexture = MetalEngine.shared.pooledTexture(spec: TextureSpec(width: 128, height: 128, pixelFormat: .rgba8UnsignedNormalized))
}
if self.blurredVerticalTexture == nil {
- self.blurredVerticalTexture = MetalContext.shared.pooledTexture(spec: TextureSpec(width: 128, height: 128, pixelFormat: .rgba8UnsignedNormalized))
+ self.blurredVerticalTexture = MetalEngine.shared.pooledTexture(spec: TextureSpec(width: 128, height: 128, pixelFormat: .rgba8UnsignedNormalized))
}
guard let rgbaTexture = self.rgbaTexture?.get(context: context) else {
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/StatusView.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/StatusView.swift
index 24b6f5ba7d..0725044128 100644
--- a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/StatusView.swift
+++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/StatusView.swift
@@ -1,5 +1,6 @@
import Foundation
import UIKit
+import Display
private final class AnimatedDotsLayer: SimpleLayer {
private let dotLayers: [SimpleLayer]
@@ -76,8 +77,18 @@ final class StatusView: UIView {
case ringing
case generatingKeys
}
+
+ struct ActiveState {
+ var signalStrength: Double
+
+ init(signalStrength: Double) {
+ self.signalStrength = signalStrength
+ }
+ }
+
enum State {
case waiting(WaitingState)
+ case active(ActiveState)
}
private var textView: TextView
@@ -110,6 +121,9 @@ final class StatusView: UIView {
case .generatingKeys:
textString = "Exchanging encryption keys"
}
+ case let .active(activeState):
+ textString = "0:00"
+ let _ = activeState
}
let textSize = self.textView.update(string: textString, fontSize: 16.0, fontWeight: 0.0, constrainedWidth: 200.0)
self.textView.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: textSize)
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/ContentOverlayLayer.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/ContentOverlayLayer.swift
index 7e29a77a13..445f16f6c4 100644
--- a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/ContentOverlayLayer.swift
+++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/ContentOverlayLayer.swift
@@ -1,5 +1,6 @@
import Foundation
import UIKit
+import Display
final class ContentOverlayLayer: SimpleLayer {
private struct Params: Equatable {
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/MirroringLayer.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/MirroringLayer.swift
index 410c8ddb37..801d6c06e0 100644
--- a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/MirroringLayer.swift
+++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/MirroringLayer.swift
@@ -1,5 +1,6 @@
import Foundation
import UIKit
+import Display
final class MirroringLayer: SimpleLayer {
var targetLayer: SimpleLayer?
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/PrivateCallScreen.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/PrivateCallScreen.swift
index 1653cbb78f..059df289dc 100644
--- a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/PrivateCallScreen.swift
+++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/PrivateCallScreen.swift
@@ -1,7 +1,42 @@
import Foundation
import UIKit
+import Display
+import MetalEngine
public final class PrivateCallScreen: UIView {
+ public struct State: Equatable {
+ public struct SignalInfo: Equatable {
+ public var quality: Double
+
+ public init(quality: Double) {
+ self.quality = quality
+ }
+ }
+
+ public enum LifecycleState: Equatable {
+ case connecting
+ case ringing
+ case exchangingKeys
+ case active(startTime: Double, signalInfo: SignalInfo)
+ }
+
+ public var lifecycleState: LifecycleState
+
+ public init(lifecycleState: LifecycleState) {
+ self.lifecycleState = lifecycleState
+ }
+ }
+
+ private struct Params: Equatable {
+ var size: CGSize
+ var insets: UIEdgeInsets
+
+ init(size: CGSize, insets: UIEdgeInsets) {
+ self.size = size
+ self.insets = insets
+ }
+ }
+
private let backgroundLayer: CallBackgroundLayer
private let contentOverlayLayer: ContentOverlayLayer
private let contentOverlayContainer: ContentOverlayContainer
@@ -13,6 +48,18 @@ public final class PrivateCallScreen: UIView {
private let buttonGroupView: ButtonGroupView
+ public var state: State = State(lifecycleState: .connecting) {
+ didSet {
+ if self.state != oldValue {
+ if let params = self.params {
+ self.updateInternal(params: params, animated: true)
+ }
+ }
+ }
+ }
+
+ private var params: Params?
+
public override init(frame: CGRect) {
self.blurContentsLayer = SimpleLayer()
@@ -44,6 +91,26 @@ public final class PrivateCallScreen: UIView {
self.contentOverlayContainer.addSubview(self.buttonGroupView)
+ self.buttonGroupView.audioPressed = { [weak self] in
+ guard let self else {
+ return
+ }
+
+ var state = self.state
+ switch state.lifecycleState {
+ case .connecting, .ringing, .exchangingKeys:
+ state.lifecycleState = .active(startTime: CFAbsoluteTimeGetCurrent(), signalInfo: State.SignalInfo(quality: 1.0))
+ case let .active(startTime, signalInfo):
+ if signalInfo.quality == 1.0 {
+ state.lifecycleState = .active(startTime: startTime, signalInfo: State.SignalInfo(quality: 0.2))
+ } else if signalInfo.quality == 0.2 {
+ state.lifecycleState = .connecting
+ }
+
+ }
+ self.state = state
+ }
+
self.buttonGroupView.toggleVideo = { [weak self] in
guard let self else {
return
@@ -65,9 +132,18 @@ public final class PrivateCallScreen: UIView {
}
public func update(size: CGSize, insets: UIEdgeInsets) {
- let backgroundFrame = CGRect(origin: CGPoint(), size: size)
+ let params = Params(size: size, insets: insets)
+ if self.params == params {
+ return
+ }
+ self.params = params
+ self.updateInternal(params: params, animated: false)
+ }
- let aspect: CGFloat = size.width / size.height
+ private func updateInternal(params: Params, animated: Bool) {
+ let backgroundFrame = CGRect(origin: CGPoint(), size: params.size)
+
+ let aspect: CGFloat = params.size.width / params.size.height
let sizeNorm: CGFloat = 64.0
let renderingSize = CGSize(width: floor(sizeNorm * aspect), height: sizeNorm)
let edgeSize: Int = 2
@@ -77,18 +153,36 @@ public final class PrivateCallScreen: UIView {
self.backgroundLayer.renderSpec = RenderLayerSpec(size: RenderSize(width: Int(renderingSize.width) + edgeSize * 2, height: Int(renderingSize.height) + edgeSize * 2))
self.backgroundLayer.frame = visualBackgroundFrame
- self.contentOverlayLayer.frame = CGRect(origin: CGPoint(), size: size)
- self.contentOverlayLayer.update(size: size, contentInsets: UIEdgeInsets())
+ let backgroundStateIndex: Int
+ switch self.state.lifecycleState {
+ case .connecting:
+ backgroundStateIndex = 0
+ case .ringing:
+ backgroundStateIndex = 0
+ case .exchangingKeys:
+ backgroundStateIndex = 0
+ case let .active(_, signalInfo):
+ if signalInfo.quality <= 0.2 {
+ backgroundStateIndex = 2
+ } else {
+ backgroundStateIndex = 1
+ }
+ }
+ self.backgroundLayer.update(stateIndex: backgroundStateIndex, animated: animated)
- self.contentOverlayContainer.frame = CGRect(origin: CGPoint(), size: size)
+ self.contentOverlayLayer.frame = CGRect(origin: CGPoint(), size: params.size)
+ self.contentOverlayLayer.update(size: params.size, contentInsets: UIEdgeInsets())
+
+ self.contentOverlayContainer.frame = CGRect(origin: CGPoint(), size: params.size)
self.blurBackgroundLayer.renderSpec = RenderLayerSpec(size: RenderSize(width: Int(renderingSize.width) + edgeSize * 2, height: Int(renderingSize.height) + edgeSize * 2))
self.blurBackgroundLayer.frame = visualBackgroundFrame
+ self.blurBackgroundLayer.update(stateIndex: backgroundStateIndex, animated: animated)
- self.buttonGroupView.frame = CGRect(origin: CGPoint(), size: size)
- self.buttonGroupView.update(size: size)
+ self.buttonGroupView.frame = CGRect(origin: CGPoint(), size: params.size)
+ self.buttonGroupView.update(size: params.size)
- self.contentView.frame = CGRect(origin: CGPoint(), size: size)
- self.contentView.update(size: size, insets: insets)
+ self.contentView.frame = CGRect(origin: CGPoint(), size: params.size)
+ self.contentView.update(size: params.size, insets: params.insets, state: self.state)
}
}
diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/SimpleLayer.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/SimpleLayer.swift
deleted file mode 100644
index 4bab9bcb80..0000000000
--- a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/SimpleLayer.swift
+++ /dev/null
@@ -1,67 +0,0 @@
-import Foundation
-import UIKit
-
-public final class NullActionClass: NSObject, CAAction {
- @objc public func run(forKey event: String, object anObject: Any, arguments dict: [AnyHashable : Any]?) {
- }
-}
-
-public let nullAction = NullActionClass()
-
-open class SimpleLayer: CALayer {
- public var didEnterHierarchy: (() -> Void)?
- public var didExitHierarchy: (() -> Void)?
- public private(set) var isInHierarchy: Bool = false
-
- override open func action(forKey event: String) -> CAAction? {
- if event == kCAOnOrderIn {
- self.isInHierarchy = true
- self.didEnterHierarchy?()
- } else if event == kCAOnOrderOut {
- self.isInHierarchy = false
- self.didExitHierarchy?()
- }
- return nullAction
- }
-
- override public init() {
- super.init()
- }
-
- override public init(layer: Any) {
- super.init(layer: layer)
- }
-
- required public init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
-}
-
-open class SimpleShapeLayer: CAShapeLayer {
- public var didEnterHierarchy: (() -> Void)?
- public var didExitHierarchy: (() -> Void)?
- public private(set) var isInHierarchy: Bool = false
-
- override open func action(forKey event: String) -> CAAction? {
- if event == kCAOnOrderIn {
- self.isInHierarchy = true
- self.didEnterHierarchy?()
- } else if event == kCAOnOrderOut {
- self.isInHierarchy = false
- self.didExitHierarchy?()
- }
- return nullAction
- }
-
- override public init() {
- super.init()
- }
-
- override public init(layer: Any) {
- super.init(layer: layer)
- }
-
- required public init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
-}
diff --git a/submodules/TelegramUI/Sources/AppDelegate.swift b/submodules/TelegramUI/Sources/AppDelegate.swift
index 159dc94d10..75737a2d13 100644
--- a/submodules/TelegramUI/Sources/AppDelegate.swift
+++ b/submodules/TelegramUI/Sources/AppDelegate.swift
@@ -41,6 +41,7 @@ import DeviceProximity
import MediaEditor
import TelegramUIDeclareEncodables
import ContextMenuScreen
+import MetalEngine
#if canImport(AppCenter)
import AppCenter
@@ -341,6 +342,8 @@ private func extractAccountManagerState(records: AccountRecordsView