• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.hpp"
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            layer.device = MTLCreateSystemDefaultDevice();
44        }
45        else
46        {
47            UNREACHABLE("MetalLayer::init(): view doesn't have metal backed layer");
48        }
49    }
50
51    void initWithView(const void* pView) API_AVAILABLE(macosx(10.11))
52    {
53        view = nullptr;
54        layer = nullptr;
55
56        id<NSObject> obj = (id<NSObject>)pView;
57
58        if([obj isKindOfClass: [NSView class]])
59        {
60            NSView* objView = (NSView*)[obj retain];
61
62            initWithLayer(objView.layer);
63
64            view = objView;
65        }
66    }
67
68    void release() API_AVAILABLE(macosx(10.11))
69    {
70        if(layer)
71        {
72            [layer.device release];
73            [layer release];
74        }
75        if(view)
76        {
77            [view release];
78        }
79    }
80
81    // Synchronizes the drawableSize to layer.bounds.size * layer.contentsScale and returns the new value of
82    // drawableSize.
83    VkExtent2D syncExtent() const API_AVAILABLE(macosx(10.11))
84    {
85        if(layer)
86        {
87            CGSize drawSize = layer.bounds.size;
88            CGFloat scaleFactor = layer.contentsScale;
89            drawSize.width = trunc(drawSize.width * scaleFactor);
90            drawSize.height = trunc(drawSize.height * scaleFactor);
91
92            [layer setDrawableSize: drawSize];
93
94            return { static_cast<uint32_t>(drawSize.width), static_cast<uint32_t>(drawSize.height) };
95        }
96        else
97        {
98            return { 0, 0 };
99        }
100    }
101
102    id<CAMetalDrawable> getNextDrawable() const API_AVAILABLE(macosx(10.11))
103    {
104        if(layer)
105        {
106            return [layer nextDrawable];
107        }
108
109        return nil;
110    }
111
112    VkExtent2D getDrawableSize() const API_AVAILABLE(macosx(10.11)) {
113        if (layer) {
114            return {
115                static_cast<uint32_t>([layer drawableSize].width),
116                static_cast<uint32_t>([layer drawableSize].height),
117            };
118        }
119        return {0, 0};
120    }
121
122private:
123    NSView* view;
124    CAMetalLayer* layer API_AVAILABLE(macosx(10.11));
125};
126
127MetalSurface::MetalSurface(const void *pCreateInfo, void *mem) : metalLayer(reinterpret_cast<MetalLayer*>(mem))
128{
129
130}
131
132void MetalSurface::destroySurface(const VkAllocationCallbacks *pAllocator) API_AVAILABLE(macosx(10.11))
133{
134    if(metalLayer)
135    {
136        metalLayer->release();
137    }
138
139    vk::freeHostMemory(metalLayer, pAllocator);
140}
141
142size_t MetalSurface::ComputeRequiredAllocationSize(const void *pCreateInfo) API_AVAILABLE(macosx(10.11))
143{
144    return sizeof(MetalLayer);
145}
146
147VkResult MetalSurface::getSurfaceCapabilities(VkSurfaceCapabilitiesKHR *pSurfaceCapabilities) const API_AVAILABLE(macosx(10.11))
148{
149    setCommonSurfaceCapabilities(pSurfaceCapabilities);
150
151    // The value of drawableSize in CAMetalLayer is set the first time a drawable is queried but after that it is the
152    // (Metal) application's responsibility to resize the drawable when the window is resized. The best time for Swiftshader
153    // to resize the drawable is when querying the capabilities of the swapchain as that's done when the Vulkan application
154    // is trying to handle a window resize.
155    VkExtent2D extent = metalLayer->syncExtent();
156    pSurfaceCapabilities->currentExtent = extent;
157    pSurfaceCapabilities->minImageExtent = extent;
158    pSurfaceCapabilities->maxImageExtent = extent;
159	return VK_SUCCESS;
160}
161
162VkResult MetalSurface::present(PresentImage* image) API_AVAILABLE(macosx(10.11))
163{
164    @autoreleasepool
165    {
166        auto drawable = metalLayer->getNextDrawable();
167        if(drawable)
168        {
169            const VkExtent3D &extent = image->getImage()->getExtent();
170            VkExtent2D drawableExtent = metalLayer->getDrawableSize();
171
172            if(drawableExtent.width != extent.width || drawableExtent.height != extent.height)
173            {
174                return VK_ERROR_OUT_OF_DATE_KHR;
175            }
176
177            [drawable.texture replaceRegion:MTLRegionMake2D(0, 0, extent.width, extent.height)
178                              mipmapLevel:0
179                              withBytes:image->getImageMemory()->getOffsetPointer(0)
180                              bytesPerRow:image->getImage()->rowPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0)];
181            [drawable present];
182
183        }
184    }
185    return VK_SUCCESS;
186}
187
188#ifdef VK_USE_PLATFORM_METAL_EXT
189MetalSurfaceEXT::MetalSurfaceEXT(const VkMetalSurfaceCreateInfoEXT *pCreateInfo, void *mem) API_AVAILABLE(macosx(10.11))
190 : MetalSurface(pCreateInfo, mem)
191{
192    metalLayer->initWithLayer(pCreateInfo->pLayer);
193}
194#endif
195
196#ifdef VK_USE_PLATFORM_MACOS_MVK
197MacOSSurfaceMVK::MacOSSurfaceMVK(const VkMacOSSurfaceCreateInfoMVK *pCreateInfo, void *mem) API_AVAILABLE(macosx(10.11))
198 : MetalSurface(pCreateInfo, mem)
199{
200    metalLayer->initWithView(pCreateInfo->pView);
201}
202#endif
203
204}
205