1 // Copyright 2017 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/DeviceVk.h" 16 17 #include "common/Platform.h" 18 #include "dawn_native/BackendConnection.h" 19 #include "dawn_native/ChainUtils_autogen.h" 20 #include "dawn_native/Error.h" 21 #include "dawn_native/ErrorData.h" 22 #include "dawn_native/VulkanBackend.h" 23 #include "dawn_native/vulkan/AdapterVk.h" 24 #include "dawn_native/vulkan/BackendVk.h" 25 #include "dawn_native/vulkan/BindGroupLayoutVk.h" 26 #include "dawn_native/vulkan/BindGroupVk.h" 27 #include "dawn_native/vulkan/BufferVk.h" 28 #include "dawn_native/vulkan/CommandBufferVk.h" 29 #include "dawn_native/vulkan/ComputePipelineVk.h" 30 #include "dawn_native/vulkan/FencedDeleter.h" 31 #include "dawn_native/vulkan/PipelineLayoutVk.h" 32 #include "dawn_native/vulkan/QuerySetVk.h" 33 #include "dawn_native/vulkan/QueueVk.h" 34 #include "dawn_native/vulkan/RenderPassCache.h" 35 #include "dawn_native/vulkan/RenderPipelineVk.h" 36 #include "dawn_native/vulkan/ResourceMemoryAllocatorVk.h" 37 #include "dawn_native/vulkan/SamplerVk.h" 38 #include "dawn_native/vulkan/ShaderModuleVk.h" 39 #include "dawn_native/vulkan/StagingBufferVk.h" 40 #include "dawn_native/vulkan/SwapChainVk.h" 41 #include "dawn_native/vulkan/TextureVk.h" 42 #include "dawn_native/vulkan/UtilsVulkan.h" 43 #include "dawn_native/vulkan/VulkanError.h" 44 45 namespace dawn_native { namespace vulkan { 46 47 // static Create(Adapter * adapter,const DawnDeviceDescriptor * descriptor)48 ResultOrError<Device*> Device::Create(Adapter* adapter, 49 const DawnDeviceDescriptor* descriptor) { 50 Ref<Device> device = AcquireRef(new Device(adapter, descriptor)); 51 DAWN_TRY(device->Initialize()); 52 return device.Detach(); 53 } 54 Device(Adapter * adapter,const DawnDeviceDescriptor * descriptor)55 Device::Device(Adapter* adapter, const DawnDeviceDescriptor* descriptor) 56 : DeviceBase(adapter, descriptor) { 57 InitTogglesFromDriver(); 58 } 59 Initialize()60 MaybeError Device::Initialize() { 61 // Copy the adapter's device info to the device so that we can change the "knobs" 62 mDeviceInfo = ToBackend(GetAdapter())->GetDeviceInfo(); 63 64 // Initialize the "instance" procs of our local function table. 65 VulkanFunctions* functions = GetMutableFunctions(); 66 *functions = ToBackend(GetAdapter())->GetVulkanInstance()->GetFunctions(); 67 68 // Two things are crucial if device initialization fails: the function pointers to destroy 69 // objects, and the fence deleter that calls these functions. Do not do anything before 70 // these two are set up, so that a failed initialization doesn't cause a crash in 71 // DestroyImpl() 72 { 73 VkPhysicalDevice physicalDevice = ToBackend(GetAdapter())->GetPhysicalDevice(); 74 75 VulkanDeviceKnobs usedDeviceKnobs = {}; 76 DAWN_TRY_ASSIGN(usedDeviceKnobs, CreateDevice(physicalDevice)); 77 *static_cast<VulkanDeviceKnobs*>(&mDeviceInfo) = usedDeviceKnobs; 78 79 DAWN_TRY(functions->LoadDeviceProcs(mVkDevice, mDeviceInfo)); 80 81 // The queue can be loaded before the fenced deleter because their lifetime is tied to 82 // the device. 83 GatherQueueFromDevice(); 84 85 mDeleter = std::make_unique<FencedDeleter>(this); 86 } 87 88 mRenderPassCache = std::make_unique<RenderPassCache>(this); 89 mResourceMemoryAllocator = std::make_unique<ResourceMemoryAllocator>(this); 90 91 mExternalMemoryService = std::make_unique<external_memory::Service>(this); 92 mExternalSemaphoreService = std::make_unique<external_semaphore::Service>(this); 93 94 DAWN_TRY(PrepareRecordingContext()); 95 96 // The environment can request to use D32S8 or D24S8 when it's not available. Override 97 // the decision if it is not applicable. 98 ApplyDepth24PlusS8Toggle(); 99 100 return DeviceBase::Initialize(Queue::Create(this)); 101 } 102 ~Device()103 Device::~Device() { 104 Destroy(); 105 } 106 CreateBindGroupImpl(const BindGroupDescriptor * descriptor)107 ResultOrError<Ref<BindGroupBase>> Device::CreateBindGroupImpl( 108 const BindGroupDescriptor* descriptor) { 109 return BindGroup::Create(this, descriptor); 110 } CreateBindGroupLayoutImpl(const BindGroupLayoutDescriptor * descriptor,PipelineCompatibilityToken pipelineCompatibilityToken)111 ResultOrError<Ref<BindGroupLayoutBase>> Device::CreateBindGroupLayoutImpl( 112 const BindGroupLayoutDescriptor* descriptor, 113 PipelineCompatibilityToken pipelineCompatibilityToken) { 114 return BindGroupLayout::Create(this, descriptor, pipelineCompatibilityToken); 115 } CreateBufferImpl(const BufferDescriptor * descriptor)116 ResultOrError<Ref<BufferBase>> Device::CreateBufferImpl(const BufferDescriptor* descriptor) { 117 return Buffer::Create(this, descriptor); 118 } CreateCommandBuffer(CommandEncoder * encoder,const CommandBufferDescriptor * descriptor)119 ResultOrError<Ref<CommandBufferBase>> Device::CreateCommandBuffer( 120 CommandEncoder* encoder, 121 const CommandBufferDescriptor* descriptor) { 122 return CommandBuffer::Create(encoder, descriptor); 123 } CreateUninitializedComputePipelineImpl(const ComputePipelineDescriptor * descriptor)124 Ref<ComputePipelineBase> Device::CreateUninitializedComputePipelineImpl( 125 const ComputePipelineDescriptor* descriptor) { 126 return ComputePipeline::CreateUninitialized(this, descriptor); 127 } CreatePipelineLayoutImpl(const PipelineLayoutDescriptor * descriptor)128 ResultOrError<Ref<PipelineLayoutBase>> Device::CreatePipelineLayoutImpl( 129 const PipelineLayoutDescriptor* descriptor) { 130 return PipelineLayout::Create(this, descriptor); 131 } CreateQuerySetImpl(const QuerySetDescriptor * descriptor)132 ResultOrError<Ref<QuerySetBase>> Device::CreateQuerySetImpl( 133 const QuerySetDescriptor* descriptor) { 134 return QuerySet::Create(this, descriptor); 135 } CreateUninitializedRenderPipelineImpl(const RenderPipelineDescriptor * descriptor)136 Ref<RenderPipelineBase> Device::CreateUninitializedRenderPipelineImpl( 137 const RenderPipelineDescriptor* descriptor) { 138 return RenderPipeline::CreateUninitialized(this, descriptor); 139 } CreateSamplerImpl(const SamplerDescriptor * descriptor)140 ResultOrError<Ref<SamplerBase>> Device::CreateSamplerImpl(const SamplerDescriptor* descriptor) { 141 return Sampler::Create(this, descriptor); 142 } CreateShaderModuleImpl(const ShaderModuleDescriptor * descriptor,ShaderModuleParseResult * parseResult)143 ResultOrError<Ref<ShaderModuleBase>> Device::CreateShaderModuleImpl( 144 const ShaderModuleDescriptor* descriptor, 145 ShaderModuleParseResult* parseResult) { 146 return ShaderModule::Create(this, descriptor, parseResult); 147 } CreateSwapChainImpl(const SwapChainDescriptor * descriptor)148 ResultOrError<Ref<SwapChainBase>> Device::CreateSwapChainImpl( 149 const SwapChainDescriptor* descriptor) { 150 return OldSwapChain::Create(this, descriptor); 151 } CreateSwapChainImpl(Surface * surface,NewSwapChainBase * previousSwapChain,const SwapChainDescriptor * descriptor)152 ResultOrError<Ref<NewSwapChainBase>> Device::CreateSwapChainImpl( 153 Surface* surface, 154 NewSwapChainBase* previousSwapChain, 155 const SwapChainDescriptor* descriptor) { 156 return SwapChain::Create(this, surface, previousSwapChain, descriptor); 157 } CreateTextureImpl(const TextureDescriptor * descriptor)158 ResultOrError<Ref<TextureBase>> Device::CreateTextureImpl(const TextureDescriptor* descriptor) { 159 return Texture::Create(this, descriptor); 160 } CreateTextureViewImpl(TextureBase * texture,const TextureViewDescriptor * descriptor)161 ResultOrError<Ref<TextureViewBase>> Device::CreateTextureViewImpl( 162 TextureBase* texture, 163 const TextureViewDescriptor* descriptor) { 164 return TextureView::Create(texture, descriptor); 165 } InitializeComputePipelineAsyncImpl(Ref<ComputePipelineBase> computePipeline,WGPUCreateComputePipelineAsyncCallback callback,void * userdata)166 void Device::InitializeComputePipelineAsyncImpl(Ref<ComputePipelineBase> computePipeline, 167 WGPUCreateComputePipelineAsyncCallback callback, 168 void* userdata) { 169 ComputePipeline::InitializeAsync(std::move(computePipeline), callback, userdata); 170 } InitializeRenderPipelineAsyncImpl(Ref<RenderPipelineBase> renderPipeline,WGPUCreateRenderPipelineAsyncCallback callback,void * userdata)171 void Device::InitializeRenderPipelineAsyncImpl(Ref<RenderPipelineBase> renderPipeline, 172 WGPUCreateRenderPipelineAsyncCallback callback, 173 void* userdata) { 174 RenderPipeline::InitializeAsync(std::move(renderPipeline), callback, userdata); 175 } 176 TickImpl()177 MaybeError Device::TickImpl() { 178 RecycleCompletedCommands(); 179 180 ExecutionSerial completedSerial = GetCompletedCommandSerial(); 181 182 for (Ref<DescriptorSetAllocator>& allocator : 183 mDescriptorAllocatorsPendingDeallocation.IterateUpTo(completedSerial)) { 184 allocator->FinishDeallocation(completedSerial); 185 } 186 187 mResourceMemoryAllocator->Tick(completedSerial); 188 mDeleter->Tick(completedSerial); 189 mDescriptorAllocatorsPendingDeallocation.ClearUpTo(completedSerial); 190 191 if (mRecordingContext.used) { 192 DAWN_TRY(SubmitPendingCommands()); 193 } 194 195 return {}; 196 } 197 GetVkInstance() const198 VkInstance Device::GetVkInstance() const { 199 return ToBackend(GetAdapter())->GetVulkanInstance()->GetVkInstance(); 200 } GetDeviceInfo() const201 const VulkanDeviceInfo& Device::GetDeviceInfo() const { 202 return mDeviceInfo; 203 } 204 GetGlobalInfo() const205 const VulkanGlobalInfo& Device::GetGlobalInfo() const { 206 return ToBackend(GetAdapter())->GetVulkanInstance()->GetGlobalInfo(); 207 } 208 GetVkDevice() const209 VkDevice Device::GetVkDevice() const { 210 return mVkDevice; 211 } 212 GetGraphicsQueueFamily() const213 uint32_t Device::GetGraphicsQueueFamily() const { 214 return mQueueFamily; 215 } 216 GetQueue() const217 VkQueue Device::GetQueue() const { 218 return mQueue; 219 } 220 GetFencedDeleter() const221 FencedDeleter* Device::GetFencedDeleter() const { 222 return mDeleter.get(); 223 } 224 GetRenderPassCache() const225 RenderPassCache* Device::GetRenderPassCache() const { 226 return mRenderPassCache.get(); 227 } 228 GetResourceMemoryAllocator() const229 ResourceMemoryAllocator* Device::GetResourceMemoryAllocator() const { 230 return mResourceMemoryAllocator.get(); 231 } 232 EnqueueDeferredDeallocation(DescriptorSetAllocator * allocator)233 void Device::EnqueueDeferredDeallocation(DescriptorSetAllocator* allocator) { 234 mDescriptorAllocatorsPendingDeallocation.Enqueue(allocator, GetPendingCommandSerial()); 235 } 236 GetPendingRecordingContext()237 CommandRecordingContext* Device::GetPendingRecordingContext() { 238 ASSERT(mRecordingContext.commandBuffer != VK_NULL_HANDLE); 239 mRecordingContext.used = true; 240 return &mRecordingContext; 241 } 242 SubmitPendingCommands()243 MaybeError Device::SubmitPendingCommands() { 244 if (!mRecordingContext.used) { 245 return {}; 246 } 247 248 DAWN_TRY(CheckVkSuccess(fn.EndCommandBuffer(mRecordingContext.commandBuffer), 249 "vkEndCommandBuffer")); 250 251 std::vector<VkPipelineStageFlags> dstStageMasks(mRecordingContext.waitSemaphores.size(), 252 VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); 253 254 VkSubmitInfo submitInfo; 255 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; 256 submitInfo.pNext = nullptr; 257 submitInfo.waitSemaphoreCount = 258 static_cast<uint32_t>(mRecordingContext.waitSemaphores.size()); 259 submitInfo.pWaitSemaphores = AsVkArray(mRecordingContext.waitSemaphores.data()); 260 submitInfo.pWaitDstStageMask = dstStageMasks.data(); 261 submitInfo.commandBufferCount = 1; 262 submitInfo.pCommandBuffers = &mRecordingContext.commandBuffer; 263 submitInfo.signalSemaphoreCount = 264 static_cast<uint32_t>(mRecordingContext.signalSemaphores.size()); 265 submitInfo.pSignalSemaphores = AsVkArray(mRecordingContext.signalSemaphores.data()); 266 267 VkFence fence = VK_NULL_HANDLE; 268 DAWN_TRY_ASSIGN(fence, GetUnusedFence()); 269 DAWN_TRY_WITH_CLEANUP( 270 CheckVkSuccess(fn.QueueSubmit(mQueue, 1, &submitInfo, fence), "vkQueueSubmit"), { 271 // If submitting to the queue fails, move the fence back into the unused fence 272 // list, as if it were never acquired. Not doing so would leak the fence since 273 // it would be neither in the unused list nor in the in-flight list. 274 mUnusedFences.push_back(fence); 275 }); 276 277 // Enqueue the semaphores before incrementing the serial, so that they can be deleted as 278 // soon as the current submission is finished. 279 for (VkSemaphore semaphore : mRecordingContext.waitSemaphores) { 280 mDeleter->DeleteWhenUnused(semaphore); 281 } 282 for (VkSemaphore semaphore : mRecordingContext.signalSemaphores) { 283 mDeleter->DeleteWhenUnused(semaphore); 284 } 285 286 IncrementLastSubmittedCommandSerial(); 287 ExecutionSerial lastSubmittedSerial = GetLastSubmittedCommandSerial(); 288 mFencesInFlight.emplace(fence, lastSubmittedSerial); 289 290 CommandPoolAndBuffer submittedCommands = {mRecordingContext.commandPool, 291 mRecordingContext.commandBuffer}; 292 mCommandsInFlight.Enqueue(submittedCommands, lastSubmittedSerial); 293 mRecordingContext = CommandRecordingContext(); 294 DAWN_TRY(PrepareRecordingContext()); 295 296 return {}; 297 } 298 CreateDevice(VkPhysicalDevice physicalDevice)299 ResultOrError<VulkanDeviceKnobs> Device::CreateDevice(VkPhysicalDevice physicalDevice) { 300 VulkanDeviceKnobs usedKnobs = {}; 301 302 // Default to asking for all avilable known extensions. 303 usedKnobs.extensions = mDeviceInfo.extensions; 304 305 // However only request the extensions that haven't been promoted in the device's apiVersion 306 std::vector<const char*> extensionNames; 307 for (DeviceExt ext : IterateBitSet(usedKnobs.extensions)) { 308 const DeviceExtInfo& info = GetDeviceExtInfo(ext); 309 310 if (info.versionPromoted > mDeviceInfo.properties.apiVersion) { 311 extensionNames.push_back(info.name); 312 } 313 } 314 315 // Some device features can only be enabled using a VkPhysicalDeviceFeatures2 struct, which 316 // is supported by the VK_EXT_get_physical_properties2 instance extension, which was 317 // promoted as a core API in Vulkan 1.1. 318 // 319 // Prepare a VkPhysicalDeviceFeatures2 struct for this use case, it will only be populated 320 // if HasExt(DeviceExt::GetPhysicalDeviceProperties2) is true. 321 VkPhysicalDeviceFeatures2 features2 = {}; 322 features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; 323 PNextChainBuilder featuresChain(&features2); 324 325 // Required for core WebGPU features. 326 usedKnobs.features.depthBiasClamp = VK_TRUE; 327 usedKnobs.features.fragmentStoresAndAtomics = VK_TRUE; 328 usedKnobs.features.fullDrawIndexUint32 = VK_TRUE; 329 usedKnobs.features.imageCubeArray = VK_TRUE; 330 usedKnobs.features.independentBlend = VK_TRUE; 331 usedKnobs.features.sampleRateShading = VK_TRUE; 332 333 if (IsRobustnessEnabled()) { 334 usedKnobs.features.robustBufferAccess = VK_TRUE; 335 } 336 337 if (mDeviceInfo.HasExt(DeviceExt::SubgroupSizeControl)) { 338 ASSERT(usedKnobs.HasExt(DeviceExt::SubgroupSizeControl)); 339 340 // Always request all the features from VK_EXT_subgroup_size_control when available. 341 usedKnobs.subgroupSizeControlFeatures = mDeviceInfo.subgroupSizeControlFeatures; 342 featuresChain.Add(&usedKnobs.subgroupSizeControlFeatures); 343 344 mComputeSubgroupSize = FindComputeSubgroupSize(); 345 } 346 347 if (mDeviceInfo.features.samplerAnisotropy == VK_TRUE) { 348 usedKnobs.features.samplerAnisotropy = VK_TRUE; 349 } 350 351 if (IsFeatureEnabled(Feature::TextureCompressionBC)) { 352 ASSERT(ToBackend(GetAdapter())->GetDeviceInfo().features.textureCompressionBC == 353 VK_TRUE); 354 usedKnobs.features.textureCompressionBC = VK_TRUE; 355 } 356 357 if (IsFeatureEnabled(Feature::TextureCompressionETC2)) { 358 ASSERT(ToBackend(GetAdapter())->GetDeviceInfo().features.textureCompressionETC2 == 359 VK_TRUE); 360 usedKnobs.features.textureCompressionETC2 = VK_TRUE; 361 } 362 363 if (IsFeatureEnabled(Feature::TextureCompressionASTC)) { 364 ASSERT(ToBackend(GetAdapter())->GetDeviceInfo().features.textureCompressionASTC_LDR == 365 VK_TRUE); 366 usedKnobs.features.textureCompressionASTC_LDR = VK_TRUE; 367 } 368 369 if (IsFeatureEnabled(Feature::PipelineStatisticsQuery)) { 370 ASSERT(ToBackend(GetAdapter())->GetDeviceInfo().features.pipelineStatisticsQuery == 371 VK_TRUE); 372 usedKnobs.features.pipelineStatisticsQuery = VK_TRUE; 373 } 374 375 if (IsFeatureEnabled(Feature::ShaderFloat16)) { 376 const VulkanDeviceInfo& deviceInfo = ToBackend(GetAdapter())->GetDeviceInfo(); 377 ASSERT(deviceInfo.HasExt(DeviceExt::ShaderFloat16Int8) && 378 deviceInfo.shaderFloat16Int8Features.shaderFloat16 == VK_TRUE && 379 deviceInfo.HasExt(DeviceExt::_16BitStorage) && 380 deviceInfo._16BitStorageFeatures.storageBuffer16BitAccess == VK_TRUE && 381 deviceInfo._16BitStorageFeatures.uniformAndStorageBuffer16BitAccess == VK_TRUE); 382 383 usedKnobs.shaderFloat16Int8Features.shaderFloat16 = VK_TRUE; 384 usedKnobs._16BitStorageFeatures.storageBuffer16BitAccess = VK_TRUE; 385 usedKnobs._16BitStorageFeatures.uniformAndStorageBuffer16BitAccess = VK_TRUE; 386 387 featuresChain.Add(&usedKnobs.shaderFloat16Int8Features, 388 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR); 389 featuresChain.Add(&usedKnobs._16BitStorageFeatures, 390 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES); 391 } 392 393 if (IsFeatureEnabled(Feature::DepthClamping)) { 394 ASSERT(ToBackend(GetAdapter())->GetDeviceInfo().features.depthClamp == VK_TRUE); 395 usedKnobs.features.depthClamp = VK_TRUE; 396 } 397 398 // Find a universal queue family 399 { 400 // Note that GRAPHICS and COMPUTE imply TRANSFER so we don't need to check for it. 401 constexpr uint32_t kUniversalFlags = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT; 402 int universalQueueFamily = -1; 403 for (unsigned int i = 0; i < mDeviceInfo.queueFamilies.size(); ++i) { 404 if ((mDeviceInfo.queueFamilies[i].queueFlags & kUniversalFlags) == 405 kUniversalFlags) { 406 universalQueueFamily = i; 407 break; 408 } 409 } 410 411 if (universalQueueFamily == -1) { 412 return DAWN_INTERNAL_ERROR("No universal queue family"); 413 } 414 mQueueFamily = static_cast<uint32_t>(universalQueueFamily); 415 } 416 417 // Choose to create a single universal queue 418 std::vector<VkDeviceQueueCreateInfo> queuesToRequest; 419 float zero = 0.0f; 420 { 421 VkDeviceQueueCreateInfo queueCreateInfo; 422 queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; 423 queueCreateInfo.pNext = nullptr; 424 queueCreateInfo.flags = 0; 425 queueCreateInfo.queueFamilyIndex = static_cast<uint32_t>(mQueueFamily); 426 queueCreateInfo.queueCount = 1; 427 queueCreateInfo.pQueuePriorities = &zero; 428 429 queuesToRequest.push_back(queueCreateInfo); 430 } 431 432 VkDeviceCreateInfo createInfo; 433 createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; 434 createInfo.pNext = nullptr; 435 createInfo.flags = 0; 436 createInfo.queueCreateInfoCount = static_cast<uint32_t>(queuesToRequest.size()); 437 createInfo.pQueueCreateInfos = queuesToRequest.data(); 438 createInfo.enabledLayerCount = 0; 439 createInfo.ppEnabledLayerNames = nullptr; 440 createInfo.enabledExtensionCount = static_cast<uint32_t>(extensionNames.size()); 441 createInfo.ppEnabledExtensionNames = extensionNames.data(); 442 443 // When we have DeviceExt::GetPhysicalDeviceProperties2, use features2 so that features not 444 // covered by VkPhysicalDeviceFeatures can be enabled. 445 if (mDeviceInfo.HasExt(DeviceExt::GetPhysicalDeviceProperties2)) { 446 features2.features = usedKnobs.features; 447 createInfo.pNext = &features2; 448 createInfo.pEnabledFeatures = nullptr; 449 } else { 450 ASSERT(features2.pNext == nullptr); 451 createInfo.pEnabledFeatures = &usedKnobs.features; 452 } 453 454 DAWN_TRY(CheckVkSuccess(fn.CreateDevice(physicalDevice, &createInfo, nullptr, &mVkDevice), 455 "vkCreateDevice")); 456 457 return usedKnobs; 458 } 459 FindComputeSubgroupSize() const460 uint32_t Device::FindComputeSubgroupSize() const { 461 if (!mDeviceInfo.HasExt(DeviceExt::SubgroupSizeControl)) { 462 return 0; 463 } 464 465 const VkPhysicalDeviceSubgroupSizeControlPropertiesEXT& ext = 466 mDeviceInfo.subgroupSizeControlProperties; 467 468 if (ext.minSubgroupSize == ext.maxSubgroupSize) { 469 return 0; 470 } 471 472 // At the moment, only Intel devices support varying subgroup sizes and 16, which is the 473 // next value after the minimum of 8, is the sweet spot according to [1]. Hence the 474 // following heuristics, which may need to be adjusted in the future for other 475 // architectures, or if a specific API is added to let client code select the size.. 476 // 477 // [1] https://bugs.freedesktop.org/show_bug.cgi?id=108875 478 uint32_t subgroupSize = ext.minSubgroupSize * 2; 479 if (subgroupSize <= ext.maxSubgroupSize) { 480 return subgroupSize; 481 } else { 482 return ext.minSubgroupSize; 483 } 484 } 485 GatherQueueFromDevice()486 void Device::GatherQueueFromDevice() { 487 fn.GetDeviceQueue(mVkDevice, mQueueFamily, 0, &mQueue); 488 } 489 InitTogglesFromDriver()490 void Device::InitTogglesFromDriver() { 491 // TODO(crbug.com/dawn/857): tighten this workaround when this issue is fixed in both 492 // Vulkan SPEC and drivers. 493 SetToggle(Toggle::UseTemporaryBufferInCompressedTextureToTextureCopy, true); 494 495 // By default try to use D32S8 for Depth24PlusStencil8 496 SetToggle(Toggle::VulkanUseD32S8, true); 497 } 498 ApplyDepth24PlusS8Toggle()499 void Device::ApplyDepth24PlusS8Toggle() { 500 bool supportsD32s8 = 501 ToBackend(GetAdapter())->IsDepthStencilFormatSupported(VK_FORMAT_D32_SFLOAT_S8_UINT); 502 bool supportsD24s8 = 503 ToBackend(GetAdapter())->IsDepthStencilFormatSupported(VK_FORMAT_D24_UNORM_S8_UINT); 504 505 ASSERT(supportsD32s8 || supportsD24s8); 506 507 if (!supportsD24s8) { 508 ForceSetToggle(Toggle::VulkanUseD32S8, true); 509 } 510 if (!supportsD32s8) { 511 ForceSetToggle(Toggle::VulkanUseD32S8, false); 512 } 513 } 514 GetMutableFunctions()515 VulkanFunctions* Device::GetMutableFunctions() { 516 return const_cast<VulkanFunctions*>(&fn); 517 } 518 GetUnusedFence()519 ResultOrError<VkFence> Device::GetUnusedFence() { 520 if (!mUnusedFences.empty()) { 521 VkFence fence = mUnusedFences.back(); 522 DAWN_TRY(CheckVkSuccess(fn.ResetFences(mVkDevice, 1, &*fence), "vkResetFences")); 523 524 mUnusedFences.pop_back(); 525 return fence; 526 } 527 528 VkFenceCreateInfo createInfo; 529 createInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; 530 createInfo.pNext = nullptr; 531 createInfo.flags = 0; 532 533 VkFence fence = VK_NULL_HANDLE; 534 DAWN_TRY(CheckVkSuccess(fn.CreateFence(mVkDevice, &createInfo, nullptr, &*fence), 535 "vkCreateFence")); 536 537 return fence; 538 } 539 CheckAndUpdateCompletedSerials()540 ResultOrError<ExecutionSerial> Device::CheckAndUpdateCompletedSerials() { 541 ExecutionSerial fenceSerial(0); 542 while (!mFencesInFlight.empty()) { 543 VkFence fence = mFencesInFlight.front().first; 544 ExecutionSerial tentativeSerial = mFencesInFlight.front().second; 545 VkResult result = VkResult::WrapUnsafe( 546 INJECT_ERROR_OR_RUN(fn.GetFenceStatus(mVkDevice, fence), VK_ERROR_DEVICE_LOST)); 547 548 // Fence are added in order, so we can stop searching as soon 549 // as we see one that's not ready. 550 if (result == VK_NOT_READY) { 551 return fenceSerial; 552 } else { 553 DAWN_TRY(CheckVkSuccess(::VkResult(result), "GetFenceStatus")); 554 } 555 556 // Update fenceSerial since fence is ready. 557 fenceSerial = tentativeSerial; 558 559 mUnusedFences.push_back(fence); 560 561 ASSERT(fenceSerial > GetCompletedCommandSerial()); 562 mFencesInFlight.pop(); 563 } 564 return fenceSerial; 565 } 566 PrepareRecordingContext()567 MaybeError Device::PrepareRecordingContext() { 568 ASSERT(!mRecordingContext.used); 569 ASSERT(mRecordingContext.commandBuffer == VK_NULL_HANDLE); 570 ASSERT(mRecordingContext.commandPool == VK_NULL_HANDLE); 571 572 // First try to recycle unused command pools. 573 if (!mUnusedCommands.empty()) { 574 CommandPoolAndBuffer commands = mUnusedCommands.back(); 575 mUnusedCommands.pop_back(); 576 DAWN_TRY_WITH_CLEANUP(CheckVkSuccess(fn.ResetCommandPool(mVkDevice, commands.pool, 0), 577 "vkResetCommandPool"), 578 { 579 // vkResetCommandPool failed (it may return out-of-memory). 580 // Free the commands in the cleanup step before returning to 581 // reclaim memory. 582 583 // The VkCommandBuffer memory should be wholly owned by the 584 // pool and freed when it is destroyed, but that's not the 585 // case in some drivers and they leak memory. So we call 586 // FreeCommandBuffers before DestroyCommandPool to be safe. 587 // TODO(enga): Only do this on a known list of bad drivers. 588 fn.FreeCommandBuffers(mVkDevice, commands.pool, 1, 589 &commands.commandBuffer); 590 fn.DestroyCommandPool(mVkDevice, commands.pool, nullptr); 591 }); 592 593 mRecordingContext.commandBuffer = commands.commandBuffer; 594 mRecordingContext.commandPool = commands.pool; 595 } else { 596 // Create a new command pool for our commands and allocate the command buffer. 597 VkCommandPoolCreateInfo createInfo; 598 createInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; 599 createInfo.pNext = nullptr; 600 createInfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; 601 createInfo.queueFamilyIndex = mQueueFamily; 602 603 DAWN_TRY(CheckVkSuccess(fn.CreateCommandPool(mVkDevice, &createInfo, nullptr, 604 &*mRecordingContext.commandPool), 605 "vkCreateCommandPool")); 606 607 VkCommandBufferAllocateInfo allocateInfo; 608 allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; 609 allocateInfo.pNext = nullptr; 610 allocateInfo.commandPool = mRecordingContext.commandPool; 611 allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; 612 allocateInfo.commandBufferCount = 1; 613 614 DAWN_TRY(CheckVkSuccess(fn.AllocateCommandBuffers(mVkDevice, &allocateInfo, 615 &mRecordingContext.commandBuffer), 616 "vkAllocateCommandBuffers")); 617 } 618 619 // Start the recording of commands in the command buffer. 620 VkCommandBufferBeginInfo beginInfo; 621 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; 622 beginInfo.pNext = nullptr; 623 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; 624 beginInfo.pInheritanceInfo = nullptr; 625 626 return CheckVkSuccess(fn.BeginCommandBuffer(mRecordingContext.commandBuffer, &beginInfo), 627 "vkBeginCommandBuffer"); 628 } 629 RecycleCompletedCommands()630 void Device::RecycleCompletedCommands() { 631 for (auto& commands : mCommandsInFlight.IterateUpTo(GetCompletedCommandSerial())) { 632 mUnusedCommands.push_back(commands); 633 } 634 mCommandsInFlight.ClearUpTo(GetCompletedCommandSerial()); 635 } 636 CreateStagingBuffer(size_t size)637 ResultOrError<std::unique_ptr<StagingBufferBase>> Device::CreateStagingBuffer(size_t size) { 638 std::unique_ptr<StagingBufferBase> stagingBuffer = 639 std::make_unique<StagingBuffer>(size, this); 640 DAWN_TRY(stagingBuffer->Initialize()); 641 return std::move(stagingBuffer); 642 } 643 CopyFromStagingToBuffer(StagingBufferBase * source,uint64_t sourceOffset,BufferBase * destination,uint64_t destinationOffset,uint64_t size)644 MaybeError Device::CopyFromStagingToBuffer(StagingBufferBase* source, 645 uint64_t sourceOffset, 646 BufferBase* destination, 647 uint64_t destinationOffset, 648 uint64_t size) { 649 // It is a validation error to do a 0-sized copy in Vulkan, check it is skipped prior to 650 // calling this function. 651 ASSERT(size != 0); 652 653 CommandRecordingContext* recordingContext = GetPendingRecordingContext(); 654 655 ToBackend(destination) 656 ->EnsureDataInitializedAsDestination(recordingContext, destinationOffset, size); 657 658 // There is no need of a barrier to make host writes available and visible to the copy 659 // operation for HOST_COHERENT memory. The Vulkan spec for vkQueueSubmit describes that it 660 // does an implicit availability, visibility and domain operation. 661 662 // Insert pipeline barrier to ensure correct ordering with previous memory operations on the 663 // buffer. 664 ToBackend(destination)->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopyDst); 665 666 VkBufferCopy copy; 667 copy.srcOffset = sourceOffset; 668 copy.dstOffset = destinationOffset; 669 copy.size = size; 670 671 this->fn.CmdCopyBuffer(recordingContext->commandBuffer, 672 ToBackend(source)->GetBufferHandle(), 673 ToBackend(destination)->GetHandle(), 1, ©); 674 675 return {}; 676 } 677 CopyFromStagingToTexture(const StagingBufferBase * source,const TextureDataLayout & src,TextureCopy * dst,const Extent3D & copySizePixels)678 MaybeError Device::CopyFromStagingToTexture(const StagingBufferBase* source, 679 const TextureDataLayout& src, 680 TextureCopy* dst, 681 const Extent3D& copySizePixels) { 682 // There is no need of a barrier to make host writes available and visible to the copy 683 // operation for HOST_COHERENT memory. The Vulkan spec for vkQueueSubmit describes that it 684 // does an implicit availability, visibility and domain operation. 685 686 CommandRecordingContext* recordingContext = GetPendingRecordingContext(); 687 688 VkBufferImageCopy region = ComputeBufferImageCopyRegion(src, *dst, copySizePixels); 689 VkImageSubresourceLayers subresource = region.imageSubresource; 690 691 ASSERT(dst->texture->GetDimension() != wgpu::TextureDimension::e1D); 692 SubresourceRange range = GetSubresourcesAffectedByCopy(*dst, copySizePixels); 693 694 if (IsCompleteSubresourceCopiedTo(dst->texture.Get(), copySizePixels, 695 subresource.mipLevel)) { 696 // Since texture has been overwritten, it has been "initialized" 697 dst->texture->SetIsSubresourceContentInitialized(true, range); 698 } else { 699 ToBackend(dst->texture)->EnsureSubresourceContentInitialized(recordingContext, range); 700 } 701 // Insert pipeline barrier to ensure correct ordering with previous memory operations on the 702 // texture. 703 ToBackend(dst->texture) 704 ->TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopyDst, range); 705 VkImage dstImage = ToBackend(dst->texture)->GetHandle(); 706 707 // Dawn guarantees dstImage be in the TRANSFER_DST_OPTIMAL layout after the 708 // copy command. 709 this->fn.CmdCopyBufferToImage(recordingContext->commandBuffer, 710 ToBackend(source)->GetBufferHandle(), dstImage, 711 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); 712 return {}; 713 } 714 ImportExternalImage(const ExternalImageDescriptorVk * descriptor,ExternalMemoryHandle memoryHandle,VkImage image,const std::vector<ExternalSemaphoreHandle> & waitHandles,VkSemaphore * outSignalSemaphore,VkDeviceMemory * outAllocation,std::vector<VkSemaphore> * outWaitSemaphores)715 MaybeError Device::ImportExternalImage(const ExternalImageDescriptorVk* descriptor, 716 ExternalMemoryHandle memoryHandle, 717 VkImage image, 718 const std::vector<ExternalSemaphoreHandle>& waitHandles, 719 VkSemaphore* outSignalSemaphore, 720 VkDeviceMemory* outAllocation, 721 std::vector<VkSemaphore>* outWaitSemaphores) { 722 const TextureDescriptor* textureDescriptor = FromAPI(descriptor->cTextureDescriptor); 723 724 const DawnTextureInternalUsageDescriptor* internalUsageDesc = nullptr; 725 FindInChain(textureDescriptor->nextInChain, &internalUsageDesc); 726 727 wgpu::TextureUsage usage = textureDescriptor->usage; 728 if (internalUsageDesc != nullptr) { 729 usage |= internalUsageDesc->internalUsage; 730 } 731 732 // Check services support this combination of handle type / image info 733 DAWN_INVALID_IF(!mExternalSemaphoreService->Supported(), 734 "External semaphore usage not supported"); 735 736 DAWN_INVALID_IF( 737 !mExternalMemoryService->SupportsImportMemory( 738 VulkanImageFormat(this, textureDescriptor->format), VK_IMAGE_TYPE_2D, 739 VK_IMAGE_TILING_OPTIMAL, 740 VulkanImageUsage(usage, GetValidInternalFormat(textureDescriptor->format)), 741 VK_IMAGE_CREATE_ALIAS_BIT_KHR), 742 "External memory usage not supported"); 743 744 // Create an external semaphore to signal when the texture is done being used 745 DAWN_TRY_ASSIGN(*outSignalSemaphore, 746 mExternalSemaphoreService->CreateExportableSemaphore()); 747 748 // Import the external image's memory 749 external_memory::MemoryImportParams importParams; 750 DAWN_TRY_ASSIGN(importParams, 751 mExternalMemoryService->GetMemoryImportParams(descriptor, image)); 752 DAWN_TRY_ASSIGN(*outAllocation, 753 mExternalMemoryService->ImportMemory(memoryHandle, importParams, image)); 754 755 // Import semaphores we have to wait on before using the texture 756 for (const ExternalSemaphoreHandle& handle : waitHandles) { 757 VkSemaphore semaphore = VK_NULL_HANDLE; 758 DAWN_TRY_ASSIGN(semaphore, mExternalSemaphoreService->ImportSemaphore(handle)); 759 outWaitSemaphores->push_back(semaphore); 760 } 761 762 return {}; 763 } 764 SignalAndExportExternalTexture(Texture * texture,VkImageLayout desiredLayout,ExternalImageExportInfoVk * info,std::vector<ExternalSemaphoreHandle> * semaphoreHandles)765 bool Device::SignalAndExportExternalTexture( 766 Texture* texture, 767 VkImageLayout desiredLayout, 768 ExternalImageExportInfoVk* info, 769 std::vector<ExternalSemaphoreHandle>* semaphoreHandles) { 770 return !ConsumedError([&]() -> MaybeError { 771 DAWN_TRY(ValidateObject(texture)); 772 773 VkSemaphore signalSemaphore; 774 VkImageLayout releasedOldLayout; 775 VkImageLayout releasedNewLayout; 776 DAWN_TRY(texture->ExportExternalTexture(desiredLayout, &signalSemaphore, 777 &releasedOldLayout, &releasedNewLayout)); 778 779 ExternalSemaphoreHandle semaphoreHandle; 780 DAWN_TRY_ASSIGN(semaphoreHandle, 781 mExternalSemaphoreService->ExportSemaphore(signalSemaphore)); 782 semaphoreHandles->push_back(semaphoreHandle); 783 info->releasedOldLayout = releasedOldLayout; 784 info->releasedNewLayout = releasedNewLayout; 785 info->isInitialized = 786 texture->IsSubresourceContentInitialized(texture->GetAllSubresources()); 787 788 return {}; 789 }()); 790 } 791 CreateTextureWrappingVulkanImage(const ExternalImageDescriptorVk * descriptor,ExternalMemoryHandle memoryHandle,const std::vector<ExternalSemaphoreHandle> & waitHandles)792 TextureBase* Device::CreateTextureWrappingVulkanImage( 793 const ExternalImageDescriptorVk* descriptor, 794 ExternalMemoryHandle memoryHandle, 795 const std::vector<ExternalSemaphoreHandle>& waitHandles) { 796 const TextureDescriptor* textureDescriptor = FromAPI(descriptor->cTextureDescriptor); 797 798 // Initial validation 799 if (ConsumedError(ValidateTextureDescriptor(this, textureDescriptor))) { 800 return nullptr; 801 } 802 if (ConsumedError(ValidateVulkanImageCanBeWrapped(this, textureDescriptor), 803 "validating that a Vulkan image can be wrapped with %s.", 804 textureDescriptor)) { 805 return nullptr; 806 } 807 808 VkSemaphore signalSemaphore = VK_NULL_HANDLE; 809 VkDeviceMemory allocation = VK_NULL_HANDLE; 810 std::vector<VkSemaphore> waitSemaphores; 811 waitSemaphores.reserve(waitHandles.size()); 812 813 // Cleanup in case of a failure, the image creation doesn't acquire the external objects 814 // if a failure happems. 815 Texture* result = nullptr; 816 // TODO(crbug.com/1026480): Consolidate this into a single CreateFromExternal call. 817 if (ConsumedError(Texture::CreateFromExternal(this, descriptor, textureDescriptor, 818 mExternalMemoryService.get()), 819 &result) || 820 ConsumedError(ImportExternalImage(descriptor, memoryHandle, result->GetHandle(), 821 waitHandles, &signalSemaphore, &allocation, 822 &waitSemaphores)) || 823 ConsumedError(result->BindExternalMemory(descriptor, signalSemaphore, allocation, 824 waitSemaphores))) { 825 // Delete the Texture if it was created 826 if (result != nullptr) { 827 result->Release(); 828 } 829 830 // Clear the signal semaphore 831 fn.DestroySemaphore(GetVkDevice(), signalSemaphore, nullptr); 832 833 // Clear image memory 834 fn.FreeMemory(GetVkDevice(), allocation, nullptr); 835 836 // Clear any wait semaphores we were able to import 837 for (VkSemaphore semaphore : waitSemaphores) { 838 fn.DestroySemaphore(GetVkDevice(), semaphore, nullptr); 839 } 840 return nullptr; 841 } 842 843 return result; 844 } 845 GetComputeSubgroupSize() const846 uint32_t Device::GetComputeSubgroupSize() const { 847 return mComputeSubgroupSize; 848 } 849 WaitForIdleForDestruction()850 MaybeError Device::WaitForIdleForDestruction() { 851 // Immediately tag the recording context as unused so we don't try to submit it in Tick. 852 // Move the mRecordingContext.used to mUnusedCommands so it can be cleaned up in 853 // ShutDownImpl 854 if (mRecordingContext.used) { 855 CommandPoolAndBuffer commands = {mRecordingContext.commandPool, 856 mRecordingContext.commandBuffer}; 857 mUnusedCommands.push_back(commands); 858 mRecordingContext = CommandRecordingContext(); 859 } 860 861 VkResult waitIdleResult = VkResult::WrapUnsafe(fn.QueueWaitIdle(mQueue)); 862 // Ignore the result of QueueWaitIdle: it can return OOM which we can't really do anything 863 // about, Device lost, which means workloads running on the GPU are no longer accessible 864 // (so they are as good as waited on) or success. 865 DAWN_UNUSED(waitIdleResult); 866 867 // Make sure all fences are complete by explicitly waiting on them all 868 while (!mFencesInFlight.empty()) { 869 VkFence fence = mFencesInFlight.front().first; 870 ExecutionSerial fenceSerial = mFencesInFlight.front().second; 871 ASSERT(fenceSerial > GetCompletedCommandSerial()); 872 873 VkResult result = VkResult::WrapUnsafe(VK_TIMEOUT); 874 do { 875 // If WaitForIdleForDesctruction is called while we are Disconnected, it means that 876 // the device lost came from the ErrorInjector and we need to wait without allowing 877 // any more error to be injected. This is because the device lost was "fake" and 878 // commands might still be running. 879 if (GetState() == State::Disconnected) { 880 result = VkResult::WrapUnsafe( 881 fn.WaitForFences(mVkDevice, 1, &*fence, true, UINT64_MAX)); 882 continue; 883 } 884 885 result = VkResult::WrapUnsafe( 886 INJECT_ERROR_OR_RUN(fn.WaitForFences(mVkDevice, 1, &*fence, true, UINT64_MAX), 887 VK_ERROR_DEVICE_LOST)); 888 } while (result == VK_TIMEOUT); 889 // Ignore errors from vkWaitForFences: it can be either OOM which we can't do anything 890 // about (and we need to keep going with the destruction of all fences), or device 891 // loss, which means the workload on the GPU is no longer accessible and we can 892 // safely destroy the fence. 893 894 fn.DestroyFence(mVkDevice, fence, nullptr); 895 mFencesInFlight.pop(); 896 } 897 return {}; 898 } 899 DestroyImpl()900 void Device::DestroyImpl() { 901 ASSERT(GetState() == State::Disconnected); 902 903 // We failed during initialization so early that we don't even have a VkDevice. There is 904 // nothing to do. 905 if (mVkDevice == VK_NULL_HANDLE) { 906 return; 907 } 908 909 // The deleter is the second thing we initialize. If it is not present, it means that 910 // only the VkDevice was created and nothing else. Destroy the device and do nothing else 911 // because the function pointers might not have been loaded (and there is nothing to 912 // destroy anyway). 913 if (mDeleter == nullptr) { 914 fn.DestroyDevice(mVkDevice, nullptr); 915 mVkDevice = VK_NULL_HANDLE; 916 return; 917 } 918 919 // Enough of the Device's initialization happened that we can now do regular robust 920 // deinitialization. 921 922 // Immediately tag the recording context as unused so we don't try to submit it in Tick. 923 mRecordingContext.used = false; 924 if (mRecordingContext.commandPool != VK_NULL_HANDLE) { 925 // The VkCommandBuffer memory should be wholly owned by the pool and freed when it is 926 // destroyed, but that's not the case in some drivers and the leak memory. 927 // So we call FreeCommandBuffers before DestroyCommandPool to be safe. 928 // TODO(enga): Only do this on a known list of bad drivers. 929 fn.FreeCommandBuffers(mVkDevice, mRecordingContext.commandPool, 1, 930 &mRecordingContext.commandBuffer); 931 fn.DestroyCommandPool(mVkDevice, mRecordingContext.commandPool, nullptr); 932 } 933 934 for (VkSemaphore semaphore : mRecordingContext.waitSemaphores) { 935 fn.DestroySemaphore(mVkDevice, semaphore, nullptr); 936 } 937 mRecordingContext.waitSemaphores.clear(); 938 939 for (VkSemaphore semaphore : mRecordingContext.signalSemaphores) { 940 fn.DestroySemaphore(mVkDevice, semaphore, nullptr); 941 } 942 mRecordingContext.signalSemaphores.clear(); 943 944 // Some commands might still be marked as in-flight if we shut down because of a device 945 // loss. Recycle them as unused so that we free them below. 946 RecycleCompletedCommands(); 947 ASSERT(mCommandsInFlight.Empty()); 948 949 for (const CommandPoolAndBuffer& commands : mUnusedCommands) { 950 // The VkCommandBuffer memory should be wholly owned by the pool and freed when it is 951 // destroyed, but that's not the case in some drivers and the leak memory. 952 // So we call FreeCommandBuffers before DestroyCommandPool to be safe. 953 // TODO(enga): Only do this on a known list of bad drivers. 954 fn.FreeCommandBuffers(mVkDevice, commands.pool, 1, &commands.commandBuffer); 955 fn.DestroyCommandPool(mVkDevice, commands.pool, nullptr); 956 } 957 mUnusedCommands.clear(); 958 959 // Some fences might still be marked as in-flight if we shut down because of a device loss. 960 // Delete them since at this point all commands are complete. 961 while (!mFencesInFlight.empty()) { 962 fn.DestroyFence(mVkDevice, *mFencesInFlight.front().first, nullptr); 963 mFencesInFlight.pop(); 964 } 965 966 for (VkFence fence : mUnusedFences) { 967 fn.DestroyFence(mVkDevice, fence, nullptr); 968 } 969 mUnusedFences.clear(); 970 971 ExecutionSerial completedSerial = GetCompletedCommandSerial(); 972 for (Ref<DescriptorSetAllocator>& allocator : 973 mDescriptorAllocatorsPendingDeallocation.IterateUpTo(completedSerial)) { 974 allocator->FinishDeallocation(completedSerial); 975 } 976 977 // Releasing the uploader enqueues buffers to be released. 978 // Call Tick() again to clear them before releasing the deleter. 979 mResourceMemoryAllocator->Tick(completedSerial); 980 mDeleter->Tick(completedSerial); 981 mDescriptorAllocatorsPendingDeallocation.ClearUpTo(completedSerial); 982 983 // Allow recycled memory to be deleted. 984 mResourceMemoryAllocator->DestroyPool(); 985 986 // The VkRenderPasses in the cache can be destroyed immediately since all commands referring 987 // to them are guaranteed to be finished executing. 988 mRenderPassCache = nullptr; 989 990 // We need handle deleting all child objects by calling Tick() again with a large serial to 991 // force all operations to look as if they were completed, and delete all objects before 992 // destroying the Deleter and vkDevice. 993 ASSERT(mDeleter != nullptr); 994 mDeleter->Tick(kMaxExecutionSerial); 995 mDeleter = nullptr; 996 997 // VkQueues are destroyed when the VkDevice is destroyed 998 // The VkDevice is needed to destroy child objects, so it must be destroyed last after all 999 // child objects have been deleted. 1000 ASSERT(mVkDevice != VK_NULL_HANDLE); 1001 fn.DestroyDevice(mVkDevice, nullptr); 1002 mVkDevice = VK_NULL_HANDLE; 1003 } 1004 GetOptimalBytesPerRowAlignment() const1005 uint32_t Device::GetOptimalBytesPerRowAlignment() const { 1006 return mDeviceInfo.properties.limits.optimalBufferCopyRowPitchAlignment; 1007 } 1008 GetOptimalBufferToTextureCopyOffsetAlignment() const1009 uint64_t Device::GetOptimalBufferToTextureCopyOffsetAlignment() const { 1010 return mDeviceInfo.properties.limits.optimalBufferCopyOffsetAlignment; 1011 } 1012 GetTimestampPeriodInNS() const1013 float Device::GetTimestampPeriodInNS() const { 1014 return mDeviceInfo.properties.limits.timestampPeriod; 1015 } 1016 1017 }} // namespace dawn_native::vulkan 1018