| // 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/vulkan/BackendVk.h" |
| #include "dawn_native/vulkan/DeviceVk.h" |
| |
| #include "common/GPUInfo.h" |
| |
| namespace dawn_native { namespace vulkan { |
| |
| Adapter::Adapter(Backend* backend, VkPhysicalDevice physicalDevice) |
| : AdapterBase(backend->GetInstance(), wgpu::BackendType::Vulkan), |
| mPhysicalDevice(physicalDevice), |
| mBackend(backend) { |
| } |
| |
| const VulkanDeviceInfo& Adapter::GetDeviceInfo() const { |
| return mDeviceInfo; |
| } |
| |
| VkPhysicalDevice Adapter::GetPhysicalDevice() const { |
| return mPhysicalDevice; |
| } |
| |
| Backend* Adapter::GetBackend() const { |
| return mBackend; |
| } |
| |
| MaybeError Adapter::Initialize() { |
| DAWN_TRY_ASSIGN(mDeviceInfo, GatherDeviceInfo(*this)); |
| DAWN_TRY(CheckCoreWebGPUSupport()); |
| |
| 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); |
| } |
| |
| InitializeSupportedFeatures(); |
| |
| 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::CheckCoreWebGPUSupport() { |
| // 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."); |
| } |
| |
| // Check base WebGPU limits are supported. |
| const VkPhysicalDeviceLimits& limits = mDeviceInfo.properties.limits; |
| if (limits.maxImageDimension1D < kMaxTextureDimension1D) { |
| return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxTextureDimension1D"); |
| } |
| if (limits.maxImageDimension2D < kMaxTextureDimension2D || |
| limits.maxImageDimensionCube < kMaxTextureDimension2D || |
| limits.maxFramebufferWidth < kMaxTextureDimension2D || |
| limits.maxFramebufferHeight < kMaxTextureDimension2D || |
| limits.maxViewportDimensions[0] < kMaxTextureDimension2D || |
| limits.maxViewportDimensions[1] < kMaxTextureDimension2D || |
| limits.viewportBoundsRange[1] < kMaxTextureDimension2D) { |
| return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxTextureDimension2D"); |
| } |
| if (limits.maxImageDimension3D < kMaxTextureDimension3D) { |
| return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxTextureDimension3D"); |
| } |
| if (limits.maxImageArrayLayers < kMaxTextureArrayLayers) { |
| return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxTextureArrayLayers"); |
| } |
| if (limits.maxBoundDescriptorSets < kMaxBindGroups) { |
| return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxBindGroups"); |
| } |
| if (limits.maxDescriptorSetUniformBuffersDynamic < |
| kMaxDynamicUniformBuffersPerPipelineLayout) { |
| return DAWN_INTERNAL_ERROR( |
| "Insufficient Vulkan limits for maxDynamicUniformBuffersPerPipelineLayout"); |
| } |
| if (limits.maxDescriptorSetStorageBuffersDynamic < |
| kMaxDynamicStorageBuffersPerPipelineLayout) { |
| return DAWN_INTERNAL_ERROR( |
| "Insufficient Vulkan limits for maxDynamicStorageBuffersPerPipelineLayout"); |
| } |
| if (limits.maxPerStageDescriptorSampledImages < kMaxSampledTexturesPerShaderStage) { |
| return DAWN_INTERNAL_ERROR( |
| "Insufficient Vulkan limits for maxSampledTexturesPerShaderStage"); |
| } |
| if (limits.maxPerStageDescriptorSamplers < kMaxSamplersPerShaderStage) { |
| return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxSamplersPerShaderStage"); |
| } |
| if (limits.maxPerStageDescriptorStorageBuffers < kMaxStorageBuffersPerShaderStage) { |
| return DAWN_INTERNAL_ERROR( |
| "Insufficient Vulkan limits for maxStorageBuffersPerShaderStage"); |
| } |
| if (limits.maxPerStageDescriptorStorageImages < kMaxStorageTexturesPerShaderStage) { |
| return DAWN_INTERNAL_ERROR( |
| "Insufficient Vulkan limits for maxStorageTexturesPerShaderStage"); |
| } |
| if (limits.maxPerStageDescriptorUniformBuffers < kMaxUniformBuffersPerShaderStage) { |
| return DAWN_INTERNAL_ERROR( |
| "Insufficient Vulkan limits for maxUniformBuffersPerShaderStage"); |
| } |
| if (limits.maxUniformBufferRange < kMaxUniformBufferBindingSize) { |
| return DAWN_INTERNAL_ERROR( |
| "Insufficient Vulkan limits for maxUniformBufferBindingSize"); |
| } |
| if (limits.maxStorageBufferRange < kMaxStorageBufferBindingSize) { |
| return DAWN_INTERNAL_ERROR( |
| "Insufficient Vulkan limits for maxStorageBufferBindingSize"); |
| } |
| if (limits.minUniformBufferOffsetAlignment > kMinUniformBufferOffsetAlignment) { |
| return DAWN_INTERNAL_ERROR( |
| "Insufficient Vulkan limits for minUniformBufferOffsetAlignment"); |
| } |
| if (limits.minStorageBufferOffsetAlignment > kMinStorageBufferOffsetAlignment) { |
| return DAWN_INTERNAL_ERROR( |
| "Insufficient Vulkan limits for minStorageBufferOffsetAlignment"); |
| } |
| if (limits.maxVertexInputBindings < kMaxVertexBuffers) { |
| return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxVertexBuffers"); |
| } |
| if (limits.maxVertexInputAttributes < kMaxVertexAttributes) { |
| return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxVertexAttributes"); |
| } |
| if (limits.maxVertexInputBindingStride < kMaxVertexBufferArrayStride || |
| limits.maxVertexInputAttributeOffset < kMaxVertexBufferArrayStride - 1) { |
| return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxVertexBufferArrayStride"); |
| } |
| if (limits.maxVertexOutputComponents < kMaxInterStageShaderComponents || |
| limits.maxFragmentInputComponents < kMaxInterStageShaderComponents) { |
| return DAWN_INTERNAL_ERROR( |
| "Insufficient Vulkan limits for maxInterStageShaderComponents"); |
| } |
| if (limits.maxComputeSharedMemorySize < kMaxComputeWorkgroupStorageSize) { |
| return DAWN_INTERNAL_ERROR( |
| "Insufficient Vulkan limits for maxComputeWorkgroupStorageSize"); |
| } |
| if (limits.maxComputeWorkGroupInvocations < kMaxComputeWorkgroupInvocations) { |
| return DAWN_INTERNAL_ERROR( |
| "Insufficient Vulkan limits for maxComputeWorkgroupInvocations"); |
| } |
| if (limits.maxComputeWorkGroupSize[0] < kMaxComputeWorkgroupSizeX || |
| limits.maxComputeWorkGroupSize[1] < kMaxComputeWorkgroupSizeY || |
| limits.maxComputeWorkGroupSize[2] < kMaxComputeWorkgroupSizeZ) { |
| return DAWN_INTERNAL_ERROR( |
| "Insufficient Vulkan limits for maxComputeWorkgroupSize"); |
| } |
| if (limits.maxComputeWorkGroupCount[0] < kMaxComputePerDimensionDispatchSize || |
| limits.maxComputeWorkGroupCount[1] < kMaxComputePerDimensionDispatchSize || |
| limits.maxComputeWorkGroupCount[2] < kMaxComputePerDimensionDispatchSize) { |
| return DAWN_INTERNAL_ERROR( |
| "Insufficient Vulkan limits for maxComputePerDimensionDispatchSize"); |
| } |
| if (limits.maxColorAttachments < kMaxColorAttachments) { |
| return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxColorAttachments"); |
| } |
| if (!IsSubset(VkSampleCountFlags(VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT), |
| limits.framebufferColorSampleCounts)) { |
| return DAWN_INTERNAL_ERROR( |
| "Insufficient Vulkan limits for framebufferColorSampleCounts"); |
| } |
| if (!IsSubset(VkSampleCountFlags(VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT), |
| limits.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 (limits.maxFragmentCombinedOutputResources < kMaxColorAttachments + |
| kMaxStorageTexturesPerShaderStage + |
| kMaxStorageBuffersPerShaderStage) { |
| return DAWN_INTERNAL_ERROR( |
| "Insufficient Vulkan maxFragmentCombinedOutputResources limit"); |
| } |
| } |
| |
| return {}; |
| } |
| |
| bool Adapter::SupportsExternalImages() const { |
| // Via dawn_native::vulkan::WrapVulkanImage |
| return external_memory::Service::CheckSupport(mDeviceInfo) && |
| external_semaphore::Service::CheckSupport(mDeviceInfo, mPhysicalDevice, |
| mBackend->GetFunctions()); |
| } |
| |
| void Adapter::InitializeSupportedFeatures() { |
| 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); |
| } |
| } |
| |
| ResultOrError<DeviceBase*> Adapter::CreateDeviceImpl(const DeviceDescriptor* descriptor) { |
| return Device::Create(this, descriptor); |
| } |
| |
| }} // namespace dawn_native::vulkan |