Skip to content

Commit 050d5ab

Browse files
committed
[WIP] Update GraphReuse
1 parent 486b073 commit 050d5ab

File tree

5 files changed

+106
-43
lines changed

5 files changed

+106
-43
lines changed

Sources/OpenSwiftUICore/Graph/GraphInputs.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,13 +114,14 @@ public struct _GraphInputs {
114114

115115
private mutating func recordReusableInput<T>(_ input: T.Type) where T: GraphInput, T.Value: GraphReusable {
116116
let filter = BloomFilter(type: input)
117-
let reusableInputs = customInputs[ReusableInputs.self]
118-
if reusableInputs.stack.top == T.self {
117+
let inputs = customInputs[ReusableInputs.self]
118+
let stack = inputs.stack
119+
guard stack.top != T.self else {
119120
return
120121
}
121122
customInputs[ReusableInputs.self] = ReusableInputStorage(
122-
filter: reusableInputs.filter.union(filter),
123-
stack: .node(value: T.self, next: reusableInputs.stack)
123+
filter: inputs.filter.union(filter),
124+
stack: .node(value: T.self, next: stack)
124125
)
125126
}
126127

Sources/OpenSwiftUICore/Graph/GraphReuse.swift

Lines changed: 90 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -41,65 +41,123 @@ extension _GraphValue where Value: GraphReusable {
4141
package static var isTriviallyReusable: Bool { Value.isTriviallyReusable }
4242
}
4343

44-
//extension _GraphInputs : GraphReusable {
45-
// package mutating func makeReusable(indirectMap: IndirectAttributeMap)
46-
// package func tryToReuse(by other: _GraphInputs, indirectMap: IndirectAttributeMap, testOnly: Bool) -> Bool
47-
//}
44+
// MARK: - _GraphInputs + GraphReusable [WIP]
4845

49-
//extension _GraphInputs {
50-
// private func reuseCustomInputs(by other: _GraphInputs, indirectMap: IndirectAttributeMap, testOnly: Bool) -> Bool {
51-
// Log.graphReuse("Reuse failed: custom input \(Self.self)")
52-
// }
53-
//}
46+
extension _GraphInputs: GraphReusable {
47+
package mutating func makeReusable(indirectMap: IndirectAttributeMap) {
48+
time.makeReusable(indirectMap: indirectMap)
49+
phase.makeReusable(indirectMap: indirectMap)
50+
changedDebugProperties.insert(.phase)
51+
environment.makeReusable(indirectMap: indirectMap)
52+
detachEnvironmentInputs()
53+
changedDebugProperties.insert(.environment)
54+
transaction.makeReusable(indirectMap: indirectMap)
55+
func project<Input>(_ type: Input.Type) where Input: GraphInput {
56+
guard !Input.isTriviallyReusable else {
57+
return
58+
}
59+
var value = self[Input.self]
60+
Input.makeReusable(indirectMap: indirectMap, value: &value)
61+
self[Input.self] = value
62+
}
63+
let stack = customInputs[ReusableInputs.self].stack
64+
for value in stack {
65+
project(value)
66+
}
67+
}
68+
69+
package func tryToReuse(by other: _GraphInputs, indirectMap: IndirectAttributeMap, testOnly: Bool) -> Bool {
70+
guard time.tryToReuse(by: other.time, indirectMap: indirectMap, testOnly: testOnly),
71+
phase.tryToReuse(by: other.phase, indirectMap: indirectMap, testOnly: testOnly),
72+
environment.tryToReuse(by: other.environment, indirectMap: indirectMap, testOnly: testOnly),
73+
transaction.tryToReuse(by: other.transaction, indirectMap: indirectMap, testOnly: testOnly)
74+
else {
75+
Log.graphReuse("Reuse failed: standard inputs")
76+
return false
77+
}
78+
return reuseCustomInputs(by: other, indirectMap: indirectMap, testOnly: testOnly)
79+
}
80+
81+
private func reuseCustomInputs(by other: _GraphInputs, indirectMap: IndirectAttributeMap, testOnly: Bool) -> Bool {
82+
let reusableInputs = customInputs[ReusableInputs.self]
83+
let otherReusableInputs = other.customInputs[ReusableInputs.self]
84+
guard reusableInputs.filter == otherReusableInputs.filter else {
85+
return false
86+
}
87+
var reusableInputsArray: [ObjectIdentifier] = []
88+
for value in reusableInputs.stack {
89+
reusableInputsArray.append(ObjectIdentifier(value))
90+
}
91+
var otherReusableInputsArray: [ObjectIdentifier] = []
92+
for value in otherReusableInputs.stack {
93+
otherReusableInputsArray.append(ObjectIdentifier(value))
94+
}
95+
guard reusableInputsArray == otherReusableInputsArray else {
96+
Log.graphReuse("Reuse failed: custom inputs type mismatch")
97+
return false
98+
}
99+
var ignoredTypes = reusableInputsArray + [ObjectIdentifier(ReusableInputs.self)]
100+
guard !customInputs.mayNotBeEqual(to: other.customInputs, ignoredTypes: &ignoredTypes) else { // FIXME
101+
Log.graphReuse("Reuse failed: custom inputs plist equality")
102+
return false
103+
}
104+
func project<Input>(_ type: Input.Type) -> Bool where Input: GraphInput {
105+
preconditionFailure("TODO: Blocked by PropertyList")
106+
// WIP
107+
// Log.graphReuse("Reuse failed: custom input \(Input.self)")
108+
}
109+
let stack = reusableInputs.stack
110+
for value in stack {
111+
guard project(value) else {
112+
return false
113+
}
114+
continue
115+
}
116+
return true
117+
}
118+
}
119+
120+
// MARK: - Attribute + GraphReusable
54121

55122
extension Attribute: GraphReusable {
56123
package mutating func makeReusable(indirectMap: IndirectAttributeMap) {
124+
let indirect: AnyAttribute
57125
if let result = indirectMap.map[identifier] {
58-
identifier = result
126+
indirect = result
59127
} else {
60-
let indirect = indirectMap.subgraph.apply {
61-
IndirectAttribute(source: self)
128+
indirect = indirectMap.subgraph.apply {
129+
IndirectAttribute(source: self).identifier
62130
}
63-
indirectMap.map[identifier] = indirect.identifier
131+
indirectMap.map[identifier] = indirect
64132
}
133+
identifier = indirect
65134
}
66-
135+
67136
package func tryToReuse(by other: Attribute<Value>, indirectMap: IndirectAttributeMap, testOnly: Bool) -> Bool {
68-
if let result = indirectMap.map[identifier] {
69-
if testOnly {
70-
return true
71-
} else {
72-
result.source = other.identifier
73-
return true
74-
}
75-
} else {
137+
guard let result = indirectMap.map[identifier] else {
76138
Log.graphReuse("Reuse failed: missing indirection for \(Value.self)")
77139
return false
78140
}
141+
if !testOnly {
142+
result.source = other.identifier
143+
}
144+
return true
79145
}
80146
}
81147

82-
import Foundation
83-
#if canImport(Darwin)
84-
import os.log
85-
#endif
86-
87148
private struct EnableGraphReuseLogging: UserDefaultKeyedFeature {
88149
static var key: String { "org.OpenSwiftUIProject.OpenSwiftUI.GraphReuseLogging" }
150+
89151
static var cachedValue: Bool?
90152
}
91153

92154
extension Log {
93-
#if canImport(Darwin)
94155
private static let graphReuseLog: Logger = Logger(subsystem: Log.subsystem, category: "GraphReuse")
95-
#endif
96-
156+
97157
static func graphReuse(_ message: @autoclosure () -> String) {
98-
#if canImport(Darwin)
99158
if EnableGraphReuseLogging.isEnabled {
100159
let message = message()
101160
graphReuseLog.log("\(message)")
102161
}
103-
#endif
104162
}
105163
}

Sources/OpenSwiftUICore/Tracing/ReuseTrace.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ package struct ReuseTrace {
5151
traceReuseFailure("reuse_bodyMismatched")
5252
}
5353

54+
@inline(__always)
55+
package static func traceNeverMadeReusableFailure(_ valueType: (any Any.Type)?) {
56+
// TODO
57+
}
58+
5459
// TODO
5560

5661
final package class Recorder {

Sources/OpenSwiftUICore/View/Input/ViewList.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,12 +1051,12 @@ extension ViewList.ID.Views: Sendable {}
10511051

10521052
// MARK: - UnaryViewGenerator
10531053

1054-
public protocol UnaryViewGenerator {
1054+
private protocol UnaryViewGenerator {
10551055
func makeView(inputs: _ViewInputs, indirectMap: IndirectAttributeMap?) -> _ViewOutputs
10561056
func tryToReuse(by other: Self, indirectMap: IndirectAttributeMap, testOnly: Bool) -> Bool
10571057
}
10581058

1059-
public struct BodyUnaryViewGenerator<V>: UnaryViewGenerator {
1059+
private struct BodyUnaryViewGenerator<V>: UnaryViewGenerator {
10601060
let body: ViewList.Elements.MakeElement
10611061

10621062
public func makeView(inputs: _ViewInputs, indirectMap: IndirectAttributeMap?) -> _ViewOutputs {
@@ -1073,10 +1073,10 @@ public struct BodyUnaryViewGenerator<V>: UnaryViewGenerator {
10731073
}
10741074
}
10751075

1076-
public struct TypedUnaryViewGenerator<V>: UnaryViewGenerator where V: View {
1076+
private struct TypedUnaryViewGenerator<V>: UnaryViewGenerator where V: View {
10771077
let view: WeakAttribute<V>
10781078

1079-
public func makeView(inputs: _ViewInputs, indirectMap: IndirectAttributeMap?) -> _ViewOutputs {
1079+
func makeView(inputs: _ViewInputs, indirectMap: IndirectAttributeMap?) -> _ViewOutputs {
10801080
guard var view = view.attribute else {
10811081
return .init()
10821082
}
@@ -1086,7 +1086,7 @@ public struct TypedUnaryViewGenerator<V>: UnaryViewGenerator where V: View {
10861086
return V.makeDebuggableView(view: _GraphValue(view), inputs: inputs)
10871087
}
10881088

1089-
public func tryToReuse(by other: TypedUnaryViewGenerator<V>, indirectMap: IndirectAttributeMap, testOnly: Bool) -> Bool {
1089+
func tryToReuse(by other: TypedUnaryViewGenerator<V>, indirectMap: IndirectAttributeMap, testOnly: Bool) -> Bool {
10901090
guard let view = view.attribute, let otherView = other.view.attribute else {
10911091
Log.graphReuse("Reuse failed: missing attribute for \(V.self)")
10921092
return false

Tests/OpenSwiftUICoreTests/Graph/GraphHostTests.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
//
22
// GraphHostTests.swift
3-
// OpenSwiftUITests
3+
// OpenSwiftUICoreTests
44

5-
import OpenSwiftUICore
65
@_spi(ForOpenSwiftUIOnly) import OpenSwiftUICore
76
import Testing
87

0 commit comments

Comments
 (0)