mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-07-03 03:35:59 +00:00
Handle DeviceLost error
Handle DeviceLostCallback once DeviceLost error occurs. Disallow any other commands or actions on device to happen after device has been lost. Bug: dawn:68 Change-Id: Icbbbadf278cae5e6213050d00439118789c863dc Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/12801 Commit-Queue: Natasha Lee <natlee@microsoft.com> Reviewed-by: Austin Eng <enga@chromium.org>
This commit is contained in:
parent
0c66bcd13a
commit
0ecc48ecb7
2
BUILD.gn
2
BUILD.gn
@ -204,6 +204,7 @@ source_set("libdawn_native_sources") {
|
|||||||
"src/dawn_native/DynamicUploader.h",
|
"src/dawn_native/DynamicUploader.h",
|
||||||
"src/dawn_native/EncodingContext.cpp",
|
"src/dawn_native/EncodingContext.cpp",
|
||||||
"src/dawn_native/EncodingContext.h",
|
"src/dawn_native/EncodingContext.h",
|
||||||
|
"src/dawn_native/Error.cpp",
|
||||||
"src/dawn_native/Error.h",
|
"src/dawn_native/Error.h",
|
||||||
"src/dawn_native/ErrorData.cpp",
|
"src/dawn_native/ErrorData.cpp",
|
||||||
"src/dawn_native/ErrorData.h",
|
"src/dawn_native/ErrorData.h",
|
||||||
@ -916,6 +917,7 @@ source_set("dawn_end2end_tests_sources") {
|
|||||||
"src/tests/end2end/DebugMarkerTests.cpp",
|
"src/tests/end2end/DebugMarkerTests.cpp",
|
||||||
"src/tests/end2end/DepthStencilStateTests.cpp",
|
"src/tests/end2end/DepthStencilStateTests.cpp",
|
||||||
"src/tests/end2end/DestroyTests.cpp",
|
"src/tests/end2end/DestroyTests.cpp",
|
||||||
|
"src/tests/end2end/DeviceLostTests.cpp",
|
||||||
"src/tests/end2end/DrawIndexedIndirectTests.cpp",
|
"src/tests/end2end/DrawIndexedIndirectTests.cpp",
|
||||||
"src/tests/end2end/DrawIndexedTests.cpp",
|
"src/tests/end2end/DrawIndexedTests.cpp",
|
||||||
"src/tests/end2end/DrawIndirectTests.cpp",
|
"src/tests/end2end/DrawIndirectTests.cpp",
|
||||||
|
@ -611,6 +611,9 @@
|
|||||||
],
|
],
|
||||||
"TODO": "enga@: Make this a Dawn extension"
|
"TODO": "enga@: Make this a Dawn extension"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "lose for testing"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "tick"
|
"name": "tick"
|
||||||
},
|
},
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include "common/Assert.h"
|
#include "common/Assert.h"
|
||||||
#include "dawn_native/Device.h"
|
#include "dawn_native/Device.h"
|
||||||
#include "dawn_native/DynamicUploader.h"
|
#include "dawn_native/DynamicUploader.h"
|
||||||
|
#include "dawn_native/ErrorData.h"
|
||||||
#include "dawn_native/ValidationUtils_autogen.h"
|
#include "dawn_native/ValidationUtils_autogen.h"
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
@ -350,6 +351,7 @@ namespace dawn_native {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MaybeError BufferBase::ValidateSetSubData(uint32_t start, uint32_t count) const {
|
MaybeError BufferBase::ValidateSetSubData(uint32_t start, uint32_t count) const {
|
||||||
|
DAWN_TRY(GetDevice()->ValidateIsAlive());
|
||||||
DAWN_TRY(GetDevice()->ValidateObject(this));
|
DAWN_TRY(GetDevice()->ValidateObject(this));
|
||||||
|
|
||||||
switch (mState) {
|
switch (mState) {
|
||||||
@ -388,6 +390,7 @@ namespace dawn_native {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MaybeError BufferBase::ValidateMap(wgpu::BufferUsage requiredUsage) const {
|
MaybeError BufferBase::ValidateMap(wgpu::BufferUsage requiredUsage) const {
|
||||||
|
DAWN_TRY(GetDevice()->ValidateIsAlive());
|
||||||
DAWN_TRY(GetDevice()->ValidateObject(this));
|
DAWN_TRY(GetDevice()->ValidateObject(this));
|
||||||
|
|
||||||
switch (mState) {
|
switch (mState) {
|
||||||
@ -407,6 +410,7 @@ namespace dawn_native {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MaybeError BufferBase::ValidateUnmap() const {
|
MaybeError BufferBase::ValidateUnmap() const {
|
||||||
|
DAWN_TRY(GetDevice()->ValidateIsAlive());
|
||||||
DAWN_TRY(GetDevice()->ValidateObject(this));
|
DAWN_TRY(GetDevice()->ValidateObject(this));
|
||||||
|
|
||||||
switch (mState) {
|
switch (mState) {
|
||||||
|
@ -95,15 +95,20 @@ namespace dawn_native {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DeviceBase::BaseDestructor() {
|
void DeviceBase::BaseDestructor() {
|
||||||
MaybeError err = WaitForIdleForDestruction();
|
if (mLossStatus != LossStatus::Alive) {
|
||||||
if (err.IsError()) {
|
return;
|
||||||
// Assert that errors are device loss so that we can continue with destruction
|
|
||||||
ASSERT(err.AcquireError()->GetType() == wgpu::ErrorType::DeviceLost);
|
|
||||||
}
|
}
|
||||||
|
// Assert that errors are device loss so that we can continue with destruction
|
||||||
|
AssertAndIgnoreDeviceLossError(WaitForIdleForDestruction());
|
||||||
Destroy();
|
Destroy();
|
||||||
|
mLossStatus = LossStatus::AlreadyLost;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeviceBase::HandleError(wgpu::ErrorType type, const char* message) {
|
void DeviceBase::HandleError(wgpu::ErrorType type, const char* message) {
|
||||||
|
if (type == wgpu::ErrorType::DeviceLost) {
|
||||||
|
HandleLoss(message);
|
||||||
|
}
|
||||||
|
// Still forward device loss to error scope so it can reject them all
|
||||||
mCurrentErrorScope->HandleError(type, message);
|
mCurrentErrorScope->HandleError(type, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,6 +170,33 @@ namespace dawn_native {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MaybeError DeviceBase::ValidateIsAlive() const {
|
||||||
|
if (DAWN_LIKELY(mLossStatus == LossStatus::Alive)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return DAWN_DEVICE_LOST_ERROR("Device is lost");
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceBase::HandleLoss(const char* message) {
|
||||||
|
if (mLossStatus == LossStatus::AlreadyLost) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Destroy();
|
||||||
|
mLossStatus = LossStatus::AlreadyLost;
|
||||||
|
|
||||||
|
if (mDeviceLostCallback) {
|
||||||
|
mDeviceLostCallback(message, mDeviceLostUserdata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceBase::LoseForTesting() {
|
||||||
|
mLossStatus = LossStatus::BeingLost;
|
||||||
|
// Assert that errors are device loss so that we can continue with destruction
|
||||||
|
AssertAndIgnoreDeviceLossError(WaitForIdleForDestruction());
|
||||||
|
HandleError(wgpu::ErrorType::DeviceLost, "Device lost for testing");
|
||||||
|
}
|
||||||
|
|
||||||
AdapterBase* DeviceBase::GetAdapter() const {
|
AdapterBase* DeviceBase::GetAdapter() const {
|
||||||
return mAdapter;
|
return mAdapter;
|
||||||
}
|
}
|
||||||
@ -563,8 +595,12 @@ namespace dawn_native {
|
|||||||
// Other Device API methods
|
// Other Device API methods
|
||||||
|
|
||||||
void DeviceBase::Tick() {
|
void DeviceBase::Tick() {
|
||||||
if (ConsumedError(TickImpl()))
|
if (ConsumedError(ValidateIsAlive())) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
if (ConsumedError(TickImpl())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
auto deferredResults = std::move(mDeferredCreateBufferMappedAsyncResults);
|
auto deferredResults = std::move(mDeferredCreateBufferMappedAsyncResults);
|
||||||
@ -651,6 +687,7 @@ namespace dawn_native {
|
|||||||
|
|
||||||
MaybeError DeviceBase::CreateBindGroupInternal(BindGroupBase** result,
|
MaybeError DeviceBase::CreateBindGroupInternal(BindGroupBase** result,
|
||||||
const BindGroupDescriptor* descriptor) {
|
const BindGroupDescriptor* descriptor) {
|
||||||
|
DAWN_TRY(ValidateIsAlive());
|
||||||
if (IsValidationEnabled()) {
|
if (IsValidationEnabled()) {
|
||||||
DAWN_TRY(ValidateBindGroupDescriptor(this, descriptor));
|
DAWN_TRY(ValidateBindGroupDescriptor(this, descriptor));
|
||||||
}
|
}
|
||||||
@ -661,6 +698,7 @@ namespace dawn_native {
|
|||||||
MaybeError DeviceBase::CreateBindGroupLayoutInternal(
|
MaybeError DeviceBase::CreateBindGroupLayoutInternal(
|
||||||
BindGroupLayoutBase** result,
|
BindGroupLayoutBase** result,
|
||||||
const BindGroupLayoutDescriptor* descriptor) {
|
const BindGroupLayoutDescriptor* descriptor) {
|
||||||
|
DAWN_TRY(ValidateIsAlive());
|
||||||
if (IsValidationEnabled()) {
|
if (IsValidationEnabled()) {
|
||||||
DAWN_TRY(ValidateBindGroupLayoutDescriptor(this, descriptor));
|
DAWN_TRY(ValidateBindGroupLayoutDescriptor(this, descriptor));
|
||||||
}
|
}
|
||||||
@ -670,6 +708,7 @@ namespace dawn_native {
|
|||||||
|
|
||||||
MaybeError DeviceBase::CreateBufferInternal(BufferBase** result,
|
MaybeError DeviceBase::CreateBufferInternal(BufferBase** result,
|
||||||
const BufferDescriptor* descriptor) {
|
const BufferDescriptor* descriptor) {
|
||||||
|
DAWN_TRY(ValidateIsAlive());
|
||||||
if (IsValidationEnabled()) {
|
if (IsValidationEnabled()) {
|
||||||
DAWN_TRY(ValidateBufferDescriptor(this, descriptor));
|
DAWN_TRY(ValidateBufferDescriptor(this, descriptor));
|
||||||
}
|
}
|
||||||
@ -680,6 +719,7 @@ namespace dawn_native {
|
|||||||
MaybeError DeviceBase::CreateComputePipelineInternal(
|
MaybeError DeviceBase::CreateComputePipelineInternal(
|
||||||
ComputePipelineBase** result,
|
ComputePipelineBase** result,
|
||||||
const ComputePipelineDescriptor* descriptor) {
|
const ComputePipelineDescriptor* descriptor) {
|
||||||
|
DAWN_TRY(ValidateIsAlive());
|
||||||
if (IsValidationEnabled()) {
|
if (IsValidationEnabled()) {
|
||||||
DAWN_TRY(ValidateComputePipelineDescriptor(this, descriptor));
|
DAWN_TRY(ValidateComputePipelineDescriptor(this, descriptor));
|
||||||
}
|
}
|
||||||
@ -704,6 +744,7 @@ namespace dawn_native {
|
|||||||
MaybeError DeviceBase::CreatePipelineLayoutInternal(
|
MaybeError DeviceBase::CreatePipelineLayoutInternal(
|
||||||
PipelineLayoutBase** result,
|
PipelineLayoutBase** result,
|
||||||
const PipelineLayoutDescriptor* descriptor) {
|
const PipelineLayoutDescriptor* descriptor) {
|
||||||
|
DAWN_TRY(ValidateIsAlive());
|
||||||
if (IsValidationEnabled()) {
|
if (IsValidationEnabled()) {
|
||||||
DAWN_TRY(ValidatePipelineLayoutDescriptor(this, descriptor));
|
DAWN_TRY(ValidatePipelineLayoutDescriptor(this, descriptor));
|
||||||
}
|
}
|
||||||
@ -712,6 +753,7 @@ namespace dawn_native {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MaybeError DeviceBase::CreateQueueInternal(QueueBase** result) {
|
MaybeError DeviceBase::CreateQueueInternal(QueueBase** result) {
|
||||||
|
DAWN_TRY(ValidateIsAlive());
|
||||||
DAWN_TRY_ASSIGN(*result, CreateQueueImpl());
|
DAWN_TRY_ASSIGN(*result, CreateQueueImpl());
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -719,6 +761,7 @@ namespace dawn_native {
|
|||||||
MaybeError DeviceBase::CreateRenderBundleEncoderInternal(
|
MaybeError DeviceBase::CreateRenderBundleEncoderInternal(
|
||||||
RenderBundleEncoder** result,
|
RenderBundleEncoder** result,
|
||||||
const RenderBundleEncoderDescriptor* descriptor) {
|
const RenderBundleEncoderDescriptor* descriptor) {
|
||||||
|
DAWN_TRY(ValidateIsAlive());
|
||||||
if (IsValidationEnabled()) {
|
if (IsValidationEnabled()) {
|
||||||
DAWN_TRY(ValidateRenderBundleEncoderDescriptor(this, descriptor));
|
DAWN_TRY(ValidateRenderBundleEncoderDescriptor(this, descriptor));
|
||||||
}
|
}
|
||||||
@ -729,6 +772,7 @@ namespace dawn_native {
|
|||||||
MaybeError DeviceBase::CreateRenderPipelineInternal(
|
MaybeError DeviceBase::CreateRenderPipelineInternal(
|
||||||
RenderPipelineBase** result,
|
RenderPipelineBase** result,
|
||||||
const RenderPipelineDescriptor* descriptor) {
|
const RenderPipelineDescriptor* descriptor) {
|
||||||
|
DAWN_TRY(ValidateIsAlive());
|
||||||
if (IsValidationEnabled()) {
|
if (IsValidationEnabled()) {
|
||||||
DAWN_TRY(ValidateRenderPipelineDescriptor(this, descriptor));
|
DAWN_TRY(ValidateRenderPipelineDescriptor(this, descriptor));
|
||||||
}
|
}
|
||||||
@ -761,6 +805,7 @@ namespace dawn_native {
|
|||||||
|
|
||||||
MaybeError DeviceBase::CreateSamplerInternal(SamplerBase** result,
|
MaybeError DeviceBase::CreateSamplerInternal(SamplerBase** result,
|
||||||
const SamplerDescriptor* descriptor) {
|
const SamplerDescriptor* descriptor) {
|
||||||
|
DAWN_TRY(ValidateIsAlive());
|
||||||
if (IsValidationEnabled()) {
|
if (IsValidationEnabled()) {
|
||||||
DAWN_TRY(ValidateSamplerDescriptor(this, descriptor));
|
DAWN_TRY(ValidateSamplerDescriptor(this, descriptor));
|
||||||
}
|
}
|
||||||
@ -770,6 +815,7 @@ namespace dawn_native {
|
|||||||
|
|
||||||
MaybeError DeviceBase::CreateShaderModuleInternal(ShaderModuleBase** result,
|
MaybeError DeviceBase::CreateShaderModuleInternal(ShaderModuleBase** result,
|
||||||
const ShaderModuleDescriptor* descriptor) {
|
const ShaderModuleDescriptor* descriptor) {
|
||||||
|
DAWN_TRY(ValidateIsAlive());
|
||||||
if (IsValidationEnabled()) {
|
if (IsValidationEnabled()) {
|
||||||
DAWN_TRY(ValidateShaderModuleDescriptor(this, descriptor));
|
DAWN_TRY(ValidateShaderModuleDescriptor(this, descriptor));
|
||||||
}
|
}
|
||||||
@ -779,6 +825,7 @@ namespace dawn_native {
|
|||||||
|
|
||||||
MaybeError DeviceBase::CreateSwapChainInternal(SwapChainBase** result,
|
MaybeError DeviceBase::CreateSwapChainInternal(SwapChainBase** result,
|
||||||
const SwapChainDescriptor* descriptor) {
|
const SwapChainDescriptor* descriptor) {
|
||||||
|
DAWN_TRY(ValidateIsAlive());
|
||||||
if (IsValidationEnabled()) {
|
if (IsValidationEnabled()) {
|
||||||
DAWN_TRY(ValidateSwapChainDescriptor(this, descriptor));
|
DAWN_TRY(ValidateSwapChainDescriptor(this, descriptor));
|
||||||
}
|
}
|
||||||
@ -788,6 +835,7 @@ namespace dawn_native {
|
|||||||
|
|
||||||
MaybeError DeviceBase::CreateTextureInternal(TextureBase** result,
|
MaybeError DeviceBase::CreateTextureInternal(TextureBase** result,
|
||||||
const TextureDescriptor* descriptor) {
|
const TextureDescriptor* descriptor) {
|
||||||
|
DAWN_TRY(ValidateIsAlive());
|
||||||
if (IsValidationEnabled()) {
|
if (IsValidationEnabled()) {
|
||||||
DAWN_TRY(ValidateTextureDescriptor(this, descriptor));
|
DAWN_TRY(ValidateTextureDescriptor(this, descriptor));
|
||||||
}
|
}
|
||||||
@ -798,6 +846,7 @@ namespace dawn_native {
|
|||||||
MaybeError DeviceBase::CreateTextureViewInternal(TextureViewBase** result,
|
MaybeError DeviceBase::CreateTextureViewInternal(TextureViewBase** result,
|
||||||
TextureBase* texture,
|
TextureBase* texture,
|
||||||
const TextureViewDescriptor* descriptor) {
|
const TextureViewDescriptor* descriptor) {
|
||||||
|
DAWN_TRY(ValidateIsAlive());
|
||||||
DAWN_TRY(ValidateObject(texture));
|
DAWN_TRY(ValidateObject(texture));
|
||||||
TextureViewDescriptor desc = GetTextureViewDescriptorWithDefaults(texture, descriptor);
|
TextureViewDescriptor desc = GetTextureViewDescriptorWithDefaults(texture, descriptor);
|
||||||
if (IsValidationEnabled()) {
|
if (IsValidationEnabled()) {
|
||||||
|
@ -29,9 +29,6 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace dawn_native {
|
namespace dawn_native {
|
||||||
|
|
||||||
using ErrorCallback = void (*)(const char* errorMessage, void* userData);
|
|
||||||
|
|
||||||
class AdapterBase;
|
class AdapterBase;
|
||||||
class AttachmentState;
|
class AttachmentState;
|
||||||
class AttachmentStateBlueprint;
|
class AttachmentStateBlueprint;
|
||||||
@ -167,6 +164,9 @@ namespace dawn_native {
|
|||||||
void SetUncapturedErrorCallback(wgpu::ErrorCallback callback, void* userdata);
|
void SetUncapturedErrorCallback(wgpu::ErrorCallback callback, void* userdata);
|
||||||
void PushErrorScope(wgpu::ErrorFilter filter);
|
void PushErrorScope(wgpu::ErrorFilter filter);
|
||||||
bool PopErrorScope(wgpu::ErrorCallback callback, void* userdata);
|
bool PopErrorScope(wgpu::ErrorCallback callback, void* userdata);
|
||||||
|
|
||||||
|
MaybeError ValidateIsAlive() const;
|
||||||
|
|
||||||
ErrorScope* GetCurrentErrorScope();
|
ErrorScope* GetCurrentErrorScope();
|
||||||
|
|
||||||
void Reference();
|
void Reference();
|
||||||
@ -189,6 +189,7 @@ namespace dawn_native {
|
|||||||
bool IsValidationEnabled() const;
|
bool IsValidationEnabled() const;
|
||||||
size_t GetLazyClearCountForTesting();
|
size_t GetLazyClearCountForTesting();
|
||||||
void IncrementLazyClearCountForTesting();
|
void IncrementLazyClearCountForTesting();
|
||||||
|
void LoseForTesting();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void SetToggle(Toggle toggle, bool isEnabled);
|
void SetToggle(Toggle toggle, bool isEnabled);
|
||||||
@ -196,6 +197,13 @@ namespace dawn_native {
|
|||||||
void BaseDestructor();
|
void BaseDestructor();
|
||||||
|
|
||||||
std::unique_ptr<DynamicUploader> mDynamicUploader;
|
std::unique_ptr<DynamicUploader> mDynamicUploader;
|
||||||
|
// LossStatus::Alive means the device is alive and can be used normally.
|
||||||
|
// LossStatus::BeingLost means the device is in the process of being lost and should not
|
||||||
|
// accept any new commands.
|
||||||
|
// LossStatus::AlreadyLost means the device has been lost and can no longer be used,
|
||||||
|
// all resources have been freed.
|
||||||
|
enum class LossStatus { Alive, BeingLost, AlreadyLost };
|
||||||
|
LossStatus mLossStatus = LossStatus::Alive;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual ResultOrError<BindGroupBase*> CreateBindGroupImpl(
|
virtual ResultOrError<BindGroupBase*> CreateBindGroupImpl(
|
||||||
@ -263,6 +271,7 @@ namespace dawn_native {
|
|||||||
// resources.
|
// resources.
|
||||||
virtual MaybeError WaitForIdleForDestruction() = 0;
|
virtual MaybeError WaitForIdleForDestruction() = 0;
|
||||||
|
|
||||||
|
void HandleLoss(const char* message);
|
||||||
wgpu::DeviceLostCallback mDeviceLostCallback = nullptr;
|
wgpu::DeviceLostCallback mDeviceLostCallback = nullptr;
|
||||||
void* mDeviceLostUserdata;
|
void* mDeviceLostUserdata;
|
||||||
|
|
||||||
|
27
src/dawn_native/Error.cpp
Normal file
27
src/dawn_native/Error.cpp
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// Copyright 2018 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/Error.h"
|
||||||
|
|
||||||
|
#include "dawn_native/ErrorData.h"
|
||||||
|
#include "dawn_native/dawn_platform.h"
|
||||||
|
|
||||||
|
namespace dawn_native {
|
||||||
|
void AssertAndIgnoreDeviceLossError(MaybeError maybeError) {
|
||||||
|
if (maybeError.IsError()) {
|
||||||
|
std::unique_ptr<ErrorData> errorData = maybeError.AcquireError();
|
||||||
|
ASSERT(errorData->GetType() == wgpu::ErrorType::DeviceLost);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace dawn_native
|
@ -81,6 +81,9 @@ namespace dawn_native {
|
|||||||
for (;;) \
|
for (;;) \
|
||||||
break
|
break
|
||||||
|
|
||||||
|
// Assert that errors are device loss so that we can continue with destruction
|
||||||
|
void AssertAndIgnoreDeviceLossError(MaybeError maybeError);
|
||||||
|
|
||||||
} // namespace dawn_native
|
} // namespace dawn_native
|
||||||
|
|
||||||
#endif // DAWNNATIVE_ERROR_H_
|
#endif // DAWNNATIVE_ERROR_H_
|
||||||
|
@ -28,7 +28,6 @@ namespace dawn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
namespace dawn_native {
|
namespace dawn_native {
|
||||||
|
|
||||||
enum class InternalErrorType : uint32_t;
|
enum class InternalErrorType : uint32_t;
|
||||||
|
|
||||||
class ErrorData {
|
class ErrorData {
|
||||||
|
@ -34,6 +34,11 @@ namespace dawn_native {
|
|||||||
|
|
||||||
void QueueBase::Submit(uint32_t commandCount, CommandBufferBase* const* commands) {
|
void QueueBase::Submit(uint32_t commandCount, CommandBufferBase* const* commands) {
|
||||||
DeviceBase* device = GetDevice();
|
DeviceBase* device = GetDevice();
|
||||||
|
if (device->ConsumedError(device->ValidateIsAlive())) {
|
||||||
|
// If device is lost, don't let any commands be submitted
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
TRACE_EVENT0(device->GetPlatform(), General, "Queue::Submit");
|
TRACE_EVENT0(device->GetPlatform(), General, "Queue::Submit");
|
||||||
if (device->IsValidationEnabled() &&
|
if (device->IsValidationEnabled() &&
|
||||||
device->ConsumedError(ValidateSubmit(commandCount, commands))) {
|
device->ConsumedError(ValidateSubmit(commandCount, commands))) {
|
||||||
|
@ -503,6 +503,7 @@ namespace dawn_native {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MaybeError TextureBase::ValidateDestroy() const {
|
MaybeError TextureBase::ValidateDestroy() const {
|
||||||
|
DAWN_TRY(GetDevice()->ValidateIsAlive());
|
||||||
DAWN_TRY(GetDevice()->ValidateObject(this));
|
DAWN_TRY(GetDevice()->ValidateObject(this));
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -403,6 +403,8 @@ namespace dawn_native { namespace d3d12 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Device::Destroy() {
|
void Device::Destroy() {
|
||||||
|
ASSERT(mLossStatus != LossStatus::AlreadyLost);
|
||||||
|
|
||||||
// Immediately forget about all pending commands
|
// Immediately forget about all pending commands
|
||||||
mPendingCommands.Release();
|
mPendingCommands.Release();
|
||||||
|
|
||||||
|
@ -277,6 +277,8 @@ namespace dawn_native { namespace metal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Device::Destroy() {
|
void Device::Destroy() {
|
||||||
|
ASSERT(mLossStatus != LossStatus::AlreadyLost);
|
||||||
|
|
||||||
[mCommandContext.AcquireCommands() release];
|
[mCommandContext.AcquireCommands() release];
|
||||||
|
|
||||||
mMapTracker = nullptr;
|
mMapTracker = nullptr;
|
||||||
|
@ -166,6 +166,8 @@ namespace dawn_native { namespace null {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Device::Destroy() {
|
void Device::Destroy() {
|
||||||
|
ASSERT(mLossStatus != LossStatus::AlreadyLost);
|
||||||
|
|
||||||
mDynamicUploader = nullptr;
|
mDynamicUploader = nullptr;
|
||||||
|
|
||||||
mPendingOperations.clear();
|
mPendingOperations.clear();
|
||||||
|
@ -162,6 +162,8 @@ namespace dawn_native { namespace opengl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Device::Destroy() {
|
void Device::Destroy() {
|
||||||
|
ASSERT(mLossStatus != LossStatus::AlreadyLost);
|
||||||
|
|
||||||
// Some operations might have been started since the last submit and waiting
|
// Some operations might have been started since the last submit and waiting
|
||||||
// on a serial that doesn't have a corresponding fence enqueued. Force all
|
// on a serial that doesn't have a corresponding fence enqueued. Force all
|
||||||
// operations to look as if they were completed (because they were).
|
// operations to look as if they were completed (because they were).
|
||||||
|
@ -733,6 +733,8 @@ namespace dawn_native { namespace vulkan {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Device::Destroy() {
|
void Device::Destroy() {
|
||||||
|
ASSERT(mLossStatus != LossStatus::AlreadyLost);
|
||||||
|
|
||||||
// Immediately tag the recording context as unused so we don't try to submit it in Tick.
|
// Immediately tag the recording context as unused so we don't try to submit it in Tick.
|
||||||
mRecordingContext.used = false;
|
mRecordingContext.used = false;
|
||||||
fn.DestroyCommandPool(mVkDevice, mRecordingContext.commandPool, nullptr);
|
fn.DestroyCommandPool(mVkDevice, mRecordingContext.commandPool, nullptr);
|
||||||
@ -741,7 +743,9 @@ namespace dawn_native { namespace vulkan {
|
|||||||
// on a serial that doesn't have a corresponding fence enqueued. Force all
|
// on a serial that doesn't have a corresponding fence enqueued. Force all
|
||||||
// operations to look as if they were completed (because they were).
|
// operations to look as if they were completed (because they were).
|
||||||
mCompletedSerial = mLastSubmittedSerial + 1;
|
mCompletedSerial = mLastSubmittedSerial + 1;
|
||||||
Tick();
|
|
||||||
|
// Assert that errors are device loss so that we can continue with destruction
|
||||||
|
AssertAndIgnoreDeviceLossError(TickImpl());
|
||||||
|
|
||||||
ASSERT(mCommandsInFlight.Empty());
|
ASSERT(mCommandsInFlight.Empty());
|
||||||
for (const CommandPoolAndBuffer& commands : mUnusedCommands) {
|
for (const CommandPoolAndBuffer& commands : mUnusedCommands) {
|
||||||
|
@ -582,6 +582,7 @@ void DawnTestBase::SetUp() {
|
|||||||
queue = device.CreateQueue();
|
queue = device.CreateQueue();
|
||||||
|
|
||||||
device.SetUncapturedErrorCallback(OnDeviceError, this);
|
device.SetUncapturedErrorCallback(OnDeviceError, this);
|
||||||
|
device.SetDeviceLostCallback(OnDeviceLost, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DawnTestBase::TearDown() {
|
void DawnTestBase::TearDown() {
|
||||||
@ -618,6 +619,10 @@ void DawnTestBase::OnDeviceError(WGPUErrorType type, const char* message, void*
|
|||||||
self->mError = true;
|
self->mError = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DawnTestBase::OnDeviceLost(const char* message, void* userdata) {
|
||||||
|
FAIL() << "Device Lost during test: " << message;
|
||||||
|
}
|
||||||
|
|
||||||
std::ostringstream& DawnTestBase::AddBufferExpectation(const char* file,
|
std::ostringstream& DawnTestBase::AddBufferExpectation(const char* file,
|
||||||
int line,
|
int line,
|
||||||
const wgpu::Buffer& buffer,
|
const wgpu::Buffer& buffer,
|
||||||
|
@ -249,6 +249,7 @@ class DawnTestBase {
|
|||||||
|
|
||||||
// Tracking for validation errors
|
// Tracking for validation errors
|
||||||
static void OnDeviceError(WGPUErrorType type, const char* message, void* userdata);
|
static void OnDeviceError(WGPUErrorType type, const char* message, void* userdata);
|
||||||
|
static void OnDeviceLost(const char* message, void* userdata);
|
||||||
bool mExpectError = false;
|
bool mExpectError = false;
|
||||||
bool mError = false;
|
bool mError = false;
|
||||||
|
|
||||||
|
192
src/tests/end2end/DeviceLostTests.cpp
Normal file
192
src/tests/end2end/DeviceLostTests.cpp
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
// 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 "tests/DawnTest.h"
|
||||||
|
|
||||||
|
#include <gmock/gmock.h>
|
||||||
|
#include "utils/ComboRenderPipelineDescriptor.h"
|
||||||
|
#include "utils/WGPUHelpers.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
using namespace testing;
|
||||||
|
|
||||||
|
class MockDeviceLostCallback {
|
||||||
|
public:
|
||||||
|
MOCK_METHOD2(Call, void(const char* message, void* userdata));
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::unique_ptr<MockDeviceLostCallback> mockDeviceLostCallback;
|
||||||
|
static void ToMockDeviceLostCallback(const char* message, void* userdata) {
|
||||||
|
mockDeviceLostCallback->Call(message, userdata);
|
||||||
|
DawnTestBase* self = static_cast<DawnTestBase*>(userdata);
|
||||||
|
self->StartExpectDeviceError();
|
||||||
|
}
|
||||||
|
|
||||||
|
class DeviceLostTest : public DawnTest {
|
||||||
|
protected:
|
||||||
|
void TestSetUp() override {
|
||||||
|
DAWN_SKIP_TEST_IF(UsesWire());
|
||||||
|
DawnTest::TestSetUp();
|
||||||
|
mockDeviceLostCallback = std::make_unique<MockDeviceLostCallback>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TearDown() override {
|
||||||
|
DawnTest::TearDown();
|
||||||
|
mockDeviceLostCallback = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetCallbackAndLoseForTesting() {
|
||||||
|
device.SetDeviceLostCallback(ToMockDeviceLostCallback, this);
|
||||||
|
EXPECT_CALL(*mockDeviceLostCallback, Call(_, this)).Times(1);
|
||||||
|
device.LoseForTesting();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test that DeviceLostCallback is invoked when LostForTestimg is called
|
||||||
|
TEST_P(DeviceLostTest, DeviceLostCallbackIsCalled) {
|
||||||
|
SetCallbackAndLoseForTesting();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that submit fails when device is lost
|
||||||
|
TEST_P(DeviceLostTest, SubmitFails) {
|
||||||
|
wgpu::CommandBuffer commands;
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
commands = encoder.Finish();
|
||||||
|
|
||||||
|
SetCallbackAndLoseForTesting();
|
||||||
|
ASSERT_DEVICE_ERROR(queue.Submit(0, &commands));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that CreateBindGroupLayout fails when device is lost
|
||||||
|
TEST_P(DeviceLostTest, CreateBindGroupLayoutFails) {
|
||||||
|
SetCallbackAndLoseForTesting();
|
||||||
|
|
||||||
|
wgpu::BindGroupLayoutBinding binding = {0, wgpu::ShaderStage::None,
|
||||||
|
wgpu::BindingType::UniformBuffer};
|
||||||
|
wgpu::BindGroupLayoutDescriptor descriptor;
|
||||||
|
descriptor.bindingCount = 1;
|
||||||
|
descriptor.bindings = &binding;
|
||||||
|
ASSERT_DEVICE_ERROR(device.CreateBindGroupLayout(&descriptor));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that CreateBindGroup fails when device is lost
|
||||||
|
TEST_P(DeviceLostTest, CreateBindGroupFails) {
|
||||||
|
SetCallbackAndLoseForTesting();
|
||||||
|
|
||||||
|
wgpu::BindGroupBinding binding;
|
||||||
|
binding.binding = 0;
|
||||||
|
binding.sampler = nullptr;
|
||||||
|
binding.textureView = nullptr;
|
||||||
|
binding.buffer = nullptr;
|
||||||
|
binding.offset = 0;
|
||||||
|
binding.size = 0;
|
||||||
|
|
||||||
|
wgpu::BindGroupDescriptor descriptor;
|
||||||
|
descriptor.layout = nullptr;
|
||||||
|
descriptor.bindingCount = 1;
|
||||||
|
descriptor.bindings = &binding;
|
||||||
|
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that CreatePipelineLayout fails when device is lost
|
||||||
|
TEST_P(DeviceLostTest, CreatePipelineLayoutFails) {
|
||||||
|
SetCallbackAndLoseForTesting();
|
||||||
|
|
||||||
|
wgpu::PipelineLayoutDescriptor descriptor;
|
||||||
|
descriptor.bindGroupLayoutCount = 0;
|
||||||
|
descriptor.bindGroupLayouts = nullptr;
|
||||||
|
ASSERT_DEVICE_ERROR(device.CreatePipelineLayout(&descriptor));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that CreateRenderBundleEncoder fails when device is lost
|
||||||
|
TEST_P(DeviceLostTest, CreateRenderBundleEncoderFails) {
|
||||||
|
SetCallbackAndLoseForTesting();
|
||||||
|
|
||||||
|
wgpu::RenderBundleEncoderDescriptor descriptor;
|
||||||
|
descriptor.colorFormatsCount = 0;
|
||||||
|
descriptor.colorFormats = nullptr;
|
||||||
|
ASSERT_DEVICE_ERROR(device.CreateRenderBundleEncoder(&descriptor));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that CreateComputePipeline fails when device is lost
|
||||||
|
TEST_P(DeviceLostTest, CreateComputePipelineFails) {
|
||||||
|
SetCallbackAndLoseForTesting();
|
||||||
|
|
||||||
|
wgpu::ComputePipelineDescriptor descriptor;
|
||||||
|
descriptor.layout = nullptr;
|
||||||
|
descriptor.computeStage.module = nullptr;
|
||||||
|
descriptor.nextInChain = nullptr;
|
||||||
|
ASSERT_DEVICE_ERROR(device.CreateComputePipeline(&descriptor));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that CreateRenderPipeline fails when device is lost
|
||||||
|
TEST_P(DeviceLostTest, CreateRenderPipelineFails) {
|
||||||
|
SetCallbackAndLoseForTesting();
|
||||||
|
|
||||||
|
utils::ComboRenderPipelineDescriptor descriptor(device);
|
||||||
|
ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that CreateSampler fails when device is lost
|
||||||
|
TEST_P(DeviceLostTest, CreateSamplerFails) {
|
||||||
|
SetCallbackAndLoseForTesting();
|
||||||
|
|
||||||
|
wgpu::SamplerDescriptor descriptor = utils::GetDefaultSamplerDescriptor();
|
||||||
|
ASSERT_DEVICE_ERROR(device.CreateSampler(&descriptor));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that CreateShaderModule fails when device is lost
|
||||||
|
TEST_P(DeviceLostTest, CreateShaderModuleFails) {
|
||||||
|
SetCallbackAndLoseForTesting();
|
||||||
|
|
||||||
|
ASSERT_DEVICE_ERROR(utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"(
|
||||||
|
#version 450
|
||||||
|
layout(location = 0) in vec4 color;
|
||||||
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
void main() {
|
||||||
|
fragColor = color;
|
||||||
|
})"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that CreateSwapChain fails when device is lost
|
||||||
|
TEST_P(DeviceLostTest, CreateSwapChainFails) {
|
||||||
|
SetCallbackAndLoseForTesting();
|
||||||
|
|
||||||
|
wgpu::SwapChainDescriptor descriptor;
|
||||||
|
descriptor.nextInChain = nullptr;
|
||||||
|
ASSERT_DEVICE_ERROR(device.CreateSwapChain(&descriptor));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that CreateTexture fails when device is lost
|
||||||
|
TEST_P(DeviceLostTest, CreateTextureFails) {
|
||||||
|
SetCallbackAndLoseForTesting();
|
||||||
|
|
||||||
|
wgpu::TextureDescriptor descriptor;
|
||||||
|
descriptor.size.width = 4;
|
||||||
|
descriptor.size.height = 4;
|
||||||
|
descriptor.size.depth = 1;
|
||||||
|
descriptor.arrayLayerCount = 1;
|
||||||
|
descriptor.mipLevelCount = 1;
|
||||||
|
descriptor.dimension = wgpu::TextureDimension::e2D;
|
||||||
|
descriptor.usage = wgpu::TextureUsage::OutputAttachment;
|
||||||
|
|
||||||
|
ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(DeviceLostTest, TickFails) {
|
||||||
|
SetCallbackAndLoseForTesting();
|
||||||
|
ASSERT_DEVICE_ERROR(device.Tick());
|
||||||
|
}
|
||||||
|
DAWN_INSTANTIATE_TEST(DeviceLostTest, D3D12Backend, VulkanBackend);
|
Loading…
x
Reference in New Issue
Block a user