Skip to content

Commit

Permalink
Add ViewGraphFeatureBuffer (#179)
Browse files Browse the repository at this point in the history
* Update UnsafeHeterogeneousBuffer.swift

* Add ViewGraphFeatureBuffer
  • Loading branch information
Kyle-Ye authored Dec 23, 2024
1 parent 958481b commit bbf40ef
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,19 +50,9 @@ package struct UnsafeHeterogeneousBuffer: Collection {
}

private mutating func allocate(_ bytes: Int) -> UnsafeMutableRawPointer {
var count = _count
var offset = 0
var size = 0
while count != 0 {
let itemSize = buf
.advanced(by: offset)
.assumingMemoryBound(to: Item.self)
.pointee
.size
offset &+= Int(itemSize)
count &-= 1
offset = count == 0 ? 0 : offset
size &+= Int(itemSize)
for element in self {
size += Int(element.item.pointee.size)
}
// Grow buffer if needed
if Int(available) < bytes {
Expand Down Expand Up @@ -108,7 +98,6 @@ package struct UnsafeHeterogeneousBuffer: Collection {
oldBuffer += size
newBuffer += size
} while count != 0 || itemSize != 0

}
buf.deallocate()
}
Expand All @@ -118,18 +107,8 @@ package struct UnsafeHeterogeneousBuffer: Collection {

package func destroy() {
defer { buf?.deallocate() }
guard _count != 0 else {
return
}
var count = _count
var offset = 0
while count != 0 {
let itemPointer = buf
.advanced(by: offset)
.assumingMemoryBound(to: Item.self)
itemPointer.pointee.vtable.deinitialize(elt: .init(item: itemPointer))
offset &+= Int(itemPointer.pointee.size)
count &-= 1
for element in self {
element.item.pointee.vtable.deinitialize(elt: element)
}
}

Expand Down
27 changes: 19 additions & 8 deletions Sources/OpenSwiftUICore/View/Graph/ViewGraph.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ package final class ViewGraph: GraphHost {

package weak var delegate: (any ViewGraphDelegate)? = nil

// private var features: ViewGraphFeaturesBuffer = .init()
private var features: ViewGraphFeatureBuffer = .init(contents: .init())

package var centersRootView: Bool = true

Expand Down Expand Up @@ -183,15 +183,20 @@ package final class ViewGraph: GraphHost {
deinit {
// FIXME
removePreferenceOutlets(isInvalidating: true)
features.contents.destroy()
}

override package var graphDelegate: GraphDelegate? { delegate }

override package var parentHost: GraphHost? { preferenceBridge?.viewGraph }

// TODO: ViewGraphFeature
// package func append<T>(feature: T) where T: ViewGraphFeature
// package subscript<T>(feature: T.Type) -> UnsafeMutablePointer<T>? where T: ViewGraphFeature
package func append<T>(feature: T) where T: ViewGraphFeature {
features.append(feature)
}

package subscript<T>(feature: T.Type) -> UnsafeMutablePointer<T>? where T: ViewGraphFeature {
features[feature]
}

override package func instantiateOutputs() {
#if canImport(Darwin)
Expand Down Expand Up @@ -231,9 +236,13 @@ package final class ViewGraph: GraphHost {
) { rootGeometry in
rootGeometry.$layoutDirection = inputs.mapEnvironment(\.layoutDirection)
}
// TODO: features related
let outputs = makeRootView(rootView, inputs)
// TODO: features related
for feature in features {
feature.modifyViewInputs(inputs: &inputs, graph: self)
}
var outputs = makeRootView(rootView, inputs)
for feature in features {
feature.modifyViewOutputs(outputs: &outputs, inputs: inputs, graph: self)
}
return outputs
}
$rootGeometry.mutateBody(
Expand Down Expand Up @@ -263,7 +272,9 @@ package final class ViewGraph: GraphHost {
override package func uninstantiateOutputs() {
#if canImport(Darwin)
removePreferenceOutlets(isInvalidating: false)
// TODO: features
for feature in features {
feature.uninstantiate(graph: self)
}
$rootGeometry.mutateBody(
as: RootGeometry.self,
invalidating: true
Expand Down
127 changes: 127 additions & 0 deletions Sources/OpenSwiftUICore/View/Graph/ViewGraphFeature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
//
// Audited for iOS 18.0
// Status: Complete
// ID: 8A0FC0E1EA10CEEE185C2315B618A95C (SwiftUICore)

package protocol ViewGraphFeature {
mutating func modifyViewInputs(inputs: inout _ViewInputs, graph: ViewGraph)
Expand All @@ -24,3 +25,129 @@ extension ViewGraphFeature {
package mutating func needsUpdate(graph: ViewGraph) -> Bool { false }
package mutating func update(graph: ViewGraph) {}
}

struct ViewGraphFeatureBuffer: Collection {
var contents: UnsafeHeterogeneousBuffer

@discardableResult
mutating func append<Feature>(_ feature: Feature) -> UnsafeHeterogeneousBuffer.Index where Feature: ViewGraphFeature {
contents.append(feature, vtable: _VTable<Feature>.self)
}

subscript<Feature>(_ type: Feature.Type) -> UnsafeMutablePointer<Feature>? where Feature: ViewGraphFeature {
guard !contents.isEmpty else { return nil }
for element in contents {
guard element.hasType(type) else {
continue
}
return element.body(as: type)
}
return nil
}

typealias Index = UnsafeHeterogeneousBuffer.Index

struct Element: ViewGraphFeature {
var base: UnsafeHeterogeneousBuffer.Element

private var vtable: VTable.Type {
base.vtable(as: VTable.self)
}

func modifyViewInputs(inputs: inout _ViewInputs, graph: ViewGraph) {
vtable.modifyViewInputs(elt: base, inputs: &inputs, graph: graph)
}

func modifyViewOutputs(outputs: inout _ViewOutputs, inputs: _ViewInputs, graph: ViewGraph) {
vtable.modifyViewOutputs(elt: base, outputs: &outputs, inputs: inputs, graph: graph)
}

func uninstantiate(graph: ViewGraph) {
vtable.uninstantiate(elt: base, graph: graph)
}

func isHiddenForReuseDidChange(graph: ViewGraph) {
vtable.isHiddenForReuseDidChange(elt: base, graph: graph)
}

func allowsAsyncUpdate(graph: ViewGraph) -> Bool? {
vtable.allowsAsyncUpdate(elt: base, graph: graph)
}

func needsUpdate(graph: ViewGraph) -> Bool {
vtable.needsUpdate(elt: base, graph: graph)
}

func update(graph: ViewGraph) {
vtable.update(elt: base, graph: graph)
}
}

var startIndex: UnsafeHeterogeneousBuffer.Index { contents.startIndex }

var endIndex: UnsafeHeterogeneousBuffer.Index { contents.endIndex }

var isEmpty: Bool { contents.isEmpty }

subscript(position: UnsafeHeterogeneousBuffer.Index) -> Element {
_read { yield Element(base: contents[position]) }
}

func index(after i: UnsafeHeterogeneousBuffer.Index) -> UnsafeHeterogeneousBuffer.Index {
contents.index(after: i)
}

private class VTable: _UnsafeHeterogeneousBuffer_VTable {
class func modifyViewInputs(elt: UnsafeHeterogeneousBuffer.Element, inputs: inout _ViewInputs, graph: ViewGraph) {}
class func modifyViewOutputs(elt: UnsafeHeterogeneousBuffer.Element, outputs: inout _ViewOutputs, inputs: _ViewInputs, graph: ViewGraph) {}
class func uninstantiate(elt: UnsafeHeterogeneousBuffer.Element, graph: ViewGraph) {}
class func isHiddenForReuseDidChange(elt: UnsafeHeterogeneousBuffer.Element, graph: ViewGraph) {}
class func needsUpdate(elt: UnsafeHeterogeneousBuffer.Element, graph: ViewGraph) -> Bool { false }
class func allowsAsyncUpdate(elt: UnsafeHeterogeneousBuffer.Element, graph: ViewGraph) -> Bool? { nil }
class func update(elt: UnsafeHeterogeneousBuffer.Element, graph: ViewGraph) {}
}

private final class _VTable<Feature>: VTable where Feature: ViewGraphFeature {
override class func hasType<T>(_ type: T.Type) -> Bool {
Feature.self == T.self
}

override class func moveInitialize(elt: UnsafeHeterogeneousBuffer.Element, from: _UnsafeHeterogeneousBuffer_Element) {
let dest = elt.body(as: Feature.self)
let source = from.body(as: Feature.self)
dest.initialize(to: source.move())
}

override class func deinitialize(elt: UnsafeHeterogeneousBuffer.Element) {
elt.body(as: Feature.self).deinitialize(count: 1)
}

override class func modifyViewInputs(elt: UnsafeHeterogeneousBuffer.Element, inputs: inout _ViewInputs, graph: ViewGraph) {
elt.body(as: Feature.self).pointee.modifyViewInputs(inputs: &inputs, graph: graph)
}

override class func modifyViewOutputs(elt: UnsafeHeterogeneousBuffer.Element, outputs: inout _ViewOutputs, inputs: _ViewInputs, graph: ViewGraph) {
elt.body(as: Feature.self).pointee.modifyViewOutputs(outputs: &outputs, inputs: inputs, graph: graph)
}

override class func uninstantiate(elt: UnsafeHeterogeneousBuffer.Element, graph: ViewGraph) {
elt.body(as: Feature.self).pointee.uninstantiate(graph: graph)
}

override class func isHiddenForReuseDidChange(elt: UnsafeHeterogeneousBuffer.Element, graph: ViewGraph) {
elt.body(as: Feature.self).pointee.isHiddenForReuseDidChange(graph: graph)
}

override class func needsUpdate(elt: UnsafeHeterogeneousBuffer.Element, graph: ViewGraph) -> Bool {
elt.body(as: Feature.self).pointee.needsUpdate(graph: graph)
}

override class func allowsAsyncUpdate(elt: UnsafeHeterogeneousBuffer.Element, graph: ViewGraph) -> Bool? {
elt.body(as: Feature.self).pointee.allowsAsyncUpdate(graph: graph)
}

override class func update(elt: UnsafeHeterogeneousBuffer.Element, graph: ViewGraph) {
elt.body(as: Feature.self).pointee.update(graph: graph)
}
}
}

0 comments on commit bbf40ef

Please sign in to comment.