Support ErrorScopes for asynchronous GPU execution
This changes updates ErrorScopes so that scopes enclosing a Queue::Submit or Queue::Signal resolve their callbacks asynchronously after GPU execution is complete. Bug: dawn:153 Change-Id: I0e0b8a9f19f3f29d1b6a3683938154b87f190a07 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/10701 Reviewed-by: Kai Ninomiya <kainino@chromium.org> Reviewed-by: Corentin Wallez <cwallez@chromium.org> Commit-Queue: Austin Eng <enga@chromium.org>
This commit is contained in:
parent
bb3c895619
commit
be990077f4
3
BUILD.gn
3
BUILD.gn
|
@ -168,6 +168,8 @@ source_set("libdawn_native_sources") {
|
||||||
"src/dawn_native/ErrorData.h",
|
"src/dawn_native/ErrorData.h",
|
||||||
"src/dawn_native/ErrorScope.cpp",
|
"src/dawn_native/ErrorScope.cpp",
|
||||||
"src/dawn_native/ErrorScope.h",
|
"src/dawn_native/ErrorScope.h",
|
||||||
|
"src/dawn_native/ErrorScopeTracker.cpp",
|
||||||
|
"src/dawn_native/ErrorScopeTracker.h",
|
||||||
"src/dawn_native/Extensions.cpp",
|
"src/dawn_native/Extensions.cpp",
|
||||||
"src/dawn_native/Extensions.h",
|
"src/dawn_native/Extensions.h",
|
||||||
"src/dawn_native/Fence.cpp",
|
"src/dawn_native/Fence.cpp",
|
||||||
|
@ -595,7 +597,6 @@ if (is_win || (is_linux && !is_chromeos) || is_mac) {
|
||||||
# Allow inclusion of <GLFW/glfw3.h>
|
# Allow inclusion of <GLFW/glfw3.h>
|
||||||
include_dirs = [ "${dawn_glfw_dir}/include" ]
|
include_dirs = [ "${dawn_glfw_dir}/include" ]
|
||||||
|
|
||||||
|
|
||||||
# The GLFW/glfw3.h header includes <GL/gl.h> by default, but the latter
|
# The GLFW/glfw3.h header includes <GL/gl.h> by default, but the latter
|
||||||
# does not exist on Fuchsia. Defining GLFW_INCLUDE_NONE helps work around
|
# does not exist on Fuchsia. Defining GLFW_INCLUDE_NONE helps work around
|
||||||
# the issue, but it needs to be defined for any file that includes the
|
# the issue, but it needs to be defined for any file that includes the
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "dawn_native/DynamicUploader.h"
|
#include "dawn_native/DynamicUploader.h"
|
||||||
#include "dawn_native/ErrorData.h"
|
#include "dawn_native/ErrorData.h"
|
||||||
#include "dawn_native/ErrorScope.h"
|
#include "dawn_native/ErrorScope.h"
|
||||||
|
#include "dawn_native/ErrorScopeTracker.h"
|
||||||
#include "dawn_native/Fence.h"
|
#include "dawn_native/Fence.h"
|
||||||
#include "dawn_native/FenceSignalTracker.h"
|
#include "dawn_native/FenceSignalTracker.h"
|
||||||
#include "dawn_native/Instance.h"
|
#include "dawn_native/Instance.h"
|
||||||
|
@ -67,6 +68,7 @@ namespace dawn_native {
|
||||||
mRootErrorScope(AcquireRef(new ErrorScope())),
|
mRootErrorScope(AcquireRef(new ErrorScope())),
|
||||||
mCurrentErrorScope(mRootErrorScope.Get()) {
|
mCurrentErrorScope(mRootErrorScope.Get()) {
|
||||||
mCaches = std::make_unique<DeviceBase::Caches>();
|
mCaches = std::make_unique<DeviceBase::Caches>();
|
||||||
|
mErrorScopeTracker = std::make_unique<ErrorScopeTracker>(this);
|
||||||
mFenceSignalTracker = std::make_unique<FenceSignalTracker>(this);
|
mFenceSignalTracker = std::make_unique<FenceSignalTracker>(this);
|
||||||
mDynamicUploader = std::make_unique<DynamicUploader>(this);
|
mDynamicUploader = std::make_unique<DynamicUploader>(this);
|
||||||
SetDefaultToggles();
|
SetDefaultToggles();
|
||||||
|
@ -121,6 +123,11 @@ namespace dawn_native {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ErrorScope* DeviceBase::GetCurrentErrorScope() {
|
||||||
|
ASSERT(mCurrentErrorScope.Get() != nullptr);
|
||||||
|
return mCurrentErrorScope.Get();
|
||||||
|
}
|
||||||
|
|
||||||
MaybeError DeviceBase::ValidateObject(const ObjectBase* object) const {
|
MaybeError DeviceBase::ValidateObject(const ObjectBase* object) const {
|
||||||
if (DAWN_UNLIKELY(object->GetDevice() != this)) {
|
if (DAWN_UNLIKELY(object->GetDevice() != this)) {
|
||||||
return DAWN_VALIDATION_ERROR("Object from a different device.");
|
return DAWN_VALIDATION_ERROR("Object from a different device.");
|
||||||
|
@ -139,6 +146,10 @@ namespace dawn_native {
|
||||||
return GetAdapter()->GetInstance()->GetPlatform();
|
return GetAdapter()->GetInstance()->GetPlatform();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ErrorScopeTracker* DeviceBase::GetErrorScopeTracker() const {
|
||||||
|
return mErrorScopeTracker.get();
|
||||||
|
}
|
||||||
|
|
||||||
FenceSignalTracker* DeviceBase::GetFenceSignalTracker() const {
|
FenceSignalTracker* DeviceBase::GetFenceSignalTracker() const {
|
||||||
return mFenceSignalTracker.get();
|
return mFenceSignalTracker.get();
|
||||||
}
|
}
|
||||||
|
@ -519,6 +530,7 @@ namespace dawn_native {
|
||||||
deferred.callback(deferred.status, deferred.result, deferred.userdata);
|
deferred.callback(deferred.status, deferred.result, deferred.userdata);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mErrorScopeTracker->Tick(GetCompletedCommandSerial());
|
||||||
mFenceSignalTracker->Tick(GetCompletedCommandSerial());
|
mFenceSignalTracker->Tick(GetCompletedCommandSerial());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ namespace dawn_native {
|
||||||
class AttachmentState;
|
class AttachmentState;
|
||||||
class AttachmentStateBlueprint;
|
class AttachmentStateBlueprint;
|
||||||
class ErrorScope;
|
class ErrorScope;
|
||||||
|
class ErrorScopeTracker;
|
||||||
class FenceSignalTracker;
|
class FenceSignalTracker;
|
||||||
class DynamicUploader;
|
class DynamicUploader;
|
||||||
class StagingBufferBase;
|
class StagingBufferBase;
|
||||||
|
@ -61,6 +62,7 @@ namespace dawn_native {
|
||||||
AdapterBase* GetAdapter() const;
|
AdapterBase* GetAdapter() const;
|
||||||
dawn_platform::Platform* GetPlatform() const;
|
dawn_platform::Platform* GetPlatform() const;
|
||||||
|
|
||||||
|
ErrorScopeTracker* GetErrorScopeTracker() const;
|
||||||
FenceSignalTracker* GetFenceSignalTracker() const;
|
FenceSignalTracker* GetFenceSignalTracker() const;
|
||||||
|
|
||||||
// Returns the Format corresponding to the dawn::TextureFormat or an error if the format
|
// Returns the Format corresponding to the dawn::TextureFormat or an error if the format
|
||||||
|
@ -153,6 +155,7 @@ namespace dawn_native {
|
||||||
void SetUncapturedErrorCallback(dawn::ErrorCallback callback, void* userdata);
|
void SetUncapturedErrorCallback(dawn::ErrorCallback callback, void* userdata);
|
||||||
void PushErrorScope(dawn::ErrorFilter filter);
|
void PushErrorScope(dawn::ErrorFilter filter);
|
||||||
bool PopErrorScope(dawn::ErrorCallback callback, void* userdata);
|
bool PopErrorScope(dawn::ErrorCallback callback, void* userdata);
|
||||||
|
ErrorScope* GetCurrentErrorScope();
|
||||||
|
|
||||||
void Reference();
|
void Reference();
|
||||||
void Release();
|
void Release();
|
||||||
|
@ -251,6 +254,7 @@ namespace dawn_native {
|
||||||
void* userdata;
|
void* userdata;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<ErrorScopeTracker> mErrorScopeTracker;
|
||||||
std::unique_ptr<FenceSignalTracker> mFenceSignalTracker;
|
std::unique_ptr<FenceSignalTracker> mFenceSignalTracker;
|
||||||
std::vector<DeferredCreateBufferMappedAsync> mDeferredCreateBufferMappedAsyncResults;
|
std::vector<DeferredCreateBufferMappedAsync> mDeferredCreateBufferMappedAsyncResults;
|
||||||
|
|
||||||
|
|
|
@ -111,4 +111,11 @@ namespace dawn_native {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ErrorScope::Destroy() {
|
||||||
|
if (!IsRoot()) {
|
||||||
|
mErrorType = dawn::ErrorType::Unknown;
|
||||||
|
mErrorMessage = "Error scope destroyed";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace dawn_native
|
} // namespace dawn_native
|
||||||
|
|
|
@ -49,6 +49,8 @@ namespace dawn_native {
|
||||||
void HandleError(dawn::ErrorType type, const char* message);
|
void HandleError(dawn::ErrorType type, const char* message);
|
||||||
void HandleError(ErrorData* error);
|
void HandleError(ErrorData* error);
|
||||||
|
|
||||||
|
void Destroy();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool IsRoot() const;
|
bool IsRoot() const;
|
||||||
static void HandleErrorImpl(ErrorScope* scope, dawn::ErrorType type, const char* message);
|
static void HandleErrorImpl(ErrorScope* scope, dawn::ErrorType type, const char* message);
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
// Copyright 2019 The Dawn Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "dawn_native/ErrorScopeTracker.h"
|
||||||
|
|
||||||
|
#include "dawn_native/Device.h"
|
||||||
|
#include "dawn_native/ErrorScope.h"
|
||||||
|
|
||||||
|
namespace dawn_native {
|
||||||
|
|
||||||
|
ErrorScopeTracker::ErrorScopeTracker(DeviceBase* device) : mDevice(device) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorScopeTracker::~ErrorScopeTracker() {
|
||||||
|
// The tracker is destroyed when the Device is destroyed. We need to
|
||||||
|
// call Destroy on all in-flight error scopes so they resolve their callbacks
|
||||||
|
// with UNKNOWN.
|
||||||
|
constexpr Serial maxSerial = std::numeric_limits<Serial>::max();
|
||||||
|
for (Ref<ErrorScope>& scope : mScopesInFlight.IterateUpTo(maxSerial)) {
|
||||||
|
scope->Destroy();
|
||||||
|
}
|
||||||
|
Tick(maxSerial);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ErrorScopeTracker::TrackUntilLastSubmitComplete(ErrorScope* scope) {
|
||||||
|
mScopesInFlight.Enqueue(scope, mDevice->GetLastSubmittedCommandSerial());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ErrorScopeTracker::Tick(Serial completedSerial) {
|
||||||
|
mScopesInFlight.ClearUpTo(completedSerial);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dawn_native
|
|
@ -0,0 +1,42 @@
|
||||||
|
// Copyright 2019 The Dawn Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#ifndef DAWNNATIVE_ERRORSCOPETRACKER_H_
|
||||||
|
#define DAWNNATIVE_ERRORSCOPETRACKER_H_
|
||||||
|
|
||||||
|
#include "common/SerialQueue.h"
|
||||||
|
#include "dawn_native/RefCounted.h"
|
||||||
|
|
||||||
|
namespace dawn_native {
|
||||||
|
|
||||||
|
class DeviceBase;
|
||||||
|
class ErrorScope;
|
||||||
|
|
||||||
|
class ErrorScopeTracker {
|
||||||
|
public:
|
||||||
|
ErrorScopeTracker(DeviceBase* device);
|
||||||
|
~ErrorScopeTracker();
|
||||||
|
|
||||||
|
void TrackUntilLastSubmitComplete(ErrorScope* scope);
|
||||||
|
|
||||||
|
void Tick(Serial completedSerial);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
DeviceBase* mDevice;
|
||||||
|
SerialQueue<Ref<ErrorScope>> mScopesInFlight;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dawn_native
|
||||||
|
|
||||||
|
#endif // DAWNNATIVE_ERRORSCOPETRACKER_H_
|
|
@ -17,6 +17,8 @@
|
||||||
#include "dawn_native/Buffer.h"
|
#include "dawn_native/Buffer.h"
|
||||||
#include "dawn_native/CommandBuffer.h"
|
#include "dawn_native/CommandBuffer.h"
|
||||||
#include "dawn_native/Device.h"
|
#include "dawn_native/Device.h"
|
||||||
|
#include "dawn_native/ErrorScope.h"
|
||||||
|
#include "dawn_native/ErrorScopeTracker.h"
|
||||||
#include "dawn_native/Fence.h"
|
#include "dawn_native/Fence.h"
|
||||||
#include "dawn_native/FenceSignalTracker.h"
|
#include "dawn_native/FenceSignalTracker.h"
|
||||||
#include "dawn_native/Texture.h"
|
#include "dawn_native/Texture.h"
|
||||||
|
@ -30,24 +32,29 @@ namespace dawn_native {
|
||||||
}
|
}
|
||||||
|
|
||||||
void QueueBase::Submit(uint32_t commandCount, CommandBufferBase* const* commands) {
|
void QueueBase::Submit(uint32_t commandCount, CommandBufferBase* const* commands) {
|
||||||
TRACE_EVENT0(GetDevice()->GetPlatform(), TRACE_DISABLED_BY_DEFAULT("gpu.dawn"),
|
DeviceBase* device = GetDevice();
|
||||||
"Queue::Submit");
|
TRACE_EVENT0(device->GetPlatform(), TRACE_DISABLED_BY_DEFAULT("gpu.dawn"), "Queue::Submit");
|
||||||
if (GetDevice()->ConsumedError(ValidateSubmit(commandCount, commands))) {
|
if (device->ConsumedError(ValidateSubmit(commandCount, commands))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ASSERT(!IsError());
|
ASSERT(!IsError());
|
||||||
|
|
||||||
SubmitImpl(commandCount, commands);
|
SubmitImpl(commandCount, commands);
|
||||||
|
device->GetErrorScopeTracker()->TrackUntilLastSubmitComplete(
|
||||||
|
device->GetCurrentErrorScope());
|
||||||
}
|
}
|
||||||
|
|
||||||
void QueueBase::Signal(FenceBase* fence, uint64_t signalValue) {
|
void QueueBase::Signal(FenceBase* fence, uint64_t signalValue) {
|
||||||
if (GetDevice()->ConsumedError(ValidateSignal(fence, signalValue))) {
|
DeviceBase* device = GetDevice();
|
||||||
|
if (device->ConsumedError(ValidateSignal(fence, signalValue))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ASSERT(!IsError());
|
ASSERT(!IsError());
|
||||||
|
|
||||||
fence->SetSignaledValue(signalValue);
|
fence->SetSignaledValue(signalValue);
|
||||||
GetDevice()->GetFenceSignalTracker()->UpdateFenceOnComplete(fence, signalValue);
|
device->GetFenceSignalTracker()->UpdateFenceOnComplete(fence, signalValue);
|
||||||
|
device->GetErrorScopeTracker()->TrackUntilLastSubmitComplete(
|
||||||
|
device->GetCurrentErrorScope());
|
||||||
}
|
}
|
||||||
|
|
||||||
FenceBase* QueueBase::CreateFence(const FenceDescriptor* descriptor) {
|
FenceBase* QueueBase::CreateFence(const FenceDescriptor* descriptor) {
|
||||||
|
|
|
@ -132,3 +132,68 @@ TEST_F(ErrorScopeValidationTest, PushPopBalanced) {
|
||||||
EXPECT_FALSE(device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this + 2));
|
EXPECT_FALSE(device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this + 2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test that error scopes do not call their callbacks until after an enclosed Queue::Submit
|
||||||
|
// completes
|
||||||
|
TEST_F(ErrorScopeValidationTest, CallbackAfterQueueSubmit) {
|
||||||
|
dawn::Queue queue = device.CreateQueue();
|
||||||
|
|
||||||
|
device.PushErrorScope(dawn::ErrorFilter::OutOfMemory);
|
||||||
|
queue.Submit(0, nullptr);
|
||||||
|
device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this);
|
||||||
|
|
||||||
|
EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(DAWN_ERROR_TYPE_NO_ERROR, _, this)).Times(1);
|
||||||
|
|
||||||
|
// Side effects of Queue::Submit only are seen after Tick()
|
||||||
|
device.Tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that parent error scopes do not call their callbacks until after an enclosed Queue::Submit
|
||||||
|
// completes
|
||||||
|
TEST_F(ErrorScopeValidationTest, CallbackAfterQueueSubmitNested) {
|
||||||
|
dawn::Queue queue = device.CreateQueue();
|
||||||
|
|
||||||
|
device.PushErrorScope(dawn::ErrorFilter::OutOfMemory);
|
||||||
|
device.PushErrorScope(dawn::ErrorFilter::OutOfMemory);
|
||||||
|
queue.Submit(0, nullptr);
|
||||||
|
device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this);
|
||||||
|
device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this + 1);
|
||||||
|
|
||||||
|
EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(DAWN_ERROR_TYPE_NO_ERROR, _, this)).Times(1);
|
||||||
|
EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(DAWN_ERROR_TYPE_NO_ERROR, _, this + 1))
|
||||||
|
.Times(1);
|
||||||
|
|
||||||
|
// Side effects of Queue::Submit only are seen after Tick()
|
||||||
|
device.Tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test a callback that returns asynchronously followed by a synchronous one
|
||||||
|
TEST_F(ErrorScopeValidationTest, AsynchronousThenSynchronous) {
|
||||||
|
dawn::Queue queue = device.CreateQueue();
|
||||||
|
|
||||||
|
device.PushErrorScope(dawn::ErrorFilter::OutOfMemory);
|
||||||
|
queue.Submit(0, nullptr);
|
||||||
|
device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this);
|
||||||
|
|
||||||
|
EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(DAWN_ERROR_TYPE_NO_ERROR, _, this + 1))
|
||||||
|
.Times(1);
|
||||||
|
device.PushErrorScope(dawn::ErrorFilter::OutOfMemory);
|
||||||
|
device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this + 1);
|
||||||
|
|
||||||
|
EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(DAWN_ERROR_TYPE_NO_ERROR, _, this)).Times(1);
|
||||||
|
|
||||||
|
// Side effects of Queue::Submit only are seen after Tick()
|
||||||
|
device.Tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that if the device is destroyed before the callback occurs, it is called with UNKNOWN.
|
||||||
|
TEST_F(ErrorScopeValidationTest, DeviceDestroyedBeforeCallback) {
|
||||||
|
dawn::Queue queue = device.CreateQueue();
|
||||||
|
|
||||||
|
device.PushErrorScope(dawn::ErrorFilter::OutOfMemory);
|
||||||
|
queue.Submit(0, nullptr);
|
||||||
|
device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this);
|
||||||
|
|
||||||
|
EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(DAWN_ERROR_TYPE_UNKNOWN, _, this)).Times(1);
|
||||||
|
device = nullptr;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue