blob: 7e4d673ec8b330b4301bf9425f25abc6453e517c [file] [log] [blame]
// Copyright 2019 The Dawn & Tint Authors
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "dawn/native/vulkan/PhysicalDeviceVk.h"
#include <algorithm>
#include <string>
#include <utility>
#include "dawn/common/GPUInfo.h"
#include "dawn/native/ChainUtils.h"
#include "dawn/native/Instance.h"
#include "dawn/native/Limits.h"
#include "dawn/native/vulkan/BackendVk.h"
#include "dawn/native/vulkan/DeviceVk.h"
#include "dawn/native/vulkan/SwapChainVk.h"
#include "dawn/native/vulkan/TextureVk.h"
#include "dawn/native/vulkan/UtilsVulkan.h"
#include "dawn/native/vulkan/VulkanError.h"
#include "dawn/platform/DawnPlatform.h"
#if DAWN_PLATFORM_IS(ANDROID)
#include "dawn/native/AHBFunctions.h"
#endif // DAWN_PLATFORM_IS(ANDROID)
namespace dawn::native::vulkan {
namespace {
gpu_info::DriverVersion DecodeVulkanDriverVersion(uint32_t vendorID, uint32_t versionRaw) {
gpu_info::DriverVersion driverVersion;
switch (vendorID) {
case gpu_info::kVendorID_Nvidia:
driverVersion = {static_cast<uint16_t>((versionRaw >> 22) & 0x3FF),
static_cast<uint16_t>((versionRaw >> 14) & 0x0FF),
static_cast<uint16_t>((versionRaw >> 6) & 0x0FF),
static_cast<uint16_t>(versionRaw & 0x003F)};
break;
case gpu_info::kVendorID_Intel:
#if DAWN_PLATFORM_IS(WINDOWS)
// Windows Vulkan driver releases together with D3D driver, so they share the same
// version. But only CCC.DDDD is encoded in 32-bit driverVersion.
driverVersion = {static_cast<uint16_t>(versionRaw >> 14),
static_cast<uint16_t>(versionRaw & 0x3FFF)};
break;
#endif
default:
// Use Vulkan driver conversions for other vendors
driverVersion = {static_cast<uint16_t>(versionRaw >> 22),
static_cast<uint16_t>((versionRaw >> 12) & 0x3FF),
static_cast<uint16_t>(versionRaw & 0xFFF)};
break;
}
return driverVersion;
}
} // anonymous namespace
PhysicalDevice::PhysicalDevice(VulkanInstance* vulkanInstance, VkPhysicalDevice physicalDevice)
: PhysicalDeviceBase(wgpu::BackendType::Vulkan),
mVkPhysicalDevice(physicalDevice),
mVulkanInstance(vulkanInstance) {}
PhysicalDevice::~PhysicalDevice() = default;
const VulkanDeviceInfo& PhysicalDevice::GetDeviceInfo() const {
return mDeviceInfo;
}
VkPhysicalDevice PhysicalDevice::GetVkPhysicalDevice() const {
return mVkPhysicalDevice;
}
VulkanInstance* PhysicalDevice::GetVulkanInstance() const {
return mVulkanInstance.Get();
}
bool PhysicalDevice::IsDepthStencilFormatSupported(VkFormat format) const {
DAWN_ASSERT(format == VK_FORMAT_D16_UNORM_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT ||
format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_S8_UINT);
VkFormatProperties properties;
mVulkanInstance->GetFunctions().GetPhysicalDeviceFormatProperties(mVkPhysicalDevice, format,
&properties);
return properties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
}
MaybeError PhysicalDevice::InitializeImpl() {
DAWN_TRY_ASSIGN(mDeviceInfo, GatherDeviceInfo(*this));
mDriverVersion = DecodeVulkanDriverVersion(mDeviceInfo.properties.vendorID,
mDeviceInfo.properties.driverVersion);
const std::string driverVersionStr = mDriverVersion.ToString();
#if DAWN_PLATFORM_IS(WINDOWS)
// Disable Vulkan adapter on Windows Intel driver < 30.0.101.2111 due to flaky
// issues.
const gpu_info::DriverVersion kDriverVersion({30, 0, 101, 2111});
if (gpu_info::IsIntel(mDeviceInfo.properties.vendorID) &&
gpu_info::CompareWindowsDriverVersion(mDeviceInfo.properties.vendorID, mDriverVersion,
kDriverVersion) == -1) {
return DAWN_FORMAT_INTERNAL_ERROR(
"Disable Intel Vulkan adapter on Windows driver version %s. See "
"https://crbug.com/1338622.",
driverVersionStr);
}
#endif
if (mDeviceInfo.HasExt(DeviceExt::DriverProperties)) {
mDriverDescription = mDeviceInfo.driverProperties.driverName;
if (mDeviceInfo.driverProperties.driverInfo[0] != '\0') {
mDriverDescription += std::string(": ") + mDeviceInfo.driverProperties.driverInfo;
}
// There may be no driver version in driverInfo.
if (mDriverDescription.find(driverVersionStr) == std::string::npos) {
mDriverDescription += std::string(" ") + driverVersionStr;
}
} else {
mDriverDescription = std::string("Vulkan driver version ") + driverVersionStr;
}
mDeviceId = mDeviceInfo.properties.deviceID;
mVendorId = mDeviceInfo.properties.vendorID;
mName = mDeviceInfo.properties.deviceName;
switch (mDeviceInfo.properties.deviceType) {
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
mAdapterType = wgpu::AdapterType::IntegratedGPU;
break;
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
mAdapterType = wgpu::AdapterType::DiscreteGPU;
break;
case VK_PHYSICAL_DEVICE_TYPE_CPU:
mAdapterType = wgpu::AdapterType::CPU;
break;
default:
mAdapterType = wgpu::AdapterType::Unknown;
break;
}
// Check for essential Vulkan extensions and features
// Needed for viewport Y-flip.
if (!mDeviceInfo.HasExt(DeviceExt::Maintenance1)) {
return DAWN_INTERNAL_ERROR("Vulkan 1.1 or Vulkan 1.0 with KHR_Maintenance1 required.");
}
// Needed for separate depth/stencilReadOnly
if (!mDeviceInfo.HasExt(DeviceExt::Maintenance2)) {
return DAWN_INTERNAL_ERROR("Vulkan 1.1 or Vulkan 1.0 with KHR_Maintenance2 required.");
}
// Needed for security
if (!mDeviceInfo.features.robustBufferAccess) {
return DAWN_INTERNAL_ERROR("Vulkan robustBufferAccess feature required.");
}
if (!mDeviceInfo.features.textureCompressionBC &&
!(mDeviceInfo.features.textureCompressionETC2 &&
mDeviceInfo.features.textureCompressionASTC_LDR)) {
return DAWN_INTERNAL_ERROR(
"Vulkan textureCompressionBC feature required or both textureCompressionETC2 and "
"textureCompressionASTC required.");
}
// Needed for the respective WebGPU features.
if (!mDeviceInfo.features.depthBiasClamp) {
return DAWN_INTERNAL_ERROR("Vulkan depthBiasClamp feature required.");
}
if (!mDeviceInfo.features.fragmentStoresAndAtomics) {
return DAWN_INTERNAL_ERROR("Vulkan fragmentStoresAndAtomics feature required.");
}
if (!mDeviceInfo.features.fullDrawIndexUint32) {
return DAWN_INTERNAL_ERROR("Vulkan fullDrawIndexUint32 feature required.");
}
if (!mDeviceInfo.features.imageCubeArray) {
return DAWN_INTERNAL_ERROR("Vulkan imageCubeArray feature required.");
}
if (!mDeviceInfo.features.independentBlend) {
return DAWN_INTERNAL_ERROR("Vulkan independentBlend feature required.");
}
if (!mDeviceInfo.features.sampleRateShading) {
return DAWN_INTERNAL_ERROR("Vulkan sampleRateShading feature required.");
}
return {};
}
void PhysicalDevice::InitializeSupportedFeaturesImpl() {
EnableFeature(Feature::AdapterPropertiesMemoryHeaps);
EnableFeature(Feature::StaticSamplers);
// Initialize supported extensions
if (mDeviceInfo.features.textureCompressionBC == VK_TRUE) {
EnableFeature(Feature::TextureCompressionBC);
}
if (mDeviceInfo.features.textureCompressionETC2 == VK_TRUE) {
EnableFeature(Feature::TextureCompressionETC2);
}
if (mDeviceInfo.features.textureCompressionASTC_LDR == VK_TRUE) {
EnableFeature(Feature::TextureCompressionASTC);
}
if (mDeviceInfo.properties.limits.timestampComputeAndGraphics == VK_TRUE) {
EnableFeature(Feature::TimestampQuery);
EnableFeature(Feature::ChromiumExperimentalTimestampQueryInsidePasses);
}
if (IsDepthStencilFormatSupported(VK_FORMAT_D32_SFLOAT_S8_UINT)) {
EnableFeature(Feature::Depth32FloatStencil8);
}
if (mDeviceInfo.features.drawIndirectFirstInstance == VK_TRUE) {
EnableFeature(Feature::IndirectFirstInstance);
}
if (mDeviceInfo.features.dualSrcBlend == VK_TRUE) {
EnableFeature(Feature::DualSourceBlending);
}
if (mDeviceInfo.features.shaderStorageImageExtendedFormats == VK_TRUE) {
EnableFeature(Feature::R8UnormStorage);
}
if (mDeviceInfo.features.shaderClipDistance == VK_TRUE) {
EnableFeature(Feature::ClipDistances);
}
bool shaderF16Enabled = false;
if (mDeviceInfo.HasExt(DeviceExt::ShaderFloat16Int8) &&
mDeviceInfo.HasExt(DeviceExt::_16BitStorage) &&
mDeviceInfo.shaderFloat16Int8Features.shaderFloat16 == VK_TRUE &&
mDeviceInfo._16BitStorageFeatures.storageBuffer16BitAccess == VK_TRUE &&
mDeviceInfo._16BitStorageFeatures.uniformAndStorageBuffer16BitAccess == VK_TRUE) {
// TODO(crbug.com/tint/2164): Investigate crashes in f16 CTS tests to enable on NVIDIA.
if (!gpu_info::IsNvidia(GetVendorId())) {
EnableFeature(Feature::ShaderF16);
shaderF16Enabled = true;
}
}
if (mDeviceInfo.HasExt(DeviceExt::DrawIndirectCount) &&
mDeviceInfo.features.multiDrawIndirect == VK_TRUE) {
EnableFeature(Feature::MultiDrawIndirect);
}
// unclippedDepth=true translates to depthClamp=true, which implicitly disables clipping.
if (mDeviceInfo.features.depthClamp == VK_TRUE) {
EnableFeature(Feature::DepthClipControl);
}
if (mDeviceInfo.HasExt(DeviceExt::SamplerYCbCrConversion) &&
mDeviceInfo.HasExt(DeviceExt::ExternalMemoryAndroidHardwareBuffer) &&
mDeviceInfo.samplerYCbCrConversionFeatures.samplerYcbcrConversion == VK_TRUE) {
EnableFeature(Feature::YCbCrVulkanSamplers);
}
VkFormatProperties rg11b10Properties;
mVulkanInstance->GetFunctions().GetPhysicalDeviceFormatProperties(
mVkPhysicalDevice, VK_FORMAT_B10G11R11_UFLOAT_PACK32, &rg11b10Properties);
if (IsSubset(static_cast<VkFormatFeatureFlags>(VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT |
VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT),
rg11b10Properties.optimalTilingFeatures)) {
EnableFeature(Feature::RG11B10UfloatRenderable);
}
VkFormatProperties bgra8unormProperties;
mVulkanInstance->GetFunctions().GetPhysicalDeviceFormatProperties(
mVkPhysicalDevice, VK_FORMAT_B8G8R8A8_UNORM, &bgra8unormProperties);
if (bgra8unormProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) {
EnableFeature(Feature::BGRA8UnormStorage);
}
bool unorm16TextureFormatsSupported = true;
for (const auto& unorm16Format :
{VK_FORMAT_R16_UNORM, VK_FORMAT_R16G16_UNORM, VK_FORMAT_R16G16B16A16_UNORM}) {
VkFormatProperties unorm16Properties;
mVulkanInstance->GetFunctions().GetPhysicalDeviceFormatProperties(
mVkPhysicalDevice, unorm16Format, &unorm16Properties);
unorm16TextureFormatsSupported &= IsSubset(
static_cast<VkFormatFeatureFlags>(VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT |
VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT |
VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT),
unorm16Properties.optimalTilingFeatures);
}
if (unorm16TextureFormatsSupported) {
EnableFeature(Feature::Unorm16TextureFormats);
}
bool snorm16TextureFormatsSupported = true;
for (const auto& snorm16Format :
{VK_FORMAT_R16_SNORM, VK_FORMAT_R16G16_SNORM, VK_FORMAT_R16G16B16A16_SNORM}) {
VkFormatProperties snorm16Properties;
mVulkanInstance->GetFunctions().GetPhysicalDeviceFormatProperties(
mVkPhysicalDevice, snorm16Format, &snorm16Properties);
snorm16TextureFormatsSupported &= IsSubset(
static_cast<VkFormatFeatureFlags>(VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT |
VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT |
VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT),
snorm16Properties.optimalTilingFeatures);
}
if (snorm16TextureFormatsSupported) {
EnableFeature(Feature::Snorm16TextureFormats);
}
if (unorm16TextureFormatsSupported && snorm16TextureFormatsSupported) {
EnableFeature(Feature::Norm16TextureFormats);
}
// 32 bit float channel formats.
VkFormatProperties r32Properties;
VkFormatProperties rg32Properties;
VkFormatProperties rgba32Properties;
mVulkanInstance->GetFunctions().GetPhysicalDeviceFormatProperties(
mVkPhysicalDevice, VK_FORMAT_R32_SFLOAT, &r32Properties);
mVulkanInstance->GetFunctions().GetPhysicalDeviceFormatProperties(
mVkPhysicalDevice, VK_FORMAT_R32G32_SFLOAT, &rg32Properties);
mVulkanInstance->GetFunctions().GetPhysicalDeviceFormatProperties(
mVkPhysicalDevice, VK_FORMAT_R32G32B32A32_SFLOAT, &rgba32Properties);
if ((r32Properties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT) &&
(rg32Properties.optimalTilingFeatures &
VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT) &&
(rgba32Properties.optimalTilingFeatures &
VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) {
EnableFeature(Feature::Float32Filterable);
}
// Multiplanar formats.
constexpr VkFormat multiplanarFormats[] = {
VK_FORMAT_G8_B8R8_2PLANE_420_UNORM,
};
bool allMultiplanarFormatsSupported = true;
for (const auto multiplanarFormat : multiplanarFormats) {
VkFormatProperties multiplanarProps;
mVulkanInstance->GetFunctions().GetPhysicalDeviceFormatProperties(
mVkPhysicalDevice, multiplanarFormat, &multiplanarProps);
if (!IsSubset(static_cast<VkFormatFeatureFlagBits>(
VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT |
VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT |
VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT),
multiplanarProps.optimalTilingFeatures)) {
allMultiplanarFormatsSupported = false;
}
}
if (allMultiplanarFormatsSupported) {
EnableFeature(Feature::DawnMultiPlanarFormats);
EnableFeature(Feature::MultiPlanarFormatExtendedUsages);
}
EnableFeature(Feature::TransientAttachments);
EnableFeature(Feature::AdapterPropertiesVk);
EnableFeature(Feature::DawnLoadResolveTexture);
// TODO(349125474): Remove deprecated ChromiumExperimentalSubgroups.
// Enable ChromiumExperimentalSubgroups feature if:
// 1. Vulkan API version is 1.1 or later, and
// 2. subgroupSupportedStages includes compute stage bit, and
// 3. subgroupSupportedOperations includes basic and ballot bits, and
// 4. VK_EXT_subgroup_size_control extension is valid, and both subgroupSizeControl
// and computeFullSubgroups is TRUE in VkPhysicalDeviceSubgroupSizeControlFeaturesEXT.
// Notes that these requirement doesn't ensure all subgroups features are supported by the
// Vulkan backend. For example, currently ChromiumExperimentalSubgroups feature allows using
// subgroups functions with f16 types in WGSL, but doesn't ensure that backend supports it.
if ((mDeviceInfo.properties.apiVersion >= VK_API_VERSION_1_1) &&
(mDeviceInfo.subgroupProperties.supportedStages & VK_SHADER_STAGE_COMPUTE_BIT) &&
(mDeviceInfo.subgroupProperties.supportedOperations & VK_SUBGROUP_FEATURE_BASIC_BIT) &&
(mDeviceInfo.subgroupProperties.supportedOperations & VK_SUBGROUP_FEATURE_BALLOT_BIT) &&
(mDeviceInfo.HasExt(DeviceExt::SubgroupSizeControl)) &&
(mDeviceInfo.subgroupSizeControlFeatures.subgroupSizeControl == VK_TRUE) &&
(mDeviceInfo.subgroupSizeControlFeatures.computeFullSubgroups == VK_TRUE)) {
EnableFeature(Feature::ChromiumExperimentalSubgroups);
}
// Enable Subgroups feature if:
// 1. Vulkan API version is 1.1 or later, and
// 2. subgroupSupportedStages includes compute and fragment stage bit, and
// 3. subgroupSupportedOperations includes vote, ballot, shuffle, shuffle relative, arithmetic,
// and quad bits, and
// 4. VK_EXT_subgroup_size_control extension is valid, and both subgroupSizeControl
// and computeFullSubgroups is TRUE in VkPhysicalDeviceSubgroupSizeControlFeaturesEXT.
if ((mDeviceInfo.properties.apiVersion >= VK_API_VERSION_1_1) &&
(mDeviceInfo.subgroupProperties.supportedStages & VK_SHADER_STAGE_COMPUTE_BIT) &&
(mDeviceInfo.subgroupProperties.supportedStages & VK_SHADER_STAGE_FRAGMENT_BIT) &&
(mDeviceInfo.subgroupProperties.supportedOperations & VK_SUBGROUP_FEATURE_BASIC_BIT) &&
(mDeviceInfo.subgroupProperties.supportedOperations & VK_SUBGROUP_FEATURE_BALLOT_BIT) &&
(mDeviceInfo.subgroupProperties.supportedOperations & VK_SUBGROUP_FEATURE_SHUFFLE_BIT) &&
(mDeviceInfo.subgroupProperties.supportedOperations &
VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT) &&
(mDeviceInfo.subgroupProperties.supportedOperations & VK_SUBGROUP_FEATURE_ARITHMETIC_BIT) &&
(mDeviceInfo.subgroupProperties.supportedOperations & VK_SUBGROUP_FEATURE_QUAD_BIT) &&
(mDeviceInfo.HasExt(DeviceExt::SubgroupSizeControl)) &&
(mDeviceInfo.subgroupSizeControlFeatures.subgroupSizeControl == VK_TRUE) &&
(mDeviceInfo.subgroupSizeControlFeatures.computeFullSubgroups == VK_TRUE)) {
EnableFeature(Feature::Subgroups);
// Enable SubgroupsF16 feature if:
// 1. Subgroups feature is enabled, and
// 2. ShaderF16 feature is enabled, and
// 3. shaderSubgroupExtendedTypes is TRUE in
// VkPhysicalDeviceShaderSubgroupExtendedTypesFeaturesKHR.
if (shaderF16Enabled &&
mDeviceInfo.shaderSubgroupExtendedTypes.shaderSubgroupExtendedTypes == VK_TRUE) {
EnableFeature(Feature::SubgroupsF16);
}
}
// Enable ChromiumExperimentalSubgroupUniformControlFlow if
// VK_KHR_shader_subgroup_uniform_control_flow is supported.
if (mDeviceInfo.HasExt(DeviceExt::ShaderSubgroupUniformControlFlow) &&
(mDeviceInfo.shaderSubgroupUniformControlFlowFeatures.shaderSubgroupUniformControlFlow ==
VK_TRUE)) {
EnableFeature(Feature::ChromiumExperimentalSubgroupUniformControlFlow);
}
if (mDeviceInfo.HasExt(DeviceExt::ExternalMemoryHost) &&
mDeviceInfo.externalMemoryHostProperties.minImportedHostPointerAlignment <= 4096) {
// TODO(crbug.com/dawn/2018): properly surface the limit.
// Linux nearly always exposes 4096.
// https://vulkan.gpuinfo.org/displayextensionproperty.php?platform=linux&extensionname=VK_EXT_external_memory_host&extensionproperty=minImportedHostPointerAlignment
EnableFeature(Feature::HostMappedPointer);
}
if (mDeviceInfo.HasExt(DeviceExt::ExternalMemoryDmaBuf) &&
mDeviceInfo.HasExt(DeviceExt::ImageDrmFormatModifier)) {
EnableFeature(Feature::SharedTextureMemoryDmaBuf);
}
if (mDeviceInfo.HasExt(DeviceExt::ExternalMemoryFD)) {
EnableFeature(Feature::SharedTextureMemoryOpaqueFD);
}
#if DAWN_PLATFORM_IS(ANDROID)
if (mDeviceInfo.HasExt(DeviceExt::ExternalMemoryAndroidHardwareBuffer)) {
if (GetOrLoadAHBFunctions()->IsValid()) {
EnableFeature(Feature::SharedTextureMemoryAHardwareBuffer);
}
}
#endif // DAWN_PLATFORM_IS(ANDROID)
if (CheckSemaphoreSupport(DeviceExt::ExternalSemaphoreZirconHandle,
VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_ZIRCON_EVENT_BIT_FUCHSIA)) {
EnableFeature(Feature::SharedFenceVkSemaphoreZirconHandle);
}
if (CheckSemaphoreSupport(DeviceExt::ExternalSemaphoreFD,
VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT_KHR)) {
EnableFeature(Feature::SharedFenceVkSemaphoreSyncFD);
}
if (CheckSemaphoreSupport(DeviceExt::ExternalSemaphoreFD,
VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR)) {
EnableFeature(Feature::SharedFenceVkSemaphoreOpaqueFD);
}
if (mDeviceInfo.HasExt(DeviceExt::ImageDrmFormatModifier)) {
EnableFeature(Feature::DrmFormatCapabilities);
}
}
MaybeError PhysicalDevice::InitializeSupportedLimitsImpl(CombinedLimits* limits) {
GetDefaultLimitsForSupportedFeatureLevel(&limits->v1);
CombinedLimits baseLimits = *limits;
const VkPhysicalDeviceLimits& vkLimits = mDeviceInfo.properties.limits;
#define CHECK_AND_SET_V1_LIMIT_IMPL(vulkanName, webgpuName, compareOp, msgSegment) \
do { \
if (vkLimits.vulkanName compareOp baseLimits.v1.webgpuName) { \
return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for " #webgpuName \
"." \
" VkPhysicalDeviceLimits::" #vulkanName \
" must be at " msgSegment " " + \
std::to_string(baseLimits.v1.webgpuName)); \
} \
limits->v1.webgpuName = vkLimits.vulkanName; \
} while (false)
#define CHECK_AND_SET_V1_MAX_LIMIT(vulkanName, webgpuName) \
CHECK_AND_SET_V1_LIMIT_IMPL(vulkanName, webgpuName, <, "least")
#define CHECK_AND_SET_V1_MIN_LIMIT(vulkanName, webgpuName) \
CHECK_AND_SET_V1_LIMIT_IMPL(vulkanName, webgpuName, >, "most")
CHECK_AND_SET_V1_MAX_LIMIT(maxImageDimension1D, maxTextureDimension1D);
CHECK_AND_SET_V1_MAX_LIMIT(maxImageDimension2D, maxTextureDimension2D);
CHECK_AND_SET_V1_MAX_LIMIT(maxImageDimensionCube, maxTextureDimension2D);
CHECK_AND_SET_V1_MAX_LIMIT(maxFramebufferWidth, maxTextureDimension2D);
CHECK_AND_SET_V1_MAX_LIMIT(maxFramebufferHeight, maxTextureDimension2D);
CHECK_AND_SET_V1_MAX_LIMIT(maxViewportDimensions[0], maxTextureDimension2D);
CHECK_AND_SET_V1_MAX_LIMIT(maxViewportDimensions[1], maxTextureDimension2D);
CHECK_AND_SET_V1_MAX_LIMIT(viewportBoundsRange[1], maxTextureDimension2D);
limits->v1.maxTextureDimension2D = std::min({
static_cast<uint32_t>(vkLimits.maxImageDimension2D),
static_cast<uint32_t>(vkLimits.maxImageDimensionCube),
static_cast<uint32_t>(vkLimits.maxFramebufferWidth),
static_cast<uint32_t>(vkLimits.maxFramebufferHeight),
static_cast<uint32_t>(vkLimits.maxViewportDimensions[0]),
static_cast<uint32_t>(vkLimits.maxViewportDimensions[1]),
static_cast<uint32_t>(vkLimits.viewportBoundsRange[1]),
});
CHECK_AND_SET_V1_MAX_LIMIT(maxImageDimension3D, maxTextureDimension3D);
CHECK_AND_SET_V1_MAX_LIMIT(maxImageArrayLayers, maxTextureArrayLayers);
CHECK_AND_SET_V1_MAX_LIMIT(maxBoundDescriptorSets, maxBindGroups);
CHECK_AND_SET_V1_MAX_LIMIT(maxDescriptorSetUniformBuffersDynamic,
maxDynamicUniformBuffersPerPipelineLayout);
CHECK_AND_SET_V1_MAX_LIMIT(maxDescriptorSetStorageBuffersDynamic,
maxDynamicStorageBuffersPerPipelineLayout);
CHECK_AND_SET_V1_MAX_LIMIT(maxPerStageDescriptorSampledImages,
maxSampledTexturesPerShaderStage);
CHECK_AND_SET_V1_MAX_LIMIT(maxPerStageDescriptorSamplers, maxSamplersPerShaderStage);
CHECK_AND_SET_V1_MAX_LIMIT(maxPerStageDescriptorStorageBuffers,
maxStorageBuffersPerShaderStage);
CHECK_AND_SET_V1_MAX_LIMIT(maxPerStageDescriptorStorageImages,
maxStorageTexturesPerShaderStage);
CHECK_AND_SET_V1_MAX_LIMIT(maxPerStageDescriptorUniformBuffers,
maxUniformBuffersPerShaderStage);
CHECK_AND_SET_V1_MAX_LIMIT(maxUniformBufferRange, maxUniformBufferBindingSize);
CHECK_AND_SET_V1_MAX_LIMIT(maxStorageBufferRange, maxStorageBufferBindingSize);
CHECK_AND_SET_V1_MAX_LIMIT(maxColorAttachments, maxColorAttachments);
// Validate against maxFragmentCombinedOutputResources, tightening the limits when necessary.
const uint32_t minFragmentCombinedOutputResources =
baseLimits.v1.maxStorageBuffersPerShaderStage +
baseLimits.v1.maxStorageTexturesPerShaderStage + baseLimits.v1.maxColorAttachments;
const uint64_t maxFragmentCombinedOutputResources =
limits->v1.maxStorageBuffersPerShaderStage + limits->v1.maxStorageTexturesPerShaderStage +
limits->v1.maxColorAttachments;
// Only re-adjust the limits when the limit makes sense w.r.t to the required WebGPU limits.
// Otherwise, we ignore the maxFragmentCombinedOutputResources since it is known to yield
// incorrect values on desktop drivers.
bool readjustFragmentCombinedOutputResources =
vkLimits.maxFragmentCombinedOutputResources > minFragmentCombinedOutputResources &&
uint64_t(vkLimits.maxFragmentCombinedOutputResources) < maxFragmentCombinedOutputResources;
if (readjustFragmentCombinedOutputResources) {
// Split extra resources across the three other limits instead of using the default values
// since it would overflow.
uint32_t extraResources =
vkLimits.maxFragmentCombinedOutputResources - minFragmentCombinedOutputResources;
limits->v1.maxColorAttachments = std::min(
baseLimits.v1.maxColorAttachments + (extraResources / 3), vkLimits.maxColorAttachments);
extraResources -= limits->v1.maxColorAttachments - baseLimits.v1.maxColorAttachments;
limits->v1.maxStorageTexturesPerShaderStage =
std::min(baseLimits.v1.maxStorageTexturesPerShaderStage + (extraResources / 2),
vkLimits.maxPerStageDescriptorStorageImages);
extraResources -= limits->v1.maxStorageTexturesPerShaderStage -
baseLimits.v1.maxStorageTexturesPerShaderStage;
limits->v1.maxStorageBuffersPerShaderStage =
std::min(baseLimits.v1.maxStorageBuffersPerShaderStage + extraResources,
vkLimits.maxPerStageDescriptorStorageBuffers);
}
CHECK_AND_SET_V1_MIN_LIMIT(minUniformBufferOffsetAlignment, minUniformBufferOffsetAlignment);
CHECK_AND_SET_V1_MIN_LIMIT(minStorageBufferOffsetAlignment, minStorageBufferOffsetAlignment);
CHECK_AND_SET_V1_MAX_LIMIT(maxVertexInputBindings, maxVertexBuffers);
CHECK_AND_SET_V1_MAX_LIMIT(maxVertexInputAttributes, maxVertexAttributes);
if (vkLimits.maxVertexInputBindingStride < baseLimits.v1.maxVertexBufferArrayStride ||
vkLimits.maxVertexInputAttributeOffset < baseLimits.v1.maxVertexBufferArrayStride - 1) {
return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxVertexBufferArrayStride");
}
// Note that some drivers have UINT32_MAX as maxVertexInputAttributeOffset so we do that +1 only
// after the std::min.
limits->v1.maxVertexBufferArrayStride =
std::min(vkLimits.maxVertexInputBindingStride - 1, vkLimits.maxVertexInputAttributeOffset) +
1;
// Reserve 4 components for the SPIR-V builtin `position`. WebGPU SPEC requires the minimum
// value of `maxInterStageShaderVariables` be 16. According to Vulkan SPEC, "the Location value
// specifies an interface slot comprised of a 32-bit four-component vector conveyed between
// stages". So on any WebGPU Vulkan backend `maxVertexOutputComponents` must be no less than
// 68 = (16 * 4 + 4).
if (vkLimits.maxVertexOutputComponents < baseLimits.v1.maxInterStageShaderVariables * 4 + 4 ||
vkLimits.maxFragmentInputComponents < baseLimits.v1.maxInterStageShaderVariables * 4 + 4) {
return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxInterStageShaderComponents");
}
limits->v1.maxInterStageShaderComponents =
std::min(vkLimits.maxVertexOutputComponents, vkLimits.maxFragmentInputComponents) - 4;
limits->v1.maxInterStageShaderVariables = limits->v1.maxInterStageShaderComponents / 4;
CHECK_AND_SET_V1_MAX_LIMIT(maxComputeSharedMemorySize, maxComputeWorkgroupStorageSize);
CHECK_AND_SET_V1_MAX_LIMIT(maxComputeWorkGroupInvocations, maxComputeInvocationsPerWorkgroup);
CHECK_AND_SET_V1_MAX_LIMIT(maxComputeWorkGroupSize[0], maxComputeWorkgroupSizeX);
CHECK_AND_SET_V1_MAX_LIMIT(maxComputeWorkGroupSize[1], maxComputeWorkgroupSizeY);
CHECK_AND_SET_V1_MAX_LIMIT(maxComputeWorkGroupSize[2], maxComputeWorkgroupSizeZ);
CHECK_AND_SET_V1_MAX_LIMIT(maxComputeWorkGroupCount[0], maxComputeWorkgroupsPerDimension);
CHECK_AND_SET_V1_MAX_LIMIT(maxComputeWorkGroupCount[1], maxComputeWorkgroupsPerDimension);
CHECK_AND_SET_V1_MAX_LIMIT(maxComputeWorkGroupCount[2], maxComputeWorkgroupsPerDimension);
limits->v1.maxComputeWorkgroupsPerDimension = std::min({
vkLimits.maxComputeWorkGroupCount[0],
vkLimits.maxComputeWorkGroupCount[1],
vkLimits.maxComputeWorkGroupCount[2],
});
if (!IsSubset(VkSampleCountFlags(VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT),
vkLimits.framebufferColorSampleCounts)) {
return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for framebufferColorSampleCounts");
}
if (!IsSubset(VkSampleCountFlags(VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT),
vkLimits.framebufferDepthSampleCounts)) {
return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for framebufferDepthSampleCounts");
}
limits->v1.maxBufferSize = kAssumedMaxBufferSize;
if (mDeviceInfo.HasExt(DeviceExt::Maintenance4)) {
limits->v1.maxBufferSize = mDeviceInfo.propertiesMaintenance4.maxBufferSize;
} else if (mDeviceInfo.HasExt(DeviceExt::Maintenance3)) {
limits->v1.maxBufferSize = mDeviceInfo.propertiesMaintenance3.maxMemoryAllocationSize;
}
if (limits->v1.maxBufferSize < baseLimits.v1.maxBufferSize) {
return DAWN_INTERNAL_ERROR("Insufficient Vulkan maxBufferSize limit");
}
if (mDeviceInfo.HasExt(DeviceExt::SubgroupSizeControl)) {
mDefaultComputeSubgroupSize = FindDefaultComputeSubgroupSize();
if (mDefaultComputeSubgroupSize > 0) {
// According to VK_EXT_subgroup_size_control, for compute shaders we must ensure
// computeInvocationsPerWorkgroup <= maxComputeWorkgroupSubgroups x computeSubgroupSize
limits->v1.maxComputeInvocationsPerWorkgroup =
std::min(limits->v1.maxComputeInvocationsPerWorkgroup,
mDeviceInfo.subgroupSizeControlProperties.maxComputeWorkgroupSubgroups *
mDefaultComputeSubgroupSize);
}
}
// Experimental limits for subgroups
limits->experimentalSubgroupLimits.minSubgroupSize =
mDeviceInfo.subgroupSizeControlProperties.minSubgroupSize;
limits->experimentalSubgroupLimits.maxSubgroupSize =
mDeviceInfo.subgroupSizeControlProperties.maxSubgroupSize;
return {};
}
bool PhysicalDevice::SupportsExternalImages() const {
// Via dawn::native::vulkan::WrapVulkanImage
return external_memory::Service::CheckSupport(mDeviceInfo) &&
external_semaphore::Service::CheckSupport(mDeviceInfo, mVkPhysicalDevice,
mVulkanInstance->GetFunctions());
}
bool PhysicalDevice::SupportsFeatureLevel(FeatureLevel) const {
return true;
}
void PhysicalDevice::SetupBackendAdapterToggles(dawn::platform::Platform* platform,
TogglesState* adapterToggles) const {}
void PhysicalDevice::SetupBackendDeviceToggles(dawn::platform::Platform* platform,
TogglesState* deviceToggles) const {
// TODO(crbug.com/dawn/857): tighten this workaround when this issue is fixed in both
// Vulkan SPEC and drivers.
deviceToggles->Default(Toggle::UseTemporaryBufferInCompressedTextureToTextureCopy, true);
if (IsAndroidQualcomm()) {
// dawn:1564, dawn:1897: Recording a compute pass after a render pass in the same command
// buffer frequently causes a crash on Qualcomm GPUs. To work around that bug, split the
// command buffer any time we are about to record a compute pass when a render pass has
// already been recorded.
deviceToggles->Default(Toggle::VulkanSplitCommandBufferOnComputePassAfterRenderPass, true);
// dawn:1569: Qualcomm devices have a bug resolving into a non-zero level of an array
// texture. Work around it by resolving into a single level texture and then copying into
// the intended layer.
deviceToggles->Default(Toggle::AlwaysResolveIntoZeroLevelAndLayer, true);
}
if (IsAndroidARM()) {
// dawn:1550: Resolving multiple color targets in a single pass fails on ARM GPUs. To
// work around the issue, passes that resolve to multiple color targets will instead be
// forced to store the multisampled targets and do the resolves as separate passes injected
// after the original one.
deviceToggles->Default(Toggle::ResolveMultipleAttachmentInSeparatePasses, true);
}
if (IsAndroidSamsung() || IsAndroidQualcomm()) {
deviceToggles->Default(Toggle::IgnoreImportedAHardwareBufferVulkanImageSize, true);
}
if (IsIntelMesa() && gpu_info::IsIntelGen12LP(GetVendorId(), GetDeviceId())) {
// dawn:1688: Intel Mesa driver has a bug about reusing the VkDeviceMemory that was
// previously bound to a 2D VkImage. To work around that bug we have to disable the resource
// sub-allocation for 2D textures with CopyDst or RenderAttachment usage.
const gpu_info::DriverVersion kBuggyDriverVersion = {21, 3, 6, 0};
if (gpu_info::CompareIntelMesaDriverVersion(GetDriverVersion(), kBuggyDriverVersion) >= 0) {
deviceToggles->Default(
Toggle::DisableSubAllocationFor2DTextureWithCopyDstOrRenderAttachment, true);
}
// chromium:1361662: Mesa driver has a bug clearing R8 mip-leveled textures on Intel Gen12
// GPUs. Work around it by clearing the whole texture as soon as they are created.
const gpu_info::DriverVersion kFixedDriverVersion = {23, 1, 0, 0};
if (gpu_info::CompareIntelMesaDriverVersion(GetDriverVersion(), kFixedDriverVersion) < 0) {
deviceToggles->Default(Toggle::VulkanClearGen12TextureWithCCSAmbiguateOnCreation, true);
}
}
if (IsIntelMesa() && (gpu_info::IsIntelGen12LP(GetVendorId(), GetDeviceId()) ||
gpu_info::IsIntelGen12HP(GetVendorId(), GetDeviceId()))) {
// Intel Mesa driver has a bug where vkCmdCopyQueryPoolResults fails to write overlapping
// queries to a same buffer after the buffer is accessed by a compute shader with correct
// resource barriers, which may caused by flush and memory coherency issue on Intel Gen12
// GPUs. Workaround for it to clear the buffer before vkCmdCopyQueryPoolResults on Mesa
// driver version < 23.1.3.
const gpu_info::DriverVersion kBuggyDriverVersion = {21, 2, 0, 0};
const gpu_info::DriverVersion kFixedDriverVersion = {23, 1, 3, 0};
if (gpu_info::CompareIntelMesaDriverVersion(GetDriverVersion(), kBuggyDriverVersion) >= 0 &&
gpu_info::CompareIntelMesaDriverVersion(GetDriverVersion(), kFixedDriverVersion) < 0) {
deviceToggles->Default(Toggle::ClearBufferBeforeResolveQueries, true);
}
}
// The environment can request to various options for depth-stencil formats that could be
// unavailable. Override the decision if it is not applicable.
bool supportsD32s8 = IsDepthStencilFormatSupported(VK_FORMAT_D32_SFLOAT_S8_UINT);
bool supportsD24s8 = IsDepthStencilFormatSupported(VK_FORMAT_D24_UNORM_S8_UINT);
bool supportsS8 = IsDepthStencilFormatSupported(VK_FORMAT_S8_UINT);
DAWN_ASSERT(supportsD32s8 || supportsD24s8);
if (!supportsD24s8) {
deviceToggles->ForceSet(Toggle::VulkanUseD32S8, true);
}
if (!supportsD32s8) {
deviceToggles->ForceSet(Toggle::VulkanUseD32S8, false);
}
// By default try to use D32S8 for Depth24PlusStencil8
deviceToggles->Default(Toggle::VulkanUseD32S8, true);
if (!supportsS8) {
deviceToggles->ForceSet(Toggle::VulkanUseS8, false);
}
// By default try to use S8 if available.
deviceToggles->Default(Toggle::VulkanUseS8, true);
// The environment can only request to use VK_KHR_zero_initialize_workgroup_memory when the
// extension is available. Override the decision if it is not applicable or
// zeroInitializeWorkgroupMemoryFeatures.shaderZeroInitializeWorkgroupMemory == VK_FALSE.
// Never use the extension on Mali devices due to a known bug (see crbug.com/tint/2101).
if (!GetDeviceInfo().HasExt(DeviceExt::ZeroInitializeWorkgroupMemory) ||
GetDeviceInfo().zeroInitializeWorkgroupMemoryFeatures.shaderZeroInitializeWorkgroupMemory ==
VK_FALSE ||
IsAndroidARM()) {
deviceToggles->ForceSet(Toggle::VulkanUseZeroInitializeWorkgroupMemoryExtension, false);
}
// By default try to initialize workgroup memory with OpConstantNull according to the Vulkan
// extension VK_KHR_zero_initialize_workgroup_memory.
deviceToggles->Default(Toggle::VulkanUseZeroInitializeWorkgroupMemoryExtension, true);
// The environment can only request to use StorageInputOutput16 when the capability is
// available.
if (GetDeviceInfo()._16BitStorageFeatures.storageInputOutput16 == VK_FALSE) {
deviceToggles->ForceSet(Toggle::VulkanUseStorageInputOutput16, false);
}
// By default try to use the StorageInputOutput16 capability.
deviceToggles->Default(Toggle::VulkanUseStorageInputOutput16, true);
// Inject fragment shaders in all vertex-only pipelines.
// TODO(crbug.com/dawn/1698): relax this requirement where the Vulkan spec allows.
// In particular, enable rasterizer discard if the depth-stencil stage is a no-op, and skip
// insertion of the placeholder fragment shader.
deviceToggles->Default(Toggle::UsePlaceholderFragmentInVertexOnlyPipeline, true);
// The environment can only request to use VK_EXT_robustness2 when the extension is available.
// Override the decision if it is not applicable or robustImageAccess2 is false.
if (!GetDeviceInfo().HasExt(DeviceExt::Robustness2) ||
GetDeviceInfo().robustness2Features.robustImageAccess2 == VK_FALSE) {
deviceToggles->ForceSet(Toggle::VulkanUseImageRobustAccess2, false);
}
// By default try to skip robustness transform on textures according to the Vulkan extension
// VK_EXT_robustness2.
deviceToggles->Default(Toggle::VulkanUseImageRobustAccess2, true);
// The environment can only request to use VK_EXT_robustness2 when the extension is available.
// Override the decision if it is not applicable or robustBufferAccess2 is false.
if (!GetDeviceInfo().HasExt(DeviceExt::Robustness2) ||
GetDeviceInfo().robustness2Features.robustBufferAccess2 == VK_FALSE) {
deviceToggles->ForceSet(Toggle::VulkanUseBufferRobustAccess2, false);
}
// By default try to disable index clamping on the runtime-sized arrays on storage buffers in
// Tint robustness transform according to the Vulkan extension VK_EXT_robustness2.
deviceToggles->Default(Toggle::VulkanUseBufferRobustAccess2, true);
// Enable the polyfill versions of dot4I8Packed() and dot4U8Packed() when the SPIR-V capability
// `DotProductInput4x8BitPackedKHR` is not supported.
if (!GetDeviceInfo().HasExt(DeviceExt::ShaderIntegerDotProduct) ||
GetDeviceInfo().shaderIntegerDotProductFeatures.shaderIntegerDotProduct == VK_FALSE) {
deviceToggles->ForceSet(Toggle::PolyFillPacked4x8DotProduct, true);
}
}
ResultOrError<Ref<DeviceBase>> PhysicalDevice::CreateDeviceImpl(
AdapterBase* adapter,
const UnpackedPtr<DeviceDescriptor>& descriptor,
const TogglesState& deviceToggles,
Ref<DeviceBase::DeviceLostEvent>&& lostEvent) {
return Device::Create(adapter, descriptor, deviceToggles, std::move(lostEvent));
}
FeatureValidationResult PhysicalDevice::ValidateFeatureSupportedWithTogglesImpl(
wgpu::FeatureName feature,
const TogglesState& toggles) const {
return {};
}
// Android devices with Qualcomm GPUs have a myriad of known issues. (dawn:1549)
bool PhysicalDevice::IsAndroidQualcomm() const {
#if DAWN_PLATFORM_IS(ANDROID)
return gpu_info::IsQualcomm_PCI(GetVendorId());
#else
return false;
#endif
}
// Android devices with ARM GPUs have known issues. (dawn:1550)
bool PhysicalDevice::IsAndroidARM() const {
#if DAWN_PLATFORM_IS(ANDROID)
return gpu_info::IsARM(GetVendorId());
#else
return false;
#endif
}
bool PhysicalDevice::IsAndroidSamsung() const {
#if DAWN_PLATFORM_IS(ANDROID)
return gpu_info::IsSamsung(GetVendorId());
#else
return false;
#endif
}
bool PhysicalDevice::IsIntelMesa() const {
if (mDeviceInfo.HasExt(DeviceExt::DriverProperties)) {
return mDeviceInfo.driverProperties.driverID == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA_KHR;
}
return false;
}
uint32_t PhysicalDevice::FindDefaultComputeSubgroupSize() const {
if (!mDeviceInfo.HasExt(DeviceExt::SubgroupSizeControl)) {
return 0;
}
const VkPhysicalDeviceSubgroupSizeControlPropertiesEXT& ext =
mDeviceInfo.subgroupSizeControlProperties;
if (ext.minSubgroupSize == ext.maxSubgroupSize) {
return 0;
}
// At the moment, only Intel devices support varying subgroup sizes and 16, which is the
// next value after the minimum of 8, is the sweet spot according to [1]. Hence the
// following heuristics, which may need to be adjusted in the future for other
// architectures, or if a specific API is added to let client code select the size.
//
// [1] https://bugs.freedesktop.org/show_bug.cgi?id=108875
uint32_t subgroupSize = ext.minSubgroupSize * 2;
if (subgroupSize <= ext.maxSubgroupSize) {
return subgroupSize;
} else {
return ext.minSubgroupSize;
}
}
bool PhysicalDevice::CheckSemaphoreSupport(DeviceExt deviceExt,
VkExternalSemaphoreHandleTypeFlagBits handleType) const {
if (!mDeviceInfo.HasExt(deviceExt)) {
return false;
}
constexpr VkFlags kRequiredSemaphoreFlags = VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT_KHR |
VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT_KHR;
VkPhysicalDeviceExternalSemaphoreInfoKHR semaphoreInfo;
semaphoreInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO_KHR;
semaphoreInfo.pNext = nullptr;
VkExternalSemaphorePropertiesKHR semaphoreProperties;
semaphoreProperties.sType = VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES_KHR;
semaphoreProperties.pNext = nullptr;
semaphoreInfo.handleType = handleType;
mVulkanInstance->GetFunctions().GetPhysicalDeviceExternalSemaphoreProperties(
mVkPhysicalDevice, &semaphoreInfo, &semaphoreProperties);
return IsSubset(kRequiredSemaphoreFlags, semaphoreProperties.externalSemaphoreFeatures);
}
uint32_t PhysicalDevice::GetDefaultComputeSubgroupSize() const {
return mDefaultComputeSubgroupSize;
}
ResultOrError<PhysicalDeviceSurfaceCapabilities> PhysicalDevice::GetSurfaceCapabilities(
InstanceBase* instance,
const Surface* surface) const {
// Gather the Vulkan surface capabilities.
VulkanSurfaceInfo vkCaps;
{
const VulkanFunctions& fn = GetVulkanInstance()->GetFunctions();
VkInstance vkInstance = GetVulkanInstance()->GetVkInstance();
VkSurfaceKHR vkSurface;
DAWN_TRY_ASSIGN(vkSurface, CreateVulkanSurface(instance, this, surface));
DAWN_TRY_ASSIGN_WITH_CLEANUP(vkCaps, GatherSurfaceInfo(*this, vkSurface),
{ fn.DestroySurfaceKHR(vkInstance, vkSurface, nullptr); });
fn.DestroySurfaceKHR(vkInstance, vkSurface, nullptr);
}
PhysicalDeviceSurfaceCapabilities capabilities;
// Convert the known swapchain usages.
capabilities.usages = wgpu::TextureUsage::None;
if (vkCaps.capabilities.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT) {
capabilities.usages |= wgpu::TextureUsage::CopySrc;
}
if (vkCaps.capabilities.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT) {
capabilities.usages |= wgpu::TextureUsage::CopyDst;
}
if (vkCaps.capabilities.supportedUsageFlags & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) {
capabilities.usages |= wgpu::TextureUsage::RenderAttachment;
}
if (vkCaps.capabilities.supportedUsageFlags & VK_IMAGE_USAGE_SAMPLED_BIT) {
capabilities.usages |= wgpu::TextureUsage::TextureBinding;
}
if (vkCaps.capabilities.supportedUsageFlags & VK_IMAGE_USAGE_STORAGE_BIT) {
capabilities.usages |= wgpu::TextureUsage::StorageBinding;
}
// Convert known swapchain formats
auto ToWGPUSwapChainFormat = [](VkFormat format) -> wgpu::TextureFormat {
switch (format) {
case VK_FORMAT_R8G8B8A8_UNORM:
return wgpu::TextureFormat::RGBA8Unorm;
case VK_FORMAT_R8G8B8A8_SRGB:
return wgpu::TextureFormat::RGBA8UnormSrgb;
case VK_FORMAT_B8G8R8A8_UNORM:
return wgpu::TextureFormat::BGRA8Unorm;
case VK_FORMAT_B8G8R8A8_SRGB:
return wgpu::TextureFormat::BGRA8UnormSrgb;
case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
return wgpu::TextureFormat::RGB10A2Unorm;
case VK_FORMAT_R16G16B16A16_SFLOAT:
return wgpu::TextureFormat::RGBA16Float;
default:
return wgpu::TextureFormat::Undefined;
}
};
for (VkSurfaceFormatKHR surfaceFormat : vkCaps.formats) {
wgpu::TextureFormat format = ToWGPUSwapChainFormat(surfaceFormat.format);
if (format != wgpu::TextureFormat::Undefined) {
capabilities.formats.push_back(format);
}
}
// Convert known present modes
auto ToWGPUPresentMode = [](VkPresentModeKHR mode) -> std::optional<wgpu::PresentMode> {
switch (mode) {
case VK_PRESENT_MODE_FIFO_KHR:
return wgpu::PresentMode::Fifo;
case VK_PRESENT_MODE_FIFO_RELAXED_KHR:
return wgpu::PresentMode::FifoRelaxed;
case VK_PRESENT_MODE_MAILBOX_KHR:
return wgpu::PresentMode::Mailbox;
case VK_PRESENT_MODE_IMMEDIATE_KHR:
return wgpu::PresentMode::Immediate;
default:
return {};
}
};
for (VkPresentModeKHR vkMode : vkCaps.presentModes) {
std::optional<wgpu::PresentMode> wgpuMode = ToWGPUPresentMode(vkMode);
if (wgpuMode) {
capabilities.presentModes.push_back(*wgpuMode);
}
}
// Compute supported alpha modes
struct AlphaModePairs {
VkCompositeAlphaFlagBitsKHR vkBit;
wgpu::CompositeAlphaMode webgpuEnum;
};
AlphaModePairs alphaModePairs[] = {
{VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, wgpu::CompositeAlphaMode::Opaque},
{VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR, wgpu::CompositeAlphaMode::Premultiplied},
{VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR, wgpu::CompositeAlphaMode::Unpremultiplied},
{VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR, wgpu::CompositeAlphaMode::Inherit},
};
for (auto mode : alphaModePairs) {
if (vkCaps.capabilities.supportedCompositeAlpha & mode.vkBit) {
capabilities.alphaModes.push_back(mode.webgpuEnum);
}
}
return capabilities;
}
const AHBFunctions* PhysicalDevice::GetOrLoadAHBFunctions() {
#if DAWN_PLATFORM_IS(ANDROID)
if (mAHBFunctions == nullptr) {
mAHBFunctions = std::make_unique<AHBFunctions>();
}
return mAHBFunctions.get();
#else
DAWN_UNREACHABLE();
#endif // DAWN_PLATFORM_IS(ANDROID)
}
void PhysicalDevice::PopulateBackendProperties(UnpackedPtr<AdapterInfo>& info) const {
if (auto* memoryHeapProperties = info.Get<AdapterPropertiesMemoryHeaps>()) {
size_t count = mDeviceInfo.memoryHeaps.size();
auto* heapInfo = new MemoryHeapInfo[count];
memoryHeapProperties->heapCount = count;
memoryHeapProperties->heapInfo = heapInfo;
for (size_t i = 0; i < count; ++i) {
heapInfo[i].size = mDeviceInfo.memoryHeaps[i].size;
heapInfo[i].properties = {};
if (mDeviceInfo.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) {
heapInfo[i].properties |= wgpu::HeapProperty::DeviceLocal;
}
}
for (const auto& memoryType : mDeviceInfo.memoryTypes) {
if (memoryType.propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
heapInfo[memoryType.heapIndex].properties |= wgpu::HeapProperty::HostVisible;
}
if (memoryType.propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) {
heapInfo[memoryType.heapIndex].properties |= wgpu::HeapProperty::HostCoherent;
}
if (memoryType.propertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) {
heapInfo[memoryType.heapIndex].properties |= wgpu::HeapProperty::HostCached;
} else {
heapInfo[memoryType.heapIndex].properties |= wgpu::HeapProperty::HostUncached;
}
}
}
if (auto* vkProperties = info.Get<AdapterPropertiesVk>()) {
vkProperties->driverVersion = mDeviceInfo.properties.driverVersion;
}
}
void PhysicalDevice::PopulateBackendFormatCapabilities(
wgpu::TextureFormat format,
UnpackedPtr<FormatCapabilities>& capabilities) const {
if (auto* drmCapabilities = capabilities.Get<DrmFormatCapabilities>()) {
auto vk_format = ColorVulkanImageFormat(format);
if (vk_format == VK_FORMAT_UNDEFINED) {
drmCapabilities->properties = nullptr;
drmCapabilities->propertiesCount = 0;
}
auto drmFormatModifiers =
GetFormatModifierProps(mVulkanInstance->GetFunctions(), mVkPhysicalDevice, vk_format);
if (!drmFormatModifiers.empty()) {
size_t count = drmFormatModifiers.size();
auto* properties = new DrmFormatProperties[count];
drmCapabilities->properties = properties;
drmCapabilities->propertiesCount = count;
for (size_t i = 0; i < count; i++) {
properties[i].modifier = drmFormatModifiers[i].drmFormatModifier;
properties[i].modifierPlaneCount =
drmFormatModifiers[i].drmFormatModifierPlaneCount;
}
}
}
}
} // namespace dawn::native::vulkan