MacOS WSI

This cl implements the VK_MVK_macos_surface extension, which
adds the vkCreateMacOSSurfaceMVK function on MacOS.

According to the spec:
"The VK_MVK_macos_surface extension is an instance extension.
 It provides a mechanism to create a VkSurfaceKHR object
 (defined by the VK_KHR_surface extension) that refers to an
 NSView, the native surface type of macOS, which is
 underpinned by a CAMetalLayer, to support rendering to the
 surface using Apple’s Metal framework."

Bug b/137673628

Change-Id: Iacf7696b1d9e52d7349ea4efa01f0acdd09a6c8f
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/33848
Reviewed-by: Hernan Liatis <hliatis@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Reviewed-by: Chris Forbes <chrisforbes@google.com>
Tested-by: Alexis Hétu <sugoi@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index cc702b2..166049e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -394,9 +394,13 @@
         set_cpp_flag("-fPIC")
     endif()
 
-    if(LINUX)
+    if(WIN32)
+        set_cpp_flag("-DVK_USE_PLATFORM_WIN32_KHR")
+    elseif(LINUX)
         set_cpp_flag("-DUSE_X11=1")
         set_cpp_flag("-DVK_USE_PLATFORM_XLIB_KHR")
+    elseif(APPLE)
+        set_cpp_flag("-DVK_USE_PLATFORM_MACOS_MVK")
     endif()
 
     # Use -g3 to have even more debug info
@@ -1812,6 +1816,11 @@
     list(APPEND OPENGL_COMPILER_LIST
         ${OPENGL_COMPILER_DIR}/ossource_posix.cpp
     )
+
+    list(APPEND VULKAN_LIST
+        ${SOURCE_DIR}/WSI/MacOSSurfaceMVK.mm
+        ${SOURCE_DIR}/WSI/MacOSSurfaceMVK.h
+    )
 elseif(ANDROID)
     list(APPEND SWIFTSHADER_LIST
         ${SOURCE_DIR}/Main/FrameBufferAndroid.cpp
diff --git a/src/Vulkan/VkGetProcAddress.cpp b/src/Vulkan/VkGetProcAddress.cpp
index 4bfb07e..c132e7f 100644
--- a/src/Vulkan/VkGetProcAddress.cpp
+++ b/src/Vulkan/VkGetProcAddress.cpp
@@ -93,6 +93,10 @@
 	MAKE_VULKAN_INSTANCE_ENTRY(vkCreateXlibSurfaceKHR),
 	MAKE_VULKAN_INSTANCE_ENTRY(vkGetPhysicalDeviceXlibPresentationSupportKHR),
 #endif
+#ifdef VK_USE_PLATFORM_MACOS_MVK
+    // VK_MVK_macos_surface
+    MAKE_VULKAN_INSTANCE_ENTRY(vkCreateMacOSSurfaceMVK),
+#endif
 };
 #undef MAKE_VULKAN_INSTANCE_ENTRY
 
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index 2450b54..ef70b3f 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -42,6 +42,10 @@
 #include "VkShaderModule.hpp"
 #include "VkRenderPass.hpp"
 
+#ifdef VK_USE_PLATFORM_MACOS_MVK
+#include "WSI/MacOSSurfaceMVK.h"
+#endif
+
 #ifdef VK_USE_PLATFORM_XLIB_KHR
 #include "WSI/XlibSurfaceKHR.hpp"
 #endif
@@ -135,6 +139,9 @@
 #ifdef VK_USE_PLATFORM_XLIB_KHR
 	{ VK_KHR_XLIB_SURFACE_EXTENSION_NAME, VK_KHR_XLIB_SURFACE_SPEC_VERSION },
 #endif
+#ifdef VK_USE_PLATFORM_MACOS_MVK
+    { VK_MVK_MACOS_SURFACE_EXTENSION_NAME, VK_MVK_MACOS_SURFACE_SPEC_VERSION },
+#endif
 };
 
 static const VkExtensionProperties deviceExtensionProperties[] =
@@ -2611,6 +2618,16 @@
 }
 #endif
 
