aboutsummaryrefslogtreecommitdiff
path: root/cube/macOS/cubepp
diff options
context:
space:
mode:
authorMark Lobodzinski <mark@lunarg.com>2018-04-11 13:38:11 -0600
committerMark Lobodzinski <mark@lunarg.com>2018-05-11 20:10:23 -0600
commit6e590c31d0ceaf3edc717d4e79aacc9afed46f5c (patch)
treeff9cc5efbb622567d2f1bb823e0a5eb179fdc2e3 /cube/macOS/cubepp
parentb37d3d8477db4ea1dc7ae25539b35a0e8e365a7f (diff)
downloadusermoji-6e590c31d0ceaf3edc717d4e79aacc9afed46f5c.tar.xz
cube: Move cube directory up to top level dir
Change-Id: Ie1116b0b6de742b4f85834a4c9d1927dd641e19b
Diffstat (limited to 'cube/macOS/cubepp')
-rw-r--r--cube/macOS/cubepp/AppDelegate.h23
-rw-r--r--cube/macOS/cubepp/AppDelegate.mm39
-rw-r--r--cube/macOS/cubepp/DemoViewController.h33
-rw-r--r--cube/macOS/cubepp/DemoViewController.mm117
-rw-r--r--cube/macOS/cubepp/Info.plist38
-rw-r--r--cube/macOS/cubepp/Resources/LunarGIcon.icnsbin0 -> 54120 bytes
-rw-r--r--cube/macOS/cubepp/Resources/Main.storyboard131
-rw-r--r--cube/macOS/cubepp/cubepp.cmake104
-rw-r--r--cube/macOS/cubepp/main.mm21
9 files changed, 506 insertions, 0 deletions
diff --git a/cube/macOS/cubepp/AppDelegate.h b/cube/macOS/cubepp/AppDelegate.h
new file mode 100644
index 00000000..0b1dfe49
--- /dev/null
+++ b/cube/macOS/cubepp/AppDelegate.h
@@ -0,0 +1,23 @@
+/*
+ * AppDelegate.h
+ *
+ * Copyright (c) 2014-2018 The Brenwill Workshop Ltd. (http://www.brenwill.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+@interface AppDelegate : NSObject<NSApplicationDelegate>
+
+@end
diff --git a/cube/macOS/cubepp/AppDelegate.mm b/cube/macOS/cubepp/AppDelegate.mm
new file mode 100644
index 00000000..2a7bcaff
--- /dev/null
+++ b/cube/macOS/cubepp/AppDelegate.mm
@@ -0,0 +1,39 @@
+/*
+ * AppDelegate.m
+ *
+ * Copyright (c) 2014-2018 The Brenwill Workshop Ltd. (http://www.brenwill.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import "AppDelegate.h"
+
+@interface AppDelegate ()
+
+@end
+
+@implementation AppDelegate
+
+- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
+ // Insert code here to initialize your application
+}
+
+- (void)applicationWillTerminate:(NSNotification *)aNotification {
+ // Insert code here to tear down your application
+}
+
+- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender {
+ return YES;
+}
+
+@end
diff --git a/cube/macOS/cubepp/DemoViewController.h b/cube/macOS/cubepp/DemoViewController.h
new file mode 100644
index 00000000..7f90cc7e
--- /dev/null
+++ b/cube/macOS/cubepp/DemoViewController.h
@@ -0,0 +1,33 @@
+/*
+ * DemoViewController.h
+ *
+ * Copyright (c) 2014-2018 The Brenwill Workshop Ltd. (http://www.brenwill.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import <AppKit/AppKit.h>
+
+#pragma mark -
+#pragma mark DemoViewController
+
+/** The main view controller for the demo storyboard. */
+@interface DemoViewController : NSViewController
+@end
+
+#pragma mark -
+#pragma mark DemoView
+
+/** The Metal-compatibile view for the demo Storyboard. */
+@interface DemoView : NSView
+@end
diff --git a/cube/macOS/cubepp/DemoViewController.mm b/cube/macOS/cubepp/DemoViewController.mm
new file mode 100644
index 00000000..049ec6b6
--- /dev/null
+++ b/cube/macOS/cubepp/DemoViewController.mm
@@ -0,0 +1,117 @@
+/*
+ * DemoViewController.m
+ *
+ * Copyright (c) 2014-2018 The Brenwill Workshop Ltd. (http://www.brenwill.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import "DemoViewController.h"
+#import <QuartzCore/CAMetalLayer.h>
+
+#include <MoltenVK/mvk_vulkan.h>
+
+#include "cube.cpp"
+
+#pragma mark -
+#pragma mark DemoViewController
+
+@implementation DemoViewController {
+ CVDisplayLinkRef _displayLink;
+ struct Demo demo;
+ NSTimer* _timer;
+}
+
+- (void)dealloc {
+ demo.cleanup();
+ CVDisplayLinkRelease(_displayLink);
+ [super dealloc];
+}
+
+/** Since this is a single-view app, initialize Vulkan during view loading. */
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ self.view.wantsLayer = YES; // Back the view with a layer created by the makeBackingLayer method.
+
+ // Convert incoming args to "C" argc/argv strings
+ NSArray *args = [[NSProcessInfo processInfo] arguments];
+ const char** argv = (const char**) alloca(sizeof(char*) * args.count);
+ for(unsigned int i = 0; i < args.count; i++) {
+ NSString *s = args[i];
+ argv[i] = s.UTF8String;
+ }
+
+ demo_main(demo, self.view, args.count, argv);
+
+ // Monitor the rendering loop for a quit condition
+ _timer = [NSTimer scheduledTimerWithTimeInterval: 0.2
+ target: self
+ selector: @selector(onTick:)
+ userInfo: self
+ repeats: YES];
+
+ // Start the rendering loop
+ CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink);
+ CVDisplayLinkSetOutputCallback(_displayLink, &DisplayLinkCallback, &demo);
+ CVDisplayLinkStart(_displayLink);
+}
+
+// Close the window if the demo is in a Quit state
+-(void)onTick:(NSTimer*)timer {
+ if (demo.quit) {
+ [[[self view] window] close];
+ }
+}
+
+#pragma mark Display loop callback function
+
+/** Rendering loop callback function for use with a CVDisplayLink. */
+static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now,
+ const CVTimeStamp* outputTime,
+ CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* target) {
+ struct Demo* demo = (struct Demo*)target;
+ demo->run();
+ if (demo->quit) {
+ CVDisplayLinkStop(displayLink);
+ CVDisplayLinkRelease(displayLink);
+ }
+ return kCVReturnSuccess;
+}
+
+@end
+
+#pragma mark -
+#pragma mark DemoView
+
+@implementation DemoView
+
+/** Indicates that the view wants to draw using the backing layer instead of using drawRect:. */
+- (BOOL)wantsUpdateLayer {
+ return YES;
+}
+
+/** Returns a Metal-compatible layer. */
++ (Class)layerClass {
+ return [CAMetalLayer class];
+}
+
+/** If the wantsLayer property is set to YES, this method will be invoked to return a layer instance. */
+- (CALayer*)makeBackingLayer {
+ CALayer* layer = [self.class.layerClass layer];
+ CGSize viewScale = [self convertSizeToBacking:CGSizeMake(1.0, 1.0)];
+ layer.contentsScale = MIN(viewScale.width, viewScale.height);
+ return layer;
+}
+
+@end
diff --git a/cube/macOS/cubepp/Info.plist b/cube/macOS/cubepp/Info.plist
new file mode 100644
index 00000000..e8a276ce
--- /dev/null
+++ b/cube/macOS/cubepp/Info.plist
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
+ <key>CFBundleGetInfoString</key>
+ <string>Cubepp</string>
+ <key>CFBundleIconFile</key>
+ <string>LunarGIcon.icns</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.lunarg.cubepp</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleLongVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleName</key>
+ <string>Cubepp</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>CSResourcesFileMapped</key>
+ <true/>
+ <key>NSHumanReadableCopyright</key>
+ <string>Copyright (c) 2018 The Khronos Group Inc. LunarG Inc. All rights reserved.</string>
+ <key>NSMainStoryboardFile</key>
+ <string>Main</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+</dict>
+</plist> \ No newline at end of file
diff --git a/cube/macOS/cubepp/Resources/LunarGIcon.icns b/cube/macOS/cubepp/Resources/LunarGIcon.icns
new file mode 100644
index 00000000..640b756f
--- /dev/null
+++ b/cube/macOS/cubepp/Resources/LunarGIcon.icns
Binary files differ
diff --git a/cube/macOS/cubepp/Resources/Main.storyboard b/cube/macOS/cubepp/Resources/Main.storyboard
new file mode 100644
index 00000000..d21c1493
--- /dev/null
+++ b/cube/macOS/cubepp/Resources/Main.storyboard
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15G26a" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
+ <dependencies>
+ <deployment identifier="macosx"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10117"/>
+ </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="MoltenVK Demo" id="1Xt-HY-uBw">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="MoltenVK Demo" systemMenu="apple" id="uQy-DD-JDr">
+ <items>
+ <menuItem title="About Demo" 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="Hide Demo" 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 Demo" 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="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="MoltenVK Demo 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>
+ <connections>
+ <outlet property="delegate" destination="Voe-Tx-rLC" id="PrD-fu-P6m"/>
+ </connections>
+ </application>
+ <customObject id="Voe-Tx-rLC" customClass="AppDelegate"/>
+ <customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="83.5" y="-47"/>
+ </scene>
+ <!--Window Controller-->
+ <scene sceneID="R2V-B0-nI4">
+ <objects>
+ <windowController id="B8D-0N-5wS" sceneMemberID="viewController">
+ <window key="window" title="MoltenVK Demo" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="IQv-IB-iLA">
+ <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
+ <rect key="contentRect" x="1051" y="656" width="300" height="200"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
+ <value key="minSize" type="size" width="300" height="200"/>
+ </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="83" y="146"/>
+ </scene>
+ <!--Demo View Controller-->
+ <scene sceneID="hIz-AP-VOD">
+ <objects>
+ <viewController id="XfG-lQ-9wD" customClass="DemoViewController" sceneMemberID="viewController">
+ <view key="view" id="m2S-Jp-Qdl" customClass="DemoView">
+ <rect key="frame" x="0.0" y="0.0" width="400" height="300"/>
+ <autoresizingMask key="autoresizingMask"/>
+ </view>
+ </viewController>
+ <customObject id="rPt-NT-nkU" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="83" y="564"/>
+ </scene>
+ </scenes>
+</document>
diff --git a/cube/macOS/cubepp/cubepp.cmake b/cube/macOS/cubepp/cubepp.cmake
new file mode 100644
index 00000000..874ed25f
--- /dev/null
+++ b/cube/macOS/cubepp/cubepp.cmake
@@ -0,0 +1,104 @@
+# Cube Application Bundle
+
+set(cubepp_SRCS
+ ${CMAKE_CURRENT_SOURCE_DIR}/macOS/cubepp/main.mm
+ ${CMAKE_CURRENT_SOURCE_DIR}/macOS/cubepp/AppDelegate.mm
+ ${CMAKE_CURRENT_SOURCE_DIR}/macOS/cubepp/DemoViewController.mm
+)
+set(cubepp_HDRS
+ ${CMAKE_CURRENT_SOURCE_DIR}/macOS/cubepp/AppDelegate.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/macOS/cubepp/DemoViewController.h
+)
+set(cubepp_RESOURCES
+ ${CMAKE_CURRENT_SOURCE_DIR}/lunarg.ppm
+ ${CMAKE_CURRENT_BINARY_DIR}/staging-json/MoltenVK_icd.json
+ ${CMAKE_CURRENT_SOURCE_DIR}/macOS/cubepp/Resources/LunarGIcon.icns
+)
+
+# Have Xcode handle the Storyboard
+if(${CMAKE_GENERATOR} MATCHES "^Xcode.*")
+ set(cubepp_RESOURCES ${cubepp_RESOURCES}
+ ${CMAKE_CURRENT_SOURCE_DIR}/macOS/cubepp/Resources/Main.storyboard
+ )
+endif()
+
+add_executable(cubepp MACOSX_BUNDLE
+ ${cubepp_SRCS}
+ ${cubepp_HDRS}
+ ${cubepp_RESOURCES}
+ cube.vert.inc cube.frag.inc
+)
+
+# Handle the Storyboard ourselves
+if(NOT ${CMAKE_GENERATOR} MATCHES "^Xcode.*")
+ # Compile the storyboard file with the ibtool.
+ add_custom_command(TARGET cubepp POST_BUILD
+ COMMAND ${IBTOOL} --errors --warnings --notices --output-format human-readable-text
+ --compile ${CMAKE_CURRENT_BINARY_DIR}/cubepp.app/Contents/Resources/Main.storyboardc
+ ${CMAKE_CURRENT_SOURCE_DIR}/macOS/cubepp/Resources/Main.storyboard
+ COMMENT "Compiling storyboard"
+ )
+endif()
+
+add_dependencies(cubepp MoltenVK_icd-staging-json)
+
+# Include demo source code dir because the MacOS cubepp's Objective-C source includes
+# the "original" cubepp application C++ source code.
+# Also include the MoltenVK helper files.
+target_include_directories(cubepp PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${MOLTENVK_DIR}/MoltenVK/include
+)
+
+target_link_libraries(cubepp ${LIBRARIES} "-framework Cocoa -framework QuartzCore")
+
+set_target_properties(cubepp PROPERTIES
+ MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/macOS/cubepp/Info.plist
+)
+
+# The RESOURCE target property cannot be used in conjunction with the MACOSX_PACKAGE_LOCATION
+# property. We need fine-grained control over the Resource directory, so we have to specify
+# the destination of all the resource files on a per-destination-directory basis.
+# If all the files went into the top-level Resource directory, then we could simply set
+# the RESOURCE property to a list of all the resource files.
+set_source_files_properties(${cubepp_RESOURCES} PROPERTIES
+ MACOSX_PACKAGE_LOCATION "Resources"
+)
+set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/staging-json/MoltenVK_icd.json" PROPERTIES
+ MACOSX_PACKAGE_LOCATION "Resources/vulkan/icd.d"
+)
+
+# Direct the MoltenVK library to the right place.
+install(FILES "${MOLTENVK_DIR}/MoltenVK/MacOS/libMoltenVK.dylib"
+ DESTINATION "demos/cubepp.app/Contents/Frameworks"
+ COMPONENT Runtime
+)
+
+# Copy the MoltenVK lib into the bundle.
+if(${CMAKE_GENERATOR} MATCHES "^Xcode.*")
+ add_custom_command(TARGET cubepp POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy "${MOLTENVK_DIR}/MoltenVK/MacOS/libMoltenVK.dylib"
+ ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/cubepp.app/Contents/Frameworks/libMoltenVK.dylib
+ DEPENDS vulkan
+ )
+else()
+ add_custom_command(TARGET cubepp POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy "${MOLTENVK_DIR}/MoltenVK/MacOS/libMoltenVK.dylib"
+ ${CMAKE_CURRENT_BINARY_DIR}/cubepp.app/Contents/Frameworks/libMoltenVK.dylib
+ DEPENDS vulkan
+ )
+endif()
+
+# Fix up the library search path in the executable to find (loader) libraries in the bundle.
+install(CODE "
+ include(BundleUtilities)
+ fixup_bundle(${CMAKE_INSTALL_PREFIX}/demos/cubepp.app \"\" \"\")
+ " COMPONENT Runtime
+)
+
+# Not sure this is needed. When activated, it makes a symlink from
+# libvulkan.dylib to libvulkan.1.dylib (which in turn symlinks to libvulkan.1.0.xx.dylib.)
+# install(FILES
+# "${CMAKE_BINARY_DIR}/loader/libvulkan.dylib"
+# DESTINATION "demos/cubepp.app/Contents/MacOS"
+# COMPONENT Runtime)
diff --git a/cube/macOS/cubepp/main.mm b/cube/macOS/cubepp/main.mm
new file mode 100644
index 00000000..cf9e0d89
--- /dev/null
+++ b/cube/macOS/cubepp/main.mm
@@ -0,0 +1,21 @@
+/*
+ * main.m
+ *
+ * Copyright (c) 2014-2018 The Brenwill Workshop Ltd. (http://www.brenwill.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+int main(int argc, const char* argv[]) { return NSApplicationMain(argc, argv); }