summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorruki <[email protected]>2021-08-22 19:20:21 +0800
committerruki <[email protected]>2021-08-22 19:20:21 +0800
commit0b3a790a6e1e81b25ff4e2d7a53c73bc42b47e26 (patch)
tree69a495dd29015e91313c7416aaf82f62f6a41175
parent0c7df1fe002288cf91601a941d02988428e2c6d0 (diff)
add metal app test
-rw-r--r--tests/projects/objc/metal_app/.gitignore21
-rw-r--r--tests/projects/objc/metal_app/Application/AAPLAppDelegate.h14
-rw-r--r--tests/projects/objc/metal_app/Application/AAPLAppDelegate.m16
-rw-r--r--tests/projects/objc/metal_app/Application/AAPLViewController.h23
-rw-r--r--tests/projects/objc/metal_app/Application/AAPLViewController.m39
-rw-r--r--tests/projects/objc/metal_app/Application/iOS/Base.lproj/LaunchScreen.storyboard27
-rw-r--r--tests/projects/objc/metal_app/Application/iOS/Base.lproj/Main.storyboard30
-rw-r--r--tests/projects/objc/metal_app/Application/iOS/Info.plist48
-rw-r--r--tests/projects/objc/metal_app/Application/macOS/Base.lproj/Main.storyboard693
-rw-r--r--tests/projects/objc/metal_app/Application/macOS/Info.plist30
-rw-r--r--tests/projects/objc/metal_app/Application/main.m36
-rw-r--r--tests/projects/objc/metal_app/Application/tvOS/Base.lproj/Main.storyboard30
-rw-r--r--tests/projects/objc/metal_app/Application/tvOS/Info.plist32
-rw-r--r--tests/projects/objc/metal_app/Configuration/SampleCode.xcconfig13
-rw-r--r--tests/projects/objc/metal_app/HelloTriangle.xcodeproj/.xcodesamplecode.plist5
-rw-r--r--tests/projects/objc/metal_app/HelloTriangle.xcodeproj/project.pbxproj681
-rw-r--r--tests/projects/objc/metal_app/HelloTriangle.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings8
-rw-r--r--tests/projects/objc/metal_app/LICENSE/LICENSE.txt8
-rw-r--r--tests/projects/objc/metal_app/README.md332
-rw-r--r--tests/projects/objc/metal_app/Renderer/AAPLRenderer.h14
-rw-r--r--tests/projects/objc/metal_app/Renderer/AAPLRenderer.m132
-rw-r--r--tests/projects/objc/metal_app/Renderer/AAPLShaderTypes.h31
-rw-r--r--tests/projects/objc/metal_app/Renderer/AAPLShaders.metal62
-rw-r--r--tests/projects/objc/metal_app/xmake.lua22
24 files changed, 2347 insertions, 0 deletions
diff --git a/tests/projects/objc/metal_app/.gitignore b/tests/projects/objc/metal_app/.gitignore
new file mode 100644
index 000000000..9e50718e2
--- /dev/null
+++ b/tests/projects/objc/metal_app/.gitignore
@@ -0,0 +1,21 @@
+# See LICENSE folder for this sample’s licensing information.
+#
+# Apple sample code gitignore configuration.
+
+# Finder
+.DS_Store
+
+# Xcode - User files
+xcuserdata/
+
+**/*.xcodeproj/project.xcworkspace/*
+!**/*.xcodeproj/project.xcworkspace/xcshareddata
+
+**/*.xcodeproj/project.xcworkspace/xcshareddata/*
+!**/*.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
+
+**/*.playground/playground.xcworkspace/*
+!**/*.playground/playground.xcworkspace/xcshareddata
+
+**/*.playground/playground.xcworkspace/xcshareddata/*
+!**/*.playground/playground.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
diff --git a/tests/projects/objc/metal_app/Application/AAPLAppDelegate.h b/tests/projects/objc/metal_app/Application/AAPLAppDelegate.h
new file mode 100644
index 000000000..a4e4a7fef
--- /dev/null
+++ b/tests/projects/objc/metal_app/Application/AAPLAppDelegate.h
@@ -0,0 +1,14 @@
+/*
+See LICENSE folder for this sample’s licensing information.
+
+Abstract:
+Header for our iOS & tvOS application delegate
+*/
+
+#import <UIKit/UIKit.h>
+
+@interface AAPLAppDelegate : UIResponder <UIApplicationDelegate>
+
+@property (strong, nonatomic) UIWindow *window;
+
+@end
diff --git a/tests/projects/objc/metal_app/Application/AAPLAppDelegate.m b/tests/projects/objc/metal_app/Application/AAPLAppDelegate.m
new file mode 100644
index 000000000..58b31e876
--- /dev/null
+++ b/tests/projects/objc/metal_app/Application/AAPLAppDelegate.m
@@ -0,0 +1,16 @@
+/*
+See LICENSE folder for this sample’s licensing information.
+
+Abstract:
+Implementation of our iOS & tvOS application delegate
+*/
+
+#import "AAPLAppDelegate.h"
+
+@implementation AAPLAppDelegate
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+ return YES;
+}
+
+@end
diff --git a/tests/projects/objc/metal_app/Application/AAPLViewController.h b/tests/projects/objc/metal_app/Application/AAPLViewController.h
new file mode 100644
index 000000000..f9ecea64f
--- /dev/null
+++ b/tests/projects/objc/metal_app/Application/AAPLViewController.h
@@ -0,0 +1,23 @@
+/*
+See LICENSE folder for this sample’s licensing information.
+
+Abstract:
+Header for our our cross-platform view controller
+*/
+
+#if defined(TARGET_IOS) || defined(TARGET_TVOS)
+@import UIKit;
+#define PlatformViewController UIViewController
+#else
+@import AppKit;
+#define PlatformViewController NSViewController
+#endif
+
+@import MetalKit;
+
+#import "AAPLRenderer.h"
+
+// Our view controller
+@interface AAPLViewController : PlatformViewController
+
+@end
diff --git a/tests/projects/objc/metal_app/Application/AAPLViewController.m b/tests/projects/objc/metal_app/Application/AAPLViewController.m
new file mode 100644
index 000000000..dc02a8c4a
--- /dev/null
+++ b/tests/projects/objc/metal_app/Application/AAPLViewController.m
@@ -0,0 +1,39 @@
+/*
+See LICENSE folder for this sample’s licensing information.
+
+Abstract:
+Implementation of our cross-platform view controller
+*/
+
+#import "AAPLViewController.h"
+#import "AAPLRenderer.h"
+
+@implementation AAPLViewController
+{
+ MTKView *_view;
+
+ AAPLRenderer *_renderer;
+}
+
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+
+ // Set the view to use the default device
+ _view = (MTKView *)self.view;
+
+ _view.device = MTLCreateSystemDefaultDevice();
+
+ NSAssert(_view.device, @"Metal is not supported on this device");
+
+ _renderer = [[AAPLRenderer alloc] initWithMetalKitView:_view];
+
+ NSAssert(_renderer, @"Renderer failed initialization");
+
+ // Initialize our renderer with the view size
+ [_renderer mtkView:_view drawableSizeWillChange:_view.drawableSize];
+
+ _view.delegate = _renderer;
+}
+
+@end
diff --git a/tests/projects/objc/metal_app/Application/iOS/Base.lproj/LaunchScreen.storyboard b/tests/projects/objc/metal_app/Application/iOS/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 000000000..fdf3f97d1
--- /dev/null
+++ b/tests/projects/objc/metal_app/Application/iOS/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11134" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
+ <dependencies>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11106"/>
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+ </dependencies>
+ <scenes>
+ <!--View Controller-->
+ <scene sceneID="EHf-IW-A2E">
+ <objects>
+ <viewController id="01J-lp-oVM" sceneMemberID="viewController">
+ <layoutGuides>
+ <viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
+ <viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
+ </layoutGuides>
+ <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+ </view>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="53" y="375"/>
+ </scene>
+ </scenes>
+</document>
diff --git a/tests/projects/objc/metal_app/Application/iOS/Base.lproj/Main.storyboard b/tests/projects/objc/metal_app/Application/iOS/Base.lproj/Main.storyboard
new file mode 100644
index 000000000..037265c96
--- /dev/null
+++ b/tests/projects/objc/metal_app/Application/iOS/Base.lproj/Main.storyboard
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11762" systemVersion="16E138" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BV1-FR-VrT">
+ <device id="retina4_7" orientation="portrait">
+ <adaptation id="fullscreen"/>
+ </device>
+ <dependencies>
+ <deployment identifier="iOS"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+ </dependencies>
+ <scenes>
+ <!--View Controller-->
+ <scene sceneID="tXr-a1-R10">
+ <objects>
+ <viewController id="BV1-FR-VrT" customClass="AAPLViewController" sceneMemberID="viewController">
+ <layoutGuides>
+ <viewControllerLayoutGuide type="top" id="8aa-yV-Osq"/>
+ <viewControllerLayoutGuide type="bottom" id="qHh-Mt-9TT"/>
+ </layoutGuides>
+ <view key="view" contentMode="scaleToFill" id="3se-qz-xqx" customClass="MTKView">
+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+ </view>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="SZV-WD-TEh" sceneMemberID="firstResponder"/>
+ </objects>
+ </scene>
+ </scenes>
+</document>
diff --git a/tests/projects/objc/metal_app/Application/iOS/Info.plist b/tests/projects/objc/metal_app/Application/iOS/Info.plist
new file mode 100644
index 000000000..154374326
--- /dev/null
+++ b/tests/projects/objc/metal_app/Application/iOS/Info.plist
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>$(PRODUCT_NAME)</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+ <key>LSRequiresIPhoneOS</key>
+ <true/>
+ <key>UILaunchStoryboardName</key>
+ <string>LaunchScreen</string>
+ <key>UIMainStoryboardFile</key>
+ <string>Main</string>
+ <key>UIRequiredDeviceCapabilities</key>
+ <array>
+ <string>armv7</string>
+ </array>
+ <key>UIStatusBarHidden</key>
+ <true/>
+ <key>UISupportedInterfaceOrientations</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ <string>UIInterfaceOrientationPortraitUpsideDown</string>
+ </array>
+ <key>UISupportedInterfaceOrientations~ipad</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationPortraitUpsideDown</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+</dict>
+</plist>
diff --git a/tests/projects/objc/metal_app/Application/macOS/Base.lproj/Main.storyboard b/tests/projects/objc/metal_app/Application/macOS/Base.lproj/Main.storyboard
new file mode 100644
index 000000000..e6be158ce
--- /dev/null
+++ b/tests/projects/objc/metal_app/Application/macOS/Base.lproj/Main.storyboard
@@ -0,0 +1,693 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="14724.1" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
+ <dependencies>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14724.1"/>
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+ </dependencies>
+ <scenes>
+ <!--Application-->
+ <scene sceneID="JPo-4y-FX3">
+ <objects>
+ <application id="hnw-xV-0zn" sceneMemberID="viewController">
+ <menu key="mainMenu" title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
+ <items>
+ <menuItem title="Hello Triangle" id="1Xt-HY-uBw">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Hello Triangle" systemMenu="apple" id="uQy-DD-JDr">
+ <items>
+ <menuItem title="About Hello Triangle" id="5kV-Vb-QxS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="orderFrontStandardAboutPanel:" target="Ady-hI-5gd" id="Exp-CZ-Vem"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
+ <menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
+ <menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
+ <menuItem title="Services" id="NMo-om-nkz">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
+ <menuItem title="Hide Hello Triangle" keyEquivalent="h" id="Olw-nP-bQN">
+ <connections>
+ <action selector="hide:" target="Ady-hI-5gd" id="PnN-Uc-m68"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="hideOtherApplications:" target="Ady-hI-5gd" id="VT4-aY-XCT"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Show All" id="Kd2-mp-pUS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="unhideAllApplications:" target="Ady-hI-5gd" id="Dhg-Le-xox"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
+ <menuItem title="Quit Hello Triangle" keyEquivalent="q" id="4sb-4s-VLi">
+ <connections>
+ <action selector="terminate:" target="Ady-hI-5gd" id="Te7-pn-YzF"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="File" id="dMs-cI-mzQ">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="File" id="bib-Uj-vzu">
+ <items>
+ <menuItem title="New" keyEquivalent="n" id="Was-JA-tGl">
+ <connections>
+ <action selector="newDocument:" target="Ady-hI-5gd" id="4Si-XN-c54"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Open…" keyEquivalent="o" id="IAo-SY-fd9">
+ <connections>
+ <action selector="openDocument:" target="Ady-hI-5gd" id="bVn-NM-KNZ"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Open Recent" id="tXI-mr-wws">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="oas-Oc-fiZ">
+ <items>
+ <menuItem title="Clear Menu" id="vNY-rz-j42">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="clearRecentDocuments:" target="Ady-hI-5gd" id="Daa-9d-B3U"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
+ <menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
+ <connections>
+ <action selector="performClose:" target="Ady-hI-5gd" id="HmO-Ls-i7Q"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Save…" keyEquivalent="s" id="pxx-59-PXV">
+ <connections>
+ <action selector="saveDocument:" target="Ady-hI-5gd" id="teZ-XB-qJY"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Save As…" keyEquivalent="S" id="Bw7-FT-i3A">
+ <connections>
+ <action selector="saveDocumentAs:" target="Ady-hI-5gd" id="mDf-zr-I0C"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Revert to Saved" keyEquivalent="r" id="KaW-ft-85H">
+ <connections>
+ <action selector="revertDocumentToSaved:" target="Ady-hI-5gd" id="iJ3-Pv-kwq"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="aJh-i4-bef"/>
+ <menuItem title="Page Setup…" keyEquivalent="P" id="qIS-W8-SiK">
+ <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
+ <connections>
+ <action selector="runPageLayout:" target="Ady-hI-5gd" id="Din-rz-gC5"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Print…" keyEquivalent="p" id="aTl-1u-JFS">
+ <connections>
+ <action selector="print:" target="Ady-hI-5gd" id="qaZ-4w-aoO"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Edit" id="5QF-Oa-p0T">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Edit" id="W48-6f-4Dl">
+ <items>
+ <menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
+ <connections>
+ <action selector="undo:" target="Ady-hI-5gd" id="M6e-cu-g7V"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
+ <connections>
+ <action selector="redo:" target="Ady-hI-5gd" id="oIA-Rs-6OD"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
+ <menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
+ <connections>
+ <action selector="cut:" target="Ady-hI-5gd" id="YJe-68-I9s"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
+ <connections>
+ <action selector="copy:" target="Ady-hI-5gd" id="G1f-GL-Joy"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
+ <connections>
+ <action selector="paste:" target="Ady-hI-5gd" id="UvS-8e-Qdg"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="pasteAsPlainText:" target="Ady-hI-5gd" id="cEh-KX-wJQ"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Delete" id="pa3-QI-u2k">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="delete:" target="Ady-hI-5gd" id="0Mk-Ml-PaM"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
+ <connections>
+ <action selector="selectAll:" target="Ady-hI-5gd" id="VNm-Mi-diN"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
+ <menuItem title="Find" id="4EN-yA-p0u">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Find" id="1b7-l0-nxx">
+ <items>
+ <menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
+ <connections>
+ <action selector="performFindPanelAction:" target="Ady-hI-5gd" id="cD7-Qs-BN4"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="performFindPanelAction:" target="Ady-hI-5gd" id="WD3-Gg-5AJ"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
+ <connections>
+ <action selector="performFindPanelAction:" target="Ady-hI-5gd" id="NDo-RZ-v9R"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
+ <connections>
+ <action selector="performFindPanelAction:" target="Ady-hI-5gd" id="HOh-sY-3ay"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
+ <connections>
+ <action selector="performFindPanelAction:" target="Ady-hI-5gd" id="U76-nv-p5D"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
+ <connections>
+ <action selector="centerSelectionInVisibleArea:" target="Ady-hI-5gd" id="IOG-6D-g5B"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
+ <items>
+ <menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
+ <connections>
+ <action selector="showGuessPanel:" target="Ady-hI-5gd" id="vFj-Ks-hy3"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
+ <connections>
+ <action selector="checkSpelling:" target="Ady-hI-5gd" id="fz7-VC-reM"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
+ <menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleContinuousSpellChecking:" target="Ady-hI-5gd" id="7w6-Qz-0kB"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleGrammarChecking:" target="Ady-hI-5gd" id="muD-Qn-j4w"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticSpellingCorrection:" target="Ady-hI-5gd" id="2lM-Qi-WAP"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Substitutions" id="9ic-FL-obx">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
+ <items>
+ <menuItem title="Show Substitutions" id="z6F-FW-3nz">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="orderFrontSubstitutionsPanel:" target="Ady-hI-5gd" id="oku-mr-iSq"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
+ <menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleSmartInsertDelete:" target="Ady-hI-5gd" id="3IJ-Se-DZD"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smart Quotes" id="hQb-2v-fYv">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticQuoteSubstitution:" target="Ady-hI-5gd" id="ptq-xd-QOA"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smart Dashes" id="rgM-f4-ycn">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticDashSubstitution:" target="Ady-hI-5gd" id="oCt-pO-9gS"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smart Links" id="cwL-P1-jid">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticLinkDetection:" target="Ady-hI-5gd" id="Gip-E3-Fov"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Data Detectors" id="tRr-pd-1PS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticDataDetection:" target="Ady-hI-5gd" id="R1I-Nq-Kbl"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Text Replacement" id="HFQ-gK-NFA">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticTextReplacement:" target="Ady-hI-5gd" id="DvP-Fe-Py6"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Transformations" id="2oI-Rn-ZJC">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Transformations" id="c8a-y6-VQd">
+ <items>
+ <menuItem title="Make Upper Case" id="vmV-6d-7jI">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="uppercaseWord:" target="Ady-hI-5gd" id="sPh-Tk-edu"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Make Lower Case" id="d9M-CD-aMd">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="lowercaseWord:" target="Ady-hI-5gd" id="iUZ-b5-hil"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Capitalize" id="UEZ-Bs-lqG">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="capitalizeWord:" target="Ady-hI-5gd" id="26H-TL-nsh"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Speech" id="xrE-MZ-jX0">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Speech" id="3rS-ZA-NoH">
+ <items>
+ <menuItem title="Start Speaking" id="Ynk-f8-cLZ">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="startSpeaking:" target="Ady-hI-5gd" id="654-Ng-kyl"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Stop Speaking" id="Oyz-dy-DGm">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="stopSpeaking:" target="Ady-hI-5gd" id="dX8-6p-jy9"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Format" id="jxT-CU-nIS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Format" id="GEO-Iw-cKr">
+ <items>
+ <menuItem title="Font" id="Gi5-1S-RQB">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Font" systemMenu="font" id="aXa-aM-Jaq">
+ <items>
+ <menuItem title="Show Fonts" keyEquivalent="t" id="Q5e-8K-NDq"/>
+ <menuItem title="Bold" tag="2" keyEquivalent="b" id="GB9-OM-e27"/>
+ <menuItem title="Italic" tag="1" keyEquivalent="i" id="Vjx-xi-njq"/>
+ <menuItem title="Underline" keyEquivalent="u" id="WRG-CD-K1S">
+ <connections>
+ <action selector="underline:" target="Ady-hI-5gd" id="FYS-2b-JAY"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="5gT-KC-WSO"/>
+ <menuItem title="Bigger" tag="3" keyEquivalent="+" id="Ptp-SP-VEL"/>
+ <menuItem title="Smaller" tag="4" keyEquivalent="-" id="i1d-Er-qST"/>
+ <menuItem isSeparatorItem="YES" id="kx3-Dk-x3B"/>
+ <menuItem title="Kern" id="jBQ-r6-VK2">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Kern" id="tlD-Oa-oAM">
+ <items>
+ <menuItem title="Use Default" id="GUa-eO-cwY">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="useStandardKerning:" target="Ady-hI-5gd" id="6dk-9l-Ckg"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Use None" id="cDB-IK-hbR">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="turnOffKerning:" target="Ady-hI-5gd" id="U8a-gz-Maa"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Tighten" id="46P-cB-AYj">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="tightenKerning:" target="Ady-hI-5gd" id="hr7-Nz-8ro"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Loosen" id="ogc-rX-tC1">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="loosenKerning:" target="Ady-hI-5gd" id="8i4-f9-FKE"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Ligatures" id="o6e-r0-MWq">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Ligatures" id="w0m-vy-SC9">
+ <items>
+ <menuItem title="Use Default" id="agt-UL-0e3">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="useStandardLigatures:" target="Ady-hI-5gd" id="7uR-wd-Dx6"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Use None" id="J7y-lM-qPV">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="turnOffLigatures:" target="Ady-hI-5gd" id="iX2-gA-Ilz"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Use All" id="xQD-1f-W4t">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="useAllLigatures:" target="Ady-hI-5gd" id="KcB-kA-TuK"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Baseline" id="OaQ-X3-Vso">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Baseline" id="ijk-EB-dga">
+ <items>
+ <menuItem title="Use Default" id="3Om-Ey-2VK">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="unscript:" target="Ady-hI-5gd" id="0vZ-95-Ywn"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Superscript" id="Rqc-34-cIF">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="superscript:" target="Ady-hI-5gd" id="3qV-fo-wpU"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Subscript" id="I0S-gh-46l">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="subscript:" target="Ady-hI-5gd" id="Q6W-4W-IGz"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Raise" id="2h7-ER-AoG">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="raiseBaseline:" target="Ady-hI-5gd" id="4sk-31-7Q9"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Lower" id="1tx-W0-xDw">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="lowerBaseline:" target="Ady-hI-5gd" id="OF1-bc-KW4"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="Ndw-q3-faq"/>
+ <menuItem title="Show Colors" keyEquivalent="C" id="bgn-CT-cEk">
+ <connections>
+ <action selector="orderFrontColorPanel:" target="Ady-hI-5gd" id="mSX-Xz-DV3"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="iMs-zA-UFJ"/>
+ <menuItem title="Copy Style" keyEquivalent="c" id="5Vv-lz-BsD">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="copyFont:" target="Ady-hI-5gd" id="GJO-xA-L4q"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Paste Style" keyEquivalent="v" id="vKC-jM-MkH">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="pasteFont:" target="Ady-hI-5gd" id="JfD-CL-leO"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Text" id="Fal-I4-PZk">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Text" id="d9c-me-L2H">
+ <items>
+ <menuItem title="Align Left" keyEquivalent="{" id="ZM1-6Q-yy1">
+ <connections>
+ <action selector="alignLeft:" target="Ady-hI-5gd" id="zUv-R1-uAa"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Center" keyEquivalent="|" id="VIY-Ag-zcb">
+ <connections>
+ <action selector="alignCenter:" target="Ady-hI-5gd" id="spX-mk-kcS"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Justify" id="J5U-5w-g23">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="alignJustified:" target="Ady-hI-5gd" id="ljL-7U-jND"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Align Right" keyEquivalent="}" id="wb2-vD-lq4">
+ <connections>
+ <action selector="alignRight:" target="Ady-hI-5gd" id="r48-bG-YeY"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="4s2-GY-VfK"/>
+ <menuItem title="Writing Direction" id="H1b-Si-o9J">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Writing Direction" id="8mr-sm-Yjd">
+ <items>
+ <menuItem title="Paragraph" enabled="NO" id="ZvO-Gk-QUH">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ </menuItem>
+ <menuItem id="YGs-j5-SAR">
+ <string key="title"> Default</string>
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="makeBaseWritingDirectionNatural:" target="Ady-hI-5gd" id="qtV-5e-UBP"/>
+ </connections>
+ </menuItem>
+ <menuItem id="Lbh-J2-qVU">
+ <string key="title"> Left to Right</string>
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="makeBaseWritingDirectionLeftToRight:" target="Ady-hI-5gd" id="S0X-9S-QSf"/>
+ </connections>
+ </menuItem>
+ <menuItem id="jFq-tB-4Kx">
+ <string key="title"> Right to Left</string>
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="makeBaseWritingDirectionRightToLeft:" target="Ady-hI-5gd" id="5fk-qB-AqJ"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="swp-gr-a21"/>
+ <menuItem title="Selection" enabled="NO" id="cqv-fj-IhA">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ </menuItem>
+ <menuItem id="Nop-cj-93Q">
+ <string key="title"> Default</string>
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="makeTextWritingDirectionNatural:" target="Ady-hI-5gd" id="lPI-Se-ZHp"/>
+ </connections>
+ </menuItem>
+ <menuItem id="BgM-ve-c93">
+ <string key="title"> Left to Right</string>
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="makeTextWritingDirectionLeftToRight:" target="Ady-hI-5gd" id="caW-Bv-w94"/>
+ </connections>
+ </menuItem>
+ <menuItem id="RB4-Sm-HuC">
+ <string key="title"> Right to Left</string>
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="makeTextWritingDirectionRightToLeft:" target="Ady-hI-5gd" id="EXD-6r-ZUu"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="fKy-g9-1gm"/>
+ <menuItem title="Show Ruler" id="vLm-3I-IUL">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleRuler:" target="Ady-hI-5gd" id="FOx-HJ-KwY"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Copy Ruler" keyEquivalent="c" id="MkV-Pr-PK5">
+ <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+ <connections>
+ <action selector="copyRuler:" target="Ady-hI-5gd" id="71i-fW-3W2"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Paste Ruler" keyEquivalent="v" id="LVM-kO-fVI">
+ <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+ <connections>
+ <action selector="pasteRuler:" target="Ady-hI-5gd" id="cSh-wd-qM2"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="View" id="H8h-7b-M4v">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="View" id="HyV-fh-RgO">
+ <items>
+ <menuItem title="Show Toolbar" keyEquivalent="t" id="snW-S8-Cw5">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="toggleToolbarShown:" target="Ady-hI-5gd" id="BXY-wc-z0C"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Customize Toolbar…" id="1UK-8n-QPP">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="runToolbarCustomizationPalette:" target="Ady-hI-5gd" id="pQI-g3-MTW"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="hB3-LF-h0Y"/>
+ <menuItem title="Show Sidebar" keyEquivalent="s" id="kIP-vf-haE">
+ <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+ <connections>
+ <action selector="toggleSidebar:" target="Ady-hI-5gd" id="iwa-gc-5KM"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
+ <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+ <connections>
+ <action selector="toggleFullScreen:" target="Ady-hI-5gd" id="dU3-MA-1Rq"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Window" id="aUF-d1-5bR">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
+ <items>
+ <menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
+ <connections>
+ <action selector="performMiniaturize:" target="Ady-hI-5gd" id="VwT-WD-YPe"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Zoom" id="R4o-n2-Eq4">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="performZoom:" target="Ady-hI-5gd" id="DIl-cC-cCs"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
+ <menuItem title="Bring All to Front" id="LE2-aR-0XJ">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="arrangeInFront:" target="Ady-hI-5gd" id="DRN-fu-gQh"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Help" id="wpr-3q-Mcd">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
+ <items>
+ <menuItem title="Hello Triangle Help" keyEquivalent="?" id="FKE-Sm-Kum">
+ <connections>
+ <action selector="showHelp:" target="Ady-hI-5gd" id="y7X-2Q-9no"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ </items>
+ </menu>
+ </application>
+ <customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="75" y="0.0"/>
+ </scene>
+ <!--Window Controller-->
+ <scene sceneID="R2V-B0-nI4">
+ <objects>
+ <windowController id="B8D-0N-5wS" sceneMemberID="viewController">
+ <window key="window" title="Hello Triangle" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="IQv-IB-iLA">
+ <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
+ <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
+ <rect key="contentRect" x="196" y="240" width="480" height="270"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
+ <connections>
+ <outlet property="delegate" destination="B8D-0N-5wS" id="c2Y-5V-HmV"/>
+ </connections>
+ </window>
+ <connections>
+ <segue destination="XfG-lQ-9wD" kind="relationship" relationship="window.shadowedContentViewController" id="cq2-FE-JQM"/>
+ </connections>
+ </windowController>
+ <customObject id="Oky-zY-oP4" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="75" y="250"/>
+ </scene>
+ <!--View Controller-->
+ <scene sceneID="hIz-AP-VOD">
+ <objects>
+ <viewController id="XfG-lQ-9wD" customClass="AAPLViewController" sceneMemberID="viewController">
+ <view key="view" wantsLayer="YES" id="m2S-Jp-Qdl" customClass="MTKView">
+ <rect key="frame" x="0.0" y="0.0" width="800" height="600"/>
+ <autoresizingMask key="autoresizingMask"/>
+ </view>
+ </viewController>
+ <customObject id="rPt-NT-nkU" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="75" y="817"/>
+ </scene>
+ </scenes>
+</document>
diff --git a/tests/projects/objc/metal_app/Application/macOS/Info.plist b/tests/projects/objc/metal_app/Application/macOS/Info.plist
new file mode 100644
index 000000000..f16cc0681
--- /dev/null
+++ b/tests/projects/objc/metal_app/Application/macOS/Info.plist
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>$(PRODUCT_NAME)</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+ <key>LSMinimumSystemVersion</key>
+ <string>$(MACOSX_DEPLOYMENT_TARGET)</string>
+ <key>NSMainStoryboardFile</key>
+ <string>Main</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+</dict>
+</plist>
diff --git a/tests/projects/objc/metal_app/Application/main.m b/tests/projects/objc/metal_app/Application/main.m
new file mode 100644
index 000000000..b35c0d5ec
--- /dev/null
+++ b/tests/projects/objc/metal_app/Application/main.m
@@ -0,0 +1,36 @@
+/*
+See LICENSE folder for this sample’s licensing information.
+
+Abstract:
+Application entry point for all platforms
+*/
+
+#if defined(TARGET_IOS) || defined(TARGET_TVOS)
+#import <UIKit/UIKit.h>
+#import <TargetConditionals.h>
+#import <Availability.h>
+#import "AAPLAppDelegate.h"
+#else
+#import <Cocoa/Cocoa.h>
+#endif
+
+#if defined(TARGET_IOS) || defined(TARGET_TVOS)
+
+int main(int argc, char * argv[]) {
+
+#if TARGET_OS_SIMULATOR && (!defined(__IPHONE_13_0) || !defined(__TVOS_13_0))
+#error No simulator support for Metal API for this SDK version. Must build for a device
+#endif
+
+ @autoreleasepool {
+ return UIApplicationMain(argc, argv, nil, NSStringFromClass([AAPLAppDelegate class]));
+ }
+}
+
+#elif defined(TARGET_MACOS)
+
+int main(int argc, const char * argv[]) {
+ return NSApplicationMain(argc, argv);
+}
+
+#endif
diff --git a/tests/projects/objc/metal_app/Application/tvOS/Base.lproj/Main.storyboard b/tests/projects/objc/metal_app/Application/tvOS/Base.lproj/Main.storyboard
new file mode 100644
index 000000000..77bc5bc2d
--- /dev/null
+++ b/tests/projects/objc/metal_app/Application/tvOS/Base.lproj/Main.storyboard
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder.AppleTV.Storyboard" version="3.0" toolsVersion="11762" systemVersion="16E138" targetRuntime="AppleTV" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BV1-FR-VrT">
+ <device id="appleTV" orientation="landscape">
+ <adaptation id="light"/>
+ </device>
+ <dependencies>
+ <deployment identifier="tvOS"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+ </dependencies>
+ <scenes>
+ <!--View Controller-->
+ <scene sceneID="tXr-a1-R10">
+ <objects>
+ <viewController id="BV1-FR-VrT" customClass="AAPLViewController" sceneMemberID="viewController">
+ <layoutGuides>
+ <viewControllerLayoutGuide type="top" id="8aa-yV-Osq"/>
+ <viewControllerLayoutGuide type="bottom" id="qHh-Mt-9TT"/>
+ </layoutGuides>
+ <view key="view" contentMode="scaleToFill" id="3se-qz-xqx" customClass="MTKView">
+ <rect key="frame" x="0.0" y="0.0" width="1920" height="1080"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+ </view>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="SZV-WD-TEh" sceneMemberID="firstResponder"/>
+ </objects>
+ </scene>
+ </scenes>
+</document>
diff --git a/tests/projects/objc/metal_app/Application/tvOS/Info.plist b/tests/projects/objc/metal_app/Application/tvOS/Info.plist
new file mode 100644
index 000000000..63dcd6c1d
--- /dev/null
+++ b/tests/projects/objc/metal_app/Application/tvOS/Info.plist
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>$(PRODUCT_NAME)</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+ <key>LSRequiresIPhoneOS</key>
+ <true/>
+ <key>UIMainStoryboardFile</key>
+ <string>Main</string>
+ <key>UIRequiredDeviceCapabilities</key>
+ <array>
+ <string>arm64</string>
+ </array>
+ <key>UIUserInterfaceStyle</key>
+ <string>Automatic</string>
+</dict>
+</plist>
diff --git a/tests/projects/objc/metal_app/Configuration/SampleCode.xcconfig b/tests/projects/objc/metal_app/Configuration/SampleCode.xcconfig
new file mode 100644
index 000000000..db86c0698
--- /dev/null
+++ b/tests/projects/objc/metal_app/Configuration/SampleCode.xcconfig
@@ -0,0 +1,13 @@
+//
+// See LICENSE folder for this sample’s licensing information.
+//
+// SampleCode.xcconfig
+//
+
+// The `SAMPLE_CODE_DISAMBIGUATOR` configuration is to make it easier to build
+// and run a sample code project. Once you set your project's development team,
+// you'll have a unique bundle identifier. This is because the bundle identifier
+// is derived based on the 'SAMPLE_CODE_DISAMBIGUATOR' value. Do not use this
+// approach in your own projects—it's only useful for sample code projects because
+// they are frequently downloaded and don't have a development team set.
+SAMPLE_CODE_DISAMBIGUATOR=${DEVELOPMENT_TEAM}
diff --git a/tests/projects/objc/metal_app/HelloTriangle.xcodeproj/.xcodesamplecode.plist b/tests/projects/objc/metal_app/HelloTriangle.xcodeproj/.xcodesamplecode.plist
new file mode 100644
index 000000000..5dd5da85f
--- /dev/null
+++ b/tests/projects/objc/metal_app/HelloTriangle.xcodeproj/.xcodesamplecode.plist
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<array/>
+</plist>
diff --git a/tests/projects/objc/metal_app/HelloTriangle.xcodeproj/project.pbxproj b/tests/projects/objc/metal_app/HelloTriangle.xcodeproj/project.pbxproj
new file mode 100644
index 000000000..f1820eccc
--- /dev/null
+++ b/tests/projects/objc/metal_app/HelloTriangle.xcodeproj/project.pbxproj
@@ -0,0 +1,681 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 3A05CBE01F731ABB00CA21B1 /* MetalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A1F1B411F0338CF001622B3 /* MetalKit.framework */; };
+ 3A05CBE11F731AC000CA21B1 /* MetalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A05CBDE1F731AB800CA21B1 /* MetalKit.framework */; };
+ 3A1F1B391F033765001622B3 /* AAPLViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A35329C1E99974500C194AD /* AAPLViewController.m */; };
+ 3A1F1B3D1F033827001622B3 /* AAPLViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A35329C1E99974500C194AD /* AAPLViewController.m */; };
+ 3A1F1B3E1F033846001622B3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A3532931E99974500C194AD /* main.m */; };
+ 3A1F1B3F1F033846001622B3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A3532931E99974500C194AD /* main.m */; };
+ 3A1F1B441F033EF3001622B3 /* AAPLAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 3ACD21341EAE60D2000D1DED /* AAPLAppDelegate.m */; };
+ 3A3532941E99974500C194AD /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A3532931E99974500C194AD /* main.m */; };
+ 3A35329D1E99974500C194AD /* AAPLViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A35329C1E99974500C194AD /* AAPLViewController.m */; };
+ 3A3532A01E99974500C194AD /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3A35329E1E99974500C194AD /* Main.storyboard */; };
+ 3A3532A31E99974500C194AD /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3A3532A11E99974500C194AD /* LaunchScreen.storyboard */; };
+ 3A3532B91E99974500C194AD /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3A3532B71E99974500C194AD /* Main.storyboard */; };
+ 3A3532CC1E99974500C194AD /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3A3532CA1E99974500C194AD /* Main.storyboard */; };
+ 3A3532CE1E99974500C194AD /* AAPLRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A3532851E99974500C194AD /* AAPLRenderer.m */; };
+ 3A3532CF1E99974500C194AD /* AAPLRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A3532851E99974500C194AD /* AAPLRenderer.m */; };
+ 3A3532D01E99974500C194AD /* AAPLRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A3532851E99974500C194AD /* AAPLRenderer.m */; };
+ 3ACD21351EAE60D2000D1DED /* AAPLAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 3ACD21341EAE60D2000D1DED /* AAPLAppDelegate.m */; };
+ 63E77A181ED2059A00E1E542 /* AAPLShaders.metal in Sources */ = {isa = PBXBuildFile; fileRef = 3A3532871E99974500C194AD /* AAPLShaders.metal */; };
+ 63E77A191ED2059E00E1E542 /* AAPLShaders.metal in Sources */ = {isa = PBXBuildFile; fileRef = 3A3532871E99974500C194AD /* AAPLShaders.metal */; };
+ 63E77A1A1ED205A200E1E542 /* AAPLShaders.metal in Sources */ = {isa = PBXBuildFile; fileRef = 3A3532871E99974500C194AD /* AAPLShaders.metal */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ 328A8E964EC41C65EC8AF01A /* LICENSE.txt */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = LICENSE.txt; sourceTree = "<group>"; };
+ 3A05CBDE1F731AB800CA21B1 /* MetalKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MetalKit.framework; path = ../../../../iPhoneOS.platform/Developer/SDKs/iPhoneOS11.0.sdk/System/Library/Frameworks/MetalKit.framework; sourceTree = SDKROOT; };
+ 3A1F1B411F0338CF001622B3 /* MetalKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MetalKit.framework; path = ../../../../AppleTVOS.platform/Developer/SDKs/AppleTVOS11.0.sdk/System/Library/Frameworks/MetalKit.framework; sourceTree = SDKROOT; };
+ 3A3532841E99974500C194AD /* AAPLRenderer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AAPLRenderer.h; sourceTree = "<group>"; };
+ 3A3532851E99974500C194AD /* AAPLRenderer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AAPLRenderer.m; sourceTree = "<group>"; };
+ 3A3532861E99974500C194AD /* AAPLShaderTypes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AAPLShaderTypes.h; sourceTree = "<group>"; };
+ 3A3532871E99974500C194AD /* AAPLShaders.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = AAPLShaders.metal; sourceTree = "<group>"; };
+ 3A35328F1E99974500C194AD /* HelloTriangle.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HelloTriangle.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 3A3532931E99974500C194AD /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+ 3A35329B1E99974500C194AD /* AAPLViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AAPLViewController.h; sourceTree = "<group>"; };
+ 3A35329C1E99974500C194AD /* AAPLViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AAPLViewController.m; sourceTree = "<group>"; };
+ 3A35329F1E99974500C194AD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+ 3A3532A21E99974500C194AD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
+ 3A3532A41E99974500C194AD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ 3A3532A91E99974500C194AD /* HelloTriangle.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HelloTriangle.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 3A3532B81E99974500C194AD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+ 3A3532BA1E99974500C194AD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ 3A3532BF1E99974500C194AD /* HelloTriangle.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HelloTriangle.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 3A3532CB1E99974500C194AD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+ 3A3532CD1E99974500C194AD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ 3ACD21331EAE60D2000D1DED /* AAPLAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AAPLAppDelegate.h; sourceTree = "<group>"; };
+ 3ACD21341EAE60D2000D1DED /* AAPLAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AAPLAppDelegate.m; sourceTree = "<group>"; };
+ 3ED283CC1EC2C6D200A23F58 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
+ 907FE32A28789CDAFD777257 /* SampleCode.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = SampleCode.xcconfig; path = Configuration/SampleCode.xcconfig; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 3A35328C1E99974500C194AD /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 3A05CBE11F731AC000CA21B1 /* MetalKit.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 3A3532A61E99974500C194AD /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 3A05CBE01F731ABB00CA21B1 /* MetalKit.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 3A3532BC1E99974500C194AD /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 3A1F1B351F0336A8001622B3 /* iOS */ = {
+ isa = PBXGroup;
+ children = (
+ 3A35329E1E99974500C194AD /* Main.storyboard */,
+ 3A3532A11E99974500C194AD /* LaunchScreen.storyboard */,
+ 3A3532A41E99974500C194AD /* Info.plist */,
+ );
+ path = iOS;
+ sourceTree = "<group>";
+ };
+ 3A1F1B361F0336DB001622B3 /* macOS */ = {
+ isa = PBXGroup;
+ children = (
+ 3A3532CA1E99974500C194AD /* Main.storyboard */,
+ 3A3532CD1E99974500C194AD /* Info.plist */,
+ );
+ path = macOS;
+ sourceTree = "<group>";
+ };
+ 3A1F1B371F0336F4001622B3 /* tvOS */ = {
+ isa = PBXGroup;
+ children = (
+ 3A3532B71E99974500C194AD /* Main.storyboard */,
+ 3A3532BA1E99974500C194AD /* Info.plist */,
+ );
+ path = tvOS;
+ sourceTree = "<group>";
+ };
+ 3A35327E1E99974500C194AD = {
+ isa = PBXGroup;
+ children = (
+ 3ED283CC1EC2C6D200A23F58 /* README.md */,
+ 3A3532831E99974500C194AD /* Renderer */,
+ 3A3532911E99974500C194AD /* Application */,
+ 3AE289861EEA0A9100DF4C9A /* Frameworks */,
+ 3A3532901E99974500C194AD /* Products */,
+ F4F7FAFBBA6575359FC81F34 /* Configuration */,
+ 9772A968083C4C14D4BE84E1 /* LICENSE */,
+ );
+ sourceTree = "<group>";
+ };
+ 3A3532831E99974500C194AD /* Renderer */ = {
+ isa = PBXGroup;
+ children = (
+ 3A3532841E99974500C194AD /* AAPLRenderer.h */,
+ 3A3532851E99974500C194AD /* AAPLRenderer.m */,
+ 3A3532861E99974500C194AD /* AAPLShaderTypes.h */,
+ 3A3532871E99974500C194AD /* AAPLShaders.metal */,
+ );
+ path = Renderer;
+ sourceTree = "<group>";
+ };
+ 3A3532901E99974500C194AD /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 3A35328F1E99974500C194AD /* HelloTriangle.app */,
+ 3A3532A91E99974500C194AD /* HelloTriangle.app */,
+ 3A3532BF1E99974500C194AD /* HelloTriangle.app */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 3A3532911E99974500C194AD /* Application */ = {
+ isa = PBXGroup;
+ children = (
+ 3ACD21331EAE60D2000D1DED /* AAPLAppDelegate.h */,
+ 3ACD21341EAE60D2000D1DED /* AAPLAppDelegate.m */,
+ 3A35329B1E99974500C194AD /* AAPLViewController.h */,
+ 3A35329C1E99974500C194AD /* AAPLViewController.m */,
+ 3A3532931E99974500C194AD /* main.m */,
+ 3A1F1B361F0336DB001622B3 /* macOS */,
+ 3A1F1B371F0336F4001622B3 /* tvOS */,
+ 3A1F1B351F0336A8001622B3 /* iOS */,
+ );
+ path = Application;
+ sourceTree = "<group>";
+ };
+ 3AE289861EEA0A9100DF4C9A /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 3A05CBDE1F731AB800CA21B1 /* MetalKit.framework */,
+ 3A1F1B411F0338CF001622B3 /* MetalKit.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+ 9772A968083C4C14D4BE84E1 /* LICENSE */ = {
+ isa = PBXGroup;
+ children = (
+ 328A8E964EC41C65EC8AF01A /* LICENSE.txt */,
+ );
+ path = LICENSE;
+ sourceTree = "<group>";
+ };
+ F4F7FAFBBA6575359FC81F34 /* Configuration */ = {
+ isa = PBXGroup;
+ children = (
+ 907FE32A28789CDAFD777257 /* SampleCode.xcconfig */,
+ );
+ name = Configuration;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 3A35328E1E99974500C194AD /* HelloTriangle-iOS */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 3A3532E21E99974500C194AD /* Build configuration list for PBXNativeTarget "HelloTriangle-iOS" */;
+ buildPhases = (
+ 3A35328B1E99974500C194AD /* Sources */,
+ 3A35328C1E99974500C194AD /* Frameworks */,
+ 3A35328D1E99974500C194AD /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = "HelloTriangle-iOS";
+ productName = iOS;
+ productReference = 3A35328F1E99974500C194AD /* HelloTriangle.app */;
+ productType = "com.apple.product-type.application";
+ };
+ 3A3532A81E99974500C194AD /* HelloTriangle-tvOS */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 3A3532E51E99974500C194AD /* Build configuration list for PBXNativeTarget "HelloTriangle-tvOS" */;
+ buildPhases = (
+ 3A3532A51E99974500C194AD /* Sources */,
+ 3A3532A61E99974500C194AD /* Frameworks */,
+ 3A3532A71E99974500C194AD /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = "HelloTriangle-tvOS";
+ productName = tvOS;
+ productReference = 3A3532A91E99974500C194AD /* HelloTriangle.app */;
+ productType = "com.apple.product-type.application";
+ };
+ 3A3532BE1E99974500C194AD /* HelloTriangle-macOS */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 3A3532E81E99974500C194AD /* Build configuration list for PBXNativeTarget "HelloTriangle-macOS" */;
+ buildPhases = (
+ 3A3532BB1E99974500C194AD /* Sources */,
+ 3A3532BC1E99974500C194AD /* Frameworks */,
+ 3A3532BD1E99974500C194AD /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = "HelloTriangle-macOS";
+ productName = macOS;
+ productReference = 3A3532BF1E99974500C194AD /* HelloTriangle.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 3A35327F1E99974500C194AD /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 1200;
+ ORGANIZATIONNAME = Apple;
+ TargetAttributes = {
+ 3A35328E1E99974500C194AD = {
+ CreatedOnToolsVersion = 8.3;
+ ProvisioningStyle = Automatic;
+ };
+ 3A3532A81E99974500C194AD = {
+ CreatedOnToolsVersion = 8.3;
+ ProvisioningStyle = Automatic;
+ };
+ 3A3532BE1E99974500C194AD = {
+ CreatedOnToolsVersion = 8.3;
+ DevelopmentTeam = 43AAQM58X3;
+ ProvisioningStyle = Automatic;
+ };
+ };
+ };
+ buildConfigurationList = 3A3532821E99974500C194AD /* Build configuration list for PBXProject "HelloTriangle" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 3A35327E1E99974500C194AD;
+ productRefGroup = 3A3532901E99974500C194AD /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 3A3532BE1E99974500C194AD /* HelloTriangle-macOS */,
+ 3A35328E1E99974500C194AD /* HelloTriangle-iOS */,
+ 3A3532A81E99974500C194AD /* HelloTriangle-tvOS */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 3A35328D1E99974500C194AD /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 3A3532A01E99974500C194AD /* Main.storyboard in Resources */,
+ 3A3532A31E99974500C194AD /* LaunchScreen.storyboard in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 3A3532A71E99974500C194AD /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 3A3532B91E99974500C194AD /* Main.storyboard in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 3A3532BD1E99974500C194AD /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 3A3532CC1E99974500C194AD /* Main.storyboard in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 3A35328B1E99974500C194AD /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 63E77A181ED2059A00E1E542 /* AAPLShaders.metal in Sources */,
+ 3A35329D1E99974500C194AD /* AAPLViewController.m in Sources */,
+ 3A3532941E99974500C194AD /* main.m in Sources */,
+ 3A3532CE1E99974500C194AD /* AAPLRenderer.m in Sources */,
+ 3ACD21351EAE60D2000D1DED /* AAPLAppDelegate.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 3A3532A51E99974500C194AD /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 63E77A191ED2059E00E1E542 /* AAPLShaders.metal in Sources */,
+ 3A3532CF1E99974500C194AD /* AAPLRenderer.m in Sources */,
+ 3A1F1B391F033765001622B3 /* AAPLViewController.m in Sources */,
+ 3A1F1B3F1F033846001622B3 /* main.m in Sources */,
+ 3A1F1B441F033EF3001622B3 /* AAPLAppDelegate.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 3A3532BB1E99974500C194AD /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 3A1F1B3D1F033827001622B3 /* AAPLViewController.m in Sources */,
+ 63E77A1A1ED205A200E1E542 /* AAPLShaders.metal in Sources */,
+ 3A1F1B3E1F033846001622B3 /* main.m in Sources */,
+ 3A3532D01E99974500C194AD /* AAPLRenderer.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ 3A35329E1E99974500C194AD /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 3A35329F1E99974500C194AD /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "<group>";
+ };
+ 3A3532A11E99974500C194AD /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 3A3532A21E99974500C194AD /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "<group>";
+ };
+ 3A3532B71E99974500C194AD /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 3A3532B81E99974500C194AD /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "<group>";
+ };
+ 3A3532CA1E99974500C194AD /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 3A3532CB1E99974500C194AD /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 3A3532E01E99974500C194AD /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 907FE32A28789CDAFD777257 /* SampleCode.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ PRODUCT_NAME = HelloTriangle;
+ };
+ name = Debug;
+ };
+ 3A3532E11E99974500C194AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 907FE32A28789CDAFD777257 /* SampleCode.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ PRODUCT_NAME = HelloTriangle;
+ };
+ name = Release;
+ };
+ 3A3532E31E99974500C194AD /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 907FE32A28789CDAFD777257 /* SampleCode.xcconfig */;
+ buildSettings = {
+ CODE_SIGN_IDENTITY = "iPhone Developer";
+ DEVELOPMENT_TEAM = "";
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(SDKROOT)/System/Library/Frameworks",
+ );
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "$(inherited)",
+ TARGET_IOS,
+ );
+ INFOPLIST_FILE = Application/iOS/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.example.apple-samplecode.HelloTriangle${SAMPLE_CODE_DISAMBIGUATOR}";
+ PRODUCT_NAME = HelloTriangle;
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 3A3532E41E99974500C194AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 907FE32A28789CDAFD777257 /* SampleCode.xcconfig */;
+ buildSettings = {
+ CODE_SIGN_IDENTITY = "iPhone Developer";
+ DEVELOPMENT_TEAM = "";
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(SDKROOT)/System/Library/Frameworks",
+ );
+ GCC_PREPROCESSOR_DEFINITIONS = TARGET_IOS;
+ INFOPLIST_FILE = Application/iOS/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ MTL_FAST_MATH = YES;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.example.apple-samplecode.HelloTriangle${SAMPLE_CODE_DISAMBIGUATOR}";
+ PRODUCT_NAME = HelloTriangle;
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 3A3532E61E99974500C194AD /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 907FE32A28789CDAFD777257 /* SampleCode.xcconfig */;
+ buildSettings = {
+ CODE_SIGN_IDENTITY = "iPhone Developer";
+ DEVELOPMENT_TEAM = "";
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(SDKROOT)/System/Library/Frameworks",
+ );
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ TARGET_TVOS,
+ "$(inherited)",
+ );
+ INFOPLIST_FILE = Application/tvOS/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.example.apple-samplecode.HelloTriangle${SAMPLE_CODE_DISAMBIGUATOR}";
+ PRODUCT_NAME = HelloTriangle;
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SDKROOT = appletvos;
+ TARGETED_DEVICE_FAMILY = 3;
+ TVOS_DEPLOYMENT_TARGET = 12.0;
+ };
+ name = Debug;
+ };
+ 3A3532E71E99974500C194AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 907FE32A28789CDAFD777257 /* SampleCode.xcconfig */;
+ buildSettings = {
+ CODE_SIGN_IDENTITY = "iPhone Developer";
+ DEVELOPMENT_TEAM = "";
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(SDKROOT)/System/Library/Frameworks",
+ );
+ GCC_PREPROCESSOR_DEFINITIONS = TARGET_TVOS;
+ INFOPLIST_FILE = Application/tvOS/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ MTL_FAST_MATH = YES;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.example.apple-samplecode.HelloTriangle${SAMPLE_CODE_DISAMBIGUATOR}";
+ PRODUCT_NAME = HelloTriangle;
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SDKROOT = appletvos;
+ TARGETED_DEVICE_FAMILY = 3;
+ TVOS_DEPLOYMENT_TARGET = 12.0;
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 3A3532E91E99974500C194AD /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 907FE32A28789CDAFD777257 /* SampleCode.xcconfig */;
+ buildSettings = {
+ CODE_SIGN_IDENTITY = "Mac Developer";
+ COMBINE_HIDPI_IMAGES = YES;
+ DEVELOPMENT_TEAM = 43AAQM58X3;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "$(inherited)",
+ TARGET_MACOS,
+ );
+ INFOPLIST_FILE = Application/macOS/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
+ MACOSX_DEPLOYMENT_TARGET = 10.12;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.example.apple-samplecode.$(PRODUCT_NAME)${SAMPLE_CODE_DISAMBIGUATOR}";
+ PRODUCT_NAME = HelloTriangle;
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SDKROOT = macosx;
+ };
+ name = Debug;
+ };
+ 3A3532EA1E99974500C194AD /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 907FE32A28789CDAFD777257 /* SampleCode.xcconfig */;
+ buildSettings = {
+ CODE_SIGN_IDENTITY = "Mac Developer";
+ COMBINE_HIDPI_IMAGES = YES;
+ DEVELOPMENT_TEAM = 43AAQM58X3;
+ GCC_PREPROCESSOR_DEFINITIONS = TARGET_MACOS;
+ INFOPLIST_FILE = Application/macOS/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
+ MACOSX_DEPLOYMENT_TARGET = 10.12;
+ MTL_FAST_MATH = YES;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.example.apple-samplecode.$(PRODUCT_NAME)${SAMPLE_CODE_DISAMBIGUATOR}";
+ PRODUCT_NAME = HelloTriangle;
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SDKROOT = macosx;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 3A3532821E99974500C194AD /* Build configuration list for PBXProject "HelloTriangle" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 3A3532E01E99974500C194AD /* Debug */,
+ 3A3532E11E99974500C194AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 3A3532E21E99974500C194AD /* Build configuration list for PBXNativeTarget "HelloTriangle-iOS" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 3A3532E31E99974500C194AD /* Debug */,
+ 3A3532E41E99974500C194AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 3A3532E51E99974500C194AD /* Build configuration list for PBXNativeTarget "HelloTriangle-tvOS" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 3A3532E61E99974500C194AD /* Debug */,
+ 3A3532E71E99974500C194AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 3A3532E81E99974500C194AD /* Build configuration list for PBXNativeTarget "HelloTriangle-macOS" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 3A3532E91E99974500C194AD /* Debug */,
+ 3A3532EA1E99974500C194AD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 3A35327F1E99974500C194AD /* Project object */;
+}
diff --git a/tests/projects/objc/metal_app/HelloTriangle.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/tests/projects/objc/metal_app/HelloTriangle.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 000000000..3ddf867a1
--- /dev/null
+++ b/tests/projects/objc/metal_app/HelloTriangle.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>BuildSystemType</key>
+ <string>Latest</string>
+</dict>
+</plist>
diff --git a/tests/projects/objc/metal_app/LICENSE/LICENSE.txt b/tests/projects/objc/metal_app/LICENSE/LICENSE.txt
new file mode 100644
index 000000000..e11438f78
--- /dev/null
+++ b/tests/projects/objc/metal_app/LICENSE/LICENSE.txt
@@ -0,0 +1,8 @@
+Copyright © 2020 Apple Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
diff --git a/tests/projects/objc/metal_app/README.md b/tests/projects/objc/metal_app/README.md
new file mode 100644
index 000000000..050a1611b
--- /dev/null
+++ b/tests/projects/objc/metal_app/README.md
@@ -0,0 +1,332 @@
+# Using a Render Pipeline to Render Primitives
+
+Render a simple 2D triangle.
+
+## Overview
+
+In [Using Metal to Draw a View’s Contents](https://developer.apple.com/documentation/metal/basic_tasks_and_concepts/using_metal_to_draw_a_view_s_contents), you learned how to set up an `MTKView` object and to change the view's contents using a render pass.
+That sample simply erased the view's contents to a background color.
+This sample shows you how to configure a render pipeline and use it as part of the render pass to draw a simple 2D colored triangle into the view.
+The sample supplies a position and color for each vertex, and the render pipeline uses that data to render the triangle, interpolating color values between the colors specified for the triangle's vertices.
+
+![Simple 2D Triangle Vertices](Documentation/2DTriangleVertices.png)
+
+The Xcode project contains schemes for running the sample on macOS, iOS, and tvOS.
+
+## Understand the Metal Render Pipeline
+
+A *render pipeline* processes drawing commands and writes data into a render pass’s targets.
+ A render pipeline has many stages, some programmed using shaders and others with fixed or configurable behavior.
+This sample focuses on the three main stages of the pipeline: the vertex stage, the rasterization stage, and the fragment stage.
+The vertex stage and fragment stage are programmable, so you write functions for them in Metal Shading Language (MSL).
+The rasterization stage has fixed behavior.
+
+**Figure 1** Main stages of the Metal graphics render pipeline
+![Main Stages of the Metal Graphics Render Pipeline](Documentation/SimplePipeline.png)
+
+Rendering starts with a drawing command, which includes a vertex count and what kind of primitive to render. For example, here's the drawing command from this sample:
+
+``` objective-c
+// Draw the triangle.
+[renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle
+ vertexStart:0
+ vertexCount:3];
+```
+
+The vertex stage provides data for each vertex. When enough vertices have been processed, the render pipeline rasterizes the primitive, determining which pixels in the render targets lie within the boundaries of the primitive. The fragment stage determines the values to write into the render targets for those pixels.
+
+In the rest of this sample, you will see how to write the vertex and fragment functions, how to create the render pipeline state object, and finally, how to encode a draw command that uses this pipeline.
+
+## Decide How Data is Processed by Your Custom Render Pipeline
+
+A vertex function generates data for a single vertex and a fragment function generates data for a single fragment, but you decide how they work.
+You configure the stages of the pipeline with a goal in mind, meaning that you know what you want the pipeline to generate and how it generates those results.
+
+Decide what data to pass into your render pipeline and what data is passed to later stages of the pipeline. There are typically three places where you do this:
+
+- The inputs to the pipeline, which are provided by your app and passed to the vertex stage.
+- The outputs of the vertex stage, which is passed to the rasterization stage.
+- The inputs to the fragment stage, which are provided by your app or generated by the rasterization stage.
+
+In this sample, the input data for the pipeline is the position of a vertex and its color. To demonstrate the kind of transformation you typically perform in a vertex function, input coordinates are defined in a custom coordinate space, measured in pixels from the center of the view. These coordinates need to be translated into Metal's coordinate system.
+
+Declare an `AAPLVertex` structure, using SIMD vector types to hold the position and color data.
+To share a single definition for how the structure is laid out in memory, declare the structure in a common header and import it in both the Metal shader and the app.
+
+``` objective-c
+typedef struct
+{
+ vector_float2 position;
+ vector_float4 color;
+} AAPLVertex;
+```
+
+SIMD types are commonplace in Metal Shading Language, and you should also use them in your app using the simd library.
+SIMD types contain multiple channels of a particular data type, so declaring the position as a `vector_float2` means it contains two 32-bit float values (which will hold the x and y coordinates.)
+Colors are stored using a `vector_float4`, so they have four channels – red, green, blue, and alpha.
+
+In the app, the input data is specified using a constant array:
+
+``` objective-c
+static const AAPLVertex triangleVertices[] =
+{
+ // 2D positions, RGBA colors
+ { { 250, -250 }, { 1, 0, 0, 1 } },
+ { { -250, -250 }, { 0, 1, 0, 1 } },
+ { { 0, 250 }, { 0, 0, 1, 1 } },
+};
+```
+
+The vertex stage generates data for a vertex, so it needs to provide a color and a transformed position.
+Declare a `RasterizerData` structure containing a position and a color value, again using SIMD types.
+
+``` metal
+struct RasterizerData
+{
+ // The [[position]] attribute of this member indicates that this value
+ // is the clip space position of the vertex when this structure is
+ // returned from the vertex function.
+ float4 position [[position]];
+
+ // Since this member does not have a special attribute, the rasterizer
+ // interpolates its value with the values of the other triangle vertices
+ // and then passes the interpolated value to the fragment shader for each
+ // fragment in the triangle.
+ float4 color;
+};
+```
+
+The output position (described in detail below) must be defined as a `vector_float4`. The color is declared as it was in the input data structure.
+
+You need to tell Metal which field in the rasterization data provides position data, because Metal doesn't enforce any particular naming convention for fields in your struct.
+Annotate the `position` field with the `[[position]]` attribute qualifier to declare that this field holds the output position.
+
+The fragment function simply passes the rasterization stage's data to later stages so it doesn't need any additional arguments.
+
+## Declare the Vertex Function
+
+Declare the vertex function, including its input arguments and the data it outputs.
+Much like compute functions were declared using the `kernel` keyword, you declare a vertex function using the `vertex` keyword.
+
+``` metal
+vertex RasterizerData
+vertexShader(uint vertexID [[vertex_id]],
+ constant AAPLVertex *vertices [[buffer(AAPLVertexInputIndexVertices)]],
+ constant vector_uint2 *viewportSizePointer [[buffer(AAPLVertexInputIndexViewportSize)]])
+```
+
+The first argument, `vertexID`, uses the `[[vertex_id]]` attribute qualifier, which is another Metal keyword.
+When you execute a render command, the GPU calls your vertex function multiple times, generating a unique value for each vertex.
+
+The second argument, `vertices`, is an array that contains the vertex data, using the `AAPLVertex` struct previously defined.
+
+To transform the position into Metal's coordinates, the function needs the size of the viewport (in pixels) that the triangle is being drawn into, so this is stored in the `viewportSizePointer` argument.
+
+The second and third arguments have the `[[buffer(n)]]` attribute qualifier.
+By default, Metal assigns slots in the argument table for each parameter automatically.
+When you add the `[[buffer(n)]]` qualifier to a buffer argument, you tell Metal explicitly which slot to use.
+Declaring slots explicitly can make it easier to revise your shaders without also needing to change your app code.
+Declare the constants for the two indicies in the shared header file.
+
+The function's output is a `RasterizerData` struct.
+
+## Write the Vertex Function
+
+Your vertex function must generate both fields of the output struct.
+Use the `vertexID` argument to index into the `vertices` array and read the input data for the vertex.
+Also, retrieve the viewport dimensions.
+
+``` metal
+float2 pixelSpacePosition = vertices[vertexID].position.xy;
+
+// Get the viewport size and cast to float.
+vector_float2 viewportSize = vector_float2(*viewportSizePointer);
+
+```
+
+Vertex functions must provide position data in *clip-space coordinates*, which are 3D points specified using a four-dimensional homogenous vector (`x,y,z,w`). The rasterization stage takes the output position and divides the `x`,`y`, and `z` coordinates by `w` to generate a 3D point in *normalized device coordinates*. Normalized device coordinates are independent of viewport size.
+
+**Figure 2** Normalized device coordinate system
+![Normalized device coordinate system](Documentation/normalizeddevicecoords.png)
+
+Normalized device coordinates use a *left-handed coordinate system* and map to positions in the viewport.
+Primitives are clipped to a box in this coordinate system and then rasterized.
+The lower-left corner of the clipping box is at an `(x,y)` coordinate of `(-1.0,-1.0)` and the upper-right corner is at `(1.0,1.0)`.
+Positive-z values point away from the camera (into the screen.)
+The visible portion of the `z` coordinate is between `0.0` (the near clipping plane) and `1.0` (the far clipping plane).
+
+Transform the input coordinate system to the normalized device coordinate system.
+
+![Vertex function coordinate transformation](Documentation/metal-coordinate-transformation.png)
+
+Because this is a 2D application and does not need homogenous coordinates, first write a default value to the output coordinate, with the the `w` value is set to `1.0` and the other coordinates set to `0.0`.
+This means that the coordinates are already in the normalized device coordinate space and the vertex function should generate (x,y) coordinates in that coordinate space.
+Divide the input position by half the viewport size to generate normalized device coordinates.
+Since this calculation is performed using SIMD types, both channels can be divided at the same time using a single line of code.
+Perform the divide and put the results in the x and y channels of the output position.
+
+``` metal
+out.position = vector_float4(0.0, 0.0, 0.0, 1.0);
+out.position.xy = pixelSpacePosition / (viewportSize / 2.0);
+```
+
+Finally, copy the color value into the `out.color` return value.
+
+``` metal
+out.color = vertices[vertexID].color;
+```
+
+## Write a Fragment Function
+
+A *fragment* is a possible change to the render targets. The rasterizer determines which pixels of the render target are covered by the primitive.
+Only fragments whose pixel centers are inside the triangle are rendered.
+
+**Figure 3** Fragments generated by the rasterization stage
+![Fragments generated by the rasterization stage](Documentation/Rasterization.png)
+
+A fragment function processes incoming information from the rasterizer for a single position and calculates output values for each of the render targets. These fragment values are processed by later stages in the pipeline, eventually being written to the render targets.
+
+- Note: The reason a fragment is called a possible change is because the pipeline stages after the fragment stage can be configured to reject some fragments or change what gets written to the render targets. In this sample, all values calculated by the fragment stage are written as-is to the render target.
+
+The fragment shader in this sample receives the same parameters that were declared in the vertex shader's output. Declare the fragment function using the `fragment` keyword. It takes a single argument, the same `RasterizerData` structure that was provided by the vertex stage. Add the `[[stage_in]]` attribute qualifier to indicate that this argument is generated by the rasterizer.
+
+``` metal
+fragment float4 fragmentShader(RasterizerData in [[stage_in]])
+```
+
+If your fragment function writes to multiple render targets, it must declare a struct with fields for each render target.
+Because this sample only has a single render target, you specify a floating-point vector directly as the function's output. This output is the color to be written to the render target.
+
+The rasterization stage calculates values for each fragment's arguments and calls the fragment function with them.
+The rasterization stage calculates its color argument as a blend of the colors at the triangle's vertices.
+The closer a fragment is to a vertex, the more that vertex contributes to the final color.
+
+**Figure 4** Interpolated fragment colors
+![Interpolated Fragment Colors](Documentation/Interpolation.png)
+
+Return the interpolated color as the function's output.
+
+``` metal
+return in.color;
+```
+
+## Create a Render Pipeline State Object
+
+Now that the functions are complete, you can create a render pipeline that uses them.
+First, get the default library and obtain a [`MTLFunction`][MTLFunction] object for each function.
+
+``` objective-c
+id<MTLLibrary> defaultLibrary = [_device newDefaultLibrary];
+
+id<MTLFunction> vertexFunction = [defaultLibrary newFunctionWithName:@"vertexShader"];
+id<MTLFunction> fragmentFunction = [defaultLibrary newFunctionWithName:@"fragmentShader"];
+```
+
+Next, create a [`MTLRenderPipelineState`][MTLRenderPipelineState] object.
+Render pipelines have more stages to configure, so you use a [`MTLRenderPipelineDescriptor`][MTLRenderPipelineDescriptor] to configure the pipeline.
+
+``` objective-c
+MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
+pipelineStateDescriptor.label = @"Simple Pipeline";
+pipelineStateDescriptor.vertexFunction = vertexFunction;
+pipelineStateDescriptor.fragmentFunction = fragmentFunction;
+pipelineStateDescriptor.colorAttachments[0].pixelFormat = mtkView.colorPixelFormat;
+
+_pipelineState = [_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor
+ error:&error];
+```
+
+In addition to specifying the vertex and fragment functions, you also declare the *pixel format* of all render targets that the pipeline will draw into.
+A pixel format ([`MTLPixelFormat`][MTLPixelFormat]) defines the memory layout of pixel data.
+For simple formats, this definition includes the number of bytes per pixel, the number of channels of data stored in a pixel, and the bit layout of those channels.
+Since this sample only has one render target and it is provided by the view, copy the view's pixel format into the render pipeline descriptor.
+Your render pipeline state must use a pixel format that is compatible with the one specified by the render pass.
+In this sample, the render pass and the pipeline state object both use the view's pixel format, so they are always the same.
+
+When Metal creates the render pipeline state object, the pipeline is configured to convert the fragment function's output into the render target's pixel format.
+If you want to target a different pixel format, you need to create a different pipeline state object.
+You can reuse the same shaders in multiple pipelines targeting different pixel formats.
+
+
+## Set a Viewport
+
+Now that you have the render pipeline state object for the pipeline, you'll render the triangle. You do this using a render command encoder. First, set the viewport, so that Metal knows which part of the render target you want to draw into.
+
+``` objective-c
+// Set the region of the drawable to draw into.
+[renderEncoder setViewport:(MTLViewport){0.0, 0.0, _viewportSize.x, _viewportSize.y, 0.0, 1.0 }];
+```
+
+## Set the Render Pipeline State
+
+Set the render pipeline state for the pipeline you want to use.
+
+``` objective-c
+[renderEncoder setRenderPipelineState:_pipelineState];
+```
+
+## Send Argument Data to the Vertex Function
+
+Often, you use buffers ([`MTLBuffer`][MTLBuffer]) to pass data to shaders.
+However, when you need to pass only a small amount of data to the vertex function, as is the case here, copy the data directly into the command buffer.
+
+The sample copies data for both parameters into the command buffer.
+The vertex data is copied from an array defined in the sample.
+The viewport data is copied from the same variable that you used to set the viewport.
+
+In this sample, the fragment function uses only the data it receives from the rasterizer, so there are no arguments to set.
+
+``` objective-c
+[renderEncoder setVertexBytes:triangleVertices
+ length:sizeof(triangleVertices)
+ atIndex:AAPLVertexInputIndexVertices];
+
+[renderEncoder setVertexBytes:&_viewportSize
+ length:sizeof(_viewportSize)
+ atIndex:AAPLVertexInputIndexViewportSize];
+```
+
+
+## Encode the Drawing Command
+
+Specify the kind of primitive, the starting index, and the number of vertices.
+When the triangle is rendered, the vertex function is called with values of 0, 1, and 2 for the `vertexID` argument.
+
+``` objective-c
+// Draw the triangle.
+[renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle
+ vertexStart:0
+ vertexCount:3];
+```
+
+As with Drawing to the Screen Using Metal, you end the encoding process and commit the command buffer.
+However, you could encode more render commands using the same set of steps.
+The final image is rendered as if the commands were processed in the order they were specified.
+(For performance, the GPU is allowed to process commands or even parts of commands in parallel, so long as the final result appears to have been rendered in order. )
+
+## Experiment with the Color Interpolation
+
+In this sample, color values were interpolated across the triangle.
+That's often what you want, but sometimes you want a value to be generated by one vertex and remain constant across the whole primitive.
+Specify the `flat` attribute qualifier on an output of the vertex function to do this.
+Try this now.
+Find the definition of `RasterizerData` in the sample project and add the `[[flat]]` qualifier to its `color` field.
+
+`float4 color [[flat]];`
+
+Run the sample again.
+The render pipeline uses the color value from the first vertex (called the *provoking vertex*) uniformly across the triangle, and it ignores the colors from the other two vertices.
+You can use a mix of flat shaded and interpolated values, simply by adding or omitting the `flat` qualifier on your vertex function's outputs.
+The [Metal Shading Language specification][ShadingLanguageSpec] defines other attribute qualifiers you can also use to modify the rasterization behavior.
+
+[ScreenDrawing]: https://developer.apple.com/documentation/metal
+[MTLDevice]: https://developer.apple.com/documentation/metal/mtldevice
+[MTLResource]: https://developer.apple.com/documentation/metal/mtlresource
+[MTLBuffer]: https://developer.apple.com/documentation/metal/mtlbuffer
+[MTLRenderPipelineState]: https://developer.apple.com/documentation/metal/mtlrenderpipelinestate
+[MTLRenderPipelineDescriptor]: https://developer.apple.com/documentation/metal/mtlrenderpipelinedescriptor
+[MTLRenderCommandEncoder]: https://developer.apple.com/documentation/metal/mtlrendercommandencoder
+[MTLPixelFormat]: https://developer.apple.com/documentation/metal/mtlpixelformat
+[MTKView]: https://developer.apple.com/documentation/metalkit/mtkview
+[ShadingLanguageSpec]: https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf
+[MTLFunction]: https://developer.apple.com/documentation/metal/mtlfunction
diff --git a/tests/projects/objc/metal_app/Renderer/AAPLRenderer.h b/tests/projects/objc/metal_app/Renderer/AAPLRenderer.h
new file mode 100644
index 000000000..f7bcba51d
--- /dev/null
+++ b/tests/projects/objc/metal_app/Renderer/AAPLRenderer.h
@@ -0,0 +1,14 @@
+/*
+See LICENSE folder for this sample’s licensing information.
+
+Abstract:
+Header for a platform independent renderer class, which performs Metal setup and per frame rendering.
+*/
+
+@import MetalKit;
+
+@interface AAPLRenderer : NSObject<MTKViewDelegate>
+
+- (nonnull instancetype)initWithMetalKitView:(nonnull MTKView *)mtkView;
+
+@end
diff --git a/tests/projects/objc/metal_app/Renderer/AAPLRenderer.m b/tests/projects/objc/metal_app/Renderer/AAPLRenderer.m
new file mode 100644
index 000000000..124bd29ff
--- /dev/null
+++ b/tests/projects/objc/metal_app/Renderer/AAPLRenderer.m
@@ -0,0 +1,132 @@
+/*
+See LICENSE folder for this sample’s licensing information.
+
+Abstract:
+Implementation of a platform independent renderer class, which performs Metal setup and per frame rendering
+*/
+
+@import simd;
+@import MetalKit;
+
+#import "AAPLRenderer.h"
+
+// Header shared between C code here, which executes Metal API commands, and .metal files, which
+// uses these types as inputs to the shaders.
+#import "AAPLShaderTypes.h"
+
+// Main class performing the rendering
+@implementation AAPLRenderer
+{
+ id<MTLDevice> _device;
+
+ // The render pipeline generated from the vertex and fragment shaders in the .metal shader file.
+ id<MTLRenderPipelineState> _pipelineState;
+
+ // The command queue used to pass commands to the device.
+ id<MTLCommandQueue> _commandQueue;
+
+ // The current size of the view, used as an input to the vertex shader.
+ vector_uint2 _viewportSize;
+}
+
+- (nonnull instancetype)initWithMetalKitView:(nonnull MTKView *)mtkView
+{
+ self = [super init];
+ if(self)
+ {
+ NSError *error;
+
+ _device = mtkView.device;
+
+ // Load all the shader files with a .metal file extension in the project.
+ id<MTLLibrary> defaultLibrary = [_device newDefaultLibrary];
+
+ id<MTLFunction> vertexFunction = [defaultLibrary newFunctionWithName:@"vertexShader"];
+ id<MTLFunction> fragmentFunction = [defaultLibrary newFunctionWithName:@"fragmentShader"];
+
+ // Configure a pipeline descriptor that is used to create a pipeline state.
+ MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
+ pipelineStateDescriptor.label = @"Simple Pipeline";
+ pipelineStateDescriptor.vertexFunction = vertexFunction;
+ pipelineStateDescriptor.fragmentFunction = fragmentFunction;
+ pipelineStateDescriptor.colorAttachments[0].pixelFormat = mtkView.colorPixelFormat;
+
+ _pipelineState = [_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor
+ error:&error];
+
+ // Pipeline State creation could fail if the pipeline descriptor isn't set up properly.
+ // If the Metal API validation is enabled, you can find out more information about what
+ // went wrong. (Metal API validation is enabled by default when a debug build is run
+ // from Xcode.)
+ NSAssert(_pipelineState, @"Failed to create pipeline state: %@", error);
+
+ // Create the command queue
+ _commandQueue = [_device newCommandQueue];
+ }
+
+ return self;
+}
+
+/// Called whenever view changes orientation or is resized
+- (void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size
+{
+ // Save the size of the drawable to pass to the vertex shader.
+ _viewportSize.x = size.width;
+ _viewportSize.y = size.height;
+}
+
+/// Called whenever the view needs to render a frame.
+- (void)drawInMTKView:(nonnull MTKView *)view
+{
+ static const AAPLVertex triangleVertices[] =
+ {
+ // 2D positions, RGBA colors
+ { { 250, -250 }, { 1, 0, 0, 1 } },
+ { { -250, -250 }, { 0, 1, 0, 1 } },
+ { { 0, 250 }, { 0, 0, 1, 1 } },
+ };
+
+ // Create a new command buffer for each render pass to the current drawable.
+ id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
+ commandBuffer.label = @"MyCommand";
+
+ // Obtain a renderPassDescriptor generated from the view's drawable textures.
+ MTLRenderPassDescriptor *renderPassDescriptor = view.currentRenderPassDescriptor;
+
+ if(renderPassDescriptor != nil)
+ {
+ // Create a render command encoder.
+ id<MTLRenderCommandEncoder> renderEncoder =
+ [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
+ renderEncoder.label = @"MyRenderEncoder";
+
+ // Set the region of the drawable to draw into.
+ [renderEncoder setViewport:(MTLViewport){0.0, 0.0, _viewportSize.x, _viewportSize.y, 0.0, 1.0 }];
+
+ [renderEncoder setRenderPipelineState:_pipelineState];
+
+ // Pass in the parameter data.
+ [renderEncoder setVertexBytes:triangleVertices
+ length:sizeof(triangleVertices)
+ atIndex:AAPLVertexInputIndexVertices];
+
+ [renderEncoder setVertexBytes:&_viewportSize
+ length:sizeof(_viewportSize)
+ atIndex:AAPLVertexInputIndexViewportSize];
+
+ // Draw the triangle.
+ [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle
+ vertexStart:0
+ vertexCount:3];
+
+ [renderEncoder endEncoding];
+
+ // Schedule a present once the framebuffer is complete using the current drawable.
+ [commandBuffer presentDrawable:view.currentDrawable];
+ }
+
+ // Finalize rendering here & push the command buffer to the GPU.
+ [commandBuffer commit];
+}
+
+@end
diff --git a/tests/projects/objc/metal_app/Renderer/AAPLShaderTypes.h b/tests/projects/objc/metal_app/Renderer/AAPLShaderTypes.h
new file mode 100644
index 000000000..b3355f911
--- /dev/null
+++ b/tests/projects/objc/metal_app/Renderer/AAPLShaderTypes.h
@@ -0,0 +1,31 @@
+/*
+See LICENSE folder for this sample’s licensing information.
+
+Abstract:
+Header containing types and enum constants shared between Metal shaders and C/ObjC source
+*/
+
+#ifndef AAPLShaderTypes_h
+#define AAPLShaderTypes_h
+
+#include <simd/simd.h>
+
+// Buffer index values shared between shader and C code to ensure Metal shader buffer inputs
+// match Metal API buffer set calls.
+typedef enum AAPLVertexInputIndex
+{
+ AAPLVertexInputIndexVertices = 0,
+ AAPLVertexInputIndexViewportSize = 1,
+} AAPLVertexInputIndex;
+
+// This structure defines the layout of vertices sent to the vertex
+// shader. This header is shared between the .metal shader and C code, to guarantee that
+// the layout of the vertex array in the C code matches the layout that the .metal
+// vertex shader expects.
+typedef struct
+{
+ vector_float2 position;
+ vector_float4 color;
+} AAPLVertex;
+
+#endif /* AAPLShaderTypes_h */
diff --git a/tests/projects/objc/metal_app/Renderer/AAPLShaders.metal b/tests/projects/objc/metal_app/Renderer/AAPLShaders.metal
new file mode 100644
index 000000000..62b05cf71
--- /dev/null
+++ b/tests/projects/objc/metal_app/Renderer/AAPLShaders.metal
@@ -0,0 +1,62 @@
+/*
+See LICENSE folder for this sample’s licensing information.
+
+Abstract:
+Metal shaders used for this sample
+*/
+
+#include <metal_stdlib>
+
+using namespace metal;
+
+// Include header shared between this Metal shader code and C code executing Metal API commands.
+#include "AAPLShaderTypes.h"
+
+// Vertex shader outputs and fragment shader inputs
+struct RasterizerData
+{
+ // The [[position]] attribute of this member indicates that this value
+ // is the clip space position of the vertex when this structure is
+ // returned from the vertex function.
+ float4 position [[position]];
+
+ // Since this member does not have a special attribute, the rasterizer
+ // interpolates its value with the values of the other triangle vertices
+ // and then passes the interpolated value to the fragment shader for each
+ // fragment in the triangle.
+ float4 color;
+};
+
+vertex RasterizerData
+vertexShader(uint vertexID [[vertex_id]],
+ constant AAPLVertex *vertices [[buffer(AAPLVertexInputIndexVertices)]],
+ constant vector_uint2 *viewportSizePointer [[buffer(AAPLVertexInputIndexViewportSize)]])
+{
+ RasterizerData out;
+
+ // Index into the array of positions to get the current vertex.
+ // The positions are specified in pixel dimensions (i.e. a value of 100
+ // is 100 pixels from the origin).
+ float2 pixelSpacePosition = vertices[vertexID].position.xy;
+
+ // Get the viewport size and cast to float.
+ vector_float2 viewportSize = vector_float2(*viewportSizePointer);
+
+
+ // To convert from positions in pixel space to positions in clip-space,
+ // divide the pixel coordinates by half the size of the viewport.
+ out.position = vector_float4(0.0, 0.0, 0.0, 1.0);
+ out.position.xy = pixelSpacePosition / (viewportSize / 2.0);
+
+ // Pass the input color directly to the rasterizer.
+ out.color = vertices[vertexID].color;
+
+ return out;
+}
+
+fragment float4 fragmentShader(RasterizerData in [[stage_in]])
+{
+ // Return the interpolated color.
+ return in.color;
+}
+
diff --git a/tests/projects/objc/metal_app/xmake.lua b/tests/projects/objc/metal_app/xmake.lua
new file mode 100644
index 000000000..54a94b26d
--- /dev/null
+++ b/tests/projects/objc/metal_app/xmake.lua
@@ -0,0 +1,22 @@
+add_rules("mode.debug", "mode.release")
+
+target("HelloTriangle")
+ add_rules("xcode.application")
+ add_includedirs("Renderer")
+ add_frameworks("MetalKit")
+ add_mflags("-fmodules")
+ add_files("Renderer/*.m")
+ if is_plat("macosx") then
+ add_files("Application/main.m")
+ add_files("Application/AAPLViewController.m")
+ add_files("Application/macOS/Info.plist")
+ add_files("Application/macOS/Base.lproj/*.storyboard")
+ add_defines("TARGET_MACOS")
+ add_frameworks("AppKit")
+ elseif is_plat("iphoneos") then
+ add_files("Application/*.m")
+ add_files("Application/iOS/Info.plist")
+ add_files("Application/iOS/Base.lproj/*.storyboard")
+ add_frameworks("UIKit")
+ add_defines("TARGET_IOS")
+ end