Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
folium-app
GitHub Repository: folium-app/Folium
Path: blob/a-new-beginning/Folium-iOS/Controllers/EmulationControllers/GrapeController.swift
2 views
//
//  GrapeController.swift
//  Folium-iOS
//
//  Created by Jarrod Norwell on 5/3/2026.
//

/*
 MARK: NOTES
 
 **** Async UIAction ****
 - [ ] Incomplete, rewrite all UIAction instances as .async(...)
 
 */

import CombinedCores
import GameController
import MetalKit
import UIKit

class GrapeController : UIViewController {
    var imageView: MTKView? = nil,
        blurredImageView: MTKView? = nil,
        secondaryImageView: MTKView? = nil,
        secondaryBlurredImageView: MTKView? = nil
    
    var visualEffectView: UIVisualEffectView? = nil
    
    var settingsButton: UIButton? = nil,
        selectButton: UIButton? = nil,
        startButton: UIButton? = nil
    
    var upButton: UIButton? = nil,
        downButton: UIButton? = nil,
        leftButton: UIButton? = nil,
        rightButton: UIButton? = nil
    
    var aButton: UIButton? = nil,
        bButton: UIButton? = nil,
        yButton: UIButton? = nil,
        xButton: UIButton? = nil
    
    var lButton: UIButton? = nil,
        rButton: UIButton? = nil
    
    var portraitConstraints: [NSLayoutConstraint] = [],
        landscapeConstraints: [NSLayoutConstraint] = []
    var portraitBottomConstraint: NSLayoutConstraint? = nil
    var topLandscapeLeadingConstraint: NSLayoutConstraint? = nil,
        topLandscapeTrailingConstraint: NSLayoutConstraint? = nil,
        bottomLandscapeLeadingConstraint: NSLayoutConstraint? = nil,
        bottomLandscapeTrailingConstraint: NSLayoutConstraint? = nil
    var constraints: (portrait: [NSLayoutConstraint], landscape: [NSLayoutConstraint]) = ([], [])
    
    var commandQueue: MTLCommandQueue!
    var pipelineState: MTLRenderPipelineState!
    var samplerState: MTLSamplerState!
    var vertexBuffer: MTLBuffer!
    
    var device: MTLDevice? = nil
    var topTexture: MTLTexture? = nil
    var bottomTexture: MTLTexture? = nil
    
