From 8bc8d14cf28198ea43a8dc5338266d97fb4eb420 Mon Sep 17 00:00:00 2001 From: Stephen Saunders Date: Mon, 20 Jun 2022 16:44:34 -0400 Subject: [PATCH] macOS retina scaling fixes, M1 Vulkan vsync workaround, CMakeLists OpenMP path fix for Apple --- base/VulkanSwapChain.cpp | 8 ++++++-- base/VulkanSwapChain.h | 4 ++++ base/VulkanUIOverlay.cpp | 8 +++++--- base/vulkanexamplebase.cpp | 2 +- examples/CMakeLists.txt | 8 ++++---- examples/gltfscenerendering/gltfscenerendering.cpp | 2 +- xcode/MVKExample.cpp | 7 ++++--- xcode/MVKExample.h | 2 +- xcode/ios/DemoViewController.mm | 2 +- xcode/macos/DemoViewController.mm | 11 ++++++----- 10 files changed, 33 insertions(+), 21 deletions(-) diff --git a/base/VulkanSwapChain.cpp b/base/VulkanSwapChain.cpp index 9d2f92b5..e12405dd 100644 --- a/base/VulkanSwapChain.cpp +++ b/base/VulkanSwapChain.cpp @@ -291,8 +291,12 @@ void VulkanSwapChain::create(uint32_t *width, uint32_t *height, bool vsync, bool // Determine the number of images 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) + // SRS - Work around known MoltenVK issue re 2x frame rate when vsync (VK_PRESENT_MODE_FIFO_KHR) enabled + struct utsname sysInfo; + uname(&sysInfo); + // SRS - When vsync is on, use minImageCount when not in fullscreen or when running on Apple Silcon + // This forces swapchain image acquire frame rate to match display vsync frame rate + if (vsync && (!fullscreen || strcmp(sysInfo.machine, "arm64") == 0)) { desiredNumberOfSwapchainImages = surfCaps.minImageCount; } diff --git a/base/VulkanSwapChain.h b/base/VulkanSwapChain.h index 29eaeb1b..8d591100 100644 --- a/base/VulkanSwapChain.h +++ b/base/VulkanSwapChain.h @@ -23,6 +23,10 @@ #include "VulkanAndroid.h" #endif +#ifdef __APPLE__ +#include +#endif + typedef struct _SwapChainBuffers { VkImage image; VkImageView view; diff --git a/base/VulkanUIOverlay.cpp b/base/VulkanUIOverlay.cpp index b18e2a5a..65e7c7b7 100644 --- a/base/VulkanUIOverlay.cpp +++ b/base/VulkanUIOverlay.cpp @@ -78,8 +78,10 @@ namespace vks } #else const std::string filename = getAssetPath() + "Roboto-Medium.ttf"; - io.Fonts->AddFontFromFileTTF(filename.c_str(), 16.0f); -#endif + io.Fonts->AddFontFromFileTTF(filename.c_str(), 16.0f * scale); + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(scale); +#endif io.Fonts->GetTexDataAsRGBA32(&fontData, &texWidth, &texHeight); VkDeviceSize uploadSize = texWidth*texHeight * 4 * sizeof(char); @@ -492,4 +494,4 @@ namespace vks ImGui::TextV(formatstr, args); va_end(args); } -} \ No newline at end of file +} diff --git a/base/vulkanexamplebase.cpp b/base/vulkanexamplebase.cpp index 01c66c40..5878252d 100644 --- a/base/vulkanexamplebase.cpp +++ b/base/vulkanexamplebase.cpp @@ -688,7 +688,7 @@ void VulkanExampleBase::updateOverlay() ImGui::NewFrame(); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0); - ImGui::SetNextWindowPos(ImVec2(10, 10)); + ImGui::SetNextWindowPos(ImVec2(10 * UIOverlay.scale, 10 * UIOverlay.scale)); ImGui::SetNextWindowSize(ImVec2(0, 0), ImGuiSetCond_FirstUseEver); ImGui::Begin("Vulkan Example", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove); ImGui::TextUnformatted(title.c_str()); diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 0876bc24..7fa6373c 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -12,21 +12,21 @@ function(buildExample EXAMPLE_NAME) SET(MAIN_HEADER ${EXAMPLE_FOLDER}/${EXAMPLE_NAME}.h) ENDIF() if(APPLE) - # SRS - Use MacPorts paths as default since they are the same on x86 and Apple Silicon, can override on cmake command line - if(NOT OpenMP_omp_LIBRARY) + # SRS - Use MacPorts paths as default since the same on x86 and Apple Silicon, can override for homebrew on cmake command line + if(NOT OpenMP_omp_LIBRARY AND EXISTS /opt/local/lib/libomp/libomp.dylib) set(OpenMP_omp_LIBRARY /opt/local/lib/libomp/libomp.dylib) endif() if(CMAKE_C_COMPILER_ID MATCHES "Clang\$") set(OpenMP_C_FLAGS "-Xclang -fopenmp") set(OpenMP_C_LIB_NAMES "omp") - if(NOT OpenMP_C_INCLUDE_DIR) + if(NOT OpenMP_C_INCLUDE_DIR AND EXISTS /opt/local/include/libomp) set(OpenMP_C_INCLUDE_DIR /opt/local/include/libomp) endif() endif() if(CMAKE_CXX_COMPILER_ID MATCHES "Clang\$") set(OpenMP_CXX_FLAGS "-Xclang -fopenmp") set(OpenMP_CXX_LIB_NAMES "omp") - if(NOT OpenMP_CXX_INCLUDE_DIR) + if(NOT OpenMP_CXX_INCLUDE_DIR AND EXISTS /opt/local/include/libomp) set(OpenMP_CXX_INCLUDE_DIR /opt/local/include/libomp) endif() endif() diff --git a/examples/gltfscenerendering/gltfscenerendering.cpp b/examples/gltfscenerendering/gltfscenerendering.cpp index 02b655d5..8493c343 100644 --- a/examples/gltfscenerendering/gltfscenerendering.cpp +++ b/examples/gltfscenerendering/gltfscenerendering.cpp @@ -652,7 +652,7 @@ void VulkanExample::OnUpdateUIOverlay(vks::UIOverlay* overlay) ImGui::NewLine(); // POI: Create a list of glTF nodes for visibility toggle - ImGui::BeginChild("#nodelist", ImVec2(200.0f, 340.0f), false); + ImGui::BeginChild("#nodelist", ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * (glTFScene.nodes.size() + 4)), false); for (auto &node : glTFScene.nodes) { if (overlay->checkBox(node.name.c_str(), &node.visible)) diff --git a/xcode/MVKExample.cpp b/xcode/MVKExample.cpp index b4f5abb1..3073d2d9 100644 --- a/xcode/MVKExample.cpp +++ b/xcode/MVKExample.cpp @@ -113,13 +113,14 @@ void MVKExample::fullScreen(bool fullscreen) { _vulkanExample->settings.fullscreen = fullscreen; } -MVKExample::MVKExample(void* view) { +MVKExample::MVKExample(void* view, double scaleUI) { _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->settings.vsync = true; // SRS - set vsync flag since this iOS/macOS example app uses displayLink vsync rendering + _vulkanExample->UIOverlay.scale = scaleUI; // SRS - set UIOverlay scale to maintain relative proportions/readability on retina displays _vulkanExample->prepare(); - _vulkanExample->renderLoop(); // SRS - this inits destWidth/destHeight/lastTimestamp/tPrevEnd, then falls through and returns + _vulkanExample->renderLoop(); // SRS - this inits destWidth/destHeight/lastTimestamp/tPrevEnd, then falls through and returns } MVKExample::~MVKExample() { diff --git a/xcode/MVKExample.h b/xcode/MVKExample.h index c505a96d..bdbf3a11 100644 --- a/xcode/MVKExample.h +++ b/xcode/MVKExample.h @@ -31,7 +31,7 @@ public: void fullScreen(bool fullscreen); // SRS - expose VulkanExampleBase::settings.fullscreen to DemoView (macOS only) - MVKExample(void* view); + MVKExample(void* view, double scaleUI); // SRS - support UIOverlay scaling parameter based on device and display type ~MVKExample(); protected: diff --git a/xcode/ios/DemoViewController.mm b/xcode/ios/DemoViewController.mm index 3e5e65d7..00328952 100644 --- a/xcode/ios/DemoViewController.mm +++ b/xcode/ios/DemoViewController.mm @@ -32,7 +32,7 @@ const std::string getAssetPath() { self.view.contentScaleFactor = UIScreen.mainScreen.nativeScale; - _mvkExample = new MVKExample(self.view); + _mvkExample = new MVKExample(self.view, 1.0f); // SRS - Use 1x scale factor for UIOverlay on iOS // SRS - Enable AppDelegate to call into DemoViewController for handling app lifecycle events (e.g. termination) auto appDelegate = (AppDelegate *)UIApplication.sharedApplication.delegate; diff --git a/xcode/macos/DemoViewController.mm b/xcode/macos/DemoViewController.mm index 53d9e21c..a0a8d308 100644 --- a/xcode/macos/DemoViewController.mm +++ b/xcode/macos/DemoViewController.mm @@ -27,6 +27,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, return kCVReturnSuccess; } +CALayer* layer; MVKExample* _mvkExample; #pragma mark - @@ -40,9 +41,9 @@ MVKExample* _mvkExample; -(void) viewDidLoad { [super viewDidLoad]; - self.view.wantsLayer = YES; // Back the view with a layer created by the makeBackingLayer method. + self.view.wantsLayer = YES; // Back the view with a layer created by the makeBackingLayer method (called immediately on set) - _mvkExample = new MVKExample(self.view); + _mvkExample = new MVKExample(self.view, layer.contentsScale); // SRS - Use backing layer scale factor for UIOverlay on macOS // SRS - Enable AppDelegate to call into DemoViewController for handling application lifecycle events (e.g. termination) auto appDelegate = (AppDelegate *)NSApplication.sharedApplication.delegate; @@ -75,7 +76,7 @@ MVKExample* _mvkExample; /** 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]; + layer = [self.class.layerClass layer]; CGSize viewScale = [self convertSizeToBacking: CGSizeMake(1.0, 1.0)]; layer.contentsScale = MIN(viewScale.width, viewScale.height); return layer; @@ -108,8 +109,8 @@ MVKExample* _mvkExample; // SRS - Handle mouse events -(NSPoint) getMouseLocalPoint:(NSEvent*) theEvent { NSPoint location = [theEvent locationInWindow]; - NSPoint point = [self convertPoint:location fromView:nil]; - point.y = self.frame.size.height - point.y; + NSPoint point = [self convertPointToBacking:location]; + point.y = self.frame.size.height*self.window.backingScaleFactor - point.y; return point; }