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:
Natasha Lee
2020-01-15 19:02:13 +00:00
committed by Commit Bot service account
parent 0c66bcd13a
commit 0ecc48ecb7
18 changed files with 322 additions and 10 deletions

View File

@@ -582,6 +582,7 @@ void DawnTestBase::SetUp() {
queue = device.CreateQueue();
device.SetUncapturedErrorCallback(OnDeviceError, this);
device.SetDeviceLostCallback(OnDeviceLost, this);
}
void DawnTestBase::TearDown() {
@@ -618,6 +619,10 @@ void DawnTestBase::OnDeviceError(WGPUErrorType type, const char* message, void*
self->mError = true;
}
void DawnTestBase::OnDeviceLost(const char* message, void* userdata) {
FAIL() << "Device Lost during test: " << message;
}
std::ostringstream& DawnTestBase::AddBufferExpectation(const char* file,
int line,
const wgpu::Buffer& buffer,

View File

@@ -249,6 +249,7 @@ class DawnTestBase {
// Tracking for validation errors
static void OnDeviceError(WGPUErrorType type, const char* message, void* userdata);
static void OnDeviceLost(const char* message, void* userdata);
bool mExpectError = false;
bool mError = false;

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