Check FP16 support on vulkan backend

This patch check FP16 support on vulkan backend, and introduces
the shader_float16 extension.

BUG=dawn:426
TEST=dawn_end2end_tests

Change-Id: Ie09568a416ce9eb2c11afeede3e7da520550d5fb
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/21901
Commit-Queue: Xinghua Cao <xinghua.cao@intel.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Jiawei Shao <jiawei.shao@intel.com>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/dawn.json b/dawn.json
index 3583770..dc39bb5 100644
--- a/dawn.json
+++ b/dawn.json
@@ -650,7 +650,8 @@
         "category": "structure",
         "extensible": false,
         "members": [
-            {"name": "texture compression BC", "type": "bool", "default": "false"}
+            {"name": "texture compression BC", "type": "bool", "default": "false"},
+            {"name": "shader float16", "type": "bool", "default": "false"}
         ]
     },
     "depth stencil state descriptor": {
diff --git a/src/dawn_native/Extensions.cpp b/src/dawn_native/Extensions.cpp
index a2b5a9d..de6214f 100644
--- a/src/dawn_native/Extensions.cpp
+++ b/src/dawn_native/Extensions.cpp
@@ -34,7 +34,12 @@
             {{Extension::TextureCompressionBC,
               {"texture_compression_bc", "Support Block Compressed (BC) texture formats",
                "https://bugs.chromium.org/p/dawn/issues/detail?id=42"},
-              &WGPUDeviceProperties::textureCompressionBC}}};
+              &WGPUDeviceProperties::textureCompressionBC},
+             {Extension::ShaderFloat16,
+              {"shader_float16",
+               "Support 16bit float arithmetic and declarations in uniform and storage buffers",
+               "https://bugs.chromium.org/p/dawn/issues/detail?id=426"},
+              &WGPUDeviceProperties::shaderFloat16}}};
 
     }  // anonymous namespace
 
diff --git a/src/dawn_native/Extensions.h b/src/dawn_native/Extensions.h
index 6e6d82d..9dbdf77 100644
--- a/src/dawn_native/Extensions.h
+++ b/src/dawn_native/Extensions.h
@@ -25,6 +25,7 @@
 
     enum class Extension {
         TextureCompressionBC,
+        ShaderFloat16,
 
         EnumCount,
         InvalidEnum = EnumCount,
diff --git a/src/dawn_native/vulkan/AdapterVk.cpp b/src/dawn_native/vulkan/AdapterVk.cpp
index 9cbe82d..86041df 100644
--- a/src/dawn_native/vulkan/AdapterVk.cpp
+++ b/src/dawn_native/vulkan/AdapterVk.cpp
@@ -73,6 +73,13 @@
         if (mDeviceInfo.features.textureCompressionBC == VK_TRUE) {
             mSupportedExtensions.EnableExtension(Extension::TextureCompressionBC);
         }
+
+        if (mDeviceInfo.shaderFloat16Int8 &&
+            mDeviceInfo.shaderFloat16Int8Features.shaderFloat16 == VK_TRUE &&
+            mDeviceInfo._16BitStorage &&
+            mDeviceInfo._16BitStorageFeatures.uniformAndStorageBuffer16BitAccess == VK_TRUE) {
+            mSupportedExtensions.EnableExtension(Extension::ShaderFloat16);
+        }
     }
 
     ResultOrError<DeviceBase*> Adapter::CreateDeviceImpl(const DeviceDescriptor* descriptor) {
diff --git a/src/dawn_native/vulkan/DeviceVk.cpp b/src/dawn_native/vulkan/DeviceVk.cpp
index 9edefcb..a4a81a4 100644
--- a/src/dawn_native/vulkan/DeviceVk.cpp
+++ b/src/dawn_native/vulkan/DeviceVk.cpp
@@ -333,6 +333,25 @@
             usedKnobs.features.textureCompressionBC = VK_TRUE;
         }
 
