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>
This commit is contained in:
Xinghua Cao 2020-05-27 02:49:08 +00:00 committed by Commit Bot service account
parent 3e332cd475
commit bdc05c3d5f
11 changed files with 197 additions and 2 deletions

View File

@ -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": {

View File

@ -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

View File

@ -25,6 +25,7 @@ namespace dawn_native {
enum class Extension {
TextureCompressionBC,
ShaderFloat16,
EnumCount,
InvalidEnum = EnumCount,

View File

@ -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<DeviceBase*> Adapter::CreateDeviceImpl(const DeviceDescriptor* descriptor) {

View File

@ -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.

View File

@ -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<VulkanGlobalInfo> GatherGlobalInfo(const Backend& backend) {
VulkanGlobalInfo info = {};
@ -221,6 +223,7 @@ namespace dawn_native { namespace vulkan {
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 @@ 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);

View File

@ -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 {

View File

@ -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",

View File

@ -1144,6 +1144,7 @@ namespace detail {
}
template class ExpectEq<uint8_t>;
template class ExpectEq<uint16_t>;
template class ExpectEq<uint32_t>;
template class ExpectEq<RGBA8>;
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
// 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 @@ namespace detail {
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>;

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