    var game: NewGrapeGame
    var grapeCore: CombinedCores.GrapeCore
    init(_ game: NewGrapeGame, _ grapeCore: CombinedCores.GrapeCore) {
        self.game = game
        self.grapeCore = grapeCore
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .systemBackground
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        device = MTLCreateSystemDefaultDevice()
    
        blurredImageView = MTKView(frame: .zero, device: device)
        guard let blurredImageView else {
            return
        }
        blurredImageView.translatesAutoresizingMaskIntoConstraints = false
        blurredImageView.colorPixelFormat = .bgra8Unorm
        blurredImageView.contentMode = .scaleAspectFill
        blurredImageView.delegate = self
        blurredImageView.framebufferOnly = false
        view.addSubview(blurredImageView)
        
        secondaryBlurredImageView = MTKView(frame: .zero, device: device)
        guard let secondaryBlurredImageView else {
            return
        }
        secondaryBlurredImageView.translatesAutoresizingMaskIntoConstraints = false
        secondaryBlurredImageView.colorPixelFormat = .bgra8Unorm
        secondaryBlurredImageView.contentMode = .scaleAspectFill
        secondaryBlurredImageView.delegate = self
        secondaryBlurredImageView.framebufferOnly = false
        view.addSubview(secondaryBlurredImageView)
        
        visualEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .systemMaterial))
        guard let visualEffectView else {
            return
        }
        visualEffectView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(visualEffectView)
        
        imageView = MTKView(frame: .zero, device: device)
        guard let imageView else {
            return
        }
        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageView.backgroundColor = .secondaryLabel
        if #available(iOS 26, *) {
            imageView.clipsToBounds = true
            imageView.cornerConfiguration = .corners(radius: .fixed(15.0))
        } else {
            imageView.clipsToBounds = true
            imageView.layer.cornerCurve = .continuous
            imageView.layer.cornerRadius = 15.0
        }
        imageView.colorPixelFormat = .bgra8Unorm
        imageView.contentMode = .scaleAspectFill
        imageView.delegate = self
        imageView.framebufferOnly = false
        visualEffectView.contentView.addSubview(imageView)
        
        secondaryImageView = MTKView(frame: .zero, device: device)
        guard let secondaryImageView else {
            return
        }
        secondaryImageView.translatesAutoresizingMaskIntoConstraints = false
        secondaryImageView.backgroundColor = .secondaryLabel
        if #available(iOS 26, *) {
            secondaryImageView.clipsToBounds = true
            secondaryImageView.cornerConfiguration = .corners(radius: .fixed(15.0))
        } else {
            secondaryImageView.clipsToBounds = true
            secondaryImageView.layer.cornerCurve = .continuous
            secondaryImageView.layer.cornerRadius = 15.0
        }
        secondaryImageView.colorPixelFormat = .bgra8Unorm
        secondaryImageView.contentMode = .scaleAspectFill
        secondaryImageView.delegate = self
        secondaryImageView.framebufferOnly = false
        secondaryImageView.isUserInteractionEnabled = true
        visualEffectView.contentView.addSubview(secondaryImageView)
        
        let settingsConfiguration: UIButton.Configuration = .configuration(.medium, .capsule, UIImage(systemName: "ellipsis"), .medium)
        settingsButton = .button(with: settingsConfiguration,
                                 actions: ({ _ in }, { _ in }), UIMenu(children: [
                                    UIMenu(options: .displayInline, children: [
                                        UIDeferredMenuElement.uncached { completion in
                                            Task {
                                                completion([
                                                    UIAction.async(title: "Stop & Exit", image: UIImage(systemName: "stop"), attributes: .destructive) { action in
                                                        await self.grapeCore.stop()
                                                        self.dismiss(animated: true)
                                                    },
                                                    UIAction.async(title: await self.grapeCore.paused ? "Resume" : "Pause",
                                                                   image: UIImage(systemName: await self.grapeCore.paused ? "play" : "pause")) { action in
                                                                       if await self.grapeCore.paused {
                                                                           await self.grapeCore.unpause()
                                                                       } else {
                                                                           await self.grapeCore.pause()
                                                                       }
                                                                   }
                                                ])
                                            }
                                        }
                                    ]),
                                    UIMenu(options: .displayInline, children: [
                                        UIMenu(title: "Delete State", image: UIImage(systemName: "minus.circle"),
                                               options: .destructive, preferredElementSize: .small, children: [
                                                UIDeferredMenuElement.uncached { completion in
                                                    if let grapeDirectory: URL = .grapeDirectory {
                                                        let statesDirectory: URL = grapeDirectory.appending(component: "states")
                                                        let states: [Int] = [0, 1, 2]
                                                        
                                                        completion(states.map { state in
                                                            let url: URL = statesDirectory.appending(component: "\(self.game.details.name)_\(state).state")
                                                            let exists: Bool = FileManager.default.fileExists(atPath: url.path(percentEncoded: false))
                                                            
                                                            return UIAction.throwing(image: UIImage(systemName: "\(state + 1).circle"),
                                                                                     attributes: exists ? .destructive : .disabled) { action in
                                                                try FileManager.default.removeItem(at: url)
                                                            }
                                                        })
                                                    } else {
                                                        completion([])
                                                    }
                                                }
                                        ]),
                                        UIMenu(title: "Save State", image: UIImage(systemName: "arrow.down.circle"),
                                               preferredElementSize: .small, children: [
                                                UIDeferredMenuElement.uncached { completion in
                                                    if let grapeDirectory: URL = .grapeDirectory {
                                                        let statesDirectory: URL = grapeDirectory.appending(component: "states")
                                                        let states: [Int] = [0, 1, 2]
                                                        
                                                        completion(states.map { state in
                                                            let url: URL = statesDirectory.appending(component: "\(self.game.details.name)_\(state).state")
                                                            let exists: Bool = FileManager.default.fileExists(atPath: url.path(percentEncoded: false))
                                                            
                                                            return UIAction.async(image: UIImage(systemName: "\(state + 1).circle")) { action in
                                                                if exists {
                                                                    await self.grapeCore.save(state: url)
                                                                } else {
                                                                    await self.grapeCore.save(state: url)
                                                                }
                                                            }
                                                        })
                                                    } else {
                                                        completion([])
                                                    }
                                                }
                                        ]),
                                        UIMenu(title: "Load State", image: UIImage(systemName: "arrow.up.circle"),
                                               preferredElementSize: .small, children: [
                                                UIDeferredMenuElement.uncached { completion in
                                                    if let grapeDirectory: URL = .grapeDirectory {
                                                        let statesDirectory: URL = grapeDirectory.appending(component: "states")
                                                        let states: [Int] = [0, 1, 2]
                                                        
                                                        completion(states.map { state in
                                                            let url: URL = statesDirectory.appending(component: "\(self.game.details.name)_\(state).state")
                                                            let exists: Bool = FileManager.default.fileExists(atPath: url.path(percentEncoded: false))
                                                            print(state, url.path(), exists)
                                                            
                                                            return UIAction.async(image: UIImage(systemName: "\(state + 1).circle"),
                                                                                  attributes: exists ? [] : .disabled) { action in
                                                                await self.grapeCore.load(state: url)
                                                            }
                                                        })
                                                    } else {
                                                        completion([])
                                                    }
                                                }
                                        ])
                                    ])
                                 ]))
        guard let settingsButton else {
            return
        }
        visualEffectView.contentView.addSubview(settingsButton)
        
        let selectConfiguration: UIButton.Configuration = .configuration(.medium, .capsule, UIImage(systemName: "minus"), .medium)
        selectButton = .button(with: selectConfiguration,
                               actions: ({ _ in
            await self.grapeCore.press(button: .select)
        }, { _ in
            await self.grapeCore.release(button: .select)
        }))
        guard let selectButton else {
            return
        }
        visualEffectView.contentView.addSubview(selectButton)
        
        let startConfiguration: UIButton.Configuration = .configuration(.medium, .capsule, UIImage(systemName: "plus"), .medium)
        startButton = .button(with: startConfiguration,
                              actions: ({ _ in
            await self.grapeCore.press(button: .start)
        }, { _ in
            await self.grapeCore.release(button: .start)
        }))
        guard let startButton else {
            return
        }
        visualEffectView.contentView.addSubview(startButton)
        
        let upConfiguration: UIButton.Configuration = .configuration(.large, .capsule, UIImage(systemName: "arrow.up"))
        upButton = .button(with: upConfiguration,
                           actions: ({ _ in
            await self.grapeCore.press(button: .up)
        }, { _ in
            await self.grapeCore.release(button: .up)
        }))
        guard let upButton else {
            return
        }
        visualEffectView.contentView.addSubview(upButton)
        
        let downConfiguration: UIButton.Configuration = .configuration(.large, .capsule, UIImage(systemName: "arrow.down"))
        downButton = .button(with: downConfiguration,
                             actions: ({ _ in
            await self.grapeCore.press(button: .down)
        }, { _ in
            await self.grapeCore.release(button: .down)
        }))
        guard let downButton else {
            return
        }
        visualEffectView.contentView.addSubview(downButton)
        
        let leftConfiguration: UIButton.Configuration = .configuration(.large, .capsule, UIImage(systemName: "arrow.left"))
        leftButton = .button(with: leftConfiguration,
                             actions: ({ _ in
            await self.grapeCore.press(button: .left)
        }, { _ in
            await self.grapeCore.release(button: .left)
        }))
        guard let leftButton else {
            return
        }
        visualEffectView.contentView.addSubview(leftButton)
        
        let rightConfiguration: UIButton.Configuration = .configuration(.large, .capsule, UIImage(systemName: "arrow.right"))
        rightButton = .button(with: rightConfiguration,
                              actions: ({ _ in
            await self.grapeCore.press(button: .right)
        }, { _ in
            await self.grapeCore.release(button: .right)
        }))
        guard let rightButton else {
            return
        }
        visualEffectView.contentView.addSubview(rightButton)
        
        let aConfiguration: UIButton.Configuration = .configuration(.large, .capsule, UIImage(systemName: "a.circle"))
        aButton = .button(with: aConfiguration,
                              actions: ({ _ in
            await self.grapeCore.press(button: .a)
        }, { _ in
            await self.grapeCore.release(button: .a)
        }))
        guard let aButton else {
            return
        }
        visualEffectView.contentView.addSubview(aButton)
        
        let bConfiguration: UIButton.Configuration = .configuration(.large, .capsule, UIImage(systemName: "b.circle"))
        bButton = .button(with: bConfiguration,
                               actions: ({ _ in
            await self.grapeCore.press(button: .b)
        }, { _ in
            await self.grapeCore.release(button: .b)
        }))
        guard let bButton else {
            return
        }
        visualEffectView.contentView.addSubview(bButton)
        
        let yConfiguration: UIButton.Configuration = .configuration(.large, .capsule, UIImage(systemName: "y.circle"))
        yButton = .button(with: yConfiguration,
                                 actions: ({ _ in
            await self.grapeCore.press(button: .y)
        }, { _ in
            await self.grapeCore.release(button: .y)
        }))
        guard let yButton else {
            return
        }
        visualEffectView.contentView.addSubview(yButton)
        
        let xConfiguration: UIButton.Configuration = .configuration(.large, .capsule, UIImage(systemName: "x.circle"))
        xButton = .button(with: xConfiguration,
                               actions: ({ _ in
            await self.grapeCore.press(button: .x)
        }, { _ in
            await self.grapeCore.release(button: .x)
        }))
        guard let xButton else {
            return
        }
        visualEffectView.contentView.addSubview(xButton)
        
        let lConfiguration: UIButton.Configuration = .configuration(.large, .capsule, UIImage(systemName: "left"))
        lButton = .button(with: lConfiguration,
                           actions: ({ _ in
            await self.grapeCore.press(button: .l)
        }, { _ in
            await self.grapeCore.release(button: .l)
        }))
        guard let lButton else {
            return
        }
        visualEffectView.contentView.addSubview(lButton)
        
        let rConfiguration: UIButton.Configuration = .configuration(.large, .capsule, UIImage(systemName: "right"))
        rButton = .button(with: rConfiguration,
                           actions: ({ _ in
            await self.grapeCore.press(button: .r)
        }, { _ in
            await self.grapeCore.release(button: .r)
        }))
        guard let rButton else {
            return
        }
        visualEffectView.contentView.addSubview(rButton)
        
        portraitBottomConstraint = secondaryBlurredImageView.bottomAnchor.constraint(equalTo: secondaryImageView.bottomAnchor)
        guard let portraitBottomConstraint else {
            return
        }
        
        if iPhone {
            constraints.portrait.append(contentsOf: [
                // blurred
                blurredImageView.topAnchor.constraint(equalTo: view.topAnchor),
                blurredImageView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
                blurredImageView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
                blurredImageView.heightAnchor.constraint(equalTo: blurredImageView.safeAreaLayoutGuide.widthAnchor, multiplier: 3 / 4),
                
                secondaryBlurredImageView.topAnchor.constraint(equalTo: blurredImageView.safeAreaLayoutGuide.bottomAnchor),
                secondaryBlurredImageView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
                secondaryBlurredImageView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
                secondaryBlurredImageView.heightAnchor.constraint(equalTo: secondaryBlurredImageView.safeAreaLayoutGuide.widthAnchor, multiplier: 3 / 4),
                
                visualEffectView.topAnchor.constraint(equalTo: view.topAnchor),
                visualEffectView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
                visualEffectView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
                visualEffectView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
                
                // unblurred
                imageView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
                imageView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 20),
                imageView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -20),
                imageView.heightAnchor.constraint(equalTo: imageView.safeAreaLayoutGuide.widthAnchor, multiplier: 3 / 4),
                
                secondaryImageView.topAnchor.constraint(equalTo: imageView.safeAreaLayoutGuide.bottomAnchor, constant: 20),
                secondaryImageView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 40),
                secondaryImageView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -40),
                secondaryImageView.heightAnchor.constraint(equalTo: secondaryImageView.safeAreaLayoutGuide.widthAnchor, multiplier: 3 / 4),
                
                settingsButton.bottomAnchor.constraint(equalTo: visualEffectView.contentView.bottomAnchor,
                                                       constant: -20.0),
                settingsButton.centerXAnchor.constraint(equalTo: visualEffectView.contentView.safeAreaLayoutGuide.centerXAnchor),
                
                selectButton.bottomAnchor.constraint(equalTo: visualEffectView.contentView.bottomAnchor,
                                                     constant: -20.0),
                selectButton.trailingAnchor.constraint(equalTo: settingsButton.safeAreaLayoutGuide.leadingAnchor,
                                                       constant: -20.0),
                
                startButton.bottomAnchor.constraint(equalTo: visualEffectView.contentView.bottomAnchor,
                                                    constant: -20.0),
                startButton.leadingAnchor.constraint(equalTo: settingsButton.safeAreaLayoutGuide.trailingAnchor,
                                                     constant: 20.0),
                
                bButton.bottomAnchor.constraint(equalTo: startButton.safeAreaLayoutGuide.topAnchor,
                                                constant: -20.0),
                bButton.trailingAnchor.constraint(equalTo: aButton.safeAreaLayoutGuide.leadingAnchor),
                
                aButton.bottomAnchor.constraint(equalTo: bButton.safeAreaLayoutGuide.topAnchor),
                aButton.trailingAnchor.constraint(equalTo: visualEffectView.contentView.safeAreaLayoutGuide.trailingAnchor,
                                                  constant: -20.0),
                
                yButton.bottomAnchor.constraint(equalTo: bButton.safeAreaLayoutGuide.topAnchor),
                yButton.trailingAnchor.constraint(equalTo: bButton.safeAreaLayoutGuide.leadingAnchor),
                
                xButton.bottomAnchor.constraint(equalTo: yButton.safeAreaLayoutGuide.topAnchor),
                xButton.leadingAnchor.constraint(equalTo: yButton.safeAreaLayoutGuide.trailingAnchor),
                
                upButton.leadingAnchor.constraint(equalTo: leftButton.safeAreaLayoutGuide.trailingAnchor),
                upButton.bottomAnchor.constraint(equalTo: leftButton.safeAreaLayoutGuide.topAnchor),
                
                leftButton.leadingAnchor.constraint(equalTo: visualEffectView.contentView.safeAreaLayoutGuide.leadingAnchor,
                                                    constant: 20.0),
                leftButton.bottomAnchor.constraint(equalTo: downButton.safeAreaLayoutGuide.topAnchor),
                
                downButton.leadingAnchor.constraint(equalTo: leftButton.safeAreaLayoutGuide.trailingAnchor),
                downButton.bottomAnchor.constraint(equalTo: selectButton.safeAreaLayoutGuide.topAnchor,
                                                   constant: -20.0),
                
                rightButton.leadingAnchor.constraint(equalTo: downButton.safeAreaLayoutGuide.trailingAnchor),
                rightButton.bottomAnchor.constraint(equalTo: downButton.safeAreaLayoutGuide.topAnchor),
                
                lButton.leadingAnchor.constraint(equalTo: visualEffectView.contentView.safeAreaLayoutGuide.leadingAnchor,
                                                 constant: 20.0),
                lButton.bottomAnchor.constraint(equalTo: upButton.safeAreaLayoutGuide.topAnchor,
                                                constant: -20.0),
                
                rButton.trailingAnchor.constraint(equalTo: visualEffectView.contentView.safeAreaLayoutGuide.trailingAnchor,
                                                  constant: -20.0),
                rButton.bottomAnchor.constraint(equalTo: upButton.safeAreaLayoutGuide.topAnchor,
                                                constant: -20.0)
            ])
            
            constraints.landscape.append(contentsOf: [
                // blurred
                
                visualEffectView.topAnchor.constraint(equalTo: view.topAnchor),
                visualEffectView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
                visualEffectView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
                visualEffectView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
                
                // unblurred
                
                startButton.bottomAnchor.constraint(equalTo: visualEffectView.contentView.bottomAnchor,
                                                    constant: -20.0),
                startButton.leadingAnchor.constraint(equalTo: imageView.safeAreaLayoutGuide.trailingAnchor,
                                                     constant: 20.0),
                
                selectButton.bottomAnchor.constraint(equalTo: visualEffectView.contentView.bottomAnchor,
                                                     constant: -20.0),
                selectButton.trailingAnchor.constraint(equalTo: imageView.safeAreaLayoutGuide.leadingAnchor,
                                                       constant: -20.0),
                
                settingsButton.bottomAnchor.constraint(equalTo: visualEffectView.contentView.bottomAnchor,
                                                       constant: -20.0),
                settingsButton.leadingAnchor.constraint(equalTo: startButton.safeAreaLayoutGuide.trailingAnchor,
                                                        constant: 20.0),
                
                imageView.topAnchor.constraint(equalTo: visualEffectView.contentView.safeAreaLayoutGuide.topAnchor,
                                               constant: 20.0),
                imageView.bottomAnchor.constraint(equalTo: visualEffectView.contentView.bottomAnchor,
                                                  constant: -20.0),
                imageView.centerXAnchor.constraint(equalTo: visualEffectView.contentView.safeAreaLayoutGuide.centerXAnchor),
                imageView.widthAnchor.constraint(equalTo: imageView.safeAreaLayoutGuide.heightAnchor,
                                                 multiplier: 8.0 / 7.0),
                
                bButton.bottomAnchor.constraint(equalTo: startButton.safeAreaLayoutGuide.topAnchor,
                                                constant: -20.0),
                bButton.trailingAnchor.constraint(equalTo: aButton.safeAreaLayoutGuide.leadingAnchor),
                
                aButton.bottomAnchor.constraint(equalTo: bButton.safeAreaLayoutGuide.topAnchor),
                aButton.trailingAnchor.constraint(equalTo: visualEffectView.contentView.safeAreaLayoutGuide.trailingAnchor,
                                                  constant: -20.0),
                
                yButton.bottomAnchor.constraint(equalTo: bButton.safeAreaLayoutGuide.topAnchor),
                yButton.trailingAnchor.constraint(equalTo: bButton.safeAreaLayoutGuide.leadingAnchor),
                
                xButton.bottomAnchor.constraint(equalTo: yButton.safeAreaLayoutGuide.topAnchor),
                xButton.leadingAnchor.constraint(equalTo: yButton.safeAreaLayoutGuide.trailingAnchor),
                
                upButton.leadingAnchor.constraint(equalTo: leftButton.safeAreaLayoutGuide.trailingAnchor),
                upButton.bottomAnchor.constraint(equalTo: leftButton.safeAreaLayoutGuide.topAnchor),
                
                leftButton.leadingAnchor.constraint(equalTo: visualEffectView.contentView.safeAreaLayoutGuide.leadingAnchor,
                                                    constant: 20.0),
                leftButton.bottomAnchor.constraint(equalTo: downButton.safeAreaLayoutGuide.topAnchor),
                
                downButton.leadingAnchor.constraint(equalTo: leftButton.safeAreaLayoutGuide.trailingAnchor),
                downButton.bottomAnchor.constraint(equalTo: selectButton.safeAreaLayoutGuide.topAnchor,
                                                   constant: -20.0),
                
                rightButton.leadingAnchor.constraint(equalTo: downButton.safeAreaLayoutGuide.trailingAnchor),
                rightButton.bottomAnchor.constraint(equalTo: downButton.safeAreaLayoutGuide.topAnchor),
                
                lButton.leadingAnchor.constraint(equalTo: visualEffectView.contentView.safeAreaLayoutGuide.leadingAnchor,
                                                 constant: 20.0),
                lButton.bottomAnchor.constraint(equalTo: upButton.safeAreaLayoutGuide.topAnchor,
                                                constant: -20.0),
                
                rButton.trailingAnchor.constraint(equalTo: visualEffectView.contentView.safeAreaLayoutGuide.trailingAnchor,
                                                  constant: -20.0),
                rButton.bottomAnchor.constraint(equalTo: upButton.safeAreaLayoutGuide.topAnchor,
                                                constant: -20.0)
            ])
        } else {
            constraints.portrait.append(contentsOf: [
                blurredImageView.topAnchor.constraint(equalTo: view.topAnchor),
                blurredImageView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
                blurredImageView.bottomAnchor.constraint(equalTo: imageView.safeAreaLayoutGuide.bottomAnchor,
                                                         constant: 20),
                blurredImageView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
                
                secondaryBlurredImageView.topAnchor.constraint(equalTo: blurredImageView.safeAreaLayoutGuide.bottomAnchor),
                secondaryBlurredImageView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
                secondaryBlurredImageView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
                
                visualEffectView.topAnchor.constraint(equalTo: view.topAnchor),
                visualEffectView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
                visualEffectView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
                visualEffectView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
                
                imageView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
                imageView.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor),
                imageView.widthAnchor.constraint(equalTo: view.safeAreaLayoutGuide.widthAnchor, multiplier: 4 / 5),
                imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor, multiplier: 3 / 4),
                
                secondaryImageView.topAnchor.constraint(equalTo: imageView.safeAreaLayoutGuide.bottomAnchor,
                                                        constant: 20),
                secondaryImageView.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor),
                secondaryImageView.widthAnchor.constraint(equalTo: view.safeAreaLayoutGuide.widthAnchor, multiplier: 4 / 5, constant: -80),
                secondaryImageView.heightAnchor.constraint(equalTo: secondaryImageView.widthAnchor, multiplier: 3 / 4),
                
                settingsButton.bottomAnchor.constraint(equalTo: visualEffectView.contentView.bottomAnchor,
                                                       constant: -20.0),
                settingsButton.centerXAnchor.constraint(equalTo: visualEffectView.contentView.safeAreaLayoutGuide.centerXAnchor),
                
                selectButton.bottomAnchor.constraint(equalTo: visualEffectView.contentView.bottomAnchor,
                                                     constant: -20.0),
                selectButton.trailingAnchor.constraint(equalTo: settingsButton.safeAreaLayoutGuide.leadingAnchor,
                                                       constant: -20.0),
                
                startButton.bottomAnchor.constraint(equalTo: visualEffectView.contentView.bottomAnchor,
                                                    constant: -20.0),
                startButton.leadingAnchor.constraint(equalTo: settingsButton.safeAreaLayoutGuide.trailingAnchor,
                                                     constant: 20.0),
                
                bButton.bottomAnchor.constraint(equalTo: startButton.safeAreaLayoutGuide.topAnchor,
                                                constant: -20.0),
                bButton.trailingAnchor.constraint(equalTo: aButton.safeAreaLayoutGuide.leadingAnchor),
                
                aButton.bottomAnchor.constraint(equalTo: bButton.safeAreaLayoutGuide.topAnchor),
                aButton.trailingAnchor.constraint(equalTo: visualEffectView.contentView.safeAreaLayoutGuide.trailingAnchor,
                                                  constant: -20.0),
                
                yButton.bottomAnchor.constraint(equalTo: bButton.safeAreaLayoutGuide.topAnchor),
                yButton.trailingAnchor.constraint(equalTo: bButton.safeAreaLayoutGuide.leadingAnchor),
                
                xButton.bottomAnchor.constraint(equalTo: yButton.safeAreaLayoutGuide.topAnchor),
                xButton.leadingAnchor.constraint(equalTo: yButton.safeAreaLayoutGuide.trailingAnchor),
                
                upButton.leadingAnchor.constraint(equalTo: leftButton.safeAreaLayoutGuide.trailingAnchor),
                upButton.bottomAnchor.constraint(equalTo: leftButton.safeAreaLayoutGuide.topAnchor),
                
                leftButton.leadingAnchor.constraint(equalTo: visualEffectView.contentView.safeAreaLayoutGuide.leadingAnchor,
                                                    constant: 20.0),
                leftButton.bottomAnchor.constraint(equalTo: downButton.safeAreaLayoutGuide.topAnchor),
                
                downButton.leadingAnchor.constraint(equalTo: leftButton.safeAreaLayoutGuide.trailingAnchor),
                downButton.bottomAnchor.constraint(equalTo: selectButton.safeAreaLayoutGuide.topAnchor,
                                                   constant: -20.0),
                
                rightButton.leadingAnchor.constraint(equalTo: downButton.safeAreaLayoutGuide.trailingAnchor),
                rightButton.bottomAnchor.constraint(equalTo: downButton.safeAreaLayoutGuide.topAnchor),
                
                lButton.leadingAnchor.constraint(equalTo: visualEffectView.contentView.safeAreaLayoutGuide.leadingAnchor,
                                                 constant: 20.0),
                lButton.bottomAnchor.constraint(equalTo: upButton.safeAreaLayoutGuide.topAnchor,
                                                constant: -20.0),
                
                rButton.trailingAnchor.constraint(equalTo: visualEffectView.contentView.safeAreaLayoutGuide.trailingAnchor,
                                                  constant: -20.0),
                rButton.bottomAnchor.constraint(equalTo: upButton.safeAreaLayoutGuide.topAnchor,
                                                constant: -20.0),
                
                portraitBottomConstraint
            ])
            
            topLandscapeLeadingConstraint = blurredImageView.leadingAnchor.constraint(equalTo: imageView.safeAreaLayoutGuide.leadingAnchor,
                                                                                      constant: -20)
            topLandscapeTrailingConstraint = blurredImageView.trailingAnchor.constraint(equalTo: imageView.safeAreaLayoutGuide.trailingAnchor,
                                                                                        constant: 20)
            bottomLandscapeLeadingConstraint = secondaryBlurredImageView.leadingAnchor.constraint(equalTo: secondaryImageView.safeAreaLayoutGuide.leadingAnchor,
                                                                                                  constant: -20)
            bottomLandscapeTrailingConstraint = secondaryBlurredImageView.trailingAnchor.constraint(equalTo: secondaryImageView.safeAreaLayoutGuide.trailingAnchor,
                                                                                                    constant: 20)
            
            guard let topLandscapeLeadingConstraint, let topLandscapeTrailingConstraint,
                  let bottomLandscapeLeadingConstraint, let bottomLandscapeTrailingConstraint else {
                return
            }
            
            constraints.landscape.append(contentsOf: [
                blurredImageView.topAnchor.constraint(equalTo: view.topAnchor),
                blurredImageView.bottomAnchor.constraint(equalTo: view.centerYAnchor),
                blurredImageView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
                
                secondaryBlurredImageView.topAnchor.constraint(equalTo: view.centerYAnchor),
                secondaryBlurredImageView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
                secondaryBlurredImageView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
                
                visualEffectView.topAnchor.constraint(equalTo: view.topAnchor),
                visualEffectView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
                visualEffectView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
                visualEffectView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
                
                imageView.topAnchor.constraint(equalTo: view.topAnchor,
                                               constant: 20),
                imageView.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor),
                imageView.bottomAnchor.constraint(equalTo: view.centerYAnchor,
                                                  constant: -10),
                imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor,
                                                 multiplier: 4 / 3),
                
                secondaryImageView.topAnchor.constraint(equalTo: view.centerYAnchor,
                                                        constant: 10),
                secondaryImageView.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor),
                secondaryImageView.bottomAnchor.constraint(equalTo: view.bottomAnchor,
                                                           constant: -20),
                secondaryImageView.widthAnchor.constraint(equalTo: secondaryImageView.heightAnchor,
                                                          multiplier: 4 / 3),
                
                startButton.bottomAnchor.constraint(equalTo: visualEffectView.contentView.bottomAnchor,
                                                    constant: -20.0),
                startButton.leadingAnchor.constraint(equalTo: imageView.safeAreaLayoutGuide.trailingAnchor,
                                                     constant: 20.0),
                
                selectButton.bottomAnchor.constraint(equalTo: visualEffectView.contentView.bottomAnchor,
                                                     constant: -20.0),
                selectButton.trailingAnchor.constraint(equalTo: imageView.safeAreaLayoutGuide.leadingAnchor,
                                                       constant: -20.0),
                
                settingsButton.bottomAnchor.constraint(equalTo: visualEffectView.contentView.bottomAnchor,
                                                       constant: -20.0),
                settingsButton.leadingAnchor.constraint(equalTo: startButton.safeAreaLayoutGuide.trailingAnchor,
                                                        constant: 20.0),
                
                bButton.bottomAnchor.constraint(equalTo: startButton.safeAreaLayoutGuide.topAnchor,
                                                constant: -20.0),
                bButton.trailingAnchor.constraint(equalTo: aButton.safeAreaLayoutGuide.leadingAnchor),
                
                aButton.bottomAnchor.constraint(equalTo: bButton.safeAreaLayoutGuide.topAnchor),
                aButton.trailingAnchor.constraint(equalTo: visualEffectView.contentView.safeAreaLayoutGuide.trailingAnchor,
                                                  constant: -20.0),
                
                yButton.bottomAnchor.constraint(equalTo: bButton.safeAreaLayoutGuide.topAnchor),
                yButton.trailingAnchor.constraint(equalTo: bButton.safeAreaLayoutGuide.leadingAnchor),
                
                xButton.bottomAnchor.constraint(equalTo: yButton.safeAreaLayoutGuide.topAnchor),
                xButton.leadingAnchor.constraint(equalTo: yButton.safeAreaLayoutGuide.trailingAnchor),
                
                upButton.leadingAnchor.constraint(equalTo: leftButton.safeAreaLayoutGuide.trailingAnchor),
                upButton.bottomAnchor.constraint(equalTo: leftButton.safeAreaLayoutGuide.topAnchor),
                
                leftButton.leadingAnchor.constraint(equalTo: visualEffectView.contentView.safeAreaLayoutGuide.leadingAnchor,
                                                    constant: 20.0),
                leftButton.bottomAnchor.constraint(equalTo: downButton.safeAreaLayoutGuide.topAnchor),
                
                downButton.leadingAnchor.constraint(equalTo: leftButton.safeAreaLayoutGuide.trailingAnchor),
                downButton.bottomAnchor.constraint(equalTo: selectButton.safeAreaLayoutGuide.topAnchor,
                                                   constant: -20.0),
                
                rightButton.leadingAnchor.constraint(equalTo: downButton.safeAreaLayoutGuide.trailingAnchor),
                rightButton.bottomAnchor.constraint(equalTo: downButton.safeAreaLayoutGuide.topAnchor),
                
                lButton.leadingAnchor.constraint(equalTo: visualEffectView.contentView.safeAreaLayoutGuide.leadingAnchor,
                                                 constant: 20.0),
                lButton.bottomAnchor.constraint(equalTo: upButton.safeAreaLayoutGuide.topAnchor,
                                                constant: -20.0),
                
                rButton.trailingAnchor.constraint(equalTo: visualEffectView.contentView.safeAreaLayoutGuide.trailingAnchor,
                                                  constant: -20.0),
                rButton.bottomAnchor.constraint(equalTo: upButton.safeAreaLayoutGuide.topAnchor,
                                                constant: -20.0),
                
                topLandscapeLeadingConstraint,
                topLandscapeTrailingConstraint,
                bottomLandscapeLeadingConstraint,
                bottomLandscapeTrailingConstraint
            ])
        }
        
        switch interfaceOrientation() {
        case .portrait:
            view.addConstraints(constraints.portrait)
        case .landscapeLeft, .landscapeRight:
            view.addConstraints(constraints.landscape)
        default:
            break
        }
        
        if let device {
            commandQueue = device.makeCommandQueue()
            guard let library = device.makeDefaultLibrary() else {
                return
            }
            
            let vertexFunction = library.makeFunction(name: "grape_vertex_main")
            let fragmentFunction = library.makeFunction(name: "grape_fragment_main")

            let pipelineDescriptor: MTLRenderPipelineDescriptor = .init()
            pipelineDescriptor.colorAttachments[0].pixelFormat = imageView.colorPixelFormat
            pipelineDescriptor.fragmentFunction = fragmentFunction
            pipelineDescriptor.vertexFunction = vertexFunction

            do {
                pipelineState = try device.makeRenderPipelineState(descriptor: pipelineDescriptor)
            } catch {
                fatalError("Failed to create pipeline state: \(error)")
            }
            
            let vertices: [Float] = [
                -1,  1,   0, 0, // top-left
                -1, -1,   0, 1, // bottom-left
                 1,  1,   1, 0, // top-right
                 1, -1,   1, 1  // bottom-right
            ]
            
            vertexBuffer = device.makeBuffer(bytes: vertices, length: vertices.count * MemoryLayout<Float>.size, options: [])
        }
        /* */
        
        Task {
            guard let device else {
                return
            }
            
            await grapeCore.insert(cartridge: game.details.url)
            
            await grapeCore.videoCallback { primaryBuffer, secondaryBuffer in
                Task { @MainActor in
                    if self.topTexture == nil {
                        let descriptor: MTLTextureDescriptor = .texture2DDescriptor(pixelFormat: .bgra8Unorm,
                                                                                    width: 256,
                                                                                    height: 192,
                                                                                    mipmapped: false)
                        descriptor.storageMode = .shared
                        descriptor.usage = [.shaderRead, .shaderWrite]
                        self.topTexture = device.makeTexture(descriptor: descriptor)
                        self.bottomTexture = device.makeTexture(descriptor: descriptor)
                        
                        let samplerDescriptor = MTLSamplerDescriptor()
                        samplerDescriptor.minFilter = .nearest
                        samplerDescriptor.magFilter = .nearest
                        samplerDescriptor.sAddressMode = .clampToEdge
                        samplerDescriptor.tAddressMode = .clampToEdge
                        self.samplerState = device.makeSamplerState(descriptor: samplerDescriptor)!
                    }
                    
                    let bytesPerRow = 256 * 4
                    
                    let region: MTLRegion = MTLRegionMake2D(0, 0, 256, 192)
                    if let texture = self.topTexture {
                        texture.replace(region: region, mipmapLevel: 0, withBytes: primaryBuffer, bytesPerRow: bytesPerRow)
                    }
                    
                    blurredImageView.setNeedsDisplay()
                    imageView.setNeedsDisplay()
                    
                    if let texture = self.bottomTexture {
                        texture.replace(region: region, mipmapLevel: 0, withBytes: secondaryBuffer, bytesPerRow: bytesPerRow)
                    }
                    
                    secondaryBlurredImageView.setNeedsDisplay()
                    secondaryImageView.setNeedsDisplay()
                }
            }
            
            await grapeCore.start()
        }
        
        Task {
            await GCController.startWirelessControllerDiscovery()
        }
        
        NotificationCenter.default.addObserver(forName: .applicationStateDidChange, object: nil, queue: .current) { notification in
            guard let applicationState: ApplicationState = notification.object as? ApplicationState else {
                return
            }
            
            Task {
                await applicationState.shouldPause ? self.grapeCore.pause() : self.grapeCore.unpause()
            }
        }
        
        NotificationCenter.default.addObserver(forName: .GCControllerDidConnect, object: nil, queue: .current) { notification in
            guard let controller: GCController = notification.object as? GCController,
                let extendedGamepad: GCExtendedGamepad = controller.extendedGamepad else {
                return
            }
            
            visualEffectView.contentView.subviews.filter { subview in
                subview.isKind(of: UIButton.classForCoder()) && subview != settingsButton
            }.forEach { button in
                UIView.animate(withDuration: 1 / 3) {
                    button.alpha = 1 / 3
                }
            }
            
            func input(button: CombinedCores.GrapeCore.GrapeButton, pressed: Bool) {
                Task {
                    if pressed {
                        await self.grapeCore.press(button: button)
                    } else {
                        await self.grapeCore.release(button: button)
                    }
                }
            }
            
            extendedGamepad.buttonA.pressedChangedHandler = { element, value, pressed in
                input(button: .b, pressed: pressed)
            }
            
            extendedGamepad.buttonB.pressedChangedHandler = { element, value, pressed in
                input(button: .a, pressed: pressed)
            }
            
            extendedGamepad.buttonX.pressedChangedHandler = { element, value, pressed in
                input(button: .y, pressed: pressed)
            }
            
            extendedGamepad.buttonY.pressedChangedHandler = { element, value, pressed in
                input(button: .y, pressed: pressed)
            }
            
            extendedGamepad.dpad.up.pressedChangedHandler = { element, value, pressed in
                input(button: .up, pressed: pressed)
            }
            
            extendedGamepad.dpad.down.pressedChangedHandler = { element, value, pressed in
                input(button: .down, pressed: pressed)
            }
            
            extendedGamepad.dpad.left.pressedChangedHandler = { element, value, pressed in
                input(button: .left, pressed: pressed)
            }
            
            extendedGamepad.dpad.right.pressedChangedHandler = { element, value, pressed in
                input(button: .right, pressed: pressed)
            }
            
            extendedGamepad.leftTrigger.pressedChangedHandler = { element, value, pressed in
                input(button: .l, pressed: pressed)
            }
            
            extendedGamepad.rightTrigger.pressedChangedHandler = { element, value, pressed in
                input(button: .r, pressed: pressed)
            }
            
            if let buttonOptions = extendedGamepad.buttonOptions {
                buttonOptions.pressedChangedHandler = { element, value, pressed in
                    input(button: .select, pressed: pressed)
                }
            }
            
            extendedGamepad.buttonMenu.pressedChangedHandler = { element, value, pressed in
                input(button: .start, pressed: pressed)
            }
        }
        
        NotificationCenter.default.addObserver(forName: .GCControllerDidDisconnect, object: nil, queue: .current) { notification in
            visualEffectView.contentView.subviews.filter { subview in
                subview.isKind(of: UIButton.classForCoder()) && subview != settingsButton
            }.forEach { button in
                UIView.animate(withDuration: 1 / 3) {
                    button.alpha = 1
                }
            }
        }
    }
    
    override var prefersHomeIndicatorAutoHidden: Bool { true }
    override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation { .portrait }
    override var supportedInterfaceOrientations: UIInterfaceOrientationMask { .portrait }
    
    override func viewSafeAreaInsetsDidChange() {
            super.viewSafeAreaInsetsDidChange()
            switch interfaceOrientation() {
            case .portrait:
                guard let portraitBottomConstraint else {
                    return
                }
                
                portraitBottomConstraint.constant = view.safeAreaInsets.top
            case .portraitUpsideDown:
                guard let portraitBottomConstraint else {
                    return
                }
                
                portraitBottomConstraint.constant = view.safeAreaInsets.bottom
            case .landscapeLeft:
                guard let topLandscapeLeadingConstraint, let topLandscapeTrailingConstraint,
                let bottomLandscapeLeadingConstraint, let bottomLandscapeTrailingConstraint else {
                    return
                }
                
                topLandscapeLeadingConstraint.constant = -view.safeAreaInsets.right
                topLandscapeTrailingConstraint.constant = view.safeAreaInsets.right
                bottomLandscapeLeadingConstraint.constant = -view.safeAreaInsets.right
                bottomLandscapeTrailingConstraint.constant = view.safeAreaInsets.right
            case .landscapeRight:
                guard let topLandscapeLeadingConstraint, let topLandscapeTrailingConstraint,
                let bottomLandscapeLeadingConstraint, let bottomLandscapeTrailingConstraint else {
                    return
                }
                
                topLandscapeLeadingConstraint.constant = -view.safeAreaInsets.left
                topLandscapeTrailingConstraint.constant = view.safeAreaInsets.left
                bottomLandscapeLeadingConstraint.constant = -view.safeAreaInsets.left
                bottomLandscapeTrailingConstraint.constant = view.safeAreaInsets.left
            default:
                break
            }
            
            Task {
                view.setNeedsUpdateConstraints()
            }
        }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        GCController.stopWirelessControllerDiscovery()
        NotificationCenter.default.removeObserver(self)
    }
    
    override func viewWillTransition(to size: CGSize, with coordinator: any UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
        coordinator.animate { context in } completion: { context in
            switch self.interfaceOrientation() {
            case .portrait:
                self.view.removeConstraints(self.constraints.landscape)
                self.view.addConstraints(self.constraints.portrait)
            case .landscapeLeft, .landscapeRight:
                self.view.removeConstraints(self.constraints.portrait)
                self.view.addConstraints(self.constraints.landscape)
            default:
                break
            }
            
            self.view.setNeedsUpdateConstraints()
        }
    }
}

