mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-07-14 17:16:01 +00:00
Reject callbacks if the device is destroyed before completion
Callbacks should all reject instead of waiting for the device to idle on shutdown. Bug: dawn:652 Change-Id: Id4a9ab2560aa34b8ea574271f61f8a499e15ab3a Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/40360 Reviewed-by: Stephen White <senorblanco@chromium.org> Reviewed-by: Corentin Wallez <cwallez@chromium.org> Commit-Queue: Austin Eng <enga@chromium.org>
This commit is contained in:
parent
bdbf98afca
commit
a75b230acf
@ -144,6 +144,15 @@ namespace dawn_native {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DeviceBase::ShutDownBase() {
|
void DeviceBase::ShutDownBase() {
|
||||||
|
// Skip handling device facilities if they haven't even been created (or failed doing so)
|
||||||
|
if (mState != State::BeingCreated) {
|
||||||
|
// Reject all error scope callbacks.
|
||||||
|
mErrorScopeTracker->ClearForShutDown();
|
||||||
|
|
||||||
|
// Reject all async pipeline creations.
|
||||||
|
mCreateReadyPipelineTracker->ClearForShutDown();
|
||||||
|
}
|
||||||
|
|
||||||
// Disconnect the device, depending on which state we are currently in.
|
// Disconnect the device, depending on which state we are currently in.
|
||||||
switch (mState) {
|
switch (mState) {
|
||||||
case State::BeingCreated:
|
case State::BeingCreated:
|
||||||
@ -171,18 +180,13 @@ namespace dawn_native {
|
|||||||
ASSERT(mCompletedSerial == mLastSubmittedSerial);
|
ASSERT(mCompletedSerial == mLastSubmittedSerial);
|
||||||
ASSERT(mFutureSerial <= mCompletedSerial);
|
ASSERT(mFutureSerial <= mCompletedSerial);
|
||||||
|
|
||||||
// Skip handling device facilities if they haven't even been created (or failed doing so)
|
|
||||||
if (mState != State::BeingCreated) {
|
if (mState != State::BeingCreated) {
|
||||||
// The GPU timeline is finished so all services can be freed immediately. They need to
|
// The GPU timeline is finished.
|
||||||
// be freed before ShutDownImpl() because they might relinquish resources that will be
|
// Tick the queue-related tasks since they should be complete. This must be done before
|
||||||
// freed by backends in the ShutDownImpl() call. Still tick the ones that might have
|
// ShutDownImpl() it may relinquish resources that will be freed by backends in the
|
||||||
// pending callbacks.
|
// ShutDownImpl() call.
|
||||||
mErrorScopeTracker->Tick(GetCompletedCommandSerial());
|
|
||||||
GetQueue()->Tick(GetCompletedCommandSerial());
|
GetQueue()->Tick(GetCompletedCommandSerial());
|
||||||
|
// Call TickImpl once last time to clean up resources
|
||||||
mCreateReadyPipelineTracker->ClearForShutDown();
|
|
||||||
|
|
||||||
// call TickImpl once last time to clean up resources
|
|
||||||
// Ignore errors so that we can continue with destruction
|
// Ignore errors so that we can continue with destruction
|
||||||
IgnoreErrors(TickImpl());
|
IgnoreErrors(TickImpl());
|
||||||
}
|
}
|
||||||
|
@ -25,13 +25,7 @@ namespace dawn_native {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ErrorScopeTracker::~ErrorScopeTracker() {
|
ErrorScopeTracker::~ErrorScopeTracker() {
|
||||||
// The tracker is destroyed when the Device is destroyed. We need to
|
ASSERT(mScopesInFlight.Empty());
|
||||||
// call Destroy on all in-flight error scopes so they resolve their callbacks
|
|
||||||
// with UNKNOWN.
|
|
||||||
for (Ref<ErrorScope>& scope : mScopesInFlight.IterateUpTo(kMaxExecutionSerial)) {
|
|
||||||
scope->UnlinkForShutdown();
|
|
||||||
}
|
|
||||||
Tick(kMaxExecutionSerial);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ErrorScopeTracker::TrackUntilLastSubmitComplete(ErrorScope* scope) {
|
void ErrorScopeTracker::TrackUntilLastSubmitComplete(ErrorScope* scope) {
|
||||||
@ -43,4 +37,11 @@ namespace dawn_native {
|
|||||||
mScopesInFlight.ClearUpTo(completedSerial);
|
mScopesInFlight.ClearUpTo(completedSerial);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ErrorScopeTracker::ClearForShutDown() {
|
||||||
|
for (Ref<ErrorScope>& scope : mScopesInFlight.IterateAll()) {
|
||||||
|
scope->UnlinkForShutdown();
|
||||||
|
}
|
||||||
|
mScopesInFlight.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace dawn_native
|
} // namespace dawn_native
|
||||||
|
@ -32,6 +32,7 @@ namespace dawn_native {
|
|||||||
void TrackUntilLastSubmitComplete(ErrorScope* scope);
|
void TrackUntilLastSubmitComplete(ErrorScope* scope);
|
||||||
|
|
||||||
void Tick(ExecutionSerial completedSerial);
|
void Tick(ExecutionSerial completedSerial);
|
||||||
|
void ClearForShutDown();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
DeviceBase* mDevice;
|
DeviceBase* mDevice;
|
||||||
|
@ -38,6 +38,10 @@ namespace dawn_wire { namespace client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Client::DoDeviceLostCallback(Device* device, char const* message) {
|
bool Client::DoDeviceLostCallback(Device* device, char const* message) {
|
||||||
|
if (device == nullptr) {
|
||||||
|
// The device might have been deleted or recreated so this isn't an error.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
device->HandleDeviceLost(message);
|
device->HandleDeviceLost(message);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -46,6 +50,10 @@ namespace dawn_wire { namespace client {
|
|||||||
uint64_t requestSerial,
|
uint64_t requestSerial,
|
||||||
WGPUErrorType errorType,
|
WGPUErrorType errorType,
|
||||||
const char* message) {
|
const char* message) {
|
||||||
|
if (device == nullptr) {
|
||||||
|
// The device might have been deleted or recreated so this isn't an error.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return device->OnPopErrorScopeCallback(requestSerial, errorType, message);
|
return device->OnPopErrorScopeCallback(requestSerial, errorType, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,16 +199,15 @@ TEST_F(ErrorScopeValidationTest, AsynchronousThenSynchronous) {
|
|||||||
// Test that if the device is destroyed before the callback occurs, it is called with NoError
|
// Test that if the device is destroyed before the callback occurs, it is called with NoError
|
||||||
// because all previous operations are waited upon before the destruction returns.
|
// because all previous operations are waited upon before the destruction returns.
|
||||||
TEST_F(ErrorScopeValidationTest, DeviceDestroyedBeforeCallback) {
|
TEST_F(ErrorScopeValidationTest, DeviceDestroyedBeforeCallback) {
|
||||||
// TODO(crbug.com/dawn/652): This has different behavior on the wire and should be consistent.
|
|
||||||
DAWN_SKIP_TEST_IF(UsesWire());
|
|
||||||
|
|
||||||
wgpu::Queue queue = device.GetQueue();
|
|
||||||
|
|
||||||
device.PushErrorScope(wgpu::ErrorFilter::OutOfMemory);
|
device.PushErrorScope(wgpu::ErrorFilter::OutOfMemory);
|
||||||
|
{
|
||||||
|
// Note: this is in its own scope to be clear the queue does not outlive the device.
|
||||||
|
wgpu::Queue queue = device.GetQueue();
|
||||||
queue.Submit(0, nullptr);
|
queue.Submit(0, nullptr);
|
||||||
|
}
|
||||||
device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this);
|
device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this);
|
||||||
|
|
||||||
EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(WGPUErrorType_NoError, _, this)).Times(1);
|
EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(WGPUErrorType_Unknown, _, this)).Times(1);
|
||||||
device = nullptr;
|
device = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user