|  | // 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.h" | 
|  | #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(); | 
|  | VkExtent3D extent = image->getImage()->getMipLevelExtent(VK_IMAGE_ASPECT_COLOR_BIT, 0); | 
|  |  | 
|  | 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 | 
|  |  | 
|  | } |