Reland "Check FP16 support on vulkan backend"

This reverts commit 0357eed7de
and reland commit bdc05c3d5f.

The Vulkan-Loader has a bug where if the instance is created
with Vulkan 1.1 and not the promoted extensions, it will skip
emulation and if the ICD doesn't support Vulkan 1.1 nor the
extensions. Enable the promoted extensions, even when creating
a Vulkan 1.1 instance.

Original change's description:
> 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>

Bug: chromium:1087896, dawn:426
Change-Id: I2c4465fb2fe957966b44d3e5840112219481c639
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/22781
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Xinghua Cao 2020-06-08 12:18:21 +00:00 committed by Commit Bot service account
parent e472c459b9
commit db8f804bc3
12 changed files with 217 additions and 13 deletions

View File

@ -651,6 +651,7 @@
"extensible": false, "extensible": false,
"members": [ "members": [
{"name": "texture compression BC", "type": "bool", "default": "false"}, {"name": "texture compression BC", "type": "bool", "default": "false"},
{"name": "shader float16", "type": "bool", "default": "false"},
{"name": "pipeline statistics query", "type": "bool", "default": "false"}, {"name": "pipeline statistics query", "type": "bool", "default": "false"},
{"name": "timestamp query", "type": "bool", "default": "false"} {"name": "timestamp query", "type": "bool", "default": "false"}
] ]

View File

