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:
Austin Eng 2019-09-17 18:24:07 +00:00 committed by Commit Bot service account
parent bb3c895619
commit be990077f4
9 changed files with 190 additions and 6 deletions

View File

@ -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

View File

@ -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());
} }

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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_

View File

@ -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) {

View File

@ -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;
}