|  | // Copyright (c) 2015 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 "gpu/command_buffer/service/path_manager.h" | 
|  |  | 
|  | #include <memory> | 
|  |  | 
|  | #include "gpu/command_buffer/service/gpu_service_test.h" | 
|  | #include "gpu/command_buffer/service/mocks.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  | #include "ui/gl/gl_mock.h" | 
|  |  | 
|  | namespace gpu { | 
|  | namespace gles2 { | 
|  |  | 
|  | class PathManagerTest : public GpuServiceTest { | 
|  | public: | 
|  | PathManagerTest() {} | 
|  |  | 
|  | protected: | 
|  | void SetUp() override { | 
|  | SetUpWithGLVersion("3.0", "GL_NV_path_rendering"); | 
|  | manager_.reset(new PathManager()); | 
|  | } | 
|  |  | 
|  | void TearDown() override { | 
|  | manager_->Destroy(true); | 
|  | manager_.reset(); | 
|  | GpuServiceTest::TearDown(); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<PathManager> manager_; | 
|  | }; | 
|  |  | 
|  | TEST_F(PathManagerTest, Basic) { | 
|  | const GLuint kClient1Id = 1; | 
|  | const GLuint kService1Id = 11; | 
|  | const GLuint kClient2Id = 2; | 
|  | GLuint service_id = 0; | 
|  | manager_->CreatePathRange(kClient1Id, kClient1Id, kService1Id); | 
|  | ASSERT_TRUE(manager_->HasPathsInRange(kClient1Id, kClient1Id)); | 
|  | EXPECT_TRUE(manager_->GetPath(kClient1Id, &service_id)); | 
|  | EXPECT_EQ(kService1Id, service_id); | 
|  |  | 
|  | // Check we get nothing for a non-existent path. | 
|  | service_id = 123u; | 
|  | ASSERT_FALSE(manager_->HasPathsInRange(kClient2Id, kClient2Id)); | 
|  | EXPECT_FALSE(manager_->GetPath(kClient2Id, &service_id)); | 
|  | EXPECT_EQ(123u, service_id); | 
|  |  | 
|  | // Check trying to remove non-existent paths does not crash. | 
|  | manager_->RemovePaths(kClient2Id, kClient2Id); | 
|  |  | 
|  | // Check that it gets deleted when the last reference is released. | 
|  | EXPECT_CALL(*gl_, DeletePathsNV(kService1Id, 1)) | 
|  | .Times(1) | 
|  | .RetiresOnSaturation(); | 
|  |  | 
|  | // Check we can't get the path after we remove it. | 
|  | manager_->RemovePaths(kClient1Id, kClient1Id); | 
|  | ASSERT_FALSE(manager_->HasPathsInRange(kClient1Id, kClient1Id)); | 
|  | EXPECT_FALSE(manager_->GetPath(kClient1Id, &service_id)); | 
|  | } | 
|  |  | 
|  | // Tests that path manager does not merge ranges that contain service ids that | 
|  | //  prevent the merging. Path ranges A and B can be merged if | 
|  | // * client ids of B start immediately after the last client id of A | 
|  | // * service ids of B start immediately after the last service id of A | 
|  | // and similarly for the 'before' case. | 
|  | TEST_F(PathManagerTest, NonContiguousServiceIds) { | 
|  | const GLuint kMergeCheckRange = 54; | 
|  |  | 
|  | const struct { | 
|  | GLuint first_client_id; | 
|  | GLuint last_client_id; | 
|  | GLuint first_service_id; | 
|  | } kIdRanges[] = {{500, 1000, 900}, {1001, 1155, 1}, {200, 499, 4888}}; | 
|  | for (auto& range : kIdRanges) { | 
|  | manager_->CreatePathRange(range.first_client_id, range.last_client_id, | 
|  | range.first_service_id); | 
|  | ASSERT_TRUE(manager_->HasPathsInRange(range.first_client_id, | 
|  | range.first_client_id)); | 
|  | ASSERT_TRUE( | 
|  | manager_->HasPathsInRange(range.last_client_id, range.last_client_id)); | 
|  | ASSERT_TRUE( | 
|  | manager_->HasPathsInRange(range.first_client_id, range.last_client_id)); | 
|  | GLuint service_id = 0u; | 
|  | EXPECT_TRUE(manager_->GetPath(range.first_client_id + 5u, &service_id)); | 
|  | EXPECT_EQ(range.first_service_id + 5u, service_id); | 
|  | } | 
|  |  | 
|  | // Insert a mergeable range last, to check that merges | 
|  | // work. Otherwise the test could succeed because merges were not | 
|  | // working. | 
|  | auto& merge_candidate = kIdRanges[1]; | 
|  | GLuint merge_candidate_range = | 
|  | merge_candidate.last_client_id - merge_candidate.first_client_id + 1; | 
|  | manager_->CreatePathRange( | 
|  | merge_candidate.last_client_id + 1, | 
|  | merge_candidate.last_client_id + kMergeCheckRange, | 
|  | merge_candidate.first_service_id + merge_candidate_range); | 
|  |  | 
|  | // We detect that ranges were not merged accidentally by detecting individual | 
|  | // deletes. | 
|  | for (auto& range : kIdRanges) { | 
|  | if (&range == &merge_candidate) | 
|  | continue; | 
|  | GLsizei range_amount = range.last_client_id - range.first_client_id + 1; | 
|  | EXPECT_CALL(*gl_, DeletePathsNV(range.first_service_id, range_amount)) | 
|  | .Times(1) | 
|  | .RetiresOnSaturation(); | 
|  | } | 
|  |  | 
|  | // Just a check that merges work. | 
|  | EXPECT_CALL(*gl_, DeletePathsNV(merge_candidate.first_service_id, | 
|  | merge_candidate_range + kMergeCheckRange)) | 
|  | .Times(1) | 
|  | .RetiresOnSaturation(); | 
|  |  | 
|  | // Remove all ids. This should cause the expected amount of DeletePathsNV | 
|  | // calls. | 
|  | manager_->RemovePaths(1, std::numeric_limits<GLsizei>::max()); | 
|  |  | 
|  | for (auto& range : kIdRanges) { | 
|  | ASSERT_FALSE( | 
|  | manager_->HasPathsInRange(range.first_client_id, range.last_client_id)); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_F(PathManagerTest, DeleteBigRange) { | 
|  | // Allocates two ranges which in path manager end up merging as one | 
|  | // big range. The range will be too big to fit in one DeletePaths | 
|  | // call.  Test that the range is deleted correctly with two calls. | 
|  | const GLuint kFirstClientId1 = 1; | 
|  | const GLsizei kRange1 = std::numeric_limits<GLsizei>::max() - 3; | 
|  | const GLuint kLastClientId1 = kFirstClientId1 + kRange1 - 1; | 
|  | const GLuint kFirstServiceId1 = 77; | 
|  | const GLuint kLastServiceId1 = kFirstServiceId1 + kRange1 - 1; | 
|  |  | 
|  | const GLuint kFirstClientId2 = kLastClientId1 + 1; | 
|  | const GLsizei kRange2 = 15; | 
|  | const GLuint kLastClientId2 = kFirstClientId2 + kRange2 - 1; | 
|  | const GLuint kFirstServiceId2 = kLastServiceId1 + 1; | 
|  |  | 
|  | const GLsizei kFirstDeleteRange = std::numeric_limits<GLsizei>::max(); | 
|  | const GLsizei kSecondDeleteRange = kRange2 - (kFirstDeleteRange - kRange1); | 
|  | const GLuint kSecondDeleteFirstServiceId = | 
|  | kFirstServiceId1 + kFirstDeleteRange; | 
|  |  | 
|  | EXPECT_CALL(*gl_, DeletePathsNV(kFirstServiceId1, | 
|  | std::numeric_limits<GLsizei>::max())) | 
|  | .RetiresOnSaturation(); | 
|  |  | 
|  | EXPECT_CALL(*gl_, DeletePathsNV(kSecondDeleteFirstServiceId, | 
|  | kSecondDeleteRange)).RetiresOnSaturation(); | 
|  |  | 
|  | manager_->CreatePathRange(kFirstClientId1, kLastClientId1, kFirstServiceId1); | 
|  | manager_->CreatePathRange(kFirstClientId2, kLastClientId2, kFirstServiceId2); | 
|  | manager_->RemovePaths(0, std::numeric_limits<GLuint>::max()); | 
|  | } | 
|  |  | 
|  | }  // namespace gles2 | 
|  |  | 
|  | }  // namespace gpu |