diff --git a/Package.resolved b/Package.resolved index f1689e8cb..cabe9deb9 100644 --- a/Package.resolved +++ b/Package.resolved @@ -7,7 +7,7 @@ "location" : "https://github.com/OpenSwiftUIProject/DarwinPrivateFrameworks.git", "state" : { "branch" : "main", - "revision" : "c27863ea8384b1f6bd85b042f27df2d45cb4c7ef" + "revision" : "e12014b79f31a1483d7633f188c520fdf40f79c1" } }, { @@ -25,7 +25,7 @@ "location" : "https://github.com/OpenSwiftUIProject/OpenGraph", "state" : { "branch" : "main", - "revision" : "ae33121850a0c2a6af055aaa20ef2d21e4c2f880" + "revision" : "c35c4812e5b0803153bbf09a95e28a115d44e798" } }, { diff --git a/Sources/OpenSwiftUI/App/AppGraph.swift b/Sources/OpenSwiftUI/App/AppGraph.swift index 5c2625890..3e99a2af6 100644 --- a/Sources/OpenSwiftUI/App/AppGraph.swift +++ b/Sources/OpenSwiftUI/App/AppGraph.swift @@ -48,7 +48,7 @@ package final class AppGraph: GraphHost { return } if launchProfileOptions.contains(.profile) { - OGGraph.startProfiling() + Graph.startProfiling() } } @@ -58,11 +58,11 @@ package final class AppGraph: GraphHost { } didCollectLaunchProfile = true if launchProfileOptions.contains(.profile) { - OGGraph.stopProfiling() + Graph.stopProfiling() } if !launchProfileOptions.isEmpty { // /tmp/graph.ag-gzon - OGGraph.archiveJSON(name: nil) + Graph.archiveJSON(name: nil) } } } diff --git a/Sources/OpenSwiftUICore/Data/DynamicProperty/DynamicProperty.swift b/Sources/OpenSwiftUICore/Data/DynamicProperty/DynamicProperty.swift index 8f0a7e536..378c9a9e6 100644 --- a/Sources/OpenSwiftUICore/Data/DynamicProperty/DynamicProperty.swift +++ b/Sources/OpenSwiftUICore/Data/DynamicProperty/DynamicProperty.swift @@ -261,10 +261,10 @@ extension BodyAccessor { @inline(__always) package func setBody(_ body: () -> Body) { let value = traceRuleBody(Container.self) { - OGGraph.withoutUpdate(body) + Graph.withoutUpdate(body) } withUnsafePointer(to: value) { value in - OGGraph.setOutputValue(value) + Graph.setOutputValue(value) } } } diff --git a/Sources/OpenSwiftUICore/OpenGraph/Graph/Graph+OpenSwiftUI.swift b/Sources/OpenSwiftUICore/OpenGraph/Graph/Graph+OpenSwiftUI.swift index 122045da4..7ec130bc3 100644 --- a/Sources/OpenSwiftUICore/OpenGraph/Graph/Graph+OpenSwiftUI.swift +++ b/Sources/OpenSwiftUICore/OpenGraph/Graph/Graph+OpenSwiftUI.swift @@ -1,3 +1,11 @@ import OpenGraphShims -extension Graph {} +extension Graph { + static func startTracing(options: Graph.TraceFlags?) { + Graph.startTracing(nil, options: options ?? ProcessEnvironment.tracingOptions) + } + + static func stopTracing() { + Graph.stopTracing(nil) + } +} diff --git a/Sources/OpenSwiftUICore/Test/Benchmark.swift b/Sources/OpenSwiftUICore/Test/Benchmark.swift index 50bbcded2..0cb4edb23 100644 --- a/Sources/OpenSwiftUICore/Test/Benchmark.swift +++ b/Sources/OpenSwiftUICore/Test/Benchmark.swift @@ -2,8 +2,7 @@ // Benchmark.swift // OpenSwiftUICore // -// Audited for iOS 18.0 -// Status: WIP +// Status: Complete // ID: 3E629D505F0A70F29ACFC010AA42C6E0 (SwiftUI) // ID: 5BCCF82F8606CD0B3127FDEEA7C13601 (SwiftUICore) @@ -11,13 +10,21 @@ public import Foundation #if canImport(QuartzCore) import QuartzCore #endif +import OpenGraphShims +// MARK: - Benchmark [6.4.41] + +@available(OpenSwiftUI_v1_0, *) public protocol _BenchmarkHost: AnyObject { func _renderForTest(interval: Double) + + @available(OpenSwiftUI_v3_0, *) func _renderAsyncForTest(interval: Double) -> Bool + func _performScrollTest(startOffset: CGFloat, iterations: Int, delta: CGFloat, length: CGFloat, completion: (() -> Void)?) } +@available(OpenSwiftUI_v1_0, *) public protocol _Benchmark: _Test { func measure(host: _BenchmarkHost) -> [Double] } @@ -26,7 +33,9 @@ package var enableProfiler = EnvironmentHelper.bool(for: "OPENSWIFTUI_PROFILE_BE package var enableTracer = EnvironmentHelper.bool(for: "OPENSWIFTUI_TRACE_BENCHMARKS") +@available(OpenSwiftUI_v1_0, *) extension _BenchmarkHost { + @available(OpenSwiftUI_v1_0, *) public func _renderAsyncForTest(interval _: Double) -> Bool { false } @@ -34,23 +43,20 @@ extension _BenchmarkHost { public func _performScrollTest(startOffset _: CGFloat, iterations _: Int, delta _: CGFloat, length _: CGFloat, completion _: (() -> Void)?) {} public func measureAction(action: () -> Void) -> Double { - // WIP: trace support - #if canImport(QuartzCore) - let begin = CACurrentMediaTime() - if enableProfiler, - let renderHost = self as? ViewRendererHost { - renderHost.startProfiling() + let begin = Time.systemUptime + if enableTracer { + Graph.startTracing(options: nil) + } else if enableProfiler { + (self as? ViewRendererHost)?.startProfiling() } action() - let end = CACurrentMediaTime() - if enableProfiler, - let renderHost = self as? ViewRendererHost { - renderHost.stopProfiling() + let end = Time.systemUptime + if enableTracer { + Graph.stopTracing() + } else if enableProfiler { + (self as? ViewRendererHost)?.stopProfiling() } return end - begin - #else - preconditionFailure("Unsupported Platfrom") - #endif } public func measureRender(interval: Double = 1.0 / 60.0) -> Double { @@ -75,14 +81,47 @@ extension _BenchmarkHost { } } +// WIP package func summarize(_ measurements: [(any _Benchmark, [Double])]) -> String { - preconditionFailure("TODO") + let benchmarkData = measurements.map { (String(describing: $0.0), $0.1) } + + let maxNameLength = benchmarkData.map { $0.0.count }.max() ?? 0 + + let results: [String] = benchmarkData.map { (name, values) in + let padding = maxNameLength - name.count + 1 + let paddingString = String(repeating: " ", count: padding) + + let average = values.reduce(0, +) / Double(values.count) + let milliseconds = average * 1000.0 + + return "\(name)\(paddingString): \(String(format: "%.3f", milliseconds))ms" + } + return results.joined(separator: "\n") } package func write(_ measurements: [(any _Benchmark, [Double])], to path: String) throws { - preconditionFailure("TODO") + let dictionary = Dictionary(uniqueKeysWithValues: measurements.map { (String(describing: $0), $1) }) + let data = try JSONSerialization.data(withJSONObject: dictionary, options: .prettyPrinted) + let manager = FileManager.default + let directory = (path as NSString).deletingLastPathComponent + try manager.createDirectory(atPath: directory, withIntermediateDirectories: true) + try data.write(to: URL(fileURLWithPath: path)) } package func log(_ measurements: [(any _Benchmark, [Double])]) { - preconditionFailure("TODO") + print(summarize(measurements)) + let path: String + if CommandLine.arguments.count < 2 { + let formatter = DateFormatter() + formatter.dateFormat = "yyyy-MM-dd-HHmmss" + path = "/tmp/org.OpenSwiftUIProject.OpenSwiftUI/Benchmarks/\(formatter.string(from: Date())).json" + } else { + path = CommandLine.arguments[1] + } + print(path) + do { + try write(measurements, to: path) + } catch { + Log.internalError(error.localizedDescription) + } } diff --git a/Sources/OpenSwiftUICore/Test/Test.swift b/Sources/OpenSwiftUICore/Test/Test.swift index cb58b42d7..0689916c0 100644 --- a/Sources/OpenSwiftUICore/Test/Test.swift +++ b/Sources/OpenSwiftUICore/Test/Test.swift @@ -2,9 +2,10 @@ // Test.swift // OpenSwiftUICore // -// Audited for iOS 18.0 // Status: Complete +// MARK: - _Test [6.4.41] + public protocol _Test { func setUpTest() func tearDownTest() diff --git a/Sources/OpenSwiftUICore/Test/TestApp.swift b/Sources/OpenSwiftUICore/Test/TestApp.swift index 088a37b21..5fb3c8a2e 100644 --- a/Sources/OpenSwiftUICore/Test/TestApp.swift +++ b/Sources/OpenSwiftUICore/Test/TestApp.swift @@ -74,6 +74,8 @@ public struct _TestApp { package static func setTestEnvironment(_ environment: EnvironmentValues?) { // TODO + host?.environmentOverride = environment + comparisonHost?.environmentOverride = environment } package static func updateTestEnvironment(_ body: (inout EnvironmentValues) -> Void) { diff --git a/Sources/OpenSwiftUICore/Test/TestIDView.swift b/Sources/OpenSwiftUICore/Test/TestIDView.swift index ec125eb89..701e375bb 100644 --- a/Sources/OpenSwiftUICore/Test/TestIDView.swift +++ b/Sources/OpenSwiftUICore/Test/TestIDView.swift @@ -2,13 +2,12 @@ // TestIDView.swift // OpenSwiftUICore // -// Audited for iOS 18.0 // Status: Complete // ID: CC151E1A36B4405FF56CDABA5D46BF1E (SwiftUICore) import OpenGraphShims -// MARK: - TestIDView + View extension +// MARK: - TestIDView + View extension [6.4.41] @_spi(Testing) @available(OpenSwiftUI_v2_0, *) @@ -18,21 +17,20 @@ extension View { } } -// MARK: - TestIDView +// MARK: - TestIDView [6.4.41] @_spi(Testing) @available(OpenSwiftUI_v2_0, *) public struct TestIDView: PrimitiveView, UnaryView where Content: View, ID: Hashable { public var content: Content + public var id: ID nonisolated public static func _makeView(view: _GraphValue, inputs: _ViewInputs) -> _ViewOutputs { let view = _GraphValue(IdentifiedView(view: view.value, id: nil)) return Content.makeDebuggableView(view: view, inputs: inputs) } - - public typealias Body = Never - + private struct IdentifiedView: StatefulRule, AsyncAttribute, IdentifierProvider, CustomStringConvertible { @Attribute var view: TestIDView var id: ID? diff --git a/Sources/OpenSwiftUICore/Util/EnvironmentHelper.swift b/Sources/OpenSwiftUICore/Util/EnvironmentHelper.swift index ec32acc3d..c1bf317bb 100644 --- a/Sources/OpenSwiftUICore/Util/EnvironmentHelper.swift +++ b/Sources/OpenSwiftUICore/Util/EnvironmentHelper.swift @@ -14,6 +14,7 @@ import WASILibc #else #error("Unsupported Platform") #endif +import OpenGraphShims package enum EnvironmentHelper { @_transparent @@ -50,5 +51,5 @@ package enum ProcessEnvironment { return UInt32(atoi(env)) } - // package static let tracingOptions: OGGraphTraceFlags = .init(rawValue: uint32(forKey: "SWIFTUI_TRACE")) + static let tracingOptions: Graph.TraceFlags = .init(rawValue: uint32(forKey: "OPENSWIFTUI_TRACE")) } diff --git a/Sources/OpenSwiftUICore/View/Graph/ViewGraph.swift b/Sources/OpenSwiftUICore/View/Graph/ViewGraph.swift index 03ef3dced..450287969 100644 --- a/Sources/OpenSwiftUICore/View/Graph/ViewGraph.swift +++ b/Sources/OpenSwiftUICore/View/Graph/ViewGraph.swift @@ -145,7 +145,7 @@ package final class ViewGraph: GraphHost { self.rootViewType = rootViewType self.requestedOutputs = requestedOutputs let data = GraphHost.Data() - OGSubgraph.current = data.globalSubgraph + Subgraph.current = data.globalSubgraph rootView = Attribute(type: Root.self).identifier _rootTransform = Attribute(RootTransform()) _transform = _rootTransform