+        if (IsExtensionEnabled(Extension::ShaderFloat16)) {
+            const VulkanDeviceInfo& deviceInfo = ToBackend(GetAdapter())->GetDeviceInfo();
+            ASSERT(deviceInfo.shaderFloat16Int8 &&
+                   deviceInfo.shaderFloat16Int8Features.shaderFloat16 == VK_TRUE &&
+                   deviceInfo._16BitStorage &&
+                   deviceInfo._16BitStorageFeatures.uniformAndStorageBuffer16BitAccess == VK_TRUE);
+
+            usedKnobs.shaderFloat16Int8 = true;
+            usedKnobs.shaderFloat16Int8Features.shaderFloat16 = VK_TRUE;
+            extensionsToRequest.push_back(kExtensionNameKhrShaderFloat16Int8);
+
+            usedKnobs._16BitStorage = true;
+            usedKnobs._16BitStorageFeatures.uniformAndStorageBuffer16BitAccess = VK_TRUE;
+            // VK_KHR_16bit_storage is promoted to Vulkan 1.1.
+            if (deviceInfo.properties.apiVersion < VK_MAKE_VERSION(1, 1, 0)) {
+                extensionsToRequest.push_back(kExtensionNameKhr16BitStorage);
+            }
+        }
+
         // Find a universal queue family
         {
             // Note that GRAPHICS and COMPUTE imply TRANSFER so we don't need to check for it.
diff --git a/src/dawn_native/vulkan/VulkanInfo.cpp b/src/dawn_native/vulkan/VulkanInfo.cpp
index 2a32262..b2d9928 100644
--- a/src/dawn_native/vulkan/VulkanInfo.cpp
+++ b/src/dawn_native/vulkan/VulkanInfo.cpp
@@ -79,6 +79,8 @@
     const char kExtensionNameKhrXlibSurface[] = "VK_KHR_xlib_surface";
     const char kExtensionNameFuchsiaImagePipeSurface[] = "VK_FUCHSIA_imagepipe_surface";
     const char kExtensionNameKhrMaintenance1[] = "VK_KHR_maintenance1";
+    const char kExtensionNameKhrShaderFloat16Int8[] = "VK_KHR_shader_float16_int8";
+    const char kExtensionNameKhr16BitStorage[] = "VK_KHR_16bit_storage";
 
     ResultOrError<VulkanGlobalInfo> GatherGlobalInfo(const Backend& backend) {
         VulkanGlobalInfo info = {};
@@ -221,6 +223,7 @@
     ResultOrError<VulkanDeviceInfo> GatherDeviceInfo(const Adapter& adapter) {
         VulkanDeviceInfo info = {};
         VkPhysicalDevice physicalDevice = adapter.GetPhysicalDevice();
+        const VulkanGlobalInfo& globalInfo = adapter.GetBackend()->GetGlobalInfo();
         const VulkanFunctions& vkFunctions = adapter.GetBackend()->GetFunctions();
 
         // Gather general info about the device
@@ -311,6 +314,23 @@
                 if (IsExtensionName(extension, kExtensionNameKhrMaintenance1)) {
                     info.maintenance1 = true;
                 }
+                if (IsExtensionName(extension, kExtensionNameKhrShaderFloat16Int8) &&
+                    globalInfo.getPhysicalDeviceProperties2) {
+                    info.shaderFloat16Int8 = true;
+                    info.shaderFloat16Int8Features.sType =
+                        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR;
+
+                    VkPhysicalDeviceFeatures2KHR physicalDeviceFeatures2 = {};
+                    physicalDeviceFeatures2.sType =
+                        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR;
+                    physicalDeviceFeatures2.pNext = &info.shaderFloat16Int8Features;
+                    vkFunctions.GetPhysicalDeviceFeatures2(physicalDevice,
+                                                           &physicalDeviceFeatures2);
+                }
+                if (IsExtensionName(extension, kExtensionNameKhr16BitStorage) &&
+                    globalInfo.getPhysicalDeviceProperties2) {
+                    info._16BitStorage = true;
+                }
             }
         }
 
@@ -319,6 +339,20 @@
             info.maintenance1 = true;
         }
 
+        // VK_KHR_16bit_storage is promoted to Vulkan 1.1, so gather information if either is
+        // present, and mark the extension as available.
+        if (info._16BitStorage || info.properties.apiVersion >= VK_MAKE_VERSION(1, 1, 0)) {
+            ASSERT(globalInfo.getPhysicalDeviceProperties2);
+            info._16BitStorage = true;
+            info._16BitStorageFeatures.sType =
+                VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES;
+
+            VkPhysicalDeviceFeatures2 physicalDeviceFeatures2 = {};
+            physicalDeviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
+            physicalDeviceFeatures2.pNext = &info._16BitStorageFeatures;
+            vkFunctions.GetPhysicalDeviceFeatures2(physicalDevice, &physicalDeviceFeatures2);
+        }
+
         // TODO(cwallez@chromium.org): gather info about formats
 
         return std::move(info);
