├── Assets
├── SwiftMetal.png
└── SwiftMetal-Bond-Logo-Mini.png
├── .gitignore
├── Resources
├── TestMedia.xcassets
│ ├── Contents.json
│ ├── photo1.imageset
│ │ ├── 5146521371_da07f238ab_o.jpg
│ │ └── Contents.json
│ └── photo2.imageset
│ │ ├── 6145695930_547db05626_o.jpg
│ │ └── Contents.json
├── Headers
│ ├── SwiftMetal.h
│ ├── SwiftMetal_tvOS.h
│ └── SwiftMetal_macOS.h
├── Info.plist
└── Template.metal
├── SwiftMetalDemo
├── Assets.xcassets
│ ├── Contents.json
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
├── ContentView.swift
├── AppDelegate.swift
├── SceneDelegate.swift
├── Base.lproj
│ └── LaunchScreen.storyboard
├── Info.plist
└── Main.swift
├── SwiftMetal.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── xcuserdata
│ ├── anton.xcuserdatad
│ │ ├── xcdebugger
│ │ │ └── Breakpoints_v2.xcbkptlist
│ │ └── xcschemes
│ │ │ └── xcschememanagement.plist
│ └── hexagons.xcuserdatad
│ │ ├── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
├── xcshareddata
│ └── xcschemes
│ │ └── SwiftMetal_iOS.xcscheme
└── project.pbxproj
├── Package.swift
├── SwiftMetalTests
├── Info.plist
└── SwiftMetalTests.swift
├── SwiftMetalTool
└── main.swift
├── SwiftMetal.podspec
├── Sources
├── SMCode.swift
├── Types
│ ├── SMValue.swift
│ ├── SMEntity.swift
│ ├── SMBool.swift
│ ├── SMFloat.swift
│ ├── SMVector.swift
│ └── SMTexture.swift
├── SMFunction.swift
├── Other
│ └── Float16.swift
├── Types.swift
├── SMShader.swift
├── SMView.swift
├── SMBuilder.swift
└── SMRenderer.swift
├── LICENSE
├── ExampleShaders
├── Photos.swift
├── Camera.swift
└── Logo.swift
└── README.md
/Assets/SwiftMetal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heestand-xyz/SwiftMetal/HEAD/Assets/SwiftMetal.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 |
3 | .build/*
4 | .swiftpm/*
5 |
6 | *.xcodeproj/project.xcworkspace/xcuserdata/*
--------------------------------------------------------------------------------
/Resources/TestMedia.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/SwiftMetalDemo/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Assets/SwiftMetal-Bond-Logo-Mini.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heestand-xyz/SwiftMetal/HEAD/Assets/SwiftMetal-Bond-Logo-Mini.png
--------------------------------------------------------------------------------
/SwiftMetalDemo/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Resources/TestMedia.xcassets/photo1.imageset/5146521371_da07f238ab_o.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heestand-xyz/SwiftMetal/HEAD/Resources/TestMedia.xcassets/photo1.imageset/5146521371_da07f238ab_o.jpg
--------------------------------------------------------------------------------
/Resources/TestMedia.xcassets/photo2.imageset/6145695930_547db05626_o.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heestand-xyz/SwiftMetal/HEAD/Resources/TestMedia.xcassets/photo2.imageset/6145695930_547db05626_o.jpg
--------------------------------------------------------------------------------
/SwiftMetal.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SwiftMetal.xcodeproj/xcuserdata/anton.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/SwiftMetal.xcodeproj/xcuserdata/hexagons.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/SwiftMetal.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Resources/TestMedia.xcassets/photo1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "5146521371_da07f238ab_o.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Resources/TestMedia.xcassets/photo2.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "6145695930_547db05626_o.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.1
2 | import PackageDescription
3 |
4 | let package = Package(
5 | name: "SwiftMetal",
6 | platforms: [
7 | .iOS(.v13),
8 | .macOS(.v10_15),
9 | .tvOS(.v13),
10 | ],
11 | products: [
12 | .library(name: "SwiftMetal", targets: ["SwiftMetal"]),
13 | ],
14 | targets: [
15 | .target(name: "SwiftMetal", path: "Sources"),
16 | .testTarget(name: "SwiftMetalTests", dependencies: ["SwiftMetal"], path: "SwiftMetalTests"),
17 | ]
18 | )
19 |
--------------------------------------------------------------------------------
/Resources/Headers/SwiftMetal.h:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftMetal.h
3 | // SwiftMetal
4 | //
5 | // Created by Anton Heestand on 2019-12-06.
6 | // Copyright © 2019 Hexagons. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for SwiftMetal.
12 | FOUNDATION_EXPORT double SwiftMetalVersionNumber;
13 |
14 | //! Project version string for SwiftMetal.
15 | FOUNDATION_EXPORT const unsigned char SwiftMetalVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Resources/Headers/SwiftMetal_tvOS.h:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftMetal_tvOS.h
3 | // SwiftMetal_tvOS
4 | //
5 | // Created by Hexagons on 2019-12-09.
6 | // Copyright © 2019 Hexagons. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for SwiftMetal_tvOS.
12 | FOUNDATION_EXPORT double SwiftMetal_tvOSVersionNumber;
13 |
14 | //! Project version string for SwiftMetal_tvOS.
15 | FOUNDATION_EXPORT const unsigned char SwiftMetal_tvOSVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Resources/Headers/SwiftMetal_macOS.h:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftMetal_macOS.h
3 | // SwiftMetal_macOS
4 | //
5 | // Created by Hexagons on 2019-12-09.
6 | // Copyright © 2019 Hexagons. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for SwiftMetal_macOS.
12 | FOUNDATION_EXPORT double SwiftMetal_macOSVersionNumber;
13 |
14 | //! Project version string for SwiftMetal_macOS.
15 | FOUNDATION_EXPORT const unsigned char SwiftMetal_macOSVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/SwiftMetalDemo/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // SwiftMetalDemo
4 | //
5 | // Created by Anton Heestand on 2019-12-09.
6 | // Copyright © 2019 Hexagons. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import SwiftMetal
11 |
12 | struct ContentView: View {
13 | // @EnvironmentObject var main: Main
14 | var body: some View {
15 | SMView {
16 | SMShader { uv in
17 | let uv4: SMFloat4 = float4(uv.x, uv.y, 0.0, 1.0)
18 | let c: SMFloat4 = max(uv4, float4(0.5))
19 | return c
20 | }
21 | }
22 | }
23 | }
24 |
25 | struct ContentView_Previews: PreviewProvider {
26 | static var previews: some View {
27 | ContentView()
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/SwiftMetalTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/SwiftMetalTool/main.swift:
--------------------------------------------------------------------------------
1 | //
2 | // main.swift
3 | // SwiftMetalTool
4 | //
5 | // Created by Hexagons on 2019-12-09.
6 | // Copyright © 2019 Hexagons. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 | import SwiftMetal
11 |
12 | print("SwiftMetal")
13 |
14 | let shader = SMShader { uv in
15 | float4(uv.x, uv.y, 0.0, 1.0)
16 | }
17 |
18 | print("Render...")
19 |
20 | let res = CGSize(width: 4096, height: 4096)
21 | let texture = try! SMRenderer.render(shader, at: res, as: .rgba32Float)
22 | let image = try! texture.image()
23 |
24 | print("Rendered!")
25 |
26 | let desktopURL = FileManager.default.urls(for: .desktopDirectory, in: .userDomainMask).first!
27 | let fileURL = desktopURL.appendingPathComponent("swift-metal-render-32.tiff")
28 | let data: Data = image.tiffRepresentation!
29 | try! data.write(to: fileURL, options: .atomic)
30 | //let pngData = NSBitmapImageRep(data: data)!.representation(using: .png, properties: [:])!
31 |
32 | print("Saved!")
33 |
--------------------------------------------------------------------------------
/Resources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | $(MARKETING_VERSION)
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 | NSHumanReadableCopyright
22 | Copyright © 2019 Hexagons. All rights reserved.
23 |
24 |
25 |
--------------------------------------------------------------------------------
/SwiftMetal.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |spec|
2 |
3 | spec.name = "SwiftMetal"
4 | spec.version = "0.1.3"
5 |
6 | spec.summary = "Write Metal in Swift"
7 | spec.description = <<-DESC
8 | Write Metal in Swift
9 | Auto generated Metal code
10 | DESC
11 |
12 | spec.homepage = "http://hexagons.se"
13 |
14 | spec.license = { :type => "MIT", :file => "LICENSE" }
15 |
16 | spec.author = { "Hexagons" => "anton@hexagons.se" }
17 | spec.social_media_url = "https://twitter.com/anton_hexagons"
18 |
19 | spec.ios.deployment_target = "13.0"
20 | spec.osx.deployment_target = "10.15"
21 | spec.tvos.deployment_target = "13.0"
22 |
23 | spec.swift_version = '5.0'
24 |
25 | spec.source = { :git => "https://github.com/hexagons/SwiftMetal.git", :branch => "master", :tag => "#{spec.version}" }
26 |
27 | spec.source_files = "Sources", "Sources/**/*.swift"
28 |
29 | end
30 |
--------------------------------------------------------------------------------
/SwiftMetalDemo/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // SwiftMetalDemo
4 | //
5 | // Created by Anton Heestand on 2019-12-09.
6 | // Copyright © 2019 Hexagons. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
15 | return true
16 | }
17 |
18 | // MARK: UISceneSession Lifecycle
19 |
20 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
21 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
22 | }
23 |
24 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {}
25 |
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/Sources/SMCode.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SMCode.swift
3 | // SwiftMetal
4 | //
5 | // Created by Anton Heestand on 2019-12-06.
6 | // Copyright © 2019 Hexagons. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct SMCode {
12 |
13 | var snippet: String
14 | var uniforms: [SMUniformPack]
15 | var variables: [SMVariablePack]
16 | var functions: [SMFunction]
17 |
18 | init(_ snippet: String, uniforms: [SMUniformPack], variables: [SMVariablePack], functions: [SMFunction]) {
19 | self.snippet = snippet
20 | self.uniforms = uniforms
21 | self.variables = variables
22 | self.functions = functions
23 | }
24 |
25 | // init(_ snippet: String) {
26 | // self.snippet = snippet
27 | // uniforms = []
28 | // variables = []
29 | // functions = []
30 | // }
31 |
32 | // init() {
33 | // snippet = ""
34 | // uniforms = []
35 | // variables = []
36 | // functions = []
37 | // }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/SwiftMetal.xcodeproj/xcuserdata/hexagons.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | SwiftMetalDemo.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 3
11 |
12 | SwiftMetalTool.xcscheme_^#shared#^_
13 |
14 | orderHint
15 | 4
16 |
17 | SwiftMetal_iOS.xcscheme_^#shared#^_
18 |
19 | orderHint
20 | 0
21 |
22 | SwiftMetal_macOS.xcscheme_^#shared#^_
23 |
24 | orderHint
25 | 1
26 |
27 | SwiftMetal_tvOS.xcscheme_^#shared#^_
28 |
29 | orderHint
30 | 2
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 ⬢ Hexagons
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/SwiftMetal.xcodeproj/xcuserdata/anton.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | SwiftMetal.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 | SwiftMetalDemo.xcscheme_^#shared#^_
13 |
14 | orderHint
15 | 3
16 |
17 | SwiftMetalTool.xcscheme_^#shared#^_
18 |
19 | orderHint
20 | 4
21 |
22 | SwiftMetal_iOS.xcscheme_^#shared#^_
23 |
24 | orderHint
25 | 0
26 |
27 | SwiftMetal_macOS.xcscheme_^#shared#^_
28 |
29 | orderHint
30 | 1
31 |
32 | SwiftMetal_tvOS.xcscheme_^#shared#^_
33 |
34 | orderHint
35 | 2
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/Sources/Types/SMValue.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SMValue.swift
3 | // SwiftMetal
4 | //
5 | // Created by Anton Heestand on 2019-12-06.
6 | // Copyright © 2019 Hexagons. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | //public protocol SMRawValue {
12 | // static var typeName: String { get }
13 | //}
14 |
15 | public class SMValue: SMEntity {
16 |
17 | public typealias FV = () -> (V)
18 |
19 | var futureValue: FV?
20 | var _value: V?
21 | public var value: V? { _value ?? futureValue?() }
22 |
23 | init(_ value: V, type: String, fromEntities: [SMEntity] = []) {
24 | self._value = value
25 | super.init(type: type, fromEntities: fromEntities)
26 | }
27 |
28 | init(_ futureValue: @escaping FV, type: String) {
29 | self.futureValue = futureValue
30 | super.init(type: type, isFuture: true)
31 | }
32 |
33 | init(operation: SMOperation, snippet: @escaping () -> (String), type: String) {
34 | super.init(type: type, operation: operation)
35 | self.snippet = snippet
36 | }
37 |
38 | init(type: String, fromEntities: [SMEntity] = []) {
39 | super.init(type: type, fromEntities: fromEntities)
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/SwiftMetalDemo/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // SwiftMetalDemo
4 | //
5 | // Created by Anton Heestand on 2019-12-09.
6 | // Copyright © 2019 Hexagons. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SwiftUI
11 |
12 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
13 |
14 | var window: UIWindow?
15 |
16 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
17 |
18 | // let main = Main()
19 |
20 | let contentView = ContentView()
21 | // .environmentObject(main)
22 |
23 | if let windowScene = scene as? UIWindowScene {
24 | let window = UIWindow(windowScene: windowScene)
25 | window.rootViewController = UIHostingController(rootView: contentView)
26 | self.window = window
27 | window.makeKeyAndVisible()
28 | }
29 |
30 | }
31 |
32 | func sceneDidDisconnect(_ scene: UIScene) {}
33 |
34 | func sceneDidBecomeActive(_ scene: UIScene) {}
35 | func sceneWillResignActive(_ scene: UIScene) {}
36 |
37 | func sceneWillEnterForeground(_ scene: UIScene) {}
38 | func sceneDidEnterBackground(_ scene: UIScene) {}
39 |
40 | }
41 |
42 |
--------------------------------------------------------------------------------
/ExampleShaders/Photos.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Photos.swift
3 | // SwiftMetal_iOS
4 | //
5 | // Created by Anton Heestand on 2019-12-12.
6 | // Copyright © 2019 Hexagons. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import SwiftMetal
11 |
12 | let photosShader: SMShader = SMShader { uv in
13 | let photo1 = SMTexture(image: UIImage(named: "photo1")!)!
14 | let photo2 = SMTexture(image: UIImage(named: "photo2")!)!
15 | let mask: SMBool = photo2.r < 0.1
16 | return mask > photo1 <=> photo2
17 | }
18 |
19 | struct PhotosView: View {
20 | var body: some View {
21 | ZStack {
22 | Color.black
23 | .edgesIgnoringSafeArea(.all)
24 | VStack(spacing: 0) {
25 | HStack(spacing: 0) {
26 | Image("photo1")
27 | .resizable()
28 | .aspectRatio(1.5, contentMode: .fit)
29 | Image("photo2")
30 | .resizable()
31 | .aspectRatio(1.5, contentMode: .fit)
32 | }
33 | SMView { photosShader }
34 | .aspectRatio(1.5, contentMode: .fit)
35 | }
36 | }
37 | }
38 | }
39 |
40 | struct PhotosView_Previews: PreviewProvider {
41 | static var previews: some View {
42 | PhotosView()
43 | .previewLayout(.fixed(width: 750, height: 750))
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Resources/Template.metal:
--------------------------------------------------------------------------------
1 | //
2 | // Template.metal
3 | // SwiftMetal
4 | //
5 | // Created by Anton Heestand on 2019-12-06.
6 | // Copyright © 2019 Hexagons. All rights reserved.
7 | //
8 |
9 | #include
10 | using namespace metal;
11 |
12 | float4 f0(float4 input) {
13 | return input + input;
14 | }
15 |
16 | struct Uniforms {
17 | float u0;
18 | };
19 |
20 | kernel void swiftMetal(constant Uniforms& us [[ buffer(0) ]],
21 | texture2d tex [[ texture(0) ]],
22 | texture2d tex0 [[ texture(1) ]],
23 | texture2d tex1 [[ texture(2) ]],
24 | uint2 pos [[ thread_position_in_grid ]],
25 | sampler smp [[ sampler(0) ]]) {
26 |
27 | int x = pos.x;
28 | int y = pos.y;
29 | int w = tex.get_width();
30 | int h = tex.get_height();
31 |
32 | if (x >= w || y >= h) { return; }
33 |
34 | float u = (float(x) + 0.5) / float(w);
35 | float v = (float(y) + 0.5) / float(h);
36 | float2 uv = float2(u, v);
37 |
38 | float4 t0 = tex0.read(pos);
39 | float4 t1 = tex1.sample(smp, uv);
40 | float4 k0 = float4(0);
41 | bool b0 = true;
42 | bool b1 = false;
43 | bool v0 = b0 && b1;
44 | float4 v1 = max(t0, t1);
45 |
46 | float4 val = f0(t0) + float4(us.u0, 0.0, 0.0, 1.0) * t1 + k0 + (!v0 ? 1.0 : 0.0) + v1;
47 |
48 | tex.write(val, pos);
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/SwiftMetalDemo/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/Sources/Types/SMEntity.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SMEntity.swift
3 | // SwiftMetal
4 | //
5 | // Created by Anton Heestand on 2019-12-06.
6 | // Copyright © 2019 Hexagons. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public class SMEntity: Identifiable, Equatable {
12 |
13 | public let id: UUID
14 |
15 | let type: String
16 |
17 | public var snippet: () -> (String) = { "#" }
18 |
19 | let operation: SMOperation?
20 |
21 | var isArg: Bool = false
22 | var returnId: UUID?
23 | var isReturn: Bool { returnId != nil }
24 |
25 | let isFuture: Bool
26 | var futureIndex: Int?
27 | var futureSnippet: String {
28 | "us.u\(futureIndex ?? -1)"
29 | }
30 |
31 | var subscriptEntity: SMEntity?
32 |
33 | var sampleTexture: SMTexture?
34 | var sampleUV: SMFloat2?
35 |
36 | let fromEntities: [SMEntity]
37 |
38 | var hasSink: Bool = false
39 | var sink: (() -> ())?
40 |
41 | var rawUniforms: [SMRawType]? { nil }
42 |
43 | var children: [SMEntity] {
44 | var children: [SMEntity] = [
45 | operation?.lhs,
46 | operation?.rhs,
47 | subscriptEntity,
48 | sampleTexture,
49 | sampleUV
50 | ]
51 | .compactMap({ $0 })
52 | children.append(contentsOf: fromEntities)
53 | return children
54 | }
55 |
56 | init(type: String, operation: SMOperation? = nil, isFuture: Bool = false, fromEntities: [SMEntity] = []) {
57 | id = UUID()
58 | self.type = type
59 | self.operation = operation
60 | self.isFuture = isFuture
61 | self.fromEntities = fromEntities
62 | if isFuture {
63 | snippet = {
64 | self.futureSnippet
65 | }
66 | }
67 | }
68 |
69 | // MARK: - Equatable
70 |
71 | public static func == (lhs: SMEntity, rhs: SMEntity) -> Bool {
72 | lhs.id == rhs.id
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/SwiftMetalDemo/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/SwiftMetalDemo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | NSCameraUsageDescription
24 | SwiftMetalDemo
25 | UIApplicationSceneManifest
26 |
27 | UIApplicationSupportsMultipleScenes
28 |
29 | UISceneConfigurations
30 |
31 | UIWindowSceneSessionRoleApplication
32 |
33 |
34 | UISceneConfigurationName
35 | Default Configuration
36 | UISceneDelegateClassName
37 | $(PRODUCT_MODULE_NAME).SceneDelegate
38 |
39 |
40 |
41 |
42 | UILaunchStoryboardName
43 | LaunchScreen
44 | UIRequiredDeviceCapabilities
45 |
46 | armv7
47 |
48 | UISupportedInterfaceOrientations
49 |
50 | UIInterfaceOrientationPortrait
51 |
52 | UISupportedInterfaceOrientations~ipad
53 |
54 | UIInterfaceOrientationPortrait
55 | UIInterfaceOrientationPortraitUpsideDown
56 | UIInterfaceOrientationLandscapeLeft
57 | UIInterfaceOrientationLandscapeRight
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/SwiftMetalTests/SwiftMetalTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftMetalTests.swift
3 | // SwiftMetalTests
4 | //
5 | // Created by Anton Heestand on 2019-12-06.
6 | // Copyright © 2019 Hexagons. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import SwiftMetal
11 |
12 | class SwiftMetalTests: XCTestCase {
13 |
14 | override func setUp() {}
15 |
16 | override func tearDown() {}
17 |
18 | func testShader() {
19 |
20 | let shader = SMShader { uv in
21 | let add: SMFunc = function { args -> SMFloat4 in
22 | let a = args[0] as! SMFloat4
23 | let b = args[1] as! SMFloat4
24 | return a + b
25 | }
26 | let sub = function { args -> SMFloat4 in
27 | (args[0] as! SMFloat4) - (args[1] as! SMFloat4)
28 | }
29 | let mult = function { args -> SMFloat4 in
30 | (args[0] as! SMFloat4) * (args[1] as! SMFloat4) * (args[2] as! SMFloat4)
31 | }
32 | let a = float4(1, 1, 1, 1)
33 | let b = float4(2, 2, 2, 2)
34 | let c = float4(2, 2, 2, 2)
35 | let d = float4(3)
36 | let e = float4(4)
37 | let aa = a + a - a
38 | let bb = b + b - b
39 | let cc = c + c - c
40 | return add.call(d, e) + mult.call(aa, bb, cc) + sub.call(d, e)
41 | }
42 |
43 | let res = CGSize(width: 1, height: 1)
44 | let render: SMTexture = try! SMRenderer.render(shader, at: res, as: .rgba16Float)
45 |
46 | if let raw8 = try? render.raw8() {
47 | if raw8.count <= 256 {
48 | print("raw8", raw8.map({ CGFloat($0) / 255 }))
49 | }
50 | XCTAssertNotEqual(raw8.first!, 0)
51 | } else if let raw16 = try? render.raw16() {
52 | if raw16.count <= 256 {
53 | print("raw16", raw16)
54 | }
55 | XCTAssertNotEqual(raw16.first!, 0.0)
56 | } else if let raw32 = try? render.raw32() {
57 | if raw32.count <= 256 {
58 | print("raw32", raw32)
59 | }
60 | XCTAssertNotEqual(raw32.first!, 0.0)
61 | }
62 |
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/Sources/SMFunction.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SMFunction.swift
3 | // SwiftMetal
4 | //
5 | // Created by Hexagons on 2019-12-07.
6 | // Copyright © 2019 Hexagons. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public class SMFunc: Identifiable {
12 |
13 | public let id: UUID
14 |
15 | let function: ([SMEntity]) -> (SMEntity)
16 |
17 | public init(_ function: @escaping ([SMEntity]) -> (R)) {
18 | id = UUID()
19 | self.function = function
20 | }
21 |
22 | public func call(_ arguments: SMEntity...) -> R {
23 | arguments.forEach { entity in
24 | entity.isArg = true
25 | }
26 | let returnEntity = function(arguments)
27 | returnEntity.returnId = id
28 | return returnEntity as! R
29 | }
30 |
31 | }
32 |
33 | public func function(_ function: @escaping ([SMEntity]) -> (R)) -> SMFunc {
34 | SMFunc(function)
35 | }
36 |
37 | struct SMFunction {
38 | let argEntities: [SMEntity]
39 | let returnEntity: SMEntity
40 | let index: Int
41 | var name: String {
42 | return "f\(index)"
43 | }
44 | var code: String {
45 | var lines: [Line] = []
46 | var declaration = ""
47 | declaration += "\(returnEntity.type) \(name)("
48 | for (i, argEntity) in argEntities.enumerated() {
49 | if i > 0 {
50 | declaration += ", "
51 | }
52 | declaration += "\(argEntity.type) a\(i)"
53 | }
54 | declaration += ") {"
55 | lines.append(Line(declaration))
56 | var snippet: String = returnEntity.snippet()
57 | for (i, argEntity) in argEntities.enumerated() {
58 | if let snippetIndexRange = snippet.range(of: argEntity.snippet()) {
59 | snippet = snippet.replacingCharacters(in: snippetIndexRange, with: "a\(i)")
60 | }
61 | }
62 | lines.append(Line(in: 1, "return \(snippet);"))
63 | lines.append(Line("}"))
64 | return Line.merge(lines)
65 | }
66 | func snippet(with args: [SMEntity]) -> String {
67 | var call = ""
68 | call += "\(name)("
69 | for (i, arg) in args.enumerated() {
70 | if i > 0 {
71 | call += ", "
72 | }
73 | call += "\(arg.snippet())"
74 | }
75 | call += ")"
76 | return call
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/Sources/Other/Float16.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Float16.swift
3 | // SwiftMetal
4 | //
5 | // Created by Hexagons on 2019-12-09.
6 | // Copyright © 2019 Hexagons. All rights reserved.
7 | //
8 | // by kerfuffle on Dec 5, 2017 1:59 AM
9 | // https://forums.developer.apple.com/thread/93282
10 | //
11 |
12 | import Foundation
13 | import Accelerate
14 |
15 | /* Utility functions for dealing with 16-bit floating point values in Swift. */
16 |
17 | /**
18 | Since Swift has no datatype for a 16-bit float we use `UInt16`s instead,
19 | which take up the same amount of memory. (Note: The simd framework does
20 | have "half" types but only for 2, 3, or 4-element vectors, not scalars.)
21 | */
22 | public typealias Float16 = UInt16
23 |
24 | /**
25 | Creates a new array of Swift `Float` values from a buffer of float-16s.
26 | */
27 | public func float16to32(_ input: UnsafeMutablePointer, count: Int) -> [Float] {
28 | var output = [Float](repeating: 0, count: count)
29 | float16to32(input: input, output: &output, count: count)
30 | return output
31 | }
32 |
33 | /**
34 | Converts a buffer of float-16s into a buffer of `Float`s, in-place.
35 | */
36 | public func float16to32(input: UnsafeMutablePointer, output: UnsafeMutableRawPointer, count: Int) {
37 | var bufferFloat16 = vImage_Buffer(data: input, height: 1, width: UInt(count), rowBytes: count * 2)
38 | var bufferFloat32 = vImage_Buffer(data: output, height: 1, width: UInt(count), rowBytes: count * 4)
39 |
40 | if vImageConvert_Planar16FtoPlanarF(&bufferFloat16, &bufferFloat32, 0) != kvImageNoError {
41 | print("Error converting float16 to float32")
42 | }
43 | }
44 |
45 | /**
46 | Creates a new array of float-16 values from a buffer of `Float`s.
47 | */
48 | public func float32to16(_ input: UnsafeMutablePointer, count: Int) -> [Float16] {
49 | var output = [Float16](repeating: 0, count: count)
50 | float32to16(input: input, output: &output, count: count)
51 | return output
52 | }
53 |
54 | /**
55 | Converts a buffer of `Float`s into a buffer of float-16s, in-place.
56 | */
57 | public func float32to16(input: UnsafeMutablePointer, output: UnsafeMutableRawPointer, count: Int) {
58 | var bufferFloat32 = vImage_Buffer(data: input, height: 1, width: UInt(count), rowBytes: count * 4)
59 | var bufferFloat16 = vImage_Buffer(data: output, height: 1, width: UInt(count), rowBytes: count * 2)
60 |
61 | if vImageConvert_PlanarFtoPlanar16F(&bufferFloat32, &bufferFloat16, 0) != kvImageNoError {
62 | print("Error converting float32 to float16")
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/ExampleShaders/Camera.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Camera.swift
3 | // SwiftMetal_iOS
4 | //
5 | // Created by Anton Heestand on 2019-12-12.
6 | // Copyright © 2019 Hexagons. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import SwiftMetal
11 | import AVKit
12 |
13 | struct CameraView: View {
14 | @ObservedObject var camera: Camera = Camera.main
15 | var body: some View {
16 | ZStack {
17 | Color.black
18 | .edgesIgnoringSafeArea(.all)
19 | SMView {
20 | SMShader { uv in
21 | SMLiveTexture(self.$camera.pixelBuffer)
22 | .sample(at: float2(uv.y, 1.0 - uv.x))
23 | }
24 | }
25 | .aspectRatio(9 / 16, contentMode: .fit)
26 | }
27 | }
28 | }
29 |
30 | class Camera: NSObject, ObservableObject, AVCaptureVideoDataOutputSampleBufferDelegate {
31 |
32 | static let main = Camera()
33 |
34 | @Published var pixelBuffer: CVPixelBuffer?
35 |
36 | let device: AVCaptureDevice
37 | let session: AVCaptureSession
38 | let input: AVCaptureDeviceInput
39 | let output: AVCaptureVideoDataOutput
40 |
41 | override init() {
42 | device = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)!
43 | session = AVCaptureSession()
44 | input = try! AVCaptureDeviceInput(device: device)
45 | output = AVCaptureVideoDataOutput()
46 | super.init()
47 | setup()
48 | }
49 |
50 | func setup() {
51 | output.alwaysDiscardsLateVideoFrames = true
52 | output.videoSettings = [
53 | kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA
54 | ]
55 | guard session.canAddInput(input) else {
56 | print("Camera - Can't add input")
57 | return
58 | }
59 | session.addInput(input)
60 | guard session.canAddOutput(output) else {
61 | print("Camera - Can't add output")
62 | return
63 | }
64 | session.addOutput(output)
65 | let queue = DispatchQueue(label: "camera.queue")
66 | output.setSampleBufferDelegate(self, queue: queue)
67 | session.startRunning()
68 | }
69 |
70 | func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
71 | guard let buffer: CVImageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
72 | print("Camera - Buffer get failed.")
73 | return
74 | }
75 | DispatchQueue.main.async {
76 | self.pixelBuffer = buffer
77 | }
78 | }
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/SwiftMetalDemo/Main.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Main.swift
3 | // SwiftMetalDemo
4 | //
5 | // Created by Anton Heestand on 2019-12-13.
6 | // Copyright © 2019 Hexagons. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SwiftMetal
11 | import Metal
12 |
13 | class Main: ObservableObject {
14 |
15 | let res: CGSize
16 |
17 | let camera: Camera
18 |
19 | var shader: SMShader!
20 |
21 | let drawableTexture: MTLTexture
22 | @Published var finalTexture: MTLTexture?
23 |
24 | @Published var value: Float = 0.5
25 |
26 | var displayLink: CADisplayLink!
27 | var rendering: Bool = false
28 |
29 | init() {
30 |
31 | let width: CGFloat = UIScreen.main.nativeBounds.width
32 | res = CGSize(width: width, height: width * (16 / 9))
33 |
34 | drawableTexture = SMTexture.emptyTexture(at: res, as: .rgba8Unorm)!
35 |
36 | camera = Camera()
37 |
38 | shader = SMShader({ uv in
39 | let cross: SMFunc = function { args -> SMFloat4 in
40 | let a: SMFloat4 = args[0] as! SMFloat4
41 | let b: SMFloat4 = args[1] as! SMFloat4
42 | let f: SMFloat = args[2] as! SMFloat
43 | let f4: SMFloat4 = float4(f)
44 | return a * (float(1.0) - f4) + b * f4
45 | }
46 | let cam = SMLiveTexture(self.camera.$pixelBuffer)
47 | .sample(at: float2(uv.y, 1.0 - uv.x))
48 | let feed = SMTexture(texture: drawableTexture)
49 | let val: SMFloat = 0.9 + 0.2 * SMLiveFloat($value)
50 | let disp = feed.sample(at: float2((uv.x - 0.5) * val + 0.5,
51 | (uv.y - 0.5) * val + 0.5))
52 | return cross.call(cam, disp, float(0.9))
53 | })
54 |
55 | displayLink = CADisplayLink(target: self, selector: #selector(frameLoop))
56 | displayLink.add(to: .current, forMode: .common)
57 |
58 | }
59 |
60 | @objc func frameLoop() {
61 | print("Demo - Frame Loop")
62 | guard !rendering else { return }
63 | rendering = true
64 | DispatchQueue.global(qos: .background).async {
65 | print("Demo - Render Start")
66 | let startTime = CFAbsoluteTimeGetCurrent()
67 | let renderedTexture = try! SMRenderer.render(self.shader, at: self.res, on: self.drawableTexture)
68 | let endTime = CFAbsoluteTimeGetCurrent()
69 | let renderTime = endTime - startTime
70 | let renderTimeMs = Double(Int(round(renderTime * 1_000_000))) / 1_000
71 | print("Demo - Render Time \(renderTimeMs)ms")
72 | DispatchQueue.main.async {
73 | print("Demo - Render Done")
74 | self.finalTexture = renderedTexture.texture //copyTexture()
75 | self.rendering = false
76 | }
77 | }
78 |
79 | }
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/SwiftMetal.xcodeproj/xcshareddata/xcschemes/SwiftMetal_iOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
54 |
60 |
61 |
67 |
68 |
69 |
70 |
72 |
73 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # SwiftMetal
4 |
5 | [](https://github.com/hexagons/SwiftMetal/blob/master/LICENSE)
6 | [](http://cocoapods.org/pods/SwiftMetal)
7 | [](http://cocoapods.org/pods/SwiftMetal)
8 |
9 |
10 | ## Install
11 |
12 | Swift Package or CocoaPods
13 |
14 | ```ruby
15 | pod 'SwiftMetal'
16 | ```
17 |
18 | ## Write Metal in Swift
19 |
20 | ~~~~swift
21 | import SwiftMetal
22 | ~~~~
23 |
24 | ~~~~swift
25 | let add: SMFunc = function { args -> SMFloat4 in
26 | let a = args[0] as! SMFloat4
27 | let b = args[1] as! SMFloat4
28 | return a + b
29 | }
30 | let shader = SMShader { uv in
31 | let a = float4(0.1, 0.0, 0.0, 1.0)
32 | let b = float4(0.2, 0.0, 0.0, 1.0)
33 | let t = SMTexture(image: UIImage(named: "photo1")!)!
34 | let c: SMFloat4 = add.call(a, a) * add.call(b, b) + t
35 | return c
36 | }
37 | let res = CGSize(width: 1024, height: 1024)
38 | let render: SMTexture = try! SMRenderer.render(shader: shader, at: res)
39 | let image: UIImage = try! render.image()
40 | let texture: MTLTexture = render.texture
41 | ~~~~
42 |
43 |
44 | ## Write Metal in SwiftUI
45 |
46 | ~~~~swift
47 | import SwiftUI
48 | import SwiftMetal
49 | ~~~~
50 |
51 | ~~~~swift
52 | struct ContentView: View {
53 | @State var value: Float = 0.5
54 | var body: some View {
55 | VStack {
56 | Slider(value: $value)
57 | SMView {
58 | SMShader { uv in
59 | let tex1 = SMTexture(image: UIImage(named: "photo1")!)!
60 | let tex2 = SMTexture(image: UIImage(named: "photo2")!)!
61 | let val = SMLiveFloat(self.$value)
62 | return tex1.sample(at: uv + float2(tex2.r * -val, 0.0))
63 | }
64 | }
65 | .aspectRatio(1.5, contentMode: .fit)
66 | .cornerRadius(10)
67 | }
68 | }
69 | }
70 | ~~~~
71 |
72 |
73 | ## Auto generated Metal code
74 |
75 | Generated from first Swift example.
76 |
77 | ~~~~metal
78 | #include
79 | using namespace metal;
80 |
81 | float4 f0(float4 a0, float4 a1) {
82 | return (a0 + a1);
83 | }
84 |
85 | kernel void swiftMetal(
86 | texture2d tex [[ texture(0) ]],
87 | texture2d tex0 [[ texture(1) ]],
88 | uint2 pos [[ thread_position_in_grid ]],
89 | sampler smp [[ sampler(0) ]]
90 | ) {
91 |
92 | if (pos.x >= tex.get_width() || pos.y >= tex.get_height()) { return; }
93 |
94 | float4 t0 = tex0.read(pos);
95 |
96 | float4 v0 = float4(0.1, 0.0, 0.0, 1.0);
97 | float4 v1 = float4(0.2, 0.0, 0.0, 1.0);
98 |
99 | float4 val = ((f0(v0, v0) * f0(v1, v1)) + t0);
100 |
101 | tex.write(val, pos);
102 |
103 | }
104 | ~~~~
105 |
--------------------------------------------------------------------------------
/ExampleShaders/Logo.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Logo.swift
3 | // SwiftMetalDemo
4 | //
5 | // Created by Anton Heestand on 2019-12-12.
6 | // Copyright © 2019 Hexagons. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import SwiftMetal
11 |
12 | let aspect: Float = 2.5
13 |
14 | func color(_ hex: String, alpha: SMFloat = 1.0) -> SMFloat4 {
15 | var hex = hex
16 | if hex[0..<1] == "#" {
17 | if hex.count == 4 {
18 | hex = hex[1..<4]
19 | } else {
20 | hex = hex[1..<7]
21 | }
22 | }
23 | if hex.count == 3 {
24 | let r = hex[0..<1]
25 | let g = hex[1..<2]
26 | let b = hex[2..<3]
27 | hex = r + r + g + g + b + b
28 | }
29 | var hexInt: UInt32 = 0
30 | let scanner: Scanner = Scanner(string: hex)
31 | scanner.scanHexInt32(&hexInt)
32 | let r = SMFloat(Float((hexInt & 0xff0000) >> 16) / 255.0)
33 | let g = SMFloat(Float((hexInt & 0xff00) >> 8) / 255.0)
34 | let b = SMFloat(Float((hexInt & 0xff) >> 0) / 255.0)
35 | return SMFloat4([r, g, b, alpha])
36 | }
37 |
38 | let logoShader: SMShader = SMShader { uv in
39 |
40 | let swiftColorA: SMFloat4 = color("#fd442a")
41 | let swiftColorB: SMFloat4 = color("#faa33d")
42 | let metalColorA: SMFloat4 = color("#1ffe72")
43 | let metalColorB: SMFloat4 = color("#1efdc6")
44 |
45 | let circle: SMFunc = function { args -> SMBool in
46 | let s: SMFloat = args[0] as! SMFloat
47 | let x: SMFloat = args[1] as! SMFloat
48 | let y: SMFloat = args[2] as! SMFloat
49 | let c: SMFloat = sqrt(pow(x, 2) + pow(y, 2))
50 | return c < s
51 | }
52 | let gradient: SMFunc = function { args -> SMFloat4 in
53 | let v: SMFloat = args[0] as! SMFloat
54 | let a: SMFloat4 = args[1] as! SMFloat4
55 | let b: SMFloat4 = args[2] as! SMFloat4
56 | let f: SMFloat4 = float4(v)
57 | return (float(1.0) - f) * a + f * b
58 | }
59 |
60 | let x: SMFloat = uv.x - 0.5
61 | let y: SMFloat = (uv.y - 0.5) / SMFloat(aspect)
62 | let h: SMFloat = sqrt(3 / 4)
63 | let s: SMFloat = (1.0 / 5.0)
64 | let s2: SMFloat = s * h
65 | let s3: SMFloat = s * sqrt(1.75)
66 |
67 | let aCL: SMBool = circle.call(s, x + s * 1.5, y)
68 | let bCL: SMBool = circle.call(s / 2, x + s * 1.5, y)
69 | let aCR: SMBool = circle.call(s, x - s * 1.5, y)
70 | let bCR: SMBool = circle.call(s / 2, x - s * 1.5, y)
71 | let aCB: SMBool = circle.call(s * 2, x, y - s2 * 3)
72 | let aCT: SMBool = circle.call(s * 2, x, y + s2 * 3)
73 | let aCX: SMBool = circle.call(s3, x, y)
74 |
75 | let o1: SMBool = aCB || aCT
76 | let o2: SMBool = (aCL || aCR || aCX) && !o1
77 | let c: SMFloat4 = o2 > float4(1.0) <=> float4(0.0)
78 |
79 | let swiftGradient: SMFloat4 = gradient.call(1.0 - uv.y, swiftColorA, swiftColorB)
80 | let metalGradient: SMFloat4 = gradient.call(1.0 - uv.y, metalColorA, metalColorB)
81 | let ug: SMFloat = uv.x * 2 - 0.5
82 | let swiftMetalGradient: SMFloat4 = gradient.call(ug, swiftGradient, metalGradient)
83 |
84 | let white: SMFloat4 = float4(1.0, 1.0, 1.0, 1.0)
85 | let black: SMFloat4 = float4(0.0, 0.0, 0.0, 1.0)
86 |
87 | let final: SMFloat4 = bCL > white <=> (bCR > black <=> (c * swiftMetalGradient))
88 |
89 | return final
90 | }
91 |
92 | struct LogoView: View {
93 | var body: some View {
94 | ZStack {
95 | Color.black
96 | .edgesIgnoringSafeArea(.all)
97 | SMView { logoShader }
98 | .aspectRatio(CGFloat(aspect), contentMode: .fit)
99 | }
100 | }
101 | }
102 |
103 | struct LogoView_Previews: PreviewProvider {
104 | static var previews: some View {
105 | LogoView()
106 | .previewLayout(.fixed(width: CGFloat(aspect) * 350, height: 350))
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/Sources/Types.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Types.swift
3 | // SwiftMetal
4 | //
5 | // Created by Hexagons on 2019-12-08.
6 | // Copyright © 2019 Hexagons. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | #if os(macOS)
11 | import Cocoa
12 | #else
13 | import UIKit
14 | #endif
15 |
16 | #if os(macOS)
17 | public typealias _View = NSView
18 | public typealias _Image = NSImage
19 | public typealias _Color = NSColor
20 | #else
21 | public typealias _View = UIView
22 | public typealias _Image = UIImage
23 | public typealias _Color = UIColor
24 | #endif
25 |
26 | class SMVariablePack {
27 | let entity: SMEntity
28 | let index: Int
29 | let snippet: String
30 | var name: String {
31 | return "v\(index)"
32 | }
33 | var code: String {
34 | code(with: snippet)
35 | }
36 | // var rawCode: String {
37 | // code(with: entity.snippet())
38 | // }
39 | init(for entity: SMEntity, at index: Int, with snippet: String) {
40 | self.entity = entity
41 | self.index = index
42 | self.snippet = snippet
43 | }
44 | fileprivate func code(with snippet: String) -> String {
45 | "\(entity.type) \(name) = \(snippet);"
46 | }
47 | }
48 |
49 | struct SMUniformPack {
50 | let entity: SMEntity
51 | let index: Int
52 | var name: String {
53 | return "u\(index)"
54 | }
55 | var code: String {
56 | "\(entity.type) \(name);"
57 | }
58 | var snippet: String {
59 | "us.\(name)"
60 | }
61 | }
62 |
63 | struct SMOperation {
64 | let lhs: SMEntity
65 | let rhs: SMEntity
66 | }
67 |
68 | public protocol SMRaw {}
69 | public protocol SMRawType: SMRaw {
70 | static var typeName: String { get }
71 | }
72 |
73 | public class SMTuple: SMRaw {
74 | let values: [SMValue