1 // Copyright 2019 The Dawn Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "dawn_native/vulkan/AdapterVk.h" 16 17 #include "dawn_native/Limits.h" 18 #include "dawn_native/vulkan/BackendVk.h" 19 #include "dawn_native/vulkan/DeviceVk.h" 20 21 #include "common/GPUInfo.h" 22 23 namespace dawn_native { namespace vulkan { 24 Adapter(InstanceBase * instance,VulkanInstance * vulkanInstance,VkPhysicalDevice physicalDevice)25 Adapter::Adapter(InstanceBase* instance, 26 VulkanInstance* vulkanInstance, 27 VkPhysicalDevice physicalDevice) 28 : AdapterBase(instance, wgpu::BackendType::Vulkan), 29 mPhysicalDevice(physicalDevice), 30 mVulkanInstance(vulkanInstance) { 31 } 32 GetDeviceInfo() const33 const VulkanDeviceInfo& Adapter::GetDeviceInfo() const { 34 return mDeviceInfo; 35 } 36 GetPhysicalDevice() const37 VkPhysicalDevice Adapter::GetPhysicalDevice() const { 38 return mPhysicalDevice; 39 } 40 GetVulkanInstance() const41 VulkanInstance* Adapter::GetVulkanInstance() const { 42 return mVulkanInstance.Get(); 43 } 44 IsDepthStencilFormatSupported(VkFormat format)45 bool Adapter::IsDepthStencilFormatSupported(VkFormat format) { 46 ASSERT(format == VK_FORMAT_D16_UNORM_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT || 47 format == VK_FORMAT_D32_SFLOAT_S8_UINT); 48 49 VkFormatProperties properties; 50 mVulkanInstance->GetFunctions().GetPhysicalDeviceFormatProperties(mPhysicalDevice, format, 51 &properties); 52 return properties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT; 53 } 54 InitializeImpl()55 MaybeError Adapter::InitializeImpl() { 56 DAWN_TRY_ASSIGN(mDeviceInfo, GatherDeviceInfo(*this)); 57 58 if (mDeviceInfo.HasExt(DeviceExt::DriverProperties)) { 59 mDriverDescription = mDeviceInfo.driverProperties.driverName; 60 if (mDeviceInfo.driverProperties.driverInfo[0] != '\0') { 61 mDriverDescription += std::string(": ") + mDeviceInfo.driverProperties.driverInfo; 62 } 63 } else { 64 mDriverDescription = 65 "Vulkan driver version: " + std::to_string(mDeviceInfo.properties.driverVersion); 66 } 67 68 mPCIInfo.deviceId = mDeviceInfo.properties.deviceID; 69 mPCIInfo.vendorId = mDeviceInfo.properties.vendorID; 70 mPCIInfo.name = mDeviceInfo.properties.deviceName; 71 72 switch (mDeviceInfo.properties.deviceType) { 73 case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: 74 mAdapterType = wgpu::AdapterType::IntegratedGPU; 75 break; 76 case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: 77 mAdapterType = wgpu::AdapterType::DiscreteGPU; 78 break; 79 case VK_PHYSICAL_DEVICE_TYPE_CPU: 80 mAdapterType = wgpu::AdapterType::CPU; 81 break; 82 default: 83 mAdapterType = wgpu::AdapterType::Unknown; 84 break; 85 } 86 87 return {}; 88 } 89 InitializeSupportedFeaturesImpl()90 MaybeError Adapter::InitializeSupportedFeaturesImpl() { 91 // Needed for viewport Y-flip. 92 if (!mDeviceInfo.HasExt(DeviceExt::Maintenance1)) { 93 return DAWN_INTERNAL_ERROR("Vulkan 1.1 or Vulkan 1.0 with KHR_Maintenance1 required."); 94 } 95 96 // Needed for security 97 if (!mDeviceInfo.features.robustBufferAccess) { 98 return DAWN_INTERNAL_ERROR("Vulkan robustBufferAccess feature required."); 99 } 100 101 if (!mDeviceInfo.features.textureCompressionBC && 102 !(mDeviceInfo.features.textureCompressionETC2 && 103 mDeviceInfo.features.textureCompressionASTC_LDR)) { 104 return DAWN_INTERNAL_ERROR( 105 "Vulkan textureCompressionBC feature required or both textureCompressionETC2 and " 106 "textureCompressionASTC required."); 107 } 108 109 // Needed for the respective WebGPU features. 110 if (!mDeviceInfo.features.depthBiasClamp) { 111 return DAWN_INTERNAL_ERROR("Vulkan depthBiasClamp feature required."); 112 } 113 if (!mDeviceInfo.features.fragmentStoresAndAtomics) { 114 return DAWN_INTERNAL_ERROR("Vulkan fragmentStoresAndAtomics feature required."); 115 } 116 if (!mDeviceInfo.features.fullDrawIndexUint32) { 117 return DAWN_INTERNAL_ERROR("Vulkan fullDrawIndexUint32 feature required."); 118 } 119 if (!mDeviceInfo.features.imageCubeArray) { 120 return DAWN_INTERNAL_ERROR("Vulkan imageCubeArray feature required."); 121 } 122 if (!mDeviceInfo.features.independentBlend) { 123 return DAWN_INTERNAL_ERROR("Vulkan independentBlend feature required."); 124 } 125 if (!mDeviceInfo.features.sampleRateShading) { 126 return DAWN_INTERNAL_ERROR("Vulkan sampleRateShading feature required."); 127 } 128 129 // Initialize supported extensions 130 if (mDeviceInfo.features.textureCompressionBC == VK_TRUE) { 131 mSupportedFeatures.EnableFeature(Feature::TextureCompressionBC); 132 } 133 134 if (mDeviceInfo.features.textureCompressionETC2 == VK_TRUE) { 135 mSupportedFeatures.EnableFeature(Feature::TextureCompressionETC2); 136 } 137 138 if (mDeviceInfo.features.textureCompressionASTC_LDR == VK_TRUE) { 139 mSupportedFeatures.EnableFeature(Feature::TextureCompressionASTC); 140 } 141 142 if (mDeviceInfo.features.pipelineStatisticsQuery == VK_TRUE) { 143 mSupportedFeatures.EnableFeature(Feature::PipelineStatisticsQuery); 144 } 145 146 if (mDeviceInfo.features.depthClamp == VK_TRUE) { 147 mSupportedFeatures.EnableFeature(Feature::DepthClamping); 148 } 149 150 if (mDeviceInfo.properties.limits.timestampComputeAndGraphics == VK_TRUE) { 151 mSupportedFeatures.EnableFeature(Feature::TimestampQuery); 152 } 153 154 return {}; 155 } 156 InitializeSupportedLimitsImpl(CombinedLimits * limits)157 MaybeError Adapter::InitializeSupportedLimitsImpl(CombinedLimits* limits) { 158 GetDefaultLimits(&limits->v1); 159 CombinedLimits baseLimits = *limits; 160 161 const VkPhysicalDeviceLimits& vkLimits = mDeviceInfo.properties.limits; 162 163 #define CHECK_AND_SET_V1_LIMIT_IMPL(vulkanName, webgpuName, compareOp, msgSegment) \ 164 do { \ 165 if (vkLimits.vulkanName compareOp baseLimits.v1.webgpuName) { \ 166 return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for " #webgpuName \ 167 "." \ 168 " VkPhysicalDeviceLimits::" #vulkanName \ 169 " must be at " msgSegment " " + \ 170 std::to_string(baseLimits.v1.webgpuName)); \ 171 } \ 172 limits->v1.webgpuName = vkLimits.vulkanName; \ 173 } while (false) 174 175 #define CHECK_AND_SET_V1_MAX_LIMIT(vulkanName, webgpuName) \ 176 CHECK_AND_SET_V1_LIMIT_IMPL(vulkanName, webgpuName, <, "least") 177 #define CHECK_AND_SET_V1_MIN_LIMIT(vulkanName, webgpuName) \ 178 CHECK_AND_SET_V1_LIMIT_IMPL(vulkanName, webgpuName, >, "most") 179 180 CHECK_AND_SET_V1_MAX_LIMIT(maxImageDimension1D, maxTextureDimension1D); 181 182 CHECK_AND_SET_V1_MAX_LIMIT(maxImageDimension2D, maxTextureDimension2D); 183 CHECK_AND_SET_V1_MAX_LIMIT(maxImageDimensionCube, maxTextureDimension2D); 184 CHECK_AND_SET_V1_MAX_LIMIT(maxFramebufferWidth, maxTextureDimension2D); 185 CHECK_AND_SET_V1_MAX_LIMIT(maxFramebufferHeight, maxTextureDimension2D); 186 CHECK_AND_SET_V1_MAX_LIMIT(maxViewportDimensions[0], maxTextureDimension2D); 187 CHECK_AND_SET_V1_MAX_LIMIT(maxViewportDimensions[1], maxTextureDimension2D); 188 CHECK_AND_SET_V1_MAX_LIMIT(viewportBoundsRange[1], maxTextureDimension2D); 189 limits->v1.maxTextureDimension2D = std::min({ 190 static_cast<uint32_t>(vkLimits.maxImageDimension2D), 191 static_cast<uint32_t>(vkLimits.maxImageDimensionCube), 192 static_cast<uint32_t>(vkLimits.maxFramebufferWidth), 193 static_cast<uint32_t>(vkLimits.maxFramebufferHeight), 194 static_cast<uint32_t>(vkLimits.maxViewportDimensions[0]), 195 static_cast<uint32_t>(vkLimits.maxViewportDimensions[1]), 196 static_cast<uint32_t>(vkLimits.viewportBoundsRange[1]), 197 }); 198 199 CHECK_AND_SET_V1_MAX_LIMIT(maxImageDimension3D, maxTextureDimension3D); 200 CHECK_AND_SET_V1_MAX_LIMIT(maxImageArrayLayers, maxTextureArrayLayers); 201 CHECK_AND_SET_V1_MAX_LIMIT(maxBoundDescriptorSets, maxBindGroups); 202 CHECK_AND_SET_V1_MAX_LIMIT(maxDescriptorSetUniformBuffersDynamic, 203 maxDynamicUniformBuffersPerPipelineLayout); 204 CHECK_AND_SET_V1_MAX_LIMIT(maxDescriptorSetStorageBuffersDynamic, 205 maxDynamicStorageBuffersPerPipelineLayout); 206 207 CHECK_AND_SET_V1_MAX_LIMIT(maxPerStageDescriptorSampledImages, 208 maxSampledTexturesPerShaderStage); 209 CHECK_AND_SET_V1_MAX_LIMIT(maxPerStageDescriptorSamplers, maxSamplersPerShaderStage); 210 CHECK_AND_SET_V1_MAX_LIMIT(maxPerStageDescriptorStorageBuffers, 211 maxStorageBuffersPerShaderStage); 212 CHECK_AND_SET_V1_MAX_LIMIT(maxPerStageDescriptorStorageImages, 213 maxStorageTexturesPerShaderStage); 214 CHECK_AND_SET_V1_MAX_LIMIT(maxPerStageDescriptorUniformBuffers, 215 maxUniformBuffersPerShaderStage); 216 CHECK_AND_SET_V1_MAX_LIMIT(maxUniformBufferRange, maxUniformBufferBindingSize); 217 CHECK_AND_SET_V1_MAX_LIMIT(maxStorageBufferRange, maxStorageBufferBindingSize); 218 219 CHECK_AND_SET_V1_MIN_LIMIT(minUniformBufferOffsetAlignment, 220 minUniformBufferOffsetAlignment); 221 CHECK_AND_SET_V1_MIN_LIMIT(minStorageBufferOffsetAlignment, 222 minStorageBufferOffsetAlignment); 223 224 CHECK_AND_SET_V1_MAX_LIMIT(maxVertexInputBindings, maxVertexBuffers); 225 CHECK_AND_SET_V1_MAX_LIMIT(maxVertexInputAttributes, maxVertexAttributes); 226 227 if (vkLimits.maxVertexInputBindingStride < baseLimits.v1.maxVertexBufferArrayStride || 228 vkLimits.maxVertexInputAttributeOffset < baseLimits.v1.maxVertexBufferArrayStride - 1) { 229 return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxVertexBufferArrayStride"); 230 } 231 limits->v1.maxVertexBufferArrayStride = std::min( 232 vkLimits.maxVertexInputBindingStride, vkLimits.maxVertexInputAttributeOffset + 1); 233 234 if (vkLimits.maxVertexOutputComponents < baseLimits.v1.maxInterStageShaderComponents || 235 vkLimits.maxFragmentInputComponents < baseLimits.v1.maxInterStageShaderComponents) { 236 return DAWN_INTERNAL_ERROR( 237 "Insufficient Vulkan limits for maxInterStageShaderComponents"); 238 } 239 limits->v1.maxInterStageShaderComponents = 240 std::min(vkLimits.maxVertexOutputComponents, vkLimits.maxFragmentInputComponents); 241 242 CHECK_AND_SET_V1_MAX_LIMIT(maxComputeSharedMemorySize, maxComputeWorkgroupStorageSize); 243 CHECK_AND_SET_V1_MAX_LIMIT(maxComputeWorkGroupInvocations, 244 maxComputeInvocationsPerWorkgroup); 245 CHECK_AND_SET_V1_MAX_LIMIT(maxComputeWorkGroupSize[0], maxComputeWorkgroupSizeX); 246 CHECK_AND_SET_V1_MAX_LIMIT(maxComputeWorkGroupSize[1], maxComputeWorkgroupSizeY); 247 CHECK_AND_SET_V1_MAX_LIMIT(maxComputeWorkGroupSize[2], maxComputeWorkgroupSizeZ); 248 249 CHECK_AND_SET_V1_MAX_LIMIT(maxComputeWorkGroupCount[0], maxComputeWorkgroupsPerDimension); 250 CHECK_AND_SET_V1_MAX_LIMIT(maxComputeWorkGroupCount[1], maxComputeWorkgroupsPerDimension); 251 CHECK_AND_SET_V1_MAX_LIMIT(maxComputeWorkGroupCount[2], maxComputeWorkgroupsPerDimension); 252 limits->v1.maxComputeWorkgroupsPerDimension = std::min({ 253 vkLimits.maxComputeWorkGroupCount[0], 254 vkLimits.maxComputeWorkGroupCount[1], 255 vkLimits.maxComputeWorkGroupCount[2], 256 }); 257 258 if (vkLimits.maxColorAttachments < kMaxColorAttachments) { 259 return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxColorAttachments"); 260 } 261 if (!IsSubset(VkSampleCountFlags(VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT), 262 vkLimits.framebufferColorSampleCounts)) { 263 return DAWN_INTERNAL_ERROR( 264 "Insufficient Vulkan limits for framebufferColorSampleCounts"); 265 } 266 if (!IsSubset(VkSampleCountFlags(VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT), 267 vkLimits.framebufferDepthSampleCounts)) { 268 return DAWN_INTERNAL_ERROR( 269 "Insufficient Vulkan limits for framebufferDepthSampleCounts"); 270 } 271 272 // Only check maxFragmentCombinedOutputResources on mobile GPUs. Desktop GPUs drivers seem 273 // to put incorrect values for this limit with things like 8 or 16 when they can do bindless 274 // storage buffers. 275 uint32_t vendorId = mDeviceInfo.properties.vendorID; 276 if (!gpu_info::IsAMD(vendorId) && !gpu_info::IsIntel(vendorId) && 277 !gpu_info::IsNvidia(vendorId)) { 278 if (vkLimits.maxFragmentCombinedOutputResources < 279 kMaxColorAttachments + baseLimits.v1.maxStorageTexturesPerShaderStage + 280 baseLimits.v1.maxStorageBuffersPerShaderStage) { 281 return DAWN_INTERNAL_ERROR( 282 "Insufficient Vulkan maxFragmentCombinedOutputResources limit"); 283 } 284 285 uint32_t maxFragmentCombinedOutputResources = 286 kMaxColorAttachments + limits->v1.maxStorageTexturesPerShaderStage + 287 limits->v1.maxStorageBuffersPerShaderStage; 288 289 if (maxFragmentCombinedOutputResources > vkLimits.maxFragmentCombinedOutputResources) { 290 // WebGPU's maxFragmentCombinedOutputResources exceeds the Vulkan limit. 291 // Decrease |maxStorageTexturesPerShaderStage| and |maxStorageBuffersPerShaderStage| 292 // to fit within the Vulkan limit. 293 uint32_t countOverLimit = maxFragmentCombinedOutputResources - 294 vkLimits.maxFragmentCombinedOutputResources; 295 296 uint32_t maxStorageTexturesOverBase = 297 limits->v1.maxStorageTexturesPerShaderStage - 298 baseLimits.v1.maxStorageTexturesPerShaderStage; 299 uint32_t maxStorageBuffersOverBase = limits->v1.maxStorageBuffersPerShaderStage - 300 baseLimits.v1.maxStorageBuffersPerShaderStage; 301 302 // Reduce the number of resources by half the overage count, but clamp to 303 // to ensure we don't go below the base limits. 304 uint32_t numFewerStorageTextures = 305 std::min(countOverLimit / 2, maxStorageTexturesOverBase); 306 uint32_t numFewerStorageBuffers = 307 std::min((countOverLimit + 1) / 2, maxStorageBuffersOverBase); 308 309 if (numFewerStorageTextures == maxStorageTexturesOverBase) { 310 // If |numFewerStorageTextures| was clamped, subtract the remaining 311 // from the storage buffers. 312 numFewerStorageBuffers = countOverLimit - numFewerStorageTextures; 313 ASSERT(numFewerStorageBuffers <= maxStorageBuffersOverBase); 314 } else if (numFewerStorageBuffers == maxStorageBuffersOverBase) { 315 // If |numFewerStorageBuffers| was clamped, subtract the remaining 316 // from the storage textures. 317 numFewerStorageTextures = countOverLimit - numFewerStorageBuffers; 318 ASSERT(numFewerStorageTextures <= maxStorageTexturesOverBase); 319 } 320 limits->v1.maxStorageTexturesPerShaderStage -= numFewerStorageTextures; 321 limits->v1.maxStorageBuffersPerShaderStage -= numFewerStorageBuffers; 322 } 323 } 324 325 return {}; 326 } 327 SupportsExternalImages() const328 bool Adapter::SupportsExternalImages() const { 329 // Via dawn_native::vulkan::WrapVulkanImage 330 return external_memory::Service::CheckSupport(mDeviceInfo) && 331 external_semaphore::Service::CheckSupport(mDeviceInfo, mPhysicalDevice, 332 mVulkanInstance->GetFunctions()); 333 } 334 CreateDeviceImpl(const DawnDeviceDescriptor * descriptor)335 ResultOrError<DeviceBase*> Adapter::CreateDeviceImpl(const DawnDeviceDescriptor* descriptor) { 336 return Device::Create(this, descriptor); 337 } 338 339 }} // namespace dawn_native::vulkan 340