summaryrefslogtreecommitdiffstats
path: root/NativeApp/Apple/Source
diff options
context:
space:
mode:
authorEgor Yusov <egor.yusov@gmail.com>2019-11-11 15:42:14 +0000
committerEgor Yusov <egor.yusov@gmail.com>2019-11-11 15:42:14 +0000
commitee177d37df85b982d6c528a7b79e3cd9ca9749d6 (patch)
treedda6c9b0c2a3f2756c08b71ebaca32644dbc9d32 /NativeApp/Apple/Source
parentUpdated HLSL2GLSL converter app (diff)
downloadDiligentTools-ee177d37df85b982d6c528a7b79e3cd9ca9749d6.tar.gz
DiligentTools-ee177d37df85b982d6c528a7b79e3cd9ca9749d6.zip
Moved Native App from master repository
Diffstat (limited to 'NativeApp/Apple/Source')
-rw-r--r--NativeApp/Apple/Source/Classes/OSX/AppDelegate.h15
-rw-r--r--NativeApp/Apple/Source/Classes/OSX/AppDelegate.m30
-rw-r--r--NativeApp/Apple/Source/Classes/OSX/FullscreenWindow.h15
-rw-r--r--NativeApp/Apple/Source/Classes/OSX/FullscreenWindow.m52
-rw-r--r--NativeApp/Apple/Source/Classes/OSX/GLView.h15
-rw-r--r--NativeApp/Apple/Source/Classes/OSX/GLView.m211
-rw-r--r--NativeApp/Apple/Source/Classes/OSX/MetalView.h30
-rw-r--r--NativeApp/Apple/Source/Classes/OSX/MetalView.m139
-rw-r--r--NativeApp/Apple/Source/Classes/OSX/ModeSelectionViewController.h30
-rw-r--r--NativeApp/Apple/Source/Classes/OSX/ModeSelectionViewController.mm83
-rw-r--r--NativeApp/Apple/Source/Classes/OSX/ViewBase.h43
-rw-r--r--NativeApp/Apple/Source/Classes/OSX/ViewBase.m147
-rw-r--r--NativeApp/Apple/Source/Classes/OSX/ViewController.h30
-rw-r--r--NativeApp/Apple/Source/Classes/OSX/ViewController.mm116
-rw-r--r--NativeApp/Apple/Source/Classes/OSX/WindowController.h14
-rw-r--r--NativeApp/Apple/Source/Classes/OSX/WindowController.m181
-rw-r--r--NativeApp/Apple/Source/Classes/iOS/AppDelegate.h18
-rw-r--r--NativeApp/Apple/Source/Classes/iOS/AppDelegate.m89
-rw-r--r--NativeApp/Apple/Source/Classes/iOS/AppViewBase.h16
-rw-r--r--NativeApp/Apple/Source/Classes/iOS/AppViewBase.m166
-rw-r--r--NativeApp/Apple/Source/Classes/iOS/BaseView.h12
-rw-r--r--NativeApp/Apple/Source/Classes/iOS/BaseView.m21
-rw-r--r--NativeApp/Apple/Source/Classes/iOS/EAGLView.h18
-rw-r--r--NativeApp/Apple/Source/Classes/iOS/EAGLView.m74
-rw-r--r--NativeApp/Apple/Source/Classes/iOS/MetalView.h7
-rw-r--r--NativeApp/Apple/Source/Classes/iOS/MetalView.m44
-rw-r--r--NativeApp/Apple/Source/Classes/iOS/ModeSelectionViewController.h29
-rw-r--r--NativeApp/Apple/Source/Classes/iOS/ModeSelectionViewController.mm64
-rw-r--r--NativeApp/Apple/Source/main.m25
29 files changed, 1734 insertions, 0 deletions
diff --git a/NativeApp/Apple/Source/Classes/OSX/AppDelegate.h b/NativeApp/Apple/Source/Classes/OSX/AppDelegate.h
new file mode 100644
index 0000000..d38403c
--- /dev/null
+++ b/NativeApp/Apple/Source/Classes/OSX/AppDelegate.h
@@ -0,0 +1,15 @@
+/*
+ Copyright (C) 2015 Apple Inc. All Rights Reserved.
+ See LICENSE.txt for this sample’s licensing information
+
+ Abstract:
+ The Application Delegate.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+@interface AppDelegate : NSObject <NSApplicationDelegate>
+
+
+@end
+
diff --git a/NativeApp/Apple/Source/Classes/OSX/AppDelegate.m b/NativeApp/Apple/Source/Classes/OSX/AppDelegate.m
new file mode 100644
index 0000000..fd52cc4
--- /dev/null
+++ b/NativeApp/Apple/Source/Classes/OSX/AppDelegate.m
@@ -0,0 +1,30 @@
+/*
+ Copyright (C) 2015 Apple Inc. All Rights Reserved.
+ See LICENSE.txt for this sample’s licensing information
+
+ Abstract:
+ The Application Delegate.
+ */
+
+#import "AppDelegate.h"
+
+@interface AppDelegate ()
+
+@end
+
+@implementation AppDelegate
+
+- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
+ NSWindow* mainWindow = [[NSApplication sharedApplication]mainWindow];
+ [mainWindow setAcceptsMouseMovedEvents:YES];
+}
+
+- (void)applicationWillTerminate:(NSNotification *)aNotification {
+ // Insert code here to tear down your application
+}
+
+- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication {
+ return YES;
+}
+
+@end
diff --git a/NativeApp/Apple/Source/Classes/OSX/FullscreenWindow.h b/NativeApp/Apple/Source/Classes/OSX/FullscreenWindow.h
new file mode 100644
index 0000000..2a61a51
--- /dev/null
+++ b/NativeApp/Apple/Source/Classes/OSX/FullscreenWindow.h
@@ -0,0 +1,15 @@
+/*
+ Copyright (C) 2015 Apple Inc. All Rights Reserved.
+ See LICENSE.txt for this sample’s licensing information
+
+ Abstract:
+ Fullscreen window class.
+ All logic here could have been done in the window controller except that, by default, borderless windows cannot be made key and input cannot go to them.
+ Therefore, this class exists to override canBecomeKeyWindow allowing this borderless window to accept inputs.
+ This class is not part of the NIB and entirely managed in code by the window controller.
+ */
+#import <Cocoa/Cocoa.h>
+
+@interface FullscreenWindow : NSWindow
+
+@end
diff --git a/NativeApp/Apple/Source/Classes/OSX/FullscreenWindow.m b/NativeApp/Apple/Source/Classes/OSX/FullscreenWindow.m
new file mode 100644
index 0000000..69c9063
--- /dev/null
+++ b/NativeApp/Apple/Source/Classes/OSX/FullscreenWindow.m
@@ -0,0 +1,52 @@
+/*
+ Copyright (C) 2015 Apple Inc. All Rights Reserved.
+ See LICENSE.txt for this sample’s licensing information
+
+ Abstract:
+ Fullscreen window class.
+ All logic here could have been done in the window controller except that, by default, borderless windows cannot be made key and input cannot go to them.
+ Therefore, this class exists to override canBecomeKeyWindow allowing this borderless window to accept inputs.
+ This class is not part of the NIB and entirely managed in code by the window controller.
+ */
+
+#import "FullscreenWindow.h"
+
+@implementation FullscreenWindow
+
+-(instancetype)init
+{
+ // Create a screen-sized window on the display you want to take over
+ NSRect screenRect = [[NSScreen mainScreen] frame];
+
+ // Initialize the window making it size of the screen and borderless
+ self = [super initWithContentRect:screenRect
+ styleMask:NSWindowStyleMaskBorderless
+ backing:NSBackingStoreBuffered
+ defer:YES];
+
+ // Set the window level to be above the menu bar to cover everything else
+ [self setLevel:NSMainMenuWindowLevel+1];
+
+ // Set opaque
+ [self setOpaque:YES];
+
+ // Hide this when user switches to another window (or app)
+ [self setHidesOnDeactivate:YES];
+
+ return self;
+}
+
+-(BOOL)canBecomeKeyWindow
+{
+ // Return yes so that this borderless window can receive input
+ return YES;
+}
+
+- (void)keyDown:(NSEvent *)event
+{
+ // Implement keyDown since controller will not get [ESC] key event which
+ // the controller uses to kill fullscreen
+ [[self windowController] keyDown:event];
+}
+
+@end
diff --git a/NativeApp/Apple/Source/Classes/OSX/GLView.h b/NativeApp/Apple/Source/Classes/OSX/GLView.h
new file mode 100644
index 0000000..666305a
--- /dev/null
+++ b/NativeApp/Apple/Source/Classes/OSX/GLView.h
@@ -0,0 +1,15 @@
+/*
+ Copyright (C) 2015 Apple Inc. All Rights Reserved.
+ See LICENSE.txt for this sample’s licensing information
+
+ Abstract:
+ OpenGL view subclass.
+ */
+
+
+#import <Cocoa/Cocoa.h>
+#import "ViewBase.h"
+
+@interface GLView : ViewBase
+
+@end
diff --git a/NativeApp/Apple/Source/Classes/OSX/GLView.m b/NativeApp/Apple/Source/Classes/OSX/GLView.m
new file mode 100644
index 0000000..2e91b35
--- /dev/null
+++ b/NativeApp/Apple/Source/Classes/OSX/GLView.m
@@ -0,0 +1,211 @@
+/*
+ Copyright (C) 2015 Apple Inc. All Rights Reserved.
+ See LICENSE.txt for this sample’s licensing information
+
+ Abstract:
+ OpenGL view subclass.
+ */
+
+#include <memory>
+
+#import "GLView.h"
+
+@interface GLView ()
+{
+ NSRect _viewRectPixels;
+}
+@end
+
+@implementation GLView
+
+
+- (CVReturn) getFrameForTime:(const CVTimeStamp*)outputTime
+{
+ // There is no autorelease pool when this method is called
+ // because it will be called from a background thread.
+ // It's important to create one or app can leak objects.
+ @autoreleasepool {
+ [self drawView];
+ }
+ return kCVReturnSuccess;
+}
+
+// This is the renderer output callback function
+static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink,
+ const CVTimeStamp* now,
+ const CVTimeStamp* outputTime,
+ CVOptionFlags flagsIn,
+ CVOptionFlags* flagsOut,
+ void* displayLinkContext)
+{
+ CVReturn result = [(__bridge GLView*)displayLinkContext getFrameForTime:outputTime];
+ return result;
+}
+
+// Prepares the receiver for service after it has been loaded
+// from an Interface Builder archive, or nib file.
+- (void) awakeFromNib
+{
+ [super awakeFromNib];
+
+ NSOpenGLPixelFormatAttribute attrs[] =
+ {
+ NSOpenGLPFADoubleBuffer,
+ NSOpenGLPFADepthSize, 24,
+ NSOpenGLPFAOpenGLProfile,
+ NSOpenGLProfileVersion4_1Core,
+ 0
+ };
+
+ NSOpenGLPixelFormat *pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
+
+ if (!pf)
+ {
+ NSLog(@"No OpenGL pixel format");
+ }
+
+ NSOpenGLContext* context = [[NSOpenGLContext alloc] initWithFormat:pf shareContext:nil];
+
+#if defined(DEBUG)
+ // When we're using a CoreProfile context, crash if we call a legacy OpenGL function
+ // This will make it much more obvious where and when such a function call is made so
+ // that we can remove such calls.
+ // Without this we'd simply get GL_INVALID_OPERATION error for calling legacy functions
+ // but it would be more difficult to see where that function was called.
+ CGLEnable([context CGLContextObj], kCGLCECrashOnRemovedFunctions);
+#endif
+
+ [self setPixelFormat:pf];
+
+ [self setOpenGLContext:context];
+
+ // Opt-In to Retina resolution
+ [self setWantsBestResolutionOpenGLSurface:YES];
+}
+
+- (void) prepareOpenGL
+{
+ [super prepareOpenGL];
+
+ // Application must be initialized befor display link is started
+ [self initGL];
+
+ CVDisplayLinkRef displayLink;
+ // Create a display link capable of being used with all active displays
+ CVDisplayLinkCreateWithActiveCGDisplays(&displayLink);
+ [self setDisplayLink:displayLink];
+
+ // Set the renderer output callback function
+ CVDisplayLinkSetOutputCallback(displayLink, &MyDisplayLinkCallback, (__bridge void*)self);
+
+ // Set the display link for the current renderer
+ CGLContextObj cglContext = [[self openGLContext] CGLContextObj];
+ CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
+ CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, cglContext, cglPixelFormat);
+
+ // Activate the display link
+ CVDisplayLinkStart(displayLink);
+}
+
+- (void) initGL
+{
+ // The reshape function may have changed the thread to which our OpenGL
+ // context is attached before prepareOpenGL and initGL are called. So call
+ // makeCurrentContext to ensure that our OpenGL context current to this
+ // thread (i.e. makeCurrentContext directs all OpenGL calls on this thread
+ // to [self openGLContext])
+ [[self openGLContext] makeCurrentContext];
+
+ // Synchronize buffer swaps with vertical refresh rate
+ GLint swapInt = 1;
+ [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
+
+ // Init the application.
+ [self initApp:nil];
+}
+
+- (void)reshape
+{
+ [super reshape];
+
+ // We draw on a secondary thread through the display link. However, when
+ // resizing the view, -drawRect is called on the main thread.
+ // Add a mutex around to avoid the threads accessing the context
+ // simultaneously when resizing.
+ CGLLockContext([[self openGLContext] CGLContextObj]);
+
+ // Get the view size in Points
+ NSRect viewRectPoints = [self bounds];
+
+ // Rendering at retina resolutions will reduce aliasing, but at the potential
+ // cost of framerate and battery life due to the GPU needing to render more
+ // pixels.
+
+ // Any calculations the renderer does which use pixel dimentions, must be
+ // in "retina" space. [NSView convertRectToBacking] converts point sizes
+ // to pixel sizes. Thus the renderer gets the size in pixels, not points,
+ // so that it can set it's viewport and perform and other pixel based
+ // calculations appropriately.
+ // viewRectPixels will be larger than viewRectPoints for retina displays.
+ // viewRectPixels will be the same as viewRectPoints for non-retina displays
+ _viewRectPixels = [self convertRectToBacking:viewRectPoints];
+
+ // Set the new dimensions in our renderer
+ auto* theApp = [self lockApp];
+ if(theApp)
+ {
+ theApp->WindowResize(_viewRectPixels.size.width, _viewRectPixels.size.height);
+ }
+ [self unlockApp];
+
+ CGLUnlockContext([[self openGLContext] CGLContextObj]);
+}
+
+
+- (void)renewGState
+{
+ // Called whenever graphics state updated (such as window resize)
+
+ // OpenGL rendering is not synchronous with other rendering on the OSX.
+ // Therefore, call disableScreenUpdatesUntilFlush so the window server
+ // doesn't render non-OpenGL content in the window asynchronously from
+ // OpenGL content, which could cause flickering. (non-OpenGL content
+ // includes the title bar and drawing done by the app with other APIs)
+ [[self window] disableScreenUpdatesUntilFlush];
+
+ [super renewGState];
+}
+
+- (void) drawRect: (NSRect) theRect
+{
+ // Called during resize operations
+
+ // Avoid flickering during resize by drawing
+ [self drawView];
+}
+
+- (void) drawView
+{
+ auto* glContext = [self openGLContext];
+
+ [glContext makeCurrentContext];
+
+ // We draw on a secondary thread through the display link
+ // When resizing the view, -reshape is called automatically on the main
+ // thread. Add a mutex around to avoid the threads accessing the context
+ // simultaneously when resizing
+ CGLLockContext([glContext CGLContextObj]);
+
+ auto* theApp = [self lockApp];
+ if(theApp)
+ {
+ theApp->Update();
+ theApp->Render();
+ }
+ [self unlockApp];
+
+ CGLFlushDrawable([glContext CGLContextObj]);
+ CGLUnlockContext([glContext CGLContextObj]);
+}
+
+@end
diff --git a/NativeApp/Apple/Source/Classes/OSX/MetalView.h b/NativeApp/Apple/Source/Classes/OSX/MetalView.h
new file mode 100644
index 0000000..d9b6a7a
--- /dev/null
+++ b/NativeApp/Apple/Source/Classes/OSX/MetalView.h
@@ -0,0 +1,30 @@
+/* Copyright 2015-2019 Egor Yusov
+ *
+ * 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
+ *
+ * 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 OF ANY PROPRIETARY RIGHTS.
+ *
+ * In no event and under no legal theory, whether in tort (including negligence),
+ * contract, or otherwise, unless required by applicable law (such as deliberate
+ * and grossly negligent acts) or agreed to in writing, shall any Contributor be
+ * liable for any damages, including any direct, indirect, special, incidental,
+ * or consequential damages of any character arising as a result of this License or
+ * out of the use or inability to use the software (including but not limited to damages
+ * for loss of goodwill, work stoppage, computer failure or malfunction, or any and
+ * all other commercial damages or losses), even if such Contributor has been advised
+ * of the possibility of such damages.
+ */
+
+#import <AppKit/AppKit.h>
+
+#include "ViewBase.h"
+
+@interface MetalView : ViewBase
+
+@end
diff --git a/NativeApp/Apple/Source/Classes/OSX/MetalView.m b/NativeApp/Apple/Source/Classes/OSX/MetalView.m
new file mode 100644
index 0000000..5d4a7dd
--- /dev/null
+++ b/NativeApp/Apple/Source/Classes/OSX/MetalView.m
@@ -0,0 +1,139 @@
+/* Copyright 2015-2019 Egor Yusov
+ *
+ * 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
+ *
+ * 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 OF ANY PROPRIETARY RIGHTS.
+ *
+ * In no event and under no legal theory, whether in tort (including negligence),
+ * contract, or otherwise, unless required by applicable law (such as deliberate
+ * and grossly negligent acts) or agreed to in writing, shall any Contributor be
+ * liable for any damages, including any direct, indirect, special, incidental,
+ * or consequential damages of any character arising as a result of this License or
+ * out of the use or inability to use the software (including but not limited to damages
+ * for loss of goodwill, work stoppage, computer failure or malfunction, or any and
+ * all other commercial damages or losses), even if such Contributor has been advised
+ * of the possibility of such damages.
+ */
+
+#import <QuartzCore/CAMetalLayer.h>
+#import "MetalView.h"
+
+@implementation MetalView
+{
+}
+
+- (void) awakeFromNib
+{
+ [super awakeFromNib];
+
+ // Back the view with a layer created by the makeBackingLayer method.
+ self.wantsLayer = YES;
+
+ [self initApp:self];
+
+ CVDisplayLinkRef displayLink;
+ CVDisplayLinkCreateWithActiveCGDisplays(&displayLink);
+ [self setDisplayLink:displayLink];
+ CVDisplayLinkSetOutputCallback(displayLink, &DisplayLinkCallback, (__bridge void*)self);
+ CVDisplayLinkStart(displayLink);
+
+ [self setPostsBoundsChangedNotifications:YES];
+ [self setPostsFrameChangedNotifications:YES];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(boundsDidChange:) name:NSViewBoundsDidChangeNotification object:self];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(boundsDidChange:) name:NSViewFrameDidChangeNotification object:self];
+}
+
+// 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;
+}
+
+-(void)render
+{
+ auto* theApp = [self lockApp];
+ if (theApp)
+ {
+ theApp->Update();
+ theApp->Render();
+ theApp->Present();
+ }
+ [self unlockApp];
+}
+
+
+- (CVReturn) getFrameForTime:(const CVTimeStamp*)outputTime
+{
+ // There is no autorelease pool when this method is called
+ // because it will be called from a background thread.
+ // It's important to create one or app can leak objects.
+ @autoreleasepool {
+ [self render];
+ }
+ return kCVReturnSuccess;
+}
+
+// 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)
+{
+ MetalView* view = (__bridge MetalView*)target;
+ CVReturn result = [view getFrameForTime:outputTime];
+ return result;
+}
+
+-(void)boundsDidChange:(NSNotification *)notification
+{
+ // It is not clear what the proper way to handle window resize is.
+ // Cube demo from MoltenVK ignores any window resize notifications and
+ // recreates the swap chain if Present or AcquireNextImage fails, causing
+ // jagged transitions.
+
+ // According to this thread, there is no solution for flickering during
+ // resize in Metal:
+ // https://forums.developer.apple.com/thread/77901
+
+ // Calling WindowResize() causes flickering.
+ // Even if [self render] is called ater WindowResize()
+ // Similar results when using Metal kit view
+ // Calling [self render] alone produces jagged transitions but no flickering.
+ // Calling nothing causes the app to crash during resize.
+
+ NSRect viewRectPoints = [self bounds];
+ NSRect viewRectPixels = [self convertRectToBacking:viewRectPoints];
+ auto* theApp = [self lockApp];
+ if (theApp)
+ {
+ theApp->WindowResize(viewRectPixels.size.width, viewRectPixels.size.height);
+ }
+ [self unlockApp];
+}
+
+@end
diff --git a/NativeApp/Apple/Source/Classes/OSX/ModeSelectionViewController.h b/NativeApp/Apple/Source/Classes/OSX/ModeSelectionViewController.h
new file mode 100644
index 0000000..895123b
--- /dev/null
+++ b/NativeApp/Apple/Source/Classes/OSX/ModeSelectionViewController.h
@@ -0,0 +1,30 @@
+/* Copyright 2015-2019 Egor Yusov
+ *
+ * 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
+ *
+ * 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 OF ANY PROPRIETARY RIGHTS.
+ *
+ * In no event and under no legal theory, whether in tort (including negligence),
+ * contract, or otherwise, unless required by applicable law (such as deliberate
+ * and grossly negligent acts) or agreed to in writing, shall any Contributor be
+ * liable for any damages, including any direct, indirect, special, incidental,
+ * or consequential damages of any character arising as a result of this License or
+ * out of the use or inability to use the software (including but not limited to damages
+ * for loss of goodwill, work stoppage, computer failure or malfunction, or any and
+ * all other commercial damages or losses), even if such Contributor has been advised
+ * of the possibility of such damages.
+ */
+
+
+#import <AppKit/AppKit.h>
+
+@interface ModeSelectionViewController : NSViewController
+{
+}
+@end
diff --git a/NativeApp/Apple/Source/Classes/OSX/ModeSelectionViewController.mm b/NativeApp/Apple/Source/Classes/OSX/ModeSelectionViewController.mm
new file mode 100644
index 0000000..3bd4dbe
--- /dev/null
+++ b/NativeApp/Apple/Source/Classes/OSX/ModeSelectionViewController.mm
@@ -0,0 +1,83 @@
+/* Copyright 2015-2019 Egor Yusov
+ *
+ * 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
+ *
+ * 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 OF ANY PROPRIETARY RIGHTS.
+ *
+ * In no event and under no legal theory, whether in tort (including negligence),
+ * contract, or otherwise, unless required by applicable law (such as deliberate
+ * and grossly negligent acts) or agreed to in writing, shall any Contributor be
+ * liable for any damages, including any direct, indirect, special, incidental,
+ * or consequential damages of any character arising as a result of this License or
+ * out of the use or inability to use the software (including but not limited to damages
+ * for loss of goodwill, work stoppage, computer failure or malfunction, or any and
+ * all other commercial damages or losses), even if such Contributor has been advised
+ * of the possibility of such damages.
+ */
+
+#import "ModeSelectionViewController.h"
+#import "GLView.h"
+#import "MetalView.h"
+#import "ViewController.h"
+
+
+@implementation ModeSelectionViewController
+{
+}
+
+- (void) setWindowTitle:(NSString*) title
+{
+ NSWindow* mainWindow = [[NSApplication sharedApplication]mainWindow];
+ [mainWindow setTitle:title];
+}
+
+- (void) terminateApp:(NSString*) error
+{
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert addButtonWithTitle:@"OK"];
+ [alert setMessageText:@"Failed to start the application"];
+ [alert setInformativeText:error];
+ [alert setAlertStyle:NSAlertStyleCritical];
+ [alert runModal];
+ [NSApp terminate:self];
+}
+
+- (IBAction)goOpenGL:(id)sender
+{
+ ViewController* glViewController = [self.storyboard instantiateControllerWithIdentifier:@"GLViewControllerID"];
+ self.view.window.contentViewController = glViewController;
+
+ GLView* glView = (GLView*)[glViewController view];
+ NSString* error = [glView getError];
+ if(error != nil)
+ {
+ [self terminateApp:error];
+ }
+
+ NSString* name = [glView getAppName];
+ [self setWindowTitle:name];
+}
+
+- (IBAction)goVulkan:(id)sender
+{
+ ViewController* metalViewController = [self.storyboard instantiateControllerWithIdentifier:@"MetalViewControllerID"];
+ self.view.window.contentViewController = metalViewController;
+
+ MetalView* mtlView = (MetalView*)[metalViewController view];
+ NSString* error = [mtlView getError];
+ if(error != nil)
+ {
+ [self terminateApp:error];
+ }
+
+ NSString* name = [mtlView getAppName];
+ [self setWindowTitle:name];
+}
+
+@end
diff --git a/NativeApp/Apple/Source/Classes/OSX/ViewBase.h b/NativeApp/Apple/Source/Classes/OSX/ViewBase.h
new file mode 100644
index 0000000..d3084dd
--- /dev/null
+++ b/NativeApp/Apple/Source/Classes/OSX/ViewBase.h
@@ -0,0 +1,43 @@
+/* Copyright 2015-2019 Egor Yusov
+ *
+ * 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
+ *
+ * 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 OF ANY PROPRIETARY RIGHTS.
+ *
+ * In no event and under no legal theory, whether in tort (including negligence),
+ * contract, or otherwise, unless required by applicable law (such as deliberate
+ * and grossly negligent acts) or agreed to in writing, shall any Contributor be
+ * liable for any damages, including any direct, indirect, special, incidental,
+ * or consequential damages of any character arising as a result of this License or
+ * out of the use or inability to use the software (including but not limited to damages
+ * for loss of goodwill, work stoppage, computer failure or malfunction, or any and
+ * all other commercial damages or losses), even if such Contributor has been advised
+ * of the possibility of such damages.
+ */
+
+#include "NativeAppBase.h"
+
+#import <AppKit/AppKit.h>
+#import <QuartzCore/CVDisplayLink.h>
+#import <Cocoa/Cocoa.h>
+
+@interface ViewBase : NSOpenGLView
+
+@property CVDisplayLinkRef displayLink;
+
+-(void)initApp:(NSView*) view;
+-(void)destroyApp;
+-(NSString*)getError;
+-(Diligent::NativeAppBase*)lockApp;
+-(void)unlockApp;
+-(void)stopDisplayLink;
+-(void)startDisplayLink;
+-(NSString*)getAppName;
+
+@end
diff --git a/NativeApp/Apple/Source/Classes/OSX/ViewBase.m b/NativeApp/Apple/Source/Classes/OSX/ViewBase.m
new file mode 100644
index 0000000..b73a5eb
--- /dev/null
+++ b/NativeApp/Apple/Source/Classes/OSX/ViewBase.m
@@ -0,0 +1,147 @@
+/* Copyright 2015-2019 Egor Yusov
+ *
+ * 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
+ *
+ * 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 OF ANY PROPRIETARY RIGHTS.
+ *
+ * In no event and under no legal theory, whether in tort (including negligence),
+ * contract, or otherwise, unless required by applicable law (such as deliberate
+ * and grossly negligent acts) or agreed to in writing, shall any Contributor be
+ * liable for any damages, including any direct, indirect, special, incidental,
+ * or consequential damages of any character arising as a result of this License or
+ * out of the use or inability to use the software (including but not limited to damages
+ * for loss of goodwill, work stoppage, computer failure or malfunction, or any and
+ * all other commercial damages or losses), even if such Contributor has been advised
+ * of the possibility of such damages.
+ */
+
+#include <memory>
+#include <string>
+
+#import "ViewBase.h"
+
+@implementation ViewBase
+{
+ std::unique_ptr<Diligent::NativeAppBase> _theApp;
+ std::string _error;
+ NSRecursiveLock* appLock;
+}
+
+@synthesize displayLink;
+
+// Prepares the receiver for service after it has been loaded
+// from an Interface Builder archive, or nib file.
+- (void) awakeFromNib
+{
+ [super awakeFromNib];
+
+ _theApp.reset(Diligent::CreateApplication());
+
+ // [self window] is nil here
+ auto* mainWindow = [[NSApplication sharedApplication] mainWindow];
+ // Register to be notified when the main window closes so we can stop the displaylink
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(windowWillClose:)
+ name:NSWindowWillCloseNotification
+ object:mainWindow];
+ mainWindow.minSize = NSSize{320, 240};
+}
+
+-(void)initApp:(NSView*) view
+{
+ // Init the application.
+ try
+ {
+ _theApp->Initialize(view);
+ }
+ catch(std::runtime_error &err)
+ {
+ _error = err.what();
+ _theApp.reset();
+ }
+}
+
+-(Diligent::NativeAppBase*)lockApp
+{
+ [appLock lock];
+ return _theApp.get();
+}
+
+-(void)unlockApp
+{
+ [appLock unlock];
+}
+
+- (BOOL)acceptsFirstResponder
+{
+ return YES; // To make keyboard events work
+}
+
+-(void)destroyApp
+{
+ // Stop the display link BEFORE releasing anything in the view
+ // otherwise the display link thread may call into the view and crash
+ // when it encounters something that has been released
+ if (displayLink)
+ {
+ CVDisplayLinkStop(displayLink);
+ }
+
+ [appLock lock];
+ _theApp.reset();
+ [appLock unlock];
+}
+
+-(void) dealloc
+{
+ [self destroyApp];
+
+ CVDisplayLinkRelease(displayLink);
+
+ [appLock release];
+
+ [super dealloc];
+}
+
+-(NSString*)getError
+{
+ return _error.empty() ? nil : [NSString stringWithFormat:@"%s", _error.c_str()];
+}
+
+
+- (void)stopDisplayLink
+{
+ if (displayLink)
+ {
+ CVDisplayLinkStop(displayLink);
+ }
+}
+
+- (void)startDisplayLink
+{
+ if (displayLink)
+ {
+ CVDisplayLinkStart(displayLink);
+ }
+}
+
+- (void) windowWillClose:(NSNotification*)notification
+{
+ [self destroyApp];
+}
+
+-(NSString*)getAppName
+{
+ auto* theApp = [self lockApp];
+ auto Title = [NSString stringWithFormat:@"%s", theApp ? theApp->GetAppTitle() : ""];
+ [self unlockApp];
+ return Title;
+}
+
+@end
diff --git a/NativeApp/Apple/Source/Classes/OSX/ViewController.h b/NativeApp/Apple/Source/Classes/OSX/ViewController.h
new file mode 100644
index 0000000..a8e4685
--- /dev/null
+++ b/NativeApp/Apple/Source/Classes/OSX/ViewController.h
@@ -0,0 +1,30 @@
+/* Copyright 2015-2019 Egor Yusov
+ *
+ * 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
+ *
+ * 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 OF ANY PROPRIETARY RIGHTS.
+ *
+ * In no event and under no legal theory, whether in tort (including negligence),
+ * contract, or otherwise, unless required by applicable law (such as deliberate
+ * and grossly negligent acts) or agreed to in writing, shall any Contributor be
+ * liable for any damages, including any direct, indirect, special, incidental,
+ * or consequential damages of any character arising as a result of this License or
+ * out of the use or inability to use the software (including but not limited to damages
+ * for loss of goodwill, work stoppage, computer failure or malfunction, or any and
+ * all other commercial damages or losses), even if such Contributor has been advised
+ * of the possibility of such damages.
+ */
+
+
+#import <AppKit/AppKit.h>
+#include "NativeAppBase.h"
+
+@interface ViewController : NSViewController
+
+@end
diff --git a/NativeApp/Apple/Source/Classes/OSX/ViewController.mm b/NativeApp/Apple/Source/Classes/OSX/ViewController.mm
new file mode 100644
index 0000000..1a6611c
--- /dev/null
+++ b/NativeApp/Apple/Source/Classes/OSX/ViewController.mm
@@ -0,0 +1,116 @@
+/* Copyright 2015-2019 Egor Yusov
+ *
+ * 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
+ *
+ * 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 OF ANY PROPRIETARY RIGHTS.
+ *
+ * In no event and under no legal theory, whether in tort (including negligence),
+ * contract, or otherwise, unless required by applicable law (such as deliberate
+ * and grossly negligent acts) or agreed to in writing, shall any Contributor be
+ * liable for any damages, including any direct, indirect, special, incidental,
+ * or consequential damages of any character arising as a result of this License or
+ * out of the use or inability to use the software (including but not limited to damages
+ * for loss of goodwill, work stoppage, computer failure or malfunction, or any and
+ * all other commercial damages or losses), even if such Contributor has been advised
+ * of the possibility of such damages.
+ */
+
+#import "ViewController.h"
+#import "ViewBase.h"
+
+@implementation ViewController
+
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+
+ // Add a tracking area in order to receive mouse events whenever the mouse is within the bounds of our view
+ NSTrackingArea *trackingArea = [[NSTrackingArea alloc] initWithRect:NSZeroRect
+ options:NSTrackingMouseMoved | NSTrackingInVisibleRect | NSTrackingActiveAlways
+ owner:self
+ userInfo:nil];
+ [self.view addTrackingArea:trackingArea];
+}
+
+- (void)handleEvent : (NSEvent *)theEvent {
+ auto* view = (ViewBase*)self.view;
+ auto* theApp = [view lockApp];
+ if(theApp){
+ theApp->HandleOSXEvent(theEvent, view);
+ }
+ [view unlockApp];
+}
+
+
+- (void)mouseDown:(NSEvent *)theEvent {
+ [self handleEvent:theEvent];
+}
+
+- (void)mouseUp:(NSEvent *)theEvent {
+ [self handleEvent:theEvent];
+}
+
+- (void)rightMouseDown:(NSEvent *)theEvent {
+ [self handleEvent:theEvent];
+}
+
+- (void)rightMouseUp:(NSEvent *)theEvent {
+ [self handleEvent:theEvent];
+}
+
+- (void)mouseMoved:(NSEvent *)theEvent {
+ [self handleEvent:theEvent];
+}
+
+- (void)mouseDragged:(NSEvent *)theEvent {
+ [self handleEvent:theEvent];
+}
+
+- (void)rightMouseDragged:(NSEvent *)theEvent {
+ [self handleEvent:theEvent];
+}
+
+- (void)keyEvent:(NSEvent *)theEvent isKeyPressed:(bool)keyPressed
+{
+ [self handleEvent:theEvent];
+}
+
+- (void)keyDown:(NSEvent *)theEvent
+{
+ [self handleEvent:theEvent];
+
+ [super keyDown:theEvent];
+}
+
+- (void)keyUp:(NSEvent *)theEvent
+{
+ [self handleEvent:theEvent];
+
+ [super keyUp:theEvent];
+}
+
+// Informs the receiver that the user has pressed or released a
+// modifier key (Shift, Control, and so on)
+- (void)flagsChanged:(NSEvent *)event
+{
+ [self handleEvent:event];
+
+ [super flagsChanged:event];
+}
+
+- (void)scrollWheel:(NSEvent *)event
+{
+ [self handleEvent:event];
+}
+
+- (BOOL)acceptsFirstResponder {
+ return YES;
+}
+
+@end
diff --git a/NativeApp/Apple/Source/Classes/OSX/WindowController.h b/NativeApp/Apple/Source/Classes/OSX/WindowController.h
new file mode 100644
index 0000000..1d9203b
--- /dev/null
+++ b/NativeApp/Apple/Source/Classes/OSX/WindowController.h
@@ -0,0 +1,14 @@
+/*
+ Copyright (C) 2015 Apple Inc. All Rights Reserved.
+ See LICENSE.txt for this sample’s licensing information
+
+ Abstract:
+ Window controller subclass.
+ */
+
+
+#import <Cocoa/Cocoa.h>
+
+@interface WindowController : NSWindowController
+
+@end
diff --git a/NativeApp/Apple/Source/Classes/OSX/WindowController.m b/NativeApp/Apple/Source/Classes/OSX/WindowController.m
new file mode 100644
index 0000000..e3f7923
--- /dev/null
+++ b/NativeApp/Apple/Source/Classes/OSX/WindowController.m
@@ -0,0 +1,181 @@
+/*
+ Copyright (C) 2015 Apple Inc. All Rights Reserved.
+ See LICENSE.txt for this sample’s licensing information
+
+ Abstract:
+ Window controller subclass.
+ */
+
+#import "WindowController.h"
+#import "FullscreenWindow.h"
+#import "ViewBase.h"
+
+@interface WindowController ()
+
+// Fullscreen window
+@property(strong) FullscreenWindow *fullscreenWindow;
+
+// Non-Fullscreen window (also the initial window)
+@property(strong) NSWindow* standardWindow;
+
+@end
+
+@implementation WindowController
+{
+ bool CommandKeyPressed;
+}
+
+- (instancetype)initWithWindow:(NSWindow *)window
+{
+ self = [super initWithWindow:window];
+
+ if (self)
+ {
+ // Initialize to nil since it indicates app is not fullscreen
+ _fullscreenWindow = nil;
+ }
+
+ CommandKeyPressed = false;
+
+ return self;
+}
+
+- (void) goFullscreen
+{
+ // If app is already fullscreen...
+ if([self fullscreenWindow])
+ {
+ //...don't do anything
+ return;
+ }
+
+ ViewBase* view = (ViewBase*)self.window.contentView;
+
+ // We must stop the display link while
+ // switching the windows to make sure
+ // that render commands are not issued
+ // from another thread
+ [view stopDisplayLink];
+
+ // Allocate a new fullscreen window
+ [self setFullscreenWindow: [[FullscreenWindow alloc] init]];
+
+ [[self fullscreenWindow] setAcceptsMouseMovedEvents:YES];
+
+ // Resize the view to screensize
+ NSRect viewRect = [[self fullscreenWindow] frame];
+
+ // Set the view to the size of the fullscreen window
+ [self.window.contentView setFrameSize: viewRect.size];
+
+ // Set the view in the fullscreen window
+ [[self fullscreenWindow] setContentView:self.window.contentView];
+
+ [self setStandardWindow:[self window]];
+
+ // Hide non-fullscreen window so it doesn't show up when switching out
+ // of this app (i.e. with CMD-TAB)
+ [[self standardWindow] orderOut:self];
+
+ // Set controller to the fullscreen window so that all input will go to
+ // this controller (self)
+ [self setWindow:[self fullscreenWindow]];
+
+ // Show the window and make it the key window for input
+ [[self fullscreenWindow] makeKeyAndOrderFront:self];
+
+ // Restore display link
+ [view startDisplayLink];
+}
+
+- (void) goWindow
+{
+ // If controller doesn't have a full screen window...
+ if([self fullscreenWindow] == nil)
+ {
+ //...app is already windowed so don't do anything
+ return;
+ }
+
+ ViewBase* view = (ViewBase*)self.window.contentView;
+
+ // We must stop the display link while
+ // switching the windows to make sure
+ // that render commands are not issued
+ // from another thread
+ [view stopDisplayLink];
+
+ // Get the rectangle of the original window
+ NSRect viewRect = [[self standardWindow] frame];
+
+ // Set the view rect to the new size
+ [self.window.contentView setFrame:viewRect];
+
+ // Hide fullscreen window
+ [[self fullscreenWindow] orderOut:self];
+
+ // Set controller to the standard window so that all input will go to
+ // this controller (self)
+ [self setWindow:[self standardWindow]];
+
+ // Set the content of the orginal window to the view
+ [[self window] setContentView: [self fullscreenWindow].contentView];
+
+ // Show the window and make it the key window for input
+ [[self window] makeKeyAndOrderFront:self];
+
+ // Ensure we set fullscreen Window to nil so our checks for
+ // windowed vs. fullscreen mode elsewhere are correct
+ [self setFullscreenWindow: nil];
+
+ // Restore display link
+ [view startDisplayLink];
+}
+
+
+- (void) keyDown:(NSEvent *)event
+{
+ unichar c = [[event charactersIgnoringModifiers] characterAtIndex:0];
+
+ switch (c)
+ {
+ // Handle [ESC] key
+ case 27:
+ if([self fullscreenWindow] != nil)
+ {
+ [self goWindow];
+ }
+ return;
+
+ // Have Command+f or Command+Enter toggle fullscreen
+ case 13:
+ case 'f':
+ if (CommandKeyPressed)
+ {
+ if([self fullscreenWindow] == nil)
+ {
+ [self goFullscreen];
+ }
+ else
+ {
+ [self goWindow];
+ }
+ }
+ return;
+ }
+
+ // Allow other character to be handled (or not and beep)
+ //[super keyDown:event];
+}
+
+// Informs the receiver that the user has pressed or released a
+// modifier key (Shift, Control, and so on)
+- (void)flagsChanged:(NSEvent *)event
+{
+ auto modifierFlags = [event modifierFlags];
+ CommandKeyPressed = modifierFlags & NSEventModifierFlagCommand;
+
+ [super flagsChanged:event];
+}
+
+@end
diff --git a/NativeApp/Apple/Source/Classes/iOS/AppDelegate.h b/NativeApp/Apple/Source/Classes/iOS/AppDelegate.h
new file mode 100644
index 0000000..3839faa
--- /dev/null
+++ b/NativeApp/Apple/Source/Classes/iOS/AppDelegate.h
@@ -0,0 +1,18 @@
+/*
+ Copyright (C) 2015 Apple Inc. All Rights Reserved.
+ See LICENSE.txt for this sample’s licensing information
+
+ Abstract:
+ The application delegate
+ */
+
+
+#import <UIKit/UIKit.h>
+
+@interface AppDelegate : NSObject <UIApplicationDelegate> {
+}
+
+@property (nonatomic, retain) UIWindow *window;
+
+@end
+
diff --git a/NativeApp/Apple/Source/Classes/iOS/AppDelegate.m b/NativeApp/Apple/Source/Classes/iOS/AppDelegate.m
new file mode 100644
index 0000000..5e1745c
--- /dev/null
+++ b/NativeApp/Apple/Source/Classes/iOS/AppDelegate.m
@@ -0,0 +1,89 @@
+/*
+ Copyright (C) 2015 Apple Inc. All Rights Reserved.
+ See LICENSE.txt for this sample’s licensing information
+
+ Abstract:
+ The application delegate
+*/
+
+#import "AppDelegate.h"
+#import "BaseView.h"
+
+@implementation AppDelegate
+
+#pragma mark -
+#pragma mark Application lifecycle
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
+{
+ // Override point for customization after application launch.
+
+ [self.window makeKeyAndVisible];
+
+ [(BaseView*)self.window.rootViewController.view startAnimation];
+
+ return YES;
+}
+
+
+- (void)applicationWillResignActive:(UIApplication *)application
+{
+ /*
+ Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
+ Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
+ */
+
+ [(BaseView*)self.window.rootViewController.view stopAnimation];
+}
+
+
+- (void)applicationDidEnterBackground:(UIApplication *)application {
+ /*
+ Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
+ If your application supports background execution, called instead of applicationWillTerminate: when the user quits.
+ */
+
+ [(BaseView*)self.window.rootViewController.view stopAnimation];
+}
+
+
+- (void)applicationWillEnterForeground:(UIApplication *)application {
+ /*
+ Called as part of transition from the background to the inactive state: here you can undo many of the changes made on entering the background.
+ */
+
+ [(BaseView*)self.window.rootViewController.view startAnimation];
+}
+
+
+- (void)applicationDidBecomeActive:(UIApplication *)application
+{
+ /*
+ Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
+ */
+
+ [(BaseView*)self.window.rootViewController.view startAnimation];
+}
+
+
+- (void)applicationWillTerminate:(UIApplication *)application {
+ /*
+ Called when the application is about to terminate.
+ See also applicationDidEnterBackground:.
+ */
+
+ [(BaseView*)self.window.rootViewController.view stopAnimation];
+ [(BaseView*)self.window.rootViewController.view terminate];
+}
+
+
+#pragma mark -
+#pragma mark Memory management
+
+- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
+ /*
+ Free up as much memory as possible by purging cached data objects that can be recreated (or reloaded from disk) later.
+ */
+}
+
+@end
diff --git a/NativeApp/Apple/Source/Classes/iOS/AppViewBase.h b/NativeApp/Apple/Source/Classes/iOS/AppViewBase.h
new file mode 100644
index 0000000..e6273e9
--- /dev/null
+++ b/NativeApp/Apple/Source/Classes/iOS/AppViewBase.h
@@ -0,0 +1,16 @@
+
+#import <UIKit/UIKit.h>
+#import "BaseView.h"
+
+@interface AppViewBase : BaseView
+
+@property (nonatomic) NSInteger animationFrameInterval;
+
+- (void) initApp:(int)deviceType;
+- (void) startAnimation;
+- (void) stopAnimation;
+- (void) terminate;
+- (void) render;
+- (NSString*)getError;
+
+@end
diff --git a/NativeApp/Apple/Source/Classes/iOS/AppViewBase.m b/NativeApp/Apple/Source/Classes/iOS/AppViewBase.m
new file mode 100644
index 0000000..e7d4c57
--- /dev/null
+++ b/NativeApp/Apple/Source/Classes/iOS/AppViewBase.m
@@ -0,0 +1,166 @@
+
+#import "AppViewBase.h"
+
+#include "NativeAppBase.h"
+#include <memory>
+#include <string>
+
+@interface AppViewBase ()
+{
+ std::unique_ptr<Diligent::NativeAppBase> _theApp;
+ NSInteger _animationFrameInterval;
+ CADisplayLink* _displayLink;
+ std::string _error;
+}
+@end
+
+@implementation AppViewBase
+
+- (void) initApp:(int)deviceType;
+{
+ try
+ {
+ _theApp.reset(Diligent::CreateApplication());
+ // Init our renderer.
+ _theApp->Initialize(deviceType, (__bridge void*)self.layer);
+ }
+ catch(std::runtime_error &err)
+ {
+ _error = err.what();
+ _theApp.reset();
+ }
+
+ [super stopAnimation];
+ _animationFrameInterval = 1;
+ _displayLink = nil;
+}
+
+- (void) render
+{
+ if(_theApp)
+ {
+ _theApp->Update();
+ _theApp->Render();
+ _theApp->Present();
+ }
+}
+
+- (void) layoutSubviews
+{
+ auto bounds = [self.layer bounds];
+ auto scale = [self.layer contentsScale];
+
+ if(_theApp)
+ {
+ _theApp->WindowResize(bounds.size.width * scale, bounds.size.height * scale);
+ }
+}
+
+- (NSInteger) animationFrameInterval
+{
+ return _animationFrameInterval;
+}
+
+- (void) setAnimationFrameInterval:(NSInteger)frameInterval
+{
+ // Frame interval defines how many display frames must pass between each time the
+ // display link fires. The display link will only fire 30 times a second when the
+ // frame internal is two on a display that refreshes 60 times a second. The default
+ // frame interval setting of one will fire 60 times a second when the display refreshes
+ // at 60 times a second. A frame interval setting of less than one results in undefined
+ // behavior.
+ if (frameInterval >= 1)
+ {
+ _animationFrameInterval = frameInterval;
+
+ if (self.animating)
+ {
+ [self stopAnimation];
+ [self startAnimation];
+ }
+ }
+}
+
+- (void) startAnimation
+{
+ if (!self.animating)
+ {
+ // Create the display link and set the callback to our drawView method
+ _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(drawView:)];
+
+ // Set it to our _animationFrameInterval
+ [_displayLink setFrameInterval:_animationFrameInterval];
+
+ // Have the display link run on the default runn loop (and the main thread)
+ [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
+
+ [super startAnimation];
+ }
+}
+
+- (void)stopAnimation
+{
+ if (self.animating)
+ {
+ [_displayLink invalidate];
+ _displayLink = nil;
+ [super stopAnimation];
+ }
+}
+
+- (void)terminate
+{
+ _theApp.reset();
+ [super terminate];
+}
+
+- (NSString*)getError
+{
+ return _error.empty() ? nil : [NSString stringWithFormat:@"%s", _error.c_str()];
+}
+
+- (void)touchesBegan:(NSSet<UITouch *> *)touches
+ withEvent:(UIEvent *)event;
+{
+ UITouch *firstTouch = touches.allObjects[0];
+ CGPoint location = [firstTouch locationInView:self];
+ if(_theApp)
+ {
+ _theApp->OnTouchBegan(location.x, location.y);
+ }
+}
+
+- (void)touchesMoved:(NSSet<UITouch *> *)touches
+ withEvent:(UIEvent *)event;
+{
+ UITouch *firstTouch = touches.allObjects[0];
+ CGPoint location = [firstTouch locationInView:self];
+ if(_theApp)
+ {
+ _theApp->OnTouchMoved(location.x, location.y);
+ }
+}
+
+- (void)touchesEnded:(NSSet<UITouch *> *)touches
+ withEvent:(UIEvent *)event;
+{
+ UITouch *firstTouch = touches.allObjects[0];
+ CGPoint location = [firstTouch locationInView:self];
+ if(_theApp)
+ {
+ _theApp->OnTouchEnded(location.x, location.y);
+ }
+}
+
+- (void)touchesCancelled:(NSSet<UITouch *> *)touches
+ withEvent:(UIEvent *)event;
+{
+ UITouch *firstTouch = touches.allObjects[0];
+ CGPoint location = [firstTouch locationInView:self];
+ if(_theApp)
+ {
+ _theApp->OnTouchEnded(location.x, location.y);
+ }
+}
+
+@end
diff --git a/NativeApp/Apple/Source/Classes/iOS/BaseView.h b/NativeApp/Apple/Source/Classes/iOS/BaseView.h
new file mode 100644
index 0000000..9e8b99e
--- /dev/null
+++ b/NativeApp/Apple/Source/Classes/iOS/BaseView.h
@@ -0,0 +1,12 @@
+
+#import <UIKit/UIKit.h>
+
+@interface BaseView : UIView
+
+@property (readonly, nonatomic, getter=isAnimating) BOOL animating;
+
+- (void) startAnimation;
+- (void) stopAnimation;
+- (void) terminate;
+
+@end
diff --git a/NativeApp/Apple/Source/Classes/iOS/BaseView.m b/NativeApp/Apple/Source/Classes/iOS/BaseView.m
new file mode 100644
index 0000000..c87e5cd
--- /dev/null
+++ b/NativeApp/Apple/Source/Classes/iOS/BaseView.m
@@ -0,0 +1,21 @@
+
+#import "BaseView.h"
+
+@implementation BaseView
+
+- (void) startAnimation
+{
+ _animating = TRUE;
+}
+
+- (void)stopAnimation
+{
+ _animating = FALSE;
+}
+
+- (void) terminate
+{
+
+}
+
+@end
diff --git a/NativeApp/Apple/Source/Classes/iOS/EAGLView.h b/NativeApp/Apple/Source/Classes/iOS/EAGLView.h
new file mode 100644
index 0000000..e5ba68a
--- /dev/null
+++ b/NativeApp/Apple/Source/Classes/iOS/EAGLView.h
@@ -0,0 +1,18 @@
+/*
+ Copyright (C) 2015 Apple Inc. All Rights Reserved.
+ See LICENSE.txt for this sample’s licensing information
+
+ Abstract:
+ The EAGLView class is a UIView subclass that renders OpenGL scene.
+*/
+
+#import "AppViewBase.h"
+
+// This class wraps the CAEAGLLayer from CoreAnimation into a convenient UIView subclass.
+// The view content is basically an EAGL surface you render your OpenGL scene into.
+// Note that setting the view non-opaque will only work if the EAGL surface has an alpha channel.
+@interface EAGLView : AppViewBase
+
+- (void) drawView:(id)sender;
+
+@end
diff --git a/NativeApp/Apple/Source/Classes/iOS/EAGLView.m b/NativeApp/Apple/Source/Classes/iOS/EAGLView.m
new file mode 100644
index 0000000..ff02820
--- /dev/null
+++ b/NativeApp/Apple/Source/Classes/iOS/EAGLView.m
@@ -0,0 +1,74 @@
+/*
+ Copyright (C) 2015 Apple Inc. All Rights Reserved.
+ See LICENSE.txt for this sample’s licensing information
+
+ Abstract:
+ The EAGLView class is a UIView subclass that renders OpenGL scene.
+*/
+
+#import "EAGLView.h"
+
+#include "DeviceCaps.h"
+
+@interface EAGLView ()
+{
+ EAGLContext* _context;
+}
+@end
+
+@implementation EAGLView
+
+// Must return the CAEAGLLayer class so that CA allocates an EAGLLayer backing for this view
++ (Class) layerClass
+{
+ return [CAEAGLLayer class];
+}
+
+// The GL view is stored in the storyboard file. When it's unarchived it's sent -initWithCoder:
+- (instancetype) initWithCoder:(NSCoder*)coder
+{
+ if ((self = [super initWithCoder:coder]))
+ {
+ // Get the layer
+ CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
+
+ eaglLayer.opaque = TRUE;
+ eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithBool:FALSE], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
+
+ _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
+
+ if (!_context || ![EAGLContext setCurrentContext:_context])
+ {
+ return nil;
+ }
+
+ [self initApp:(int)Diligent::DeviceType::OpenGLES];
+ }
+
+ return self;
+}
+
+- (void) drawView:(id)sender
+{
+ [EAGLContext setCurrentContext:_context];
+
+ // There is no autorelease pool when this method is called
+ // because it will be called from a background thread.
+ // It's important to create one or app can leak objects.
+ @autoreleasepool
+ {
+ [self render];
+ }
+}
+
+- (void) dealloc
+{
+ [self terminate];
+
+ // tear down context
+ if ([EAGLContext currentContext] == _context)
+ [EAGLContext setCurrentContext:nil];
+}
+
+@end
diff --git a/NativeApp/Apple/Source/Classes/iOS/MetalView.h b/NativeApp/Apple/Source/Classes/iOS/MetalView.h
new file mode 100644
index 0000000..a485b27
--- /dev/null
+++ b/NativeApp/Apple/Source/Classes/iOS/MetalView.h
@@ -0,0 +1,7 @@
+#import "AppViewBase.h"
+
+@interface MetalView : AppViewBase
+
+- (void) drawView:(id)sender;
+
+@end
diff --git a/NativeApp/Apple/Source/Classes/iOS/MetalView.m b/NativeApp/Apple/Source/Classes/iOS/MetalView.m
new file mode 100644
index 0000000..67c2a27
--- /dev/null
+++ b/NativeApp/Apple/Source/Classes/iOS/MetalView.m
@@ -0,0 +1,44 @@
+#import "MetalView.h"
+
+#if VULKAN_SUPPORTED
+#import <QuartzCore/CAMetalLayer.h>
+#endif
+
+#include "DeviceCaps.h"
+
+@implementation MetalView
+
+#if VULKAN_SUPPORTED
++ (Class) layerClass
+{
+ return [CAMetalLayer class];
+}
+#endif
+
+- (instancetype) initWithCoder:(NSCoder*)coder
+{
+ if ((self = [super initWithCoder:coder]))
+ {
+ [self initApp:(int)Diligent::DeviceType::Vulkan];
+ }
+
+ return self;
+}
+
+- (void) drawView:(id)sender
+{
+ // There is no autorelease pool when this method is called
+ // because it will be called from a background thread.
+ // It's important to create one or app can leak objects.
+ @autoreleasepool
+ {
+ [self render];
+ }
+}
+
+- (void) dealloc
+{
+ [self terminate];
+}
+
+@end
diff --git a/NativeApp/Apple/Source/Classes/iOS/ModeSelectionViewController.h b/NativeApp/Apple/Source/Classes/iOS/ModeSelectionViewController.h
new file mode 100644
index 0000000..4a08d7d
--- /dev/null
+++ b/NativeApp/Apple/Source/Classes/iOS/ModeSelectionViewController.h
@@ -0,0 +1,29 @@
+/* Copyright 2015-2019 Egor Yusov
+ *
+ * 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
+ *
+ * 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 OF ANY PROPRIETARY RIGHTS.
+ *
+ * In no event and under no legal theory, whether in tort (including negligence),
+ * contract, or otherwise, unless required by applicable law (such as deliberate
+ * and grossly negligent acts) or agreed to in writing, shall any Contributor be
+ * liable for any damages, including any direct, indirect, special, incidental,
+ * or consequential damages of any character arising as a result of this License or
+ * out of the use or inability to use the software (including but not limited to damages
+ * for loss of goodwill, work stoppage, computer failure or malfunction, or any and
+ * all other commercial damages or losses), even if such Contributor has been advised
+ * of the possibility of such damages.
+ */
+
+#import <UIKit/UIKit.h>
+
+@interface ModeSelectionViewController : UIViewController
+{
+}
+@end
diff --git a/NativeApp/Apple/Source/Classes/iOS/ModeSelectionViewController.mm b/NativeApp/Apple/Source/Classes/iOS/ModeSelectionViewController.mm
new file mode 100644
index 0000000..8958c10
--- /dev/null
+++ b/NativeApp/Apple/Source/Classes/iOS/ModeSelectionViewController.mm
@@ -0,0 +1,64 @@
+/* Copyright 2015-2019 Egor Yusov
+ *
+ * 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
+ *
+ * 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 OF ANY PROPRIETARY RIGHTS.
+ *
+ * In no event and under no legal theory, whether in tort (including negligence),
+ * contract, or otherwise, unless required by applicable law (such as deliberate
+ * and grossly negligent acts) or agreed to in writing, shall any Contributor be
+ * liable for any damages, including any direct, indirect, special, incidental,
+ * or consequential damages of any character arising as a result of this License or
+ * out of the use or inability to use the software (including but not limited to damages
+ * for loss of goodwill, work stoppage, computer failure or malfunction, or any and
+ * all other commercial damages or losses), even if such Contributor has been advised
+ * of the possibility of such damages.
+ */
+
+#import "ModeSelectionViewController.h"
+#import "BaseView.h"
+#import "AppViewBase.h"
+
+@implementation ModeSelectionViewController
+{
+}
+
+-(void)selectViewController:(NSString*)controllerID
+{
+ auto animating = ((BaseView*)self.view).animating;
+
+ UIViewController* viewController = [self.storyboard instantiateViewControllerWithIdentifier:controllerID];
+ self.view.window.rootViewController = viewController;
+
+ NSString *error = [(AppViewBase*)viewController.view getError];
+ if(error != nil)
+ {
+ UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Failed to start the application" message:error delegate:self cancelButtonTitle:@"OK" otherButtonTitles:@"Whatever", nil];
+ [alert show];
+ }
+
+ if(animating)
+ {
+ [(BaseView*)viewController.view startAnimation];
+ }
+}
+
+- (IBAction)goOpenGLES:(id)sender
+{
+ [self selectViewController:@"EAGLViewControllerID"];
+}
+
+- (IBAction)goVulkan:(id)sender
+{
+#if VULKAN_SUPPORTED
+ [self selectViewController:@"MetalViewControllerID"];
+#endif
+}
+
+@end
diff --git a/NativeApp/Apple/Source/main.m b/NativeApp/Apple/Source/main.m
new file mode 100644
index 0000000..7134864
--- /dev/null
+++ b/NativeApp/Apple/Source/main.m
@@ -0,0 +1,25 @@
+/*
+ Copyright (C) 2015 Apple Inc. All Rights Reserved.
+ See LICENSE.txt for this sample’s licensing information
+
+ Abstract:
+ Standard AppKit entry point.
+ */
+
+#if PLATFORM_IOS
+#import <UIKit/UIKit.h>
+#import "AppDelegate.h"
+#else // OS X
+#import <Cocoa/Cocoa.h>
+#endif
+
+int main(int argc, char * argv[]) {
+
+#if PLATFORM_IOS
+ @autoreleasepool {
+ return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
+ }
+#else
+ return NSApplicationMain(argc, (const char**)argv);
+#endif
+}