I'm experimenting with SwiftUI and Metal. I've got 2 windows, one with various lists and controls and the other a Metal window.
I had the slider data updating the Metal window but when I moved the slider the FPS dropped from 60 to around 25. I removed all links between the views and moving the sliders still drops the FPS in the metal window.
It seems that the list views slow down the FPS as well.
I create the metal window on startup using:
metalWindow = NSWindow(contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
backing: .buffered, defer: false)
metalWindow.center()
metalWindow.setFrameAutosaveName("Metal Window")
metalWindow.makeKeyAndOrderFront(nil)
mtkView = MTKView()
mtkView.translatesAutoresizingMaskIntoConstraints = false
metalWindow.contentView!.addSubview(mtkView)
metalWindow.contentView!.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "|[mtkView]|", options: [], metrics: nil, views: ["mtkView" : mtkView!]))
metalWindow.contentView!.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[mtkView]|", options: [], metrics: nil, views: ["mtkView" : mtkView!]))
device = MTLCreateSystemDefaultDevice()!
mtkView.device = device
mtkView.colorPixelFormat = .bgra8Unorm
commandQueue = device.makeCommandQueue()!
mtkView.delegate = self
mtkView.sampleCount = 4
mtkView.clearColor = MTLClearColor(red: 0.0, green: 0.5, blue: 1.0, alpha: 1.0)
mtkView.depthStencilPixelFormat = .depth32Float
The control window is a SwiftUI view:
struct ControlPanelView: View {
@ObservedObject var controlPanel = ControlPanel()
@State private var cameraPos = Floatx3()
@State private var lightingPos = Floatx3()
var body: some View {
HStack{
VStack{
VStack{
Text("Objects")
List(self.controlPanel.objectFiles) { object in
Text(object.name)
}
}
VStack{
Text("Textures")
List(self.controlPanel.textureFiles) { texture in
HStack {
Image(nsImage: texture.image).resizable()
.frame(width: 32, height: 32)
Text(texture.name)
}
}
}
}
VStack{
HStack{
XYZControl(heading: "Camera Controls", xyzPos: $cameraPos)
XYZControl(heading: "Lighting Controls", xyzPos: $lightingPos)
}.padding()
HStack{
Text("Frames Per Second:")
//Text(String(renderer.finalFpsCount))
}
}
}.border(Color.red).padding()
}
}
struct XYZControl: View {
var heading : String
@Binding var xyzPos : Floatx3
var body: some View {
VStack{
Text(heading).padding(.bottom, 5.0)
PositionSlider(sliderValue: $xyzPos.x, label: "X", minimum: -15.0, maximum: 15.0)
PositionSlider(sliderValue: $xyzPos.y, label: "Y", minimum: -15.0, maximum: 15.0)
PositionSlider(sliderValue: $xyzPos.z, label: "Z", minimum: -15.0, maximum: 15.0)
}.border(Color.yellow).padding(.leading)
}
}
struct PositionSlider: View {
@Binding var sliderValue : Float
var label : String
var minimum : Float
var maximum : Float
static let posFormatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.maximumFractionDigits = 3
return formatter
}()
var body: some View {
VStack{
Text(label)
HStack{
Text("\(Self.posFormatter.string(from:NSNumber(value: minimum))!)")
Slider(value: $sliderValue, in: minimum ... maximum)
Text("\(Self.posFormatter.string(from:NSNumber(value: maximum))!)")
}.padding(.horizontal).frame(width: 150.0, height: 15.0, alignment: .leading)
Text("\(Self.posFormatter.string(from:NSNumber(value: sliderValue))!)")
}.border(Color.white)
}
}
Can anyone help with why the frame rate drops?
I removed the metal window and incorporated it into the control window using NSViewRepresentable so it looks like this now:
This still has the same problem.
The render code (not doing much!). It still slows down even when not rendering the teapot.
func draw(in view: MTKView) {
if let commandBuffer = commandQueue.makeCommandBuffer() {
commandBuffer.label = "Frame command buffer"
if let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: view.currentRenderPassDescriptor!) {
renderEncoder.label = "render encoder"
renderEncoder.endEncoding()
}
commandBuffer.present(view.currentDrawable!)
commandBuffer.addCompletedHandler { completedCommandBuffer in
self.computeFPS()
}
commandBuffer.commit()
}
}
As 0xBFE1A8 said SwiftUI was blocking the main thread. I moved the draw function into a callback setup using CVDisplayLink .global(qos: .userInteractive). This stops the rendering slowing down when the sliders are moved.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With