@@ -5,7 +5,7 @@ import Metal
5
5
import MetalKit
6
6
7
7
// This must be in sync with the definition in shaders.metal
8
- public struct FragmentConstants {
8
+ struct FragmentConstants {
9
9
public var foregroundColor : SIMD4 < Float >
10
10
public var backgroundColor : SIMD4 < Float >
11
11
public var isFFT : Bool
@@ -17,13 +17,15 @@ public struct FragmentConstants {
17
17
public var padding : Int = 0
18
18
}
19
19
20
- public class FloatPlot : MTKView , MTKViewDelegate {
21
- let waveformTexture : MTLTexture !
20
+ class FloatPlot : NSObject {
21
+ var waveformTexture : MTLTexture ?
22
22
let commandQueue : MTLCommandQueue !
23
23
let pipelineState : MTLRenderPipelineState !
24
- let bufferSampleCount : Int
24
+ var bufferSampleCount : Int
25
25
var dataCallback : ( ) -> [ Float ]
26
26
var constants : FragmentConstants
27
+ let layerRenderPassDescriptor : MTLRenderPassDescriptor
28
+ let device = MTLCreateSystemDefaultDevice ( )
27
29
28
30
public init ( frame frameRect: CGRect ,
29
31
constants: FragmentConstants ,
@@ -32,15 +34,6 @@ public class FloatPlot: MTKView, MTKViewDelegate {
32
34
self . constants = constants
33
35
bufferSampleCount = Int ( frameRect. width)
34
36
35
- let desc = MTLTextureDescriptor ( )
36
- desc. textureType = . type1D
37
- desc. width = Int ( frameRect. width)
38
- desc. pixelFormat = . r32Float
39
- assert ( desc. height == 1 )
40
- assert ( desc. depth == 1 )
41
-
42
- let device = MTLCreateSystemDefaultDevice ( )
43
- waveformTexture = device? . makeTexture ( descriptor: desc)
44
37
commandQueue = device!. makeCommandQueue ( )
45
38
46
39
let library = try ! device? . makeDefaultLibrary ( bundle: Bundle . module)
@@ -51,7 +44,6 @@ public class FloatPlot: MTKView, MTKViewDelegate {
51
44
let pipelineStateDescriptor = MTLRenderPipelineDescriptor ( )
52
45
pipelineStateDescriptor. vertexFunction = vertexProgram
53
46
pipelineStateDescriptor. fragmentFunction = fragmentProgram
54
- pipelineStateDescriptor. sampleCount = 1
55
47
56
48
let colorAttachment = pipelineStateDescriptor. colorAttachments [ 0 ] !
57
49
colorAttachment. pixelFormat = . bgra8Unorm
@@ -63,23 +55,45 @@ public class FloatPlot: MTKView, MTKViewDelegate {
63
55
64
56
pipelineState = try ! device!. makeRenderPipelineState ( descriptor: pipelineStateDescriptor)
65
57
66
- super. init ( frame: frameRect, device: device)
67
-
68
- clearColor = . init( red: 0.0 , green: 0.0 , blue: 0.0 , alpha: 0 )
69
-
70
- delegate = self
58
+ layerRenderPassDescriptor = MTLRenderPassDescriptor ( )
59
+ layerRenderPassDescriptor. colorAttachments [ 0 ] . loadAction = . clear
60
+ layerRenderPassDescriptor. colorAttachments [ 0 ] . storeAction = . store
61
+ layerRenderPassDescriptor. colorAttachments [ 0 ] . clearColor = MTLClearColorMake ( 0 , 0 , 0 , 0 ) ;
71
62
}
72
63
73
64
@available ( * , unavailable)
74
65
required init ( coder _: NSCoder ) {
75
66
fatalError ( " init(coder:) has not been implemented " )
76
67
}
77
68
69
+ func resize( width: Int ) {
70
+
71
+ if width == 0 {
72
+ return
73
+ }
74
+
75
+ let desc = MTLTextureDescriptor ( )
76
+ desc. textureType = . type1D
77
+ desc. width = width
78
+ desc. pixelFormat = . r32Float
79
+ assert ( desc. height == 1 )
80
+ assert ( desc. depth == 1 )
81
+
82
+ waveformTexture = device? . makeTexture ( descriptor: desc)
83
+ bufferSampleCount = width
84
+
85
+ }
86
+
78
87
func updateWaveform( samples: [ Float ] ) {
79
88
if samples. count == 0 {
80
89
return
81
90
}
82
91
92
+ guard let waveformTexture else {
93
+ print ( " ⚠️ updateWaveform: waveformTexture is nil " )
94
+ return
95
+ }
96
+
83
97
var resampled = [ Float] ( repeating: 0 , count: bufferSampleCount)
84
98
85
99
for i in 0 ..< bufferSampleCount {
@@ -97,24 +111,60 @@ public class FloatPlot: MTKView, MTKViewDelegate {
97
111
}
98
112
}
99
113
100
- public func mtkView( _: MTKView , drawableSizeWillChange _: CGSize ) {
101
- // We may want to resize the texture.
114
+ func encode( to commandBuffer: MTLCommandBuffer , pass: MTLRenderPassDescriptor ) {
115
+ guard let encoder = commandBuffer. makeRenderCommandEncoder ( descriptor: pass) else { return }
116
+
117
+ encoder. setRenderPipelineState ( pipelineState)
118
+ encoder. setFragmentTexture ( waveformTexture, index: 0 )
119
+ assert ( MemoryLayout< FragmentConstants> . size == 48 )
120
+ encoder. setFragmentBytes ( & constants, length: MemoryLayout< FragmentConstants> . size, index: 0 )
121
+ encoder. drawPrimitives ( type: . triangleStrip, vertexStart: 0 , vertexCount: 4 )
122
+ encoder. endEncoding ( )
102
123
}
103
124
104
- public func draw( in view: MTKView ) {
125
+ func draw( to layer: CAMetalLayer ) {
126
+
105
127
updateWaveform ( samples: dataCallback ( ) )
128
+
129
+ let size = layer. drawableSize
130
+ let w = Float ( size. width)
131
+ let h = Float ( size. height)
132
+ // let scale = Float(view.contentScaleFactor)
106
133
107
- if let commandBuffer = commandQueue. makeCommandBuffer ( ) {
108
- if let renderPassDescriptor = currentRenderPassDescriptor {
109
- guard let encoder = commandBuffer. makeRenderCommandEncoder ( descriptor: renderPassDescriptor) else { return }
134
+ if w == 0 || h == 0 {
135
+ return
136
+ }
137
+
138
+ guard let commandBuffer = commandQueue. makeCommandBuffer ( ) else {
139
+ return
140
+ }
141
+
142
+ if let currentDrawable = layer. nextDrawable ( ) {
143
+
144
+ layerRenderPassDescriptor. colorAttachments [ 0 ] . texture = currentDrawable. texture
110
145
111
- encoder. setRenderPipelineState ( pipelineState)
112
- encoder. setFragmentTexture ( waveformTexture, index: 0 )
113
- assert ( MemoryLayout< FragmentConstants> . size == 48 )
114
- encoder. setFragmentBytes ( & constants, length: MemoryLayout< FragmentConstants> . size, index: 0 )
115
- encoder. drawPrimitives ( type: . triangleStrip, vertexStart: 0 , vertexCount: 4 )
116
- encoder. endEncoding ( )
146
+ encode ( to: commandBuffer, pass: layerRenderPassDescriptor)
117
147
148
+ commandBuffer. present ( currentDrawable)
149
+ } else {
150
+ print ( " ⚠️ couldn't get drawable " )
151
+ }
152
+ commandBuffer. commit ( )
153
+ }
154
+ }
155
+
156
+ #if !os(visionOS)
157
+ extension FloatPlot : MTKViewDelegate {
158
+ public func mtkView( _ view: MTKView , drawableSizeWillChange size: CGSize ) {
159
+ resize ( width: Int ( size. width) )
160
+ }
161
+
162
+ public func draw( in view: MTKView ) {
163
+ updateWaveform ( samples: dataCallback ( ) )
164
+
165
+ if let commandBuffer = commandQueue. makeCommandBuffer ( ) {
166
+ if let renderPassDescriptor = view. currentRenderPassDescriptor {
167
+ encode ( to: commandBuffer, pass: renderPassDescriptor)
118
168
if let drawable = view. currentDrawable {
119
169
commandBuffer. present ( drawable)
120
170
}
@@ -125,3 +175,38 @@ public class FloatPlot: MTKView, MTKViewDelegate {
125
175
}
126
176
}
127
177
}
178
+ #endif
179
+
180
+ #if !os(visionOS)
181
+ public class FloatPlotCoordinator {
182
+ var renderer : FloatPlot
183
+
184
+ init ( renderer: FloatPlot ) {
185
+ self . renderer = renderer
186
+ }
187
+
188
+ var view : MTKView {
189
+ let view = MTKView ( frame: CGRect ( x: 0 , y: 0 , width: 1024 , height: 1024 ) , device: renderer. device)
190
+ view. clearColor = . init( red: 0.0 , green: 0.0 , blue: 0.0 , alpha: 0 )
191
+ view. delegate = renderer
192
+ return view
193
+ }
194
+ }
195
+ #else
196
+ public class FloatPlotCoordinator {
197
+ var renderer : FloatPlot
198
+
199
+ init ( renderer: FloatPlot ) {
200
+ self . renderer = renderer
201
+ }
202
+
203
+ var view : MetalView {
204
+ let view = MetalView ( frame: CGRect ( x: 0 , y: 0 , width: 1024 , height: 1024 ) )
205
+ view. renderer = renderer
206
+ view. metalLayer. pixelFormat = . bgra8Unorm
207
+ view. metalLayer. isOpaque = false
208
+ view. createDisplayLink ( )
209
+ return view
210
+ }
211
+ }
212
+ #endif
0 commit comments