1// Copyright 2019 The SwiftShader Authors. All Rights Reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15#include "MetalSurface.h" 16#include "Vulkan/VkDeviceMemory.hpp" 17#include "Vulkan/VkImage.hpp" 18 19#include <Metal/Metal.h> 20#include <QuartzCore/CAMetalLayer.h> 21#include <AppKit/NSView.h> 22 23namespace vk { 24 25class MetalLayer 26{ 27public: 28 void initWithLayer(const void* pLayer) API_AVAILABLE(macosx(10.11)) 29 { 30 view = nullptr; 31 layer = nullptr; 32 33 id<NSObject> obj = (id<NSObject>)pLayer; 34 35 if(!NSThread.isMainThread) 36 { 37 UNREACHABLE("MetalLayer::init(): not called from main thread"); 38 } 39 if([obj isKindOfClass: [CAMetalLayer class]]) 40 { 41 layer = (CAMetalLayer*)[obj retain]; 42 layer.framebufferOnly = false; 43 } 44 else 45 { 46 UNREACHABLE("MetalLayer::init(): view doesn't have metal backed layer"); 47 } 48 } 49 50 void initWithView(const void* pView) API_AVAILABLE(macosx(10.11)) 51 { 52 view = nullptr; 53 layer = nullptr; 54 55 id<NSObject> obj = (id<NSObject>)pView; 56 57 if([obj isKindOfClass: [NSView class]]) 58 { 59 NSView* objView = (NSView*)[obj retain]; 60 61 initWithLayer(objView.layer); 62 63 view = objView; 64 } 65 } 66 67 void release() API_AVAILABLE(macosx(10.11)) 68 { 69 if(layer) 70 { 71 [layer release]; 72 } 73 if(view) 74 { 75 [view release]; 76 } 77 } 78 79 VkExtent2D getExtent() const API_AVAILABLE(macosx(10.11)) 80 { 81 if(layer) 82 { 83 CGSize drawSize = layer.bounds.size; 84 CGFloat scaleFactor = layer.contentsScale; 85 drawSize.width = trunc(drawSize.width * scaleFactor); 86 drawSize.height = trunc(drawSize.height * scaleFactor); 87 return { static_cast<uint32_t>(drawSize.width), static_cast<uint32_t>(drawSize.height) }; 88 } 89 else 90 { 91 return { 0, 0 }; 92 } 93 } 94 95 id<CAMetalDrawable> getNextDrawable() const API_AVAILABLE(macosx(10.11)) 96 { 97 if(layer) 98 { 99 return [layer nextDrawable]; 100 } 101 102 return nil; 103 } 104 105private: 106 NSView* view; 107 CAMetalLayer* layer API_AVAILABLE(macosx(10.11)); 108}; 109 110MetalSurface::MetalSurface(const void *pCreateInfo, void *mem) : metalLayer(reinterpret_cast<MetalLayer*>(mem)) 111{ 112 113} 114 115void MetalSurface::destroySurface(const VkAllocationCallbacks *pAllocator) API_AVAILABLE(macosx(10.11)) 116{ 117 if(metalLayer) 118 { 119 metalLayer->release(); 120 } 121 122 vk::deallocate(metalLayer, pAllocator); 123} 124 125size_t MetalSurface::ComputeRequiredAllocationSize(const void *pCreateInfo) API_AVAILABLE(macosx(10.11)) 126{ 127 return sizeof(MetalLayer); 128} 129 130void MetalSurface::getSurfaceCapabilities(VkSurfaceCapabilitiesKHR *pSurfaceCapabilities) const API_AVAILABLE(macosx(10.11)) 131{ 132 SurfaceKHR::getSurfaceCapabilities(pSurfaceCapabilities); 133 134 VkExtent2D extent = metalLayer->getExtent(); 135 pSurfaceCapabilities->currentExtent = extent; 136 pSurfaceCapabilities->minImageExtent = extent; 137 pSurfaceCapabilities->maxImageExtent = extent; 138} 139 140VkResult MetalSurface::present(PresentImage* image) API_AVAILABLE(macosx(10.11)) 141{ 142 @autoreleasepool 143 { 144 auto drawable = metalLayer->getNextDrawable(); 145 if(drawable) 146 { 147 VkExtent2D windowExtent = metalLayer->getExtent(); 148 VkExtent3D extent = image->getImage()->getMipLevelExtent(VK_IMAGE_ASPECT_COLOR_BIT, 0); 149 150 if(windowExtent.width != extent.width || windowExtent.height != extent.height) 151 { 152 return VK_ERROR_OUT_OF_DATE_KHR; 153 } 154 155 [drawable.texture replaceRegion:MTLRegionMake2D(0, 0, extent.width, extent.height) 156 mipmapLevel:0 157 withBytes:image->getImageMemory()->getOffsetPointer(0) 158 bytesPerRow:image->getImage()->rowPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0)]; 159 [drawable present]; 160 161 } 162 } 163 return VK_SUCCESS; 164} 165 166#ifdef VK_USE_PLATFORM_METAL_EXT 167MetalSurfaceEXT::MetalSurfaceEXT(const VkMetalSurfaceCreateInfoEXT *pCreateInfo, void *mem) API_AVAILABLE(macosx(10.11)) 168 : MetalSurface(pCreateInfo, mem) 169{ 170 metalLayer->initWithLayer(pCreateInfo->pLayer); 171} 172#endif 173 174#ifdef VK_USE_PLATFORM_MACOS_MVK 175MacOSSurfaceMVK::MacOSSurfaceMVK(const VkMacOSSurfaceCreateInfoMVK *pCreateInfo, void *mem) API_AVAILABLE(macosx(10.11)) 176 : MetalSurface(pCreateInfo, mem) 177{ 178 metalLayer->initWithView(pCreateInfo->pView); 179} 180#endif 181 182} 183