xref: /aosp_15_r20/external/swiftshader/src/WSI/MetalSurface.mm (revision 03ce13f70fcc45d86ee91b7ee4cab1936a95046e)
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(const void *pSurfaceInfoPNext,
148                                              VkSurfaceCapabilitiesKHR *pSurfaceCapabilities,
149                                              void *pSurfaceCapabilitiesPNext) const
150    API_AVAILABLE(macosx(10.11))
151{
152    // The value of drawableSize in CAMetalLayer is set the first time a drawable is queried but after that it is the
153    // (Metal) application's responsibility to resize the drawable when the window is resized. The best time for Swiftshader
154    // to resize the drawable is when querying the capabilities of the swapchain as that's done when the Vulkan application
155    // is trying to handle a window resize.
156    VkExtent2D extent = metalLayer->syncExtent();
157    pSurfaceCapabilities->currentExtent = extent;
158    pSurfaceCapabilities->minImageExtent = extent;
159    pSurfaceCapabilities->maxImageExtent = extent;
160
161    setCommonSurfaceCapabilities(pSurfaceInfoPNext, pSurfaceCapabilities,
162                                 pSurfaceCapabilitiesPNext);
163    return VK_SUCCESS;
164}
165
166VkResult MetalSurface::present(PresentImage* image) API_AVAILABLE(macosx(10.11))
167{
168    @autoreleasepool
169    {
170        auto drawable = metalLayer->getNextDrawable();
171        if(drawable)
172        {
173            const VkExtent3D &extent = image->getImage()->getExtent();
174            VkExtent2D drawableExtent = metalLayer->getDrawableSize();
175
176            if(drawableExtent.width != extent.width || drawableExtent.height != extent.height)
177            {
178                return VK_ERROR_OUT_OF_DATE_KHR;
179            }
180
181            [drawable.texture replaceRegion:MTLRegionMake2D(0, 0, extent.width, extent.height)
182                              mipmapLevel:0
183                              withBytes:image->getImageMemory()->getOffsetPointer(0)
184                              bytesPerRow:image->getImage()->rowPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0)];
185            [drawable present];
186
187        }
188    }
189    return VK_SUCCESS;
190}
191
192#ifdef VK_USE_PLATFORM_METAL_EXT
193MetalSurfaceEXT::MetalSurfaceEXT(const VkMetalSurfaceCreateInfoEXT *pCreateInfo, void *mem) API_AVAILABLE(macosx(10.11))
194 : MetalSurface(pCreateInfo, mem)
195{
196    metalLayer->initWithLayer(pCreateInfo->pLayer);
197}
198#endif
199
200#ifdef VK_USE_PLATFORM_MACOS_MVK
201MacOSSurfaceMVK::MacOSSurfaceMVK(const VkMacOSSurfaceCreateInfoMVK *pCreateInfo, void *mem) API_AVAILABLE(macosx(10.11))
202 : MetalSurface(pCreateInfo, mem)
203{
204    metalLayer->initWithView(pCreateInfo->pView);
205}
206#endif
207
208}
209