forked from przemyslawzaworski/Metal-API-Minimal-Example
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDemo.swift
141 lines (127 loc) · 6.14 KB
/
Demo.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// Compile from terminal: swiftc Demo.swift
import Cocoa
import AppKit
import Metal
import MetalKit
import simd
class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate
{
var mainWindow: NSWindow?
func applicationDidFinishLaunching(_ notification: Notification)
{
let windowMask = NSWindow.StyleMask(rawValue: (NSWindow.StyleMask.titled.rawValue | NSWindow.StyleMask.closable.rawValue))
let window = NSWindow(contentRect: NSMakeRect(0, 0, 1280, 720), styleMask: windowMask, backing: NSWindow.BackingStoreType.buffered, defer: true)
window.orderFrontRegardless()
window.title = "Metal API Demo"
window.setFrameOrigin(NSMakePoint(100, 100))
window.backgroundColor = NSColor.black
window.contentViewController = ViewController()
self.mainWindow = window
app.activate(ignoringOtherApps: true)
}
func applicationWillBecomeActive(_ notification: Notification) { }
func applicationShouldTerminateAfterLastWindowClosed(_ app: NSApplication) -> Bool { return true }
public class func makeMenu() -> NSMenu
{
let mainMenu = NSMenu()
let mainAppMenuItem = NSMenuItem(title: "\(ProcessInfo.processInfo.processName)", action: nil, keyEquivalent: "")
mainMenu.addItem(mainAppMenuItem)
let appMenu = NSMenu()
mainAppMenuItem.submenu = appMenu
appMenu.addItem(withTitle: "Quit \(ProcessInfo.processInfo.processName)", action: #selector(NSApplication.terminate(_:)),keyEquivalent: "q")
return mainMenu
}
}
class ViewController: NSViewController
{
var metalView: MTKView?
var metalRenderer: MetalRenderer?
override func loadView() -> ()
{
self.metalView = MTKView()
self.view = self.metalView!
self.metalView?.frame = NSMakeRect(0, 0, 1280, 720)
self.metalView?.layer = CAMetalLayer()
self.metalRenderer = MetalRenderer()
self.metalView?.delegate = self.metalRenderer
self.metalView?.preferredFramesPerSecond = 60
}
override func viewDidLoad() -> ()
{
super.viewDidLoad()
}
}
class MetalRenderer: NSObject, MTKViewDelegate
{
var device: MTLDevice?
var positionBuffer: MTLBuffer?
var uvBuffer: MTLBuffer?
var renderPipeline: MTLRenderPipelineState?
var commandQueue: MTLCommandQueue?
let shaderSource: String =
"using namespace metal;\n" +
"struct Attribute\n" +
"{\n" +
"float4 position [[position]];\n" +
"float2 uv;\n" +
"};\n" +
"vertex Attribute VSMain(constant float4 *position [[buffer(0)]], constant float2 *uv [[buffer(1)]], uint id [[vertex_id]])\n" +
"{\n" +
"Attribute attribute;\n" +
"attribute.position = position[id];\n" +
"attribute.uv = uv[id];\n" +
"return attribute;\n" +
"}\n" +
"fragment float4 PSMain(Attribute attribute [[stage_in]])\n" +
"{\n" +
"return float4(attribute.uv, 0.0, 1.0);\n" +
"}\n";
override init()
{
super.init()
self.device = MTLCreateSystemDefaultDevice()
let positions: [Float] = [-1.0, -1.0, 0.0, 1.0, 1.0, -1.0, 0.0, 1.0, -1.0, 1.0, 0.0, 1.0, 1.0, -1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0, -1.0, 1.0, 0.0, 1.0]
let texcoords: [Float] = [0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0]
self.positionBuffer = self.device?.makeBuffer(bytes:positions,length:(MemoryLayout<Float>.stride * positions.count),options:MTLResourceOptions.storageModeShared)
self.uvBuffer = self.device?.makeBuffer(bytes:texcoords,length:(MemoryLayout<Float>.stride * texcoords.count),options:MTLResourceOptions.storageModeShared)
let metalLibrary = try! self.device?.makeLibrary(source:shaderSource, options:nil)
let vertexFunction = metalLibrary?.makeFunction(name: "VSMain")
let fragmentFunction = metalLibrary?.makeFunction(name: "PSMain")
let renderPipelineDescriptor = MTLRenderPipelineDescriptor()
renderPipelineDescriptor.vertexFunction = vertexFunction
renderPipelineDescriptor.fragmentFunction = fragmentFunction
renderPipelineDescriptor.colorAttachments[0].pixelFormat = MTLPixelFormat.bgra8Unorm
self.renderPipeline = try! self.device?.makeRenderPipelineState(descriptor: renderPipelineDescriptor)
self.commandQueue = self.device?.makeCommandQueue()
}
public func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) -> () { }
public func draw(in view: MTKView) -> ()
{
(view.layer as! CAMetalLayer).device = self.device
if let drawable = (view.layer as! CAMetalLayer).nextDrawable()
{
let frameBufferTexture = drawable.texture
let renderPassDescriptor = MTLRenderPassDescriptor.init()
renderPassDescriptor.colorAttachments[0].texture = frameBufferTexture
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0)
renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreAction.store
renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadAction.clear
let commandBuffer = self.commandQueue?.makeCommandBuffer()
let commandEncoder = commandBuffer?.makeRenderCommandEncoder(descriptor: renderPassDescriptor)
commandEncoder?.setRenderPipelineState(self.renderPipeline!)
commandEncoder?.setVertexBuffer(self.positionBuffer!, offset: 0, index: 0)
commandEncoder?.setVertexBuffer(self.uvBuffer!, offset: 0, index: 1)
commandEncoder?.drawPrimitives(type:MTLPrimitiveType.triangle, vertexStart:0, vertexCount:6, instanceCount:1)
commandEncoder?.endEncoding()
commandBuffer?.present(drawable)
commandBuffer?.commit()
}
}
}
let app = NSApplication.shared
app.setActivationPolicy(NSApplication.ActivationPolicy.regular)
app.mainMenu = AppDelegate.makeMenu()
print(app.mainMenu?.items as Any)
let controller = AppDelegate()
app.delegate = controller
app.run()