| // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <stdint.h> |
| |
| #include <memory> |
| #include <vector> |
| |
| #include "gpu/config/gpu_control_list.h" |
| #include "gpu/config/gpu_info.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| const char kOsVersion[] = "10.6.4"; |
| const uint32_t kIntelVendorId = 0x8086; |
| const uint32_t kNvidiaVendorId = 0x10de; |
| const uint32_t kAmdVendorId = 0x10de; |
| |
| #define LONG_STRING_CONST(...) #__VA_ARGS__ |
| |
| #define EXPECT_EMPTY_SET(feature_set) EXPECT_EQ(0u, feature_set.size()) |
| #define EXPECT_SINGLE_FEATURE(feature_set, feature) \ |
| EXPECT_TRUE(feature_set.size() == 1 && feature_set.count(feature) == 1) |
| |
| namespace gpu { |
| |
| enum TestFeatureType { |
| TEST_FEATURE_0 = 1, |
| TEST_FEATURE_1 = 1 << 2, |
| TEST_FEATURE_2 = 1 << 3, |
| }; |
| |
| class GpuControlListTest : public testing::Test { |
| public: |
| GpuControlListTest() { } |
| |
| ~GpuControlListTest() override {} |
| |
| const GPUInfo& gpu_info() const { |
| return gpu_info_; |
| } |
| |
| GpuControlList* Create() { |
| GpuControlList* rt = new GpuControlList(); |
| rt->AddSupportedFeature("test_feature_0", TEST_FEATURE_0); |
| rt->AddSupportedFeature("test_feature_1", TEST_FEATURE_1); |
| rt->AddSupportedFeature("test_feature_2", TEST_FEATURE_2); |
| return rt; |
| } |
| |
| protected: |
| void SetUp() override { |
| gpu_info_.gpu.vendor_id = kNvidiaVendorId; |
| gpu_info_.gpu.device_id = 0x0640; |
| gpu_info_.driver_vendor = "NVIDIA"; |
| gpu_info_.driver_version = "1.6.18"; |
| gpu_info_.driver_date = "7-14-2009"; |
| gpu_info_.machine_model_name = "MacBookPro"; |
| gpu_info_.machine_model_version = "7.1"; |
| gpu_info_.gl_vendor = "NVIDIA Corporation"; |
| gpu_info_.gl_renderer = "NVIDIA GeForce GT 120 OpenGL Engine"; |
| } |
| |
| void TearDown() override {} |
| |
| private: |
| GPUInfo gpu_info_; |
| }; |
| |
| TEST_F(GpuControlListTest, DefaultControlListSettings) { |
| std::unique_ptr<GpuControlList> control_list(Create()); |
| // Default control list settings: all feature are allowed. |
| std::set<int> features = control_list->MakeDecision( |
| GpuControlList::kOsMacosx, kOsVersion, gpu_info()); |
| EXPECT_EMPTY_SET(features); |
| } |
| |
| TEST_F(GpuControlListTest, EmptyControlList) { |
| // Empty list: all features are allowed. |
| const std::string empty_list_json = LONG_STRING_CONST( |
| { |
| "name": "gpu control list", |
| "version": "2.5", |
| "entries": [ |
| ] |
| } |
| ); |
| std::unique_ptr<GpuControlList> control_list(Create()); |
| |
| EXPECT_TRUE(control_list->LoadList(empty_list_json, |
| GpuControlList::kAllOs)); |
| EXPECT_EQ("2.5", control_list->version()); |
| std::set<int> features = control_list->MakeDecision( |
| GpuControlList::kOsMacosx, kOsVersion, gpu_info()); |
| EXPECT_EMPTY_SET(features); |
| } |
| |
| TEST_F(GpuControlListTest, DetailedEntryAndInvalidJson) { |
| // exact setting. |
| const std::string exact_list_json = LONG_STRING_CONST( |
| { |
| "name": "gpu control list", |
| "version": "0.1", |
| "entries": [ |
| { |
| "id": 5, |
| "os": { |
| "type": "macosx", |
| "version": { |
| "op": "=", |
| "value": "10.6.4" |
| } |
| }, |
| "vendor_id": "0x10de", |
| "device_id": ["0x0640"], |
| "driver_version": { |
| "op": "=", |
| "value": "1.6.18" |
| }, |
| "features": [ |
| "test_feature_0" |
| ] |
| } |
| ] |
| } |
| ); |
| std::unique_ptr<GpuControlList> control_list(Create()); |
| |
| EXPECT_TRUE(control_list->LoadList(exact_list_json, GpuControlList::kAllOs)); |
| std::set<int> features = control_list->MakeDecision( |
| GpuControlList::kOsMacosx, kOsVersion, gpu_info()); |
| EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0); |
| |
| // Invalid json input should not change the current control_list settings. |
| const std::string invalid_json = "invalid"; |
| |
| EXPECT_FALSE(control_list->LoadList(invalid_json, GpuControlList::kAllOs)); |
| features = control_list->MakeDecision( |
| GpuControlList::kOsMacosx, kOsVersion, gpu_info()); |
| EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0); |
| std::vector<uint32_t> entries; |
| control_list->GetDecisionEntries(&entries, false); |
| ASSERT_EQ(1u, entries.size()); |
| EXPECT_EQ(5u, entries[0]); |
| EXPECT_EQ(5u, control_list->max_entry_id()); |
| } |
| |
| TEST_F(GpuControlListTest, VendorOnAllOsEntry) { |
| // ControlList a vendor on all OS. |
| const std::string vendor_json = LONG_STRING_CONST( |
| { |
| "name": "gpu control list", |
| "version": "0.1", |
| "entries": [ |
| { |
| "id": 1, |
| "vendor_id": "0x10de", |
| "features": [ |
| "test_feature_0" |
| ] |
| } |
| ] |
| } |
| ); |
| std::unique_ptr<GpuControlList> control_list(Create()); |
| |
| // ControlList entries won't be filtered to the current OS only upon loading. |
| EXPECT_TRUE(control_list->LoadList(vendor_json, GpuControlList::kAllOs)); |
| std::set<int> features = control_list->MakeDecision( |
| GpuControlList::kOsMacosx, kOsVersion, gpu_info()); |
| EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0); |
| features = control_list->MakeDecision( |
| GpuControlList::kOsWin, kOsVersion, gpu_info()); |
| EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0); |
| features = control_list->MakeDecision( |
| GpuControlList::kOsLinux, kOsVersion, gpu_info()); |
| EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0); |
| #if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_MACOSX) || \ |
| defined(OS_OPENBSD) |
| // ControlList entries will be filtered to the current OS only upon loading. |
| EXPECT_TRUE(control_list->LoadList( |
| vendor_json, GpuControlList::kCurrentOsOnly)); |
| features = control_list->MakeDecision( |
| GpuControlList::kOsMacosx, kOsVersion, gpu_info()); |
| EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0); |
| features = control_list->MakeDecision( |
| GpuControlList::kOsWin, kOsVersion, gpu_info()); |
| EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0); |
| features = control_list->MakeDecision( |
| GpuControlList::kOsLinux, kOsVersion, gpu_info()); |
| EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0); |
| #endif |
| } |
| |
| TEST_F(GpuControlListTest, UnknownField) { |
| const std::string unknown_field_json = LONG_STRING_CONST( |
| { |
| "name": "gpu control list", |
| "version": "0.1", |
| "entries": [ |
| { |
| "id": 1, |
| "unknown_field": 0, |
| "features": [ |
| "test_feature_1" |
| ] |
| }, |
| { |
| "id": 2, |
| "features": [ |
| "test_feature_0" |
| ] |
| } |
| ] |
| } |
| ); |
| std::unique_ptr<GpuControlList> control_list(Create()); |
| |
| EXPECT_FALSE(control_list->LoadList( |
| unknown_field_json, GpuControlList::kAllOs)); |
| } |
| |
| TEST_F(GpuControlListTest, UnknownExceptionField) { |
| const std::string unknown_exception_field_json = LONG_STRING_CONST( |
| { |
| "name": "gpu control list", |
| "version": "0.1", |
| "entries": [ |
| { |
| "id": 1, |
| "unknown_field": 0, |
| "features": [ |
| "test_feature_2" |
| ] |
| }, |
| { |
| "id": 2, |
| "exceptions": [ |
| { |
| "unknown_field": 0 |
| } |
| ], |
| "features": [ |
| "test_feature_1" |
| ] |
| }, |
| { |
| "id": 3, |
| "features": [ |
| "test_feature_0" |
| ] |
| } |
| ] |
| } |
| ); |
| std::unique_ptr<GpuControlList> control_list(Create()); |
| |
| EXPECT_FALSE(control_list->LoadList( |
| unknown_exception_field_json, GpuControlList::kAllOs)); |
| } |
| |
| TEST_F(GpuControlListTest, DisabledEntry) { |
| const std::string disabled_json = LONG_STRING_CONST( |
| { |
| "name": "gpu control list", |
| "version": "0.1", |
| "entries": [ |
| { |
| "id": 1, |
| "disabled": true, |
| "features": [ |
| "test_feature_0" |
| ] |
| } |
| ] |
| } |
| ); |
| std::unique_ptr<GpuControlList> control_list(Create()); |
| EXPECT_TRUE(control_list->LoadList(disabled_json, GpuControlList::kAllOs)); |
| std::set<int> features = control_list->MakeDecision( |
| GpuControlList::kOsWin, kOsVersion, gpu_info()); |
| EXPECT_EMPTY_SET(features); |
| std::vector<uint32_t> flag_entries; |
| control_list->GetDecisionEntries(&flag_entries, false); |
| EXPECT_EQ(0u, flag_entries.size()); |
| control_list->GetDecisionEntries(&flag_entries, true); |
| EXPECT_EQ(1u, flag_entries.size()); |
| } |
| |
| TEST_F(GpuControlListTest, NeedsMoreInfo) { |
| const std::string json = LONG_STRING_CONST( |
| { |
| "name": "gpu control list", |
| "version": "0.1", |
| "entries": [ |
| { |
| "id": 1, |
| "os": { |
| "type": "win" |
| }, |
| "vendor_id": "0x10de", |
| "driver_version": { |
| "op": "<", |
| "value": "12" |
| }, |
| "features": [ |
| "test_feature_0" |
| ] |
| } |
| ] |
| } |
| ); |
| GPUInfo gpu_info; |
| gpu_info.gpu.vendor_id = kNvidiaVendorId; |
| |
| std::unique_ptr<GpuControlList> control_list(Create()); |
| EXPECT_TRUE(control_list->LoadList(json, GpuControlList::kAllOs)); |
| |
| std::set<int> features = control_list->MakeDecision( |
| GpuControlList::kOsWin, kOsVersion, gpu_info); |
| EXPECT_EMPTY_SET(features); |
| EXPECT_TRUE(control_list->needs_more_info()); |
| std::vector<uint32_t> decision_entries; |
| control_list->GetDecisionEntries(&decision_entries, false); |
| EXPECT_EQ(0u, decision_entries.size()); |
| |
| gpu_info.driver_version = "11"; |
| features = control_list->MakeDecision( |
| GpuControlList::kOsWin, kOsVersion, gpu_info); |
| EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0); |
| EXPECT_FALSE(control_list->needs_more_info()); |
| control_list->GetDecisionEntries(&decision_entries, false); |
| EXPECT_EQ(1u, decision_entries.size()); |
| } |
| |
| TEST_F(GpuControlListTest, NeedsMoreInfoForExceptions) { |
| const std::string json = LONG_STRING_CONST( |
| { |
| "name": "gpu control list", |
| "version": "0.1", |
| "entries": [ |
| { |
| "id": 1, |
| "os": { |
| "type": "linux" |
| }, |
| "vendor_id": "0x8086", |
| "exceptions": [ |
| { |
| "gl_renderer": ".*mesa.*" |
| } |
| ], |
| "features": [ |
| "test_feature_0" |
| ] |
| } |
| ] |
| } |
| ); |
| GPUInfo gpu_info; |
| gpu_info.gpu.vendor_id = kIntelVendorId; |
| |
| std::unique_ptr<GpuControlList> control_list(Create()); |
| EXPECT_TRUE(control_list->LoadList(json, GpuControlList::kAllOs)); |
| |
| // The case this entry does not apply. |
| std::set<int> features = control_list->MakeDecision( |
| GpuControlList::kOsMacosx, kOsVersion, gpu_info); |
| EXPECT_EMPTY_SET(features); |
| EXPECT_FALSE(control_list->needs_more_info()); |
| |
| // The case this entry might apply, but need more info. |
| features = control_list->MakeDecision( |
| GpuControlList::kOsLinux, kOsVersion, gpu_info); |
| // Ignore exceptions if main entry info matches |
| EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0); |
| EXPECT_TRUE(control_list->needs_more_info()); |
| |
| // The case we have full info, and the exception applies (so the entry |
| // does not apply). |
| gpu_info.gl_renderer = "mesa"; |
| features = control_list->MakeDecision( |
| GpuControlList::kOsLinux, kOsVersion, gpu_info); |
| EXPECT_EMPTY_SET(features); |
| EXPECT_FALSE(control_list->needs_more_info()); |
| |
| // The case we have full info, and this entry applies. |
| gpu_info.gl_renderer = "my renderer"; |
| features = control_list->MakeDecision(GpuControlList::kOsLinux, kOsVersion, |
| gpu_info); |
| EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0); |
| EXPECT_FALSE(control_list->needs_more_info()); |
| } |
| |
| TEST_F(GpuControlListTest, IgnorableEntries) { |
| // If an entry will not change the control_list decisions, then it should not |
| // trigger the needs_more_info flag. |
| const std::string json = LONG_STRING_CONST( |
| { |
| "name": "gpu control list", |
| "version": "0.1", |
| "entries": [ |
| { |
| "id": 1, |
| "os": { |
| "type": "linux" |
| }, |
| "vendor_id": "0x8086", |
| "features": [ |
| "test_feature_0" |
| ] |
| }, |
| { |
| "id": 2, |
| "os": { |
| "type": "linux" |
| }, |
| "vendor_id": "0x8086", |
| "driver_version": { |
| "op": "<", |
| "value": "10.7" |
| }, |
| "features": [ |
| "test_feature_0" |
| ] |
| } |
| ] |
| } |
| ); |
| GPUInfo gpu_info; |
| gpu_info.gpu.vendor_id = kIntelVendorId; |
| |
| std::unique_ptr<GpuControlList> control_list(Create()); |
| EXPECT_TRUE(control_list->LoadList(json, GpuControlList::kAllOs)); |
| std::set<int> features = control_list->MakeDecision( |
| GpuControlList::kOsLinux, kOsVersion, gpu_info); |
| EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0); |
| EXPECT_FALSE(control_list->needs_more_info()); |
| } |
| |
| TEST_F(GpuControlListTest, ExceptionWithoutVendorId) { |
| const std::string json = LONG_STRING_CONST( |
| { |
| "name": "gpu control list", |
| "version": "0.1", |
| "entries": [ |
| { |
| "id": 1, |
| "os": { |
| "type": "linux" |
| }, |
| "vendor_id": "0x8086", |
| "exceptions": [ |
| { |
| "device_id": ["0x2a06"], |
| "driver_version": { |
| "op": ">=", |
| "value": "8.1" |
| } |
| }, |
| { |
| "device_id": ["0x2a02"], |
| "driver_version": { |
| "op": ">=", |
| "value": "9.1" |
| } |
| } |
| ], |
| "features": [ |
| "test_feature_0" |
| ] |
| } |
| ] |
| } |
| ); |
| GPUInfo gpu_info; |
| gpu_info.gpu.vendor_id = kIntelVendorId; |
| gpu_info.gpu.device_id = 0x2a02; |
| gpu_info.driver_version = "9.1"; |
| |
| std::unique_ptr<GpuControlList> control_list(Create()); |
| EXPECT_TRUE(control_list->LoadList(json, GpuControlList::kAllOs)); |
| |
| std::set<int> features = control_list->MakeDecision( |
| GpuControlList::kOsLinux, kOsVersion, gpu_info); |
| EXPECT_EMPTY_SET(features); |
| |
| gpu_info.driver_version = "9.0"; |
| features = control_list->MakeDecision( |
| GpuControlList::kOsLinux, kOsVersion, gpu_info); |
| EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0); |
| } |
| |
| TEST_F(GpuControlListTest, AMDSwitchable) { |
| GPUInfo gpu_info; |
| gpu_info.amd_switchable = true; |
| gpu_info.gpu.vendor_id = kAmdVendorId; |
| gpu_info.gpu.device_id = 0x6760; |
| GPUInfo::GPUDevice integrated_gpu; |
| integrated_gpu.vendor_id = kIntelVendorId; |
| integrated_gpu.device_id = 0x0116; |
| gpu_info.secondary_gpus.push_back(integrated_gpu); |
| |
| { // amd_switchable_discrete entry |
| const std::string json= LONG_STRING_CONST( |
| { |
| "name": "gpu control list", |
| "version": "0.1", |
| "entries": [ |
| { |
| "id": 1, |
| "os": { |
| "type": "win" |
| }, |
| "multi_gpu_style": "amd_switchable_discrete", |
| "features": [ |
| "test_feature_0" |
| ] |
| } |
| ] |
| } |
| ); |
| |
| std::unique_ptr<GpuControlList> control_list(Create()); |
| EXPECT_TRUE(control_list->LoadList(json, GpuControlList::kAllOs)); |
| |
| // Integrated GPU is active |
| gpu_info.gpu.active = false; |
| gpu_info.secondary_gpus[0].active = true; |
| std::set<int> features = control_list->MakeDecision( |
| GpuControlList::kOsWin, kOsVersion, gpu_info); |
| EXPECT_EMPTY_SET(features); |
| |
| // Discrete GPU is active |
| gpu_info.gpu.active = true; |
| gpu_info.secondary_gpus[0].active = false; |
| features = control_list->MakeDecision( |
| GpuControlList::kOsWin, kOsVersion, gpu_info); |
| EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0); |
| } |
| |
| { // amd_switchable_integrated entry |
| const std::string json= LONG_STRING_CONST( |
| { |
| "name": "gpu control list", |
| "version": "0.1", |
| "entries": [ |
| { |
| "id": 1, |
| "os": { |
| "type": "win" |
| }, |
| "multi_gpu_style": "amd_switchable_integrated", |
| "features": [ |
| "test_feature_0" |
| ] |
| } |
| ] |
| } |
| ); |
| |
| std::unique_ptr<GpuControlList> control_list(Create()); |
| EXPECT_TRUE(control_list->LoadList(json, GpuControlList::kAllOs)); |
| |
| // Discrete GPU is active |
| gpu_info.gpu.active = true; |
| gpu_info.secondary_gpus[0].active = false; |
| std::set<int> features = control_list->MakeDecision( |
| GpuControlList::kOsWin, kOsVersion, gpu_info); |
| EXPECT_EMPTY_SET(features); |
| |
| // Integrated GPU is active |
| gpu_info.gpu.active = false; |
| gpu_info.secondary_gpus[0].active = true; |
| features = control_list->MakeDecision( |
| GpuControlList::kOsWin, kOsVersion, gpu_info); |
| EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0); |
| |
| // For non AMD switchable |
| gpu_info.amd_switchable = false; |
| features = control_list->MakeDecision( |
| GpuControlList::kOsWin, kOsVersion, gpu_info); |
| EXPECT_EMPTY_SET(features); |
| } |
| } |
| |
| TEST_F(GpuControlListTest, DisabledExtensionTest) { |
| // exact setting. |
| const std::string exact_list_json = LONG_STRING_CONST( |
| { |
| "name": "gpu control list", |
| "version": "0.1", |
| "entries": [ |
| { |
| "id": 1, |
| "os": { |
| "type": "win" |
| }, |
| "disabled_extensions": [ |
| "test_extension2", |
| "test_extension1" |
| ] |
| }, |
| { |
| "id": 2, |
| "os": { |
| "type": "win" |
| }, |
| "disabled_extensions": [ |
| "test_extension3", |
| "test_extension2" |
| ] |
| } |
| ] |
| } |
| ); |
| std::unique_ptr<GpuControlList> control_list(Create()); |
| |
| EXPECT_TRUE(control_list->LoadList(exact_list_json, GpuControlList::kAllOs)); |
| GPUInfo gpu_info; |
| control_list->MakeDecision(GpuControlList::kOsWin, kOsVersion, gpu_info); |
| |
| std::vector<std::string> disabled_extensions = |
| control_list->GetDisabledExtensions(); |
| |
| ASSERT_EQ(3u, disabled_extensions.size()); |
| ASSERT_STREQ("test_extension1", disabled_extensions[0].c_str()); |
| ASSERT_STREQ("test_extension2", disabled_extensions[1].c_str()); |
| ASSERT_STREQ("test_extension3", disabled_extensions[2].c_str()); |
| } |
| |
| TEST_F(GpuControlListTest, DisabledInProcessGPUTest) { |
| const std::string exact_list_json = LONG_STRING_CONST( |
| { |
| "name": "gpu control list", |
| "version": "0.1", |
| "entries": [ |
| { |
| "id": 1, |
| "os": { |
| "type": "win" |
| }, |
| "in_process_gpu": true, |
| "features": [ |
| "test_feature_0" |
| ] |
| } |
| ] |
| } |
| ); |
| std::unique_ptr<GpuControlList> control_list(Create()); |
| |
| EXPECT_TRUE(control_list->LoadList(exact_list_json, GpuControlList::kAllOs)); |
| GPUInfo gpu_info; |
| |
| gpu_info.in_process_gpu = true; |
| std::set<int> features = control_list->MakeDecision( |
| GpuControlList::kOsWin, kOsVersion, gpu_info); |
| EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0); |
| |
| gpu_info.in_process_gpu = false; |
| features = control_list->MakeDecision( |
| GpuControlList::kOsWin, kOsVersion, gpu_info); |
| EXPECT_EMPTY_SET(features); |
| } |
| |
| TEST_F(GpuControlListTest, SameGPUTwiceTest) { |
| const std::string json = LONG_STRING_CONST( |
| { |
| "name": "gpu control list", |
| "version": "0.1", |
| "entries": [ |
| { |
| "id": 1, |
| "os": { |
| "type": "win" |
| }, |
| "vendor_id": "0x8086", |
| "features": [ |
| "test_feature_0" |
| ] |
| } |
| ] |
| } |
| ); |
| GPUInfo gpu_info; |
| gpu_info.gpu.vendor_id = kIntelVendorId; |
| // Real case on Intel GMA* on Windows |
| gpu_info.secondary_gpus.push_back(gpu_info.gpu); |
| |
| std::unique_ptr<GpuControlList> control_list(Create()); |
| EXPECT_TRUE(control_list->LoadList(json, GpuControlList::kAllOs)); |
| std::set<int> features = control_list->MakeDecision( |
| GpuControlList::kOsWin, kOsVersion, gpu_info); |
| EXPECT_SINGLE_FEATURE(features, TEST_FEATURE_0); |
| EXPECT_FALSE(control_list->needs_more_info()); |
| } |
| |
| } // namespace gpu |