@@ -57,7 +57,7 @@ struct Work: Sendable {
57
57
}
58
58
}
59
59
60
- func render( scene: Scene , ctx: JSObject , renderTime : JSObject , concurrency: Int , executor: WebWorkerTaskExecutor ) async {
60
+ func render( scene: Scene , ctx: JSObject , renderTimeElement : JSObject , concurrency: Int , executor: ( some TaskExecutor ) ? ) async {
61
61
62
62
let imageBuffer = UnsafeMutableBufferPointer< Color> . allocate( capacity: scene. width * scene. height)
63
63
// Initialize the buffer with black color
@@ -67,12 +67,16 @@ func render(scene: Scene, ctx: JSObject, renderTime: JSObject, concurrency: Int,
67
67
let clock = ContinuousClock ( )
68
68
let start = clock. now
69
69
70
+ func updateRenderTime( ) {
71
+ let renderSceneDuration = clock. now - start
72
+ renderTimeElement. textContent = . string( " Render time: \( renderSceneDuration) " )
73
+ }
74
+
70
75
var checkTimer : JSValue ?
71
76
checkTimer = JSObject . global. setInterval!( JSClosure { _ in
72
77
print ( " Checking thread work... " )
73
78
renderInCanvas ( ctx: ctx, image: imageView)
74
- let renderSceneDuration = clock. now - start
75
- renderTime. textContent = . string( " Render time: \( renderSceneDuration) " )
79
+ updateRenderTime ( )
76
80
return . undefined
77
81
} , 250 )
78
82
@@ -94,30 +98,41 @@ func render(scene: Scene, ctx: JSObject, renderTime: JSObject, concurrency: Int,
94
98
checkTimer = nil
95
99
96
100
renderInCanvas ( ctx: ctx, image: imageView)
101
+ updateRenderTime ( )
97
102
imageBuffer. deallocate ( )
98
103
print ( " All work done " )
99
104
}
100
105
106
+ func onClick( ) async throws {
107
+ let document = JSObject . global. document
108
+
109
+ let canvasElement = document. getElementById ( " canvas " ) . object!
110
+ let renderTimeElement = document. getElementById ( " render-time " ) . object!
111
+
112
+ let concurrency = max ( Int ( document. getElementById ( " concurrency " ) . object!. value. string!) ?? 1 , 1 )
113
+ let background = document. getElementById ( " background " ) . object!. checked. boolean!
114
+ let size = Int ( document. getElementById ( " size " ) . object!. value. string ?? " 800 " ) !
115
+
116
+ let ctx = canvasElement. getContext!( " 2d " ) . object!
117
+
118
+ let scene = createDemoScene ( size: size)
119
+ let executor = background ? try await WebWorkerTaskExecutor ( numberOfThreads: concurrency) : nil
120
+ canvasElement. width = . number( Double ( scene. width) )
121
+ canvasElement. height = . number( Double ( scene. height) )
122
+
123
+ await render ( scene: scene, ctx: ctx, renderTimeElement: renderTimeElement, concurrency: concurrency, executor: executor)
124
+ executor? . terminate ( )
125
+ print ( " Render done " )
126
+ }
127
+
101
128
func main( ) async throws {
102
- let canvas = JSObject . global. document. getElementById ( " canvas " ) . object!
103
- let renderButton = JSObject . global. document. getElementById ( " render-button " ) . object!
104
- let concurrency = JSObject . global. document. getElementById ( " concurrency " ) . object!
105
- concurrency. value = JSObject . global. navigator. hardwareConcurrency
106
- let scene = createDemoScene ( )
107
- canvas. width = . number( Double ( scene. width) )
108
- canvas. height = . number( Double ( scene. height) )
109
-
110
- _ = renderButton. addEventListener!( " click " , JSClosure { _ in
129
+ let renderButtonElement = JSObject . global. document. getElementById ( " render-button " ) . object!
130
+ let concurrencyElement = JSObject . global. document. getElementById ( " concurrency " ) . object!
131
+ concurrencyElement. value = JSObject . global. navigator. hardwareConcurrency
132
+
133
+ _ = renderButtonElement. addEventListener!( " click " , JSClosure { _ in
111
134
Task {
112
- let canvas = JSObject . global. document. getElementById ( " canvas " ) . object!
113
- let renderTime = JSObject . global. document. getElementById ( " render-time " ) . object!
114
- let concurrency = JSObject . global. document. getElementById ( " concurrency " ) . object!
115
- let concurrencyValue = max ( Int ( concurrency. value. string!) ?? 1 , 1 )
116
- let ctx = canvas. getContext!( " 2d " ) . object!
117
- let executor = try await WebWorkerTaskExecutor ( numberOfThreads: concurrencyValue)
118
- await render ( scene: scene, ctx: ctx, renderTime: renderTime, concurrency: concurrencyValue, executor: executor)
119
- executor. terminate ( )
120
- print ( " Render done " )
135
+ try await onClick ( )
121
136
}
122
137
return JSValue . undefined
123
138
} )
@@ -126,3 +141,21 @@ func main() async throws {
126
141
Task {
127
142
try await main ( )
128
143
}
144
+
145
+
146
+ #if canImport(wasi_pthread)
147
+ import wasi_pthread
148
+ import WASILibc
149
+
150
+ /// Trick to avoid blocking the main thread. pthread_mutex_lock function is used by
151
+ /// the Swift concurrency runtime.
152
+ @_cdecl ( " pthread_mutex_lock " )
153
+ func pthread_mutex_lock( _ mutex: UnsafeMutablePointer < pthread_mutex_t > ) -> Int32 {
154
+ // DO NOT BLOCK MAIN THREAD
155
+ var ret : Int32
156
+ repeat {
157
+ ret = pthread_mutex_trylock ( mutex)
158
+ } while ret == EBUSY
159
+ return ret
160
+ }
161
+ #endif
0 commit comments