This repository was archived by the owner on Feb 25, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Impeller] Test
FenceWaiterVK
and fix termination bugs (#45870)
Fixes flutter/flutter#134751, and @jonahwilliams suspects it could be related to a number of other flaky/texture leak scenarios (flutter/flutter#133506 (comment)) that only happen sometimes (i.e. on CI but not real devices), i.e. stuff like: ```txt --- Vulkan Debug Report  ---------------------------------------- |         Severity: Error |           Type: { Validation } |         ID Name: VUID-vkDestroyBuffer-buffer-00922 |        ID Number: -464217071 |    Queue Breadcrumbs: [NONE] |  CMD Buffer Breadcrumbs: [NONE] |     Related Objects: Device [94498356231456] [ImpellerDevice] |         Trigger: Validation Error: [ VUID-vkDestroyBuffer-buffer-00922 ] Object 0: handle = 0x55f21cf47d20, name = ImpellerDevice, type = VK_OBJECT_TYPE_DEVICE; | MessageID = 0xe4549c11 | Cannot call vkDestroyBuffer on VkBuffer 0xbb00000000bb[] that is currently in use by a command buffer. The Vulkan spec states: All submitted commands that refer to buffer, either directly or via a VkBufferView, must have completed execution (https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkDestroyBuffer-buffer-00922) ----------------------------------------------------------------- ``` --- ~~This PR will look a bit like a mess until the last 2 PRs merge in, but locally it appears to fix fence races/segfaults that I was seeing on CI, including on Linux with validations enabled. We can test it further tomorrow.~~ EDIT: Updated.
- Loading branch information
1 parent
94464bf
commit a0055e5
Showing
9 changed files
with
305 additions
and
77 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
130 changes: 130 additions & 0 deletions
130
impeller/renderer/backend/vulkan/fence_waiter_vk_unittests.cc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
// Copyright 2013 The Flutter Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#include "fml/synchronization/waitable_event.h" | ||
#include "gtest/gtest.h" // IWYU pragma: keep | ||
#include "impeller/renderer/backend/vulkan/fence_waiter_vk.h" // IWYU pragma: keep | ||
#include "impeller/renderer/backend/vulkan/test/mock_vulkan.h" | ||
|
||
namespace impeller { | ||
namespace testing { | ||
|
||
TEST(FenceWaiterVKTest, IgnoresNullFence) { | ||
auto const context = MockVulkanContextBuilder().Build(); | ||
auto const waiter = context->GetFenceWaiter(); | ||
EXPECT_FALSE(waiter->AddFence(vk::UniqueFence(), []() {})); | ||
} | ||
|
||
TEST(FenceWaiterVKTest, IgnoresNullCallback) { | ||
auto const context = MockVulkanContextBuilder().Build(); | ||
auto const device = context->GetDevice(); | ||
auto const waiter = context->GetFenceWaiter(); | ||
|
||
auto fence = device.createFenceUnique({}).value; | ||
EXPECT_FALSE(waiter->AddFence(std::move(fence), nullptr)); | ||
} | ||
|
||
TEST(FenceWaiterVKTest, ExecutesFenceCallback) { | ||
auto const context = MockVulkanContextBuilder().Build(); | ||
auto const device = context->GetDevice(); | ||
auto const waiter = context->GetFenceWaiter(); | ||
|
||
auto signal = fml::ManualResetWaitableEvent(); | ||
auto fence = device.createFenceUnique({}).value; | ||
waiter->AddFence(std::move(fence), [&signal]() { signal.Signal(); }); | ||
|
||
signal.Wait(); | ||
} | ||
|
||
TEST(FenceWaiterVKTest, ExecutesFenceCallbackX2) { | ||
auto const context = MockVulkanContextBuilder().Build(); | ||
auto const device = context->GetDevice(); | ||
auto const waiter = context->GetFenceWaiter(); | ||
|
||
auto signal = fml::ManualResetWaitableEvent(); | ||
auto fence = device.createFenceUnique({}).value; | ||
waiter->AddFence(std::move(fence), [&signal]() { signal.Signal(); }); | ||
|
||
auto signal2 = fml::ManualResetWaitableEvent(); | ||
auto fence2 = device.createFenceUnique({}).value; | ||
waiter->AddFence(std::move(fence2), [&signal2]() { signal2.Signal(); }); | ||
|
||
signal.Wait(); | ||
signal2.Wait(); | ||
} | ||
|
||
TEST(FenceWaiterVKTest, ExecutesNewFenceThenOldFence) { | ||
auto const context = MockVulkanContextBuilder().Build(); | ||
auto const device = context->GetDevice(); | ||
auto const waiter = context->GetFenceWaiter(); | ||
|
||
auto signal = fml::ManualResetWaitableEvent(); | ||
auto fence = device.createFenceUnique({}).value; | ||
MockFence::SetStatus(fence, vk::Result::eNotReady); | ||
auto raw_fence = MockFence::GetRawPointer(fence); | ||
waiter->AddFence(std::move(fence), [&signal]() { signal.Signal(); }); | ||
|
||
// The easiest way to verify that the callback was _not_ called is to wait | ||
// for a timeout, but that could introduce flakiness. Instead, we'll add a | ||
// second fence that will signal immediately, and wait for that one instead. | ||
{ | ||
auto signal2 = fml::ManualResetWaitableEvent(); | ||
auto fence2 = device.createFenceUnique({}).value; | ||
MockFence::SetStatus(fence2, vk::Result::eSuccess); | ||
waiter->AddFence(std::move(fence2), [&signal2]() { signal2.Signal(); }); | ||
signal2.Wait(); | ||
} | ||
|
||
// Now, we'll signal the first fence, and wait for the callback to be called. | ||
raw_fence->SetStatus(vk::Result::eSuccess); | ||
|
||
// Now, we'll signal the first fence, and wait for the callback to be called. | ||
signal.Wait(); | ||
} | ||
|
||
TEST(FenceWaiterVKTest, AddFenceDoesNothingIfTerminating) { | ||
auto signal = fml::ManualResetWaitableEvent(); | ||
|
||
{ | ||
auto const context = MockVulkanContextBuilder().Build(); | ||
auto const device = context->GetDevice(); | ||
auto const waiter = context->GetFenceWaiter(); | ||
waiter->Terminate(); | ||
|
||
auto fence = device.createFenceUnique({}).value; | ||
waiter->AddFence(std::move(fence), [&signal]() { signal.Signal(); }); | ||
} | ||
|
||
// Ensure the fence did _not_ signal. | ||
EXPECT_TRUE(signal.WaitWithTimeout(fml::TimeDelta::FromMilliseconds(100))); | ||
} | ||
|
||
TEST(FenceWaiterVKTest, InProgressFencesStillWaitIfTerminated) { | ||
MockFence* raw_fence = nullptr; | ||
auto signal = fml::ManualResetWaitableEvent(); | ||
|
||
auto const context = MockVulkanContextBuilder().Build(); | ||
auto const device = context->GetDevice(); | ||
auto const waiter = context->GetFenceWaiter(); | ||
|
||
// Add a fence that isn't signalled yet. | ||
auto fence = device.createFenceUnique({}).value; | ||
|
||
// Even if the fence is eSuccess, it's not guaranteed to be called in time. | ||
MockFence::SetStatus(fence, vk::Result::eNotReady); | ||
raw_fence = MockFence::GetRawPointer(fence); | ||
waiter->AddFence(std::move(fence), [&signal]() { signal.Signal(); }); | ||
|
||
// Terminate the waiter. | ||
waiter->Terminate(); | ||
|
||
// Signal the fence. | ||
raw_fence->SetStatus(vk::Result::eSuccess); | ||
|
||
// This will hang if the fence was not signalled. | ||
signal.Wait(); | ||
} | ||
|
||
} // namespace testing | ||
} // namespace impeller |
Oops, something went wrong.