• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &copy);
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, &region);
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