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
|
|
@ -229,7 +229,7 @@ void VulkanSwapChain::connect(VkInstance instance, VkPhysicalDevice physicalDevi
|
||||||
* @param height Pointer to the height of the swapchain (may be adjusted to fit the requirements of the swapchain)
|
* @param height Pointer to the height of the swapchain (may be adjusted to fit the requirements of the swapchain)
|
||||||
* @param vsync (Optional) Can be used to force vsync-ed rendering (by using VK_PRESENT_MODE_FIFO_KHR as presentation mode)
|
* @param vsync (Optional) Can be used to force vsync-ed rendering (by using VK_PRESENT_MODE_FIFO_KHR as presentation mode)
|
||||||
*/
|
*/
|
||||||
void VulkanSwapChain::create(uint32_t *width, uint32_t *height, bool vsync)
|
void VulkanSwapChain::create(uint32_t *width, uint32_t *height, bool vsync, bool fullscreen)
|
||||||
{
|
{
|
||||||
// Store the current swap chain handle so we can use it later on to ease up recreation
|
// Store the current swap chain handle so we can use it later on to ease up recreation
|
||||||
VkSwapchainKHR oldSwapchain = swapChain;
|
VkSwapchainKHR oldSwapchain = swapChain;
|
||||||
|
|
@ -290,6 +290,13 @@ void VulkanSwapChain::create(uint32_t *width, uint32_t *height, bool vsync)
|
||||||
|
|
||||||
// Determine the number of images
|
// Determine the number of images
|
||||||
uint32_t desiredNumberOfSwapchainImages = surfCaps.minImageCount + 1;
|
uint32_t desiredNumberOfSwapchainImages = surfCaps.minImageCount + 1;
|
||||||
|
#if (defined(VK_USE_PLATFORM_MACOS_MVK) && defined(VK_EXAMPLE_XCODE_GENERATED))
|
||||||
|
// SRS - Work around known MoltenVK issue re 2x frame rate when vsync (VK_PRESENT_MODE_FIFO_KHR) enabled in windowed mode
|
||||||
|
if (vsync && !fullscreen)
|
||||||
|
{
|
||||||
|
desiredNumberOfSwapchainImages = surfCaps.minImageCount;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if ((surfCaps.maxImageCount > 0) && (desiredNumberOfSwapchainImages > surfCaps.maxImageCount))
|
if ((surfCaps.maxImageCount > 0) && (desiredNumberOfSwapchainImages > surfCaps.maxImageCount))
|
||||||
{
|
{
|
||||||
desiredNumberOfSwapchainImages = surfCaps.maxImageCount;
|
desiredNumberOfSwapchainImages = surfCaps.maxImageCount;
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ public:
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
void connect(VkInstance instance, VkPhysicalDevice physicalDevice, VkDevice device);
|
void connect(VkInstance instance, VkPhysicalDevice physicalDevice, VkDevice device);
|
||||||
void create(uint32_t* width, uint32_t* height, bool vsync = false);
|
void create(uint32_t* width, uint32_t* height, bool vsync = false, bool fullscreen = false);
|
||||||
VkResult acquireNextImage(VkSemaphore presentCompleteSemaphore, uint32_t* imageIndex);
|
VkResult acquireNextImage(VkSemaphore presentCompleteSemaphore, uint32_t* imageIndex);
|
||||||
VkResult queuePresent(VkQueue queue, uint32_t imageIndex, VkSemaphore waitSemaphore = VK_NULL_HANDLE);
|
VkResult queuePresent(VkQueue queue, uint32_t imageIndex, VkSemaphore waitSemaphore = VK_NULL_HANDLE);
|
||||||
void cleanup();
|
void cleanup();
|
||||||
|
|
|
||||||
|
|
@ -255,7 +255,7 @@ VkPipelineShaderStageCreateInfo VulkanExampleBase::loadShader(std::string fileNa
|
||||||
|
|
||||||
void VulkanExampleBase::nextFrame()
|
void VulkanExampleBase::nextFrame()
|
||||||
{
|
{
|
||||||
auto tStart = std::chrono::high_resolution_clock::now();
|
//auto tStart = std::chrono::high_resolution_clock::now();
|
||||||
if (viewUpdated)
|
if (viewUpdated)
|
||||||
{
|
{
|
||||||
viewUpdated = false;
|
viewUpdated = false;
|
||||||
|
|
@ -265,12 +265,10 @@ void VulkanExampleBase::nextFrame()
|
||||||
render();
|
render();
|
||||||
frameCounter++;
|
frameCounter++;
|
||||||
auto tEnd = std::chrono::high_resolution_clock::now();
|
auto tEnd = std::chrono::high_resolution_clock::now();
|
||||||
auto tDiff = std::chrono::duration<double, std::milli>(tEnd - tStart).count();
|
// SRS - Calculate tDiff as time between frames vs. rendering time for Win32/macOS/iOS vsync portability
|
||||||
#if (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK))
|
//auto tDiff = std::chrono::duration<double, std::milli>(tEnd - tStart).count();
|
||||||
frameTimer = (float)tDiff * refreshPeriod; // SRS - Multiply by refresh period due to displayLink callback rendering on iOS and macOS
|
auto tDiff = std::chrono::duration<double, std::milli>(tEnd - tPrevEnd).count();
|
||||||
#else
|
|
||||||
frameTimer = (float)tDiff / 1000.0f;
|
frameTimer = (float)tDiff / 1000.0f;
|
||||||
#endif
|
|
||||||
camera.update(frameTimer);
|
camera.update(frameTimer);
|
||||||
if (camera.moving())
|
if (camera.moving())
|
||||||
{
|
{
|
||||||
|
|
@ -298,6 +296,8 @@ void VulkanExampleBase::nextFrame()
|
||||||
frameCounter = 0;
|
frameCounter = 0;
|
||||||
lastTimestamp = tEnd;
|
lastTimestamp = tEnd;
|
||||||
}
|
}
|
||||||
|
tPrevEnd = tEnd;
|
||||||
|
|
||||||
// TODO: Cap UI overlay update rates
|
// TODO: Cap UI overlay update rates
|
||||||
updateOverlay();
|
updateOverlay();
|
||||||
}
|
}
|
||||||
|
|
@ -316,6 +316,7 @@ void VulkanExampleBase::renderLoop()
|
||||||
destWidth = width;
|
destWidth = width;
|
||||||
destHeight = height;
|
destHeight = height;
|
||||||
lastTimestamp = std::chrono::high_resolution_clock::now();
|
lastTimestamp = std::chrono::high_resolution_clock::now();
|
||||||
|
tPrevEnd = lastTimestamp;
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
MSG msg;
|
MSG msg;
|
||||||
bool quitMessageReceived = false;
|
bool quitMessageReceived = false;
|
||||||
|
|
@ -733,10 +734,13 @@ void VulkanExampleBase::prepareFrame()
|
||||||
{
|
{
|
||||||
// Acquire the next image from the swap chain
|
// Acquire the next image from the swap chain
|
||||||
VkResult result = swapChain.acquireNextImage(semaphores.presentComplete, ¤tBuffer);
|
VkResult result = swapChain.acquireNextImage(semaphores.presentComplete, ¤tBuffer);
|
||||||
// Recreate the swapchain if it's no longer compatible with the surface (OUT_OF_DATE) or no longer optimal for presentation (SUBOPTIMAL)
|
// Recreate the swapchain if it's no longer compatible with the surface (OUT_OF_DATE)
|
||||||
|
// SRS - If no longer optimal (VK_SUBOPTIMAL_KHR), wait until submitFrame() in case number of swapchain images will change on resize
|
||||||
if ((result == VK_ERROR_OUT_OF_DATE_KHR) || (result == VK_SUBOPTIMAL_KHR)) {
|
if ((result == VK_ERROR_OUT_OF_DATE_KHR) || (result == VK_SUBOPTIMAL_KHR)) {
|
||||||
|
if (result == VK_ERROR_OUT_OF_DATE_KHR) {
|
||||||
windowResize();
|
windowResize();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
VK_CHECK_RESULT(result);
|
VK_CHECK_RESULT(result);
|
||||||
}
|
}
|
||||||
|
|
@ -745,15 +749,16 @@ void VulkanExampleBase::prepareFrame()
|
||||||
void VulkanExampleBase::submitFrame()
|
void VulkanExampleBase::submitFrame()
|
||||||
{
|
{
|
||||||
VkResult result = swapChain.queuePresent(queue, currentBuffer, semaphores.renderComplete);
|
VkResult result = swapChain.queuePresent(queue, currentBuffer, semaphores.renderComplete);
|
||||||
if (!((result == VK_SUCCESS) || (result == VK_SUBOPTIMAL_KHR))) {
|
// Recreate the swapchain if it's no longer compatible with the surface (OUT_OF_DATE) or no longer optimal for presentation (SUBOPTIMAL)
|
||||||
if (result == VK_ERROR_OUT_OF_DATE_KHR) {
|
if ((result == VK_ERROR_OUT_OF_DATE_KHR) || (result == VK_SUBOPTIMAL_KHR)) {
|
||||||
// Swap chain is no longer compatible with the surface and needs to be recreated
|
|
||||||
windowResize();
|
windowResize();
|
||||||
|
if (result == VK_ERROR_OUT_OF_DATE_KHR) {
|
||||||
return;
|
return;
|
||||||
} else {
|
|
||||||
VK_CHECK_RESULT(result);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
VK_CHECK_RESULT(result);
|
||||||
|
}
|
||||||
VK_CHECK_RESULT(vkQueueWaitIdle(queue));
|
VK_CHECK_RESULT(vkQueueWaitIdle(queue));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1524,6 +1529,8 @@ void VulkanExampleBase::handleAppCommand(android_app * app, int32_t cmd)
|
||||||
#if defined(VK_EXAMPLE_XCODE_GENERATED)
|
#if defined(VK_EXAMPLE_XCODE_GENERATED)
|
||||||
@interface AppDelegate : NSObject<NSApplicationDelegate>
|
@interface AppDelegate : NSObject<NSApplicationDelegate>
|
||||||
{
|
{
|
||||||
|
@public
|
||||||
|
VulkanExampleBase *vulkanExample;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
@ -1532,11 +1539,35 @@ void VulkanExampleBase::handleAppCommand(android_app * app, int32_t cmd)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SRS - Dispatch rendering loop onto a queue for max frame rate concurrent rendering vs displayLink vsync rendering
|
||||||
|
// - vsync command line option (-vs) on macOS now works like other platforms (using VK_PRESENT_MODE_FIFO_KHR)
|
||||||
|
dispatch_group_t concurrentGroup;
|
||||||
|
|
||||||
|
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
|
||||||
|
{
|
||||||
|
concurrentGroup = dispatch_group_create();
|
||||||
|
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0);
|
||||||
|
dispatch_group_async(concurrentGroup, concurrentQueue, ^{
|
||||||
|
|
||||||
|
while (!vulkanExample->quit) {
|
||||||
|
vulkanExample->displayLinkOutputCb();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
|
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
|
||||||
{
|
{
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SRS - Tell rendering loop to quit, then wait for concurrent queue to terminate before deleting vulkanExample
|
||||||
|
- (void)applicationWillTerminate:(NSNotification *)aNotification
|
||||||
|
{
|
||||||
|
vulkanExample->quit = YES;
|
||||||
|
dispatch_group_wait(concurrentGroup, DISPATCH_TIME_FOREVER);
|
||||||
|
delete vulkanExample;
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
const std::string getAssetPath() {
|
const std::string getAssetPath() {
|
||||||
|
|
@ -1582,11 +1613,10 @@ static CVReturn displayLinkOutputCallback(CVDisplayLinkRef displayLink, const CV
|
||||||
- (void)viewDidMoveToWindow
|
- (void)viewDidMoveToWindow
|
||||||
{
|
{
|
||||||
CVDisplayLinkCreateWithActiveCGDisplays(&displayLink);
|
CVDisplayLinkCreateWithActiveCGDisplays(&displayLink);
|
||||||
CVDisplayLinkSetOutputCallback(displayLink, &displayLinkOutputCallback, vulkanExample);
|
// SRS - Disable displayLink vsync rendering in favour of max frame rate concurrent rendering
|
||||||
|
// - vsync command line option (-vs) on macOS now works like other platforms (using VK_PRESENT_MODE_FIFO_KHR)
|
||||||
|
//CVDisplayLinkSetOutputCallback(displayLink, &displayLinkOutputCallback, vulkanExample);
|
||||||
CVDisplayLinkStart(displayLink);
|
CVDisplayLinkStart(displayLink);
|
||||||
// SRS - Pause 1 ms for displayLink startup then get the actual refresh period of the display
|
|
||||||
usleep(1000);
|
|
||||||
vulkanExample->refreshPeriod = CVDisplayLinkGetActualOutputVideoRefreshPeriod(displayLink);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)acceptsFirstResponder
|
- (BOOL)acceptsFirstResponder
|
||||||
|
|
@ -1661,13 +1691,13 @@ static CVReturn displayLinkOutputCallback(CVDisplayLinkRef displayLink, const CV
|
||||||
|
|
||||||
- (void)mouseUp:(NSEvent *)event
|
- (void)mouseUp:(NSEvent *)event
|
||||||
{
|
{
|
||||||
auto point = [self getMouseLocalPoint:event];
|
|
||||||
vulkanExample->mousePos = glm::vec2(point.x, point.y);
|
|
||||||
vulkanExample->mouseButtons.left = false;
|
vulkanExample->mouseButtons.left = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)rightMouseDown:(NSEvent *)event
|
- (void)rightMouseDown:(NSEvent *)event
|
||||||
{
|
{
|
||||||
|
auto point = [self getMouseLocalPoint:event];
|
||||||
|
vulkanExample->mousePos = glm::vec2(point.x, point.y);
|
||||||
vulkanExample->mouseButtons.right = true;
|
vulkanExample->mouseButtons.right = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1678,6 +1708,8 @@ static CVReturn displayLinkOutputCallback(CVDisplayLinkRef displayLink, const CV
|
||||||
|
|
||||||
- (void)otherMouseDown:(NSEvent *)event
|
- (void)otherMouseDown:(NSEvent *)event
|
||||||
{
|
{
|
||||||
|
auto point = [self getMouseLocalPoint:event];
|
||||||
|
vulkanExample->mousePos = glm::vec2(point.x, point.y);
|
||||||
vulkanExample->mouseButtons.middle = true;
|
vulkanExample->mouseButtons.middle = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1717,6 +1749,9 @@ static CVReturn displayLinkOutputCallback(CVDisplayLinkRef displayLink, const CV
|
||||||
-(float)wheelDelta * 0.05f * vulkanExample->camera.movementSpeed));
|
-(float)wheelDelta * 0.05f * vulkanExample->camera.movementSpeed));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SRS - Window resizing already handled by windowResize() in VulkanExampleBase::submitFrame()
|
||||||
|
// - handling window resize events here is redundant and can cause interaction problems
|
||||||
|
/*
|
||||||
- (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)frameSize
|
- (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)frameSize
|
||||||
{
|
{
|
||||||
CVDisplayLinkStop(displayLink);
|
CVDisplayLinkStop(displayLink);
|
||||||
|
|
@ -1729,6 +1764,17 @@ static CVReturn displayLinkOutputCallback(CVDisplayLinkRef displayLink, const CV
|
||||||
vulkanExample->windowDidResize();
|
vulkanExample->windowDidResize();
|
||||||
CVDisplayLinkStart(displayLink);
|
CVDisplayLinkStart(displayLink);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
- (void)windowWillEnterFullScreen:(NSNotification *)notification
|
||||||
|
{
|
||||||
|
vulkanExample->settings.fullscreen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)windowWillExitFullScreen:(NSNotification *)notification
|
||||||
|
{
|
||||||
|
vulkanExample->settings.fullscreen = false;
|
||||||
|
}
|
||||||
|
|
||||||
- (BOOL)windowShouldClose:(NSWindow *)sender
|
- (BOOL)windowShouldClose:(NSWindow *)sender
|
||||||
{
|
{
|
||||||
|
|
@ -1738,6 +1784,7 @@ static CVReturn displayLinkOutputCallback(CVDisplayLinkRef displayLink, const CV
|
||||||
- (void)windowWillClose:(NSNotification *)notification
|
- (void)windowWillClose:(NSNotification *)notification
|
||||||
{
|
{
|
||||||
CVDisplayLinkStop(displayLink);
|
CVDisplayLinkStop(displayLink);
|
||||||
|
CVDisplayLinkRelease(displayLink);
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
@ -1748,7 +1795,9 @@ void* VulkanExampleBase::setupWindow(void* view)
|
||||||
#if defined(VK_EXAMPLE_XCODE_GENERATED)
|
#if defined(VK_EXAMPLE_XCODE_GENERATED)
|
||||||
NSApp = [NSApplication sharedApplication];
|
NSApp = [NSApplication sharedApplication];
|
||||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||||
[NSApp setDelegate:[AppDelegate new]];
|
auto nsAppDelegate = [AppDelegate new];
|
||||||
|
nsAppDelegate->vulkanExample = this;
|
||||||
|
[NSApp setDelegate:nsAppDelegate];
|
||||||
|
|
||||||
const auto kContentRect = NSMakeRect(0.0f, 0.0f, width, height);
|
const auto kContentRect = NSMakeRect(0.0f, 0.0f, width, height);
|
||||||
const auto kWindowStyle = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable;
|
const auto kWindowStyle = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable;
|
||||||
|
|
@ -1761,6 +1810,9 @@ void* VulkanExampleBase::setupWindow(void* view)
|
||||||
[window setAcceptsMouseMovedEvents:YES];
|
[window setAcceptsMouseMovedEvents:YES];
|
||||||
[window center];
|
[window center];
|
||||||
[window makeKeyAndOrderFront:nil];
|
[window makeKeyAndOrderFront:nil];
|
||||||
|
if (settings.fullscreen) {
|
||||||
|
[window toggleFullScreen:nil];
|
||||||
|
}
|
||||||
|
|
||||||
auto nsView = [[View alloc] initWithFrame:kContentRect];
|
auto nsView = [[View alloc] initWithFrame:kContentRect];
|
||||||
nsView->vulkanExample = this;
|
nsView->vulkanExample = this;
|
||||||
|
|
@ -2755,6 +2807,12 @@ void VulkanExampleBase::windowResize()
|
||||||
createCommandBuffers();
|
createCommandBuffers();
|
||||||
buildCommandBuffers();
|
buildCommandBuffers();
|
||||||
|
|
||||||
|
// SRS - Recreate fences in case number of swapchain images has changed on resize
|
||||||
|
for (auto& fence : waitFences) {
|
||||||
|
vkDestroyFence(device, fence, nullptr);
|
||||||
|
}
|
||||||
|
createSynchronizationPrimitives();
|
||||||
|
|
||||||
vkDeviceWaitIdle(device);
|
vkDeviceWaitIdle(device);
|
||||||
|
|
||||||
if ((width > 0.0f) && (height > 0.0f)) {
|
if ((width > 0.0f) && (height > 0.0f)) {
|
||||||
|
|
@ -2824,7 +2882,7 @@ void VulkanExampleBase::initSwapchain()
|
||||||
|
|
||||||
void VulkanExampleBase::setupSwapChain()
|
void VulkanExampleBase::setupSwapChain()
|
||||||
{
|
{
|
||||||
swapChain.create(&width, &height, settings.vsync);
|
swapChain.create(&width, &height, settings.vsync, settings.fullscreen);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanExampleBase::OnUpdateUIOverlay(vks::UIOverlay *overlay) {}
|
void VulkanExampleBase::OnUpdateUIOverlay(vks::UIOverlay *overlay) {}
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ protected:
|
||||||
// Frame counter to display fps
|
// Frame counter to display fps
|
||||||
uint32_t frameCounter = 0;
|
uint32_t frameCounter = 0;
|
||||||
uint32_t lastFPS = 0;
|
uint32_t lastFPS = 0;
|
||||||
std::chrono::time_point<std::chrono::high_resolution_clock> lastTimestamp;
|
std::chrono::time_point<std::chrono::high_resolution_clock> lastTimestamp, tPrevEnd;
|
||||||
// Vulkan instance, stores all per-application states
|
// Vulkan instance, stores all per-application states
|
||||||
VkInstance instance;
|
VkInstance instance;
|
||||||
std::vector<std::string> supportedInstanceExtensions;
|
std::vector<std::string> supportedInstanceExtensions;
|
||||||
|
|
@ -254,7 +254,9 @@ public:
|
||||||
int64_t lastTapTime = 0;
|
int64_t lastTapTime = 0;
|
||||||
#elif (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK))
|
#elif (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK))
|
||||||
void* view;
|
void* view;
|
||||||
double refreshPeriod = 1.0/60.0; // SRS - default refreshPeriod for 60 fps display
|
#if defined(VK_EXAMPLE_XCODE_GENERATED)
|
||||||
|
bool quit = false;
|
||||||
|
#endif
|
||||||
#elif defined(VK_USE_PLATFORM_DIRECTFB_EXT)
|
#elif defined(VK_USE_PLATFORM_DIRECTFB_EXT)
|
||||||
bool quit = false;
|
bool quit = false;
|
||||||
IDirectFB *dfb = nullptr;
|
IDirectFB *dfb = nullptr;
|
||||||
|
|
@ -534,7 +536,6 @@ int main(const int argc, const char *argv[]) \
|
||||||
vulkanExample->setupWindow(nullptr); \
|
vulkanExample->setupWindow(nullptr); \
|
||||||
vulkanExample->prepare(); \
|
vulkanExample->prepare(); \
|
||||||
vulkanExample->renderLoop(); \
|
vulkanExample->renderLoop(); \
|
||||||
delete(vulkanExample); \
|
|
||||||
} \
|
} \
|
||||||
return 0; \
|
return 0; \
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,6 @@ void MVKExample::displayLinkOutputCb() { // SRS - expose
|
||||||
_vulkanExample->displayLinkOutputCb();
|
_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)
|
void MVKExample::keyPressed(uint32_t keyChar) { // SRS - handle keyboard key presses only (e.g. Pause, Space, etc)
|
||||||
switch (keyChar)
|
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));
|
_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) {
|
MVKExample::MVKExample(void* view) {
|
||||||
_vulkanExample = new VulkanExample();
|
_vulkanExample = new VulkanExample();
|
||||||
|
_vulkanExample->settings.vsync = true; // SRS - this iOS/macOS example uses displayLink vsync rendering - set before calling prepare()
|
||||||
_vulkanExample->initVulkan();
|
_vulkanExample->initVulkan();
|
||||||
_vulkanExample->setupWindow(view);
|
_vulkanExample->setupWindow(view);
|
||||||
_vulkanExample->prepare();
|
_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() {
|
MVKExample::~MVKExample() {
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ class MVKExample {
|
||||||
public:
|
public:
|
||||||
void renderFrame();
|
void renderFrame();
|
||||||
void displayLinkOutputCb(); // SRS - expose VulkanExampleBase::displayLinkOutputCb() to DemoViewController
|
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 keyPressed(uint32_t keyChar); // SRS - expose keyboard events to DemoViewController
|
||||||
void keyDown(uint32_t keyChar);
|
void keyDown(uint32_t keyChar);
|
||||||
|
|
@ -30,6 +29,8 @@ public:
|
||||||
void mouseDragged(double x, double y);
|
void mouseDragged(double x, double y);
|
||||||
void scrollWheel(short wheelDelta);
|
void scrollWheel(short wheelDelta);
|
||||||
|
|
||||||
|
void fullScreen(bool fullscreen); // SRS - expose VulkanExampleBase::settings.fullscreen to DemoView (macOS only)
|
||||||
|
|
||||||
MVKExample(void* view);
|
MVKExample(void* view);
|
||||||
~MVKExample();
|
~MVKExample();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@
|
||||||
# include "../examples/particlefire/particlefire.cpp"
|
# include "../examples/particlefire/particlefire.cpp"
|
||||||
#endif
|
#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
|
#ifdef MVK_renderheadless
|
||||||
# include "../examples/renderheadless/renderheadless.cpp"
|
# include "../examples/renderheadless/renderheadless.cpp"
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -191,7 +191,7 @@
|
||||||
# include "../examples/vertexattributes/vertexattributes.cpp"
|
# include "../examples/vertexattributes/vertexattributes.cpp"
|
||||||
#endif
|
#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
|
#ifdef MVK_oit
|
||||||
# include "../examples/oit/oit.cpp"
|
# include "../examples/oit/oit.cpp"
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -297,7 +297,7 @@
|
||||||
# include "../examples/computecloth/computecloth.cpp"
|
# include "../examples/computecloth/computecloth.cpp"
|
||||||
#endif
|
#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
|
#ifdef MVK_computeheadless
|
||||||
# include "../examples/computeheadless/computeheadless.cpp"
|
# include "../examples/computeheadless/computeheadless.cpp"
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
||||||
|
|
||||||
@property (strong, nonatomic) UIWindow *window;
|
@property (strong, nonatomic) UIWindow *window;
|
||||||
|
@property (strong, nonatomic) UIViewController *viewController;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import "AppDelegate.h"
|
#import "AppDelegate.h"
|
||||||
|
#import "DemoViewController.h"
|
||||||
|
|
||||||
@interface AppDelegate ()
|
@interface AppDelegate ()
|
||||||
|
|
||||||
|
|
@ -39,6 +40,7 @@
|
||||||
|
|
||||||
- (void)applicationWillTerminate:(UIApplication *)application {
|
- (void)applicationWillTerminate:(UIApplication *)application {
|
||||||
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
|
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
|
||||||
|
[(DemoViewController *)_viewController shutdownExample];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
/** The main view controller for the demo storyboard. */
|
/** The main view controller for the demo storyboard. */
|
||||||
@interface DemoViewController : UIViewController <UIKeyInput>
|
@interface DemoViewController : UIViewController <UIKeyInput>
|
||||||
|
-(void) shutdownExample;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import "DemoViewController.h"
|
#import "DemoViewController.h"
|
||||||
|
#import "AppDelegate.h"
|
||||||
|
|
||||||
#include "MVKExample.h"
|
#include "MVKExample.h"
|
||||||
|
|
||||||
|
|
@ -22,6 +23,7 @@ const std::string getAssetPath() {
|
||||||
MVKExample* _mvkExample;
|
MVKExample* _mvkExample;
|
||||||
CADisplayLink* _displayLink;
|
CADisplayLink* _displayLink;
|
||||||
BOOL _viewHasAppeared;
|
BOOL _viewHasAppeared;
|
||||||
|
CGPoint _startPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Since this is a single-view app, init Vulkan when the view is loaded. */
|
/** Since this is a single-view app, init Vulkan when the view is loaded. */
|
||||||
|
|
@ -32,25 +34,36 @@ const std::string getAssetPath() {
|
||||||
|
|
||||||
_mvkExample = new MVKExample(self.view);
|
_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;
|
uint32_t fps = 60;
|
||||||
_displayLink = [CADisplayLink displayLinkWithTarget: self selector: @selector(renderFrame)];
|
_displayLink = [CADisplayLink displayLinkWithTarget: self selector: @selector(renderFrame)];
|
||||||
[_displayLink setFrameInterval: 60 / fps];
|
[_displayLink setFrameInterval: 60 / fps];
|
||||||
[_displayLink addToRunLoop: NSRunLoop.currentRunLoop forMode: NSDefaultRunLoopMode];
|
[_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
|
// Setup double tap gesture to toggle virtual keyboard
|
||||||
UITapGestureRecognizer* tapSelector = [[UITapGestureRecognizer alloc]
|
UITapGestureRecognizer* tapSelector = [[[UITapGestureRecognizer alloc]
|
||||||
initWithTarget: self action: @selector(handleTapGesture:)];
|
initWithTarget: self action: @selector(handleTapGesture:)] autorelease];
|
||||||
tapSelector.numberOfTapsRequired = 2;
|
tapSelector.numberOfTapsRequired = 2;
|
||||||
tapSelector.cancelsTouchesInView = YES;
|
tapSelector.cancelsTouchesInView = YES;
|
||||||
|
tapSelector.requiresExclusiveTouchType = YES;
|
||||||
[self.view addGestureRecognizer: tapSelector];
|
[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
|
// SRS - Setup pinch gesture to detect and activate zoom
|
||||||
UIPinchGestureRecognizer* pinchSelector = [[UIPinchGestureRecognizer alloc]
|
UIPinchGestureRecognizer* pinchSelector = [[[UIPinchGestureRecognizer alloc]
|
||||||
initWithTarget: self action: @selector(handlePinchGesture:)];
|
initWithTarget: self action: @selector(handlePinchGesture:)] autorelease];
|
||||||
pinchSelector.cancelsTouchesInView = YES;
|
pinchSelector.cancelsTouchesInView = YES;
|
||||||
|
pinchSelector.requiresExclusiveTouchType = YES;
|
||||||
[self.view addGestureRecognizer: pinchSelector];
|
[self.view addGestureRecognizer: pinchSelector];
|
||||||
|
|
||||||
_viewHasAppeared = NO;
|
_viewHasAppeared = NO;
|
||||||
|
|
@ -68,9 +81,9 @@ const std::string getAssetPath() {
|
||||||
_mvkExample->displayLinkOutputCb(); // SRS - Call displayLinkOutputCb() to animate frames vs. renderFrame() for static image
|
_mvkExample->displayLinkOutputCb(); // SRS - Call displayLinkOutputCb() to animate frames vs. renderFrame() for static image
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void) dealloc {
|
-(void) shutdownExample {
|
||||||
|
[_displayLink invalidate];
|
||||||
delete _mvkExample;
|
delete _mvkExample;
|
||||||
[super dealloc];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Toggle the display of the virtual keyboard
|
// Toggle the display of the virtual keyboard
|
||||||
|
|
@ -109,58 +122,84 @@ const std::string getAssetPath() {
|
||||||
|
|
||||||
#pragma mark UITouch methods
|
#pragma mark UITouch methods
|
||||||
|
|
||||||
// SRS - Handle touch events
|
|
||||||
-(CGPoint) getTouchLocalPoint:(UIEvent*) theEvent {
|
-(CGPoint) getTouchLocalPoint:(UIEvent*) theEvent {
|
||||||
UITouch *touch = [[theEvent allTouches] anyObject];
|
UITouch *touch = [[theEvent allTouches] anyObject];
|
||||||
CGPoint point = [touch locationInView: self.view];
|
CGPoint point = [touch locationInView: self.view];
|
||||||
point.x = point.x * self.view.contentScaleFactor;
|
point.x *= self.view.contentScaleFactor;
|
||||||
point.y = point.y * self.view.contentScaleFactor;
|
point.y *= self.view.contentScaleFactor;
|
||||||
return point;
|
return point;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SRS - Handle touch events
|
||||||
-(void) touchesBegan:(NSSet*) touches withEvent:(UIEvent*) theEvent {
|
-(void) touchesBegan:(NSSet*) touches withEvent:(UIEvent*) theEvent {
|
||||||
auto point = [self getTouchLocalPoint:theEvent];
|
|
||||||
if (touches.count == 1) {
|
if (touches.count == 1) {
|
||||||
// Single touch for imgui select and camera rotation
|
auto point = [self getTouchLocalPoint: theEvent];
|
||||||
_mvkExample->mouseDown(point.x, point.y);
|
_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 {
|
-(void) touchesMoved:(NSSet*) touches withEvent:(UIEvent*) theEvent {
|
||||||
|
if (touches.count == 1) {
|
||||||
auto point = [self getTouchLocalPoint: theEvent];
|
auto point = [self getTouchLocalPoint: theEvent];
|
||||||
_mvkExample->mouseDragged(point.x, point.y);
|
_mvkExample->mouseDragged(point.x, point.y);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
-(void) touchesEnded:(NSSet*) touches withEvent:(UIEvent*) theEvent {
|
-(void) touchesEnded:(NSSet*) touches withEvent:(UIEvent*) theEvent {
|
||||||
_mvkExample->mouseUp();
|
_mvkExample->mouseUp();
|
||||||
_mvkExample->otherMouseUp();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void) touchesCancelled:(NSSet*) touches withEvent:(UIEvent*) theEvent {
|
-(void) touchesCancelled:(NSSet*) touches withEvent:(UIEvent*) theEvent {
|
||||||
_mvkExample->mouseUp();
|
_mvkExample->mouseUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
#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();
|
_mvkExample->otherMouseUp();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SRS - Respond to pinch gestures for zoom
|
// SRS - Respond to pinch gestures for zoom
|
||||||
-(void) handlePinchGesture: (UIPinchGestureRecognizer*) gestureRecognizer {
|
-(void) handlePinchGesture: (UIPinchGestureRecognizer*) gestureRecognizer {
|
||||||
if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
|
switch (gestureRecognizer.state) {
|
||||||
auto point = [gestureRecognizer locationInView:self.view];
|
case UIGestureRecognizerStateBegan: {
|
||||||
point.x = point.x * self.view.contentScaleFactor;
|
_startPoint = [self getGestureLocalPoint: gestureRecognizer];
|
||||||
point.y = point.y * self.view.contentScaleFactor;
|
_mvkExample->rightMouseDown(_startPoint.x, _startPoint.y);
|
||||||
_mvkExample->rightMouseDown(point.x, point.y);
|
break;
|
||||||
}
|
}
|
||||||
else if (gestureRecognizer.state == UIGestureRecognizerStateChanged) {
|
case UIGestureRecognizerStateChanged: {
|
||||||
auto point = [gestureRecognizer locationInView:self.view];
|
_mvkExample->mouseDragged(_startPoint.x, _startPoint.y - self.view.frame.size.height * log(gestureRecognizer.scale));
|
||||||
point.x = point.x * self.view.contentScaleFactor;
|
break;
|
||||||
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) {
|
default: {
|
||||||
_mvkExample->rightMouseUp();
|
_mvkExample->rightMouseUp();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,4 +9,6 @@
|
||||||
|
|
||||||
@interface AppDelegate : NSObject <NSApplicationDelegate>
|
@interface AppDelegate : NSObject <NSApplicationDelegate>
|
||||||
|
|
||||||
|
@property (strong, nonatomic) NSViewController *viewController;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import "AppDelegate.h"
|
#import "AppDelegate.h"
|
||||||
|
#import "DemoViewController.h"
|
||||||
|
|
||||||
@interface AppDelegate ()
|
@interface AppDelegate ()
|
||||||
|
|
||||||
|
|
@ -19,6 +20,7 @@
|
||||||
|
|
||||||
- (void)applicationWillTerminate:(NSNotification *)aNotification {
|
- (void)applicationWillTerminate:(NSNotification *)aNotification {
|
||||||
// Insert code here to tear down your application
|
// Insert code here to tear down your application
|
||||||
|
[(DemoViewController *)_viewController shutdownExample];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender {
|
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender {
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
/** The main view controller for the demo storyboard. */
|
/** The main view controller for the demo storyboard. */
|
||||||
@interface DemoViewController : NSViewController
|
@interface DemoViewController : NSViewController
|
||||||
|
-(void) shutdownExample;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import "DemoViewController.h"
|
#import "DemoViewController.h"
|
||||||
|
#import "AppDelegate.h"
|
||||||
#import <QuartzCore/CAMetalLayer.h>
|
#import <QuartzCore/CAMetalLayer.h>
|
||||||
|
|
||||||
#include "MVKExample.h"
|
#include "MVKExample.h"
|
||||||
|
|
@ -43,25 +44,19 @@ MVKExample* _mvkExample;
|
||||||
|
|
||||||
_mvkExample = new MVKExample(self.view);
|
_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);
|
CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink);
|
||||||
CVDisplayLinkSetOutputCallback(_displayLink, &DisplayLinkCallback, _mvkExample);
|
CVDisplayLinkSetOutputCallback(_displayLink, &DisplayLinkCallback, _mvkExample);
|
||||||
CVDisplayLinkStart(_displayLink);
|
CVDisplayLinkStart(_displayLink);
|
||||||
}
|
}
|
||||||
|
|
||||||
// SRS - Center the window and set VulkanExampleBase::refreshPeriod from the active displayLink
|
-(void) shutdownExample {
|
||||||
-(void) viewWillAppear {
|
CVDisplayLinkStop(_displayLink);
|
||||||
[super viewWillAppear];
|
|
||||||
|
|
||||||
NSWindow* window = self.view.window;
|
|
||||||
[window center];
|
|
||||||
|
|
||||||
_mvkExample->setRefreshPeriod(CVDisplayLinkGetActualOutputVideoRefreshPeriod(_displayLink));
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void) dealloc {
|
|
||||||
CVDisplayLinkRelease(_displayLink);
|
CVDisplayLinkRelease(_displayLink);
|
||||||
delete _mvkExample;
|
delete _mvkExample;
|
||||||
[super dealloc];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
@ -86,6 +81,15 @@ MVKExample* _mvkExample;
|
||||||
return layer;
|
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; }
|
-(BOOL) acceptsFirstResponder { return YES; }
|
||||||
|
|
||||||
// SRS - Handle keyboard events
|
// SRS - Handle keyboard events
|
||||||
|
|
@ -151,9 +155,24 @@ MVKExample* _mvkExample;
|
||||||
_mvkExample->mouseDragged(point.x, point.y);
|
_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 {
|
-(void) scrollWheel:(NSEvent*) theEvent {
|
||||||
short wheelDelta = [theEvent deltaY];
|
short wheelDelta = [theEvent deltaY];
|
||||||
_mvkExample->scrollWheel(wheelDelta);
|
_mvkExample->scrollWheel(wheelDelta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)windowWillEnterFullScreen:(NSNotification *)notification
|
||||||
|
{
|
||||||
|
_mvkExample->fullScreen(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)windowWillExitFullScreen:(NSNotification *)notification
|
||||||
|
{
|
||||||
|
_mvkExample->fullScreen(false);
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue