| // Copyright 2021 The Dawn & Tint Authors |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are met: |
| // |
| // 1. Redistributions of source code must retain the above copyright notice, this |
| // list of conditions and the following disclaimer. |
| // |
| // 2. Redistributions in binary form must reproduce the above copyright notice, |
| // this list of conditions and the following disclaimer in the documentation |
| // and/or other materials provided with the distribution. |
| // |
| // 3. Neither the name of the copyright holder nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include <initializer_list> |
| #include <unordered_set> |
| #include <vector> |
| |
| #include "dawn/common/Constants.h" |
| #include "dawn/common/StringViewUtils.h" |
| #include "dawn/tests/MockCallback.h" |
| #include "dawn/tests/StringViewMatchers.h" |
| #include "dawn/tests/unittests/wire/WireFutureTest.h" |
| #include "dawn/tests/unittests/wire/WireTest.h" |
| |
| #include "dawn/wire/WireClient.h" |
| #include "dawn/wire/WireServer.h" |
| |
| #include "webgpu/webgpu_cpp.h" |
| |
| namespace dawn::wire { |
| namespace { |
| |
| using testing::_; |
| using testing::EmptySizedString; |
| using testing::InvokeWithoutArgs; |
| using testing::IsNull; |
| using testing::NonEmptySizedString; |
| using testing::NotNull; |
| using testing::Return; |
| using testing::SizedString; |
| using testing::StrEq; |
| using testing::WithArg; |
| |
| class WireInstanceBasicTest : public WireTest {}; |
| |
| // Test that an Instance can be reserved and injected into the wire. |
| TEST_F(WireInstanceBasicTest, ReserveAndInject) { |
| auto reserved = GetWireClient()->ReserveInstance(); |
| wgpu::Instance instance = wgpu::Instance::Acquire(reserved.instance); |
| |
| WGPUInstance apiInstance = api.GetNewInstance(); |
| EXPECT_CALL(api, InstanceAddRef(apiInstance)); |
| EXPECT_TRUE(GetWireServer()->InjectInstance(apiInstance, reserved.handle)); |
| |
| instance = nullptr; |
| |
| EXPECT_CALL(api, InstanceRelease(apiInstance)); |
| FlushClient(); |
| } |
| |
| using WireInstanceTestBase = WireFutureTest<wgpu::RequestAdapterCallback<void>*>; |
| class WireInstanceTests : public WireInstanceTestBase { |
| protected: |
| void RequestAdapter(wgpu::RequestAdapterOptions const* options) { |
| this->mFutureIDs.push_back( |
| instance |
| .RequestAdapter(options, this->GetParam().callbackMode, this->mMockCb.Callback()) |
| .id); |
| } |
| }; |
| |
| DAWN_INSTANTIATE_WIRE_FUTURE_TEST_P(WireInstanceTests); |
| |
| // Test that RequestAdapterOptions are passed from the client to the server. |
| TEST_P(WireInstanceTests, RequestAdapterPassesOptions) { |
| for (auto powerPreference : |
| {wgpu::PowerPreference::LowPower, wgpu::PowerPreference::HighPerformance}) { |
| wgpu::RequestAdapterOptions options = {}; |
| options.powerPreference = powerPreference; |
| |
| RequestAdapter(&options); |
| |
| EXPECT_CALL(api, OnInstanceRequestAdapter(apiInstance, NotNull(), _)) |
| .WillOnce(WithArg<1>([&](const WGPURequestAdapterOptions* apiOptions) { |
| EXPECT_EQ(apiOptions->powerPreference, |
| static_cast<WGPUPowerPreference>(options.powerPreference)); |
| EXPECT_EQ(apiOptions->forceFallbackAdapter, options.forceFallbackAdapter); |
| api.CallInstanceRequestAdapterCallback(apiInstance, WGPURequestAdapterStatus_Error, |
| nullptr, kEmptyOutputStringView); |
| })); |
| |
| FlushClient(); |
| FlushFutures(); |
| ExpectWireCallbacksWhen([&](auto& mockCb) { |
| EXPECT_CALL(mockCb, Call).Times(1); |
| |
| FlushCallbacks(); |
| }); |
| } |
| } |
| |
| // Test that RequestAdapter forwards the adapter information to the client. |
| TEST_P(WireInstanceTests, RequestAdapterSuccess) { |
| wgpu::RequestAdapterOptions options = {}; |
| RequestAdapter(&options); |
| |
| WGPUAdapterInfo fakeInfo = {}; |
| fakeInfo.vendor = ToOutputStringView("fake-vendor"); |
| fakeInfo.architecture = ToOutputStringView("fake-architecture"); |
| fakeInfo.device = ToOutputStringView("fake-device"); |
| fakeInfo.description = ToOutputStringView("fake-description"); |
| fakeInfo.backendType = WGPUBackendType_D3D12; |
| fakeInfo.adapterType = WGPUAdapterType_IntegratedGPU; |
| fakeInfo.vendorID = 0x134; |
| fakeInfo.deviceID = 0x918; |
| fakeInfo.subgroupMinSize = kDefaultSubgroupMinSize; |
| fakeInfo.subgroupMaxSize = kDefaultSubgroupMaxSize; |
| |
| wgpu::Limits fakeLimits = {}; |
| fakeLimits.maxTextureDimension1D = 433; |
| fakeLimits.maxVertexAttributes = 1243; |
| |
| std::initializer_list<WGPUFeatureName> fakeFeaturesList = { |
| WGPUFeatureName_Depth32FloatStencil8, |
| WGPUFeatureName_TextureCompressionBC, |
| }; |
| WGPUSupportedFeatures fakeFeatures = {fakeFeaturesList.size(), std::data(fakeFeaturesList)}; |
| |
| // Expect the server to receive the message. Then, mock a fake reply. |
| WGPUAdapter apiAdapter = api.GetNewAdapter(); |
| EXPECT_CALL(api, OnInstanceRequestAdapter(apiInstance, NotNull(), _)) |
| .WillOnce(InvokeWithoutArgs([&] { |
| EXPECT_CALL(api, AdapterHasFeature(apiAdapter, _)).WillRepeatedly(Return(false)); |
| |
| EXPECT_CALL(api, AdapterGetInfo(apiAdapter, NotNull())) |
| .WillOnce(WithArg<1>([&](WGPUAdapterInfo* info) { |
| *info = fakeInfo; |
| return WGPUStatus_Success; |
| })); |
| |
| EXPECT_CALL(api, AdapterGetLimits(apiAdapter, NotNull())) |
| .WillOnce(WithArg<1>([&](WGPULimits* limits) { |
| *reinterpret_cast<wgpu::Limits*>(limits) = fakeLimits; |
| return WGPUStatus_Success; |
| })); |
| |
| EXPECT_CALL(api, AdapterGetFeatures(apiAdapter, NotNull())) |
| .WillOnce( |
| WithArg<1>([&](WGPUSupportedFeatures* features) { *features = fakeFeatures; })); |
| |
| api.CallInstanceRequestAdapterCallback(apiInstance, WGPURequestAdapterStatus_Success, |
| apiAdapter, kEmptyOutputStringView); |
| })); |
| |
| FlushClient(); |
| FlushFutures(); |
| |
| // Expect the callback in the client and all the adapter information to match. |
| ExpectWireCallbacksWhen([&](auto& mockCb) { |
| EXPECT_CALL(mockCb, |
| Call(wgpu::RequestAdapterStatus::Success, NotNull(), EmptySizedString())) |
| .WillOnce(WithArg<1>([&](wgpu::Adapter adapter) { |
| WGPUAdapterInfo info = {}; |
| adapter.GetInfo(reinterpret_cast<wgpu::AdapterInfo*>(&info)); |
| EXPECT_NE(info.vendor.length, WGPU_STRLEN); |
| EXPECT_EQ(info.vendor, fakeInfo.vendor); |
| EXPECT_NE(info.architecture.length, WGPU_STRLEN); |
| EXPECT_EQ(info.architecture, fakeInfo.architecture); |
| EXPECT_NE(info.device.length, WGPU_STRLEN); |
| EXPECT_EQ(info.device, fakeInfo.device); |
| EXPECT_NE(info.description.length, WGPU_STRLEN); |
| EXPECT_EQ(info.description, fakeInfo.description); |
| EXPECT_EQ(info.backendType, fakeInfo.backendType); |
| EXPECT_EQ(info.adapterType, fakeInfo.adapterType); |
| EXPECT_EQ(info.vendorID, fakeInfo.vendorID); |
| EXPECT_EQ(info.deviceID, fakeInfo.deviceID); |
| EXPECT_EQ(info.subgroupMinSize, fakeInfo.subgroupMinSize); |
| EXPECT_EQ(info.subgroupMaxSize, fakeInfo.subgroupMaxSize); |
| |
| wgpu::Limits limits = {}; |
| EXPECT_EQ(adapter.GetLimits(&limits), wgpu::Status::Success); |
| EXPECT_EQ(limits.maxTextureDimension1D, fakeLimits.maxTextureDimension1D); |
| EXPECT_EQ(limits.maxVertexAttributes, fakeLimits.maxVertexAttributes); |
| |
| WGPUSupportedFeatures features = {}; |
| adapter.GetFeatures(reinterpret_cast<wgpu::SupportedFeatures*>(&features)); |
| |
| std::vector<WGPUFeatureName> featuresList( |
| features.features, features.features + features.featureCount); |
| ASSERT_EQ(featuresList.size(), fakeFeaturesList.size()); |
| std::unordered_set<WGPUFeatureName> featureSet(fakeFeaturesList); |
| for (WGPUFeatureName feature : featuresList) { |
| EXPECT_EQ(featureSet.erase(feature), 1u); |
| } |
| })); |
| |
| FlushCallbacks(); |
| }); |
| } |
| |
| // Test that RequestAdapter forwards all chained properties to the client. |
| TEST_P(WireInstanceTests, RequestAdapterPassesChainedProperties) { |
| wgpu::RequestAdapterOptions options = {}; |
| RequestAdapter(&options); |
| |
| WGPUMemoryHeapInfo fakeHeapInfo[3] = { |
| {WGPUHeapProperty_DeviceLocal, 64}, |
| {WGPUHeapProperty_DeviceLocal | WGPUHeapProperty_HostVisible, 136}, |
| {WGPUHeapProperty_HostCached | WGPUHeapProperty_HostVisible, 460}, |
| }; |
| |
| WGPUAdapterPropertiesMemoryHeaps fakeMemoryHeapProperties = {}; |
| fakeMemoryHeapProperties.chain.sType = WGPUSType_AdapterPropertiesMemoryHeaps; |
| fakeMemoryHeapProperties.heapCount = 3; |
| fakeMemoryHeapProperties.heapInfo = fakeHeapInfo; |
| |
| WGPUAdapterPropertiesD3D fakeD3DProperties = {}; |
| fakeD3DProperties.chain.sType = WGPUSType_AdapterPropertiesD3D; |
| fakeD3DProperties.shaderModel = 61; |
| |
| WGPUAdapterPropertiesVk fakeVkProperties = {}; |
| fakeVkProperties.chain.sType = WGPUSType_AdapterPropertiesVk; |
| fakeVkProperties.driverVersion = 0x801F6000; |
| |
| WGPUSubgroupMatrixConfig fakeMatrixConfigs[3] = { |
| {WGPUSubgroupMatrixComponentType_F32, WGPUSubgroupMatrixComponentType_F32, 8, 4, 2}, |
| {WGPUSubgroupMatrixComponentType_U32, WGPUSubgroupMatrixComponentType_I32, 4, 8, 16}, |
| {WGPUSubgroupMatrixComponentType_F16, WGPUSubgroupMatrixComponentType_F32, 2, 16, 4}, |
| }; |
| |
| WGPUAdapterPropertiesSubgroupMatrixConfigs fakeSubgroupMatrixConfigs = {}; |
| fakeSubgroupMatrixConfigs.chain.sType = WGPUSType_AdapterPropertiesSubgroupMatrixConfigs; |
| fakeSubgroupMatrixConfigs.configCount = 3; |
| fakeSubgroupMatrixConfigs.configs = fakeMatrixConfigs; |
| |
| WGPUDawnAdapterPropertiesPowerPreference fakePowerProperties = {}; |
| fakePowerProperties.chain.sType = WGPUSType_DawnAdapterPropertiesPowerPreference; |
| fakePowerProperties.powerPreference = WGPUPowerPreference::WGPUPowerPreference_LowPower; |
| |
| WGPUAdapterPropertiesExplicitComputeSubgroupSizeConfigs fakeExplicitComputeSubgroupSizeConfigs = |
| {}; |
| fakeExplicitComputeSubgroupSizeConfigs.chain.sType = |
| WGPUSType_AdapterPropertiesExplicitComputeSubgroupSizeConfigs; |
| fakeExplicitComputeSubgroupSizeConfigs.minExplicitComputeSubgroupSize = 8; |
| fakeExplicitComputeSubgroupSizeConfigs.maxExplicitComputeSubgroupSize = 32; |
| |
| std::initializer_list<WGPUFeatureName> fakeFeaturesList = { |
| WGPUFeatureName_AdapterPropertiesMemoryHeaps, |
| WGPUFeatureName_AdapterPropertiesD3D, |
| WGPUFeatureName_AdapterPropertiesVk, |
| WGPUFeatureName_ChromiumExperimentalSubgroupMatrix, |
| WGPUFeatureName_ChromiumExperimentalSubgroupSizeControl, |
| }; |
| WGPUSupportedFeatures fakeFeatures = {fakeFeaturesList.size(), std::data(fakeFeaturesList)}; |
| |
| // Expect the server to receive the message. Then, mock a fake reply. |
| WGPUAdapter apiAdapter = api.GetNewAdapter(); |
| EXPECT_CALL(api, OnInstanceRequestAdapter(apiInstance, NotNull(), _)) |
| .WillOnce(InvokeWithoutArgs([&] { |
| EXPECT_CALL(api, AdapterGetLimits(apiAdapter, NotNull())).Times(1); |
| EXPECT_CALL(api, AdapterGetFeatures(apiAdapter, NotNull())) |
| .WillOnce( |
| WithArg<1>([&](WGPUSupportedFeatures* features) { *features = fakeFeatures; })); |
| |
| for (WGPUFeatureName feature : fakeFeaturesList) { |
| EXPECT_CALL(api, AdapterHasFeature(apiAdapter, feature)).WillOnce(Return(true)); |
| } |
| EXPECT_CALL(api, AdapterGetInfo(apiAdapter, NotNull())) |
| .WillOnce(WithArg<1>([&](WGPUAdapterInfo* info) { |
| WGPUChainedStruct* chain = info->nextInChain; |
| while (chain != nullptr) { |
| auto* next = chain->next; |
| switch (chain->sType) { |
| case WGPUSType_AdapterPropertiesMemoryHeaps: |
| *reinterpret_cast<WGPUAdapterPropertiesMemoryHeaps*>(chain) = |
| fakeMemoryHeapProperties; |
| break; |
| case WGPUSType_AdapterPropertiesD3D: |
| *reinterpret_cast<WGPUAdapterPropertiesD3D*>(chain) = |
| fakeD3DProperties; |
| break; |
| case WGPUSType_AdapterPropertiesVk: |
| *reinterpret_cast<WGPUAdapterPropertiesVk*>(chain) = |
| fakeVkProperties; |
| break; |
| case WGPUSType_AdapterPropertiesSubgroupMatrixConfigs: |
| *reinterpret_cast<WGPUAdapterPropertiesSubgroupMatrixConfigs*>( |
| chain) = fakeSubgroupMatrixConfigs; |
| break; |
| case WGPUSType_DawnAdapterPropertiesPowerPreference: |
| *reinterpret_cast<WGPUDawnAdapterPropertiesPowerPreference*>( |
| chain) = fakePowerProperties; |
| break; |
| case WGPUSType_AdapterPropertiesExplicitComputeSubgroupSizeConfigs: |
| *reinterpret_cast< |
| WGPUAdapterPropertiesExplicitComputeSubgroupSizeConfigs*>( |
| chain) = fakeExplicitComputeSubgroupSizeConfigs; |
| break; |
| default: |
| ADD_FAILURE() << "Unexpected chain"; |
| return WGPUStatus_Error; |
| } |
| // Update next pointer back to the original since it would be overwritten |
| // in the switch statement |
| chain->next = next; |
| chain = next; |
| } |
| return WGPUStatus_Success; |
| })); |
| |
| api.CallInstanceRequestAdapterCallback(apiInstance, WGPURequestAdapterStatus_Success, |
| apiAdapter, kEmptyOutputStringView); |
| })); |
| |
| FlushClient(); |
| FlushFutures(); |
| |
| // Expect the callback in the client and the adapter information to match. |
| ExpectWireCallbacksWhen([&](auto& mockCb) { |
| EXPECT_CALL(mockCb, |
| Call(wgpu::RequestAdapterStatus::Success, NotNull(), EmptySizedString())) |
| .WillOnce(WithArg<1>([&](wgpu::Adapter adapter) { |
| // Request info without a chained struct. |
| // It should be nullptr. |
| WGPUAdapterInfo info = {}; |
| adapter.GetInfo(reinterpret_cast<wgpu::AdapterInfo*>(&info)); |
| EXPECT_EQ(info.nextInChain, nullptr); |
| |
| // Request the memory heap properties. |
| WGPUAdapterPropertiesMemoryHeaps memoryHeapProperties = {}; |
| memoryHeapProperties.chain.sType = WGPUSType_AdapterPropertiesMemoryHeaps; |
| info.nextInChain = &memoryHeapProperties.chain; |
| adapter.GetInfo(reinterpret_cast<wgpu::AdapterInfo*>(&info)); |
| |
| // Expect everything matches the fake properties returned by the server. |
| EXPECT_EQ(memoryHeapProperties.heapCount, fakeMemoryHeapProperties.heapCount); |
| for (size_t i = 0; i < fakeMemoryHeapProperties.heapCount; ++i) { |
| EXPECT_EQ(memoryHeapProperties.heapInfo[i].properties, |
| fakeMemoryHeapProperties.heapInfo[i].properties); |
| EXPECT_EQ(memoryHeapProperties.heapInfo[i].size, |
| fakeMemoryHeapProperties.heapInfo[i].size); |
| } |
| |
| // Get the D3D properties. |
| WGPUAdapterPropertiesD3D d3dProperties = {}; |
| d3dProperties.chain.sType = WGPUSType_AdapterPropertiesD3D; |
| info.nextInChain = &d3dProperties.chain; |
| adapter.GetInfo(reinterpret_cast<wgpu::AdapterInfo*>(&info)); |
| // Expect them to match. |
| EXPECT_EQ(d3dProperties.shaderModel, fakeD3DProperties.shaderModel); |
| |
| // Get the Vulkan properties. |
| WGPUAdapterPropertiesVk vkProperties = {}; |
| vkProperties.chain.sType = WGPUSType_AdapterPropertiesVk; |
| info.nextInChain = &vkProperties.chain; |
| adapter.GetInfo(reinterpret_cast<wgpu::AdapterInfo*>(&info)); |
| // Expect them to match. |
| EXPECT_EQ(vkProperties.driverVersion, fakeVkProperties.driverVersion); |
| |
| // Get the subgroup matrix properties. |
| WGPUAdapterPropertiesSubgroupMatrixConfigs subgroupMatrixConfigs = {}; |
| subgroupMatrixConfigs.chain.sType = |
| WGPUSType_AdapterPropertiesSubgroupMatrixConfigs; |
| info.nextInChain = &subgroupMatrixConfigs.chain; |
| adapter.GetInfo(reinterpret_cast<wgpu::AdapterInfo*>(&info)); |
| |
| // Expect everything matches the fake properties returned by the server. |
| EXPECT_EQ(subgroupMatrixConfigs.configCount, fakeSubgroupMatrixConfigs.configCount); |
| for (size_t i = 0; i < fakeSubgroupMatrixConfigs.configCount; ++i) { |
| EXPECT_EQ(subgroupMatrixConfigs.configs[i].componentType, |
| fakeSubgroupMatrixConfigs.configs[i].componentType); |
| EXPECT_EQ(subgroupMatrixConfigs.configs[i].resultComponentType, |
| fakeSubgroupMatrixConfigs.configs[i].resultComponentType); |
| EXPECT_EQ(subgroupMatrixConfigs.configs[i].M, |
| fakeSubgroupMatrixConfigs.configs[i].M); |
| EXPECT_EQ(subgroupMatrixConfigs.configs[i].N, |
| fakeSubgroupMatrixConfigs.configs[i].N); |
| EXPECT_EQ(subgroupMatrixConfigs.configs[i].K, |
| fakeSubgroupMatrixConfigs.configs[i].K); |
| } |
| |
| // Get the power properties. |
| WGPUDawnAdapterPropertiesPowerPreference powerProperties = {}; |
| powerProperties.chain.sType = WGPUSType_DawnAdapterPropertiesPowerPreference; |
| info.nextInChain = &powerProperties.chain; |
| adapter.GetInfo(reinterpret_cast<wgpu::AdapterInfo*>(&info)); |
| // Expect them to match. |
| EXPECT_EQ(powerProperties.powerPreference, fakePowerProperties.powerPreference); |
| |
| // Get the explicit compute subgroup size properties |
| WGPUAdapterPropertiesExplicitComputeSubgroupSizeConfigs subgroupSizeConfigs = {}; |
| subgroupSizeConfigs.chain.sType = |
| WGPUSType_AdapterPropertiesExplicitComputeSubgroupSizeConfigs; |
| info.nextInChain = &subgroupSizeConfigs.chain; |
| adapter.GetInfo(reinterpret_cast<wgpu::AdapterInfo*>(&info)); |
| // Expect them to match |
| EXPECT_EQ(subgroupSizeConfigs.minExplicitComputeSubgroupSize, |
| fakeExplicitComputeSubgroupSizeConfigs.minExplicitComputeSubgroupSize); |
| EXPECT_EQ(subgroupSizeConfigs.maxExplicitComputeSubgroupSize, |
| fakeExplicitComputeSubgroupSizeConfigs.maxExplicitComputeSubgroupSize); |
| })); |
| |
| FlushCallbacks(); |
| }); |
| } |
| |
| // Test that features returned by the implementation that aren't supported in the wire are not |
| // exposed. |
| TEST_P(WireInstanceTests, RequestAdapterWireLacksFeatureSupport) { |
| wgpu::RequestAdapterOptions options = {}; |
| RequestAdapter(&options); |
| |
| std::initializer_list<WGPUFeatureName> fakeFeaturesList = { |
| WGPUFeatureName_Depth32FloatStencil8, |
| // Default feature is an undefined feature. |
| {}, |
| }; |
| WGPUSupportedFeatures fakeFeatures = {fakeFeaturesList.size(), std::data(fakeFeaturesList)}; |
| |
| // Expect the server to receive the message. Then, mock a fake reply. |
| WGPUAdapter apiAdapter = api.GetNewAdapter(); |
| EXPECT_CALL(api, OnInstanceRequestAdapter(apiInstance, NotNull(), _)) |
| .WillOnce(InvokeWithoutArgs([&] { |
| EXPECT_CALL(api, AdapterHasFeature(apiAdapter, _)).WillRepeatedly(Return(false)); |
| EXPECT_CALL(api, AdapterGetInfo(apiAdapter, NotNull())).Times(1); |
| EXPECT_CALL(api, AdapterGetLimits(apiAdapter, NotNull())).Times(1); |
| |
| EXPECT_CALL(api, AdapterGetFeatures(apiAdapter, NotNull())) |
| .WillOnce( |
| WithArg<1>([&](WGPUSupportedFeatures* features) { *features = fakeFeatures; })); |
| |
| api.CallInstanceRequestAdapterCallback(apiInstance, WGPURequestAdapterStatus_Success, |
| apiAdapter, kEmptyOutputStringView); |
| })); |
| |
| FlushClient(); |
| FlushFutures(); |
| |
| // Expect the callback in the client and all the adapter information to match. |
| ExpectWireCallbacksWhen([&](auto& mockCb) { |
| EXPECT_CALL(mockCb, |
| Call(wgpu::RequestAdapterStatus::Success, NotNull(), EmptySizedString())) |
| .WillOnce(WithArg<1>([&](wgpu::Adapter adapter) { |
| WGPUSupportedFeatures features = {}; |
| adapter.GetFeatures(reinterpret_cast<wgpu::SupportedFeatures*>(&features)); |
| ASSERT_EQ(features.featureCount, 1u); |
| EXPECT_EQ(*features.features, WGPUFeatureName_Depth32FloatStencil8); |
| })); |
| |
| FlushCallbacks(); |
| }); |
| } |
| |
| // Test that RequestAdapter errors forward to the client. |
| TEST_P(WireInstanceTests, RequestAdapterError) { |
| wgpu::RequestAdapterOptions options = {}; |
| RequestAdapter(&options); |
| |
| // Expect the server to receive the message. Then, mock an error. |
| EXPECT_CALL(api, OnInstanceRequestAdapter(apiInstance, NotNull(), _)) |
| .WillOnce(InvokeWithoutArgs([&] { |
| api.CallInstanceRequestAdapterCallback(apiInstance, WGPURequestAdapterStatus_Error, |
| nullptr, ToOutputStringView("Some error")); |
| })); |
| |
| FlushClient(); |
| FlushFutures(); |
| |
| // Expect the callback in the client. |
| ExpectWireCallbacksWhen([&](auto& mockCb) { |
| EXPECT_CALL(mockCb, |
| Call(wgpu::RequestAdapterStatus::Error, IsNull(), SizedString("Some error"))) |
| .Times(1); |
| |
| FlushCallbacks(); |
| }); |
| } |
| |
| // Test that RequestAdapter receives unknown status if the instance is deleted before the callback |
| // happens. |
| TEST_P(WireInstanceTests, RequestAdapterInstanceDestroyedBeforeCallback) { |
| // For spontaneous, dropping the instance does not immediately call the callback because it is |
| // allowed to resolve later. |
| DAWN_SKIP_TEST_IF(IsSpontaneous()); |
| |
| wgpu::RequestAdapterOptions options = {}; |
| RequestAdapter(&options); |
| |
| ExpectWireCallbacksWhen([&](auto& mockCb) { |
| EXPECT_CALL(mockCb, Call(wgpu::RequestAdapterStatus::CallbackCancelled, IsNull(), |
| NonEmptySizedString())) |
| .Times(1); |
| |
| instance = nullptr; |
| }); |
| } |
| |
| // Test that RequestAdapter receives unknown status if the wire is disconnected |
| // before the callback happens. |
| TEST_P(WireInstanceTests, RequestAdapterWireDisconnectBeforeCallback) { |
| wgpu::RequestAdapterOptions options = {}; |
| RequestAdapter(&options); |
| |
| ExpectWireCallbacksWhen([&](auto& mockCb) { |
| EXPECT_CALL(mockCb, Call(wgpu::RequestAdapterStatus::CallbackCancelled, IsNull(), |
| NonEmptySizedString())) |
| .Times(1); |
| |
| GetWireClient()->Disconnect(); |
| }); |
| } |
| |
| } // anonymous namespace |
| } // namespace dawn::wire |