From 0e05242f3a5b43ca40a1e50376dabee7b2a23559 Mon Sep 17 00:00:00 2001 From: Kyle Date: Thu, 14 May 2026 23:02:58 +0800 Subject: [PATCH 01/18] Fix text make issue --- .../Render/DisplayList/DisplayListViewPlatform.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift index 20bb40b06..c7c01b7ab 100644 --- a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift +++ b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift @@ -560,8 +560,10 @@ extension DisplayList.ViewUpdater.Platform { container: view, state: .init(kind: .platformLayer) ) - case .text: - let view = definition.makeDrawingView(options: .init(base: .init())) as AnyObject + case let .text(text, _): + var options = RasterizationOptions() + options.isAccelerated = text.needsDrawingGroup + let view = definition.makeDrawingView(options: .init(base: options)) as AnyObject return DisplayList.ViewUpdater.ViewInfo( view: view, layer: viewLayer(view), From 0f6b670bbf853e33236289e8a493997503f8e73c Mon Sep 17 00:00:00 2001 From: Kyle Date: Thu, 14 May 2026 23:03:13 +0800 Subject: [PATCH 02/18] Update Platform.updateItemView --- .../Render/DisplayList/DisplayList.swift | 32 ++ .../DisplayList/DisplayListViewPlatform.swift | 318 ++++++++++++++++-- .../Shims/QuartzCore/CABackdropLayer.h | 6 + 3 files changed, 332 insertions(+), 24 deletions(-) diff --git a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayList.swift b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayList.swift index 2f9dda039..623bd4126 100644 --- a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayList.swift +++ b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayList.swift @@ -177,6 +177,38 @@ extension DisplayList { indirect case drawing(any ORBDisplayListContents, CGPoint, RasterizationOptions) indirect case view(any _DisplayList_ViewFactory) case placeholder(id: Identity) + + @inline(__always) + var caseName: String { + switch self { + case .backdrop: + return "backdrop" + case .color: + return "color" + case .chameleonColor: + return "chameleonColor" + case .image: + return "image" + case .shape: + return "shape" + case .shadow: + return "shadow" + case .platformView: + return "platformView" + case .platformLayer: + return "platformLayer" + case .text: + return "text" + case .flattened: + return "flattened" + case .drawing: + return "drawing" + case .view: + return "view" + case .placeholder: + return "placeholder" + } + } } package init(_ value: Content.Value, seed: Seed) { diff --git a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift index c7c01b7ab..588e68540 100644 --- a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift +++ b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift @@ -381,9 +381,291 @@ extension DisplayList.ViewUpdater.Platform { item: DisplayList.Item, state: UnsafePointer ) { - _openSwiftUIUnimplementedFailure() + var item = item + switch item.value { + case let .content(content): + guard viewInfo.seeds.content != content.seed else { + guard viewInfo.state.isContentGeometryEnabled else { + if viewInfo.state.kind == .drawing && viewInfo.state.size != item.size { + let drawable = viewInfo.view as! PlatformDrawable + viewInfo.isInvalid = !drawable.update(content: nil, required: true) + } + updateState( + &viewInfo, + item: item, + size: item.size, + state: state + ) + return + } + var size = item.size + var localState = state.pointee + switch content.value { + case let .image(image): + let orientation = image.bitmapOrientation + if orientation != .up { + localState.transform = CGAffineTransform( + orientation: orientation, + in: size + ).concatenating(localState.transform) + size = size.apply(orientation) + } + case let .shape(path, paint, style): + updateShapeView( + &viewInfo, + state: &localState, + size: &size, + path: path, + paint: paint, + style: style, + contentsChanged: false + ) + default: + viewInfo.seeds.content = .init() + Log.internalError( + "Invalid size-dependent display list content: %s, %s", + content.value.caseName, + "\(viewInfo.state.kind)" + ) + } + localState.versions.transform.combine(with: item.version) + withUnsafePointer(to: localState) { statePtr in + updateState( + &viewInfo, + item: item, + size: size, + state: statePtr + ) + } + return + } + var localState = state.pointee + var size = item.size + viewInfo.isInvalid = false + viewInfo.state.isContentGeometryEnabled = false + switch content.value { + case let .backdrop(effect): + if viewInfo.state.kind != .backdrop { + viewInfo = _makeItemView(item: item, state: state) + } + #if canImport(QuartzCore) + let layer = viewInfo.layer as! CABackdropLayer + let hasZeroScale = effect.scale == 0 + layer.scale = hasZeroScale ? 1.0 : CGFloat(effect.scale) + layer.allowsInPlaceFiltering = hasZeroScale + layer.backgroundColor = effect.color.cgColor + let groupID = state.pointee.backdropGroupID + layer.groupName = groupID == 0 ? nil : "OpenSwiftUI-\(groupID)" + #else + _openSwiftUIPlatformUnimplementedWarning() + #endif + case let .color(color): + if viewInfo.state.kind != .color { + viewInfo = _makeItemView(item: item, state: state) + } + viewInfo.layer.backgroundColor = color.cgColor + case .chameleonColor: + if viewInfo.state.kind != .chameleonColor { + viewInfo = _makeItemView(item: item, state: state) + } + case let .image(image): + if viewInfo.state.kind != .image { + viewInfo = _makeItemView(item: item, state: state) + } + let layer = viewInfo.layer as! ImageLayer + layer.update(image: image, size: size) + let orientation = image.bitmapOrientation + if orientation != .up { + localState.transform = CGAffineTransform( + orientation: orientation, + in: size + ).concatenating(localState.transform) + } + size = size.apply(orientation) + viewInfo.state.isContentGeometryEnabled = true + case let .shape(path, paint, style): + if viewInfo.state.kind != .shape { + viewInfo = _makeItemView(item: item, state: state) + } + updateShapeView( + &viewInfo, + state: &localState, + size: &size, + path: path, + paint: paint, + style: style, + contentsChanged: true + ) + case let .shadow(path, shadow): + if viewInfo.state.kind != .shadow { + viewInfo = _makeItemView(item: item, state: state) + } + updateShadowView( + &viewInfo, + path: path, + shadow: shadow, + size: size + ) + case let .platformView(factory): + if viewInfo.state.kind != .platformView { + viewInfo = _makeItemView(item: item, state: state) + } + let oldView = viewInfo.view + factory.updatePlatformView(&viewInfo.view) + let newView = viewInfo.view + if oldView !== newView { + definition.makePlatformView(view: newView, kind: .platformView) + viewInfo.reset(platform: self) + } + case let .platformLayer(factory): + if viewInfo.state.kind != .platformLayer { + viewInfo = _makeItemView(item: item, state: state) + } + let layer = viewInfo.layer + #if canImport(QuartzCore) + layer.contentsScale = state.pointee.globals.pointee.environment.contentsScale + #endif + factory.updatePlatformLayer(layer) + case let .text(text, textSize): + if viewInfo.state.kind != .drawing { + viewInfo = _makeItemView(item: item, state: state) + } + var options = RasterizationOptions() + options.isAccelerated = text.needsDrawingGroup + updateDrawingView( + &viewInfo, + options: options, + contentsScale: state.pointee.globals.pointee.environment.contentsScale, + content: .platformCallback { size in + text.text.draw( + in: CGRect(origin: .zero, size: size), + with: textSize, + applyingMarginOffsets: true, + containsResolvable: text.text.isDynamic, + context: .shared, + renderer: text.renderer + ) + }, + sizeChanged: viewInfo.state.size != item.size + ) + viewInfo.nextUpdate = min( + viewInfo.nextUpdate, + text.text.nextUpdate( + after: state.pointee.globals.pointee.time, + equivalentDate: .now, + reduceFrequency: false + ) + ) + case let .flattened(list, offset, options): + if viewInfo.state.kind != .drawing { + viewInfo = _makeItemView(item: item, state: state) + } + let time = state.pointee.globals.pointee.time + updateDrawingView( + &viewInfo, + options: options, + contentsScale: state.pointee.globals.pointee.environment.contentsScale, + content: .displayList( + list, + offset, + time + ), + sizeChanged: viewInfo.state.size != item.size + ) + viewInfo.nextUpdate = min(viewInfo.nextUpdate, list.nextUpdate(after: time)) + case let .drawing(contents, offset, options): + if viewInfo.state.kind != .drawing { + viewInfo = _makeItemView(item: item, state: state) + } + updateDrawingView( + &viewInfo, + options: options, + contentsScale: state.pointee.globals.pointee.environment.contentsScale, + content: .rbDisplayList(contents, offset), + sizeChanged: viewInfo.state.size != item.size + ) + case .view, .placeholder: + _openSwiftUIUnreachableCode() + } + if viewInfo.state.isContentGeometryEnabled { + localState.versions.transform.combine(with: item.version) + } + if !viewInfo.isInvalid, viewInfo.nextUpdate == .infinity { + viewInfo.seeds.content = content.seed + } + withUnsafePointer(to: localState) { statePtr in + updateState( + &viewInfo, + item: item, + size: size, + state: statePtr + ) + } + case let .effect(effect, _): // TBA + let contentChanged = viewInfo.seeds.content != item.version.seed + if contentChanged { + viewInfo.seeds.content = item.version.seed + switch effect { + case .geometryGroup: + if viewInfo.state.kind != .geometry { + viewInfo = _makeItemView(item: item, state: state) + } + case .compositingGroup: + if viewInfo.state.kind != .compositing { + viewInfo = _makeItemView(item: item, state: state) + } + #if canImport(QuartzCore) + viewInfo.layer.allowsGroupOpacity = true + viewInfo.layer.allowsGroupBlending = true + #endif + case let .platformGroup(factory): + if viewInfo.state.kind != .platformGroup { + viewInfo = _makeItemView(item: item, state: state) + } + let oldView = viewInfo.view + factory.updatePlatformGroup(&viewInfo.view) + if oldView !== viewInfo.view { + definition.makePlatformView(view: viewInfo.view, kind: .platformGroup) + viewInfo.reset(platform: self) + } + viewInfo.container = factory.platformGroupContainer(viewInfo.view) + case .mask: + if viewInfo.state.kind != .mask { + viewInfo = _makeItemView(item: item, state: state) + } + case let .transform(transform): + guard let projectionTransform = transform.projectionTransform else { + _openSwiftUIUnreachableCode() + } + if viewInfo.state.kind != .projection { + viewInfo = _makeItemView(item: item, state: state) + } + definition.setProjectionTransform( + projectionTransform, + projectionView: viewInfo.view + ) + case .platform: + if viewInfo.state.kind != .platformEffect { + viewInfo = _makeItemView(item: item, state: state) + } + case .identity, .backdropGroup, .archive, .properties, .opacity, + .blendMode, .clip, .filter, .animation, .contentTransition, + .view, .accessibility, .state, .interpolatorRoot, + .interpolatorLayer, .interpolatorAnimation: + break + } + } + updateState( + &viewInfo, + item: item, + size: item.size, + state: state + ) + case .empty, .states: + _openSwiftUIUnreachableCode() + } } - + func updateItemViewAsync( layer: inout DisplayList.ViewUpdater.AsyncLayer, index: DisplayList.Index, @@ -1143,9 +1425,11 @@ extension DisplayList.ViewUpdater.Platform { case let .shape(path, _, _): definition.setPath(path, shapeView: viewInfo.view) case let .platformView(factory): - var view = viewInfo.view - factory.updatePlatformView(&view) - updateViewReference(&viewInfo, view: view, kind: .platformView) + let oldView = viewInfo.view + factory.updatePlatformView(&viewInfo.view) + if oldView !== viewInfo.view { + viewInfo.reset(platform: self) + } case let .platformLayer(factory): #if canImport(QuartzCore) if let layer = viewInfo.view as? CALayer { @@ -1174,9 +1458,11 @@ extension DisplayList.ViewUpdater.Platform { ) } case let .platformGroup(factory): - var view = viewInfo.view - factory.updatePlatformGroup(&view) - updateViewReference(&viewInfo, view: view, kind: .platformGroup) + let oldView = viewInfo.view + factory.updatePlatformGroup(&viewInfo.view) + if oldView !== viewInfo.view { + viewInfo.reset(platform: self) + } viewInfo.container = factory.platformGroupContainer(viewInfo.view) case .identity, .geometryGroup, .compositingGroup, .backdropGroup, .archive, .properties, .opacity, .blendMode, .clip, .mask, .filter, .animation, @@ -1203,7 +1489,6 @@ extension DisplayList.ViewUpdater.Platform { // options: options, // contentsScale: contentsScale // ) -// updateViewReference(&viewInfo, view: view, kind: .drawing) // var drawableContent = PlatformDrawableContent() // switch content.value { // case let .flattened(list, offset, _): @@ -1225,21 +1510,6 @@ extension DisplayList.ViewUpdater.Platform { // ) } - // TBA - @inline(__always) - private func updateViewReference( - _ viewInfo: inout DisplayList.ViewUpdater.ViewInfo, - view: AnyObject, - kind: PlatformViewDefinition.ViewKind - ) { - guard viewInfo.view !== view else { - return - } - viewInfo.view = view - viewInfo.container = view - viewInfo.reset(platform: self) - } - func forEachChild( of viewInfo: DisplayList.ViewUpdater.ViewInfo, do body: (AnyObject) -> Void diff --git a/Sources/OpenSwiftUI_SPI/Shims/QuartzCore/CABackdropLayer.h b/Sources/OpenSwiftUI_SPI/Shims/QuartzCore/CABackdropLayer.h index 95326c8cf..8e153392a 100644 --- a/Sources/OpenSwiftUI_SPI/Shims/QuartzCore/CABackdropLayer.h +++ b/Sources/OpenSwiftUI_SPI/Shims/QuartzCore/CABackdropLayer.h @@ -6,9 +6,15 @@ #if __has_include() +#import #import @interface CABackdropLayer : CALayer + +@property (nonatomic) CGFloat scale; +@property (nonatomic) BOOL allowsInPlaceFiltering; +@property (nonatomic, copy, nullable) NSString *groupName; + @end #endif /* __has_include() */ From 84fa8e2cf4f47733717a763718b054047e62714c Mon Sep 17 00:00:00 2001 From: Kyle Date: Thu, 14 May 2026 23:06:00 +0800 Subject: [PATCH 03/18] Add DisplayList.nextUpdate --- .../Render/DisplayList/DisplayList.swift | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayList.swift b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayList.swift index 623bd4126..5b05bf1a9 100644 --- a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayList.swift +++ b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayList.swift @@ -107,6 +107,26 @@ package struct DisplayList: Equatable { package var isEmpty: Bool { items.isEmpty } + + // TODO + + // TBA + package func nextUpdate(after time: Time) -> Time { + guard !features.contains(.animations) else { + return time + } + var nextUpdate = Time.infinity + guard features.contains(.dynamicContent) else { + return nextUpdate + } + for item in items { + nextUpdate = min(nextUpdate, item.nextUpdate(after: time)) + if nextUpdate <= time { + break + } + } + return nextUpdate + } } @available(*, unavailable) @@ -709,6 +729,51 @@ extension DisplayList { } } +extension DisplayList.Item { + package func nextUpdate(after time: Time) -> Time { + switch value { + case let .content(content): + switch content.value { + case let .text(text, _): + return text.text.nextUpdate( + after: time, + equivalentDate: .now, + reduceFrequency: false + ) + case let .flattened(list, _, _): + return list.nextUpdate(after: time) + default: + return .infinity + } + case let .effect(effect, list): + var nextUpdate = list.nextUpdate(after: time) + if case let .mask(mask, _) = effect { + nextUpdate = min(nextUpdate, mask.nextUpdate(after: time)) + } + return nextUpdate + case let .states(states): + var nextUpdate = Time.infinity + for (_, list) in states { + let nestedUpdate: Time + if list.features.contains(.animations) { + nestedUpdate = time + } else if list.features.contains(.dynamicContent) { + nestedUpdate = list.nextUpdate(after: time) + } else { + nestedUpdate = .infinity + } + nextUpdate = min(nextUpdate, nestedUpdate) + if nextUpdate <= time { + break + } + } + return nextUpdate + case .empty: + return .infinity + } + } +} + package struct AccessibilityNodeAttachment {} package protocol _DisplayList_AnyEffectAnimation: ProtobufMessage { From 37d3cc9d35b381d1ce6e353961ec0bee38438085 Mon Sep 17 00:00:00 2001 From: Kyle Date: Thu, 14 May 2026 23:10:10 +0800 Subject: [PATCH 04/18] Update Platform.updateItemView effect handling --- .../DisplayList/DisplayListViewPlatform.swift | 319 +++++++----------- 1 file changed, 116 insertions(+), 203 deletions(-) diff --git a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift index 588e68540..0b8d1e912 100644 --- a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift +++ b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift @@ -385,58 +385,7 @@ extension DisplayList.ViewUpdater.Platform { switch item.value { case let .content(content): guard viewInfo.seeds.content != content.seed else { - guard viewInfo.state.isContentGeometryEnabled else { - if viewInfo.state.kind == .drawing && viewInfo.state.size != item.size { - let drawable = viewInfo.view as! PlatformDrawable - viewInfo.isInvalid = !drawable.update(content: nil, required: true) - } - updateState( - &viewInfo, - item: item, - size: item.size, - state: state - ) - return - } - var size = item.size - var localState = state.pointee - switch content.value { - case let .image(image): - let orientation = image.bitmapOrientation - if orientation != .up { - localState.transform = CGAffineTransform( - orientation: orientation, - in: size - ).concatenating(localState.transform) - size = size.apply(orientation) - } - case let .shape(path, paint, style): - updateShapeView( - &viewInfo, - state: &localState, - size: &size, - path: path, - paint: paint, - style: style, - contentsChanged: false - ) - default: - viewInfo.seeds.content = .init() - Log.internalError( - "Invalid size-dependent display list content: %s, %s", - content.value.caseName, - "\(viewInfo.state.kind)" - ) - } - localState.versions.transform.combine(with: item.version) - withUnsafePointer(to: localState) { statePtr in - updateState( - &viewInfo, - item: item, - size: size, - state: statePtr - ) - } + updateSizeDependentContent(&viewInfo, item: item, state: state) return } var localState = state.pointee @@ -601,59 +550,66 @@ extension DisplayList.ViewUpdater.Platform { state: statePtr ) } - case let .effect(effect, _): // TBA + case let .effect(effect, _): let contentChanged = viewInfo.seeds.content != item.version.seed - if contentChanged { - viewInfo.seeds.content = item.version.seed - switch effect { - case .geometryGroup: - if viewInfo.state.kind != .geometry { - viewInfo = _makeItemView(item: item, state: state) - } - case .compositingGroup: - if viewInfo.state.kind != .compositing { - viewInfo = _makeItemView(item: item, state: state) - } - #if canImport(QuartzCore) - viewInfo.layer.allowsGroupOpacity = true - viewInfo.layer.allowsGroupBlending = true - #endif - case let .platformGroup(factory): - if viewInfo.state.kind != .platformGroup { - viewInfo = _makeItemView(item: item, state: state) - } - let oldView = viewInfo.view - factory.updatePlatformGroup(&viewInfo.view) - if oldView !== viewInfo.view { - definition.makePlatformView(view: viewInfo.view, kind: .platformGroup) - viewInfo.reset(platform: self) - } - viewInfo.container = factory.platformGroupContainer(viewInfo.view) - case .mask: - if viewInfo.state.kind != .mask { - viewInfo = _makeItemView(item: item, state: state) - } - case let .transform(transform): - guard let projectionTransform = transform.projectionTransform else { - _openSwiftUIUnreachableCode() - } + var changed = contentChanged + if !changed { + let transformChanged: Bool + if case let .transform(transform) = effect, + transform.projectionTransform != nil, + viewInfo.seeds.transform != state.pointee.versions.transform.seed { + transformChanged = true + } else { + transformChanged = false + } + changed = changed || transformChanged + } + guard changed else { + updateSizeDependentContent(&viewInfo, item: item, state: state) + return + } + viewInfo.seeds.content = item.version.seed + switch effect { + case .geometryGroup: + if viewInfo.state.kind != .geometry { + viewInfo = _makeItemView(item: item, state: state) + } + case .compositingGroup: + if viewInfo.state.kind != .compositing { + viewInfo = _makeItemView(item: item, state: state) + } + case let .platformGroup(factory): + if viewInfo.state.kind != .platformGroup { + viewInfo = _makeItemView(item: item, state: state) + } + let oldView = viewInfo.view + factory.updatePlatformGroup(&viewInfo.view) + let newView = viewInfo.view + if oldView !== newView { + definition.makePlatformView(view: newView, kind: .platformGroup) + viewInfo.reset(platform: self) + } + viewInfo.container = factory.platformGroupContainer(newView) + case .mask: + if viewInfo.state.kind != .mask { + viewInfo = _makeItemView(item: item, state: state) + } + case let .transform(transform): + if let projectionTransform = transform.projectionTransform { if viewInfo.state.kind != .projection { viewInfo = _makeItemView(item: item, state: state) } definition.setProjectionTransform( - projectionTransform, + projectionTransform.concatenating(ProjectionTransform(state.pointee.transform)), projectionView: viewInfo.view ) - case .platform: - if viewInfo.state.kind != .platformEffect { - viewInfo = _makeItemView(item: item, state: state) - } - case .identity, .backdropGroup, .archive, .properties, .opacity, - .blendMode, .clip, .filter, .animation, .contentTransition, - .view, .accessibility, .state, .interpolatorRoot, - .interpolatorLayer, .interpolatorAnimation: - break } + case .platform: + if viewInfo.state.kind != .platformEffect { + viewInfo = _makeItemView(item: item, state: state) + } + default: + _openSwiftUIUnreachableCode() } updateState( &viewInfo, @@ -666,6 +622,69 @@ extension DisplayList.ViewUpdater.Platform { } } + @inline(__always) + private func updateSizeDependentContent( + _ viewInfo: inout DisplayList.ViewUpdater.ViewInfo, + item: DisplayList.Item, + state: UnsafePointer + ) { + guard viewInfo.state.isContentGeometryEnabled else { + if viewInfo.state.kind == .drawing && viewInfo.state.size != item.size { + let drawable = viewInfo.view as! PlatformDrawable + viewInfo.isInvalid = !drawable.update(content: nil, required: true) + } + updateState( + &viewInfo, + item: item, + size: item.size, + state: state + ) + return + } + guard case let .content(content) = item.value else { + _openSwiftUIUnreachableCode() + } + var size = item.size + var localState = state.pointee + switch content.value { + case let .image(image): + let orientation = image.bitmapOrientation + if orientation != .up { + localState.transform = CGAffineTransform( + orientation: orientation, + in: size + ).concatenating(localState.transform) + size = size.apply(orientation) + } + case let .shape(path, paint, style): + updateShapeView( + &viewInfo, + state: &localState, + size: &size, + path: path, + paint: paint, + style: style, + contentsChanged: false + ) + default: + viewInfo.seeds.content = .init() + Log.internalError( + "Invalid size-dependent display list content: %s, %s", + content.value.caseName, + "\(viewInfo.state.kind)" + ) + } + localState.versions.transform.combine(with: item.version) + withUnsafePointer(to: localState) { statePtr in + updateState( + &viewInfo, + item: item, + size: size, + state: statePtr + ) + } + } + func updateItemViewAsync( layer: inout DisplayList.ViewUpdater.AsyncLayer, index: DisplayList.Index, @@ -1404,112 +1423,6 @@ extension DisplayList.ViewUpdater.Platform { return drawable } - // TBA - @inline(__always) - private func updateContentView( - _ viewInfo: inout DisplayList.ViewUpdater.ViewInfo, - content: DisplayList.Content, - item: DisplayList.Item, - state: UnsafePointer - ) { - switch content.value { - case let .color(resolved): - #if canImport(QuartzCore) - let cgColor = resolved.cgColor - viewLayer(viewInfo.view).backgroundColor = cgColor - #else - _openSwiftUIPlatformUnimplementedWarning() - #endif - case .backdrop, .chameleonColor, .image, .shadow, .view, .placeholder: - break - case let .shape(path, _, _): - definition.setPath(path, shapeView: viewInfo.view) - case let .platformView(factory): - let oldView = viewInfo.view - factory.updatePlatformView(&viewInfo.view) - if oldView !== viewInfo.view { - viewInfo.reset(platform: self) - } - case let .platformLayer(factory): - #if canImport(QuartzCore) - if let layer = viewInfo.view as? CALayer { - factory.updatePlatformLayer(layer) - } - #endif - case .text, .flattened, .drawing: - updateDrawingContent(&viewInfo, content: content, item: item, state: state) - } - } - - // TBA - @inline(__always) - private func updateEffectView( - _ viewInfo: inout DisplayList.ViewUpdater.ViewInfo, - effect: DisplayList.Effect, - item: DisplayList.Item, - state: UnsafePointer - ) { - switch effect { - case let .transform(transform): - if case let .projection(projectionTransform) = transform { - definition.setProjectionTransform( - projectionTransform, - projectionView: viewInfo.view - ) - } - case let .platformGroup(factory): - let oldView = viewInfo.view - factory.updatePlatformGroup(&viewInfo.view) - if oldView !== viewInfo.view { - viewInfo.reset(platform: self) - } - viewInfo.container = factory.platformGroupContainer(viewInfo.view) - case .identity, .geometryGroup, .compositingGroup, .backdropGroup, .archive, - .properties, .opacity, .blendMode, .clip, .mask, .filter, .animation, - .contentTransition, .view, .accessibility, .platform, .state, - .interpolatorRoot, .interpolatorLayer, .interpolatorAnimation: - break - } - } - - // TBA - @inline(__always) - private func updateDrawingContent( - _ viewInfo: inout DisplayList.ViewUpdater.ViewInfo, - content: DisplayList.Content, - item: DisplayList.Item, - state: UnsafePointer - ) { - _openSwiftUIUnimplementedFailure() -// let contentsScale = state.pointee.globals.pointee.environment.contentsScale -// let options = drawingOptions(for: content, state: state) -// var view = viewInfo.view -// let drawable = updateDrawingView( -// &view, -// options: options, -// contentsScale: contentsScale -// ) -// var drawableContent = PlatformDrawableContent() -// switch content.value { -// case let .flattened(list, offset, _): -// drawableContent.storage = .displayList( -// list, -// offset, -// state.pointee.globals.pointee.time -// ) -// case let .drawing(contents, offset, _): -// drawableContent.storage = .rbDisplayList(contents, offset) -// case .text: -// drawableContent.storage = .empty -// default: -// return -// } -// _ = drawable.update( -// content: drawableContent, -// required: item.features.contains(.required) -// ) - } - func forEachChild( of viewInfo: DisplayList.ViewUpdater.ViewInfo, do body: (AnyObject) -> Void From 7010866e5f146ec5113cda44cf25f0e5548242b4 Mon Sep 17 00:00:00 2001 From: Kyle Date: Thu, 14 May 2026 23:13:37 +0800 Subject: [PATCH 05/18] Fix AsyncRenderExample build issue --- Example/Shared/Render/Async/AsyncRenderExample.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Example/Shared/Render/Async/AsyncRenderExample.swift b/Example/Shared/Render/Async/AsyncRenderExample.swift index ff6bb7b87..d5c610ea1 100644 --- a/Example/Shared/Render/Async/AsyncRenderExample.swift +++ b/Example/Shared/Render/Async/AsyncRenderExample.swift @@ -2,6 +2,12 @@ // GeometryEffectExample.swift // Shared +#if OPENSWIFTUI +import OpenSwiftUI +#else +import SwiftUI +#endif + struct AsyncRenderExample: View { @State private var items = [6] From e7c2339aacc5b7010318e432bf393f69010eedaf Mon Sep 17 00:00:00 2001 From: Kyle Date: Thu, 14 May 2026 23:18:42 +0800 Subject: [PATCH 06/18] Update Platform.updateItemViewAsync --- .../DisplayList/DisplayListAsyncLayer.swift | 44 +++ .../DisplayList/DisplayListViewPlatform.swift | 288 ++++++++++++++++-- .../OpenSwiftUICore/Shape/ShapeLayer.swift | 26 -- .../View/Image/ImageLayer.swift | 19 +- 4 files changed, 325 insertions(+), 52 deletions(-) diff --git a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListAsyncLayer.swift b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListAsyncLayer.swift index 8f89b0653..819c839c6 100644 --- a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListAsyncLayer.swift +++ b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListAsyncLayer.swift @@ -60,5 +60,49 @@ extension DisplayList.ViewUpdater { var isContentGeometryEnabled: Bool { flags.contains(.contentGeometry) } + + @inline(__always) + mutating func update

( + _ property: P.Type, + from oldValue: P.Value, + to newValue: P.Value + ) where P: Property, P.Value: Equatable { + guard oldValue != newValue else { + return + } + cache.pointee.setAsyncValue( + P.boxValue(newValue), + for: P.keyPath, + in: layer, + usingPresentationModifier: P.supportsPresentationModifier + ) + } + } + + // MARK: - AsyncLayer.Property + + struct BackgroundColor: AsyncLayer.Property { + static let keyPath = "backgroundColor" + + static func boxValue(_ value: Color.Resolved) -> NSObject { + #if canImport(Darwin) + unsafeDowncast(value.cgColor, to: NSObject.self) + #else + _openSwiftUIPlatformUnimplementedFailure() + #endif + } } + + struct LayerProjectionTransform: AsyncLayer.Property { + static let keyPath = "transform" + + static func boxValue(_ value: ProjectionTransform) -> NSObject { + #if canImport(QuartzCore) + NSValue(caTransform3D: CATransform3D(value)) + #else + _openSwiftUIPlatformUnimplementedFailure() + #endif + } + } + } diff --git a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift index 0b8d1e912..bd42df14c 100644 --- a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift +++ b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift @@ -423,14 +423,11 @@ extension DisplayList.ViewUpdater.Platform { } let layer = viewInfo.layer as! ImageLayer layer.update(image: image, size: size) - let orientation = image.bitmapOrientation - if orientation != .up { - localState.transform = CGAffineTransform( - orientation: orientation, - in: size - ).concatenating(localState.transform) - } - size = size.apply(orientation) + adjustImageContentGeometry( + image: image, + state: &localState, + size: &size + ) viewInfo.state.isContentGeometryEnabled = true case let .shape(path, paint, style): if viewInfo.state.kind != .shape { @@ -648,14 +645,11 @@ extension DisplayList.ViewUpdater.Platform { var localState = state.pointee switch content.value { case let .image(image): - let orientation = image.bitmapOrientation - if orientation != .up { - localState.transform = CGAffineTransform( - orientation: orientation, - in: size - ).concatenating(localState.transform) - size = size.apply(orientation) - } + adjustImageContentGeometry( + image: image, + state: &localState, + size: &size + ) case let .shape(path, paint, style): updateShapeView( &viewInfo, @@ -693,7 +687,267 @@ extension DisplayList.ViewUpdater.Platform { newItem: DisplayList.Item, newState: UnsafePointer ) -> Bool { - _openSwiftUIUnimplementedFailure() + switch (oldItem.value, newItem.value) { + case let (.content(oldContent), .content(newContent)): + guard oldContent.seed != newContent.seed else { + return updateSizeDependentContentAsync( + layer: &layer, + oldItem: oldItem, + oldState: oldState, + newItem: newItem, + newState: newState + ) + } + var oldLocalState = oldState.pointee + var newLocalState = newState.pointee + var oldSize = oldItem.size + var newSize = newItem.size + layer.isInvalid = false + switch (oldContent.value, newContent.value) { + case let (.color(oldColor), .color(newColor)): + layer.update( + DisplayList.ViewUpdater.BackgroundColor.self, + from: oldColor, + to: newColor + ) + case let (.image(oldImage), .image(newImage)): + guard ImageLayer.updateAsync( + layer: &layer, + oldImage: oldImage, + oldSize: oldSize, + newImage: newImage, + newSize: newSize + ) else { + return false + } + adjustImageContentGeometry( + image: oldImage, + state: &oldLocalState, + size: &oldSize + ) + adjustImageContentGeometry( + image: newImage, + state: &newLocalState, + size: &newSize + ) + case let (.shape(oldPath, oldPaint, oldStyle), .shape(newPath, newPaint, newStyle)): + guard updateShapeViewAsync( + layer: &layer, + oldState: &oldLocalState, + oldSize: &oldSize, + oldPath: oldPath, + oldPaint: oldPaint, + oldStyle: oldStyle, + newState: &newLocalState, + newSize: &newSize, + newPath: newPath, + newPaint: newPaint, + newStyle: newStyle, + contentsChanged: true + ) else { + return false + } + case let (.flattened(_, _, oldOptions), .flattened(newList, newOffset, newOptions)): + let time = newState.pointee.globals.pointee.time + guard updateDrawingViewAsync( + &layer, + oldOptions: oldOptions, + newOptions: newOptions, + content: .displayList(newList, newOffset, time), + sizeChanged: oldItem.size != newItem.size, + newSize: newItem.size, + newState: newState + ) else { + return false + } + layer.nextUpdate = min(layer.nextUpdate, newList.nextUpdate(after: time)) + case let (.drawing(_, _, oldOptions), .drawing(newContents, newOffset, newOptions)): + guard updateDrawingViewAsync( + &layer, + oldOptions: oldOptions, + newOptions: newOptions, + content: .rbDisplayList(newContents, newOffset), + sizeChanged: oldItem.size != newItem.size, + newSize: newItem.size, + newState: newState + ) else { + return false + } + default: + return false + } + return withUnsafePointer(to: oldLocalState) { oldStatePtr in + withUnsafePointer(to: newLocalState) { newStatePtr in + updateStateAsync( + layer: &layer, + oldItem: oldItem, + oldSize: oldSize, + oldState: oldStatePtr, + newItem: newItem, + newSize: newSize, + newState: newStatePtr + ) + } + } + case let (.effect(oldEffect, _), .effect(newEffect, _)): + let contentChanged = oldItem.version != newItem.version + var changed = contentChanged + if !changed { + let transformChanged: Bool + if case let .transform(oldTransform) = oldEffect, + oldTransform.projectionTransform != nil, + oldState.pointee.versions.transform != newState.pointee.versions.transform { + transformChanged = true + } else { + transformChanged = false + } + changed = changed || transformChanged + } + guard changed else { + return updateSizeDependentContentAsync( + layer: &layer, + oldItem: oldItem, + oldState: oldState, + newItem: newItem, + newState: newState + ) + } + switch (oldEffect, newEffect) { + case let (.platformGroup(oldFactory), .platformGroup(newFactory)): + guard !oldFactory.needsUpdateFor(newValue: newFactory) else { + return false + } + case let (.transform(oldTransform), .transform(newTransform)): + if let oldProjectionTransform = oldTransform.projectionTransform, + let newProjectionTransform = newTransform.projectionTransform { + layer.update( + DisplayList.ViewUpdater.LayerProjectionTransform.self, + from: oldProjectionTransform.concatenating(ProjectionTransform(oldState.pointee.transform)), + to: newProjectionTransform.concatenating(ProjectionTransform(newState.pointee.transform)) + ) + } + default: + break + } + return updateStateAsync( + layer: &layer, + oldItem: oldItem, + oldSize: oldItem.size, + oldState: oldState, + newItem: newItem, + newSize: newItem.size, + newState: newState + ) + default: + return false + } + } + + @inline(__always) + private func updateSizeDependentContentAsync( + layer: inout DisplayList.ViewUpdater.AsyncLayer, + oldItem: DisplayList.Item, + oldState: UnsafePointer, + newItem: DisplayList.Item, + newState: UnsafePointer + ) -> Bool { + guard layer.isContentGeometryEnabled else { + return updateStateAsync( + layer: &layer, + oldItem: oldItem, + oldSize: oldItem.size, + oldState: oldState, + newItem: newItem, + newSize: newItem.size, + newState: newState + ) + } + guard case let .content(oldContent) = oldItem.value, + case let .content(newContent) = newItem.value + else { + return false + } + var oldLocalState = oldState.pointee + var newLocalState = newState.pointee + var oldSize = oldItem.size + var newSize = newItem.size + switch (oldContent.value, newContent.value) { + case let (.image(oldImage), .image(newImage)): + adjustImageContentGeometry( + image: oldImage, + state: &oldLocalState, + size: &oldSize + ) + adjustImageContentGeometry( + image: newImage, + state: &newLocalState, + size: &newSize + ) + case let (.shape(oldPath, _, _), .shape(newPath, _, _)): + adjustShapeContentGeometry( + layer: layer, + state: &oldLocalState, + size: &oldSize, + path: oldPath + ) + adjustShapeContentGeometry( + layer: layer, + state: &newLocalState, + size: &newSize, + path: newPath + ) + default: + return false + } + return withUnsafePointer(to: oldLocalState) { oldStatePtr in + withUnsafePointer(to: newLocalState) { newStatePtr in + updateStateAsync( + layer: &layer, + oldItem: oldItem, + oldSize: oldSize, + oldState: oldStatePtr, + newItem: newItem, + newSize: newSize, + newState: newStatePtr + ) + } + } + } + + @inline(__always) + private func adjustImageContentGeometry( + image: GraphicsImage, + state: inout DisplayList.ViewUpdater.Model.State, + size: inout CGSize + ) { + let orientation = image.bitmapOrientation + if orientation != .up { + state.transform = CGAffineTransform( + orientation: orientation, + in: size + ).concatenating(state.transform) + size = size.apply(orientation) + } + } + + @inline(__always) + private func adjustShapeContentGeometry( + layer: DisplayList.ViewUpdater.AsyncLayer, + state: inout DisplayList.ViewUpdater.Model.State, + size: inout CGSize, + path: Path + ) { + let bounds = ShapeLayerHelper.makeLayerBounds( + size: size, + path: path, + layerType: type(of: layer.layer), + contentsScale: state.globals.pointee.environment.contentsScale + ) + state.transform = state.transform.translatedBy( + x: bounds.origin.x, + y: bounds.origin.y + ) + size = bounds.size } func updateState( diff --git a/Sources/OpenSwiftUICore/Shape/ShapeLayer.swift b/Sources/OpenSwiftUICore/Shape/ShapeLayer.swift index 30a962bbf..9d27938b0 100644 --- a/Sources/OpenSwiftUICore/Shape/ShapeLayer.swift +++ b/Sources/OpenSwiftUICore/Shape/ShapeLayer.swift @@ -111,32 +111,6 @@ extension DisplayList.ViewUpdater.AsyncLayer { return false } } - - private mutating func update

( - _ property: P.Type, - from oldValue: P.Value, - to newValue: P.Value - ) where P: Property, P.Value: Equatable { - guard oldValue != newValue else { - return - } - setValue(newValue, for: property) - } - - private mutating func setValue

( - _ value: P.Value, - for property: P.Type - ) where P: Property { - guard !isInvalid else { - return - } - cache.pointee.setAsyncValue( - P.boxValue(value), - for: P.keyPath, - in: layer, - usingPresentationModifier: P.supportsPresentationModifier - ) - } } private struct ShadowColorProperty: DisplayList.ViewUpdater.AsyncLayer.Property { diff --git a/Sources/OpenSwiftUICore/View/Image/ImageLayer.swift b/Sources/OpenSwiftUICore/View/Image/ImageLayer.swift index 4257d5cba..a2c4cc19b 100644 --- a/Sources/OpenSwiftUICore/View/Image/ImageLayer.swift +++ b/Sources/OpenSwiftUICore/View/Image/ImageLayer.swift @@ -132,15 +132,16 @@ final package class ImageLayer: CALayer { add(animation, forKey: nil) } -// func updateAsync( -// layer: DisplayList.ViewUpdater.AsyncLayer, -// oldImage: GraphicsImage, -// oldSize: CGSize, -// newImage: GraphicsImage, -// newSize: CGSize -// ) -> Bool { -// _openSwiftUIUnimplementedFailure() -// } + static func updateAsync( + layer: inout DisplayList.ViewUpdater.AsyncLayer, + oldImage: GraphicsImage, + oldSize: CGSize, + newImage: GraphicsImage, + newSize: CGSize + ) -> Bool { + _openSwiftUIUnimplementedWarning() + return false + } } // MARK: - GraphicsImage + LayerStretch From 120116293a7b99ebdcb746af5cd576eeafe98841 Mon Sep 17 00:00:00 2001 From: Kyle Date: Thu, 14 May 2026 23:19:58 +0800 Subject: [PATCH 07/18] Fix makeItemView warning --- .../Render/DisplayList/DisplayListViewPlatform.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift index bd42df14c..64d8db4cb 100644 --- a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift +++ b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift @@ -1144,8 +1144,8 @@ extension DisplayList.ViewUpdater.Platform { let info = DisplayList.ViewUpdater.ViewInfo(platform: self, kind: .compositing) #if canImport(QuartzCore) let layer = info.layer - info.layer.allowsGroupOpacity = true - info.layer.allowsGroupBlending = true + layer.allowsGroupOpacity = true + layer.allowsGroupBlending = true #else _openSwiftUIPlatformUnimplementedWarning() #endif From f4cff18fc79d658d2564b788cc3b33410a929157 Mon Sep 17 00:00:00 2001 From: Kyle Date: Thu, 14 May 2026 23:22:00 +0800 Subject: [PATCH 08/18] Update Platform.updateStateAsync --- .../DisplayList/DisplayListAsyncLayer.swift | 33 +++++++- .../DisplayList/DisplayListViewPlatform.swift | 79 ++++++++++++++++++- 2 files changed, 110 insertions(+), 2 deletions(-) diff --git a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListAsyncLayer.swift b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListAsyncLayer.swift index 819c839c6..ca2662110 100644 --- a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListAsyncLayer.swift +++ b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListAsyncLayer.swift @@ -70,8 +70,16 @@ extension DisplayList.ViewUpdater { guard oldValue != newValue else { return } + setValue(P.self, to: newValue) + } + + @inline(__always) + mutating func setValue

( + _ property: P.Type, + to value: P.Value + ) where P: Property { cache.pointee.setAsyncValue( - P.boxValue(newValue), + P.boxValue(value), for: P.keyPath, in: layer, usingPresentationModifier: P.supportsPresentationModifier @@ -105,4 +113,27 @@ extension DisplayList.ViewUpdater { } } + struct OpacityLayer: AsyncLayer.Property { + static let keyPath = "opacity" + + static func boxValue(_ value: Float) -> NSObject { + NSNumber(value: value) + } + } + + struct ContentsMultiplyColor: AsyncLayer.Property { + static let keyPath = "contentsMultiplyColor" + + static func boxValue(_ value: Color.Resolved?) -> NSObject { + #if canImport(Darwin) + guard let value else { + return NSNull() + } + return unsafeDowncast(value.cgColor, to: NSObject.self) + #else + _openSwiftUIPlatformUnimplementedFailure() + #endif + } + } + } diff --git a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift index 64d8db4cb..c5f8fb9d3 100644 --- a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift +++ b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift @@ -1032,7 +1032,73 @@ extension DisplayList.ViewUpdater.Platform { newSize: CGSize, newState: UnsafePointer ) -> Bool { - _openSwiftUIUnimplementedFailure() + guard oldState.pointee.properties == newState.pointee.properties else { + return false + } + layer.update( + DisplayList.ViewUpdater.OpacityLayer.self, + from: oldState.pointee.opacity, + to: newState.pointee.opacity + ) + guard oldState.pointee.versions.blend == newState.pointee.versions.blend else { + return false + } + if oldState.pointee.versions.filters != newState.pointee.versions.filters { + var oldFilters = oldState.pointee.filters + var newFilters = newState.pointee.filters + if layer.kind == .drawing { + let oldColor = oldFilters.popColorMultiply(drawable: layer.layer.delegate as? PlatformDrawable) + let newColor = newFilters.popColorMultiply(drawable: layer.layer.delegate as? PlatformDrawable) + layer.update( + DisplayList.ViewUpdater.ContentsMultiplyColor.self, + from: oldColor, + to: newColor + ) + } + guard GraphicsFilter.updateAsync( + layer: &layer, + oldFilters: oldFilters, + newFilters: newFilters + ) else { + return false + } + } + if oldState.pointee.versions.clips != newState.pointee.versions.clips || + oldState.pointee.versions.transform != newState.pointee.versions.transform { + guard updateClipShapesAsync( + asyncLayer: &layer, + oldState: oldState, + newState: newState + ) else { + return false + } + } + guard let boundsChanged = updateGeometryAsync( + asyncLayer: &layer, + oldItem: oldItem, + oldSize: oldSize, + oldState: oldState, + newItem: newItem, + newSize: newSize, + newState: newState + ) else { + return false + } + if boundsChanged || + oldState.pointee.versions.shadow != newState.pointee.versions.shadow || + oldItem.version != newItem.version { + guard updateShadowAsync( + asyncLayer: &layer, + oldState: oldState, + oldItem: oldItem, + newState: newState, + newItem: newItem, + boundsChanged: boundsChanged + ) else { + return false + } + } + return true } func _makeItemView( @@ -1752,3 +1818,14 @@ extension [GraphicsFilter] { // _openSwiftUIUnimplementedFailure() // } //} + +extension GraphicsFilter { + static func updateAsync( + layer: inout DisplayList.ViewUpdater.AsyncLayer, + oldFilters: [GraphicsFilter], + newFilters: [GraphicsFilter] + ) -> Bool { + _openSwiftUIUnimplementedWarning() + return false + } +} From d9f631ac3d8ea6ba6f0272568e86d4a50825ec8d Mon Sep 17 00:00:00 2001 From: Kyle Date: Thu, 14 May 2026 23:24:43 +0800 Subject: [PATCH 09/18] Update Platform.updateClipShapesAsync --- .../DisplayList/DisplayListAsyncLayer.swift | 8 ++++ .../DisplayList/DisplayListViewPlatform.swift | 43 ++++++++++++++++++- Sources/OpenSwiftUICore/Shape/MaskLayer.swift | 11 +++++ 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListAsyncLayer.swift b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListAsyncLayer.swift index ca2662110..9f508e9f2 100644 --- a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListAsyncLayer.swift +++ b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListAsyncLayer.swift @@ -121,6 +121,14 @@ extension DisplayList.ViewUpdater { } } + struct CornerRadiusLayer: AsyncLayer.Property { + static let keyPath = "cornerRadius" + + static func boxValue(_ value: CGFloat) -> NSObject { + NSNumber(value: Double(value)) + } + } + struct ContentsMultiplyColor: AsyncLayer.Property { static let keyPath = "contentsMultiplyColor" diff --git a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift index c5f8fb9d3..cc156cd95 100644 --- a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift +++ b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift @@ -1698,7 +1698,48 @@ extension DisplayList.ViewUpdater.Platform { oldState: UnsafePointer, newState: UnsafePointer ) -> Bool { - _openSwiftUIUnimplementedFailure() + #if canImport(QuartzCore) + guard !oldState.pointee.clips.isEmpty || !newState.pointee.clips.isEmpty else { + return true + } + if asyncLayer.isClipRectEnabled { + guard let oldClipRect = oldState.pointee.clipRect(), + let newClipRect = newState.pointee.clipRect(), + oldClipRect.style == newClipRect.style + else { + return false + } + asyncLayer.update( + DisplayList.ViewUpdater.CornerRadiusLayer.self, + from: oldClipRect.clampedCornerRadius, + to: newClipRect.clampedCornerRadius + ) + return true + } else { + guard newState.pointee.clipRect() == nil, + let maskLayer = asyncLayer.layer.mask else { + return false + } + var maskAsyncLayer = DisplayList.ViewUpdater.AsyncLayer( + layer: maskLayer, + cache: asyncLayer.cache, + kind: asyncLayer.kind, + flags: asyncLayer.flags, + nextUpdate: asyncLayer.nextUpdate, + isInvalid: asyncLayer.isInvalid + ) + return MaskLayer.updateClipsAsync( + layer: &maskAsyncLayer, + oldClips: oldState.pointee.clips, + newClips: newState.pointee.clips, + oldTransform: oldState.pointee.transform.inverted(), + newTransform: newState.pointee.transform.inverted() + ) + } + #else + _openSwiftUIUnimplementedWarning() + return false + #endif } private func updateGeometryAsync( diff --git a/Sources/OpenSwiftUICore/Shape/MaskLayer.swift b/Sources/OpenSwiftUICore/Shape/MaskLayer.swift index fbafcbc53..3347d25f3 100644 --- a/Sources/OpenSwiftUICore/Shape/MaskLayer.swift +++ b/Sources/OpenSwiftUICore/Shape/MaskLayer.swift @@ -112,6 +112,17 @@ final class MaskLayer: CAShapeLayer { finalTransform.ty += position.x * finalTransform.b + position.y * finalTransform.d - position.y layer.setAffineTransform(finalTransform) } + + static func updateClipsAsync( + layer: inout DisplayList.ViewUpdater.AsyncLayer, + oldClips: [DisplayList.ViewUpdater.Model.Clip], + newClips: [DisplayList.ViewUpdater.Model.Clip], + oldTransform: CGAffineTransform, + newTransform: CGAffineTransform + ) -> Bool { + _openSwiftUIUnimplementedWarning() + return false + } } #endif From a287b8d270be78b84e7cf51a4f3fe71202047451 Mon Sep 17 00:00:00 2001 From: Kyle Date: Thu, 14 May 2026 23:25:16 +0800 Subject: [PATCH 10/18] Update Platform.updateGeometryAsync --- .../DisplayList/DisplayListAsyncLayer.swift | 36 +++++++++ .../DisplayList/DisplayListViewPlatform.swift | 75 ++++++++++++++++++- 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListAsyncLayer.swift b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListAsyncLayer.swift index 9f508e9f2..b1e2e64bd 100644 --- a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListAsyncLayer.swift +++ b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListAsyncLayer.swift @@ -101,6 +101,42 @@ extension DisplayList.ViewUpdater { } } + struct PositionLayer: AsyncLayer.Property { + static let keyPath = "position" + + static func boxValue(_ value: CGPoint) -> NSObject { + #if canImport(QuartzCore) + NSValue(point: value) + #else + _openSwiftUIPlatformUnimplementedFailure() + #endif + } + } + + struct BoundsLayer: AsyncLayer.Property { + static let keyPath = "bounds" + + static func boxValue(_ value: CGRect) -> NSObject { + #if canImport(QuartzCore) + NSValue(rect: value) + #else + _openSwiftUIPlatformUnimplementedFailure() + #endif + } + } + + struct AffineTransformLayer: AsyncLayer.Property { + static let keyPath = "transform" + + static func boxValue(_ value: CGAffineTransform) -> NSObject { + #if canImport(QuartzCore) + NSValue(caTransform3D: CATransform3DMakeAffineTransform(value)) + #else + _openSwiftUIPlatformUnimplementedFailure() + #endif + } + } + struct LayerProjectionTransform: AsyncLayer.Property { static let keyPath = "transform" diff --git a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift index cc156cd95..2191c7dab 100644 --- a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift +++ b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift @@ -1751,7 +1751,80 @@ extension DisplayList.ViewUpdater.Platform { newSize: CGSize, newState: UnsafePointer ) -> Bool? { - _openSwiftUIUnimplementedFailure() + #if canImport(QuartzCore) + var oldBounds = CGRect(origin: .zero, size: oldSize) + var newBounds = CGRect(origin: .zero, size: newSize) + var oldPosition = CGPoint( + x: oldState.pointee.transform.tx, + y: oldState.pointee.transform.ty + ) + var newPosition = CGPoint( + x: newState.pointee.transform.tx, + y: newState.pointee.transform.ty + ) + + if asyncLayer.isClipRectEnabled, + let oldClipRect = oldState.pointee.clipRect(), + let newClipRect = newState.pointee.clipRect() { + oldBounds = oldClipRect.rect + newBounds = newClipRect.rect + oldPosition.x += oldBounds.origin.x + oldPosition.y += oldBounds.origin.y + newPosition.x += newBounds.origin.x + newPosition.y += newBounds.origin.y + } + + let boundsChanged = oldBounds != newBounds + if boundsChanged { + switch asyncLayer.kind { + case .platformView, .platformGroup, .platformLayer: + return nil + default: + break + } + asyncLayer.setValue( + DisplayList.ViewUpdater.BoundsLayer.self, + to: newBounds + ) + if asyncLayer.kind == .mask { + var maskLayer = DisplayList.ViewUpdater.AsyncLayer( + layer: asyncLayer.layer.mask!, + cache: asyncLayer.cache, + kind: asyncLayer.kind, + flags: asyncLayer.flags, + nextUpdate: asyncLayer.nextUpdate, + isInvalid: asyncLayer.isInvalid + ) + maskLayer.setValue( + DisplayList.ViewUpdater.BoundsLayer.self, + to: newBounds + ) + } + } + guard !asyncLayer.isProjectionGeometryEnabled else { + return boundsChanged + } + asyncLayer.update( + DisplayList.ViewUpdater.PositionLayer.self, + from: oldPosition, + to: newPosition + ) + var oldTransform = oldState.pointee.transform + oldTransform.tx = 0 + oldTransform.ty = 0 + var newTransform = newState.pointee.transform + newTransform.tx = 0 + newTransform.ty = 0 + asyncLayer.update( + DisplayList.ViewUpdater.AffineTransformLayer.self, + from: oldTransform, + to: newTransform + ) + return boundsChanged + #else + _openSwiftUIUnimplementedWarning() + return false + #endif } private func updateShadowAsync( From 11817312bc5bb5fbe4fe53e31ca2ee1c0e93adab Mon Sep 17 00:00:00 2001 From: Kyle Date: Thu, 14 May 2026 23:32:40 +0800 Subject: [PATCH 11/18] Update Platform.updateShapeViewAsync --- .../DisplayList/DisplayListViewPlatform.swift | 56 ++++++++++++++++++- .../OpenSwiftUICore/Shape/ShapeLayer.swift | 18 ++++++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift index 2191c7dab..3fe4b7c1f 100644 --- a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift +++ b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift @@ -1392,7 +1392,61 @@ extension DisplayList.ViewUpdater.Platform { newStyle: FillStyle, contentsChanged: Bool ) -> Bool { - _openSwiftUIUnimplementedFailure() + let currentLayerType = type(of: layer.layer) + let oldOriginalSize = oldSize + let newOriginalSize = newSize + let oldContentsScale = oldState.globals.pointee.environment.contentsScale + let newContentsScale = newState.globals.pointee.environment.contentsScale + let oldBounds = ShapeLayerHelper.makeLayerBounds( + size: oldOriginalSize, + path: oldPath, + layerType: currentLayerType, + contentsScale: oldContentsScale + ) + let newBounds = ShapeLayerHelper.makeLayerBounds( + size: newOriginalSize, + path: newPath, + layerType: currentLayerType, + contentsScale: newContentsScale + ) + if contentsChanged { + var oldHelper = ShapeLayerHelper( + layer: layer.layer, + layerType: currentLayerType, + path: oldPath, + origin: oldBounds.origin, + paint: oldPaint, + paintBounds: CGRect( + origin: CGPoint(x: -oldBounds.origin.x, y: -oldBounds.origin.y), + size: oldOriginalSize + ), + style: oldStyle, + contentsScale: oldContentsScale, + mayClip: !oldState.hasDODEffects + ) + var newHelper = ShapeLayerHelper( + layer: layer.layer, + layerType: currentLayerType, + path: newPath, + origin: newBounds.origin, + paint: newPaint, + paintBounds: CGRect( + origin: CGPoint(x: -newBounds.origin.x, y: -newBounds.origin.y), + size: newOriginalSize + ), + style: newStyle, + contentsScale: newContentsScale, + mayClip: !newState.hasDODEffects + ) + guard ShapeLayerHelper.updateAsync(layer: &layer, old: &oldHelper, new: &newHelper) else { + return false + } + } + oldState.transform = oldState.transform.translatedBy(x: oldBounds.origin.x, y: oldBounds.origin.y) + newState.transform = newState.transform.translatedBy(x: newBounds.origin.x, y: newBounds.origin.y) + oldSize = oldBounds.size + newSize = newBounds.size + return true } private func updateDrawingViewAsync( diff --git a/Sources/OpenSwiftUICore/Shape/ShapeLayer.swift b/Sources/OpenSwiftUICore/Shape/ShapeLayer.swift index 9d27938b0..1310d6457 100644 --- a/Sources/OpenSwiftUICore/Shape/ShapeLayer.swift +++ b/Sources/OpenSwiftUICore/Shape/ShapeLayer.swift @@ -46,6 +46,24 @@ struct ShapeLayerHelper: ResolvedPaintVisitor { ) -> CGRect { _openSwiftUIUnimplementedFailure() } + + static func updateAsync( + layer: inout DisplayList.ViewUpdater.AsyncLayer, + old: UnsafeMutablePointer, + new: UnsafeMutablePointer + ) -> Bool { + guard old.pointee.style.isEOFilled == new.pointee.style.isEOFilled, + old.pointee.style.isAntialiased == new.pointee.style.isAntialiased, + old.pointee.mayClip == new.pointee.mayClip + else { + return false + } + return withUnsafeMutablePointer(to: &layer) { layer in + var helper = ShapeLayerAsyncHelper(layer: layer, old: old, new: new, result: false) + old.pointee.paint.visit(&helper) + return helper.result + } + } } // MARK: - ShapeLayerShadowHelper [WIP] From 09b5f868ac38b3fe433c01181933e32a201bf64c Mon Sep 17 00:00:00 2001 From: Kyle Date: Thu, 14 May 2026 23:38:10 +0800 Subject: [PATCH 12/18] Update Platform.forEachChild --- .../Render/DisplayList/DisplayListViewPlatform.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift index 3fe4b7c1f..be2e9d3c8 100644 --- a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift +++ b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift @@ -1915,7 +1915,6 @@ extension DisplayList.ViewUpdater.Platform { of viewInfo: DisplayList.ViewUpdater.ViewInfo, do body: (AnyObject) -> Void ) { - #if canImport(Darwin) let kind = viewInfo.state.kind if kind.isContainer { for subview in subviews(viewInfo.container) { @@ -1925,10 +1924,9 @@ extension DisplayList.ViewUpdater.Platform { if kind == .mask, let maskView = maskView(viewInfo.view) { for subview in subviews(maskView) { - body(subview as AnyObject) + body(subview) } } - #endif } } From c1840dcc0e216a407450c72d1bc7703ee58a92f5 Mon Sep 17 00:00:00 2001 From: Kyle Date: Thu, 14 May 2026 23:44:00 +0800 Subject: [PATCH 13/18] Update Platform.updateShadowAsync --- .../DisplayList/DisplayListViewPlatform.swift | 79 ++++++++++++- .../OpenSwiftUICore/Shape/ShapeLayer.swift | 108 ++++++++++++------ 2 files changed, 151 insertions(+), 36 deletions(-) diff --git a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift index be2e9d3c8..f5ae898b7 100644 --- a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift +++ b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift @@ -1889,7 +1889,84 @@ extension DisplayList.ViewUpdater.Platform { newItem: DisplayList.Item, boundsChanged: Bool ) -> Bool { - _openSwiftUIUnimplementedFailure() + #if canImport(QuartzCore) + let oldShadow = oldState.pointee.shadow?.value + let newShadow = newState.pointee.shadow?.value + switch (oldShadow, newShadow) { + case (nil, nil): + return true + case (nil, _?), (_?, nil): + return false + case let (oldShadow?, newShadow?): + guard boundsChanged || oldShadow != newShadow else { + return true + } + guard asyncLayer.kind != .inherited, + case let .content(oldContent) = oldItem.value, + case let .content(newContent) = newItem.value + else { + return asyncLayer.updateShadowStyle( + oldShadow: oldShadow, + newShadow: newShadow + ) + } + switch (oldContent.value, newContent.value) { + case let (.color(oldColor), .color(newColor)): + return _updateShadowAsync( + layer: &asyncLayer, + oldShadow: oldShadow, + newShadow: newShadow, + oldPaintOpacity: oldColor.opacity, + newPaintOpacity: newColor.opacity + ) + case let (.shape(oldPath, oldPaint, _), .shape(newPath, newPaint, _)): + let layerType = type(of: asyncLayer.layer) + let oldBounds = ShapeLayerHelper.makeLayerBounds( + size: oldItem.size, + path: oldPath, + layerType: layerType, + contentsScale: oldState.pointee.globals.pointee.environment.contentsScale + ) + let newBounds = ShapeLayerHelper.makeLayerBounds( + size: newItem.size, + path: newPath, + layerType: layerType, + contentsScale: newState.pointee.globals.pointee.environment.contentsScale + ) + var oldHelper = ShapeLayerShadowHelper( + platform: self, + layer: asyncLayer.layer, + path: oldPath, + offset: oldBounds.origin, + shadow: oldShadow, + updateShape: false + ) + var newHelper = ShapeLayerShadowHelper( + platform: self, + layer: asyncLayer.layer, + path: newPath, + offset: newBounds.origin, + shadow: newShadow, + updateShape: false + ) + return ShapeLayerShadowHelper.updateAsync( + layer: &asyncLayer, + old: &oldHelper, + new: &newHelper, + oldPaint: oldPaint, + newPaint: newPaint + ) + default: + return asyncLayer.updateShadowStyle( + oldShadow: oldShadow, + newShadow: newShadow + ) + } + } + #else + _openSwiftUIUnimplementedWarning() + return false + #endif } func updateDrawingView( diff --git a/Sources/OpenSwiftUICore/Shape/ShapeLayer.swift b/Sources/OpenSwiftUICore/Shape/ShapeLayer.swift index 1310d6457..a7c318833 100644 --- a/Sources/OpenSwiftUICore/Shape/ShapeLayer.swift +++ b/Sources/OpenSwiftUICore/Shape/ShapeLayer.swift @@ -79,6 +79,47 @@ struct ShapeLayerShadowHelper: ResolvedPaintVisitor { mutating func visitPaint

(_ paint: P) where P: ResolvedPaint { _openSwiftUIUnimplementedFailure() } + + @inline(__always) + static func updateAsync( + layer: inout DisplayList.ViewUpdater.AsyncLayer, + old: UnsafeMutablePointer, + new: UnsafeMutablePointer, + oldPaint: AnyResolvedPaint, + newPaint: AnyResolvedPaint + ) -> Bool { + return withUnsafeMutablePointer(to: &layer) { layer in + var helper = ShapeLayerAsyncShadowHelper( + layer: layer, + old: old, + new: new, + newPaint: newPaint, + result: false + ) + oldPaint.visit(&helper) + return helper.result + } + } +} + +func _updateShadowAsync( + layer: inout DisplayList.ViewUpdater.AsyncLayer, + oldShadow: ResolvedShadowStyle?, + newShadow: ResolvedShadowStyle?, + oldPaintOpacity: Float, + newPaintOpacity: Float +) -> Bool { + var oldShadow = oldShadow + var newShadow = newShadow + if var shadow = oldShadow { + shadow.color = shadow.color.multiplyingOpacity(by: oldPaintOpacity) + oldShadow = shadow + } + if var shadow = newShadow { + shadow.color = shadow.color.multiplyingOpacity(by: newPaintOpacity) + newShadow = shadow + } + return layer.updateShadowStyle(oldShadow: oldShadow, newShadow: newShadow) } // MARK: - Async Shape Helpers @@ -106,7 +147,7 @@ private struct ShapeLayerAsyncShadowHelper: ResolvedPaintVisitor { } } -// FIXME: ShapeLayerShadowHelper & ShapeLayerAsyncShadowHelper +// MARK: - AsyncLayer + shadow extension DisplayList.ViewUpdater.AsyncLayer { @discardableResult @@ -117,51 +158,48 @@ extension DisplayList.ViewUpdater.AsyncLayer { switch (oldShadow, newShadow) { case (nil, nil): return true - case let (oldShadow?, newShadow?): - guard oldShadow.kind == newShadow.kind else { - return false - } - update(ShadowOffsetProperty.self, from: oldShadow.offset, to: newShadow.offset) - update(ShadowRadiusProperty.self, from: oldShadow.radius, to: newShadow.radius) - update(ShadowColorProperty.self, from: oldShadow.color, to: newShadow.color) - return !isInvalid + case let (oldShadow?, newShadow?) where oldShadow.kind == newShadow.kind: + update(DisplayList.ViewUpdater.ShadowOffsetProperty.self, from: oldShadow.offset, to: newShadow.offset) + update(DisplayList.ViewUpdater.ShadowRadiusProperty.self, from: oldShadow.radius, to: newShadow.radius) + update(DisplayList.ViewUpdater.ShadowColorProperty.self, from: oldShadow.color, to: newShadow.color) + return true default: return false } } } -private struct ShadowColorProperty: DisplayList.ViewUpdater.AsyncLayer.Property { - static let keyPath = "shadowColor" +extension DisplayList.ViewUpdater { + struct ShadowOffsetProperty: DisplayList.ViewUpdater.AsyncLayer.Property { + static let keyPath = "shadowOffset" - static func boxValue(_ value: Color.Resolved) -> NSObject { - #if canImport(Darwin) - return value.cgColor as! NSObject - #else - return NSObject() - #endif + static func boxValue(_ value: CGSize) -> NSObject { + #if canImport(Darwin) + NSValue(size: value) + #else + _openSwiftUIPlatformUnimplementedFailure() + #endif + } } -} + + struct ShadowRadiusProperty: DisplayList.ViewUpdater.AsyncLayer.Property { + static let keyPath = "shadowRadius" -private struct ShadowRadiusProperty: DisplayList.ViewUpdater.AsyncLayer.Property { - static let keyPath = "shadowRadius" - - static func boxValue(_ value: CGFloat) -> NSObject { - NSNumber(value: Double(value)) + static func boxValue(_ value: Double) -> NSObject { + NSNumber(value: value) + } } -} -private struct ShadowOffsetProperty: DisplayList.ViewUpdater.AsyncLayer.Property { - static let keyPath = "shadowOffset" - - static func boxValue(_ value: CGSize) -> NSObject { - #if canImport(Darwin) -// return NSValue(size: value) - // FIXME - return NSObject() - #else - return NSObject() - #endif + struct ShadowColorProperty: DisplayList.ViewUpdater.AsyncLayer.Property { + static let keyPath = "shadowColor" + + static func boxValue(_ value: Color.Resolved) -> NSObject { + #if canImport(Darwin) + unsafeDowncast(value.cgColor, to: NSObject.self) + #else + _openSwiftUIPlatformUnimplementedFailure() + #endif + } } } From fddd4a113df7cc608617dda6e6953968f50ec7d1 Mon Sep 17 00:00:00 2001 From: Kyle Date: Thu, 14 May 2026 23:45:27 +0800 Subject: [PATCH 14/18] Update Platform header information --- .../Render/DisplayList/DisplayListViewPlatform.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift index f5ae898b7..f8ac4cf7a 100644 --- a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift +++ b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayListViewPlatform.swift @@ -3,7 +3,7 @@ // OpenSwiftUICore // // Audited for 6.5.4 -// Status: Blocked by GraphicsContext and Platform +// Status: Blocked by GraphicsContext // ID: 8BBC66CBE42B8A65F8A2F3799C81A349 (SwiftUICore) public import OpenQuartzCoreShims @@ -187,7 +187,7 @@ extension DisplayList.ViewUpdater { } } -// MARK: - DisplayList.ViewUpdater.Platform API [WIP] +// MARK: - DisplayList.ViewUpdater.Platform API extension DisplayList.ViewUpdater.Platform { package init(definition: PlatformViewDefinition.Type) { @@ -2031,7 +2031,7 @@ extension DisplayList.GraphicsRenderer { } } -// MARK: - GraphicsFilter + Platform Filters +// MARK: - GraphicsFilter + Platform Filters [TODO] extension [GraphicsFilter] { fileprivate mutating func popColorMultiply( From 311fa4758b2b0d988a30d57bf96da0f734006007 Mon Sep 17 00:00:00 2001 From: Kyle Date: Thu, 14 May 2026 23:46:16 +0800 Subject: [PATCH 15/18] Update xcsheme --- .../xcshareddata/xcschemes/OSUI_Example.xcscheme | 5 +++++ .../xcshareddata/xcschemes/OSUI_HostingExample.xcscheme | 5 +++++ .../xcshareddata/xcschemes/SUI_HostingExample.xcscheme | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/Example/Example.xcodeproj/xcshareddata/xcschemes/OSUI_Example.xcscheme b/Example/Example.xcodeproj/xcshareddata/xcschemes/OSUI_Example.xcscheme index f9ebc5585..b1b0b8e82 100644 --- a/Example/Example.xcodeproj/xcshareddata/xcschemes/OSUI_Example.xcscheme +++ b/Example/Example.xcodeproj/xcshareddata/xcschemes/OSUI_Example.xcscheme @@ -69,6 +69,11 @@ + + + + + + Date: Thu, 14 May 2026 23:46:58 +0800 Subject: [PATCH 16/18] Update nextUpdate API --- .../Animation/Timeline/TimelineSchedule.swift | 13 +++ .../Render/DisplayList/DisplayList.swift | 103 ++++++++++++++---- .../ShapeStyle/ShapeStyleRendering.swift | 10 +- .../View/Text/Text/Text+View.swift | 10 +- 4 files changed, 109 insertions(+), 27 deletions(-) diff --git a/Sources/OpenSwiftUICore/Animation/Timeline/TimelineSchedule.swift b/Sources/OpenSwiftUICore/Animation/Timeline/TimelineSchedule.swift index 56e343699..d5760b131 100644 --- a/Sources/OpenSwiftUICore/Animation/Timeline/TimelineSchedule.swift +++ b/Sources/OpenSwiftUICore/Animation/Timeline/TimelineSchedule.swift @@ -132,6 +132,19 @@ extension TimelineSchedule { ) -> AnySequence { AnySequence(entries(within: range, mode: mode, limit: limit)) } +} + +extension TimelineSchedule { + package func nextEntry( + after date: Date, + mode: TimelineScheduleMode, + limit: UInt? + ) -> Date { + entries(from: date, mode: mode) + .lazy + .abort(after: limit) + .first { $0 > date } ?? .distantFuture + } private func entries( within range: Range, diff --git a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayList.swift b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayList.swift index 5b05bf1a9..4647ad690 100644 --- a/Sources/OpenSwiftUICore/Render/DisplayList/DisplayList.swift +++ b/Sources/OpenSwiftUICore/Render/DisplayList/DisplayList.swift @@ -110,19 +110,17 @@ package struct DisplayList: Equatable { // TODO - // TBA package func nextUpdate(after time: Time) -> Time { guard !features.contains(.animations) else { return time } var nextUpdate = Time.infinity - guard features.contains(.dynamicContent) else { - return nextUpdate - } - for item in items { - nextUpdate = min(nextUpdate, item.nextUpdate(after: time)) - if nextUpdate <= time { - break + if features.contains(.dynamicContent) { + for item in items { + nextUpdate = min(nextUpdate, item.nextUpdate(after: time)) + if nextUpdate == time { + break + } } } return nextUpdate @@ -670,6 +668,8 @@ extension DisplayList.Item { extension DisplayList { // FIXME package class InterpolatorGroup { + var maxDuration: Double = .zero + private struct Contents { var list: DisplayList var origin: CGPoint @@ -716,6 +716,10 @@ extension DisplayList { [] } + func nextUpdate(after _: Time) -> Time { + .infinity + } + func rewriteDisplayList( _ list: inout DisplayList, time: Attribute