@ -35,6 +35,11 @@ namespace dawn_native {
{"texture_compression_bc", "Support Block Compressed (BC) texture formats", {"texture_compression_bc", "Support Block Compressed (BC) texture formats",
"https://bugs.chromium.org/p/dawn/issues/detail?id=42"}, "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},
{Extension::PipelineStatisticsQuery, {Extension::PipelineStatisticsQuery,
{"pipeline_statistics_query", "Support Pipeline Statistics Query", {"pipeline_statistics_query", "Support Pipeline Statistics Query",
"https://bugs.chromium.org/p/dawn/issues/detail?id=434"}, "https://bugs.chromium.org/p/dawn/issues/detail?id=434"},

View File

@ -25,6 +25,7 @@ namespace dawn_native {
enum class Extension { enum class Extension {
TextureCompressionBC, TextureCompressionBC,
ShaderFloat16,
PipelineStatisticsQuery, PipelineStatisticsQuery,
TimestampQuery, TimestampQuery,

View File

@ -74,6 +74,13 @@ namespace dawn_native { namespace vulkan {
mSupportedExtensions.EnableExtension(Extension::TextureCompressionBC); mSupportedExtensions.EnableExtension(Extension::TextureCompressionBC);
} }
if (mDeviceInfo.shaderFloat16Int8 &&
mDeviceInfo.shaderFloat16Int8Features.shaderFloat16 == VK_TRUE &&
mDeviceInfo._16BitStorage &&
mDeviceInfo._16BitStorageFeatures.uniformAndStorageBuffer16BitAccess == VK_TRUE) {
mSupportedExtensions.EnableExtension(Extension::ShaderFloat16);
}
if (mDeviceInfo.features.pipelineStatisticsQuery == VK_TRUE) { if (mDeviceInfo.features.pipelineStatisticsQuery == VK_TRUE) {
mSupportedExtensions.EnableExtension(Extension::PipelineStatisticsQuery); mSupportedExtensions.EnableExtension(Extension::PipelineStatisticsQuery);
} }

View File

@ -253,7 +253,17 @@ namespace dawn_native { namespace vulkan {
usedKnobs.getPhysicalDeviceProperties2 = true; usedKnobs.getPhysicalDeviceProperties2 = true;
usedKnobs.externalMemoryCapabilities = true; usedKnobs.externalMemoryCapabilities = true;
usedKnobs.externalSemaphoreCapabilities = true; usedKnobs.externalSemaphoreCapabilities = true;
} else { }
// The Vulkan-Loader has emulation of VkPhysicalDevices functions such as
// vkGetPhysicalDeviceProperties2 when the ICD doesn't support the extension. However the
// loader has a bug where if the instance is created with Vulkan 1.1 and not the promoted
// extensions, it will skip emulation and if the ICD doesn't support Vulkan 1.1 nor the
// extensions, we will crash on nullptr function pointer when the loader tries to call the
// ICD's vkGetPhysicalDeviceProperties2. See
// https://github.com/KhronosGroup/Vulkan-Loader/issues/412. We work around this by
// specifying we want to enable the promoted extensions, even when we create a Vulkan 1.1
// instance.
if (mGlobalInfo.externalMemoryCapabilities) { if (mGlobalInfo.externalMemoryCapabilities) {
extensionsToRequest.push_back(kExtensionNameKhrExternalMemoryCapabilities); extensionsToRequest.push_back(kExtensionNameKhrExternalMemoryCapabilities);
usedKnobs.externalMemoryCapabilities = true; usedKnobs.externalMemoryCapabilities = true;
@ -266,7 +276,6 @@ namespace dawn_native { namespace vulkan {
extensionsToRequest.push_back(kExtensionNameKhrGetPhysicalDeviceProperties2); extensionsToRequest.push_back(kExtensionNameKhrGetPhysicalDeviceProperties2);
usedKnobs.getPhysicalDeviceProperties2 = true; usedKnobs.getPhysicalDeviceProperties2 = true;
} }
}
VkApplicationInfo appInfo; VkApplicationInfo appInfo;
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;

View File

@ -328,6 +328,25 @@ namespace dawn_native { namespace vulkan {
usedKnobs.features.textureCompressionBC = VK_TRUE; 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 // Find a universal queue family
{ {
// Note that GRAPHICS and COMPUTE imply TRANSFER so we don't need to check for it. // Note that GRAPHICS and COMPUTE imply TRANSFER so we don't need to check for it.

View File

@ -79,6 +79,8 @@ namespace dawn_native { namespace vulkan {
const char kExtensionNameKhrXlibSurface[] = "VK_KHR_xlib_surface"; const char kExtensionNameKhrXlibSurface[] = "VK_KHR_xlib_surface";
const char kExtensionNameFuchsiaImagePipeSurface[] = "VK_FUCHSIA_imagepipe_surface"; const char kExtensionNameFuchsiaImagePipeSurface[] = "VK_FUCHSIA_imagepipe_surface";
const char kExtensionNameKhrMaintenance1[] = "VK_KHR_maintenance1"; 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) { ResultOrError<VulkanGlobalInfo> GatherGlobalInfo(const Backend& backend) {
VulkanGlobalInfo info = {}; VulkanGlobalInfo info = {};
@ -221,6 +223,7 @@ namespace dawn_native { namespace vulkan {
ResultOrError<VulkanDeviceInfo> GatherDeviceInfo(const Adapter& adapter) { ResultOrError<VulkanDeviceInfo> GatherDeviceInfo(const Adapter& adapter) {
VulkanDeviceInfo info = {}; VulkanDeviceInfo info = {};
VkPhysicalDevice physicalDevice = adapter.GetPhysicalDevice(); VkPhysicalDevice physicalDevice = adapter.GetPhysicalDevice();
const VulkanGlobalInfo& globalInfo = adapter.GetBackend()->GetGlobalInfo();
const VulkanFunctions& vkFunctions = adapter.GetBackend()->GetFunctions(); const VulkanFunctions& vkFunctions = adapter.GetBackend()->GetFunctions();
// Gather general info about the device // Gather general info about the device
@ -311,6 +314,23 @@ namespace dawn_native { namespace vulkan {
if (IsExtensionName(extension, kExtensionNameKhrMaintenance1)) { if (IsExtensionName(extension, kExtensionNameKhrMaintenance1)) {
info.maintenance1 = true; 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; 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 // TODO(cwallez@chromium.org): gather info about formats
return std::move(info); return std::move(info);

View File

@ -52,6 +52,8 @@ namespace dawn_native { namespace vulkan {
extern const char kExtensionNameKhrXlibSurface[]; extern const char kExtensionNameKhrXlibSurface[];
extern const char kExtensionNameFuchsiaImagePipeSurface[]; extern const char kExtensionNameFuchsiaImagePipeSurface[];
extern const char kExtensionNameKhrMaintenance1[]; extern const char kExtensionNameKhrMaintenance1[];
extern const char kExtensionNameKhrShaderFloat16Int8[];
extern const char kExtensionNameKhr16BitStorage[];
// Global information - gathered before the instance is created // Global information - gathered before the instance is created
struct VulkanGlobalKnobs { struct VulkanGlobalKnobs {
@ -85,6 +87,8 @@ namespace dawn_native { namespace vulkan {
// Device information - gathered before the device is created. // Device information - gathered before the device is created.
struct VulkanDeviceKnobs { struct VulkanDeviceKnobs {
VkPhysicalDeviceFeatures features; VkPhysicalDeviceFeatures features;
VkPhysicalDeviceShaderFloat16Int8FeaturesKHR shaderFloat16Int8Features;
VkPhysicalDevice16BitStorageFeaturesKHR _16BitStorageFeatures;
// Extensions, promoted extensions are set to true if their core version is supported. // Extensions, promoted extensions are set to true if their core version is supported.
bool debugMarker = false; bool debugMarker = false;
@ -98,6 +102,8 @@ namespace dawn_native { namespace vulkan {
bool externalSemaphoreZirconHandle = false; bool externalSemaphoreZirconHandle = false;
bool swapchain = false; bool swapchain = false;
bool maintenance1 = false; bool maintenance1 = false;
bool shaderFloat16Int8 = false;
bool _16BitStorage = false;
}; };
struct VulkanDeviceInfo : VulkanDeviceKnobs { struct VulkanDeviceInfo : VulkanDeviceKnobs {

View File

@ -287,6 +287,7 @@ source_set("dawn_end2end_tests_sources") {
"end2end/RenderPassTests.cpp", "end2end/RenderPassTests.cpp",
"end2end/SamplerTests.cpp", "end2end/SamplerTests.cpp",
"end2end/ScissorTests.cpp", "end2end/ScissorTests.cpp",
"end2end/ShaderFloat16Tests.cpp",
"end2end/StorageTextureTests.cpp", "end2end/StorageTextureTests.cpp",
"end2end/SubresourceOutputAttachmentTests.cpp", "end2end/SubresourceOutputAttachmentTests.cpp",
"end2end/TextureFormatTests.cpp", "end2end/TextureFormatTests.cpp",

View File

@ -1144,6 +1144,7 @@ namespace detail {
} }
template class ExpectEq<uint8_t>; template class ExpectEq<uint8_t>;
template class ExpectEq<uint16_t>;
template class ExpectEq<uint32_t>; template class ExpectEq<uint32_t>;
template class ExpectEq<RGBA8>; template class ExpectEq<RGBA8>;
template class ExpectEq<float>; template class ExpectEq<float>;

View File

@ -30,6 +30,14 @@
// until the end of the test. Also expectations use a copy to a MapRead buffer to get the data // 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 // so resources should have the CopySrc allowed usage bit if you want to add expectations on
// them. // 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) \ #define EXPECT_BUFFER_U32_EQ(expected, buffer, offset) \
AddBufferExpectation(__FILE__, __LINE__, buffer, offset, sizeof(uint32_t), \ AddBufferExpectation(__FILE__, __LINE__, buffer, offset, sizeof(uint32_t), \
new ::detail::ExpectEq<uint32_t>(expected)) new ::detail::ExpectEq<uint32_t>(expected))
@ -418,6 +426,7 @@ namespace detail {
std::vector<T> mExpected; std::vector<T> mExpected;
}; };
extern template class ExpectEq<uint8_t>; extern template class ExpectEq<uint8_t>;
extern template class ExpectEq<int16_t>;
extern template class ExpectEq<uint32_t>; extern template class ExpectEq<uint32_t>;
extern template class ExpectEq<RGBA8>; extern template class ExpectEq<RGBA8>;
extern template class ExpectEq<float>; extern template class ExpectEq<float>;

View File

@ -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, Basic16BitFloatFeaturesTest) {
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());