diff --git a/MoltenVK/MoltenVK/API/mvk_datatypes.h b/MoltenVK/MoltenVK/API/mvk_datatypes.h index f5c103b4..b0e2dac7 100644 --- a/MoltenVK/MoltenVK/API/mvk_datatypes.h +++ b/MoltenVK/MoltenVK/API/mvk_datatypes.h @@ -434,6 +434,12 @@ VkExtent2D mvkVkExtent2DFromCGSize(CGSize cgSize); /** Returns a CGSize that corresponds to the specified VkExtent2D. */ CGSize mvkCGSizeFromVkExtent2D(VkExtent2D vkExtent); +/** Returns a CGPoint that corresponds to the specified VkOffset2D. */ +CGPoint mvkCGPointFromVkOffset2D(VkOffset2D vkOffset); + +/** Returns a CGRect that corresponds to the specified VkRectLayerKHR. The layer is ignored. */ +CGRect mvkCGRectFromVkRectLayerKHR(VkRectLayerKHR vkRect); + /** Returns a Metal MTLOrigin constructed from a VkOffset3D. */ static inline MTLOrigin mvkMTLOriginFromVkOffset3D(VkOffset3D vkOffset) { return MTLOriginMake(vkOffset.x, vkOffset.y, vkOffset.z); diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm index c60cb011..96b34cc3 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm @@ -583,8 +583,12 @@ MVKQueuePresentSurfaceSubmission::MVKQueuePresentSurfaceSubmission(MVKQueue* que const VkPresentTimesInfoGOOGLE* pPresentTimesInfo = nullptr; const VkSwapchainPresentFenceInfoEXT* pPresentFenceInfo = nullptr; const VkSwapchainPresentModeInfoEXT* pPresentModeInfo = nullptr; + const VkPresentRegionsKHR* pPresentRegions = nullptr; for (auto* next = (const VkBaseInStructure*)pPresentInfo->pNext; next; next = next->pNext) { switch (next->sType) { + case VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR: + pPresentRegions = (const VkPresentRegionsKHR*) next; + break; case VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_FENCE_INFO_EXT: pPresentFenceInfo = (const VkSwapchainPresentFenceInfoEXT*) next; break; @@ -616,6 +620,10 @@ MVKQueuePresentSurfaceSubmission::MVKQueuePresentSurfaceSubmission(MVKQueue* que pFences = pPresentFenceInfo->pFences; MVKAssert(pPresentFenceInfo->swapchainCount == scCnt, "VkSwapchainPresentFenceInfoEXT swapchainCount must match VkPresentInfo swapchainCount."); } + const VkPresentRegionKHR* pRegions = nullptr; + if (pPresentRegions) { + pRegions = pPresentRegions->pRegions; + } VkResult* pSCRslts = pPresentInfo->pResults; _presentInfo.reserve(scCnt); @@ -630,6 +638,7 @@ MVKQueuePresentSurfaceSubmission::MVKQueuePresentSurfaceSubmission(MVKQueue* que presentInfo.presentID = pPresentTimes[scIdx].presentID; presentInfo.desiredPresentTime = pPresentTimes[scIdx].desiredPresentTime; } + mvkSC->setLayerNeedsDisplay(pRegions ? &pRegions[scIdx] : nullptr); _presentInfo.push_back(presentInfo); VkResult scRslt = mvkSC->getSurfaceStatus(); if (pSCRslts) { pSCRslts[scIdx] = scRslt; } diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h index 95bcb137..523a5807 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h @@ -99,6 +99,9 @@ public: /** VK_GOOGLE_display_timing - returns past presentation times */ VkResult getPastPresentationTiming(uint32_t *pCount, VkPastPresentationTimingGOOGLE *pPresentationTimings); + /** Marks parts of the underlying CAMetalLayer as needing update. */ + void setLayerNeedsDisplay(const VkPresentRegionKHR* pRegion); + void destroy() override; #pragma mark Construction diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm index c52896d0..8be023fb 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm @@ -582,6 +582,30 @@ void MVKSwapchain::recordPresentTime(const MVKImagePresentInfo& presentInfo, uin _presentHistoryIndex = (_presentHistoryIndex + 1) % kMaxPresentationHistory; } +void MVKSwapchain::setLayerNeedsDisplay(const VkPresentRegionKHR* pRegion) { + if (!pRegion || pRegion->rectangleCount == 0) { + [_mtlLayer setNeedsDisplay]; + return; + } + + for (uint32_t i = 0; i < pRegion->rectangleCount; ++i) { + CGRect cgRect = mvkCGRectFromVkRectLayerKHR(pRegion->pRectangles[i]); +#if MVK_MACOS + // VK_KHR_incremental_present specifies an upper-left origin, but macOS by default + // uses a lower-left origin. + cgRect.origin.y = _mtlLayer.bounds.size.height - cgRect.origin.y; +#endif + // We were given rectangles in pixels, but -[CALayer setNeedsDisplayInRect:] wants them + // in points, which is pixels / contentsScale. + CGFloat scaleFactor = _mtlLayer.contentsScale; + cgRect.origin.x /= scaleFactor; + cgRect.origin.y /= scaleFactor; + cgRect.size.width /= scaleFactor; + cgRect.size.height /= scaleFactor; + [_mtlLayer setNeedsDisplayInRect:cgRect]; + } +} + // A retention loop exists between the swapchain and its images. The swapchain images // retain the swapchain because they can be in flight when the app destroys the swapchain. // Release the images now, when the app destroys the swapchain, so they will be destroyed when diff --git a/MoltenVK/MoltenVK/Layers/MVKExtensions.def b/MoltenVK/MoltenVK/Layers/MVKExtensions.def index efa84e0f..a7c610dc 100644 --- a/MoltenVK/MoltenVK/Layers/MVKExtensions.def +++ b/MoltenVK/MoltenVK/Layers/MVKExtensions.def @@ -68,6 +68,7 @@ MVK_EXTENSION(KHR_get_physical_device_properties2, KHR_GET_PHYSICAL_DEVICE_PR MVK_EXTENSION(KHR_get_surface_capabilities2, KHR_GET_SURFACE_CAPABILITIES_2, INSTANCE, 10.11, 8.0) MVK_EXTENSION(KHR_imageless_framebuffer, KHR_IMAGELESS_FRAMEBUFFER, DEVICE, 10.11, 8.0) MVK_EXTENSION(KHR_image_format_list, KHR_IMAGE_FORMAT_LIST, DEVICE, 10.11, 8.0) +MVK_EXTENSION(KHR_incremental_present, KHR_INCREMENTAL_PRESENT, DEVICE, 10.11, 8.0) MVK_EXTENSION(KHR_maintenance1, KHR_MAINTENANCE1, DEVICE, 10.11, 8.0) MVK_EXTENSION(KHR_maintenance2, KHR_MAINTENANCE2, DEVICE, 10.11, 8.0) MVK_EXTENSION(KHR_maintenance3, KHR_MAINTENANCE3, DEVICE, 10.11, 8.0) diff --git a/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm b/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm index 23c567cc..caa77623 100644 --- a/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm +++ b/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm @@ -780,10 +780,15 @@ MVK_PUBLIC_SYMBOL VkExtent2D mvkVkExtent2DFromCGSize(CGSize cgSize) { } MVK_PUBLIC_SYMBOL CGSize mvkCGSizeFromVkExtent2D(VkExtent2D vkExtent) { - CGSize cgSize; - cgSize.width = vkExtent.width; - cgSize.height = vkExtent.height; - return cgSize; + return CGSizeMake(vkExtent.width, vkExtent.height); +} + +MVK_PUBLIC_SYMBOL CGPoint mvkCGPointFromVkOffset2D(VkOffset2D vkOffset) { + return CGPointMake(vkOffset.x, vkOffset.y); +} + +MVK_PUBLIC_SYMBOL CGRect mvkCGRectFromVkRectLayerKHR(VkRectLayerKHR vkRect) { + return { mvkCGPointFromVkOffset2D(vkRect.offset), mvkCGSizeFromVkExtent2D(vkRect.extent) }; }