Vulkan: Fix crashes on Device destruction if Device::Initialize fails

If Device creation fails, several things are just partially initialized
and the destroy sequence crashes dereferencing null data.

This commit marks the Vulkan device as lost until after it is created.
This avoids parts of the destroy sequence which are unecessary since
the Device was never successfully created and no commands are in flight.

Bug: chromium:1043095
Change-Id: I8e121709fa19b215e118a615b639380d1db1f3f2
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/15460
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Austin Eng 2020-01-25 09:35:50 +00:00 committed by Commit Bot service account
parent 13e2e139a5
commit 0df4753ba6
1 changed files with 16 additions and 3 deletions

View File

@ -50,6 +50,9 @@ namespace dawn_native { namespace vulkan {
if (descriptor != nullptr) { if (descriptor != nullptr) {
ApplyToggleOverrides(descriptor); ApplyToggleOverrides(descriptor);
} }
// Set the device as lost until successfully created.
mLossStatus = LossStatus::AlreadyLost;
} }
MaybeError Device::Initialize() { MaybeError Device::Initialize() {
@ -91,14 +94,22 @@ namespace dawn_native { namespace vulkan {
mDescriptorSetService = nullptr; mDescriptorSetService = nullptr;
// The frontend asserts DynamicUploader is destructed by the backend.
// It is usually destructed in Destroy(), but Destroy isn't always called if device
// initialization failed.
mDynamicUploader = nullptr;
// We still need to properly handle Vulkan object deletion even if the device has been lost, // We still need to properly handle Vulkan object deletion even if the device has been lost,
// so the Deleter and vkDevice cannot be destroyed in Device::Destroy(). // so the Deleter and vkDevice cannot be destroyed in Device::Destroy().
// We need handle deleting all child objects by calling Tick() again with a large serial to // We need handle deleting all child objects by calling Tick() again with a large serial to
// force all operations to look as if they were completed, and delete all objects before // force all operations to look as if they were completed, and delete all objects before
// destroying the Deleter and vkDevice. // destroying the Deleter and vkDevice.
mCompletedSerial = std::numeric_limits<Serial>::max(); // The Deleter may be null if initialization failed.
mDeleter->Tick(mCompletedSerial); if (mDeleter != nullptr) {
mDeleter = nullptr; mCompletedSerial = std::numeric_limits<Serial>::max();
mDeleter->Tick(mCompletedSerial);
mDeleter = nullptr;
}
// VkQueues are destroyed when the VkDevice is destroyed // VkQueues are destroyed when the VkDevice is destroyed
// The VkDevice is needed to destroy child objects, so it must be destroyed last after all // The VkDevice is needed to destroy child objects, so it must be destroyed last after all
@ -406,6 +417,8 @@ namespace dawn_native { namespace vulkan {
DAWN_TRY(CheckVkSuccess(fn.CreateDevice(physicalDevice, &createInfo, nullptr, &mVkDevice), DAWN_TRY(CheckVkSuccess(fn.CreateDevice(physicalDevice, &createInfo, nullptr, &mVkDevice),
"vkCreateDevice")); "vkCreateDevice"));
// Device created. Mark it as alive.
mLossStatus = LossStatus::Alive;
return usedKnobs; return usedKnobs;
} }