Introduce Device::GetDefaultQueue and deprecate CreateQueue

This makes all backends register the default queue at device
initialization time, so that the same queue is returned by
each call to GetDefaultQueue.

All usages of CreateQueue are replaced by GetDefaultQueue
except a couple ones that could use the queue initialized by
DawnTest::SetUp.

A deprecation warning mechanism is added so that users of Dawn
can now that they should upgrade their usage of the API. It also
comes with a backdoor so we can test that they are emitted.

New DeprecatedAPITests are added that will contain tests for
deprecated APIs, and will also check that deprecation warnings
are produced.

The special casing of GetDefaultQueue in the wire will be done
in a follow-up CL to ease the review. It happens to work through
the regular wire mechanisms at the moment but returns a different
object on each GetDefaultQueue call.

Bug: dawn:22

Change-Id: I78dc1fa474769674278d30040e8d05c658b88360
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/19724
Reviewed-by: Austin Eng <enga@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Corentin Wallez
2020-04-17 16:45:17 +00:00
committed by Commit Bot service account
parent 9f5a1c5141
commit 8a437947a8
40 changed files with 206 additions and 106 deletions

View File

@@ -50,9 +50,7 @@ if (!build_with_chromium) {
static_library("gtest") {
testonly = true
sources = [
"${googletest_dir}/googletest/src/gtest-all.cc",
]
sources = [ "${googletest_dir}/googletest/src/gtest-all.cc" ]
public_configs = [ ":gtest_config" ]
}
@@ -66,9 +64,7 @@ if (!build_with_chromium) {
static_library("gmock") {
testonly = true
sources = [
"${googletest_dir}/googlemock/src/gmock-all.cc",
]
sources = [ "${googletest_dir}/googlemock/src/gmock-all.cc" ]
public_configs = [ ":gmock_config" ]
}
@@ -241,6 +237,7 @@ source_set("dawn_end2end_tests_sources") {
"end2end/CopyTests.cpp",
"end2end/CullingTests.cpp",
"end2end/DebugMarkerTests.cpp",
"end2end/DeprecatedAPITests.cpp",
"end2end/DepthStencilStateTests.cpp",
"end2end/DestroyTests.cpp",
"end2end/DeviceLostTests.cpp",
@@ -257,6 +254,7 @@ source_set("dawn_end2end_tests_sources") {
"end2end/ObjectCachingTests.cpp",
"end2end/OpArrayLengthTests.cpp",
"end2end/PrimitiveTopologyTests.cpp",
"end2end/QueueTests.cpp",
"end2end/RenderBundleTests.cpp",
"end2end/RenderPassLoadOpTests.cpp",
"end2end/RenderPassTests.cpp",
@@ -325,9 +323,7 @@ source_set("dawn_white_box_tests_sources") {
"${dawn_root}/src/dawn_native:dawn_native_static",
]
sources = [
"DawnTest.h",
]
sources = [ "DawnTest.h" ]
if (dawn_enable_vulkan) {
deps += [ "${dawn_root}/third_party/khronos:vulkan_headers" ]

View File

@@ -729,7 +729,7 @@ void DawnTestBase::SetUp() {
// the deferred expectations.
dawnProcSetProcs(&procs);
device = wgpu::Device::Acquire(cDevice);
queue = device.CreateQueue();
queue = device.GetDefaultQueue();
device.SetUncapturedErrorCallback(OnDeviceError, this);
device.SetDeviceLostCallback(OnDeviceLost, this);

View File

@@ -92,7 +92,6 @@ TEST_P(ClipSpaceTest, ClipSpace) {
renderPass.Draw(6);
renderPass.EndPass();
wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
wgpu::Queue queue = device.CreateQueue();
queue.Submit(1, &commandBuffer);
EXPECT_PIXEL_RGBA8_EQ(RGBA8::kRed, colorTexture, kSize - 1, kSize - 1);

View File

@@ -87,7 +87,6 @@ class CullingTest : public DawnTest {
renderPass.Draw(6);
renderPass.EndPass();
wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
wgpu::Queue queue = device.CreateQueue();
queue.Submit(1, &commandBuffer);
const RGBA8 kBackgroundColor = RGBA8::kBlue;

View File

@@ -0,0 +1,58 @@
// Copyright 2020 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.
// This file contains test for deprecated parts of Dawn's API while following WebGPU's evolution.
// It contains test for the "old" behavior that will be deleted once users are migrated, tests that
// a deprecation warning is emitted when the "old" behavior is used, and tests that an error is
// emitted when both the old and the new behavior are used (when applicable).
#include "tests/DawnTest.h"
class DeprecationTests : public DawnTest {};
#define EXPECT_DEPRECATION_WARNING(statement) \
do { \
if (UsesWire()) { \
statement; \
} else { \
size_t warningsBefore = \
dawn_native::GetDeprecationWarningCountForTesting(device.Get()); \
statement; \
size_t warningsAfter = \
dawn_native::GetDeprecationWarningCountForTesting(device.Get()); \
EXPECT_EQ(warningsAfter, warningsBefore + 1); \
} \
} while (0)
// Tests for Device::CreateQueue -> Device::GetDefaultQueue.
// Test that using CreateQueue produces a deprecation warning
TEST_P(DeprecationTests, CreateQueueIsDeprecated) {
EXPECT_DEPRECATION_WARNING(device.CreateQueue());
}
// Test that queues created from CreateQueue can be used for things
TEST_P(DeprecationTests, CreateQueueReturnsFunctionalQueue) {
wgpu::Queue q;
EXPECT_DEPRECATION_WARNING(q = device.CreateQueue());
q.Submit(0, nullptr);
}
DAWN_INSTANTIATE_TEST(DeprecationTests,
D3D12Backend(),
MetalBackend(),
NullBackend(),
OpenGLBackend(),
VulkanBackend());

View File

@@ -238,7 +238,6 @@ TEST_P(MultisampledRenderingTest, ResolveInto2DTexture) {
}
wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
wgpu::Queue queue = device.CreateQueue();
queue.Submit(1, &commandBuffer);
VerifyResolveTarget(kGreen, mResolveTexture);
@@ -279,7 +278,6 @@ TEST_P(MultisampledRenderingTest, MultisampledRenderingWithDepthTest) {
}
wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
wgpu::Queue queue = device.CreateQueue();
queue.Submit(1, &commandBuffer);
// The color of the pixel in the middle of mResolveTexture should be green if MSAA resolve runs
@@ -317,7 +315,6 @@ TEST_P(MultisampledRenderingTest, ResolveInAnotherRenderPass) {
}
wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
wgpu::Queue queue = device.CreateQueue();
queue.Submit(1, &commandBuffer);
VerifyResolveTarget(kGreen, mResolveTexture);
@@ -351,7 +348,6 @@ TEST_P(MultisampledRenderingTest, ResolveIntoMultipleResolveTargets) {
}
wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
wgpu::Queue queue = device.CreateQueue();
queue.Submit(1, &commandBuffer);
VerifyResolveTarget(kRed, mResolveTexture);
@@ -390,7 +386,6 @@ TEST_P(MultisampledRenderingTest, ResolveOneMultisampledTextureTwice) {
}
wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
wgpu::Queue queue = device.CreateQueue();
queue.Submit(1, &commandBuffer);
VerifyResolveTarget(kGreen, mResolveTexture);
@@ -429,7 +424,6 @@ TEST_P(MultisampledRenderingTest, ResolveIntoOneMipmapLevelOf2DTexture) {
}
wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
wgpu::Queue queue = device.CreateQueue();
queue.Submit(1, &commandBuffer);
VerifyResolveTarget(kGreen, resolveTexture, kBaseMipLevel, 0);
@@ -488,7 +482,6 @@ TEST_P(MultisampledRenderingTest, ResolveInto2DArrayTexture) {
}
wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
wgpu::Queue queue = device.CreateQueue();
queue.Submit(1, &commandBuffer);
VerifyResolveTarget(kRed, resolveTexture1, kBaseMipLevel1, kBaseArrayLayer1);

View File

@@ -0,0 +1,36 @@
// Copyright 2020 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.
// This file contains test for deprecated parts of Dawn's API while following WebGPU's evolution.
// It contains test for the "old" behavior that will be deleted once users are migrated, tests that
// a deprecation warning is emitted when the "old" behavior is used, and tests that an error is
// emitted when both the old and the new behavior are used (when applicable).
#include "tests/DawnTest.h"
class QueueTests : public DawnTest {};
// Test that GetDefaultQueue always returns the same object.
TEST_P(QueueTests, GetDefaultQueueSameObject) {
wgpu::Queue q1 = device.GetDefaultQueue();
wgpu::Queue q2 = device.GetDefaultQueue();
EXPECT_EQ(q1.Get() == q2.Get(), !UsesWire());
}
DAWN_INSTANTIATE_TEST(QueueTests,
D3D12Backend(),
MetalBackend(),
NullBackend(),
OpenGLBackend(),
VulkanBackend());

View File

@@ -86,7 +86,6 @@ class SwapChainValidationTests : public DawnTest {
pass.EndPass();
wgpu::CommandBuffer commands = encoder.Finish();
wgpu::Queue queue = device.CreateQueue();
ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
}
};
@@ -244,7 +243,6 @@ TEST_P(SwapChainValidationTests, ReturnedViewCharacteristics) {
pass.EndPass();
wgpu::CommandBuffer commands = encoder.Finish();
wgpu::Queue queue = device.CreateQueue();
queue.Submit(1, &commands);
// Check that view doesn't have extra formats like Sampled.

View File

@@ -152,7 +152,6 @@ class ViewportTest : public DawnTest {
}
wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
wgpu::Queue queue = device.CreateQueue();
queue.Submit(1, &commandBuffer);
const RGBA8 kColor[ColorTypeCount] = {

View File

@@ -98,7 +98,7 @@ class BufferValidationTest : public ValidationTest {
mockBufferMapReadCallback = std::make_unique<MockBufferMapReadCallback>();
mockBufferMapWriteCallback = std::make_unique<MockBufferMapWriteCallback>();
queue = device.CreateQueue();
queue = device.GetDefaultQueue();
}
void TearDown() override {

View File

@@ -134,7 +134,7 @@ TEST_F(ErrorScopeValidationTest, PushPopBalanced) {
// Test that error scopes do not call their callbacks until after an enclosed Queue::Submit
// completes
TEST_F(ErrorScopeValidationTest, CallbackAfterQueueSubmit) {
wgpu::Queue queue = device.CreateQueue();
wgpu::Queue queue = device.GetDefaultQueue();
device.PushErrorScope(wgpu::ErrorFilter::OutOfMemory);
queue.Submit(0, nullptr);
@@ -149,7 +149,7 @@ TEST_F(ErrorScopeValidationTest, CallbackAfterQueueSubmit) {
// Test that parent error scopes do not call their callbacks until after an enclosed Queue::Submit
// completes
TEST_F(ErrorScopeValidationTest, CallbackAfterQueueSubmitNested) {
wgpu::Queue queue = device.CreateQueue();
wgpu::Queue queue = device.GetDefaultQueue();
device.PushErrorScope(wgpu::ErrorFilter::OutOfMemory);
device.PushErrorScope(wgpu::ErrorFilter::OutOfMemory);
@@ -167,7 +167,7 @@ TEST_F(ErrorScopeValidationTest, CallbackAfterQueueSubmitNested) {
// Test a callback that returns asynchronously followed by a synchronous one
TEST_F(ErrorScopeValidationTest, AsynchronousThenSynchronous) {
wgpu::Queue queue = device.CreateQueue();
wgpu::Queue queue = device.GetDefaultQueue();
device.PushErrorScope(wgpu::ErrorFilter::OutOfMemory);
queue.Submit(0, nullptr);
@@ -187,7 +187,7 @@ TEST_F(ErrorScopeValidationTest, AsynchronousThenSynchronous) {
// Test that if the device is destroyed before the callback occurs, it is called with NoError
// because all previous operations are waited upon before the destruction returns.
TEST_F(ErrorScopeValidationTest, DeviceDestroyedBeforeCallback) {
wgpu::Queue queue = device.CreateQueue();
wgpu::Queue queue = device.GetDefaultQueue();
device.PushErrorScope(wgpu::ErrorFilter::OutOfMemory);
queue.Submit(0, nullptr);

View File

@@ -57,7 +57,7 @@ class FenceValidationTest : public ValidationTest {
ValidationTest::SetUp();
mockFenceOnCompletionCallback = std::make_unique<MockFenceOnCompletionCallback>();
queue = device.CreateQueue();
queue = device.GetDefaultQueue();
}
void TearDown() override {
@@ -182,8 +182,9 @@ TEST_F(FenceValidationTest, SignalSuccess) {
}
// Test it is invalid to signal a fence on a different queue than it was created on
TEST_F(FenceValidationTest, SignalWrongQueue) {
wgpu::Queue queue2 = device.CreateQueue();
// DISABLED until we have support for multiple queues
TEST_F(FenceValidationTest, DISABLED_SignalWrongQueue) {
wgpu::Queue queue2 = device.GetDefaultQueue();
wgpu::FenceDescriptor descriptor;
descriptor.initialValue = 1;
@@ -193,8 +194,9 @@ TEST_F(FenceValidationTest, SignalWrongQueue) {
}
// Test that signaling a fence on a wrong queue does not update fence signaled value
TEST_F(FenceValidationTest, SignalWrongQueueDoesNotUpdateValue) {
wgpu::Queue queue2 = device.CreateQueue();
// DISABLED until we have support for multiple queues
TEST_F(FenceValidationTest, DISABLED_SignalWrongQueueDoesNotUpdateValue) {
wgpu::Queue queue2 = device.GetDefaultQueue();
wgpu::FenceDescriptor descriptor;
descriptor.initialValue = 1;

View File

@@ -48,7 +48,7 @@ TEST_F(QueueSubmitValidationTest, SubmitWithMappedBuffer) {
commands = encoder.Finish();
}
wgpu::Queue queue = device.CreateQueue();
wgpu::Queue queue = device.GetDefaultQueue();
// Submitting when the buffer has never been mapped should succeed
queue.Submit(1, &commands);

View File

@@ -23,6 +23,10 @@ namespace {
class TextureValidationTest : public ValidationTest {
protected:
void SetUp() override {
queue = device.GetDefaultQueue();
}
wgpu::TextureDescriptor CreateDefaultTextureDescriptor() {
wgpu::TextureDescriptor descriptor;
descriptor.size.width = kWidth;
@@ -37,7 +41,7 @@ class TextureValidationTest : public ValidationTest {
return descriptor;
}
wgpu::Queue queue = device.CreateQueue();
wgpu::Queue queue;
private:
static constexpr uint32_t kWidth = 32;

View File

@@ -233,9 +233,9 @@ TEST_F(WireArgumentTests, ObjectsAsPointerArgument) {
}
// Create queue
WGPUQueue queue = wgpuDeviceCreateQueue(device);
WGPUQueue queue = wgpuDeviceGetDefaultQueue(device);
WGPUQueue apiQueue = api.GetNewQueue();
EXPECT_CALL(api, DeviceCreateQueue(apiDevice)).WillOnce(Return(apiQueue));
EXPECT_CALL(api, DeviceGetDefaultQueue(apiDevice)).WillOnce(Return(apiQueue));
// Submit command buffer and check we got a call with both API-side command buffers
wgpuQueueSubmit(queue, 2, cmdBufs);

View File

@@ -44,9 +44,9 @@ class WireFenceTests : public WireTest {
std::make_unique<StrictMock<MockFenceOnCompletionCallback>>();
{
queue = wgpuDeviceCreateQueue(device);
queue = wgpuDeviceGetDefaultQueue(device);
apiQueue = api.GetNewQueue();
EXPECT_CALL(api, DeviceCreateQueue(apiDevice)).WillOnce(Return(apiQueue));
EXPECT_CALL(api, DeviceGetDefaultQueue(apiDevice)).WillOnce(Return(apiQueue));
FlushClient();
}
{
@@ -228,9 +228,9 @@ TEST_F(WireFenceTests, DestroyBeforeOnCompletionEnd) {
// Test that signaling a fence on a wrong queue is invalid
TEST_F(WireFenceTests, SignalWrongQueue) {
WGPUQueue queue2 = wgpuDeviceCreateQueue(device);
WGPUQueue queue2 = wgpuDeviceGetDefaultQueue(device);
WGPUQueue apiQueue2 = api.GetNewQueue();
EXPECT_CALL(api, DeviceCreateQueue(apiDevice)).WillOnce(Return(apiQueue2));
EXPECT_CALL(api, DeviceGetDefaultQueue(apiDevice)).WillOnce(Return(apiQueue2));
FlushClient();
wgpuQueueSignal(queue2, fence, 2u); // error
@@ -241,9 +241,9 @@ TEST_F(WireFenceTests, SignalWrongQueue) {
// Test that signaling a fence on a wrong queue does not update fence signaled value
TEST_F(WireFenceTests, SignalWrongQueueDoesNotUpdateValue) {
WGPUQueue queue2 = wgpuDeviceCreateQueue(device);
WGPUQueue queue2 = wgpuDeviceGetDefaultQueue(device);
WGPUQueue apiQueue2 = api.GetNewQueue();
EXPECT_CALL(api, DeviceCreateQueue(apiDevice)).WillOnce(Return(apiQueue2));
EXPECT_CALL(api, DeviceGetDefaultQueue(apiDevice)).WillOnce(Return(apiQueue2));
FlushClient();
wgpuQueueSignal(queue2, fence, 2u); // error

View File

@@ -135,14 +135,14 @@ TEST_F(WireMultipleDeviceTests, ValidatesSameDevice) {
WireHolder wireB;
// Create the objects
WGPUQueue queueA = wgpuDeviceCreateQueue(wireA.ClientDevice());
WGPUQueue queueB = wgpuDeviceCreateQueue(wireB.ClientDevice());
WGPUQueue queueA = wgpuDeviceGetDefaultQueue(wireA.ClientDevice());
WGPUQueue queueB = wgpuDeviceGetDefaultQueue(wireB.ClientDevice());
WGPUFenceDescriptor desc = {};
WGPUFence fenceA = wgpuQueueCreateFence(queueA, &desc);
// Flush on wire B. We should see the queue created.
EXPECT_CALL(*wireB.Api(), DeviceCreateQueue(wireB.ServerDevice()))
EXPECT_CALL(*wireB.Api(), DeviceGetDefaultQueue(wireB.ServerDevice()))
.WillOnce(Return(wireB.Api()->GetNewQueue()));
wireB.FlushClient();

View File

@@ -305,7 +305,7 @@ namespace dawn_native { namespace vulkan {
wgpu::CommandBuffer commands = encoder.Finish();
wgpu::Queue queue = dawnDevice.CreateQueue();
wgpu::Queue queue = dawnDevice.GetDefaultQueue();
queue.Submit(1, &commands);
}
@@ -482,7 +482,7 @@ namespace dawn_native { namespace vulkan {
ClearImage(secondDevice, copySrcTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
// Copy color B on |secondDevice|
wgpu::Queue secondDeviceQueue = secondDevice.CreateQueue();
wgpu::Queue secondDeviceQueue = secondDevice.GetDefaultQueue();
SimpleCopyTextureToTexture(secondDevice, secondDeviceQueue, copySrcTexture,
secondDeviceWrappedTexture);
@@ -578,7 +578,7 @@ namespace dawn_native { namespace vulkan {
secondDevice, &defaultDescriptor, nextFd, defaultStride, defaultModifier, {signalFd});
// Copy color B on |secondDevice|
wgpu::Queue secondDeviceQueue = secondDevice.CreateQueue();
wgpu::Queue secondDeviceQueue = secondDevice.GetDefaultQueue();
// Create a buffer on |secondDevice|
wgpu::Buffer copySrcBuffer =
@@ -681,8 +681,8 @@ namespace dawn_native { namespace vulkan {
wgpu::Device::Acquire(reinterpret_cast<WGPUDevice>(thirdDeviceVk));
// Make queue for device 2 and 3
wgpu::Queue secondDeviceQueue = secondDevice.CreateQueue();
wgpu::Queue thirdDeviceQueue = thirdDevice.CreateQueue();
wgpu::Queue secondDeviceQueue = secondDevice.GetDefaultQueue();
wgpu::Queue thirdDeviceQueue = thirdDevice.GetDefaultQueue();
// Create BOs for A, B, C
gbm_bo* gbmBoA = CreateGbmBo(1, 1, true /* linear */);
@@ -773,7 +773,7 @@ namespace dawn_native { namespace vulkan {
textures.push_back(device.CreateTexture(&descriptor));
}
wgpu::Queue secondDeviceQueue = secondDevice.CreateQueue();
wgpu::Queue secondDeviceQueue = secondDevice.GetDefaultQueue();
// Make an image on |secondDevice|
gbm_bo* gbmBo = CreateGbmBo(640, 480, false /* linear */);

View File

@@ -422,7 +422,7 @@ namespace dawn_native { namespace vulkan {
wgpu::CommandBuffer commands = encoder.Finish();
wgpu::Queue queue = dawnDevice.CreateQueue();
wgpu::Queue queue = dawnDevice.GetDefaultQueue();
queue.Submit(1, &commands);
}
@@ -618,7 +618,7 @@ namespace dawn_native { namespace vulkan {
ClearImage(secondDevice, copySrcTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
// Copy color B on |secondDevice|
wgpu::Queue secondDeviceQueue = secondDevice.CreateQueue();
wgpu::Queue secondDeviceQueue = secondDevice.GetDefaultQueue();
SimpleCopyTextureToTexture(secondDevice, secondDeviceQueue, copySrcTexture,
secondDeviceWrappedTexture);
@@ -722,7 +722,7 @@ namespace dawn_native { namespace vulkan {
defaultMemoryTypeIndex, {signalFd});
// Copy color B on |secondDevice|
wgpu::Queue secondDeviceQueue = secondDevice.CreateQueue();
wgpu::Queue secondDeviceQueue = secondDevice.GetDefaultQueue();
// Create a buffer on |secondDevice|
wgpu::Buffer copySrcBuffer =
@@ -831,8 +831,8 @@ namespace dawn_native { namespace vulkan {
wgpu::Device::Acquire(reinterpret_cast<WGPUDevice>(thirdDeviceVk));
// Make queue for device 2 and 3
wgpu::Queue secondDeviceQueue = secondDevice.CreateQueue();
wgpu::Queue thirdDeviceQueue = thirdDevice.CreateQueue();
wgpu::Queue secondDeviceQueue = secondDevice.GetDefaultQueue();
wgpu::Queue thirdDeviceQueue = thirdDevice.GetDefaultQueue();
// Allocate memory for A, B, C
VkImage imageA;
@@ -944,7 +944,7 @@ namespace dawn_native { namespace vulkan {
textures.push_back(device.CreateTexture(&descriptor));
}
wgpu::Queue secondDeviceQueue = secondDevice.CreateQueue();
wgpu::Queue secondDeviceQueue = secondDevice.GetDefaultQueue();
// Make an image on |secondDevice|
VkImage imageA;