Skip to content

Commit

Permalink
Fixed Threading Issue
Browse files Browse the repository at this point in the history
  • Loading branch information
Lakr233 committed Aug 16, 2024
1 parent 5d4b1a5 commit f5235cc
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 69 deletions.
4 changes: 4 additions & 0 deletions Example/ColorfulApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
500E7C462C6F5397001D7AA1 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 500E7C452C6F5397001D7AA1 /* main.swift */; };
507C2F092C36FB1000BCB5FA /* ChessboardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507C2F082C36FB1000BCB5FA /* ChessboardView.swift */; };
50C663882B19B04800AF7053 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50C663872B19B04800AF7053 /* App.swift */; };
50C6638A2B19B04800AF7053 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50C663892B19B04800AF7053 /* ContentView.swift */; };
Expand All @@ -15,6 +16,7 @@
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
500E7C452C6F5397001D7AA1 /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
507C2F082C36FB1000BCB5FA /* ChessboardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChessboardView.swift; sourceTree = "<group>"; };
50C663842B19B04800AF7053 /* ColorfulApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ColorfulApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
50C663872B19B04800AF7053 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -55,6 +57,7 @@
50C663862B19B04800AF7053 /* ColorfulApp */ = {
isa = PBXGroup;
children = (
500E7C452C6F5397001D7AA1 /* main.swift */,
50C663872B19B04800AF7053 /* App.swift */,
50C663892B19B04800AF7053 /* ContentView.swift */,
507C2F082C36FB1000BCB5FA /* ChessboardView.swift */,
Expand Down Expand Up @@ -145,6 +148,7 @@
files = (
50C6638A2B19B04800AF7053 /* ContentView.swift in Sources */,
507C2F092C36FB1000BCB5FA /* ChessboardView.swift in Sources */,
500E7C462C6F5397001D7AA1 /* main.swift in Sources */,
50C663882B19B04800AF7053 /* App.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
1 change: 0 additions & 1 deletion Example/ColorfulApp/App.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import ColorfulX
import SwiftUI

@main
struct App: SwiftUI.App {
var body: some Scene {
WindowGroup {
Expand Down
40 changes: 40 additions & 0 deletions Example/ColorfulApp/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//
// main.swift
//
//
// Created by 秋星桥 on 2024/8/16.
//

import ColorfulX
import Foundation

App.main()

/*

let threadTestTarget = AnimatedMulticolorGradientView()
let colors = ColorfulPreset.appleIntelligence.colors.map { ColorElement($0) }
threadTestTarget.setColors(colors.map { .init($0) })

DispatchQueue.main.async {
Thread {
while true {
DispatchQueue.main.asyncAndWait {
threadTestTarget.layoutSublayers(of: threadTestTarget.layer)
}
}
}.start()
Thread {
while true {
let colors = ColorfulPreset.allCases
.randomElement()!
.colors
.map { ColorElement($0) }
threadTestTarget.setColors(colors.map { .init($0) })
}
}.start()
}

CFRunLoopRun()

*/
9 changes: 9 additions & 0 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@
"version" : "1.0.3"
}
},
{
"identity" : "msdisplaylink",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Lakr233/MSDisplayLink.git",
"state" : {
"revision" : "08ad21d886e2e3f990ccbc6813eb6cdad8c427cd",
"version" : "1.0.0"
}
},
{
"identity" : "springinterpolation",
"kind" : "remoteSourceControl",
Expand Down
32 changes: 32 additions & 0 deletions Sources/ColorfulX/AnimatedMulticolorGradientView+Speckle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,35 @@ public extension AnimatedMulticolorGradientView {
}
}
}

extension AnimatedMulticolorGradientView.Speckle {
var colorVector: ColorVector {
let progress = transitionProgress.context.currentPos
return previousColor.lerp(to: targetColor, percent: progress)
}
}

