Initial Android surface

Bug: dawn:286

Change-Id: I50b45706f031254ac5beba5c07c3c4c3d7f9ea12
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/84200
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3813bfe..6dd0b02 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -88,6 +88,9 @@
     endif()
 elseif(APPLE)
     set(ENABLE_METAL ON)
+elseif(ANDROID)
+    set(ENABLE_VULKAN ON)
+    set(ENABLE_OPENGLES ON)
 elseif(UNIX)
     set(ENABLE_OPENGLES ON)
     set(ENABLE_DESKTOP_GL ON)
@@ -96,7 +99,7 @@
 endif()
 
 # GLFW is not supported in UWP
-if((WIN32 AND NOT WINDOWS_STORE) OR UNIX)
+if((WIN32 AND NOT WINDOWS_STORE) OR UNIX AND NOT ANDROID)
     set(DAWN_SUPPORTS_GLFW_FOR_WINDOWING ON)
 endif()
 
diff --git a/dawn_wire.json b/dawn_wire.json
index 58ef049..2e2318e 100644
--- a/dawn_wire.json
+++ b/dawn_wire.json
@@ -176,7 +176,8 @@
             "SurfaceDescriptorFromWindowsHWND",
             "SurfaceDescriptorFromXlibWindow",
             "SurfaceDescriptorFromWindowsCoreWindow",
-            "SurfaceDescriptorFromWindowsSwapChainPanel"
+            "SurfaceDescriptorFromWindowsSwapChainPanel",
+            "SurfaceDescriptorFromAndroidNativeWindow"
         ],
         "client_side_commands": [
             "AdapterCreateDevice",
diff --git a/src/dawn/native/Surface.cpp b/src/dawn/native/Surface.cpp
index bede05f..378a275 100644
--- a/src/dawn/native/Surface.cpp
+++ b/src/dawn/native/Surface.cpp
@@ -50,6 +50,9 @@
             case Surface::Type::Xlib:
                 s->Append("Xlib");
                 break;
+            case Surface::Type::AndroidWindow:
+                s->Append("AndroidWindow");
+                break;
         }
         return {true};
     }