extension GrapeController {
    @objc func image(_ image: UIImage, didFinishSavingWithError error: NSError?, contextInfo: UnsafeRawPointer) {
        if #available(iOS 17.5, *), let settingsButton {
            UINotificationFeedbackGenerator(view: settingsButton).notificationOccurred(error == nil ? .success : .error)
        } else {
            UINotificationFeedbackGenerator().notificationOccurred(error == nil ? .success : .error)
        }
    }
}

extension GrapeController {
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)
        guard let secondaryImageView, let touch = touches.first, touch.view == secondaryImageView else {
            return
        }
        
        let locationInView = touch.location(in: secondaryImageView)
        let viewSize = secondaryImageView.frame.size
        
        let mappedX = (locationInView.x / viewSize.width) * 256
        let mappedY = (locationInView.y / viewSize.height) * 192

        let finalPoint = CGPoint(x: mappedX, y: mappedY)
        
        Task {
            await grapeCore.touchBegan(point: finalPoint)
        }
    }
    
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesEnded(touches, with: event)
        Task {
            await grapeCore.touchEnded()
        }
    }
    
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesMoved(touches, with: event)
        guard let secondaryImageView, let touch = touches.first, touch.view == secondaryImageView else {
            return
        }
        
        let locationInView = touch.location(in: secondaryImageView)
        let viewSize = secondaryImageView.frame.size
        
        let mappedX = (locationInView.x / viewSize.width) * 256
        let mappedY = (locationInView.y / viewSize.height) * 192

        let finalPoint = CGPoint(x: mappedX, y: mappedY)
        
        Task {
            await grapeCore.touchMoved(point: finalPoint)
        }
    }
}

extension GrapeController : MTKViewDelegate {
    func draw(in view: MTKView) {
        guard let commandBuffer = commandQueue.makeCommandBuffer(),
              let topTexture = self.topTexture,
              let bottomTexture = self.bottomTexture else { return }

        // --- Second Pass: draw to screen ---
        guard let viewPass = view.currentRenderPassDescriptor,
              let drawable = view.currentDrawable else { return }
        
        func pass(_ texture: MTLTexture) {
            if let encoder = commandBuffer.makeRenderCommandEncoder(descriptor: viewPass) {
                encoder.setRenderPipelineState(pipelineState)
                encoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
                encoder.setFragmentTexture(texture, index: 0)
                encoder.setFragmentSamplerState(samplerState, index: 0)
                encoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4)
                encoder.endEncoding()
            }
        }
        
        if view == imageView || view == blurredImageView {
            pass(topTexture)
        }
        
        if view == secondaryImageView || view == secondaryBlurredImageView {
            pass(bottomTexture)
        }
        
        commandBuffer.present(drawable)
        commandBuffer.commit()
    }
    
    func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {}
}