mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
[WIP] MetalEngine
This commit is contained in:
parent
5976d495b0
commit
88a0fd7a81
@ -71,6 +71,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
case keepChatNavigationStack(PresentationTheme, Bool)
|
||||
case skipReadHistory(PresentationTheme, Bool)
|
||||
case unidirectionalSwipeToReply(Bool)
|
||||
case dustEffect(Bool)
|
||||
case crashOnSlowQueries(PresentationTheme, Bool)
|
||||
case crashOnMemoryPressure(PresentationTheme, Bool)
|
||||
case clearTips(PresentationTheme)
|
||||
@ -119,7 +120,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
return DebugControllerSection.logs.rawValue
|
||||
case .logToFile, .logToConsole, .redactSensitiveData:
|
||||
return DebugControllerSection.logging.rawValue
|
||||
case .keepChatNavigationStack, .skipReadHistory, .unidirectionalSwipeToReply, .crashOnSlowQueries, .crashOnMemoryPressure:
|
||||
case .keepChatNavigationStack, .skipReadHistory, .unidirectionalSwipeToReply, .dustEffect, .crashOnSlowQueries, .crashOnMemoryPressure:
|
||||
return DebugControllerSection.experiments.rawValue
|
||||
case .clearTips, .resetNotifications, .crash, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetCacheIndex, .reindexCache, .resetBiometricsData, .resetWebViewCache, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .storiesExperiment, .storiesJpegExperiment, .playlistPlayback, .enableQuickReactionSwitch, .voiceConference, .experimentalCompatibility, .enableDebugDataDisplay, .acceleratedStickers, .inlineForums, .localTranscription, .enableReactionOverrides, .restorePurchases:
|
||||
return DebugControllerSection.experiments.rawValue
|
||||
@ -168,70 +169,72 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
return 15
|
||||
case .unidirectionalSwipeToReply:
|
||||
return 16
|
||||
case .crashOnSlowQueries:
|
||||
case .dustEffect:
|
||||
return 17
|
||||
case .crashOnMemoryPressure:
|
||||
case .crashOnSlowQueries:
|
||||
return 18
|
||||
case .clearTips:
|
||||
case .crashOnMemoryPressure:
|
||||
return 19
|
||||
case .resetNotifications:
|
||||
case .clearTips:
|
||||
return 20
|
||||
case .crash:
|
||||
case .resetNotifications:
|
||||
return 21
|
||||
case .resetData:
|
||||
case .crash:
|
||||
return 22
|
||||
case .resetDatabase:
|
||||
case .resetData:
|
||||
return 23
|
||||
case .resetDatabaseAndCache:
|
||||
case .resetDatabase:
|
||||
return 24
|
||||
case .resetHoles:
|
||||
case .resetDatabaseAndCache:
|
||||
return 25
|
||||
case .reindexUnread:
|
||||
case .resetHoles:
|
||||
return 26
|
||||
case .resetCacheIndex:
|
||||
case .reindexUnread:
|
||||
return 27
|
||||
case .reindexCache:
|
||||
case .resetCacheIndex:
|
||||
return 28
|
||||
case .resetBiometricsData:
|
||||
case .reindexCache:
|
||||
return 29
|
||||
case .resetWebViewCache:
|
||||
case .resetBiometricsData:
|
||||
return 30
|
||||
case .optimizeDatabase:
|
||||
case .resetWebViewCache:
|
||||
return 31
|
||||
case .photoPreview:
|
||||
case .optimizeDatabase:
|
||||
return 32
|
||||
case .knockoutWallpaper:
|
||||
case .photoPreview:
|
||||
return 33
|
||||
case .experimentalCompatibility:
|
||||
case .knockoutWallpaper:
|
||||
return 34
|
||||
case .enableDebugDataDisplay:
|
||||
case .experimentalCompatibility:
|
||||
return 35
|
||||
case .acceleratedStickers:
|
||||
case .enableDebugDataDisplay:
|
||||
return 36
|
||||
case .inlineForums:
|
||||
case .acceleratedStickers:
|
||||
return 37
|
||||
case .localTranscription:
|
||||
case .inlineForums:
|
||||
return 38
|
||||
case .enableReactionOverrides:
|
||||
case .localTranscription:
|
||||
return 39
|
||||
case .restorePurchases:
|
||||
case .enableReactionOverrides:
|
||||
return 40
|
||||
case .logTranslationRecognition:
|
||||
case .restorePurchases:
|
||||
return 41
|
||||
case .resetTranslationStates:
|
||||
case .logTranslationRecognition:
|
||||
return 42
|
||||
case .storiesExperiment:
|
||||
case .resetTranslationStates:
|
||||
return 43
|
||||
case .storiesJpegExperiment:
|
||||
case .storiesExperiment:
|
||||
return 44
|
||||
case .playlistPlayback:
|
||||
case .storiesJpegExperiment:
|
||||
return 45
|
||||
case .enableQuickReactionSwitch:
|
||||
case .playlistPlayback:
|
||||
return 46
|
||||
case .voiceConference:
|
||||
case .enableQuickReactionSwitch:
|
||||
return 47
|
||||
case .voiceConference:
|
||||
return 48
|
||||
case let .preferredVideoCodec(index, _, _, _):
|
||||
return 48 + index
|
||||
return 49 + index
|
||||
case .disableVideoAspectScaling:
|
||||
return 100
|
||||
case .enableNetworkFramework:
|
||||
@ -939,6 +942,14 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
return settings
|
||||
}).start()
|
||||
})
|
||||
case let .dustEffect(value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: "Dust Effect", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
let _ = updateExperimentalUISettingsInteractively(accountManager: arguments.sharedContext.accountManager, { settings in
|
||||
var settings = settings
|
||||
settings.dustEffect = value
|
||||
return settings
|
||||
}).start()
|
||||
})
|
||||
case let .crashOnSlowQueries(_, value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: "Crash when slow", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
let _ = updateExperimentalUISettingsInteractively(accountManager: arguments.sharedContext.accountManager, { settings in
|
||||
@ -1387,6 +1398,7 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present
|
||||
entries.append(.skipReadHistory(presentationData.theme, experimentalSettings.skipReadHistory))
|
||||
#endif
|
||||
entries.append(.unidirectionalSwipeToReply(experimentalSettings.unidirectionalSwipeToReply))
|
||||
entries.append(.dustEffect(experimentalSettings.dustEffect))
|
||||
}
|
||||
entries.append(.crashOnSlowQueries(presentationData.theme, experimentalSettings.crashOnLongQueries))
|
||||
entries.append(.crashOnMemoryPressure(presentationData.theme, experimentalSettings.crashOnMemoryPressure))
|
||||
|
@ -4669,6 +4669,14 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func forEachRemovedItemNode(_ f: (ASDisplayNode) -> Void) {
|
||||
for itemNode in self.itemNodes {
|
||||
if itemNode.index == nil {
|
||||
f(itemNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func enumerateItemNodes(_ f: (ASDisplayNode) -> Bool) {
|
||||
for itemNode in self.itemNodes {
|
||||
|
@ -411,6 +411,7 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/ContextMenuScreen",
|
||||
"//submodules/TelegramUI/Components/PeerAllowedReactionsScreen",
|
||||
"//submodules/MetalEngine",
|
||||
"//submodules/TelegramUI/Components/DustEffect",
|
||||
] + select({
|
||||
"@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets,
|
||||
"//build-system:ios_sim_arm64": [],
|
||||
|
@ -5382,4 +5382,22 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
||||
override public func contentFrame() -> CGRect {
|
||||
return self.backgroundNode.frame
|
||||
}
|
||||
|
||||
override public func makeContentSnapshot() -> (UIImage, CGRect)? {
|
||||
UIGraphicsBeginImageContextWithOptions(self.backgroundNode.view.bounds.size, false, 0.0)
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
|
||||
context.translateBy(x: -self.backgroundNode.frame.minX, y: -self.insets.top - self.backgroundNode.frame.minY)
|
||||
|
||||
self.view.drawHierarchy(in: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: self.view.bounds.size), afterScreenUpdates: false)
|
||||
|
||||
let image = UIGraphicsGetImageFromCurrentImageContext()
|
||||
UIGraphicsEndImageContext()
|
||||
|
||||
guard let image else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return (image, self.backgroundNode.frame)
|
||||
}
|
||||
}
|
||||
|
@ -716,6 +716,10 @@ open class ChatMessageItemView: ListViewItemNode, ChatMessageItemNodeProtocol {
|
||||
return nil
|
||||
}
|
||||
|
||||
open func makeContentSnapshot() -> (UIImage, CGRect)? {
|
||||
return nil
|
||||
}
|
||||
|
||||
open func getMessageContextSourceNode(stableId: UInt32?) -> ContextExtractedContentContainingNode? {
|
||||
return nil
|
||||
}
|
||||
|
@ -124,6 +124,6 @@ fragment half4 dustEffectFragment(
|
||||
) {
|
||||
constexpr sampler sampler(coord::normalized, address::clamp_to_edge, filter::linear);
|
||||
|
||||
half3 color = inTexture.sample(sampler, float2(in.uv.x, 1.0 - in.uv.y)).rgb;
|
||||
return half4(color * in.alpha, in.alpha);
|
||||
half4 color = inTexture.sample(sampler, float2(in.uv.x, 1.0 - in.uv.y));
|
||||
return color * in.alpha;
|
||||
}
|
||||
|
@ -95,9 +95,10 @@ public final class DustEffectLayer: MetalEngineSubjectLayer, MetalEngineSubject
|
||||
}
|
||||
|
||||
private var updateLink: SharedDisplayLinkDriver.Link?
|
||||
|
||||
private var items: [Item] = []
|
||||
|
||||
public var becameEmpty: (() -> Void)?
|
||||
|
||||
override public init() {
|
||||
super.init()
|
||||
|
||||
@ -127,14 +128,20 @@ public final class DustEffectLayer: MetalEngineSubjectLayer, MetalEngineSubject
|
||||
}
|
||||
|
||||
private func updateItems() {
|
||||
var didRemoveItems = false
|
||||
for i in (0 ..< self.items.count).reversed() {
|
||||
self.items[i].phase += 1.0 / 60.0
|
||||
self.items[i].phase += (1.0 / 60.0) / Float(UIView.animationDurationFactor())
|
||||
|
||||
if self.items[i].phase >= 4.0 {
|
||||
self.items.remove(at: i)
|
||||
didRemoveItems = true
|
||||
}
|
||||
}
|
||||
self.updateNeedsAnimation()
|
||||
|
||||
if didRemoveItems && self.items.isEmpty {
|
||||
self.becameEmpty?()
|
||||
}
|
||||
}
|
||||
|
||||
private func updateNeedsAnimation() {
|
||||
@ -222,7 +229,7 @@ public final class DustEffectLayer: MetalEngineSubjectLayer, MetalEngineSubject
|
||||
computeEncoder.setBytes(&particleCount, length: 4 * 2, index: 1)
|
||||
var phase = item.phase
|
||||
computeEncoder.setBytes(&phase, length: 4, index: 2)
|
||||
var timeStep: Float = 1.0 / 60.0
|
||||
var timeStep: Float = (1.0 / 60.0) / Float(UIView.animationDurationFactor())
|
||||
computeEncoder.setBytes(&timeStep, length: 4, index: 3)
|
||||
computeEncoder.dispatchThreadgroups(threadgroupCount, threadsPerThreadgroup: threadgroupSize)
|
||||
}
|
||||
|
@ -17117,11 +17117,22 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
}
|
||||
contextItems.append(.action(ContextMenuActionItem(text: localOptionText, textColor: .destructive, icon: { _ in nil }, action: { [weak self] _, f in
|
||||
contextItems.append(.action(ContextMenuActionItem(text: localOptionText, textColor: .destructive, icon: { _ in nil }, action: { [weak self] c, f in
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } })
|
||||
let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: unsendPersonalMessages ? .forEveryone : .forLocalPeer).startStandalone()
|
||||
f(.dismissWithoutContent)
|
||||
|
||||
if strongSelf.context.sharedContext.immediateExperimentalUISettings.dustEffect {
|
||||
c.dismiss(completion: { [weak strongSelf] in
|
||||
guard let strongSelf else {
|
||||
return
|
||||
}
|
||||
strongSelf.chatDisplayNode.historyNode.setCurrentDeleteAnimationCorrelationIds(messageIds)
|
||||
let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: unsendPersonalMessages ? .forEveryone : .forLocalPeer).startStandalone()
|
||||
})
|
||||
} else {
|
||||
f(.dismissWithoutContent)
|
||||
let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: unsendPersonalMessages ? .forEveryone : .forLocalPeer).startStandalone()
|
||||
}
|
||||
}
|
||||
})))
|
||||
items.append(ActionSheetButtonItem(title: localOptionText, color: .destructive, action: { [weak self, weak actionSheet] in
|
||||
|
@ -31,6 +31,7 @@ import ChatMessageItemImpl
|
||||
import ChatMessageItemView
|
||||
import ChatMessageTransitionNode
|
||||
import ChatControllerInteraction
|
||||
import DustEffect
|
||||
|
||||
struct ChatTopVisibleMessageRange: Equatable {
|
||||
var lowerBound: MessageIndex
|
||||
@ -699,6 +700,8 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
|
||||
private var toLang: String?
|
||||
|
||||
private var dustEffectLayer: DustEffectLayer?
|
||||
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>), chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, tagMask: MessageTags?, source: ChatHistoryListSource = .default, subject: ChatControllerSubject?, controllerInteraction: ChatControllerInteraction, selectedMessages: Signal<Set<MessageId>?, NoError>, mode: ChatHistoryListMode = .bubbles, messageTransitionNode: @escaping () -> ChatMessageTransitionNodeImpl? = { nil }) {
|
||||
var tagMask = tagMask
|
||||
if case .pinnedMessages = subject {
|
||||
@ -3195,6 +3198,50 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
}
|
||||
}
|
||||
|
||||
if let currentDeleteAnimationCorrelationIds = strongSelf.currentDeleteAnimationCorrelationIds {
|
||||
var foundItemNodes: [ChatMessageItemView] = []
|
||||
strongSelf.forEachRemovedItemNode { itemNode in
|
||||
if let itemNode = itemNode as? ChatMessageItemView, let item = itemNode.item {
|
||||
for (message, _) in item.content {
|
||||
if currentDeleteAnimationCorrelationIds.contains(message.id) {
|
||||
foundItemNodes.append(itemNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !foundItemNodes.isEmpty {
|
||||
strongSelf.currentDeleteAnimationCorrelationIds = nil
|
||||
if strongSelf.context.sharedContext.immediateExperimentalUISettings.dustEffect {
|
||||
if strongSelf.dustEffectLayer == nil {
|
||||
let dustEffectLayer = DustEffectLayer()
|
||||
dustEffectLayer.position = strongSelf.bounds.center
|
||||
dustEffectLayer.bounds = CGRect(origin: CGPoint(), size: strongSelf.bounds.size)
|
||||
strongSelf.dustEffectLayer = dustEffectLayer
|
||||
dustEffectLayer.zPosition = 10.0
|
||||
dustEffectLayer.transform = CATransform3DMakeRotation(CGFloat(Double.pi), 0.0, 0.0, 1.0)
|
||||
strongSelf.layer.addSublayer(dustEffectLayer)
|
||||
dustEffectLayer.becameEmpty = { [weak strongSelf] in
|
||||
guard let strongSelf else {
|
||||
return
|
||||
}
|
||||
strongSelf.dustEffectLayer?.removeFromSuperlayer()
|
||||
strongSelf.dustEffectLayer = nil
|
||||
}
|
||||
}
|
||||
if let dustEffectLayer = strongSelf.dustEffectLayer {
|
||||
for itemNode in foundItemNodes {
|
||||
guard let (image, subFrame) = itemNode.makeContentSnapshot() else {
|
||||
continue
|
||||
}
|
||||
let itemFrame = itemNode.layer.convert(subFrame, to: dustEffectLayer)
|
||||
dustEffectLayer.addItem(frame: itemFrame, image: image)
|
||||
itemNode.isHidden = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !newIncomingReactions.isEmpty {
|
||||
let messageIds = Array(newIncomingReactions.keys)
|
||||
|
||||
@ -3872,6 +3919,11 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
func setCurrentSendAnimationCorrelationIds(_ value: Set<Int64>?) {
|
||||
self.currentSendAnimationCorrelationIds = value
|
||||
}
|
||||
|
||||
private var currentDeleteAnimationCorrelationIds: Set<MessageId>?
|
||||
func setCurrentDeleteAnimationCorrelationIds(_ value: Set<MessageId>?) {
|
||||
self.currentDeleteAnimationCorrelationIds = value
|
||||
}
|
||||
|
||||
var animationCorrelationMessagesFound: (([Int64: ChatMessageItemView]) -> Void)?
|
||||
|
||||
|
@ -54,6 +54,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
public var storiesJpegExperiment: Bool
|
||||
public var crashOnMemoryPressure: Bool
|
||||
public var unidirectionalSwipeToReply: Bool
|
||||
public var dustEffect: Bool
|
||||
|
||||
public static var defaultSettings: ExperimentalUISettings {
|
||||
return ExperimentalUISettings(
|
||||
@ -85,7 +86,8 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
storiesExperiment: false,
|
||||
storiesJpegExperiment: false,
|
||||
crashOnMemoryPressure: false,
|
||||
unidirectionalSwipeToReply: false
|
||||
unidirectionalSwipeToReply: false,
|
||||
dustEffect: false
|
||||
)
|
||||
}
|
||||
|
||||
@ -118,7 +120,8 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
storiesExperiment: Bool,
|
||||
storiesJpegExperiment: Bool,
|
||||
crashOnMemoryPressure: Bool,
|
||||
unidirectionalSwipeToReply: Bool
|
||||
unidirectionalSwipeToReply: Bool,
|
||||
dustEffect: Bool
|
||||
) {
|
||||
self.keepChatNavigationStack = keepChatNavigationStack
|
||||
self.skipReadHistory = skipReadHistory
|
||||
@ -149,6 +152,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
self.storiesJpegExperiment = storiesJpegExperiment
|
||||
self.crashOnMemoryPressure = crashOnMemoryPressure
|
||||
self.unidirectionalSwipeToReply = unidirectionalSwipeToReply
|
||||
self.dustEffect = dustEffect
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
@ -183,6 +187,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
self.storiesJpegExperiment = try container.decodeIfPresent(Bool.self, forKey: "storiesJpegExperiment") ?? false
|
||||
self.crashOnMemoryPressure = try container.decodeIfPresent(Bool.self, forKey: "crashOnMemoryPressure") ?? false
|
||||
self.unidirectionalSwipeToReply = try container.decodeIfPresent(Bool.self, forKey: "unidirectionalSwipeToReply") ?? false
|
||||
self.dustEffect = try container.decodeIfPresent(Bool.self, forKey: "dustEffect") ?? false
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
@ -217,6 +222,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
try container.encode(self.storiesJpegExperiment, forKey: "storiesJpegExperiment")
|
||||
try container.encode(self.crashOnMemoryPressure, forKey: "crashOnMemoryPressure")
|
||||
try container.encode(self.unidirectionalSwipeToReply, forKey: "unidirectionalSwipeToReply")
|
||||
try container.encode(self.dustEffect, forKey: "dustEffect")
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user