+#ifdef VK_USE_PLATFORM_MACOS_MVK
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateMacOSSurfaceMVK(VkInstance instance, const VkMacOSSurfaceCreateInfoMVK* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface)
+{
+    TRACE("(VkInstance instance = %p, VkMacOSSurfaceCreateInfoMVK* pCreateInfo = %p, VkAllocationCallbacks* pAllocator = %p, VkSurface* pSurface = %p)",
+          instance, pCreateInfo, pAllocator, pSurface);
+
+    return vk::MacOSSurfaceMVK::Create(pAllocator, pCreateInfo, pSurface);
+}
+#endif
+
 #ifndef __ANDROID__
 VKAPI_ATTR void VKAPI_CALL vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface, const VkAllocationCallbacks* pAllocator)
 {
diff --git a/src/WSI/MacOSSurfaceMVK.h b/src/WSI/MacOSSurfaceMVK.h
new file mode 100644
index 0000000..7822fb6
--- /dev/null
+++ b/src/WSI/MacOSSurfaceMVK.h
@@ -0,0 +1,45 @@
+// 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.
+
+#ifndef SWIFTSHADER_MACOSSURFACEMVK_HPP
+#define SWIFTSHADER_MACOSSURFACEMVK_HPP
+
+#include "Vulkan/VkObject.hpp"
+#include "VkSurfaceKHR.hpp"
+#include "vulkan/vulkan_macos.h"
+
+namespace vk {
+
+class MetalLayer;
+
+class MacOSSurfaceMVK : public SurfaceKHR, public ObjectBase<MacOSSurfaceMVK, VkSurfaceKHR> {
+public:
+    MacOSSurfaceMVK(const VkMacOSSurfaceCreateInfoMVK *pCreateInfo, void *mem);
+
+    void destroySurface(const VkAllocationCallbacks *pAllocator) override;
+
+    static size_t ComputeRequiredAllocationSize(const VkMacOSSurfaceCreateInfoMVK *pCreateInfo);
+
+    void getSurfaceCapabilities(VkSurfaceCapabilitiesKHR *pSurfaceCapabilities) const override;
+
+    virtual void attachImage(PresentImage* image) override {}
+    virtual void detachImage(PresentImage* image) override {}
+    void present(PresentImage* image) override;
+
+private:
+    MetalLayer* metalLayer = nullptr;
+};
+
+}
+#endif //SWIFTSHADER_MACOSSURFACEMVK_HPP
diff --git a/src/WSI/MacOSSurfaceMVK.mm b/src/WSI/MacOSSurfaceMVK.mm
new file mode 100644
index 0000000..090cc20
--- /dev/null
+++ b/src/WSI/MacOSSurfaceMVK.mm
@@ -0,0 +1,144 @@
+// 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 "MacOSSurfaceMVK.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 init(const void* pView)
+    {
+        view = nullptr;
+        layer = nullptr;
+
+        id<NSObject> obj = (id<NSObject>)pView;
+
+        if([obj isKindOfClass: [NSView class]])
+        {
+            if(!NSThread.isMainThread)
+            {
+                UNREACHABLE("MetalLayer::init(): not called from main thread");
+            }
+            view = (NSView*)[obj retain];
+
+            obj = view.layer;
+            if ([obj isKindOfClass: [CAMetalLayer class]])
+            {
+                layer = (CAMetalLayer*)[obj retain];
+            }
+            else
+            {
+                UNREACHABLE("MetalLayer::init(): view doesn't have metal backed layer");
+            }
+        }
+    }
+
+    void release()
+    {
+        if(layer)
+        {
+            [layer release];
+        }
+
+        if(view)
+        {
+            [view release];
+        }
+    }
+
+    VkExtent2D getExtent() const
+    {
+        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
+    {
+        if(layer)
+        {
+            return [layer nextDrawable];
+        }
+
+        return nil;
+    }
+
+private:
+    NSView* view;
+    CAMetalLayer* layer;
+};
+
+MacOSSurfaceMVK::MacOSSurfaceMVK(const VkMacOSSurfaceCreateInfoMVK *pCreateInfo, void *mem) :
+    metalLayer(reinterpret_cast<MetalLayer*>(mem))
+{
+    metalLayer->init(pCreateInfo->pView);
+}
+
+void MacOSSurfaceMVK::destroySurface(const VkAllocationCallbacks *pAllocator)
+{
+    if(metalLayer)
+    {
+        metalLayer->release();
+    }
+
+    vk::deallocate(metalLayer, pAllocator);
+}
+
+size_t MacOSSurfaceMVK::ComputeRequiredAllocationSize(const VkMacOSSurfaceCreateInfoMVK *pCreateInfo)
+{
+    return sizeof(MetalLayer);
+}
+
+void MacOSSurfaceMVK::getSurfaceCapabilities(VkSurfaceCapabilitiesKHR *pSurfaceCapabilities) const
+{
+    SurfaceKHR::getSurfaceCapabilities(pSurfaceCapabilities);
+
+    VkExtent2D extent = metalLayer->getExtent();
+    pSurfaceCapabilities->currentExtent = extent;
+    pSurfaceCapabilities->minImageExtent = extent;
+    pSurfaceCapabilities->maxImageExtent = extent;
+}
+
+void MacOSSurfaceMVK::present(PresentImage* image)
+{
+    auto drawable = metalLayer->getNextDrawable();
+    if(drawable)
+    {
+        VkExtent3D extent = image->getImage()->getMipLevelExtent(VK_IMAGE_ASPECT_COLOR_BIT, 0);
+        [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];
+    }
+}
+
+}