diff --git a/src/dawn_native/vulkan/VulkanInfo.h b/src/dawn_native/vulkan/VulkanInfo.h
index 354d9b3..81ef054 100644
--- a/src/dawn_native/vulkan/VulkanInfo.h
+++ b/src/dawn_native/vulkan/VulkanInfo.h
@@ -52,6 +52,8 @@
     extern const char kExtensionNameKhrXlibSurface[];
     extern const char kExtensionNameFuchsiaImagePipeSurface[];
     extern const char kExtensionNameKhrMaintenance1[];
+    extern const char kExtensionNameKhrShaderFloat16Int8[];
+    extern const char kExtensionNameKhr16BitStorage[];
 
     // Global information - gathered before the instance is created
     struct VulkanGlobalKnobs {
@@ -85,6 +87,8 @@
     // Device information - gathered before the device is created.
     struct VulkanDeviceKnobs {
         VkPhysicalDeviceFeatures features;
+        VkPhysicalDeviceShaderFloat16Int8FeaturesKHR shaderFloat16Int8Features;
+        VkPhysicalDevice16BitStorageFeaturesKHR _16BitStorageFeatures;
 
         // Extensions, promoted extensions are set to true if their core version is supported.
         bool debugMarker = false;
@@ -98,6 +102,8 @@
         bool externalSemaphoreZirconHandle = false;
         bool swapchain = false;
         bool maintenance1 = false;
+        bool shaderFloat16Int8 = false;
+        bool _16BitStorage = false;
     };
 
     struct VulkanDeviceInfo : VulkanDeviceKnobs {
diff --git a/src/tests/BUILD.gn b/src/tests/BUILD.gn
index 6f3799c..9f6ddc1 100644
--- a/src/tests/BUILD.gn
+++ b/src/tests/BUILD.gn
@@ -287,6 +287,7 @@
     "end2end/RenderPassTests.cpp",
     "end2end/SamplerTests.cpp",
     "end2end/ScissorTests.cpp",
+    "end2end/ShaderFloat16Tests.cpp",
     "end2end/StorageTextureTests.cpp",
     "end2end/TextureFormatTests.cpp",
     "end2end/TextureSubresourceTests.cpp",
diff --git a/src/tests/DawnTest.cpp b/src/tests/DawnTest.cpp
index 21ddcaa..09848de 100644
--- a/src/tests/DawnTest.cpp
+++ b/src/tests/DawnTest.cpp
@@ -1144,6 +1144,7 @@
     }
 
     template class ExpectEq<uint8_t>;
+    template class ExpectEq<uint16_t>;
     template class ExpectEq<uint32_t>;
     template class ExpectEq<RGBA8>;
     template class ExpectEq<float>;
diff --git a/src/tests/DawnTest.h b/src/tests/DawnTest.h
index f80197f..3fd3f20 100644
--- a/src/tests/DawnTest.h
+++ b/src/tests/DawnTest.h
@@ -30,6 +30,14 @@
 // until the end of the test. Also expectations use a copy to a MapRead buffer to get the data
 // so resources should have the CopySrc allowed usage bit if you want to add expectations on
 // them.
+#define EXPECT_BUFFER_U16_EQ(expected, buffer, offset)                         \
+    AddBufferExpectation(__FILE__, __LINE__, buffer, offset, sizeof(uint16_t), \
+                         new ::detail::ExpectEq<uint16_t>(expected))
+
+#define EXPECT_BUFFER_U16_RANGE_EQ(expected, buffer, offset, count)                    \
+    AddBufferExpectation(__FILE__, __LINE__, buffer, offset, sizeof(uint16_t) * count, \
+                         new ::detail::ExpectEq<uint16_t>(expected, count))
+
 #define EXPECT_BUFFER_U32_EQ(expected, buffer, offset)                         \
     AddBufferExpectation(__FILE__, __LINE__, buffer, offset, sizeof(uint32_t), \
                          new ::detail::ExpectEq<uint32_t>(expected))
@@ -418,6 +426,7 @@
         std::vector<T> mExpected;
     };
     extern template class ExpectEq<uint8_t>;
