| // Copyright 2019 The SwiftShader Authors. All Rights Reserved. | 
 | // | 
 | // 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 | 
 | // | 
 | // Unless required by applicable law or agreed to in writing, software | 
 | // distributed under the License is distributed on an "AS IS" BASIS, | 
 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | // See the License for the specific language governing permissions and | 
 | // limitations under the License. | 
 |  | 
 | #include "MetalSurface.hpp" | 
 | #include "Vulkan/VkDeviceMemory.hpp" | 
 | #include "Vulkan/VkImage.hpp" | 
 |  | 
 | #include <Metal/Metal.h> | 
 | #include <QuartzCore/CAMetalLayer.h> | 
 | #include <AppKit/NSView.h> | 
 |  | 
 | namespace vk { | 
 |  | 
 | class MetalLayer | 
 | { | 
 | public: | 
 |     void initWithLayer(const void* pLayer) API_AVAILABLE(macosx(10.11)) | 
 |     { | 
 |         view = nullptr; | 
 |         layer = nullptr; | 
 |  | 
 |         id<NSObject> obj = (id<NSObject>)pLayer; | 
 |  | 
 |         if(!NSThread.isMainThread) | 
 |         { | 
 |             UNREACHABLE("MetalLayer::init(): not called from main thread"); | 
 |         } | 
 |         if([obj isKindOfClass: [CAMetalLayer class]]) | 
 |         { | 
 |             layer = (CAMetalLayer*)[obj retain]; | 
 |             layer.framebufferOnly = false; | 
 |             layer.device = MTLCreateSystemDefaultDevice(); | 
 |         } | 
 |         else | 
 |         { | 
 |             UNREACHABLE("MetalLayer::init(): view doesn't have metal backed layer"); | 
 |         } | 
 |     } | 
 |  | 
 |     void initWithView(const void* pView) API_AVAILABLE(macosx(10.11)) | 
 |     { | 
 |         view = nullptr; | 
 |         layer = nullptr; | 
 |  | 
 |         id<NSObject> obj = (id<NSObject>)pView; | 
 |  | 
 |         if([obj isKindOfClass: [NSView class]]) | 
 |         { | 
 |             NSView* objView = (NSView*)[obj retain]; | 
 |  | 
 |             initWithLayer(objView.layer); | 
 |  | 
 |             view = objView; | 
 |         } | 
 |     } | 
 |  | 
 |     void release() API_AVAILABLE(macosx(10.11)) | 
 |     { | 
 |         if(layer) | 
 |         { | 
 |             [layer.device release]; | 
 |             [layer release]; | 
 |         } | 
 |         if(view) | 
 |         { | 
 |             [view release]; | 
 |         } | 
 |     } | 
 |  | 
 |     VkExtent2D getExtent() const API_AVAILABLE(macosx(10.11)) | 
 |     { | 
 |         if(layer) | 
 |         { | 
 |             CGSize drawSize = layer.bounds.size; | 
 |             CGFloat scaleFactor = layer.contentsScale; | 
 |             drawSize.width = trunc(drawSize.width * scaleFactor); | 
 |             drawSize.height = trunc(drawSize.height * scaleFactor); | 
 |             return { static_cast<uint32_t>(drawSize.width), static_cast<uint32_t>(drawSize.height) }; | 
 |         } | 
 |         else | 
 |         { | 
 |             return { 0, 0 }; | 
 |         } | 
 |     } | 
 |  | 
 |     id<CAMetalDrawable> getNextDrawable() const API_AVAILABLE(macosx(10.11)) | 
 |     { | 
 |         if(layer) | 
 |         { | 
 |             return [layer nextDrawable]; | 
 |         } | 
 |  | 
 |         return nil; | 
 |     } | 
 |  | 
 | private: | 
 |     NSView* view; | 
 |     CAMetalLayer* layer API_AVAILABLE(macosx(10.11)); | 
 | }; | 
 |  | 
 | MetalSurface::MetalSurface(const void *pCreateInfo, void *mem) : metalLayer(reinterpret_cast<MetalLayer*>(mem)) | 
 | { | 
 |  | 
 | } | 
 |  | 
 | void MetalSurface::destroySurface(const VkAllocationCallbacks *pAllocator) API_AVAILABLE(macosx(10.11)) | 
 | { | 
 |     if(metalLayer) | 
 |     { | 
 |         metalLayer->release(); | 
 |     } | 
 |  | 
 |     vk::deallocate(metalLayer, pAllocator); | 
 | } | 
 |  | 
 | size_t MetalSurface::ComputeRequiredAllocationSize(const void *pCreateInfo) API_AVAILABLE(macosx(10.11)) | 
 | { | 
 |     return sizeof(MetalLayer); | 
 | } | 
 |  | 
 | void MetalSurface::getSurfaceCapabilities(VkSurfaceCapabilitiesKHR *pSurfaceCapabilities) const API_AVAILABLE(macosx(10.11)) | 
 | { | 
 |     SurfaceKHR::getSurfaceCapabilities(pSurfaceCapabilities); | 
 |  | 
 |     VkExtent2D extent = metalLayer->getExtent(); | 
 |     pSurfaceCapabilities->currentExtent = extent; | 
 |     pSurfaceCapabilities->minImageExtent = extent; | 
 |     pSurfaceCapabilities->maxImageExtent = extent; | 
 | } | 
 |  | 
 | VkResult MetalSurface::present(PresentImage* image) API_AVAILABLE(macosx(10.11)) | 
 | { | 
 |     @autoreleasepool | 
 |     { | 
 |         auto drawable = metalLayer->getNextDrawable(); | 
 |         if(drawable) | 
 |         { | 
 |             VkExtent2D windowExtent = metalLayer->getExtent(); | 
 |             const VkExtent3D &extent = image->getImage()->getExtent(); | 
 |  | 
 |             if(windowExtent.width != extent.width || windowExtent.height != extent.height) | 
 |             { | 
 |                 return VK_ERROR_OUT_OF_DATE_KHR; | 
 |             } | 
 |  | 
 |             [drawable.texture replaceRegion:MTLRegionMake2D(0, 0, extent.width, extent.height) | 
 |                               mipmapLevel:0 | 
 |                               withBytes:image->getImageMemory()->getOffsetPointer(0) | 
 |                               bytesPerRow:image->getImage()->rowPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0)]; | 
 |             [drawable present]; | 
 |  | 
 |         } | 
 |     } | 
 |     return VK_SUCCESS; | 
 | } | 
 |  | 
 | #ifdef VK_USE_PLATFORM_METAL_EXT | 
 | MetalSurfaceEXT::MetalSurfaceEXT(const VkMetalSurfaceCreateInfoEXT *pCreateInfo, void *mem) API_AVAILABLE(macosx(10.11)) | 
 |  : MetalSurface(pCreateInfo, mem) | 
 | { | 
 |     metalLayer->initWithLayer(pCreateInfo->pLayer); | 
 | } | 
 | #endif | 
 |  | 
 | #ifdef VK_USE_PLATFORM_MACOS_MVK | 
 | MacOSSurfaceMVK::MacOSSurfaceMVK(const VkMacOSSurfaceCreateInfoMVK *pCreateInfo, void *mem) API_AVAILABLE(macosx(10.11)) | 
 |  : MetalSurface(pCreateInfo, mem) | 
 | { | 
 |     metalLayer->initWithView(pCreateInfo->pView); | 
 | } | 
 | #endif | 
 |  | 
 | } |