mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-06-23 23:13:46 +00:00
This will allow the Vulkan backend connection to be initialized, and then selectively discover adapters on just one of the instances. This is needed so that discovery of the fallback WebGPU adapter can avoid initializing any adapters other than SwiftShader. Bug: chromium:1266550 Change-Id: Ia8b31c0239da89a41aa89f1c09a66e9e56e10d95 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/69980 Reviewed-by: Loko Kung <lokokung@google.com> Commit-Queue: Austin Eng <enga@chromium.org>
340 lines
17 KiB
C++
340 lines
17 KiB
C++
// Copyright 2019 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 "dawn_native/vulkan/AdapterVk.h"
|
|
|
|
#include "dawn_native/Limits.h"
|
|
#include "dawn_native/vulkan/BackendVk.h"
|
|
#include "dawn_native/vulkan/DeviceVk.h"
|
|
|
|
#include "common/GPUInfo.h"
|
|
|
|
namespace dawn_native { namespace vulkan {
|
|
|
|
Adapter::Adapter(InstanceBase* instance,
|
|
VulkanInstance* vulkanInstance,
|
|
VkPhysicalDevice physicalDevice)
|
|
: AdapterBase(instance, wgpu::BackendType::Vulkan),
|
|
mPhysicalDevice(physicalDevice),
|
|
mVulkanInstance(vulkanInstance) {
|
|
}
|
|
|
|
const VulkanDeviceInfo& Adapter::GetDeviceInfo() const {
|
|
return mDeviceInfo;
|
|
}
|
|
|
|
VkPhysicalDevice Adapter::GetPhysicalDevice() const {
|
|
return mPhysicalDevice;
|
|
}
|
|
|
|
VulkanInstance* Adapter::GetVulkanInstance() const {
|
|
return mVulkanInstance.Get();
|
|
}
|
|
|
|
bool Adapter::IsDepthStencilFormatSupported(VkFormat format) {
|
|
ASSERT(format == VK_FORMAT_D16_UNORM_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT ||
|
|
format == VK_FORMAT_D32_SFLOAT_S8_UINT);
|
|
|
|
VkFormatProperties properties;
|
|
mVulkanInstance->GetFunctions().GetPhysicalDeviceFormatProperties(mPhysicalDevice, format,
|
|
&properties);
|
|
return properties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
|
}
|
|
|
|
MaybeError Adapter::InitializeImpl() {
|
|
DAWN_TRY_ASSIGN(mDeviceInfo, GatherDeviceInfo(*this));
|
|
|
|
if (mDeviceInfo.HasExt(DeviceExt::DriverProperties)) {
|
|
mDriverDescription = mDeviceInfo.driverProperties.driverName;
|
|
if (mDeviceInfo.driverProperties.driverInfo[0] != '\0') {
|
|
mDriverDescription += std::string(": ") + mDeviceInfo.driverProperties.driverInfo;
|
|
}
|
|
} else {
|
|
mDriverDescription =
|
|
"Vulkan driver version: " + std::to_string(mDeviceInfo.properties.driverVersion);
|
|
}
|
|
|
|
mPCIInfo.deviceId = mDeviceInfo.properties.deviceID;
|
|
mPCIInfo.vendorId = mDeviceInfo.properties.vendorID;
|
|
mPCIInfo.name = 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;
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
MaybeError Adapter::InitializeSupportedFeaturesImpl() {
|
|
// 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 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.");
|
|
}
|
|
|
|
// Initialize supported extensions
|
|
if (mDeviceInfo.features.textureCompressionBC == VK_TRUE) {
|
|
mSupportedFeatures.EnableFeature(Feature::TextureCompressionBC);
|
|
}
|
|
|
|
if (mDeviceInfo.features.textureCompressionETC2 == VK_TRUE) {
|
|
mSupportedFeatures.EnableFeature(Feature::TextureCompressionETC2);
|
|
}
|
|
|
|
if (mDeviceInfo.features.textureCompressionASTC_LDR == VK_TRUE) {
|
|
mSupportedFeatures.EnableFeature(Feature::TextureCompressionASTC);
|
|
}
|
|
|
|
if (mDeviceInfo.features.pipelineStatisticsQuery == VK_TRUE) {
|
|
mSupportedFeatures.EnableFeature(Feature::PipelineStatisticsQuery);
|
|
}
|
|
|
|
if (mDeviceInfo.features.depthClamp == VK_TRUE) {
|
|
mSupportedFeatures.EnableFeature(Feature::DepthClamping);
|
|
}
|
|
|
|
if (mDeviceInfo.properties.limits.timestampComputeAndGraphics == VK_TRUE) {
|
|
mSupportedFeatures.EnableFeature(Feature::TimestampQuery);
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
MaybeError Adapter::InitializeSupportedLimitsImpl(CombinedLimits* limits) {
|
|
GetDefaultLimits(&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_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");
|
|
}
|
|
limits->v1.maxVertexBufferArrayStride = std::min(
|
|
vkLimits.maxVertexInputBindingStride, vkLimits.maxVertexInputAttributeOffset + 1);
|
|
|
|
if (vkLimits.maxVertexOutputComponents < baseLimits.v1.maxInterStageShaderComponents ||
|
|
vkLimits.maxFragmentInputComponents < baseLimits.v1.maxInterStageShaderComponents) {
|
|
return DAWN_INTERNAL_ERROR(
|
|
"Insufficient Vulkan limits for maxInterStageShaderComponents");
|
|
}
|
|
limits->v1.maxInterStageShaderComponents =
|
|
std::min(vkLimits.maxVertexOutputComponents, vkLimits.maxFragmentInputComponents);
|
|
|
|
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 (vkLimits.maxColorAttachments < kMaxColorAttachments) {
|
|
return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxColorAttachments");
|
|
}
|
|
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");
|
|
}
|
|
|
|
// Only check maxFragmentCombinedOutputResources on mobile GPUs. Desktop GPUs drivers seem
|
|
// to put incorrect values for this limit with things like 8 or 16 when they can do bindless
|
|
// storage buffers.
|
|
uint32_t vendorId = mDeviceInfo.properties.vendorID;
|
|
if (!gpu_info::IsAMD(vendorId) && !gpu_info::IsIntel(vendorId) &&
|
|
!gpu_info::IsNvidia(vendorId)) {
|
|
if (vkLimits.maxFragmentCombinedOutputResources <
|
|
kMaxColorAttachments + baseLimits.v1.maxStorageTexturesPerShaderStage +
|
|
baseLimits.v1.maxStorageBuffersPerShaderStage) {
|
|
return DAWN_INTERNAL_ERROR(
|
|
"Insufficient Vulkan maxFragmentCombinedOutputResources limit");
|
|
}
|
|
|
|
uint32_t maxFragmentCombinedOutputResources =
|
|
kMaxColorAttachments + limits->v1.maxStorageTexturesPerShaderStage +
|
|
limits->v1.maxStorageBuffersPerShaderStage;
|
|
|
|
if (maxFragmentCombinedOutputResources > vkLimits.maxFragmentCombinedOutputResources) {
|
|
// WebGPU's maxFragmentCombinedOutputResources exceeds the Vulkan limit.
|
|
// Decrease |maxStorageTexturesPerShaderStage| and |maxStorageBuffersPerShaderStage|
|
|
// to fit within the Vulkan limit.
|
|
uint32_t countOverLimit = maxFragmentCombinedOutputResources -
|
|
vkLimits.maxFragmentCombinedOutputResources;
|
|
|
|
uint32_t maxStorageTexturesOverBase =
|
|
limits->v1.maxStorageTexturesPerShaderStage -
|
|
baseLimits.v1.maxStorageTexturesPerShaderStage;
|
|
uint32_t maxStorageBuffersOverBase = limits->v1.maxStorageBuffersPerShaderStage -
|
|
baseLimits.v1.maxStorageBuffersPerShaderStage;
|
|
|
|
// Reduce the number of resources by half the overage count, but clamp to
|
|
// to ensure we don't go below the base limits.
|
|
uint32_t numFewerStorageTextures =
|
|
std::min(countOverLimit / 2, maxStorageTexturesOverBase);
|
|
uint32_t numFewerStorageBuffers =
|
|
std::min((countOverLimit + 1) / 2, maxStorageBuffersOverBase);
|
|
|
|
if (numFewerStorageTextures == maxStorageTexturesOverBase) {
|
|
// If |numFewerStorageTextures| was clamped, subtract the remaining
|
|
// from the storage buffers.
|
|
numFewerStorageBuffers = countOverLimit - numFewerStorageTextures;
|
|
ASSERT(numFewerStorageBuffers <= maxStorageBuffersOverBase);
|
|
} else if (numFewerStorageBuffers == maxStorageBuffersOverBase) {
|
|
// If |numFewerStorageBuffers| was clamped, subtract the remaining
|
|
// from the storage textures.
|
|
numFewerStorageTextures = countOverLimit - numFewerStorageBuffers;
|
|
ASSERT(numFewerStorageTextures <= maxStorageTexturesOverBase);
|
|
}
|
|
limits->v1.maxStorageTexturesPerShaderStage -= numFewerStorageTextures;
|
|
limits->v1.maxStorageBuffersPerShaderStage -= numFewerStorageBuffers;
|
|
}
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
bool Adapter::SupportsExternalImages() const {
|
|
// Via dawn_native::vulkan::WrapVulkanImage
|
|
return external_memory::Service::CheckSupport(mDeviceInfo) &&
|
|
external_semaphore::Service::CheckSupport(mDeviceInfo, mPhysicalDevice,
|
|
mVulkanInstance->GetFunctions());
|
|
}
|
|
|
|
ResultOrError<DeviceBase*> Adapter::CreateDeviceImpl(const DawnDeviceDescriptor* descriptor) {
|
|
return Device::Create(this, descriptor);
|
|
}
|
|
|
|
}} // namespace dawn_native::vulkan
|