@@ -65,6 +68,7 @@
                         descriptor);
 
         DAWN_TRY(ValidateSingleSType(descriptor->nextInChain,
+                                     wgpu::SType::SurfaceDescriptorFromAndroidNativeWindow,
                                      wgpu::SType::SurfaceDescriptorFromMetalLayer,
                                      wgpu::SType::SurfaceDescriptorFromWindowsHWND,
                                      wgpu::SType::SurfaceDescriptorFromWindowsCoreWindow,
@@ -82,6 +86,17 @@
         }
 #endif  // defined(DAWN_ENABLE_BACKEND_METAL)
 
+#if defined(DAWN_PLATFORM_ANDROID)
+        const SurfaceDescriptorFromAndroidNativeWindow* androidDesc = nullptr;
+        FindInChain(descriptor->nextInChain, &androidDesc);
+        // Currently the best validation we can do since it's not possible to check if the pointer
+        // to a ANativeWindow is valid.
+        if (androidDesc) {
+            DAWN_INVALID_IF(androidDesc->window == nullptr, "Android window is not set.");
+            return {};
+        }
+#endif  // defined(DAWN_PLATFORM_ANDROID)
+
 #if defined(DAWN_PLATFORM_WINDOWS)
 #    if defined(DAWN_PLATFORM_WIN32)
         const SurfaceDescriptorFromWindowsHWND* hwndDesc = nullptr;
@@ -142,20 +157,24 @@
     Surface::Surface(InstanceBase* instance, const SurfaceDescriptor* descriptor)
         : mInstance(instance) {
         ASSERT(descriptor->nextInChain != nullptr);
+        const SurfaceDescriptorFromAndroidNativeWindow* androidDesc = nullptr;
         const SurfaceDescriptorFromMetalLayer* metalDesc = nullptr;
         const SurfaceDescriptorFromWindowsHWND* hwndDesc = nullptr;
         const SurfaceDescriptorFromWindowsCoreWindow* coreWindowDesc = nullptr;
         const SurfaceDescriptorFromWindowsSwapChainPanel* swapChainPanelDesc = nullptr;
         const SurfaceDescriptorFromXlibWindow* xDesc = nullptr;
+        FindInChain(descriptor->nextInChain, &androidDesc);
         FindInChain(descriptor->nextInChain, &metalDesc);
         FindInChain(descriptor->nextInChain, &hwndDesc);
         FindInChain(descriptor->nextInChain, &coreWindowDesc);
         FindInChain(descriptor->nextInChain, &swapChainPanelDesc);
         FindInChain(descriptor->nextInChain, &xDesc);
-        ASSERT(metalDesc || hwndDesc || xDesc);
         if (metalDesc) {
             mType = Type::MetalLayer;
             mMetalLayer = metalDesc->layer;
+        } else if (androidDesc) {
+            mType = Type::AndroidWindow;
+            mAndroidNativeWindow = androidDesc->window;
         } else if (hwndDesc) {
             mType = Type::WindowsHWND;
             mHInstance = hwndDesc->hinstance;
@@ -202,6 +221,11 @@
         return mType;
     }
 
+    void* Surface::GetAndroidNativeWindow() const {
+        ASSERT(mType == Type::AndroidWindow);
+        return mAndroidNativeWindow;
+    }
+
     void* Surface::GetMetalLayer() const {
         ASSERT(mType == Type::MetalLayer);
         return mMetalLayer;
diff --git a/src/dawn/native/Surface.h b/src/dawn/native/Surface.h
index c6320af..367a298 100644
--- a/src/dawn/native/Surface.h
+++ b/src/dawn/native/Surface.h
@@ -50,13 +50,23 @@
         NewSwapChainBase* GetAttachedSwapChain();
 
         // These are valid to call on all Surfaces.
-        enum class Type { MetalLayer, WindowsHWND, WindowsCoreWindow, WindowsSwapChainPanel, Xlib };
+        enum class Type {
+            AndroidWindow,
+            MetalLayer,
+            WindowsHWND,
+            WindowsCoreWindow,
+            WindowsSwapChainPanel,
+            Xlib
+        };
         Type GetType() const;
         InstanceBase* GetInstance();
 
         // Valid to call if the type is MetalLayer
         void* GetMetalLayer() const;
 
+        // Valid to call if the type is Android
+        void* GetAndroidNativeWindow() const;
+
         // Valid to call if the type is WindowsHWND
         void* GetHInstance() const;
         void* GetHWND() const;
@@ -83,6 +93,9 @@
         // MetalLayer
         void* mMetalLayer = nullptr;
 
+        // ANativeWindow
+        void* mAndroidNativeWindow = nullptr;
+
         // WindowsHwnd
         void* mHInstance = nullptr;
         void* mHWND = nullptr;
diff --git a/src/dawn/native/SwapChain.cpp b/src/dawn/native/SwapChain.cpp
index 6b4c331..5fffc09 100644
--- a/src/dawn/native/SwapChain.cpp
+++ b/src/dawn/native/SwapChain.cpp
@@ -74,11 +74,17 @@
 
             DAWN_TRY(ValidatePresentMode(descriptor->presentMode));
 
-            // TODO(crbug.com/dawn/160): Lift this restriction once
-            // wgpu::Instance::GetPreferredSurfaceFormat is implemented.
-            DAWN_INVALID_IF(descriptor->format != wgpu::TextureFormat::BGRA8Unorm,
+// TODO(crbug.com/dawn/160): Lift this restriction once wgpu::Instance::GetPreferredSurfaceFormat is
+// implemented.
+// TODO(dawn:286):
+#if defined(DAWN_PLATFORM_ANDROID)
+            constexpr wgpu::TextureFormat kRequireSwapChainFormat = wgpu::TextureFormat::RGBA8Unorm;
+#else
+            constexpr wgpu::TextureFormat kRequireSwapChainFormat = wgpu::TextureFormat::BGRA8Unorm;
+#endif  // !defined(DAWN_PLATFORM_ANDROID)
+            DAWN_INVALID_IF(descriptor->format != kRequireSwapChainFormat,
                             "Format (%s) is not %s, which is (currently) the only accepted format.",
-                            descriptor->format, wgpu::TextureFormat::BGRA8Unorm);
+                            descriptor->format, kRequireSwapChainFormat);
 
             DAWN_INVALID_IF(descriptor->usage != wgpu::TextureUsage::RenderAttachment,
                             "Usage (%s) is not %s, which is (currently) the only accepted usage.",
diff --git a/src/dawn/native/vulkan/SwapChainVk.cpp b/src/dawn/native/vulkan/SwapChainVk.cpp
index d0750e6..2c80d06 100644
--- a/src/dawn/native/vulkan/SwapChainVk.cpp
+++ b/src/dawn/native/vulkan/SwapChainVk.cpp
@@ -134,6 +134,30 @@
                     break;
 #endif  // defined(DAWN_PLATFORM_WINDOWS)
 
+#if defined(DAWN_PLATFORM_ANDROID)
+                case Surface::Type::AndroidWindow: {
+                    if (info.HasExt(InstanceExt::AndroidSurface)) {
+                        ASSERT(surface->GetAndroidNativeWindow() != nullptr);
+
+                        VkAndroidSurfaceCreateInfoKHR createInfo;
+                        createInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
+                        createInfo.pNext = nullptr;
+                        createInfo.flags = 0;
+                        createInfo.window =
+                            static_cast<struct ANativeWindow*>(surface->GetAndroidNativeWindow());
+
+                        VkSurfaceKHR vkSurface = VK_NULL_HANDLE;
+                        DAWN_TRY(CheckVkSuccess(
+                            fn.CreateAndroidSurfaceKHR(instance, &createInfo, nullptr, &*vkSurface),
+                            "CreateAndroidSurfaceKHR"));
+                        return vkSurface;
+                    }
+
+                    break;
+                }
+
+#endif  // defined(DAWN_PLATFORM_ANDROID)
+
 #if defined(DAWN_USE_X11)
                 case Surface::Type::Xlib: {
                     if (info.HasExt(InstanceExt::XlibSurface)) {
@@ -382,22 +406,22 @@
             config.wgpuUsage = GetUsage();
         }
 
-        // Only support BGRA8Unorm with SRGB color space for now.
-        bool hasBGRA8Unorm = false;
+        // Only support BGRA8Unorm (and RGBA8Unorm on android) with SRGB color space for now.
+        config.wgpuFormat = GetFormat();
+        config.format = VulkanImageFormat(ToBackend(GetDevice()), config.wgpuFormat);
+        config.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
+
+        bool formatIsSupported = false;
         for (const VkSurfaceFormatKHR& format : surfaceInfo.formats) {
-            if (format.format == VK_FORMAT_B8G8R8A8_UNORM &&
-                format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
-                hasBGRA8Unorm = true;
+            if (format.format == config.format && format.colorSpace == config.colorSpace) {
+                formatIsSupported = true;
                 break;
             }
         }
-        if (!hasBGRA8Unorm) {
-            return DAWN_INTERNAL_ERROR(
-                "Vulkan SwapChain must support BGRA8Unorm with sRGB colorspace.");
+        if (!formatIsSupported) {
+            return DAWN_INTERNAL_ERROR(absl::StrFormat(
+                "Vulkan SwapChain must support %s with sRGB colorspace.", config.wgpuFormat));
         }
-        config.format = VK_FORMAT_B8G8R8A8_UNORM;
-        config.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
-        config.wgpuFormat = wgpu::TextureFormat::BGRA8Unorm;
 
         // Only the identity transform with opaque alpha is supported for now.
         DAWN_INVALID_IF((surfaceInfo.capabilities.supportedTransforms &
@@ -406,11 +430,26 @@
 
         config.transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
 
-        DAWN_INVALID_IF((surfaceInfo.capabilities.supportedCompositeAlpha &
-                         VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR) == 0,
-                        "Vulkan SwapChain must support opaque alpha.");
-
         config.alphaMode = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
+#if !defined(DAWN_PLATFORM_ANDROID)
+        DAWN_INVALID_IF((surfaceInfo.capabilities.supportedCompositeAlpha &
+                            VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR) == 0,
+                        "Vulkan SwapChain must support opaque alpha.");
+#else
+        // TODO(dawn:286): investigate composite alpha for WebGPU native
+        VkCompositeAlphaFlagBitsKHR compositeAlphaFlags[4] = {
+            VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
+            VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR,
+            VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR,
+            VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR,
+        };
+        for (uint32_t i = 0; i < 4; i++) {
+            if (surfCapabilities.supportedCompositeAlpha & compositeAlphaFlags[i]) {
+                config.alphaMode = compositeAlphaFlags[i];
+                break;
+            }
+        }
+#endif  // #if !defined(DAWN_PLATFORM_ANDROID)
 
         // Choose the number of images for the swapchain= and clamp it to the min and max from the
         // surface capabilities. maxImageCount = 0 means there is no limit.
diff --git a/src/dawn/native/vulkan/VulkanExtensions.cpp b/src/dawn/native/vulkan/VulkanExtensions.cpp
index e34736e..3f54e54 100644
--- a/src/dawn/native/vulkan/VulkanExtensions.cpp
+++ b/src/dawn/native/vulkan/VulkanExtensions.cpp
@@ -47,6 +47,7 @@
         {InstanceExt::Win32Surface, "VK_KHR_win32_surface", NeverPromoted},
         {InstanceExt::XcbSurface, "VK_KHR_xcb_surface", NeverPromoted},
         {InstanceExt::XlibSurface, "VK_KHR_xlib_surface", NeverPromoted},
+        {InstanceExt::AndroidSurface, "VK_KHR_android_surface", NeverPromoted},
 
         {InstanceExt::DebugUtils, "VK_EXT_debug_utils", NeverPromoted},
         {InstanceExt::ValidationFeatures, "VK_EXT_validation_features", NeverPromoted},
@@ -99,6 +100,7 @@
                     hasDependencies = HasDep(InstanceExt::GetPhysicalDeviceProperties2);
                     break;
 
+                case InstanceExt::AndroidSurface:
                 case InstanceExt::FuchsiaImagePipeSurface:
                 case InstanceExt::MetalSurface:
                 case InstanceExt::WaylandSurface:
diff --git a/src/dawn/native/vulkan/VulkanExtensions.h b/src/dawn/native/vulkan/VulkanExtensions.h
index 4e3cf19..d58c35e 100644
--- a/src/dawn/native/vulkan/VulkanExtensions.h
+++ b/src/dawn/native/vulkan/VulkanExtensions.h
@@ -37,6 +37,7 @@
         Win32Surface,
         XcbSurface,
         XlibSurface,
+        AndroidSurface,
 
         // Others
         DebugUtils,
diff --git a/src/dawn/native/vulkan/VulkanFunctions.cpp b/src/dawn/native/vulkan/VulkanFunctions.cpp
index 782cd5a..48e9709 100644
--- a/src/dawn/native/vulkan/VulkanFunctions.cpp
+++ b/src/dawn/native/vulkan/VulkanFunctions.cpp
@@ -147,6 +147,12 @@
         }
 #endif  // defined(DAWN_PLATFORM_WINDOWS)
 
+#if defined(DAWN_PLATFORM_ANDROID)
+        if (globalInfo.HasExt(InstanceExt::AndroidSurface)) {
+            GET_INSTANCE_PROC(CreateAndroidSurfaceKHR);
+        }
+#endif  // defined(DAWN_PLATFORM_ANDROID)
+
 #if defined(DAWN_USE_X11)
         if (globalInfo.HasExt(InstanceExt::XlibSurface)) {
             GET_INSTANCE_PROC(CreateXlibSurfaceKHR);
diff --git a/src/dawn/native/vulkan/VulkanFunctions.h b/src/dawn/native/vulkan/VulkanFunctions.h
index 07b8b39..0de5192 100644
--- a/src/dawn/native/vulkan/VulkanFunctions.h
+++ b/src/dawn/native/vulkan/VulkanFunctions.h
@@ -132,6 +132,10 @@
             GetPhysicalDeviceWin32PresentationSupportKHR = nullptr;
 #endif  // defined(DAWN_PLATFORM_WINDOWS)
 
+#if defined(DAWN_PLATFORM_ANDROID)
+        PFN_vkCreateAndroidSurfaceKHR CreateAndroidSurfaceKHR = nullptr;
+#endif  // defined(DAWN_PLATFORM_ANDROID)
+
 #if defined(DAWN_USE_X11)
         // KHR_xlib_surface
         PFN_vkCreateXlibSurfaceKHR CreateXlibSurfaceKHR = nullptr;
diff --git a/src/dawn/utils/CMakeLists.txt b/src/dawn/utils/CMakeLists.txt
index 3ed0994..6e4d6f7 100644
--- a/src/dawn/utils/CMakeLists.txt
+++ b/src/dawn/utils/CMakeLists.txt
@@ -43,7 +43,6 @@
             dawn_proc
             dawn_wire
             SPIRV-Tools-opt
-            glfw
 )
 
 if(WIN32 AND NOT WINDOWS_STORE)
diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt
index 7c8619f..136c848 100644
--- a/third_party/CMakeLists.txt
+++ b/third_party/CMakeLists.txt
@@ -32,7 +32,7 @@
     add_subdirectory(${DAWN_SPIRV_TOOLS_DIR} "${CMAKE_CURRENT_BINARY_DIR}/spirv-tools")
 endif()
 
-if (NOT TARGET glfw)
+if (NOT TARGET glfw AND DAWN_SUPPORTS_GLFW_FOR_WINDOWING)
     set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE)
     set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE)
     set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)