• 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.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