• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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