From bdc05c3d5fef780382c3fd5e5ebcb14bd929819a Mon Sep 17 00:00:00 2001 From: Xinghua Cao Date: Wed, 27 May 2020 02:49:08 +0000 Subject: [PATCH] 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 Reviewed-by: Corentin Wallez Reviewed-by: Jiawei Shao Reviewed-by: Kai Ninomiya Reviewed-by: Austin Eng --- dawn.json | 3 +- src/dawn_native/Extensions.cpp | 7 +- src/dawn_native/Extensions.h | 1 + src/dawn_native/vulkan/AdapterVk.cpp | 7 ++ src/dawn_native/vulkan/DeviceVk.cpp | 19 ++++ src/dawn_native/vulkan/VulkanInfo.cpp | 34 +++++++ src/dawn_native/vulkan/VulkanInfo.h | 6 ++ src/tests/BUILD.gn | 1 + src/tests/DawnTest.cpp | 1 + src/tests/DawnTest.h | 9 ++ src/tests/end2end/ShaderFloat16Tests.cpp | 111 +++++++++++++++++++++++ 11 files changed, 197 insertions(+), 2 deletions(-) create mode 100644 src/tests/end2end/ShaderFloat16Tests.cpp diff --git a/dawn.json b/dawn.json index 35837701c0..dc39bb5d2f 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 a2b5a9dff1..de6214f9a5 100644 --- a/src/dawn_native/Extensions.cpp +++ b/src/dawn_native/Extensions.cpp @@ -34,7 +34,12 @@ namespace dawn_native { {{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 6e6d82d9f4..9dbdf77322 100644 --- a/src/dawn_native/Extensions.h +++ b/src/dawn_native/Extensions.h @@ -25,6 +25,7 @@ namespace dawn_native { 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 9cbe82d010..86041dfe44 100644 --- a/src/dawn_native/vulkan/AdapterVk.cpp +++ b/src/dawn_native/vulkan/AdapterVk.cpp @@ -73,6 +73,13 @@ namespace dawn_native { namespace vulkan { 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 Adapter::CreateDeviceImpl(const DeviceDescriptor* descriptor) { diff --git a/src/dawn_native/vulkan/DeviceVk.cpp b/src/dawn_native/vulkan/DeviceVk.cpp index 9edefcb361..a4a81a401e 100644 --- a/src/dawn_native/vulkan/DeviceVk.cpp +++ b/src/dawn_native/vulkan/DeviceVk.cpp @@ -333,6 +333,25 @@ namespace dawn_native { namespace vulkan { 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 2a3226245b..b2d99289af 100644 --- a/src/dawn_native/vulkan/VulkanInfo.cpp +++ b/src/dawn_native/vulkan/VulkanInfo.cpp @@ -79,6 +79,8 @@ namespace dawn_native { namespace vulkan { 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 GatherGlobalInfo(const Backend& backend) { VulkanGlobalInfo info = {}; @@ -221,6 +223,7 @@ namespace dawn_native { namespace vulkan { ResultOrError 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 @@ namespace dawn_native { namespace vulkan { 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 @@ namespace dawn_native { namespace vulkan { 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 354d9b38c9..81ef0549da 100644 --- a/src/dawn_native/vulkan/VulkanInfo.h +++ b/src/dawn_native/vulkan/VulkanInfo.h @@ -52,6 +52,8 @@ namespace dawn_native { namespace vulkan { 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 @@ namespace dawn_native { namespace vulkan { // 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 @@ namespace dawn_native { namespace vulkan { 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 6f3799c153..9f6ddc1f8a 100644 --- a/src/tests/BUILD.gn +++ b/src/tests/BUILD.gn @@ -287,6 +287,7 @@ source_set("dawn_end2end_tests_sources") { "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 21ddcaa5e6..09848de150 100644 --- a/src/tests/DawnTest.cpp +++ b/src/tests/DawnTest.cpp @@ -1144,6 +1144,7 @@ namespace detail { } template class ExpectEq; + template class ExpectEq; template class ExpectEq; template class ExpectEq; template class ExpectEq; diff --git a/src/tests/DawnTest.h b/src/tests/DawnTest.h index f80197fab3..3fd3f20122 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(expected)) + +#define EXPECT_BUFFER_U16_RANGE_EQ(expected, buffer, offset, count) \ + AddBufferExpectation(__FILE__, __LINE__, buffer, offset, sizeof(uint16_t) * count, \ + new ::detail::ExpectEq(expected, count)) + #define EXPECT_BUFFER_U32_EQ(expected, buffer, offset) \ AddBufferExpectation(__FILE__, __LINE__, buffer, offset, sizeof(uint32_t), \ new ::detail::ExpectEq(expected)) @@ -418,6 +426,7 @@ namespace detail { std::vector mExpected; }; extern template class ExpectEq; + extern template class ExpectEq; extern template class ExpectEq; extern template class ExpectEq; extern template class ExpectEq; diff --git a/src/tests/end2end/ShaderFloat16Tests.cpp b/src/tests/end2end/ShaderFloat16Tests.cpp new file mode 100644 index 0000000000..73bd0bbce0 --- /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 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