public extension AnimatedMulticolorGradientView {
func setColors(_ colors: [ColorVector], interpolationEnabled: Bool = true, repeatToFillColorSlots: Bool = true) {
var colors = colors
if colors.isEmpty { colors.append(.init(v: .zero, space: .rgb)) }
colors = colors.map { $0.color(in: .lab) }

let endingIndex = repeatToFillColorSlots ? Uniforms.COLOR_SLOT : min(colors.count, Uniforms.COLOR_SLOT)
guard endingIndex > 0 else { return }

alteringSpeckles { speckles in
for idx in 0 ..< endingIndex {
var read = speckles[idx]
let color: ColorVector = colors[idx % colors.count]
guard read.targetColor != color else { continue }
let interpolationEnabled = interpolationEnabled && read.enabled
read.enabled = true
read.targetColor = color
read.previousColor = interpolationEnabled ? read.colorVector : color
read.transitionProgress.setCurrent(interpolationEnabled ? 0 : 1, 0)
speckles[idx] = read
}
}
}
}
56 changes: 30 additions & 26 deletions Sources/ColorfulX/AnimatedMulticolorGradientView+Update.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,51 +17,55 @@ extension AnimatedMulticolorGradientView {

func initializeRenderParameters() {
var rand = randomLocationPair()
for idx in 0 ..< colorElements.count {
for idx in 0 ..< speckles.count {
rand = randomLocationPair()
colorElements[idx].position.setCurrent(.init(x: rand.x, y: rand.y))
rand = randomLocationPair()
colorElements[idx].position.setTarget(.init(x: rand.x, y: rand.y))
alteringSpeckles { speckles in
speckles[idx].position.setCurrent(.init(x: rand.x, y: rand.y))
rand = randomLocationPair()
speckles[idx].position.setTarget(.init(x: rand.x, y: rand.y))
}
}
}

func updateRenderParameters(deltaTime: Double) {
// clear the flag
defer { renderInputWasModified = false }

let moveDelta = deltaTime * speed * 0.5 // just slow down

for idx in 0 ..< colorElements.count where colorElements[idx].enabled {
var inplaceEdit = colorElements[idx]
defer { colorElements[idx] = inplaceEdit }

if inplaceEdit.transitionProgress.context.currentPos < 1 {
inplaceEdit.transitionProgress.update(withDeltaTime: deltaTime * transitionSpeed)
}
if moveDelta > 0 {
inplaceEdit.position.update(withDeltaTime: moveDelta)
var points: [MulticolorGradientView.Parameters.ColorStop]?
alteringSpeckles { speckles in
for idx in 0 ..< speckles.count where speckles[idx].enabled {
if speckles[idx].transitionProgress.context.currentPos < 1 {
speckles[idx].transitionProgress.update(withDeltaTime: deltaTime * transitionSpeed)
}
if moveDelta > 0 {
speckles[idx].position.update(withDeltaTime: moveDelta)

let pos_x = inplaceEdit.position.x.context.currentPos
let tar_x = inplaceEdit.position.x.context.targetPos
let pos_y = inplaceEdit.position.y.context.currentPos
let tar_y = inplaceEdit.position.y.context.targetPos
if abs(pos_x - tar_x) < 0.125 || abs(pos_y - tar_y) < 0.125 {
let rand = randomLocationPair()
inplaceEdit.position.setTarget(.init(x: rand.x, y: rand.y))
let pos_x = speckles[idx].position.x.context.currentPos
let tar_x = speckles[idx].position.x.context.targetPos
let pos_y = speckles[idx].position.y.context.currentPos
let tar_y = speckles[idx].position.y.context.targetPos
if abs(pos_x - tar_x) < 0.125 || abs(pos_y - tar_y) < 0.125 {
let rand = randomLocationPair()
speckles[idx].position.setTarget(.init(x: rand.x, y: rand.y))
}
}
}
}

parameters = .init(
points: colorElements
points = speckles
.filter(\.enabled)
.map { .init(
color: computeSpeckleColor($0),
color: $0.colorVector,
position: .init(
x: $0.position.x.context.currentPos,
y: $0.position.y.context.currentPos
)
) },
) }
}
guard let points else { return }

parameters = .init(
points: points,
bias: bias,
noise: noise
)
Expand Down
48 changes: 9 additions & 39 deletions Sources/ColorfulX/AnimatedMulticolorGradientView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@ open class AnimatedMulticolorGradientView: MulticolorGradientView {
didSet { lastRenderParametersUpdate = obtainCurrentTimestamp() }
}

public internal(set) var colorElements: [Speckle] {
public private(set) var speckles: [Speckle] {
didSet { renderInputWasModified = true }
}

private let specklesAccessLock = NSLock()

public var speed: Double = 1.0 {
didSet { renderInputWasModified = true }
}
Expand All @@ -55,32 +57,11 @@ open class AnimatedMulticolorGradientView: MulticolorGradientView {
// MARK: - FUNCTION

override public init() {
colorElements = .init(repeating: .init(position: SPRING_ENGINE), count: Uniforms.COLOR_SLOT)
speckles = .init(repeating: .init(position: SPRING_ENGINE), count: Uniforms.COLOR_SLOT)
super.init()
initializeRenderParameters()
}

public func setColors(_ colors: [ColorVector], interpolationEnabled: Bool = true, repeatToFillColorSlots: Bool = true) {
var colors = colors
if colors.isEmpty { colors.append(.init(v: .zero, space: .rgb)) }
colors = colors.map { $0.color(in: .lab) }

let endingIndex = repeatToFillColorSlots ? Uniforms.COLOR_SLOT : min(colors.count, Uniforms.COLOR_SLOT)
guard endingIndex > 0 else { return }
for idx in 0 ..< endingIndex {
var read = colorElements[idx]
let color: ColorVector = colors[idx % colors.count]
guard read.targetColor != color else { continue }
let interpolationEnabled = interpolationEnabled && read.enabled
let currentColor = computeSpeckleColor(read)
read.enabled = true
read.targetColor = color
read.previousColor = interpolationEnabled ? currentColor : color
read.transitionProgress.setCurrent(interpolationEnabled ? 0 : 1, 0)
colorElements[idx] = read
}
}

// MARK: - GETTER

@inline(__always)
Expand Down Expand Up @@ -123,7 +104,7 @@ open class AnimatedMulticolorGradientView: MulticolorGradientView {

@inline(__always)
func isColorTransitionCompleted() -> Bool {
colorElements
speckles
.filter(\.enabled)
.allSatisfy { $0.transitionProgress.context.currentPos >= 1 }
}
Expand All @@ -139,16 +120,9 @@ open class AnimatedMulticolorGradientView: MulticolorGradientView {

// MARK: - RENDER LIFE CYCLE

private let updateParametersLock = NSLock()

override public func layoutSublayers(of layer: CALayer) {
super.layoutSublayers(of: layer)

updateParametersLock.lock()
renderInputWasModified = true
updateRenderParameters(deltaTime: deltaTimeForRenderParametersUpdate())
updateParametersLock.unlock()

renderIfNeeded()
}

Expand All @@ -159,17 +133,13 @@ open class AnimatedMulticolorGradientView: MulticolorGradientView {

override func vsync() {
guard shouldRenderNextFrameWithinSynchornization() else { return }

updateParametersLock.lock()
updateRenderParameters(deltaTime: deltaTimeForRenderParametersUpdate())
updateParametersLock.unlock()

// sine the render parameters were updated, we call super.vsync to render
super.vsync()
}

func computeSpeckleColor(_ speckle: Speckle) -> ColorVector {
let progress = speckle.transitionProgress.context.currentPos
return speckle.previousColor.lerp(to: speckle.targetColor, percent: progress)
func alteringSpeckles(_ callback: (inout [Speckle]) -> Void) {
specklesAccessLock.lock()
callback(&speckles)
specklesAccessLock.unlock()
}
}
14 changes: 11 additions & 3 deletions Sources/ColorfulX/MulticolorGradientView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,17 @@ open class MulticolorGradientView: MetalView {
commandBuffer.commit()
commandBuffer.waitUntilScheduled()

drawable.present()
currentDrawable = drawable
commandBuffer.waitUntilCompleted()
if Thread.isMainThread {
drawable.present()
currentDrawable = drawable
commandBuffer.waitUntilCompleted()
} else {
DispatchQueue.main.asyncAndWait {
drawable.present()
currentDrawable = drawable
commandBuffer.waitUntilCompleted()
}
}
}
}

Expand Down

0 comments on commit f5235cc

Please sign in to comment.