+    extern template class ExpectEq<int16_t>;
     extern template class ExpectEq<uint32_t>;
     extern template class ExpectEq<RGBA8>;
     extern template class ExpectEq<float>;
diff --git a/src/tests/end2end/ShaderFloat16Tests.cpp b/src/tests/end2end/ShaderFloat16Tests.cpp
new file mode 100644
index 0000000..73bd0bb
--- /dev/null
+++ b/src/tests/end2end/ShaderFloat16Tests.cpp
@@ -0,0 +1,111 @@
+// Copyright 2020 The Dawn Authors
+//
+// 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 "common/Math.h"
+#include "tests/DawnTest.h"
+
+#include "utils/WGPUHelpers.h"
+
+class ShaderFloat16Tests : public DawnTest {
+  protected:
+    std::vector<const char*> GetRequiredExtensions() override {
+        mIsShaderFloat16Supported = SupportsExtensions({"shader_float16"});
+        if (!mIsShaderFloat16Supported) {
+            return {};
+        }
+
+        return {"shader_float16"};
+    }
+
+    bool IsShaderFloat16Supported() const {
+        return mIsShaderFloat16Supported;
+    }
+
+    bool mIsShaderFloat16Supported = false;
+};
+
+// Test basic 16bit float arithmetic and 16bit storage features.
+TEST_P(ShaderFloat16Tests, VendorIdFilter) {
+    DAWN_SKIP_TEST_IF(!IsShaderFloat16Supported());
+
+    uint16_t uniformData[] = {Float32ToFloat16(1.23), Float32ToFloat16(0.0)};  // 0.0 is a padding.
+    wgpu::Buffer uniformBuffer = utils::CreateBufferFromData(
+        device, &uniformData, sizeof(uniformData), wgpu::BufferUsage::Uniform);
+
+    uint16_t bufferInData[] = {Float32ToFloat16(2.34), Float32ToFloat16(0.0)};  // 0.0 is a padding.
+    wgpu::Buffer bufferIn = utils::CreateBufferFromData(device, &bufferInData, sizeof(bufferInData),
+                                                        wgpu::BufferUsage::Storage);
+
+    // TODO(xinghua.cao@intel.com): the zero for padding is required now. No need to
+    // createBufferFromData once buffer lazy-zero-init is done.
+    uint16_t bufferOutData[] = {Float32ToFloat16(0.0), Float32ToFloat16(0.0)};
+    wgpu::Buffer bufferOut =
+        utils::CreateBufferFromData(device, &bufferOutData, sizeof(bufferOutData),
+                                    wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc);
+
+    wgpu::ShaderModule module =
+        utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"(
+        #version 450
+
+        #extension GL_AMD_gpu_shader_half_float : require
+
+        struct S {
+            float16_t f;
+            float16_t padding;
+        };
+        layout(std140, set = 0, binding = 0) uniform uniformBuf {
+            S c;
+        };
+
+        layout(std140, set = 0, binding = 1) readonly buffer bufA {
+            S a;
+        } ;
+
+        layout(std140, set = 0, binding = 2) buffer bufB {
+            S b;
+        } ;
+
+        void main() {
+            b.f = a.f + c.f;
+        }
+
+        )");
+
+    wgpu::ComputePipelineDescriptor csDesc;
+    csDesc.computeStage.module = module;
+    csDesc.computeStage.entryPoint = "main";
+    wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&csDesc);
+
+    wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
+                                                     {
+                                                         {0, uniformBuffer, 0, sizeof(uniformData)},
+                                                         {1, bufferIn, 0, sizeof(bufferInData)},
+                                                         {2, bufferOut, 0, sizeof(bufferOutData)},
+                                                     });
+
+    wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+    wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
+    pass.SetPipeline(pipeline);
+    pass.SetBindGroup(0, bindGroup);
+    pass.Dispatch(1);
+    pass.EndPass();
+    wgpu::CommandBuffer commands = encoder.Finish();
+    queue.Submit(1, &commands);
+
+    uint16_t expected[] = {Float32ToFloat16(3.57), Float32ToFloat16(0.0)};  // 0.0 is a padding.
+
+    EXPECT_BUFFER_U16_RANGE_EQ(expected, bufferOut, 0, 2);
+}
+
+DAWN_INSTANTIATE_TEST(ShaderFloat16Tests, VulkanBackend());
\ No newline at end of file