Fixes for vulkanExample: frame timing now equals diff between frames for Win & macOS portability, support vsync off rendering on macOS, support swapchain image count change on resize, handle macOS fullscreen; Fixes for xcode example: use PanGestureRecognizer on iOS, add macOS cursor tracking, cleanup Vulkan on shutdown
This commit is contained in:
parent
a1e19ea5de
commit
cb343c329a
15 changed files with 222 additions and 87 deletions
|
|
@ -17,10 +17,6 @@ void MVKExample::displayLinkOutputCb() { // SRS - expose
|
|||
_vulkanExample->displayLinkOutputCb();
|
||||
}
|
||||
|
||||
void MVKExample::setRefreshPeriod(double refreshPeriod) { // SRS - set VulkanExampleBase::refreshPeriod from DemoViewController displayLink
|
||||
_vulkanExample->refreshPeriod = refreshPeriod;
|
||||
}
|
||||
|
||||
void MVKExample::keyPressed(uint32_t keyChar) { // SRS - handle keyboard key presses only (e.g. Pause, Space, etc)
|
||||
switch (keyChar)
|
||||
{
|
||||
|
|
@ -113,12 +109,17 @@ void MVKExample::scrollWheel(short wheelDelta) {
|
|||
_vulkanExample->camera.translate(glm::vec3(0.0f, 0.0f, wheelDelta * 0.05f * _vulkanExample->camera.movementSpeed));
|
||||
}
|
||||
|
||||
void MVKExample::fullScreen(bool fullscreen) {
|
||||
_vulkanExample->settings.fullscreen = fullscreen;
|
||||
}
|
||||
|
||||
MVKExample::MVKExample(void* view) {
|
||||
_vulkanExample = new VulkanExample();
|
||||
_vulkanExample->settings.vsync = true; // SRS - this iOS/macOS example uses displayLink vsync rendering - set before calling prepare()
|
||||
_vulkanExample->initVulkan();
|
||||
_vulkanExample->setupWindow(view);
|
||||
_vulkanExample->prepare();
|
||||
_vulkanExample->renderLoop(); // SRS - init VulkanExampleBase::destWidth & destHeight, then fall through and return
|
||||
_vulkanExample->renderLoop(); // SRS - this inits destWidth/destHeight/lastTimestamp/tPrevEnd, then falls through and returns
|
||||
}
|
||||
|
||||
MVKExample::~MVKExample() {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ class MVKExample {
|
|||
public:
|
||||
void renderFrame();
|
||||
void displayLinkOutputCb(); // SRS - expose VulkanExampleBase::displayLinkOutputCb() to DemoViewController
|
||||
void setRefreshPeriod(double refreshPeriod); // SRS - set VulkanExampleBase::refreshPeriod from DemoViewController displayLink
|
||||
|
||||
void keyPressed(uint32_t keyChar); // SRS - expose keyboard events to DemoViewController
|
||||
void keyDown(uint32_t keyChar);
|
||||
|
|
@ -29,7 +28,9 @@ public:
|
|||
void otherMouseUp();
|
||||
void mouseDragged(double x, double y);
|
||||
void scrollWheel(short wheelDelta);
|
||||
|
||||
|
||||
void fullScreen(bool fullscreen); // SRS - expose VulkanExampleBase::settings.fullscreen to DemoView (macOS only)
|
||||
|
||||
MVKExample(void* view);
|
||||
~MVKExample();
|
||||
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@
|
|||
# include "../examples/particlefire/particlefire.cpp"
|
||||
#endif
|
||||
|
||||
// Build issue when using this xcode examples project, builds/runs fine using vulkanExamples project.
|
||||
// No headless target when using xcode examples project, builds/runs fine using vulkanExamples project.
|
||||
#ifdef MVK_renderheadless
|
||||
# include "../examples/renderheadless/renderheadless.cpp"
|
||||
#endif
|
||||
|
|
@ -191,7 +191,7 @@
|
|||
# include "../examples/vertexattributes/vertexattributes.cpp"
|
||||
#endif
|
||||
|
||||
// Runs but nothing displays. MoltenVK format VK_FORMAT_R32_UINT doesn't contain VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT
|
||||
// Does not run. MoltenVK/Metal does not support stores and atomic operations in the fragment stage.
|
||||
#ifdef MVK_oit
|
||||
# include "../examples/oit/oit.cpp"
|
||||
#endif
|
||||
|
|
@ -297,7 +297,7 @@
|
|||
# include "../examples/computecloth/computecloth.cpp"
|
||||
#endif
|
||||
|
||||
// Build issue when using this xcode examples project, builds/runs fine using vulkanExamples project.
|
||||
// No headless target when using xcode examples project, builds/runs fine using vulkanExamples project.
|
||||
#ifdef MVK_computeheadless
|
||||
# include "../examples/computeheadless/computeheadless.cpp"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
||||
|
||||
@property (strong, nonatomic) UIWindow *window;
|
||||
@property (strong, nonatomic) UIViewController *viewController;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
#import "AppDelegate.h"
|
||||
#import "DemoViewController.h"
|
||||
|
||||
@interface AppDelegate ()
|
||||
|
||||
|
|
@ -39,6 +40,7 @@
|
|||
|
||||
- (void)applicationWillTerminate:(UIApplication *)application {
|
||||
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
|
||||
[(DemoViewController *)_viewController shutdownExample];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
/** The main view controller for the demo storyboard. */
|
||||
@interface DemoViewController : UIViewController <UIKeyInput>
|
||||
-(void) shutdownExample;
|
||||
@end
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
#import "DemoViewController.h"
|
||||
#import "AppDelegate.h"
|
||||
|
||||
#include "MVKExample.h"
|
||||
|
||||
|
|
@ -22,6 +23,7 @@ const std::string getAssetPath() {
|
|||
MVKExample* _mvkExample;
|
||||
CADisplayLink* _displayLink;
|
||||
BOOL _viewHasAppeared;
|
||||
CGPoint _startPoint;
|
||||
}
|
||||
|
||||
/** Since this is a single-view app, init Vulkan when the view is loaded. */
|
||||
|
|
@ -31,26 +33,37 @@ const std::string getAssetPath() {
|
|||
self.view.contentScaleFactor = UIScreen.mainScreen.nativeScale;
|
||||
|
||||
_mvkExample = new MVKExample(self.view);
|
||||
|
||||
|
||||
// SRS - Enable AppDelegate to call into DemoViewController for handling app lifecycle events (e.g. termination)
|
||||
auto appDelegate = (AppDelegate *)UIApplication.sharedApplication.delegate;
|
||||
appDelegate.viewController = self;
|
||||
|
||||
uint32_t fps = 60;
|
||||
_displayLink = [CADisplayLink displayLinkWithTarget: self selector: @selector(renderFrame)];
|
||||
[_displayLink setFrameInterval: 60 / fps];
|
||||
[_displayLink addToRunLoop: NSRunLoop.currentRunLoop forMode: NSDefaultRunLoopMode];
|
||||
|
||||
// SRS - set VulkanExampleBase::refreshPeriod to calibrate the frame animation rate
|
||||
_mvkExample->setRefreshPeriod( 1.0 / fps );
|
||||
|
||||
// Setup double tap gesture to toggle virtual keyboard
|
||||
UITapGestureRecognizer* tapSelector = [[UITapGestureRecognizer alloc]
|
||||
initWithTarget: self action: @selector(handleTapGesture:)];
|
||||
UITapGestureRecognizer* tapSelector = [[[UITapGestureRecognizer alloc]
|
||||
initWithTarget: self action: @selector(handleTapGesture:)] autorelease];
|
||||
tapSelector.numberOfTapsRequired = 2;
|
||||
tapSelector.cancelsTouchesInView = YES;
|
||||
tapSelector.requiresExclusiveTouchType = YES;
|
||||
[self.view addGestureRecognizer: tapSelector];
|
||||
|
||||
// SRS - Setup pan gesture to detect and activate translation
|
||||
UIPanGestureRecognizer* panSelector = [[[UIPanGestureRecognizer alloc]
|
||||
initWithTarget: self action: @selector(handlePanGesture:)] autorelease];
|
||||
panSelector.minimumNumberOfTouches = 2;
|
||||
panSelector.cancelsTouchesInView = YES;
|
||||
panSelector.requiresExclusiveTouchType = YES;
|
||||
[self.view addGestureRecognizer: panSelector];
|
||||
|
||||
// SRS - Setup pinch gesture to detect and activate zoom
|
||||
UIPinchGestureRecognizer* pinchSelector = [[UIPinchGestureRecognizer alloc]
|
||||
initWithTarget: self action: @selector(handlePinchGesture:)];
|
||||
UIPinchGestureRecognizer* pinchSelector = [[[UIPinchGestureRecognizer alloc]
|
||||
initWithTarget: self action: @selector(handlePinchGesture:)] autorelease];
|
||||
pinchSelector.cancelsTouchesInView = YES;
|
||||
pinchSelector.requiresExclusiveTouchType = YES;
|
||||
[self.view addGestureRecognizer: pinchSelector];
|
||||
|
||||
_viewHasAppeared = NO;
|
||||
|
|
@ -68,9 +81,9 @@ const std::string getAssetPath() {
|
|||
_mvkExample->displayLinkOutputCb(); // SRS - Call displayLinkOutputCb() to animate frames vs. renderFrame() for static image
|
||||
}
|
||||
|
||||
-(void) dealloc {
|
||||
delete _mvkExample;
|
||||
[super dealloc];
|
||||
-(void) shutdownExample {
|
||||
[_displayLink invalidate];
|
||||
delete _mvkExample;
|
||||
}
|
||||
|
||||
// Toggle the display of the virtual keyboard
|
||||
|
|
@ -109,58 +122,84 @@ const std::string getAssetPath() {
|
|||
|
||||
#pragma mark UITouch methods
|
||||
|
||||
// SRS - Handle touch events
|
||||
-(CGPoint) getTouchLocalPoint:(UIEvent*) theEvent {
|
||||
UITouch *touch = [[theEvent allTouches] anyObject];
|
||||
CGPoint point = [touch locationInView:self.view];
|
||||
point.x = point.x * self.view.contentScaleFactor;
|
||||
point.y = point.y * self.view.contentScaleFactor;
|
||||
CGPoint point = [touch locationInView: self.view];
|
||||
point.x *= self.view.contentScaleFactor;
|
||||
point.y *= self.view.contentScaleFactor;
|
||||
return point;
|
||||
}
|
||||
|
||||
// SRS - Handle touch events
|
||||
-(void) touchesBegan:(NSSet*) touches withEvent:(UIEvent*) theEvent {
|
||||
auto point = [self getTouchLocalPoint:theEvent];
|
||||
if (touches.count == 1) {
|
||||
// Single touch for imgui select and camera rotation
|
||||
auto point = [self getTouchLocalPoint: theEvent];
|
||||
_mvkExample->mouseDown(point.x, point.y);
|
||||
}
|
||||
else {
|
||||
// Multi-touch for swipe translation (note: pinch gesture will cancel/override)
|
||||
_mvkExample->otherMouseDown(point.x, point.y);
|
||||
}
|
||||
}
|
||||
|
||||
-(void) touchesMoved:(NSSet*) touches withEvent:(UIEvent*) theEvent {
|
||||
auto point = [self getTouchLocalPoint:theEvent];
|
||||
_mvkExample->mouseDragged(point.x, point.y);
|
||||
if (touches.count == 1) {
|
||||
auto point = [self getTouchLocalPoint: theEvent];
|
||||
_mvkExample->mouseDragged(point.x, point.y);
|
||||
}
|
||||
}
|
||||
|
||||
-(void) touchesEnded:(NSSet*) touches withEvent:(UIEvent*) theEvent {
|
||||
_mvkExample->mouseUp();
|
||||
_mvkExample->otherMouseUp();
|
||||
}
|
||||
|
||||
-(void) touchesCancelled:(NSSet*) touches withEvent:(UIEvent*) theEvent {
|
||||
_mvkExample->mouseUp();
|
||||
_mvkExample->otherMouseUp();
|
||||
}
|
||||
|
||||
#pragma mark UIGesture methods
|
||||
|
||||
-(CGPoint) getGestureLocalPoint:(UIGestureRecognizer*) gestureRecognizer {
|
||||
CGPoint point = [gestureRecognizer locationInView: self.view];
|
||||
point.x *= self.view.contentScaleFactor;
|
||||
point.y *= self.view.contentScaleFactor;
|
||||
return point;
|
||||
}
|
||||
|
||||
// SRS - Respond to pan gestures for translation
|
||||
-(void) handlePanGesture: (UIPanGestureRecognizer*) gestureRecognizer {
|
||||
switch (gestureRecognizer.state) {
|
||||
case UIGestureRecognizerStateBegan: {
|
||||
_startPoint = [self getGestureLocalPoint: gestureRecognizer];
|
||||
_mvkExample->otherMouseDown(_startPoint.x, _startPoint.y);
|
||||
break;
|
||||
}
|
||||
case UIGestureRecognizerStateChanged: {
|
||||
auto translation = [gestureRecognizer translationInView: self.view];
|
||||
translation.x *= self.view.contentScaleFactor;
|
||||
translation.y *= self.view.contentScaleFactor;
|
||||
_mvkExample->mouseDragged(_startPoint.x + translation.x, _startPoint.y + translation.y);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
_mvkExample->otherMouseUp();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SRS - Respond to pinch gestures for zoom
|
||||
-(void) handlePinchGesture: (UIPinchGestureRecognizer*) gestureRecognizer {
|
||||
if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
|
||||
auto point = [gestureRecognizer locationInView:self.view];
|
||||
point.x = point.x * self.view.contentScaleFactor;
|
||||
point.y = point.y * self.view.contentScaleFactor;
|
||||
_mvkExample->rightMouseDown(point.x, point.y);
|
||||
}
|
||||
else if (gestureRecognizer.state == UIGestureRecognizerStateChanged) {
|
||||
auto point = [gestureRecognizer locationInView:self.view];
|
||||
point.x = point.x * self.view.contentScaleFactor;
|
||||
point.y = point.y * self.view.contentScaleFactor;
|
||||
_mvkExample->mouseDragged(point.x, point.y - gestureRecognizer.view.frame.size.height / 2.0 * log(gestureRecognizer.scale));
|
||||
}
|
||||
else if (gestureRecognizer.state == UIGestureRecognizerStateEnded) {
|
||||
_mvkExample->rightMouseUp();
|
||||
switch (gestureRecognizer.state) {
|
||||
case UIGestureRecognizerStateBegan: {
|
||||
_startPoint = [self getGestureLocalPoint: gestureRecognizer];
|
||||
_mvkExample->rightMouseDown(_startPoint.x, _startPoint.y);
|
||||
break;
|
||||
}
|
||||
case UIGestureRecognizerStateChanged: {
|
||||
_mvkExample->mouseDragged(_startPoint.x, _startPoint.y - self.view.frame.size.height * log(gestureRecognizer.scale));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
_mvkExample->rightMouseUp();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,4 +9,6 @@
|
|||
|
||||
@interface AppDelegate : NSObject <NSApplicationDelegate>
|
||||
|
||||
@property (strong, nonatomic) NSViewController *viewController;
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
#import "AppDelegate.h"
|
||||
#import "DemoViewController.h"
|
||||
|
||||
@interface AppDelegate ()
|
||||
|
||||
|
|
@ -19,6 +20,7 @@
|
|||
|
||||
- (void)applicationWillTerminate:(NSNotification *)aNotification {
|
||||
// Insert code here to tear down your application
|
||||
[(DemoViewController *)_viewController shutdownExample];
|
||||
}
|
||||
|
||||
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
/** The main view controller for the demo storyboard. */
|
||||
@interface DemoViewController : NSViewController
|
||||
-(void) shutdownExample;
|
||||
@end
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
#import "DemoViewController.h"
|
||||
#import "AppDelegate.h"
|
||||
#import <QuartzCore/CAMetalLayer.h>
|
||||
|
||||
#include "MVKExample.h"
|
||||
|
|
@ -43,25 +44,19 @@ MVKExample* _mvkExample;
|
|||
|
||||
_mvkExample = new MVKExample(self.view);
|
||||
|
||||
// SRS - Enable AppDelegate to call into DemoViewController for handling application lifecycle events (e.g. termination)
|
||||
auto appDelegate = (AppDelegate *)NSApplication.sharedApplication.delegate;
|
||||
appDelegate.viewController = self;
|
||||
|
||||
CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink);
|
||||
CVDisplayLinkSetOutputCallback(_displayLink, &DisplayLinkCallback, _mvkExample);
|
||||
CVDisplayLinkStart(_displayLink);
|
||||
}
|
||||
|
||||
// SRS - Center the window and set VulkanExampleBase::refreshPeriod from the active displayLink
|
||||
-(void) viewWillAppear {
|
||||
[super viewWillAppear];
|
||||
|
||||
NSWindow* window = self.view.window;
|
||||
[window center];
|
||||
|
||||
_mvkExample->setRefreshPeriod(CVDisplayLinkGetActualOutputVideoRefreshPeriod(_displayLink));
|
||||
}
|
||||
|
||||
-(void) dealloc {
|
||||
-(void) shutdownExample {
|
||||
CVDisplayLinkStop(_displayLink);
|
||||
CVDisplayLinkRelease(_displayLink);
|
||||
delete _mvkExample;
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -86,6 +81,15 @@ MVKExample* _mvkExample;
|
|||
return layer;
|
||||
}
|
||||
|
||||
// SRS - Activate mouse cursor tracking within the view, set view as window delegate, and center the window
|
||||
- (void) viewDidMoveToWindow {
|
||||
auto trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] options: (NSTrackingMouseMoved | NSTrackingActiveAlways | NSTrackingInVisibleRect) owner:self userInfo:nil];
|
||||
[self addTrackingArea: trackingArea];
|
||||
|
||||
[self.window setDelegate: self.window.contentView];
|
||||
[self.window center];
|
||||
}
|
||||
|
||||
-(BOOL) acceptsFirstResponder { return YES; }
|
||||
|
||||
// SRS - Handle keyboard events
|
||||
|
|
@ -151,9 +155,24 @@ MVKExample* _mvkExample;
|
|||
_mvkExample->mouseDragged(point.x, point.y);
|
||||
}
|
||||
|
||||
-(void) mouseMoved:(NSEvent*) theEvent {
|
||||
auto point = [self getMouseLocalPoint:theEvent];
|
||||
_mvkExample->mouseDragged(point.x, point.y);
|
||||
}
|
||||
|
||||
-(void) scrollWheel:(NSEvent*) theEvent {
|
||||
short wheelDelta = [theEvent deltaY];
|
||||
_mvkExample->scrollWheel(wheelDelta);
|
||||
}
|
||||
|
||||
- (void)windowWillEnterFullScreen:(NSNotification *)notification
|
||||
{
|
||||
_mvkExample->fullScreen(true);
|
||||
}
|
||||
|
||||
- (void)windowWillExitFullScreen:(NSNotification *)notification
|
||||
{
|
||||
_mvkExample->fullScreen(false);
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue