| // Copyright 2012 The Chromium Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include <stddef.h> | |
| #include <stdint.h> | |
| #include <memory> | |
| #include "base/compiler_specific.h" | |
| #include "base/functional/bind.h" | |
| #include "base/time/time.h" | |
| #include "gpu/command_buffer/client/client_test_helper.h" | |
| #include "gpu/command_buffer/common/gles2_cmd_format.h" | |
| #include "gpu/command_buffer/service/error_state_mock.h" | |
| #include "gpu/command_buffer/service/feature_info.h" | |
| #include "gpu/command_buffer/service/gles2_cmd_decoder.h" | |
| #include "gpu/command_buffer/service/gles2_cmd_decoder_mock.h" | |
| #include "gpu/command_buffer/service/gles2_query_manager.h" | |
| #include "gpu/command_buffer/service/gpu_service_test.h" | |
| #include "gpu/command_buffer/service/gpu_tracer.h" | |
| #include "gpu/command_buffer/service/test_helper.h" | |
| #include "testing/gtest/include/gtest/gtest.h" | |
| #include "ui/gl/gl_context.h" | |
| #include "ui/gl/gl_mock.h" | |
| #include "ui/gl/gpu_timing.h" | |
| #include "ui/gl/gpu_timing_fake.h" | |
| using ::testing::_; | |
| using ::testing::InSequence; | |
| using ::testing::Return; | |
| using ::testing::SetArgPointee; | |
| namespace gpu { | |
| namespace gles2 { | |
| class QueryManagerTest : public GpuServiceTest { | |
| public: | |
| static const uint32_t kSharedMemoryOffset = 132; | |
| static const uint32_t kSharedMemory2Offset = 232; | |
| static const size_t kSharedBufferSize = 2048; | |
| static const int32_t kInvalidSharedMemoryId = -1; | |
| static const uint32_t kInvalidSharedMemoryOffset = kSharedBufferSize + 1; | |
| static const uint32_t kInitialResult = 0xBDBDBDBDu; | |
| static const uint8_t kInitialMemoryValue = 0xBDu; | |
| QueryManagerTest() = default; | |
| ~QueryManagerTest() override = default; | |
| protected: | |
| void SetUp() override { | |
| GpuServiceTest::SetUpWithGLVersion("OpenGL ES 3.0", ""); | |
| SetUpMockGL(""); | |
| } | |
| void TearDown() override { | |
| manager_->Destroy(false); | |
| manager_.reset(); | |
| decoder_.reset(); | |
| command_buffer_service_.reset(); | |
| GpuServiceTest::TearDown(); | |
| } | |
| void SetUpMockGL(const char* extension_expectations) { | |
| command_buffer_service_ = std::make_unique<FakeCommandBufferServiceBase>(); | |
| scoped_refptr<gpu::Buffer> buffer = | |
| command_buffer_service_->CreateTransferBufferHelper(kSharedBufferSize, | |
| &shared_memory_id_); | |
| UNSAFE_TODO( | |
| memset(buffer->memory(), kInitialMemoryValue, kSharedBufferSize)); | |
| buffer = command_buffer_service_->CreateTransferBufferHelper( | |
| kSharedBufferSize, &shared_memory2_id_); | |
| UNSAFE_TODO( | |
| memset(buffer->memory(), kInitialMemoryValue, kSharedBufferSize)); | |
| decoder_ = std::make_unique<MockGLES2Decoder>( | |
| &client_, command_buffer_service_.get(), &outputter_); | |
| TestHelper::SetupFeatureInfoInitExpectations( | |
| gl_.get(), extension_expectations); | |
| EXPECT_CALL(*decoder_.get(), GetGLContext()) | |
| .WillRepeatedly(Return(GetGLContext())); | |
| auto feature_info = base::MakeRefCounted<FeatureInfo>(); | |
| feature_info->InitializeForTesting(); | |
| manager_ = | |
| std::make_unique<GLES2QueryManager>(decoder_.get(), feature_info.get()); | |
| } | |
| QueryManager::Query* CreateQuery(GLenum target, | |
| GLuint client_id, | |
| int32_t shm_id, | |
| uint32_t shm_offset, | |
| GLuint service_id) { | |
| return CreateQueryOnManager(manager_.get(), target, client_id, shm_id, | |
| shm_offset, service_id); | |
| } | |
| QueryManager::Query* CreateQueryOnManager(QueryManager* manager, | |
| GLenum target, | |
| GLuint client_id, | |
| int32_t shm_id, | |
| uint32_t shm_offset, | |
| GLuint service_id) { | |
| if (service_id) { | |
| EXPECT_CALL(*gl_, GenQueries(1, _)) | |
| .WillOnce(SetArgPointee<1>(service_id)) | |
| .RetiresOnSaturation(); | |
| } | |
| scoped_refptr<gpu::Buffer> buffer = decoder_->GetSharedMemoryBuffer(shm_id); | |
| if (!buffer) | |
| return nullptr; | |
| QuerySync* sync = static_cast<QuerySync*>( | |
| buffer->GetDataAddress(shm_offset, sizeof(QuerySync))); | |
| if (!sync) | |
| return nullptr; | |
| return manager->CreateQuery(target, client_id, std::move(buffer), sync); | |
| } | |
| void QueueQuery(QueryManager::Query* query, | |
| GLuint service_id, | |
| base::subtle::Atomic32 submit_count) { | |
| EXPECT_CALL(*gl_, BeginQuery(query->target(), service_id)) | |
| .Times(1) | |
| .RetiresOnSaturation(); | |
| EXPECT_CALL(*gl_, EndQuery(query->target())) | |
| .Times(1) | |
| .RetiresOnSaturation(); | |
| manager_->BeginQuery(query); | |
| manager_->EndQuery(query, submit_count); | |
| } | |
| TraceOutputter outputter_; | |
| std::unique_ptr<FakeCommandBufferServiceBase> command_buffer_service_; | |
| FakeDecoderClient client_; | |
| std::unique_ptr<MockGLES2Decoder> decoder_; | |
| std::unique_ptr<GLES2QueryManager> manager_; | |
| int32_t shared_memory_id_ = 0; | |
| int32_t shared_memory2_id_ = 0; | |
| }; | |
| class QueryManagerTimerQueryTest : public QueryManagerTest { | |
| protected: | |
| void SetUp() override { | |
| GpuServiceTest::SetUpWithGLVersion("OpenGL ES 3.0", | |
| "GL_EXT_disjoint_timer_query"); | |
| fake_timing_queries_.ExpectDisjointCalls(*gl_); | |
| SetUpMockGL("GL_EXT_disjoint_timer_query"); | |
| } | |
| gl::GPUTimingFake fake_timing_queries_; | |
| }; | |
| // GCC requires these declarations, but MSVC requires they not be present | |
| #ifndef COMPILER_MSVC | |
| const uint32_t QueryManagerTest::kSharedMemoryOffset; | |
| const uint32_t QueryManagerTest::kSharedMemory2Offset; | |
| const size_t QueryManagerTest::kSharedBufferSize; | |
| const int32_t QueryManagerTest::kInvalidSharedMemoryId; | |
| const uint32_t QueryManagerTest::kInvalidSharedMemoryOffset; | |
| const uint32_t QueryManagerTest::kInitialResult; | |
| const uint8_t QueryManagerTest::kInitialMemoryValue; | |
| #endif | |
| TEST_F(QueryManagerTest, Basic) { | |
| const GLuint kClient1Id = 1; | |
| const GLuint kService1Id = 11; | |
| const GLuint kClient2Id = 2; | |
| EXPECT_FALSE(manager_->HavePendingQueries()); | |
| // Check we can create a Query. | |
| scoped_refptr<QueryManager::Query> query( | |
| CreateQuery(GL_ANY_SAMPLES_PASSED_EXT, kClient1Id, shared_memory_id_, | |
| kSharedMemoryOffset, kService1Id)); | |
| ASSERT_TRUE(query.get() != nullptr); | |
| // Check we can get the same Query. | |
| EXPECT_EQ(query.get(), manager_->GetQuery(kClient1Id)); | |
| // Check we get nothing for a non-existent query. | |
| EXPECT_TRUE(manager_->GetQuery(kClient2Id) == nullptr); | |
| // Check we can delete the query. | |
| EXPECT_CALL(*gl_, DeleteQueries(1, ::testing::Pointee(kService1Id))) | |
| .Times(1) | |
| .RetiresOnSaturation(); | |
| manager_->RemoveQuery(kClient1Id); | |
| // Check we get nothing for a non-existent query. | |
| EXPECT_TRUE(manager_->GetQuery(kClient1Id) == nullptr); | |
| // Check query is deleted | |
| EXPECT_TRUE(query->IsDeleted()); | |
| EXPECT_FALSE(manager_->HavePendingQueries()); | |
| } | |
| TEST_F(QueryManagerTest, Destroy) { | |
| const GLuint kClient1Id = 1; | |
| const GLuint kService1Id = 11; | |
| // Create Query. | |
| scoped_refptr<QueryManager::Query> query( | |
| CreateQuery(GL_ANY_SAMPLES_PASSED_EXT, kClient1Id, shared_memory_id_, | |
| kSharedMemoryOffset, kService1Id)); | |
| ASSERT_TRUE(query.get() != nullptr); | |
| EXPECT_CALL(*gl_, DeleteQueries(1, ::testing::Pointee(kService1Id))) | |
| .Times(1) | |
| .RetiresOnSaturation(); | |
| manager_->Destroy(true); | |
| // Check we get nothing for a non-existent query. | |
| EXPECT_TRUE(manager_->GetQuery(kClient1Id) == nullptr); | |
| // Check query is deleted | |
| EXPECT_TRUE(query->IsDeleted()); | |
| } | |
| TEST_F(QueryManagerTest, QueryBasic) { | |
| const GLuint kClient1Id = 1; | |
| const GLuint kService1Id = 11; | |
| const GLenum kTarget = GL_ANY_SAMPLES_PASSED_EXT; | |
| // Create Query. | |
| scoped_refptr<QueryManager::Query> query( | |
| CreateQuery(kTarget, kClient1Id, shared_memory_id_, kSharedMemoryOffset, | |
| kService1Id)); | |
| ASSERT_TRUE(query.get() != nullptr); | |
| EXPECT_TRUE(query->IsValid()); | |
| EXPECT_FALSE(query->IsDeleted()); | |
| EXPECT_FALSE(query->IsPending()); | |
| EXPECT_EQ(kTarget, query->target()); | |
| } | |
| TEST_F(QueryManagerTest, ProcessPendingQuery) { | |
| const GLuint kClient1Id = 1; | |
| const GLuint kService1Id = 11; | |
| const GLenum kTarget = GL_ANY_SAMPLES_PASSED_EXT; | |
| const base::subtle::Atomic32 kSubmitCount = 123; | |
| const GLuint kResult = 1; | |
| // Check nothing happens if there are no pending queries. | |
| manager_->ProcessPendingQueries(false); | |
| // Create Query. | |
| scoped_refptr<QueryManager::Query> query( | |
| CreateQuery(kTarget, kClient1Id, shared_memory_id_, kSharedMemoryOffset, | |
| kService1Id)); | |
| ASSERT_TRUE(query.get() != nullptr); | |
| // Setup shared memory like client would. | |
| QuerySync* sync = decoder_->GetSharedMemoryAs<QuerySync*>( | |
| shared_memory_id_, kSharedMemoryOffset, sizeof(*sync)); | |
| ASSERT_TRUE(sync != nullptr); | |
| sync->Reset(); | |
| // Queue it | |
| QueueQuery(query.get(), kService1Id, kSubmitCount); | |
| EXPECT_TRUE(query->IsPending()); | |
| EXPECT_TRUE(manager_->HavePendingQueries()); | |
| // Process with return not available. | |
| // Expect 1 GL command. | |
| EXPECT_CALL(*gl_, | |
| GetQueryObjectuiv(kService1Id, GL_QUERY_RESULT_AVAILABLE_EXT, _)) | |
| .WillOnce(SetArgPointee<2>(0)) | |
| .RetiresOnSaturation(); | |
| manager_->ProcessPendingQueries(false); | |
| EXPECT_TRUE(query->IsPending()); | |
| EXPECT_EQ(0, base::subtle::Atomic32{sync->process_count}); | |
| EXPECT_EQ(0u, uint64_t{sync->result}); | |
| // Process with return available. | |
| // Expect 2 GL commands. | |
| EXPECT_CALL(*gl_, | |
| GetQueryObjectuiv(kService1Id, GL_QUERY_RESULT_AVAILABLE_EXT, _)) | |
| .WillOnce(SetArgPointee<2>(1)) | |
| .RetiresOnSaturation(); | |
| EXPECT_CALL(*gl_, GetQueryObjectuiv(kService1Id, GL_QUERY_RESULT_EXT, _)) | |
| .WillOnce(SetArgPointee<2>(kResult)) | |
| .RetiresOnSaturation(); | |
| manager_->ProcessPendingQueries(false); | |
| EXPECT_FALSE(query->IsPending()); | |
| EXPECT_EQ(kSubmitCount, base::subtle::Atomic32{sync->process_count}); | |
| EXPECT_EQ(kResult, uint64_t{sync->result}); | |
| EXPECT_FALSE(manager_->HavePendingQueries()); | |
| // Process with no queries. | |
| // Expect no GL commands/ | |
| manager_->ProcessPendingQueries(false); | |
| } | |
| TEST_F(QueryManagerTest, ProcessPendingQueries) { | |
| const GLuint kClient1Id = 1; | |
| const GLuint kService1Id = 11; | |
| const GLuint kClient2Id = 2; | |
| const GLuint kService2Id = 12; | |
| const GLuint kClient3Id = 3; | |
| const GLuint kService3Id = 13; | |
| const GLenum kTarget = GL_ANY_SAMPLES_PASSED_EXT; | |
| const base::subtle::Atomic32 kSubmitCount1 = 123; | |
| const base::subtle::Atomic32 kSubmitCount2 = 123; | |
| const base::subtle::Atomic32 kSubmitCount3 = 123; | |
| const GLuint kResult1 = 1; | |
| const GLuint kResult2 = 1; | |
| const GLuint kResult3 = 1; | |
| // Setup shared memory like client would. | |
| QuerySync* sync1 = decoder_->GetSharedMemoryAs<QuerySync*>( | |
| shared_memory_id_, kSharedMemoryOffset, sizeof(*sync1) * 3); | |
| ASSERT_TRUE(sync1 != nullptr); | |
| QuerySync* sync2 = UNSAFE_TODO(sync1 + 1); | |
| QuerySync* sync3 = UNSAFE_TODO(sync2 + 1); | |
| // Create Queries. | |
| scoped_refptr<QueryManager::Query> query1( | |
| CreateQuery(kTarget, kClient1Id, shared_memory_id_, | |
| kSharedMemoryOffset + sizeof(*sync1) * 0, kService1Id)); | |
| scoped_refptr<QueryManager::Query> query2( | |
| CreateQuery(kTarget, kClient2Id, shared_memory_id_, | |
| kSharedMemoryOffset + sizeof(*sync1) * 1, kService2Id)); | |
| scoped_refptr<QueryManager::Query> query3( | |
| CreateQuery(kTarget, kClient3Id, shared_memory_id_, | |
| kSharedMemoryOffset + sizeof(*sync1) * 2, kService3Id)); | |
| ASSERT_TRUE(query1.get() != nullptr); | |
| ASSERT_TRUE(query2.get() != nullptr); | |
| ASSERT_TRUE(query3.get() != nullptr); | |
| EXPECT_FALSE(manager_->HavePendingQueries()); | |
| sync1->Reset(); | |
| sync2->Reset(); | |
| sync3->Reset(); | |
| // Queue them | |
| QueueQuery(query1.get(), kService1Id, kSubmitCount1); | |
| QueueQuery(query2.get(), kService2Id, kSubmitCount2); | |
| QueueQuery(query3.get(), kService3Id, kSubmitCount3); | |
| EXPECT_TRUE(query1->IsPending()); | |
| EXPECT_TRUE(query2->IsPending()); | |
| EXPECT_TRUE(query3->IsPending()); | |
| EXPECT_TRUE(manager_->HavePendingQueries()); | |
| // Process with return available for first 2 queries. | |
| // Expect 4 GL commands. | |
| { | |
| InSequence s; | |
| EXPECT_CALL( | |
| *gl_, GetQueryObjectuiv(kService1Id, GL_QUERY_RESULT_AVAILABLE_EXT, _)) | |
| .WillOnce(SetArgPointee<2>(1)) | |
| .RetiresOnSaturation(); | |
| EXPECT_CALL(*gl_, GetQueryObjectuiv(kService1Id, GL_QUERY_RESULT_EXT, _)) | |
| .WillOnce(SetArgPointee<2>(kResult1)) | |
| .RetiresOnSaturation(); | |
| EXPECT_CALL( | |
| *gl_, GetQueryObjectuiv(kService2Id, GL_QUERY_RESULT_AVAILABLE_EXT, _)) | |
| .WillOnce(SetArgPointee<2>(1)) | |
| .RetiresOnSaturation(); | |
| EXPECT_CALL(*gl_, GetQueryObjectuiv(kService2Id, GL_QUERY_RESULT_EXT, _)) | |
| .WillOnce(SetArgPointee<2>(kResult2)) | |
| .RetiresOnSaturation(); | |
| EXPECT_CALL( | |
| *gl_, GetQueryObjectuiv(kService3Id, GL_QUERY_RESULT_AVAILABLE_EXT, _)) | |
| .WillOnce(SetArgPointee<2>(0)) | |
| .RetiresOnSaturation(); | |
| manager_->ProcessPendingQueries(false); | |
| } | |
| EXPECT_FALSE(query1->IsPending()); | |
| EXPECT_FALSE(query2->IsPending()); | |
| EXPECT_TRUE(query3->IsPending()); | |
| EXPECT_EQ(kSubmitCount1, base::subtle::Atomic32{sync1->process_count}); | |
| EXPECT_EQ(kSubmitCount2, base::subtle::Atomic32{sync2->process_count}); | |
| EXPECT_EQ(kResult1, uint64_t{sync1->result}); | |
| EXPECT_EQ(kResult2, uint64_t{sync2->result}); | |
| EXPECT_EQ(0, base::subtle::Atomic32{sync3->process_count}); | |
| EXPECT_EQ(0u, uint64_t{sync3->result}); | |
| EXPECT_TRUE(manager_->HavePendingQueries()); | |
| // Process with renaming query. No result. | |
| // Expect 1 GL commands. | |
| EXPECT_CALL(*gl_, | |
| GetQueryObjectuiv(kService3Id, GL_QUERY_RESULT_AVAILABLE_EXT, _)) | |
| .WillOnce(SetArgPointee<2>(0)) | |
| .RetiresOnSaturation(); | |
| manager_->ProcessPendingQueries(false); | |
| EXPECT_TRUE(query3->IsPending()); | |
| EXPECT_EQ(0, base::subtle::Atomic32{sync3->process_count}); | |
| EXPECT_EQ(0u, uint64_t{sync3->result}); | |
| EXPECT_TRUE(manager_->HavePendingQueries()); | |
| // Process with renaming query. With result. | |
| // Expect 2 GL commands. | |
| EXPECT_CALL(*gl_, | |
| GetQueryObjectuiv(kService3Id, GL_QUERY_RESULT_AVAILABLE_EXT, _)) | |
| .WillOnce(SetArgPointee<2>(1)) | |
| .RetiresOnSaturation(); | |
| EXPECT_CALL(*gl_, GetQueryObjectuiv(kService3Id, GL_QUERY_RESULT_EXT, _)) | |
| .WillOnce(SetArgPointee<2>(kResult3)) | |
| .RetiresOnSaturation(); | |
| manager_->ProcessPendingQueries(false); | |
| EXPECT_FALSE(query3->IsPending()); | |
| EXPECT_EQ(kSubmitCount3, base::subtle::Atomic32{sync3->process_count}); | |
| EXPECT_EQ(kResult3, uint64_t{sync3->result}); | |
| EXPECT_FALSE(manager_->HavePendingQueries()); | |
| } | |
| TEST_F(QueryManagerTest, ExitWithPendingQuery) { | |
| const GLuint kClient1Id = 1; | |
| const GLuint kService1Id = 11; | |
| const GLenum kTarget = GL_ANY_SAMPLES_PASSED_EXT; | |
| const base::subtle::Atomic32 kSubmitCount = 123; | |
| // Create Query. | |
| scoped_refptr<QueryManager::Query> query( | |
| CreateQuery(kTarget, kClient1Id, shared_memory_id_, kSharedMemoryOffset, | |
| kService1Id)); | |
| ASSERT_TRUE(query.get() != nullptr); | |
| // Queue it | |
| QueueQuery(query.get(), kService1Id, kSubmitCount); | |
| } | |
| TEST_F(QueryManagerTimerQueryTest, TimeElapsedQuery) { | |
| const GLuint kClient1Id = 1; | |
| const GLenum kTarget = GL_TIME_ELAPSED_EXT; | |
| const base::subtle::Atomic32 kSubmitCount = 123; | |
| decoder_->GetGLContext()->CreateGPUTimingClient()->SetCpuTimeForTesting( | |
| base::BindRepeating(&gl::GPUTimingFake::GetFakeCPUTime)); | |
| QueryManager::Query* query = CreateQuery( | |
| kTarget, kClient1Id, shared_memory_id_, kSharedMemoryOffset, 0); | |
| ASSERT_TRUE(query != nullptr); | |
| fake_timing_queries_.ExpectGPUTimerQuery(*gl_, true); | |
| fake_timing_queries_.SetCurrentGLTime(200 * | |
| base::Time::kNanosecondsPerMicrosecond); | |
| manager_->BeginQuery(query); | |
| fake_timing_queries_.SetCurrentGLTime(300 * | |
| base::Time::kNanosecondsPerMicrosecond); | |
| manager_->EndQuery(query, kSubmitCount); | |
| manager_->ProcessPendingQueries(false); | |
| EXPECT_TRUE(query->IsFinished()); | |
| QuerySync* sync = decoder_->GetSharedMemoryAs<QuerySync*>( | |
| shared_memory_id_, kSharedMemoryOffset, sizeof(*sync)); | |
| const uint64_t expected_result = | |
| 100u * base::Time::kNanosecondsPerMicrosecond; | |
| EXPECT_EQ(expected_result, uint64_t{sync->result}); | |
| manager_->Destroy(false); | |
| } | |
| TEST_F(QueryManagerTimerQueryTest, TimeElapsedPauseResume) { | |
| const GLuint kClient1Id = 1; | |
| const GLenum kTarget = GL_TIME_ELAPSED_EXT; | |
| const base::subtle::Atomic32 kSubmitCount = 123; | |
| decoder_->GetGLContext()->CreateGPUTimingClient()->SetCpuTimeForTesting( | |
| base::BindRepeating(&gl::GPUTimingFake::GetFakeCPUTime)); | |
| QueryManager::Query* query = CreateQuery( | |
| kTarget, kClient1Id, shared_memory_id_, kSharedMemoryOffset, 0); | |
| ASSERT_TRUE(query != nullptr); | |
| fake_timing_queries_.ExpectGPUTimerQuery(*gl_, true); | |
| fake_timing_queries_.SetCurrentGLTime(200 * | |
| base::Time::kNanosecondsPerMicrosecond); | |
| manager_->BeginQuery(query); | |
| // Pause and Resume here. | |
| fake_timing_queries_.SetCurrentGLTime(300 * | |
| base::Time::kNanosecondsPerMicrosecond); | |
| manager_->PauseQueries(); | |
| fake_timing_queries_.SetCurrentGLTime(400 * | |
| base::Time::kNanosecondsPerMicrosecond); | |
| manager_->ResumeQueries(); | |
| fake_timing_queries_.SetCurrentGLTime(500 * | |
| base::Time::kNanosecondsPerMicrosecond); | |
| manager_->EndQuery(query, kSubmitCount); | |
| manager_->ProcessPendingQueries(false); | |
| EXPECT_TRUE(query->IsFinished()); | |
| QuerySync* sync = decoder_->GetSharedMemoryAs<QuerySync*>( | |
| shared_memory_id_, kSharedMemoryOffset, sizeof(*sync)); | |
| const uint64_t expected_result = | |
| 300u * base::Time::kNanosecondsPerMicrosecond; | |
| EXPECT_EQ(expected_result, uint64_t{sync->result}); | |
| // Make sure next query works properly. | |
| fake_timing_queries_.SetCurrentGLTime(600 * | |
| base::Time::kNanosecondsPerMicrosecond); | |
| manager_->BeginQuery(query); | |
| fake_timing_queries_.SetCurrentGLTime(700 * | |
| base::Time::kNanosecondsPerMicrosecond); | |
| manager_->EndQuery(query, kSubmitCount + 1); | |
| manager_->ProcessPendingQueries(false); | |
| EXPECT_TRUE(query->IsFinished()); | |
| const uint64_t expected_result2 = | |
| 100u * base::Time::kNanosecondsPerMicrosecond; | |
| EXPECT_EQ(expected_result2, uint64_t{sync->result}); | |
| manager_->Destroy(false); | |
| } | |
| TEST_F(QueryManagerTimerQueryTest, TimeElapsedDisjoint) { | |
| DisjointValueSync* disjoint_sync = | |
| decoder_->GetSharedMemoryAs<DisjointValueSync*>( | |
| shared_memory2_id_, kSharedMemory2Offset, sizeof(*disjoint_sync)); | |
| manager_->SetDisjointSync(shared_memory2_id_, kSharedMemory2Offset); | |
| const uint32_t current_disjoint_value = disjoint_sync->GetDisjointCount(); | |
| ASSERT_EQ(0u, current_disjoint_value); | |
| const GLuint kClient1Id = 1; | |
| const GLenum kTarget = GL_TIME_ELAPSED_EXT; | |
| const base::subtle::Atomic32 kSubmitCount = 123; | |
| QueryManager::Query* query = CreateQuery( | |
| kTarget, kClient1Id, shared_memory_id_, kSharedMemoryOffset, 0); | |
| ASSERT_TRUE(query != nullptr); | |
| // Disjoint happening before the query should not trigger a disjoint event. | |
| fake_timing_queries_.SetDisjoint(); | |
| fake_timing_queries_.ExpectGPUTimerQuery(*gl_, true); | |
| manager_->BeginQuery(query); | |
| manager_->EndQuery(query, kSubmitCount); | |
| manager_->ProcessPendingQueries(false); | |
| EXPECT_TRUE(query->IsFinished()); | |
| EXPECT_EQ(current_disjoint_value, disjoint_sync->GetDisjointCount()); | |
| // Disjoint happening during query should trigger disjoint event. | |
| fake_timing_queries_.ExpectGPUTimerQuery(*gl_, true); | |
| manager_->BeginQuery(query); | |
| fake_timing_queries_.SetDisjoint(); | |
| manager_->EndQuery(query, kSubmitCount); | |
| manager_->ProcessPendingQueries(false); | |
| EXPECT_TRUE(query->IsFinished()); | |
| EXPECT_NE(current_disjoint_value, disjoint_sync->GetDisjointCount()); | |
| manager_->Destroy(false); | |
| } | |
| TEST_F(QueryManagerTimerQueryTest, TimeStampQuery) { | |
| const GLuint kClient1Id = 1; | |
| const GLenum kTarget = GL_TIMESTAMP_EXT; | |
| const base::subtle::Atomic32 kSubmitCount = 123; | |
| decoder_->GetGLContext()->CreateGPUTimingClient()->SetCpuTimeForTesting( | |
| base::BindRepeating(&gl::GPUTimingFake::GetFakeCPUTime)); | |
| QueryManager::Query* query = CreateQuery( | |
| kTarget, kClient1Id, shared_memory_id_, kSharedMemoryOffset, 0); | |
| ASSERT_TRUE(query != nullptr); | |
| const uint64_t expected_result = | |
| 100u * base::Time::kNanosecondsPerMicrosecond; | |
| fake_timing_queries_.SetCurrentGLTime(expected_result); | |
| fake_timing_queries_.ExpectGPUTimeStampQuery(*gl_, false); | |
| manager_->QueryCounter(query, kSubmitCount); | |
| manager_->ProcessPendingQueries(false); | |
| QuerySync* sync = decoder_->GetSharedMemoryAs<QuerySync*>( | |
| shared_memory_id_, kSharedMemoryOffset, sizeof(*sync)); | |
| EXPECT_EQ(expected_result, uint64_t{sync->result}); | |
| manager_->Destroy(false); | |
| } | |
| TEST_F(QueryManagerTimerQueryTest, TimeStampQueryPending) { | |
| const GLuint kClient1Id = 1; | |
| const GLenum kTarget = GL_TIMESTAMP_EXT; | |
| const base::subtle::Atomic32 kSubmitCount = 123; | |
| decoder_->GetGLContext()->CreateGPUTimingClient()->SetCpuTimeForTesting( | |
| base::BindRepeating(&gl::GPUTimingFake::GetFakeCPUTime)); | |
| QueryManager::Query* query = CreateQuery( | |
| kTarget, kClient1Id, shared_memory_id_, kSharedMemoryOffset, 0); | |
| ASSERT_TRUE(query != nullptr); | |
| const uint64_t expected_result = | |
| 100u * base::Time::kNanosecondsPerMicrosecond; | |
| fake_timing_queries_.SetCurrentGLTime(expected_result); | |
| fake_timing_queries_.ExpectGPUTimeStampQuery(*gl_, false); | |
| manager_->QueryCounter(query, kSubmitCount); | |
| EXPECT_TRUE(query->IsPending()); | |
| fake_timing_queries_.ExpectGPUTimeStampQuery(*gl_, false); | |
| manager_->QueryCounter(query, kSubmitCount); | |
| manager_->ProcessPendingQueries(false); | |
| QuerySync* sync = decoder_->GetSharedMemoryAs<QuerySync*>( | |
| shared_memory_id_, kSharedMemoryOffset, sizeof(*sync)); | |
| EXPECT_EQ(expected_result, uint64_t{sync->result}); | |
| manager_->Destroy(false); | |
| } | |
| TEST_F(QueryManagerTimerQueryTest, TimeStampDisjoint) { | |
| DisjointValueSync* disjoint_sync = | |
| decoder_->GetSharedMemoryAs<DisjointValueSync*>( | |
| shared_memory2_id_, kSharedMemory2Offset, sizeof(*disjoint_sync)); | |
| manager_->SetDisjointSync(shared_memory2_id_, kSharedMemory2Offset); | |
| const uint32_t current_disjoint_value = disjoint_sync->GetDisjointCount(); | |
| ASSERT_EQ(0u, current_disjoint_value); | |
| const GLuint kClient1Id = 1; | |
| const GLenum kTarget = GL_TIMESTAMP_EXT; | |
| const base::subtle::Atomic32 kSubmitCount = 123; | |
| QueryManager::Query* query = CreateQuery( | |
| kTarget, kClient1Id, shared_memory_id_, kSharedMemoryOffset, 0); | |
| ASSERT_TRUE(query != nullptr); | |
| // Disjoint happening before the query should not trigger a disjoint event. | |
| fake_timing_queries_.SetDisjoint(); | |
| fake_timing_queries_.ExpectGPUTimeStampQuery(*gl_, false); | |
| manager_->QueryCounter(query, kSubmitCount); | |
| manager_->ProcessPendingQueries(false); | |
| EXPECT_TRUE(query->IsFinished()); | |
| EXPECT_EQ(current_disjoint_value, disjoint_sync->GetDisjointCount()); | |
| // Disjoint happening during query should trigger disjoint event. | |
| fake_timing_queries_.ExpectGPUTimeStampQuery(*gl_, false); | |
| manager_->QueryCounter(query, kSubmitCount); | |
| fake_timing_queries_.SetDisjoint(); | |
| manager_->ProcessPendingQueries(false); | |
| EXPECT_TRUE(query->IsFinished()); | |
| EXPECT_NE(current_disjoint_value, disjoint_sync->GetDisjointCount()); | |
| manager_->Destroy(false); | |
| } | |
| TEST_F(QueryManagerTimerQueryTest, DisjointContinualTest) { | |
| DisjointValueSync* disjoint_sync = | |
| decoder_->GetSharedMemoryAs<DisjointValueSync*>( | |
| shared_memory2_id_, kSharedMemory2Offset, sizeof(*disjoint_sync)); | |
| manager_->SetDisjointSync(shared_memory2_id_, kSharedMemory2Offset); | |
| const uint32_t current_disjoint_value = disjoint_sync->GetDisjointCount(); | |
| ASSERT_EQ(0u, current_disjoint_value); | |
| // Disjoint value should not be updated until we have a timestamp query. | |
| fake_timing_queries_.SetDisjoint(); | |
| manager_->ProcessFrameBeginUpdates(); | |
| EXPECT_EQ(current_disjoint_value, disjoint_sync->GetDisjointCount()); | |
| const GLuint kClient1Id = 1; | |
| const GLenum kTarget = GL_TIMESTAMP_EXT; | |
| const base::subtle::Atomic32 kSubmitCount = 123; | |
| QueryManager::Query* query = CreateQuery( | |
| kTarget, kClient1Id, shared_memory_id_, kSharedMemoryOffset, 0); | |
| ASSERT_TRUE(query != nullptr); | |
| fake_timing_queries_.ExpectGPUTimeStampQuery(*gl_, false); | |
| manager_->QueryCounter(query, kSubmitCount); | |
| manager_->ProcessPendingQueries(false); | |
| EXPECT_EQ(current_disjoint_value, disjoint_sync->GetDisjointCount()); | |
| fake_timing_queries_.SetDisjoint(); | |
| manager_->ProcessFrameBeginUpdates(); | |
| EXPECT_NE(current_disjoint_value, disjoint_sync->GetDisjointCount()); | |
| manager_->Destroy(false); | |
| } | |
| TEST_F(QueryManagerTest, GetErrorQuery) { | |
| const GLuint kClient1Id = 1; | |
| const GLenum kTarget = GL_GET_ERROR_QUERY_CHROMIUM; | |
| const base::subtle::Atomic32 kSubmitCount = 123; | |
| TestHelper::SetupFeatureInfoInitExpectations(gl_.get(), ""); | |
| auto feature_info = base::MakeRefCounted<FeatureInfo>(); | |
| feature_info->InitializeForTesting(); | |
| std::unique_ptr<GLES2QueryManager> manager( | |
| new GLES2QueryManager(decoder_.get(), feature_info.get())); | |
| QueryManager::Query* query = | |
| CreateQueryOnManager(manager.get(), kTarget, kClient1Id, | |
| shared_memory_id_, kSharedMemoryOffset, 0); | |
| ASSERT_TRUE(query != nullptr); | |
| // Setup shared memory like client would. | |
| QuerySync* sync = decoder_->GetSharedMemoryAs<QuerySync*>( | |
| shared_memory_id_, kSharedMemoryOffset, sizeof(*sync)); | |
| ASSERT_TRUE(sync != nullptr); | |
| sync->Reset(); | |
| manager->BeginQuery(query); | |
| MockErrorState mock_error_state; | |
| EXPECT_CALL(*decoder_.get(), GetErrorState()) | |
| .WillRepeatedly(Return(&mock_error_state)); | |
| EXPECT_CALL(mock_error_state, GetGLError()) | |
| .WillOnce(Return(GL_INVALID_ENUM)) | |
| .RetiresOnSaturation(); | |
| manager->EndQuery(query, kSubmitCount); | |
| EXPECT_FALSE(query->IsPending()); | |
| EXPECT_EQ(static_cast<GLuint>(GL_INVALID_ENUM), uint64_t{sync->result}); | |
| manager->Destroy(false); | |
| } | |
| TEST_F(QueryManagerTest, OcclusionQuery) { | |
| const GLuint kClient1Id = 1; | |
| const GLuint kService1Id = 11; | |
| const GLenum kTarget = GL_SAMPLES_PASSED_ARB; | |
| const base::subtle::Atomic32 kSubmitCount = 123; | |
| TestHelper::SetupFeatureInfoInitExpectations(gl_.get(), | |
| "GL_EXT_occlusion_query"); | |
| auto feature_info = base::MakeRefCounted<FeatureInfo>(); | |
| feature_info->InitializeForTesting(); | |
| std::unique_ptr<GLES2QueryManager> manager( | |
| new GLES2QueryManager(decoder_.get(), feature_info.get())); | |
| QueryManager::Query* query = | |
| CreateQueryOnManager(manager.get(), kTarget, kClient1Id, | |
| shared_memory_id_, kSharedMemoryOffset, kService1Id); | |
| ASSERT_TRUE(query != nullptr); | |
| EXPECT_CALL(*gl_, BeginQuery(GL_SAMPLES_PASSED_ARB, kService1Id)) | |
| .Times(1) | |
| .RetiresOnSaturation(); | |
| EXPECT_CALL(*gl_, EndQuery(GL_SAMPLES_PASSED_ARB)) | |
| .Times(1) | |
| .RetiresOnSaturation(); | |
| manager->BeginQuery(query); | |
| manager->EndQuery(query, kSubmitCount); | |
| manager->Destroy(false); | |
| } | |
| } // namespace gles2 | |
| } // namespace gpu |