Add multiple device testing capability in DawnTest.

- Factors out device creation code to helper.
- Updates callbacks and test infra to support different devices and use mock callbacks.
- Updates some tests that were using outdated device lost callbacks and multiple devices.

Change-Id: I4210280420b8dadbc6355d27995ccf0cd864108c
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/87480
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Loko Kung <lokokung@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
Loko Kung 2022-04-28 21:55:33 +00:00 committed by Dawn LUCI CQ
parent bd8de5d2a4
commit 04912aa836
7 changed files with 171 additions and 208 deletions

View File

@ -30,6 +30,7 @@
#include "dawn/common/Platform.h"
#include "dawn/common/SystemUtils.h"
#include "dawn/dawn_proc.h"
#include "dawn/native/Device.h"
#include "dawn/native/Instance.h"
#include "dawn/native/dawn_platform.h"
#include "dawn/utils/ComboRenderPipelineDescriptor.h"
@ -903,31 +904,9 @@ bool DawnTestBase::SupportsFeatures(const std::vector<wgpu::FeatureName>& featur
return true;
}
void DawnTestBase::SetUp() {
{
// Find the adapter that exactly matches our adapter properties.
const auto& adapters = gTestEnv->GetInstance()->GetAdapters();
const auto& it = std::find_if(
adapters.begin(), adapters.end(), [&](const dawn::native::Adapter& adapter) {
wgpu::AdapterProperties properties;
adapter.GetProperties(&properties);
return (mParam.adapterProperties.selected &&
properties.deviceID == mParam.adapterProperties.deviceID &&
properties.vendorID == mParam.adapterProperties.vendorID &&
properties.adapterType == mParam.adapterProperties.adapterType &&
properties.backendType == mParam.adapterProperties.backendType &&
strcmp(properties.name, mParam.adapterProperties.adapterName.c_str()) == 0);
});
ASSERT(it != adapters.end());
mBackendAdapter = *it;
}
// Setup the per-test platform. Tests can provide one by overloading CreateTestPlatform. This is
// NOT a thread-safe operation and is allowed here for testing only.
mTestPlatform = CreateTestPlatform();
dawn::native::FromAPI(gTestEnv->GetInstance()->Get())
->SetPlatformForTesting(mTestPlatform.get());
std::pair<wgpu::Device, WGPUDevice> DawnTestBase::CreateDeviceImpl(std::string isolationKey) {
// TODO(dawn:1399) Always flush wire before creating to avoid reuse of the same handle.
FlushWire();
// Create the device from the adapter
for (const char* forceEnabledWorkaround : mParam.forceEnabledWorkarounds) {
@ -975,30 +954,16 @@ void DawnTestBase::SetUp() {
togglesDesc.forceDisabledToggles = forceDisabledToggles.data();
togglesDesc.forceDisabledTogglesCount = forceDisabledToggles.size();
std::tie(device, backendDevice) =
mWireHelper->RegisterDevice(mBackendAdapter.CreateDevice(&deviceDescriptor));
ASSERT_NE(nullptr, backendDevice);
std::string traceName =
std::string(::testing::UnitTest::GetInstance()->current_test_info()->test_suite_name()) +
"_" + ::testing::UnitTest::GetInstance()->current_test_info()->name();
mWireHelper->BeginWireTrace(traceName.c_str());
queue = device.GetQueue();
device.SetUncapturedErrorCallback(OnDeviceError, this);
device.SetDeviceLostCallback(OnDeviceLost, this);
#if defined(DAWN_ENABLE_BACKEND_DESKTOP_GL)
if (IsOpenGL()) {
glfwMakeContextCurrent(gTestEnv->GetOpenGLWindow());
}
#endif // defined(DAWN_ENABLE_BACKEND_DESKTOP_GL)
#if defined(DAWN_ENABLE_BACKEND_OPENGLES)
if (IsOpenGLES()) {
glfwMakeContextCurrent(gTestEnv->GetOpenGLESWindow());
}
#endif // defined(DAWN_ENABLE_BACKEND_OPENGLES)
wgpu::DawnCacheDeviceDescriptor cacheDesc = {};
togglesDesc.nextInChain = &cacheDesc;
cacheDesc.isolationKey = isolationKey.c_str();
auto devices = mWireHelper->RegisterDevice(mBackendAdapter.CreateDevice(&deviceDescriptor));
wgpu::Device device = devices.first;
device.SetUncapturedErrorCallback(mDeviceErrorCallback.Callback(),
mDeviceErrorCallback.MakeUserdata(device.Get()));
device.SetDeviceLostCallback(mDeviceLostCallback.Callback(),
mDeviceLostCallback.MakeUserdata(device.Get()));
device.SetLoggingCallback(
[](WGPULoggingType type, char const* message, void*) {
switch (type) {
@ -1017,6 +982,60 @@ void DawnTestBase::SetUp() {
}
},
nullptr);
return devices;
}
wgpu::Device DawnTestBase::CreateDevice(std::string isolationKey) {
return CreateDeviceImpl(isolationKey).first;
}
void DawnTestBase::SetUp() {
{
// Find the adapter that exactly matches our adapter properties.
const auto& adapters = gTestEnv->GetInstance()->GetAdapters();
const auto& it = std::find_if(
adapters.begin(), adapters.end(), [&](const dawn::native::Adapter& adapter) {
wgpu::AdapterProperties properties;
adapter.GetProperties(&properties);
return (mParam.adapterProperties.selected &&
properties.deviceID == mParam.adapterProperties.deviceID &&
properties.vendorID == mParam.adapterProperties.vendorID &&
properties.adapterType == mParam.adapterProperties.adapterType &&
properties.backendType == mParam.adapterProperties.backendType &&
strcmp(properties.name, mParam.adapterProperties.adapterName.c_str()) == 0);
});
ASSERT(it != adapters.end());
mBackendAdapter = *it;
}
// Setup the per-test platform. Tests can provide one by overloading CreateTestPlatform. This is
// NOT a thread-safe operation and is allowed here for testing only.
mTestPlatform = CreateTestPlatform();
dawn::native::FromAPI(gTestEnv->GetInstance()->Get())
->SetPlatformForTesting(mTestPlatform.get());
std::string traceName =
std::string(::testing::UnitTest::GetInstance()->current_test_info()->test_suite_name()) +
"_" + ::testing::UnitTest::GetInstance()->current_test_info()->name();
mWireHelper->BeginWireTrace(traceName.c_str());
// Create the device from the adapter
std::tie(device, backendDevice) = CreateDeviceImpl();
ASSERT_NE(nullptr, backendDevice);
queue = device.GetQueue();
#if defined(DAWN_ENABLE_BACKEND_DESKTOP_GL)
if (IsOpenGL()) {
glfwMakeContextCurrent(gTestEnv->GetOpenGLWindow());
}
#endif // defined(DAWN_ENABLE_BACKEND_DESKTOP_GL)
#if defined(DAWN_ENABLE_BACKEND_OPENGLES)
if (IsOpenGLES()) {
glfwMakeContextCurrent(gTestEnv->GetOpenGLESWindow());
}
#endif // defined(DAWN_ENABLE_BACKEND_OPENGLES)
}
void DawnTestBase::TearDown() {
@ -1033,50 +1052,36 @@ void DawnTestBase::TearDown() {
EXPECT_EQ(mLastWarningCount,
dawn::native::GetDeprecationWarningCountForTesting(device.Get()));
}
// The device will be destroyed soon after, so we want to set the expectation.
ExpectDeviceDestruction();
}
void DawnTestBase::StartExpectDeviceError(testing::Matcher<std::string> errorMatcher) {
mExpectError = true;
mError = false;
mErrorMatcher = errorMatcher;
}
bool DawnTestBase::EndExpectDeviceError() {
mExpectError = false;
mErrorMatcher = testing::_;
return mError;
}
void DawnTestBase::ExpectDeviceDestruction() {
mExpectDestruction = true;
}
// static
void DawnTestBase::OnDeviceError(WGPUErrorType type, const char* message, void* userdata) {
ASSERT(type != WGPUErrorType_NoError);
DawnTestBase* self = static_cast<DawnTestBase*>(userdata);
ASSERT_TRUE(self->mExpectError) << "Got unexpected device error: " << message;
ASSERT_FALSE(self->mError) << "Got two errors in expect block";
if (self->mExpectError) {
ASSERT_THAT(message, self->mErrorMatcher);
void DawnTestBase::DestroyDevice(wgpu::Device device) {
wgpu::Device resolvedDevice;
if (device != nullptr) {
resolvedDevice = device;
} else {
resolvedDevice = this->device;
}
self->mError = true;
EXPECT_CALL(mDeviceLostCallback,
Call(WGPUDeviceLostReason_Destroyed, testing::_, resolvedDevice.Get()))
.Times(1);
resolvedDevice.Destroy();
FlushWire();
testing::Mock::VerifyAndClearExpectations(&mDeviceLostCallback);
}
void DawnTestBase::OnDeviceLost(WGPUDeviceLostReason reason, const char* message, void* userdata) {
DawnTestBase* self = static_cast<DawnTestBase*>(userdata);
if (self->mExpectDestruction) {
EXPECT_EQ(reason, WGPUDeviceLostReason_Destroyed);
return;
void DawnTestBase::LoseDeviceForTesting(wgpu::Device device) {
wgpu::Device resolvedDevice;
if (device != nullptr) {
resolvedDevice = device;
} else {
resolvedDevice = this->device;
}
// Using ADD_FAILURE + ASSERT instead of FAIL to prevent the current test from continuing with a
// corrupt state.
ADD_FAILURE() << "Device lost during test: " << message;
ASSERT(false);
EXPECT_CALL(mDeviceLostCallback,
Call(WGPUDeviceLostReason_Undefined, testing::_, resolvedDevice.Get()))
.Times(1);
resolvedDevice.LoseForTesting();
FlushWire();
testing::Mock::VerifyAndClearExpectations(&mDeviceLostCallback);
}
std::ostringstream& DawnTestBase::AddBufferExpectation(const char* file,

View File

@ -27,6 +27,7 @@
#include "dawn/dawn_proc_table.h"
#include "dawn/native/DawnNative.h"
#include "dawn/platform/DawnPlatform.h"
#include "dawn/tests/MockCallback.h"
#include "dawn/tests/ParamGenerator.h"
#include "dawn/tests/ToggleParser.h"
#include "dawn/utils/ScopedAutoreleasePool.h"
@ -100,16 +101,22 @@
#define EXPECT_TEXTURE_FLOAT16_EQ(...) \
AddTextureExpectation<float, uint16_t>(__FILE__, __LINE__, __VA_ARGS__)
#define ASSERT_DEVICE_ERROR_MSG(statement, matcher) \
StartExpectDeviceError(matcher); \
statement; \
FlushWire(); \
if (!EndExpectDeviceError()) { \
FAIL() << "Expected device error in:\n " << #statement; \
} \
do { \
#define ASSERT_DEVICE_ERROR_MSG_ON(device, statement, matcher) \
FlushWire(); \
EXPECT_CALL(mDeviceErrorCallback, \
Call(testing::Ne(WGPUErrorType_NoError), matcher, device.Get())); \
statement; \
FlushWire(); \
testing::Mock::VerifyAndClearExpectations(&mDeviceErrorCallback); \
do { \
} while (0)
#define ASSERT_DEVICE_ERROR_MSG(statement, matcher) \
ASSERT_DEVICE_ERROR_MSG_ON(this->device, statement, matcher)
#define ASSERT_DEVICE_ERROR_ON(device, statement) \
ASSERT_DEVICE_ERROR_MSG_ON(device, statement, testing::_)
#define ASSERT_DEVICE_ERROR(statement) ASSERT_DEVICE_ERROR_MSG(statement, testing::_)
struct RGBA8 {
@ -311,10 +318,8 @@ class DawnTestBase {
bool HasToggleEnabled(const char* workaround) const;
void StartExpectDeviceError(testing::Matcher<std::string> errorMatcher = testing::_);
bool EndExpectDeviceError();
void ExpectDeviceDestruction();
void DestroyDevice(wgpu::Device device = nullptr);
void LoseDeviceForTesting(wgpu::Device device = nullptr);
bool HasVendorIdFilter() const;
uint32_t GetVendorIdFilter() const;
@ -348,6 +353,11 @@ class DawnTestBase {
size_t mLastWarningCount = 0;
// Mock callbacks tracking errors and destruction. Device lost is a nice mock since tests that
// do not care about device destruction can ignore the callback entirely.
testing::MockCallback<WGPUErrorCallback> mDeviceErrorCallback;
testing::NiceMock<testing::MockCallback<WGPUDeviceLostCallback>> mDeviceLostCallback;
// Helper methods to implement the EXPECT_ macros
std::ostringstream& AddBufferExpectation(const char* file,
int line,
@ -512,6 +522,9 @@ class DawnTestBase {
bool SupportsFeatures(const std::vector<wgpu::FeatureName>& features);
// Exposed device creation helper for tests to use when needing more than 1 device.
wgpu::Device CreateDevice(std::string isolationKey = "");
// Called in SetUp() to get the features required to be enabled in the tests. The tests must
// check if the required features are supported by the adapter in this function and guarantee
// the returned features are all supported by the adapter. The tests may provide different
@ -532,13 +545,8 @@ class DawnTestBase {
AdapterTestParam mParam;
std::unique_ptr<utils::WireHelper> mWireHelper;
// Tracking for validation errors
static void OnDeviceError(WGPUErrorType type, const char* message, void* userdata);
static void OnDeviceLost(WGPUDeviceLostReason reason, const char* message, void* userdata);
bool mExpectError = false;
bool mError = false;
testing::Matcher<std::string> mErrorMatcher;
bool mExpectDestruction = false;
// Internal device creation function for default device creation with some optional overrides.
std::pair<wgpu::Device, WGPUDevice> CreateDeviceImpl(std::string isolationKey = "");
std::ostringstream& AddTextureExpectationImpl(const char* file,
int line,

View File

@ -440,8 +440,7 @@ TEST_P(CreatePipelineAsyncTest, DestroyDeviceBeforeCallbackOfCreateComputePipeli
task->message = message;
},
&task);
ExpectDeviceDestruction();
device.Destroy();
DestroyDevice();
}
// Verify there is no error when the device is destroyed before the callback of
@ -474,8 +473,7 @@ TEST_P(CreatePipelineAsyncTest, DestroyDeviceBeforeCallbackOfCreateRenderPipelin
task->message = message;
},
&task);
ExpectDeviceDestruction();
device.Destroy();
DestroyDevice();
}
// Verify the code path of CreateComputePipelineAsync() to directly return the compute pipeline

View File

@ -173,10 +173,7 @@ TEST_P(DestroyTest, DestroyDeviceBeforeSubmit) {
DAWN_TEST_UNSUPPORTED_IF(UsesWire());
wgpu::CommandBuffer commands = CreateTriangleCommandBuffer();
// Tests normally don't expect a device lost error, but since we are destroying the device, we
// actually do, so we need to override the default device lost callback.
ExpectDeviceDestruction();
device.Destroy();
DestroyDevice();
ASSERT_DEVICE_ERROR_MSG(queue.Submit(1, &commands), HasSubstr("[Device] is lost."));
}
@ -190,10 +187,7 @@ TEST_P(DestroyTest, DestroyDeviceLingeringBGL) {
device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Filtering}});
utils::MakeBindGroup(device, layout, {{0, device.CreateSampler()}});
// Tests normally don't expect a device lost error, but since we are destroying the device, we
// actually do, so we need to override the default device lost callback.
ExpectDeviceDestruction();
device.Destroy();
DestroyDevice();
}
DAWN_INSTANTIATE_TEST(DestroyTest,

View File

@ -26,20 +26,6 @@ using testing::_;
using testing::Exactly;
using testing::MockCallback;
class MockDeviceLostCallback {
public:
MOCK_METHOD(void, Call, (WGPUDeviceLostReason reason, const char* message, void* userdata));
};
static std::unique_ptr<MockDeviceLostCallback> mockDeviceLostCallback;
static void ToMockDeviceLostCallback(WGPUDeviceLostReason reason,
const char* message,
void* userdata) {
mockDeviceLostCallback->Call(reason, message, userdata);
DawnTestBase* self = static_cast<DawnTestBase*>(userdata);
self->StartExpectDeviceError();
}
class MockQueueWorkDoneCallback {
public:
MOCK_METHOD(void, Call, (WGPUQueueWorkDoneStatus status, void* userdata));
@ -57,26 +43,14 @@ class DeviceLostTest : public DawnTest {
void SetUp() override {
DawnTest::SetUp();
DAWN_TEST_UNSUPPORTED_IF(UsesWire());
mockDeviceLostCallback = std::make_unique<MockDeviceLostCallback>();
mockQueueWorkDoneCallback = std::make_unique<MockQueueWorkDoneCallback>();
// SetDeviceLostCallback will trigger the callback task manager and clean all deferred
// callback tasks, so it should be called at the beginning of each test to prevent
// unexpectedly triggering callback tasks created during test
device.SetDeviceLostCallback(ToMockDeviceLostCallback, this);
}
void TearDown() override {
mockDeviceLostCallback = nullptr;
mockQueueWorkDoneCallback = nullptr;
DawnTest::TearDown();
}
void LoseForTesting() {
EXPECT_CALL(*mockDeviceLostCallback, Call(WGPUDeviceLostReason_Undefined, _, this))
.Times(1);
device.LoseForTesting();
}
static void MapFailCallback(WGPUBufferMapAsyncStatus status, void* userdata) {
EXPECT_EQ(WGPUBufferMapAsyncStatus_DeviceLost, status);
EXPECT_EQ(&fakeUserData, userdata);
@ -85,7 +59,7 @@ class DeviceLostTest : public DawnTest {
// Test that DeviceLostCallback is invoked when LostForTestimg is called
TEST_P(DeviceLostTest, DeviceLostCallbackIsCalled) {
LoseForTesting();
LoseDeviceForTesting();
}
// Test that submit fails when device is lost
@ -94,13 +68,13 @@ TEST_P(DeviceLostTest, SubmitFails) {
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
commands = encoder.Finish();
LoseForTesting();
LoseDeviceForTesting();
ASSERT_DEVICE_ERROR(queue.Submit(0, &commands));
}
// Test that CreateBindGroupLayout fails when device is lost
TEST_P(DeviceLostTest, CreateBindGroupLayoutFails) {
LoseForTesting();
LoseDeviceForTesting();
wgpu::BindGroupLayoutEntry entry;
entry.binding = 0;
@ -129,13 +103,13 @@ TEST_P(DeviceLostTest, GetBindGroupLayoutFails) {
wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&descriptor);
LoseForTesting();
LoseDeviceForTesting();
ASSERT_DEVICE_ERROR(pipeline.GetBindGroupLayout(0).Get());
}
// Test that CreateBindGroup fails when device is lost
TEST_P(DeviceLostTest, CreateBindGroupFails) {
LoseForTesting();
LoseDeviceForTesting();
wgpu::BindGroupEntry entry;
entry.binding = 0;
@ -154,7 +128,7 @@ TEST_P(DeviceLostTest, CreateBindGroupFails) {
// Test that CreatePipelineLayout fails when device is lost
TEST_P(DeviceLostTest, CreatePipelineLayoutFails) {
LoseForTesting();
LoseDeviceForTesting();
wgpu::PipelineLayoutDescriptor descriptor;
descriptor.bindGroupLayoutCount = 0;
@ -164,7 +138,7 @@ TEST_P(DeviceLostTest, CreatePipelineLayoutFails) {
// Tests that CreateRenderBundleEncoder fails when device is lost
TEST_P(DeviceLostTest, CreateRenderBundleEncoderFails) {
LoseForTesting();
LoseDeviceForTesting();
wgpu::RenderBundleEncoderDescriptor descriptor;
descriptor.colorFormatsCount = 0;
@ -174,7 +148,7 @@ TEST_P(DeviceLostTest, CreateRenderBundleEncoderFails) {
// Tests that CreateComputePipeline fails when device is lost
TEST_P(DeviceLostTest, CreateComputePipelineFails) {
LoseForTesting();
LoseDeviceForTesting();
wgpu::ComputePipelineDescriptor descriptor = {};
descriptor.layout = nullptr;
@ -184,7 +158,7 @@ TEST_P(DeviceLostTest, CreateComputePipelineFails) {
// Tests that CreateRenderPipeline fails when device is lost
TEST_P(DeviceLostTest, CreateRenderPipelineFails) {
LoseForTesting();
LoseDeviceForTesting();
utils::ComboRenderPipelineDescriptor descriptor;
ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor));
@ -192,14 +166,14 @@ TEST_P(DeviceLostTest, CreateRenderPipelineFails) {
// Tests that CreateSampler fails when device is lost
TEST_P(DeviceLostTest, CreateSamplerFails) {
LoseForTesting();
LoseDeviceForTesting();
ASSERT_DEVICE_ERROR(device.CreateSampler());
}
// Tests that CreateShaderModule fails when device is lost
TEST_P(DeviceLostTest, CreateShaderModuleFails) {
LoseForTesting();
LoseDeviceForTesting();
ASSERT_DEVICE_ERROR(utils::CreateShaderModule(device, R"(
@stage(fragment)
@ -210,7 +184,7 @@ TEST_P(DeviceLostTest, CreateShaderModuleFails) {
// Tests that CreateSwapChain fails when device is lost
TEST_P(DeviceLostTest, CreateSwapChainFails) {
LoseForTesting();
LoseDeviceForTesting();
wgpu::SwapChainDescriptor descriptor = {};
ASSERT_DEVICE_ERROR(device.CreateSwapChain(nullptr, &descriptor));
@ -218,7 +192,7 @@ TEST_P(DeviceLostTest, CreateSwapChainFails) {
// Tests that CreateTexture fails when device is lost
TEST_P(DeviceLostTest, CreateTextureFails) {
LoseForTesting();
LoseDeviceForTesting();
wgpu::TextureDescriptor descriptor;
descriptor.size.width = 4;
@ -233,7 +207,7 @@ TEST_P(DeviceLostTest, CreateTextureFails) {
// Test that CreateBuffer fails when device is lost
TEST_P(DeviceLostTest, CreateBufferFails) {
LoseForTesting();
LoseDeviceForTesting();
wgpu::BufferDescriptor bufferDescriptor;
bufferDescriptor.size = sizeof(float);
@ -248,7 +222,7 @@ TEST_P(DeviceLostTest, BufferMapAsyncFailsForWriting) {
bufferDescriptor.usage = wgpu::BufferUsage::MapWrite;
wgpu::Buffer buffer = device.CreateBuffer(&bufferDescriptor);
LoseForTesting();
LoseDeviceForTesting();
ASSERT_DEVICE_ERROR(buffer.MapAsync(wgpu::MapMode::Write, 0, 4, MapFailCallback,
const_cast<int*>(&fakeUserData)));
}
@ -263,7 +237,7 @@ TEST_P(DeviceLostTest, BufferMapAsyncBeforeLossFailsForWriting) {
buffer.MapAsync(wgpu::MapMode::Write, 0, 4, MapFailCallback, const_cast<int*>(&fakeUserData));
LoseForTesting();
LoseDeviceForTesting();
}
// Test that buffer.Unmap fails after device is lost
@ -274,7 +248,7 @@ TEST_P(DeviceLostTest, BufferUnmapFails) {
bufferDescriptor.mappedAtCreation = true;
wgpu::Buffer buffer = device.CreateBuffer(&bufferDescriptor);
LoseForTesting();
LoseDeviceForTesting();
ASSERT_DEVICE_ERROR(buffer.Unmap());
}
@ -285,7 +259,7 @@ TEST_P(DeviceLostTest, CreateBufferMappedAtCreationFails) {
bufferDescriptor.usage = wgpu::BufferUsage::MapWrite;
bufferDescriptor.mappedAtCreation = true;
LoseForTesting();
LoseDeviceForTesting();
ASSERT_DEVICE_ERROR(device.CreateBuffer(&bufferDescriptor));
}
@ -297,7 +271,7 @@ TEST_P(DeviceLostTest, BufferMapAsyncFailsForReading) {
wgpu::Buffer buffer = device.CreateBuffer(&bufferDescriptor);
LoseForTesting();
LoseDeviceForTesting();
ASSERT_DEVICE_ERROR(buffer.MapAsync(wgpu::MapMode::Read, 0, 4, MapFailCallback,
const_cast<int*>(&fakeUserData)));
}
@ -313,7 +287,7 @@ TEST_P(DeviceLostTest, BufferMapAsyncBeforeLossFailsForReading) {
buffer.MapAsync(wgpu::MapMode::Read, 0, 4, MapFailCallback, const_cast<int*>(&fakeUserData));
LoseForTesting();
LoseDeviceForTesting();
}
// Test that WriteBuffer fails after device is lost
@ -324,14 +298,14 @@ TEST_P(DeviceLostTest, WriteBufferFails) {
wgpu::Buffer buffer = device.CreateBuffer(&bufferDescriptor);
LoseForTesting();
LoseDeviceForTesting();
float data = 12.0f;
ASSERT_DEVICE_ERROR(queue.WriteBuffer(buffer, 0, &data, sizeof(data)));
}
// Test it's possible to GetMappedRange on a buffer created mapped after device loss
TEST_P(DeviceLostTest, GetMappedRange_CreateBufferMappedAtCreationAfterLoss) {
LoseForTesting();
LoseDeviceForTesting();
wgpu::BufferDescriptor desc;
desc.size = 4;
@ -351,7 +325,7 @@ TEST_P(DeviceLostTest, GetMappedRange_CreateBufferMappedAtCreationBeforeLoss) {
wgpu::Buffer buffer = device.CreateBuffer(&desc);
void* rangeBeforeLoss = buffer.GetMappedRange();
LoseForTesting();
LoseDeviceForTesting();
ASSERT_NE(buffer.GetMappedRange(), nullptr);
ASSERT_EQ(buffer.GetMappedRange(), rangeBeforeLoss);
@ -368,7 +342,7 @@ TEST_P(DeviceLostTest, GetMappedRange_MapAsyncReading) {
queue.Submit(0, nullptr);
const void* rangeBeforeLoss = buffer.GetConstMappedRange();
LoseForTesting();
LoseDeviceForTesting();
ASSERT_NE(buffer.GetConstMappedRange(), nullptr);
ASSERT_EQ(buffer.GetConstMappedRange(), rangeBeforeLoss);
@ -385,7 +359,7 @@ TEST_P(DeviceLostTest, GetMappedRange_MapAsyncWriting) {
queue.Submit(0, nullptr);
const void* rangeBeforeLoss = buffer.GetConstMappedRange();
LoseForTesting();
LoseDeviceForTesting();
ASSERT_NE(buffer.GetConstMappedRange(), nullptr);
ASSERT_EQ(buffer.GetConstMappedRange(), rangeBeforeLoss);
@ -399,13 +373,13 @@ TEST_P(DeviceLostTest, CommandEncoderFinishFails) {
wgpu::CommandBuffer commands;
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
LoseForTesting();
LoseDeviceForTesting();
ASSERT_DEVICE_ERROR(encoder.Finish());
}
// Test that QueueOnSubmittedWorkDone fails after device is lost.
TEST_P(DeviceLostTest, QueueOnSubmittedWorkDoneFails) {
LoseForTesting();
LoseDeviceForTesting();
// callback should have device lost status
EXPECT_CALL(*mockQueueWorkDoneCallback, Call(WGPUQueueWorkDoneStatus_DeviceLost, nullptr))
@ -420,33 +394,29 @@ TEST_P(DeviceLostTest, QueueOnSubmittedWorkDoneBeforeLossFails) {
.Times(1);
queue.OnSubmittedWorkDone(0, ToMockQueueWorkDone, nullptr);
LoseForTesting();
LoseDeviceForTesting();
}
// Test that LostForTesting can only be called on one time
TEST_P(DeviceLostTest, LoseForTestingOnce) {
// First LoseForTesting call should occur normally. The callback is already set in SetUp.
EXPECT_CALL(*mockDeviceLostCallback, Call(WGPUDeviceLostReason_Undefined, _, this)).Times(1);
device.LoseForTesting();
TEST_P(DeviceLostTest, LoseDeviceForTestingOnce) {
// First LoseDeviceForTesting call should occur normally. The callback is already set in SetUp.
LoseDeviceForTesting();
// Second LoseForTesting call should result in no callbacks. The LoseForTesting will return
// without doing anything when it sees that device has already been lost.
device.SetDeviceLostCallback(ToMockDeviceLostCallback, this);
EXPECT_CALL(*mockDeviceLostCallback, Call(_, _, this)).Times(0);
// Second LoseDeviceForTesting call should result in no callbacks. Note we also reset the
// callback first since by default the device clears the callback after the device is lost.
device.SetDeviceLostCallback(mDeviceLostCallback.Callback(),
mDeviceLostCallback.MakeUserdata(device.Get()));
EXPECT_CALL(mDeviceLostCallback, Call(WGPUDeviceLostReason_Undefined, testing::_, device.Get()))
.Times(0);
device.LoseForTesting();
FlushWire();
testing::Mock::VerifyAndClearExpectations(&mDeviceLostCallback);
}
TEST_P(DeviceLostTest, DeviceLostDoesntCallUncapturedError) {
// Set no callback.
device.SetDeviceLostCallback(nullptr, nullptr);
// Set the uncaptured error callback which should not be called on
// device lost.
MockCallback<WGPUErrorCallback> mockErrorCallback;
device.SetUncapturedErrorCallback(mockErrorCallback.Callback(),
mockErrorCallback.MakeUserdata(nullptr));
EXPECT_CALL(mockErrorCallback, Call(_, _, _)).Times(Exactly(0));
device.LoseForTesting();
// Since the device has a default error callback set that fails if it is called, we just need
// to lose the device and verify no failures.
LoseDeviceForTesting();
}
// Test that WGPUCreatePipelineAsyncStatus_DeviceLost can be correctly returned when device is lost
@ -466,7 +436,7 @@ TEST_P(DeviceLostTest, DeviceLostBeforeCreatePipelineAsyncCallback) {
};
device.CreateComputePipelineAsync(&descriptor, callback, nullptr);
LoseForTesting();
LoseDeviceForTesting();
}
// This is a regression test for crbug.com/1212385 where Dawn didn't clean up all
@ -493,7 +463,7 @@ TEST_P(DeviceLostTest, FreeBindGroupAfterDeviceLossWithPendingCommands) {
queue.Submit(0, nullptr);
queue.Submit(0, nullptr);
LoseForTesting();
LoseDeviceForTesting();
// Releasing the bing group places the bind group layout into a queue in the Vulkan backend
// for recycling of descriptor sets. So, after these release calls there is still one last
@ -513,7 +483,7 @@ TEST_P(DeviceLostTest, SetLabelAfterDeviceLoss) {
descriptor.size = 4;
descriptor.usage = wgpu::BufferUsage::Uniform;
wgpu::Buffer buffer = device.CreateBuffer(&descriptor);
LoseForTesting();
LoseDeviceForTesting();
buffer.SetLabel(label.c_str());
}

View File

@ -222,7 +222,7 @@ TEST_P(SwapChainTests, SwitchingDevice) {
// See https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/2256
DAWN_SUPPRESS_TEST_IF(IsVulkan() && IsBackendValidationEnabled());
wgpu::Device device2 = wgpu::Device::Acquire(GetAdapter().CreateDevice());
wgpu::Device device2 = CreateDevice();
for (int i = 0; i < 3; i++) {
wgpu::Device deviceToUse;

View File

@ -319,38 +319,26 @@ TEST_P(SwapChainValidationTests, SwapChainIsInvalidAfterSurfaceDestruction_After
ASSERT_DEVICE_ERROR(replacedSwapChain.Present());
}
// Test that after Device is Lost, all swap chain operations fail
static void ToMockDeviceLostCallback(WGPUDeviceLostReason reason,
const char* message,
void* userdata) {
DawnTest* self = static_cast<DawnTest*>(userdata);
self->StartExpectDeviceError();
}
// Test that new swap chain present fails after device is lost
TEST_P(SwapChainValidationTests, NewSwapChainPresentFailsAfterDeviceLost) {
device.SetDeviceLostCallback(ToMockDeviceLostCallback, this);
wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &goodDescriptor);
wgpu::TextureView view = swapchain.GetCurrentTextureView();
device.LoseForTesting();
LoseDeviceForTesting();
ASSERT_DEVICE_ERROR(swapchain.Present());
}
// Test that new swap chain get current texture view fails after device is lost
TEST_P(SwapChainValidationTests, NewSwapChainGetCurrentTextureViewFailsAfterDevLost) {
device.SetDeviceLostCallback(ToMockDeviceLostCallback, this);
wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &goodDescriptor);
device.LoseForTesting();
LoseDeviceForTesting();
ASSERT_DEVICE_ERROR(swapchain.GetCurrentTextureView());
}
// Test that creation of a new swapchain fails after device is lost
TEST_P(SwapChainValidationTests, CreateNewSwapChainFailsAfterDevLost) {
device.SetDeviceLostCallback(ToMockDeviceLostCallback, this);
device.LoseForTesting();
LoseDeviceForTesting();
ASSERT_DEVICE_ERROR(device.CreateSwapChain(surface, &goodDescriptor));
}