diff options
author | dyen <dyen@chromium.org> | 2015-07-16 15:59:30 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-07-16 23:00:45 +0000 |
commit | da06bae00bee789d12cd6f4418018d048b04cfd2 (patch) | |
tree | 79826f0d5e5e3aa6188b8054bceacfce03f2a37c | |
parent | 65410813e3997a19f99d35f059e8d8f8b6986d35 (diff) | |
download | chromium_src-da06bae00bee789d12cd6f4418018d048b04cfd2.zip chromium_src-da06bae00bee789d12cd6f4418018d048b04cfd2.tar.gz chromium_src-da06bae00bee789d12cd6f4418018d048b04cfd2.tar.bz2 |
Added support for TimeStamp queries using QueryCounterEXT.
BUG=345227
TEST=trybots
Review URL: https://codereview.chromium.org/1233233002
Cr-Commit-Position: refs/heads/master@{#339153}
33 files changed, 1061 insertions, 201 deletions
diff --git a/gpu/GLES2/gl2chromium_autogen.h b/gpu/GLES2/gl2chromium_autogen.h index 4ca2794..8016ec1 100644 --- a/gpu/GLES2/gl2chromium_autogen.h +++ b/gpu/GLES2/gl2chromium_autogen.h @@ -244,6 +244,7 @@ #define glTexStorage2DEXT GLES2_GET_FUN(TexStorage2DEXT) #define glGenQueriesEXT GLES2_GET_FUN(GenQueriesEXT) #define glDeleteQueriesEXT GLES2_GET_FUN(DeleteQueriesEXT) +#define glQueryCounterEXT GLES2_GET_FUN(QueryCounterEXT) #define glIsQueryEXT GLES2_GET_FUN(IsQueryEXT) #define glBeginQueryEXT GLES2_GET_FUN(BeginQueryEXT) #define glBeginTransformFeedback GLES2_GET_FUN(BeginTransformFeedback) diff --git a/gpu/command_buffer/build_gles2_cmd_buffer.py b/gpu/command_buffer/build_gles2_cmd_buffer.py index cc6a578..a01d1b9 100755 --- a/gpu/command_buffer/build_gles2_cmd_buffer.py +++ b/gpu/command_buffer/build_gles2_cmd_buffer.py @@ -3820,6 +3820,14 @@ _FUNCTION_INFO = { 'pepper_interface': 'Query', 'extension': "occlusion_query_EXT", }, + 'QueryCounterEXT' : { + 'type': 'Manual', + 'cmd_args': 'GLidQuery id, GLenumQueryTarget target, ' + 'void* sync_data, GLuint submit_count', + 'data_transfer_methods': ['shm'], + 'gl_test_func': 'glQueryCounter', + 'extension': "disjoint_timer_query_EXT", + }, 'GetQueryObjectuivEXT': { 'gen_cmd': False, 'client_test': False, diff --git a/gpu/command_buffer/client/gles2_c_lib_autogen.h b/gpu/command_buffer/client/gles2_c_lib_autogen.h index 956b0d5..986538f 100644 --- a/gpu/command_buffer/client/gles2_c_lib_autogen.h +++ b/gpu/command_buffer/client/gles2_c_lib_autogen.h @@ -1049,6 +1049,9 @@ void GLES2GenQueriesEXT(GLsizei n, GLuint* queries) { void GLES2DeleteQueriesEXT(GLsizei n, const GLuint* queries) { gles2::GetGLContext()->DeleteQueriesEXT(n, queries); } +void GLES2QueryCounterEXT(GLuint id, GLenum target) { + gles2::GetGLContext()->QueryCounterEXT(id, target); +} GLboolean GLES2IsQueryEXT(GLuint id) { return gles2::GetGLContext()->IsQueryEXT(id); } @@ -2389,6 +2392,10 @@ extern const NameToFunc g_gles2_function_table[] = { reinterpret_cast<GLES2FunctionPointer>(glDeleteQueriesEXT), }, { + "glQueryCounterEXT", + reinterpret_cast<GLES2FunctionPointer>(glQueryCounterEXT), + }, + { "glIsQueryEXT", reinterpret_cast<GLES2FunctionPointer>(glIsQueryEXT), }, diff --git a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h index 1a34ab7..9138538 100644 --- a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h +++ b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h @@ -2228,6 +2228,17 @@ void DeleteQueriesEXTImmediate(GLsizei n, const GLuint* queries) { } } +void QueryCounterEXT(GLuint id, + GLenum target, + uint32_t sync_data_shm_id, + uint32_t sync_data_shm_offset, + GLuint submit_count) { + gles2::cmds::QueryCounterEXT* c = GetCmdSpace<gles2::cmds::QueryCounterEXT>(); + if (c) { + c->Init(id, target, sync_data_shm_id, sync_data_shm_offset, submit_count); + } +} + void BeginQueryEXT(GLenum target, GLuint id, uint32_t sync_data_shm_id, diff --git a/gpu/command_buffer/client/gles2_implementation.cc b/gpu/command_buffer/client/gles2_implementation.cc index d4e77f2..df474a7 100644 --- a/gpu/command_buffer/client/gles2_implementation.cc +++ b/gpu/command_buffer/client/gles2_implementation.cc @@ -718,7 +718,6 @@ bool GLES2Implementation::GetHelper(GLenum pname, GLint* params) { return true; } break; - // TODO(dyen): Also support GL_GPU_DISJOINT_EXT. case GL_TIMESTAMP_EXT: // We convert all GPU timestamps to CPU time. *params = base::saturated_cast<GLint>( @@ -4863,7 +4862,6 @@ void GLES2Implementation::BeginQueryEXT(GLenum target, GLuint id) { << GLES2Util::GetStringQueryTarget(target) << ", " << id << ")"); - // Check capabilities switch (target) { case GL_COMMANDS_ISSUED_CHROMIUM: case GL_LATENCY_QUERY_CHROMIUM: @@ -4888,7 +4886,6 @@ void GLES2Implementation::BeginQueryEXT(GLenum target, GLuint id) { return; } break; - // TODO(dyen): Also support GL_TIMESTAMP. case GL_TIME_ELAPSED_EXT: if (!capabilities_.timer_queries) { SetGLError( @@ -4899,7 +4896,7 @@ void GLES2Implementation::BeginQueryEXT(GLenum target, GLuint id) { break; default: SetGLError( - GL_INVALID_OPERATION, "glBeginQueryEXT", "unknown query target"); + GL_INVALID_ENUM, "glBeginQueryEXT", "unknown query target"); return; } @@ -4910,13 +4907,11 @@ void GLES2Implementation::BeginQueryEXT(GLenum target, GLuint id) { return; } - // id = 0 INV_OP if (id == 0) { SetGLError(GL_INVALID_OPERATION, "glBeginQueryEXT", "id is 0"); return; } - // if not GENned INV_OPERATION if (!query_id_allocator_->InUse(id)) { SetGLError(GL_INVALID_OPERATION, "glBeginQueryEXT", "invalid id"); return; @@ -4939,6 +4934,41 @@ void GLES2Implementation::EndQueryEXT(GLenum target) { CheckGLError(); } +void GLES2Implementation::QueryCounterEXT(GLuint id, GLenum target) { + GPU_CLIENT_SINGLE_THREAD_CHECK(); + GPU_CLIENT_LOG("[" << GetLogPrefix() << "] QueryCounterEXT(" + << id + << ", " << GLES2Util::GetStringQueryTarget(target) << ")"); + + switch (target) { + case GL_TIMESTAMP_EXT: + if (!capabilities_.timer_queries) { + SetGLError( + GL_INVALID_OPERATION, "glQueryCounterEXT", + "not enabled for timing queries"); + return; + } + break; + default: + SetGLError( + GL_INVALID_ENUM, "glQueryCounterEXT", "unknown query target"); + return; + } + + if (id == 0) { + SetGLError(GL_INVALID_OPERATION, "glQueryCounterEXT", "id is 0"); + return; + } + + if (!query_id_allocator_->InUse(id)) { + SetGLError(GL_INVALID_OPERATION, "glQueryCounterEXT", "invalid id"); + return; + } + + if (query_tracker_->QueryCounter(id, target, this)) + CheckGLError(); +} + void GLES2Implementation::GetQueryivEXT( GLenum target, GLenum pname, GLint* params) { GPU_CLIENT_SINGLE_THREAD_CHECK(); diff --git a/gpu/command_buffer/client/gles2_implementation_autogen.h b/gpu/command_buffer/client/gles2_implementation_autogen.h index d3c3817..e1c5718 100644 --- a/gpu/command_buffer/client/gles2_implementation_autogen.h +++ b/gpu/command_buffer/client/gles2_implementation_autogen.h @@ -789,6 +789,8 @@ void GenQueriesEXT(GLsizei n, GLuint* queries) override; void DeleteQueriesEXT(GLsizei n, const GLuint* queries) override; +void QueryCounterEXT(GLuint id, GLenum target) override; + GLboolean IsQueryEXT(GLuint id) override; void BeginQueryEXT(GLenum target, GLuint id) override; diff --git a/gpu/command_buffer/client/gles2_implementation_unittest.cc b/gpu/command_buffer/client/gles2_implementation_unittest.cc index c354fd9..2580c8c 100644 --- a/gpu/command_buffer/client/gles2_implementation_unittest.cc +++ b/gpu/command_buffer/client/gles2_implementation_unittest.cc @@ -412,7 +412,8 @@ class GLES2ImplementationTest : public testing::Test { bool lose_context_when_out_of_memory, bool transfer_buffer_initialize_fail, bool sync_query, - bool occlusion_query_boolean) { + bool occlusion_query_boolean, + bool timer_queries) { command_buffer_.reset(new StrictMock<MockClientCommandBuffer>()); if (!command_buffer_->Initialize()) return false; @@ -457,6 +458,7 @@ class GLES2ImplementationTest : public testing::Test { bind_generates_resource_service ? 1 : 0; capabilities.sync_query = sync_query; capabilities.occlusion_query_boolean = occlusion_query_boolean; + capabilities.timer_queries = timer_queries; EXPECT_CALL(*gpu_control_, GetCapabilities()) .WillOnce(testing::Return(capabilities)); @@ -549,7 +551,8 @@ class GLES2ImplementationTest : public testing::Test { lose_context_when_out_of_memory(false), transfer_buffer_initialize_fail(false), sync_query(true), - occlusion_query_boolean(true) {} + occlusion_query_boolean(true), + timer_queries(true) {} bool bind_generates_resource_client; bool bind_generates_resource_service; @@ -557,6 +560,7 @@ class GLES2ImplementationTest : public testing::Test { bool transfer_buffer_initialize_fail; bool sync_query; bool occlusion_query_boolean; + bool timer_queries; }; bool Initialize(const ContextInitOptions& init_options) { @@ -571,7 +575,8 @@ class GLES2ImplementationTest : public testing::Test { init_options.lose_context_when_out_of_memory, init_options.transfer_buffer_initialize_fail, init_options.sync_query, - init_options.occlusion_query_boolean)) + init_options.occlusion_query_boolean, + init_options.timer_queries)) success = false; } @@ -3217,6 +3222,7 @@ TEST_F(GLES2ImplementationManualInitTest, BadQueryTargets) { ContextInitOptions init_options; init_options.sync_query = false; init_options.occlusion_query_boolean = false; + init_options.timer_queries = false; ASSERT_TRUE(Initialize(init_options)); GLuint id = 0; @@ -3235,9 +3241,104 @@ TEST_F(GLES2ImplementationManualInitTest, BadQueryTargets) { EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); EXPECT_EQ(nullptr, GetQuery(id)); + gl_->BeginQueryEXT(GL_TIME_ELAPSED_EXT, id); + EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); + EXPECT_EQ(nullptr, GetQuery(id)); + gl_->BeginQueryEXT(0x123, id); + EXPECT_EQ(GL_INVALID_ENUM, CheckError()); + EXPECT_EQ(nullptr, GetQuery(id)); + + gl_->QueryCounterEXT(id, GL_TIMESTAMP_EXT); EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); EXPECT_EQ(nullptr, GetQuery(id)); + + gl_->QueryCounterEXT(id, 0x123); + EXPECT_EQ(GL_INVALID_ENUM, CheckError()); + EXPECT_EQ(nullptr, GetQuery(id)); +} + +TEST_F(GLES2ImplementationTest, QueryCounterEXT) { + GLuint expected_ids[2] = { 1, 2 }; // These must match what's actually genned. + struct GenCmds { + cmds::GenQueriesEXTImmediate gen; + GLuint data[2]; + }; + GenCmds expected_gen_cmds; + expected_gen_cmds.gen.Init(arraysize(expected_ids), &expected_ids[0]); + GLuint ids[arraysize(expected_ids)] = { 0, }; + gl_->GenQueriesEXT(arraysize(expected_ids), &ids[0]); + EXPECT_EQ(0, memcmp( + &expected_gen_cmds, commands_, sizeof(expected_gen_cmds))); + GLuint id1 = ids[0]; + GLuint id2 = ids[1]; + ClearCommands(); + + // Test QueryCounterEXT fails if id = 0. + gl_->QueryCounterEXT(0, GL_TIMESTAMP_EXT); + EXPECT_TRUE(NoCommandsWritten()); + EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); + + // Test QueryCounterEXT fails if target is unknown. + ClearCommands(); + gl_->QueryCounterEXT(id1, GL_TIME_ELAPSED_EXT); + EXPECT_TRUE(NoCommandsWritten()); + EXPECT_EQ(GL_INVALID_ENUM, CheckError()); + + // Test QueryCounterEXT inserts command. + struct QueryCounterCmds { + cmds::QueryCounterEXT query_counter; + }; + QueryCounterCmds expected_query_counter_cmds; + const void* commands = GetPut(); + gl_->QueryCounterEXT(id1, GL_TIMESTAMP_EXT); + EXPECT_EQ(GL_NO_ERROR, CheckError()); + QueryTracker::Query* query = GetQuery(id1); + ASSERT_TRUE(query != NULL); + expected_query_counter_cmds.query_counter.Init( + GL_TIMESTAMP_EXT, id1, query->shm_id(), query->shm_offset(), + query->submit_count()); + EXPECT_EQ(0, memcmp(&expected_query_counter_cmds, commands, + sizeof(expected_query_counter_cmds))); + ClearCommands(); + + // Test 2nd QueryCounterEXT succeeds. + commands = GetPut(); + gl_->QueryCounterEXT(id2, GL_TIMESTAMP_EXT); + EXPECT_EQ(GL_NO_ERROR, CheckError()); + QueryTracker::Query* query2 = GetQuery(id2); + ASSERT_TRUE(query2 != NULL); + expected_query_counter_cmds.query_counter.Init( + GL_TIMESTAMP_EXT, id2, query2->shm_id(), query2->shm_offset(), + query2->submit_count()); + EXPECT_EQ(0, memcmp(&expected_query_counter_cmds, commands, + sizeof(expected_query_counter_cmds))); + ClearCommands(); + + // Test QueryCounterEXT increments count. + base::subtle::Atomic32 old_submit_count = query->submit_count(); + commands = GetPut(); + gl_->QueryCounterEXT(id1, GL_TIMESTAMP_EXT); + EXPECT_EQ(GL_NO_ERROR, CheckError()); + EXPECT_NE(old_submit_count, query->submit_count()); + expected_query_counter_cmds.query_counter.Init( + GL_TIMESTAMP_EXT, id1, query->shm_id(), query->shm_offset(), + query->submit_count()); + EXPECT_EQ(0, memcmp(&expected_query_counter_cmds, commands, + sizeof(expected_query_counter_cmds))); + ClearCommands(); + + // Test GetQueryObjectuivEXT CheckResultsAvailable + GLuint available = 0xBDu; + ClearCommands(); + gl_->GetQueryObjectuivEXT(id1, GL_QUERY_RESULT_AVAILABLE_EXT, &available); + EXPECT_EQ(0u, available); + + // Test GetQueryObjectui64vEXT CheckResultsAvailable + GLuint64 available2 = 0xBDu; + ClearCommands(); + gl_->GetQueryObjectui64vEXT(id1, GL_QUERY_RESULT_AVAILABLE_EXT, &available2); + EXPECT_EQ(0u, available2); } TEST_F(GLES2ImplementationTest, ErrorQuery) { diff --git a/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h b/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h index 2d36497..97a467a 100644 --- a/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h +++ b/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h @@ -2724,6 +2724,7 @@ TEST_F(GLES2ImplementationTest, DeleteQueriesEXT) { gl_->DeleteQueriesEXT(arraysize(ids), &ids[0]); EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); } +// TODO(zmo): Implement unit test for QueryCounterEXT // TODO(zmo): Implement unit test for BeginQueryEXT TEST_F(GLES2ImplementationTest, BeginTransformFeedback) { diff --git a/gpu/command_buffer/client/gles2_interface_autogen.h b/gpu/command_buffer/client/gles2_interface_autogen.h index c8797728..531fb1d 100644 --- a/gpu/command_buffer/client/gles2_interface_autogen.h +++ b/gpu/command_buffer/client/gles2_interface_autogen.h @@ -590,6 +590,7 @@ virtual void TexStorage2DEXT(GLenum target, GLsizei height) = 0; virtual void GenQueriesEXT(GLsizei n, GLuint* queries) = 0; virtual void DeleteQueriesEXT(GLsizei n, const GLuint* queries) = 0; +virtual void QueryCounterEXT(GLuint id, GLenum target) = 0; virtual GLboolean IsQueryEXT(GLuint id) = 0; virtual void BeginQueryEXT(GLenum target, GLuint id) = 0; virtual void BeginTransformFeedback(GLenum primitivemode) = 0; diff --git a/gpu/command_buffer/client/gles2_interface_stub_autogen.h b/gpu/command_buffer/client/gles2_interface_stub_autogen.h index 5e2a0ac..824fcf9 100644 --- a/gpu/command_buffer/client/gles2_interface_stub_autogen.h +++ b/gpu/command_buffer/client/gles2_interface_stub_autogen.h @@ -573,6 +573,7 @@ void TexStorage2DEXT(GLenum target, GLsizei height) override; void GenQueriesEXT(GLsizei n, GLuint* queries) override; void DeleteQueriesEXT(GLsizei n, const GLuint* queries) override; +void QueryCounterEXT(GLuint id, GLenum target) override; GLboolean IsQueryEXT(GLuint id) override; void BeginQueryEXT(GLenum target, GLuint id) override; void BeginTransformFeedback(GLenum primitivemode) override; diff --git a/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h b/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h index 7fe32b8..bd6aa6d 100644 --- a/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h +++ b/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h @@ -975,6 +975,8 @@ void GLES2InterfaceStub::GenQueriesEXT(GLsizei /* n */, GLuint* /* queries */) { void GLES2InterfaceStub::DeleteQueriesEXT(GLsizei /* n */, const GLuint* /* queries */) { } +void GLES2InterfaceStub::QueryCounterEXT(GLuint /* id */, GLenum /* target */) { +} GLboolean GLES2InterfaceStub::IsQueryEXT(GLuint /* id */) { return 0; } diff --git a/gpu/command_buffer/client/gles2_trace_implementation_autogen.h b/gpu/command_buffer/client/gles2_trace_implementation_autogen.h index 3da7e56..b4e77c8 100644 --- a/gpu/command_buffer/client/gles2_trace_implementation_autogen.h +++ b/gpu/command_buffer/client/gles2_trace_implementation_autogen.h @@ -573,6 +573,7 @@ void TexStorage2DEXT(GLenum target, GLsizei height) override; void GenQueriesEXT(GLsizei n, GLuint* queries) override; void DeleteQueriesEXT(GLsizei n, const GLuint* queries) override; +void QueryCounterEXT(GLuint id, GLenum target) override; GLboolean IsQueryEXT(GLuint id) override; void BeginQueryEXT(GLenum target, GLuint id) override; void BeginTransformFeedback(GLenum primitivemode) override; diff --git a/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h b/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h index c7b76109..2386727 100644 --- a/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h +++ b/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h @@ -1642,6 +1642,11 @@ void GLES2TraceImplementation::DeleteQueriesEXT(GLsizei n, gl_->DeleteQueriesEXT(n, queries); } +void GLES2TraceImplementation::QueryCounterEXT(GLuint id, GLenum target) { + TRACE_EVENT_BINARY_EFFICIENT0("gpu", "GLES2Trace::QueryCounterEXT"); + gl_->QueryCounterEXT(id, target); +} + GLboolean GLES2TraceImplementation::IsQueryEXT(GLuint id) { TRACE_EVENT_BINARY_EFFICIENT0("gpu", "GLES2Trace::IsQueryEXT"); return gl_->IsQueryEXT(id); diff --git a/gpu/command_buffer/client/query_tracker.cc b/gpu/command_buffer/client/query_tracker.cc index 37a1478..c85c6bb 100644 --- a/gpu/command_buffer/client/query_tracker.cc +++ b/gpu/command_buffer/client/query_tracker.cc @@ -166,6 +166,14 @@ void QueryTracker::Query::End(GLES2Implementation* gl) { MarkAsPending(gl->helper()->InsertToken()); } +void QueryTracker::Query::QueryCounter(GLES2Implementation* gl) { + MarkAsActive(); + flush_count_ = gl->helper()->flush_generation(); + gl->helper()->QueryCounterEXT(target(), id(), shm_id(), shm_offset(), + submit_count()); + MarkAsPending(gl->helper()->InsertToken()); +} + bool QueryTracker::Query::CheckResultsAvailable( CommandBufferHelper* helper) { if (Pending()) { @@ -324,5 +332,27 @@ bool QueryTracker::EndQuery(GLenum target, GLES2Implementation* gl) { return true; } +bool QueryTracker::QueryCounter(GLuint id, GLenum target, + GLES2Implementation* gl) { + QueryTracker::Query* query = GetQuery(id); + if (!query) { + query = CreateQuery(id, target); + if (!query) { + gl->SetGLError(GL_OUT_OF_MEMORY, + "glQueryCounterEXT", + "transfer buffer allocation failed"); + return false; + } + } else if (query->target() != target) { + gl->SetGLError(GL_INVALID_OPERATION, + "glQueryCounterEXT", + "target does not match"); + return false; + } + + query->QueryCounter(gl); + return true; +} + } // namespace gles2 } // namespace gpu diff --git a/gpu/command_buffer/client/query_tracker.h b/gpu/command_buffer/client/query_tracker.h index abd2f5d..45f5681 100644 --- a/gpu/command_buffer/client/query_tracker.h +++ b/gpu/command_buffer/client/query_tracker.h @@ -143,6 +143,7 @@ class GLES2_IMPL_EXPORT QueryTracker { void Begin(GLES2Implementation* gl); void End(GLES2Implementation* gl); + void QueryCounter(GLES2Implementation* gl); GLuint id_; GLenum target_; @@ -167,6 +168,7 @@ class GLES2_IMPL_EXPORT QueryTracker { bool BeginQuery(GLuint id, GLenum target, GLES2Implementation* gl); bool EndQuery(GLenum target, GLES2Implementation* gl); + bool QueryCounter(GLuint id, GLenum target, GLES2Implementation* gl); private: typedef base::hash_map<GLuint, Query*> QueryIdMap; diff --git a/gpu/command_buffer/client/query_tracker_unittest.cc b/gpu/command_buffer/client/query_tracker_unittest.cc index 51c8699..cba94d1 100644 --- a/gpu/command_buffer/client/query_tracker_unittest.cc +++ b/gpu/command_buffer/client/query_tracker_unittest.cc @@ -290,6 +290,7 @@ TEST_F(QueryTrackerTest, ManyQueries) { EXPECT_EQ(use_count_before_remove - 1, GetBucketUsedCount(bucket)); } } + } // namespace gles2 } // namespace gpu diff --git a/gpu/command_buffer/cmd_buffer_functions.txt b/gpu/command_buffer/cmd_buffer_functions.txt index abaf27b..cf88d38 100644 --- a/gpu/command_buffer/cmd_buffer_functions.txt +++ b/gpu/command_buffer/cmd_buffer_functions.txt @@ -231,6 +231,7 @@ GL_APICALL void GL_APIENTRY glFramebufferTexture2DMultisampleEXT (GLenum GL_APICALL void GL_APIENTRY glTexStorage2DEXT (GLenumTextureBindTarget target, GLsizei levels, GLenumTextureInternalFormatStorage internalFormat, GLsizei width, GLsizei height); GL_APICALL void GL_APIENTRY glGenQueriesEXT (GLsizeiNotNegative n, GLuint* queries); GL_APICALL void GL_APIENTRY glDeleteQueriesEXT (GLsizeiNotNegative n, const GLuint* queries); +GL_APICALL void GL_APIENTRY glQueryCounterEXT (GLidQuery id, GLenumQueryTarget target); GL_APICALL GLboolean GL_APIENTRY glIsQueryEXT (GLidQuery id); GL_APICALL void GL_APIENTRY glBeginQueryEXT (GLenumQueryTarget target, GLidQuery id); GL_APICALL void GL_APIENTRY glBeginTransformFeedback (GLenumTransformFeedbackPrimitiveMode primitivemode); diff --git a/gpu/command_buffer/common/gles2_cmd_format_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_autogen.h index 1c0a9c0..6a0c173 100644 --- a/gpu/command_buffer/common/gles2_cmd_format_autogen.h +++ b/gpu/command_buffer/common/gles2_cmd_format_autogen.h @@ -11055,6 +11055,65 @@ static_assert(offsetof(DeleteQueriesEXTImmediate, header) == 0, static_assert(offsetof(DeleteQueriesEXTImmediate, n) == 4, "offset of DeleteQueriesEXTImmediate n should be 4"); +struct QueryCounterEXT { + typedef QueryCounterEXT ValueType; + static const CommandId kCmdId = kQueryCounterEXT; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + static const uint8 cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3); + + static uint32_t ComputeSize() { + return static_cast<uint32_t>(sizeof(ValueType)); // NOLINT + } + + void SetHeader() { header.SetCmd<ValueType>(); } + + void Init(GLuint _id, + GLenum _target, + uint32_t _sync_data_shm_id, + uint32_t _sync_data_shm_offset, + GLuint _submit_count) { + SetHeader(); + id = _id; + target = _target; + sync_data_shm_id = _sync_data_shm_id; + sync_data_shm_offset = _sync_data_shm_offset; + submit_count = _submit_count; + } + + void* Set(void* cmd, + GLuint _id, + GLenum _target, + uint32_t _sync_data_shm_id, + uint32_t _sync_data_shm_offset, + GLuint _submit_count) { + static_cast<ValueType*>(cmd)->Init(_id, _target, _sync_data_shm_id, + _sync_data_shm_offset, _submit_count); + return NextCmdAddress<ValueType>(cmd); + } + + gpu::CommandHeader header; + uint32_t id; + uint32_t target; + uint32_t sync_data_shm_id; + uint32_t sync_data_shm_offset; + uint32_t submit_count; +}; + +static_assert(sizeof(QueryCounterEXT) == 24, + "size of QueryCounterEXT should be 24"); +static_assert(offsetof(QueryCounterEXT, header) == 0, + "offset of QueryCounterEXT header should be 0"); +static_assert(offsetof(QueryCounterEXT, id) == 4, + "offset of QueryCounterEXT id should be 4"); +static_assert(offsetof(QueryCounterEXT, target) == 8, + "offset of QueryCounterEXT target should be 8"); +static_assert(offsetof(QueryCounterEXT, sync_data_shm_id) == 12, + "offset of QueryCounterEXT sync_data_shm_id should be 12"); +static_assert(offsetof(QueryCounterEXT, sync_data_shm_offset) == 16, + "offset of QueryCounterEXT sync_data_shm_offset should be 16"); +static_assert(offsetof(QueryCounterEXT, submit_count) == 20, + "offset of QueryCounterEXT submit_count should be 20"); + struct BeginQueryEXT { typedef BeginQueryEXT ValueType; static const CommandId kCmdId = kBeginQueryEXT; diff --git a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h index 2557c25..aa9bf84 100644 --- a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h +++ b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h @@ -3783,6 +3783,22 @@ TEST_F(GLES2FormatTest, DeleteQueriesEXTImmediate) { // TODO(gman): Check that ids were inserted; } +TEST_F(GLES2FormatTest, QueryCounterEXT) { + cmds::QueryCounterEXT& cmd = *GetBufferAs<cmds::QueryCounterEXT>(); + void* next_cmd = cmd.Set(&cmd, static_cast<GLuint>(11), + static_cast<GLenum>(12), static_cast<uint32_t>(13), + static_cast<uint32_t>(14), static_cast<GLuint>(15)); + EXPECT_EQ(static_cast<uint32_t>(cmds::QueryCounterEXT::kCmdId), + cmd.header.command); + EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u); + EXPECT_EQ(static_cast<GLuint>(11), cmd.id); + EXPECT_EQ(static_cast<GLenum>(12), cmd.target); + EXPECT_EQ(static_cast<uint32_t>(13), cmd.sync_data_shm_id); + EXPECT_EQ(static_cast<uint32_t>(14), cmd.sync_data_shm_offset); + EXPECT_EQ(static_cast<GLuint>(15), cmd.submit_count); + CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd)); +} + TEST_F(GLES2FormatTest, BeginQueryEXT) { cmds::BeginQueryEXT& cmd = *GetBufferAs<cmds::BeginQueryEXT>(); void* next_cmd = diff --git a/gpu/command_buffer/common/gles2_cmd_ids_autogen.h b/gpu/command_buffer/common/gles2_cmd_ids_autogen.h index 2b0941b..356968c 100644 --- a/gpu/command_buffer/common/gles2_cmd_ids_autogen.h +++ b/gpu/command_buffer/common/gles2_cmd_ids_autogen.h @@ -239,84 +239,85 @@ OP(TexStorage2DEXT) /* 480 */ \ OP(GenQueriesEXTImmediate) /* 481 */ \ OP(DeleteQueriesEXTImmediate) /* 482 */ \ - OP(BeginQueryEXT) /* 483 */ \ - OP(BeginTransformFeedback) /* 484 */ \ - OP(EndQueryEXT) /* 485 */ \ - OP(EndTransformFeedback) /* 486 */ \ - OP(InsertEventMarkerEXT) /* 487 */ \ - OP(PushGroupMarkerEXT) /* 488 */ \ - OP(PopGroupMarkerEXT) /* 489 */ \ - OP(GenVertexArraysOESImmediate) /* 490 */ \ - OP(DeleteVertexArraysOESImmediate) /* 491 */ \ - OP(IsVertexArrayOES) /* 492 */ \ - OP(BindVertexArrayOES) /* 493 */ \ - OP(SwapBuffers) /* 494 */ \ - OP(GetMaxValueInBufferCHROMIUM) /* 495 */ \ - OP(EnableFeatureCHROMIUM) /* 496 */ \ - OP(MapBufferRange) /* 497 */ \ - OP(UnmapBuffer) /* 498 */ \ - OP(ResizeCHROMIUM) /* 499 */ \ - OP(GetRequestableExtensionsCHROMIUM) /* 500 */ \ - OP(RequestExtensionCHROMIUM) /* 501 */ \ - OP(GetProgramInfoCHROMIUM) /* 502 */ \ - OP(GetUniformBlocksCHROMIUM) /* 503 */ \ - OP(GetTransformFeedbackVaryingsCHROMIUM) /* 504 */ \ - OP(GetUniformsES3CHROMIUM) /* 505 */ \ - OP(GetTranslatedShaderSourceANGLE) /* 506 */ \ - OP(PostSubBufferCHROMIUM) /* 507 */ \ - OP(TexImageIOSurface2DCHROMIUM) /* 508 */ \ - OP(CopyTextureCHROMIUM) /* 509 */ \ - OP(CopySubTextureCHROMIUM) /* 510 */ \ - OP(CompressedCopyTextureCHROMIUM) /* 511 */ \ - OP(DrawArraysInstancedANGLE) /* 512 */ \ - OP(DrawElementsInstancedANGLE) /* 513 */ \ - OP(VertexAttribDivisorANGLE) /* 514 */ \ - OP(GenMailboxCHROMIUM) /* 515 */ \ - OP(ProduceTextureCHROMIUMImmediate) /* 516 */ \ - OP(ProduceTextureDirectCHROMIUMImmediate) /* 517 */ \ - OP(ConsumeTextureCHROMIUMImmediate) /* 518 */ \ - OP(CreateAndConsumeTextureCHROMIUMImmediate) /* 519 */ \ - OP(BindUniformLocationCHROMIUMBucket) /* 520 */ \ - OP(GenValuebuffersCHROMIUMImmediate) /* 521 */ \ - OP(DeleteValuebuffersCHROMIUMImmediate) /* 522 */ \ - OP(IsValuebufferCHROMIUM) /* 523 */ \ - OP(BindValuebufferCHROMIUM) /* 524 */ \ - OP(SubscribeValueCHROMIUM) /* 525 */ \ - OP(PopulateSubscribedValuesCHROMIUM) /* 526 */ \ - OP(UniformValuebufferCHROMIUM) /* 527 */ \ - OP(BindTexImage2DCHROMIUM) /* 528 */ \ - OP(ReleaseTexImage2DCHROMIUM) /* 529 */ \ - OP(TraceBeginCHROMIUM) /* 530 */ \ - OP(TraceEndCHROMIUM) /* 531 */ \ - OP(AsyncTexSubImage2DCHROMIUM) /* 532 */ \ - OP(AsyncTexImage2DCHROMIUM) /* 533 */ \ - OP(WaitAsyncTexImage2DCHROMIUM) /* 534 */ \ - OP(WaitAllAsyncTexImage2DCHROMIUM) /* 535 */ \ - OP(DiscardFramebufferEXTImmediate) /* 536 */ \ - OP(LoseContextCHROMIUM) /* 537 */ \ - OP(InsertSyncPointCHROMIUM) /* 538 */ \ - OP(WaitSyncPointCHROMIUM) /* 539 */ \ - OP(DrawBuffersEXTImmediate) /* 540 */ \ - OP(DiscardBackbufferCHROMIUM) /* 541 */ \ - OP(ScheduleOverlayPlaneCHROMIUM) /* 542 */ \ - OP(SwapInterval) /* 543 */ \ - OP(FlushDriverCachesCHROMIUM) /* 544 */ \ - OP(MatrixLoadfCHROMIUMImmediate) /* 545 */ \ - OP(MatrixLoadIdentityCHROMIUM) /* 546 */ \ - OP(GenPathsCHROMIUM) /* 547 */ \ - OP(DeletePathsCHROMIUM) /* 548 */ \ - OP(IsPathCHROMIUM) /* 549 */ \ - OP(PathCommandsCHROMIUM) /* 550 */ \ - OP(PathParameterfCHROMIUM) /* 551 */ \ - OP(PathParameteriCHROMIUM) /* 552 */ \ - OP(PathStencilFuncCHROMIUM) /* 553 */ \ - OP(StencilFillPathCHROMIUM) /* 554 */ \ - OP(StencilStrokePathCHROMIUM) /* 555 */ \ - OP(CoverFillPathCHROMIUM) /* 556 */ \ - OP(CoverStrokePathCHROMIUM) /* 557 */ \ - OP(StencilThenCoverFillPathCHROMIUM) /* 558 */ \ - OP(StencilThenCoverStrokePathCHROMIUM) /* 559 */ \ - OP(BlendBarrierKHR) /* 560 */ + OP(QueryCounterEXT) /* 483 */ \ + OP(BeginQueryEXT) /* 484 */ \ + OP(BeginTransformFeedback) /* 485 */ \ + OP(EndQueryEXT) /* 486 */ \ + OP(EndTransformFeedback) /* 487 */ \ + OP(InsertEventMarkerEXT) /* 488 */ \ + OP(PushGroupMarkerEXT) /* 489 */ \ + OP(PopGroupMarkerEXT) /* 490 */ \ + OP(GenVertexArraysOESImmediate) /* 491 */ \ + OP(DeleteVertexArraysOESImmediate) /* 492 */ \ + OP(IsVertexArrayOES) /* 493 */ \ + OP(BindVertexArrayOES) /* 494 */ \ + OP(SwapBuffers) /* 495 */ \ + OP(GetMaxValueInBufferCHROMIUM) /* 496 */ \ + OP(EnableFeatureCHROMIUM) /* 497 */ \ + OP(MapBufferRange) /* 498 */ \ + OP(UnmapBuffer) /* 499 */ \ + OP(ResizeCHROMIUM) /* 500 */ \ + OP(GetRequestableExtensionsCHROMIUM) /* 501 */ \ + OP(RequestExtensionCHROMIUM) /* 502 */ \ + OP(GetProgramInfoCHROMIUM) /* 503 */ \ + OP(GetUniformBlocksCHROMIUM) /* 504 */ \ + OP(GetTransformFeedbackVaryingsCHROMIUM) /* 505 */ \ + OP(GetUniformsES3CHROMIUM) /* 506 */ \ + OP(GetTranslatedShaderSourceANGLE) /* 507 */ \ + OP(PostSubBufferCHROMIUM) /* 508 */ \ + OP(TexImageIOSurface2DCHROMIUM) /* 509 */ \ + OP(CopyTextureCHROMIUM) /* 510 */ \ + OP(CopySubTextureCHROMIUM) /* 511 */ \ + OP(CompressedCopyTextureCHROMIUM) /* 512 */ \ + OP(DrawArraysInstancedANGLE) /* 513 */ \ + OP(DrawElementsInstancedANGLE) /* 514 */ \ + OP(VertexAttribDivisorANGLE) /* 515 */ \ + OP(GenMailboxCHROMIUM) /* 516 */ \ + OP(ProduceTextureCHROMIUMImmediate) /* 517 */ \ + OP(ProduceTextureDirectCHROMIUMImmediate) /* 518 */ \ + OP(ConsumeTextureCHROMIUMImmediate) /* 519 */ \ + OP(CreateAndConsumeTextureCHROMIUMImmediate) /* 520 */ \ + OP(BindUniformLocationCHROMIUMBucket) /* 521 */ \ + OP(GenValuebuffersCHROMIUMImmediate) /* 522 */ \ + OP(DeleteValuebuffersCHROMIUMImmediate) /* 523 */ \ + OP(IsValuebufferCHROMIUM) /* 524 */ \ + OP(BindValuebufferCHROMIUM) /* 525 */ \ + OP(SubscribeValueCHROMIUM) /* 526 */ \ + OP(PopulateSubscribedValuesCHROMIUM) /* 527 */ \ + OP(UniformValuebufferCHROMIUM) /* 528 */ \ + OP(BindTexImage2DCHROMIUM) /* 529 */ \ + OP(ReleaseTexImage2DCHROMIUM) /* 530 */ \ + OP(TraceBeginCHROMIUM) /* 531 */ \ + OP(TraceEndCHROMIUM) /* 532 */ \ + OP(AsyncTexSubImage2DCHROMIUM) /* 533 */ \ + OP(AsyncTexImage2DCHROMIUM) /* 534 */ \ + OP(WaitAsyncTexImage2DCHROMIUM) /* 535 */ \ + OP(WaitAllAsyncTexImage2DCHROMIUM) /* 536 */ \ + OP(DiscardFramebufferEXTImmediate) /* 537 */ \ + OP(LoseContextCHROMIUM) /* 538 */ \ + OP(InsertSyncPointCHROMIUM) /* 539 */ \ + OP(WaitSyncPointCHROMIUM) /* 540 */ \ + OP(DrawBuffersEXTImmediate) /* 541 */ \ + OP(DiscardBackbufferCHROMIUM) /* 542 */ \ + OP(ScheduleOverlayPlaneCHROMIUM) /* 543 */ \ + OP(SwapInterval) /* 544 */ \ + OP(FlushDriverCachesCHROMIUM) /* 545 */ \ + OP(MatrixLoadfCHROMIUMImmediate) /* 546 */ \ + OP(MatrixLoadIdentityCHROMIUM) /* 547 */ \ + OP(GenPathsCHROMIUM) /* 548 */ \ + OP(DeletePathsCHROMIUM) /* 549 */ \ + OP(IsPathCHROMIUM) /* 550 */ \ + OP(PathCommandsCHROMIUM) /* 551 */ \ + OP(PathParameterfCHROMIUM) /* 552 */ \ + OP(PathParameteriCHROMIUM) /* 553 */ \ + OP(PathStencilFuncCHROMIUM) /* 554 */ \ + OP(StencilFillPathCHROMIUM) /* 555 */ \ + OP(StencilStrokePathCHROMIUM) /* 556 */ \ + OP(CoverFillPathCHROMIUM) /* 557 */ \ + OP(CoverStrokePathCHROMIUM) /* 558 */ \ + OP(StencilThenCoverFillPathCHROMIUM) /* 559 */ \ + OP(StencilThenCoverStrokePathCHROMIUM) /* 560 */ \ + OP(BlendBarrierKHR) /* 561 */ enum CommandId { kStartPoint = cmd::kLastCommonId, // All GLES2 commands start after this. diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc index b62a44f..7b5b57b 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc @@ -11622,7 +11622,6 @@ error::Error GLES2DecoderImpl::HandleBeginQueryEXT(uint32 immediate_data_size, } break; case GL_TIME_ELAPSED: - // TODO(dyen): Also support GL_TIMESTAMP. if (!query_manager_->GPUTimingAvailable()) { LOCAL_SET_GL_ERROR( GL_INVALID_OPERATION, "glBeginQueryEXT", @@ -11632,7 +11631,7 @@ error::Error GLES2DecoderImpl::HandleBeginQueryEXT(uint32 immediate_data_size, break; default: LOCAL_SET_GL_ERROR( - GL_INVALID_OPERATION, "glBeginQueryEXT", + GL_INVALID_ENUM, "glBeginQueryEXT", "unknown query target"); return error::kNoError; } @@ -11703,6 +11702,50 @@ error::Error GLES2DecoderImpl::HandleEndQueryEXT(uint32 immediate_data_size, return error::kNoError; } +error::Error GLES2DecoderImpl::HandleQueryCounterEXT(uint32 immediate_data_size, + const void* cmd_data) { + const gles2::cmds::QueryCounterEXT& c = + *static_cast<const gles2::cmds::QueryCounterEXT*>(cmd_data); + GLuint client_id = static_cast<GLuint>(c.id); + GLenum target = static_cast<GLenum>(c.target); + int32 sync_shm_id = static_cast<int32>(c.sync_data_shm_id); + uint32 sync_shm_offset = static_cast<uint32>(c.sync_data_shm_offset); + uint32 submit_count = static_cast<GLuint>(c.submit_count); + + switch (target) { + case GL_TIMESTAMP: + if (!query_manager_->GPUTimingAvailable()) { + LOCAL_SET_GL_ERROR( + GL_INVALID_OPERATION, "glQueryCounterEXT", + "not enabled for timing queries"); + return error::kNoError; + } + break; + default: + LOCAL_SET_GL_ERROR( + GL_INVALID_ENUM, "glQueryCounterEXT", + "unknown query target"); + return error::kNoError; + } + + QueryManager::Query* query = query_manager_->GetQuery(client_id); + if (!query) { + if (!query_manager_->IsValidQuery(client_id)) { + LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, + "glQueryCounterEXT", + "id not made by glGenQueriesEXT"); + return error::kNoError; + } + query = query_manager_->CreateQuery( + target, client_id, sync_shm_id, sync_shm_offset); + } + if (!query_manager_->QueryCounter(query, submit_count)) { + return error::kOutOfBounds; + } + + return error::kNoError; +} + bool GLES2DecoderImpl::GenVertexArraysOESHelper( GLsizei n, const GLuint* client_ids) { for (GLsizei ii = 0; ii < n; ++ii) { diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc index a76346f..ae525d4 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc @@ -26,6 +26,7 @@ #include "ui/gl/gl_implementation.h" #include "ui/gl/gl_mock.h" #include "ui/gl/gl_surface_stub.h" +#include "ui/gl/gpu_timing_fake.h" #if !defined(GL_DEPTH24_STENCIL8) @@ -562,7 +563,7 @@ TEST_P(GLES2DecoderManualInitTest, BeginEndQueryEXT) { struct QueryType { GLenum type; - bool is_gl; + bool is_counter; }; const QueryType kQueryTypes[] = { @@ -572,112 +573,169 @@ const QueryType kQueryTypes[] = { {GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM, false}, {GL_GET_ERROR_QUERY_CHROMIUM, false}, {GL_COMMANDS_COMPLETED_CHROMIUM, false}, - {GL_ANY_SAMPLES_PASSED_EXT, true}, - {GL_TIME_ELAPSED, true}, + {GL_ANY_SAMPLES_PASSED_EXT, false}, + {GL_TIME_ELAPSED, false}, + {GL_TIMESTAMP, true}, }; +const GLsync kGlSync = reinterpret_cast<GLsync>(0xdeadbeef); -static void CheckBeginEndQueryBadMemoryFails(GLES2DecoderTestBase* test, - GLuint client_id, - GLuint service_id, - const QueryType& query_type, - int32 shm_id, - uint32 shm_offset) { - // We need to reset the decoder on each iteration, because we lose the - // context every time. - GLES2DecoderTestBase::InitState init; - init.extensions = "GL_EXT_occlusion_query_boolean" - " GL_ARB_sync" - " GL_ARB_timer_query"; - init.gl_version = "opengl es 2.0"; - init.has_alpha = true; - init.request_alpha = true; - init.bind_generates_resource = true; - test->InitDecoder(init); - ::testing::StrictMock< ::gfx::MockGLInterface>* gl = test->GetGLMock(); - - BeginQueryEXT begin_cmd; - +static void ExecuteGenerateQueryCmd(GLES2DecoderTestBase* test, + ::gfx::MockGLInterface* gl, + GLenum target, + GLuint client_id, + GLuint service_id) { test->GenHelper<GenQueriesEXTImmediate>(client_id); - - if (query_type.is_gl) { + if (GL_ANY_SAMPLES_PASSED_EXT == target) { EXPECT_CALL(*gl, GenQueries(1, _)) .WillOnce(SetArgPointee<1>(service_id)) .RetiresOnSaturation(); - EXPECT_CALL(*gl, BeginQuery(query_type.type, service_id)) + } +} + +static error::Error ExecuteBeginQueryCmd(GLES2DecoderTestBase* test, + ::gfx::MockGLInterface* gl, + ::gfx::GPUTimingFake* timing_queries, + GLenum target, + GLuint client_id, + GLuint service_id, + int32 shm_id, + uint32 shm_offset) { + if (GL_ANY_SAMPLES_PASSED_EXT == target) { + EXPECT_CALL(*gl, BeginQuery(target, service_id)) .Times(1) .RetiresOnSaturation(); + } else if (GL_TIME_ELAPSED == target) { + timing_queries->ExpectGPUTimerQuery(*gl, true); } - // Test bad shared memory fails - begin_cmd.Init(query_type.type, client_id, shm_id, shm_offset); - error::Error error1 = test->ExecuteCmd(begin_cmd); + BeginQueryEXT begin_cmd; + begin_cmd.Init(target, client_id, shm_id, shm_offset); + return test->ExecuteCmd(begin_cmd); +} - if (query_type.is_gl) { - EXPECT_CALL(*gl, EndQuery(query_type.type)) +static error::Error ExecuteEndQueryCmd(GLES2DecoderTestBase* test, + ::gfx::MockGLInterface* gl, + GLenum target, + uint32_t submit_count) { + if (GL_ANY_SAMPLES_PASSED_EXT == target) { + EXPECT_CALL(*gl, EndQuery(target)) .Times(1) .RetiresOnSaturation(); - } - if (query_type.type == GL_GET_ERROR_QUERY_CHROMIUM) { + } else if (GL_GET_ERROR_QUERY_CHROMIUM == target) { EXPECT_CALL(*gl, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); - } - GLsync kGlSync = reinterpret_cast<GLsync>(0xdeadbeef); - if (query_type.type == GL_COMMANDS_COMPLETED_CHROMIUM) { + } else if (GL_COMMANDS_COMPLETED_CHROMIUM == target) { EXPECT_CALL(*gl, Flush()).RetiresOnSaturation(); EXPECT_CALL(*gl, FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0)) .WillOnce(Return(kGlSync)) .RetiresOnSaturation(); #if DCHECK_IS_ON() EXPECT_CALL(*gl, IsSync(kGlSync)) - .WillOnce(Return(GL_TRUE)) - .RetiresOnSaturation(); + .WillRepeatedly(Return(GL_TRUE)); #endif } EndQueryEXT end_cmd; - end_cmd.Init(query_type.type, 1); - error::Error error2 = test->ExecuteCmd(end_cmd); + end_cmd.Init(target, submit_count); + return test->ExecuteCmd(end_cmd); +} + +static error::Error ExecuteQueryCounterCmd(GLES2DecoderTestBase* test, + ::gfx::MockGLInterface* gl, + ::gfx::GPUTimingFake* timing_queries, + GLenum target, + GLuint client_id, + GLuint service_id, + int32 shm_id, + uint32 shm_offset, + uint32_t submit_count) { + if (GL_TIMESTAMP == target) { + timing_queries->ExpectGPUTimeStampQuery(*gl, false); + } + + QueryCounterEXT query_counter_cmd; + query_counter_cmd.Init(client_id, + target, + shm_id, + shm_offset, + submit_count); + return test->ExecuteCmd(query_counter_cmd); +} - if (query_type.is_gl) { +static bool ProcessQuery(GLES2DecoderTestBase* test, + ::gfx::MockGLInterface* gl, + GLenum target, + GLuint service_id) { + if (GL_ANY_SAMPLES_PASSED_EXT == target) { EXPECT_CALL( *gl, GetQueryObjectuiv(service_id, GL_QUERY_RESULT_AVAILABLE_EXT, _)) .WillOnce(SetArgPointee<2>(1)) .RetiresOnSaturation(); - if (query_type.type == GL_TIME_ELAPSED) { - EXPECT_CALL(*gl, GetQueryObjectui64v(service_id, GL_QUERY_RESULT_EXT, _)) - .WillOnce(SetArgPointee<2>(1)) - .RetiresOnSaturation(); - } else { - EXPECT_CALL(*gl, GetQueryObjectuiv(service_id, GL_QUERY_RESULT_EXT, _)) - .WillOnce(SetArgPointee<2>(1)) - .RetiresOnSaturation(); - } - EXPECT_CALL(*gl, DeleteQueries(1, _)).Times(1).RetiresOnSaturation(); - } - if (query_type.type == GL_COMMANDS_COMPLETED_CHROMIUM) { -#if DCHECK_IS_ON() - EXPECT_CALL(*gl, IsSync(kGlSync)) - .WillOnce(Return(GL_TRUE)) + EXPECT_CALL(*gl, GetQueryObjectuiv(service_id, GL_QUERY_RESULT_EXT, _)) + .WillOnce(SetArgPointee<2>(1)) .RetiresOnSaturation(); -#endif + } else if (GL_COMMANDS_COMPLETED_CHROMIUM == target) { EXPECT_CALL(*gl, ClientWaitSync(kGlSync, _, _)) .WillOnce(Return(GL_ALREADY_SIGNALED)) .RetiresOnSaturation(); -#if DCHECK_IS_ON() - EXPECT_CALL(*gl, IsSync(kGlSync)) - .WillOnce(Return(GL_TRUE)) - .RetiresOnSaturation(); -#endif EXPECT_CALL(*gl, DeleteSync(kGlSync)).Times(1).RetiresOnSaturation(); } QueryManager* query_manager = test->GetDecoder()->GetQueryManager(); - ASSERT_TRUE(query_manager != NULL); - bool process_success = query_manager->ProcessPendingQueries(false); + EXPECT_TRUE(nullptr != query_manager); + if (!query_manager) + return false; + + return query_manager->ProcessPendingQueries(false); +} + +static void CheckBeginEndQueryBadMemoryFails(GLES2DecoderTestBase* test, + GLuint client_id, + GLuint service_id, + const QueryType& query_type, + int32 shm_id, + uint32 shm_offset) { + // We need to reset the decoder on each iteration, because we lose the + // context every time. + GLES2DecoderTestBase::InitState init; + init.extensions = "GL_EXT_occlusion_query_boolean" + " GL_ARB_sync" + " GL_ARB_timer_query"; + init.gl_version = "opengl es 3.0"; + init.has_alpha = true; + init.request_alpha = true; + init.bind_generates_resource = true; + test->InitDecoder(init); + ::testing::StrictMock< ::gfx::MockGLInterface>* gl = test->GetGLMock(); + ::gfx::GPUTimingFake gpu_timing_queries; + + ExecuteGenerateQueryCmd(test, gl, query_type.type, + client_id, service_id); + + // Test bad shared memory fails + error::Error error1 = error::kNoError; + error::Error error2 = error::kNoError; + if (query_type.is_counter) { + error1 = ExecuteQueryCounterCmd(test, gl, &gpu_timing_queries, + query_type.type, + client_id, service_id, + shm_id, shm_offset, 1); + } else { + error1 = ExecuteBeginQueryCmd(test, gl, &gpu_timing_queries, + query_type.type, + client_id, service_id, + shm_id, shm_offset); + error2 = ExecuteEndQueryCmd(test, gl, query_type.type, 1); + } + + bool process_success = ProcessQuery(test, gl, query_type.type, service_id); EXPECT_TRUE(error1 != error::kNoError || error2 != error::kNoError || !process_success); + + if (GL_ANY_SAMPLES_PASSED_EXT == query_type.type) + EXPECT_CALL(*gl, DeleteQueries(1, _)).Times(1).RetiresOnSaturation(); test->ResetDecoder(); } @@ -711,6 +769,79 @@ TEST_P(GLES2DecoderManualInitTest, BeginEndQueryEXTBadMemoryOffsetFails) { } } +TEST_P(GLES2DecoderManualInitTest, QueryReuseTest) { + for (size_t i = 0; i < arraysize(kQueryTypes); ++i) { + const QueryType& query_type = kQueryTypes[i]; + + GLES2DecoderTestBase::InitState init; + init.extensions = "GL_EXT_occlusion_query_boolean" + " GL_ARB_sync" + " GL_ARB_timer_query"; + init.gl_version = "opengl es 3.0"; + init.has_alpha = true; + init.request_alpha = true; + init.bind_generates_resource = true; + InitDecoder(init); + ::testing::StrictMock< ::gfx::MockGLInterface>* gl = GetGLMock(); + ::gfx::GPUTimingFake gpu_timing_queries; + + ExecuteGenerateQueryCmd(this, gl, query_type.type, + kNewClientId, kNewServiceId); + + // Query once. + if (query_type.is_counter) { + EXPECT_EQ(error::kNoError, ExecuteQueryCounterCmd(this, gl, + &gpu_timing_queries, + query_type.type, + kNewClientId, + kNewServiceId, + kSharedMemoryId, + kSharedMemoryOffset, + 1)); + } else { + EXPECT_EQ(error::kNoError, ExecuteBeginQueryCmd(this, gl, + &gpu_timing_queries, + query_type.type, + kNewClientId, + kNewServiceId, + kSharedMemoryId, + kSharedMemoryOffset)); + EXPECT_EQ(error::kNoError, ExecuteEndQueryCmd(this, gl, + query_type.type, 1)); + } + + EXPECT_TRUE(ProcessQuery(this, gl, query_type.type, kNewServiceId)); + + // Reuse query. + if (query_type.is_counter) { + EXPECT_EQ(error::kNoError, ExecuteQueryCounterCmd(this, gl, + &gpu_timing_queries, + query_type.type, + kNewClientId, + kNewServiceId, + kSharedMemoryId, + kSharedMemoryOffset, + 2)); + } else { + EXPECT_EQ(error::kNoError, ExecuteBeginQueryCmd(this, gl, + &gpu_timing_queries, + query_type.type, + kNewClientId, + kNewServiceId, + kSharedMemoryId, + kSharedMemoryOffset)); + EXPECT_EQ(error::kNoError, ExecuteEndQueryCmd(this, gl, + query_type.type, 2)); + } + + EXPECT_TRUE(ProcessQuery(this, gl, query_type.type, kNewServiceId)); + + if (GL_ANY_SAMPLES_PASSED_EXT == query_type.type) + EXPECT_CALL(*gl, DeleteQueries(1, _)).Times(1).RetiresOnSaturation(); + ResetDecoder(); + } +} + TEST_P(GLES2DecoderTest, BeginEndQueryEXTCommandsIssuedCHROMIUM) { BeginQueryEXT begin_cmd; @@ -798,7 +929,6 @@ TEST_P(GLES2DecoderManualInitTest, BeginEndQueryEXTCommandsCompletedCHROMIUM) { ASSERT_TRUE(query != NULL); EXPECT_FALSE(query->pending()); - GLsync kGlSync = reinterpret_cast<GLsync>(0xdeadbeef); EXPECT_CALL(*gl_, Flush()).RetiresOnSaturation(); EXPECT_CALL(*gl_, FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0)) .WillOnce(Return(kGlSync)) @@ -883,12 +1013,81 @@ TEST_P(GLES2DecoderManualInitTest, BeginInvalidTargetQueryFails) { EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); + begin_cmd.Init(GL_TIME_ELAPSED, + kNewClientId, + kSharedMemoryId, + kSharedMemoryOffset); + EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd)); + EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); + begin_cmd.Init(0xdeadbeef, kNewClientId, kSharedMemoryId, kSharedMemoryOffset); EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd)); + EXPECT_EQ(GL_INVALID_ENUM, GetGLError()); +} + +TEST_P(GLES2DecoderManualInitTest, QueryCounterEXTTimeStamp) { + InitState init; + init.extensions = "GL_ARB_timer_query"; + init.gl_version = "opengl 2.0"; + init.has_alpha = true; + init.request_alpha = true; + init.bind_generates_resource = true; + InitDecoder(init); + + GenHelper<GenQueriesEXTImmediate>(kNewClientId); + + EXPECT_CALL(*gl_, GenQueries(1, _)) + .WillOnce(SetArgPointee<1>(kNewServiceId)) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, QueryCounter(kNewServiceId, GL_TIMESTAMP)) + .Times(1) + .RetiresOnSaturation(); + QueryCounterEXT query_counter_cmd; + query_counter_cmd.Init(kNewClientId, + GL_TIMESTAMP, + kSharedMemoryId, + kSharedMemoryOffset, + 1); + EXPECT_EQ(error::kNoError, ExecuteCmd(query_counter_cmd)); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); + + QueryManager* query_manager = decoder_->GetQueryManager(); + ASSERT_TRUE(query_manager != NULL); + QueryManager::Query* query = query_manager->GetQuery(kNewClientId); + ASSERT_TRUE(query != NULL); + EXPECT_TRUE(query->pending()); +} + +TEST_P(GLES2DecoderManualInitTest, InvalidTargetQueryCounterFails) { + InitState init; + init.extensions = ""; + init.gl_version = "opengl es 2.0"; + init.has_alpha = true; + init.request_alpha = true; + init.bind_generates_resource = true; + InitDecoder(init); + + GenHelper<GenQueriesEXTImmediate>(kNewClientId); + + QueryCounterEXT query_counter_cmd; + query_counter_cmd.Init(kNewClientId, + GL_TIMESTAMP, + kSharedMemoryId, + kSharedMemoryOffset, + 1); + EXPECT_EQ(error::kNoError, ExecuteCmd(query_counter_cmd)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); + + query_counter_cmd.Init(kNewClientId, + 0xdeadbeef, + kSharedMemoryId, + kSharedMemoryOffset, + 1); + EXPECT_EQ(error::kNoError, ExecuteCmd(query_counter_cmd)); + EXPECT_EQ(GL_INVALID_ENUM, GetGLError()); } TEST_P(GLES2DecoderTest, IsEnabledReturnsCachedValue) { diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_3_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_3_autogen.h index 7c0638e..0f28885 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_3_autogen.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_3_autogen.h @@ -325,6 +325,8 @@ TEST_P(GLES2DecoderTest3, ViewportInvalidArgs3_0) { // TODO(gman): TexStorage2DEXT // TODO(gman): GenQueriesEXTImmediate // TODO(gman): DeleteQueriesEXTImmediate +// TODO(gman): QueryCounterEXT + // TODO(gman): BeginQueryEXT TEST_P(GLES2DecoderTest3, BeginTransformFeedbackValidArgs) { diff --git a/gpu/command_buffer/service/query_manager.cc b/gpu/command_buffer/service/query_manager.cc index 3de501a..c4b7bfb 100644 --- a/gpu/command_buffer/service/query_manager.cc +++ b/gpu/command_buffer/service/query_manager.cc @@ -66,6 +66,7 @@ class AsyncPixelTransfersCompletedQuery bool Begin() override; bool End(base::subtle::Atomic32 submit_count) override; + bool QueryCounter(base::subtle::Atomic32 submit_count) override; bool Process(bool did_finish) override; void Destroy(bool have_context) override; @@ -107,6 +108,12 @@ bool AsyncPixelTransfersCompletedQuery::End( return AddToPendingTransferQueue(submit_count); } +bool AsyncPixelTransfersCompletedQuery::QueryCounter( + base::subtle::Atomic32 submit_count) { + NOTREACHED(); + return false; +} + bool AsyncPixelTransfersCompletedQuery::Process(bool did_finish) { QuerySync* sync = manager()->decoder()->GetSharedMemoryAs<QuerySync*>( shm_id(), shm_offset(), sizeof(*sync)); @@ -143,6 +150,7 @@ class AllSamplesPassedQuery : public QueryManager::Query { GLuint service_id); bool Begin() override; bool End(base::subtle::Atomic32 submit_count) override; + bool QueryCounter(base::subtle::Atomic32 submit_count) override; bool Process(bool did_finish) override; void Destroy(bool have_context) override; @@ -171,6 +179,11 @@ bool AllSamplesPassedQuery::End(base::subtle::Atomic32 submit_count) { return AddToPendingQueue(submit_count); } +bool AllSamplesPassedQuery::QueryCounter(base::subtle::Atomic32 submit_count) { + NOTREACHED(); + return false; +} + bool AllSamplesPassedQuery::Process(bool did_finish) { GLuint available = 0; glGetQueryObjectuiv( @@ -202,6 +215,7 @@ class CommandsIssuedQuery : public QueryManager::Query { bool Begin() override; bool End(base::subtle::Atomic32 submit_count) override; + bool QueryCounter(base::subtle::Atomic32 submit_count) override; bool Process(bool did_finish) override; void Destroy(bool have_context) override; @@ -228,6 +242,11 @@ bool CommandsIssuedQuery::End(base::subtle::Atomic32 submit_count) { return MarkAsCompleted(elapsed.InMicroseconds()); } +bool CommandsIssuedQuery::QueryCounter(base::subtle::Atomic32 submit_count) { + NOTREACHED(); + return false; +} + bool CommandsIssuedQuery::Process(bool did_finish) { NOTREACHED(); return true; @@ -249,6 +268,7 @@ class CommandLatencyQuery : public QueryManager::Query { bool Begin() override; bool End(base::subtle::Atomic32 submit_count) override; + bool QueryCounter(base::subtle::Atomic32 submit_count) override; bool Process(bool did_finish) override; void Destroy(bool have_context) override; @@ -271,6 +291,11 @@ bool CommandLatencyQuery::End(base::subtle::Atomic32 submit_count) { return MarkAsCompleted(now.InMicroseconds()); } +bool CommandLatencyQuery::QueryCounter(base::subtle::Atomic32 submit_count) { + NOTREACHED(); + return false; +} + bool CommandLatencyQuery::Process(bool did_finish) { NOTREACHED(); return true; @@ -295,6 +320,7 @@ class AsyncReadPixelsCompletedQuery bool Begin() override; bool End(base::subtle::Atomic32 submit_count) override; + bool QueryCounter(base::subtle::Atomic32 submit_count) override; bool Process(bool did_finish) override; void Destroy(bool have_context) override; @@ -329,6 +355,12 @@ bool AsyncReadPixelsCompletedQuery::End(base::subtle::Atomic32 submit_count) { return Process(false); } +bool AsyncReadPixelsCompletedQuery::QueryCounter( + base::subtle::Atomic32 submit_count) { + NOTREACHED(); + return false; +} + void AsyncReadPixelsCompletedQuery::Complete() { completed_ = true; complete_result_ = MarkAsCompleted(1); @@ -355,6 +387,7 @@ class GetErrorQuery : public QueryManager::Query { bool Begin() override; bool End(base::subtle::Atomic32 submit_count) override; + bool QueryCounter(base::subtle::Atomic32 submit_count) override; bool Process(bool did_finish) override; void Destroy(bool have_context) override; @@ -378,6 +411,11 @@ bool GetErrorQuery::End(base::subtle::Atomic32 submit_count) { return MarkAsCompleted(manager()->decoder()->GetErrorState()->GetGLError()); } +bool GetErrorQuery::QueryCounter(base::subtle::Atomic32 submit_count) { + NOTREACHED(); + return false; +} + bool GetErrorQuery::Process(bool did_finish) { NOTREACHED(); return true; @@ -402,6 +440,7 @@ class CommandsCompletedQuery : public QueryManager::Query { // Overridden from QueryManager::Query: bool Begin() override; bool End(base::subtle::Atomic32 submit_count) override; + bool QueryCounter(base::subtle::Atomic32 submit_count) override; bool Process(bool did_finish) override; void Destroy(bool have_context) override; @@ -430,6 +469,11 @@ bool CommandsCompletedQuery::End(base::subtle::Atomic32 submit_count) { return AddToPendingQueue(submit_count); } +bool CommandsCompletedQuery::QueryCounter(base::subtle::Atomic32 submit_count) { + NOTREACHED(); + return false; +} + bool CommandsCompletedQuery::Process(bool did_finish) { // Note: |did_finish| guarantees that the GPU has passed the fence but // we cannot assume that GLFence::HasCompleted() will return true yet as @@ -460,6 +504,7 @@ class TimeElapsedQuery : public QueryManager::Query { // Overridden from QueryManager::Query: bool Begin() override; bool End(base::subtle::Atomic32 submit_count) override; + bool QueryCounter(base::subtle::Atomic32 submit_count) override; bool Process(bool did_finish) override; void Destroy(bool have_context) override; @@ -487,6 +532,11 @@ bool TimeElapsedQuery::End(base::subtle::Atomic32 submit_count) { return AddToPendingQueue(submit_count); } +bool TimeElapsedQuery::QueryCounter(base::subtle::Atomic32 submit_count) { + NOTREACHED(); + return false; +} + bool TimeElapsedQuery::Process(bool did_finish) { if (!gpu_timer_->IsAvailable()) return true; @@ -505,6 +555,71 @@ void TimeElapsedQuery::Destroy(bool have_context) { TimeElapsedQuery::~TimeElapsedQuery() {} +class TimeStampQuery : public QueryManager::Query { + public: + TimeStampQuery(QueryManager* manager, + GLenum target, + int32 shm_id, + uint32 shm_offset); + + // Overridden from QueryManager::Query: + bool Begin() override; + bool End(base::subtle::Atomic32 submit_count) override; + bool QueryCounter(base::subtle::Atomic32 submit_count) override; + bool Process(bool did_finish) override; + void Destroy(bool have_context) override; + + protected: + ~TimeStampQuery() override; + + private: + scoped_ptr<gfx::GPUTimer> gpu_timer_; +}; + +TimeStampQuery::TimeStampQuery(QueryManager* manager, + GLenum target, + int32 shm_id, + uint32 shm_offset) + : Query(manager, target, shm_id, shm_offset), + gpu_timer_(manager->CreateGPUTimer(false)) {} + +bool TimeStampQuery::Begin() { + NOTREACHED(); + return false; +} + +bool TimeStampQuery::End(base::subtle::Atomic32 submit_count) { + NOTREACHED(); + return false; +} + +bool TimeStampQuery::QueryCounter(base::subtle::Atomic32 submit_count) { + gpu_timer_->QueryTimeStamp(); + return AddToPendingQueue(submit_count); +} + +bool TimeStampQuery::Process(bool did_finish) { + if (!gpu_timer_->IsAvailable()) + return true; + + int64_t start = 0; + int64_t end = 0; + gpu_timer_->GetStartEndTimestamps(&start, &end); + DCHECK(start == end); + + const uint64_t nano_seconds = start * base::Time::kNanosecondsPerMicrosecond; + return MarkAsCompleted(nano_seconds); +} + +void TimeStampQuery::Destroy(bool have_context) { + if (gpu_timer_.get()) { + gpu_timer_->Destroy(have_context); + gpu_timer_.reset(); + } +} + +TimeStampQuery::~TimeStampQuery() {} + QueryManager::QueryManager( GLES2Decoder* decoder, FeatureInfo* feature_info) @@ -573,6 +688,9 @@ QueryManager::Query* QueryManager::CreateQuery( case GL_TIME_ELAPSED: query = new TimeElapsedQuery(this, target, shm_id, shm_offset); break; + case GL_TIMESTAMP: + query = new TimeStampQuery(this, target, shm_id, shm_offset); + break; default: { GLuint service_id = 0; glGenQueries(1, &service_id); @@ -827,5 +945,11 @@ bool QueryManager::EndQuery(Query* query, base::subtle::Atomic32 submit_count) { return query->End(submit_count); } +bool QueryManager::QueryCounter( + Query* query, base::subtle::Atomic32 submit_count) { + DCHECK(query); + return query->QueryCounter(submit_count); +} + } // namespace gles2 } // namespace gpu diff --git a/gpu/command_buffer/service/query_manager.h b/gpu/command_buffer/service/query_manager.h index e453d03..e07d018 100644 --- a/gpu/command_buffer/service/query_manager.h +++ b/gpu/command_buffer/service/query_manager.h @@ -69,6 +69,9 @@ class GPU_EXPORT QueryManager { virtual bool End(base::subtle::Atomic32 submit_count) = 0; // Returns false if shared memory for sync is invalid. + virtual bool QueryCounter(base::subtle::Atomic32 submit_count) = 0; + + // Returns false if shared memory for sync is invalid. virtual bool Process(bool did_finish) = 0; virtual void Destroy(bool have_context) = 0; @@ -174,6 +177,9 @@ class GPU_EXPORT QueryManager { // Returns false if any query is pointing to invalid shared memory. bool EndQuery(Query* query, base::subtle::Atomic32 submit_count); + // Returns false if any query is pointing to invalid shared memory. + bool QueryCounter(Query* query, base::subtle::Atomic32 submit_count); + // Processes pending queries. Returns false if any queries are pointing // to invalid shared memory. |did_finish| is true if this is called as // a result of calling glFinish(). @@ -249,7 +255,6 @@ class GPU_EXPORT QueryManager { // Async pixel transfer queries waiting for completion. QueryQueue pending_transfer_queries_; - // Used for timer queries. scoped_refptr<gfx::GPUTimingClient> gpu_timing_client_; DISALLOW_COPY_AND_ASSIGN(QueryManager); diff --git a/gpu/command_buffer/service/query_manager_unittest.cc b/gpu/command_buffer/service/query_manager_unittest.cc index f6e3682..87273f6 100644 --- a/gpu/command_buffer/service/query_manager_unittest.cc +++ b/gpu/command_buffer/service/query_manager_unittest.cc @@ -38,13 +38,17 @@ class QueryManagerTest : public GpuServiceTest { protected: void SetUp() override { - GpuServiceTest::SetUpWithGLVersion("2.1", "GL_ARB_occlusion_query"); + GpuServiceTest::SetUpWithGLVersion("2.1", + "GL_ARB_occlusion_query, " + "GL_ARB_timer_query"); engine_.reset(new MockCommandBufferEngine()); decoder_.reset(new MockGLES2Decoder()); decoder_->set_engine(engine_.get()); TestHelper::SetupFeatureInfoInitExpectations( gl_.get(), - "GL_EXT_occlusion_query_boolean"); + "GL_EXT_occlusion_query_boolean, GL_ARB_timer_query"); + EXPECT_CALL(*decoder_.get(), GetGLContext()) + .WillRepeatedly(Return(GetGLContext())); scoped_refptr<FeatureInfo> feature_info(new FeatureInfo()); feature_info->Initialize(); manager_.reset(new QueryManager(decoder_.get(), feature_info.get())); @@ -526,6 +530,50 @@ TEST_F(QueryManagerTest, ARBOcclusionQuery) { manager->Destroy(false); } +TEST_F(QueryManagerTest, TimeElapsedQuery) { + const GLuint kClient1Id = 1; + const GLuint kService1Id = 11; + const GLenum kTarget = GL_TIME_ELAPSED_EXT; + const base::subtle::Atomic32 kSubmitCount = 123; + + EXPECT_CALL(*gl_, GenQueries(1, _)) + .WillOnce(SetArgumentPointee<1>(kService1Id)) + .RetiresOnSaturation(); + QueryManager::Query* query = manager_->CreateQuery( + kTarget, kClient1Id, kSharedMemoryId, kSharedMemoryOffset); + ASSERT_TRUE(query != NULL); + + EXPECT_CALL(*gl_, BeginQuery(GL_TIME_ELAPSED_EXT, kService1Id)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, EndQuery(GL_TIME_ELAPSED_EXT)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_TRUE(manager_->BeginQuery(query)); + EXPECT_TRUE(manager_->EndQuery(query, kSubmitCount)); + manager_->Destroy(false); +} + +TEST_F(QueryManagerTest, TimeStampQuery) { + const GLuint kClient1Id = 1; + const GLuint kService1Id = 11; + const GLenum kTarget = GL_TIMESTAMP_EXT; + const base::subtle::Atomic32 kSubmitCount = 123; + + EXPECT_CALL(*gl_, GenQueries(1, _)) + .WillOnce(SetArgumentPointee<1>(kService1Id)) + .RetiresOnSaturation(); + QueryManager::Query* query = manager_->CreateQuery( + kTarget, kClient1Id, kSharedMemoryId, kSharedMemoryOffset); + ASSERT_TRUE(query != NULL); + + EXPECT_CALL(*gl_, QueryCounter(kService1Id, GL_TIMESTAMP_EXT)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_TRUE(manager_->QueryCounter(query, kSubmitCount)); + manager_->Destroy(false); +} + TEST_F(QueryManagerTest, GetErrorQuery) { const GLuint kClient1Id = 1; const GLenum kTarget = GL_GET_ERROR_QUERY_CHROMIUM; diff --git a/mojo/gpu/mojo_gles2_impl_autogen.cc b/mojo/gpu/mojo_gles2_impl_autogen.cc index f677723..7c875c8 100644 --- a/mojo/gpu/mojo_gles2_impl_autogen.cc +++ b/mojo/gpu/mojo_gles2_impl_autogen.cc @@ -1255,6 +1255,9 @@ void MojoGLES2Impl::DeleteQueriesEXT(GLsizei n, const GLuint* queries) { MojoGLES2MakeCurrent(context_); glDeleteQueriesEXT(n, queries); } +void MojoGLES2Impl::QueryCounterEXT(GLuint id, GLenum target) { + NOTREACHED() << "Unimplemented QueryCounterEXT."; +} GLboolean MojoGLES2Impl::IsQueryEXT(GLuint id) { MojoGLES2MakeCurrent(context_); return glIsQueryEXT(id); diff --git a/mojo/gpu/mojo_gles2_impl_autogen.h b/mojo/gpu/mojo_gles2_impl_autogen.h index 0a90a7b..6d4e45e 100644 --- a/mojo/gpu/mojo_gles2_impl_autogen.h +++ b/mojo/gpu/mojo_gles2_impl_autogen.h @@ -595,6 +595,7 @@ class MojoGLES2Impl : public gpu::gles2::GLES2Interface { GLsizei height) override; void GenQueriesEXT(GLsizei n, GLuint* queries) override; void DeleteQueriesEXT(GLsizei n, const GLuint* queries) override; + void QueryCounterEXT(GLuint id, GLenum target) override; GLboolean IsQueryEXT(GLuint id) override; void BeginQueryEXT(GLenum target, GLuint id) override; void BeginTransformFeedback(GLenum primitivemode) override; diff --git a/ui/gl/gpu_timing.cc b/ui/gl/gpu_timing.cc index c902254..e78442e 100644 --- a/ui/gl/gpu_timing.cc +++ b/ui/gl/gpu_timing.cc @@ -11,6 +11,9 @@ namespace gfx { +class TimeElapsedTimerQuery; +class TimerQuery; + int64_t NanoToMicro(uint64_t nano_seconds) { const uint64_t up = nano_seconds + base::Time::kNanosecondsPerMicrosecond / 2; return static_cast<int64_t>(up / base::Time::kNanosecondsPerMicrosecond); @@ -313,6 +316,7 @@ GPUTimingImpl::GPUTimingImpl(GLContextReal* context) { timer_type_ = GPUTiming::kTimerTypeARB; } else if (context->HasExtension("GL_EXT_timer_query")) { timer_type_ = GPUTiming::kTimerTypeEXT; + force_time_elapsed_query_ = true; } } @@ -375,8 +379,7 @@ void GPUTimingImpl::EndElapsedTimeQuery(scoped_refptr<QueryResult> result) { } scoped_refptr<QueryResult> GPUTimingImpl::DoTimeStampQuery() { - DCHECK(timer_type_ == GPUTiming::kTimerTypeDisjoint || - timer_type_ == GPUTiming::kTimerTypeARB); + DCHECK(timer_type_ != GPUTiming::kTimerTypeInvalid); if (force_time_elapsed_query_) { // Replace with elapsed timer queries instead. @@ -467,7 +470,7 @@ GPUTimer::~GPUTimer() { void GPUTimer::Destroy(bool have_context) { if (have_context) { - if (!end_requested_) { + if (timer_state_ == kTimerState_WaitingForEnd) { DCHECK(gpu_timing_client_->gpu_timing_); DCHECK(elapsed_timer_result_.get()); gpu_timing_client_->gpu_timing_->EndElapsedTimeQuery( @@ -476,56 +479,81 @@ void GPUTimer::Destroy(bool have_context) { } } +void GPUTimer::Reset() { + // We can reset from any state other than when a Start() is waiting for End(). + DCHECK(timer_state_ != kTimerState_WaitingForEnd); + time_stamp_result_ = nullptr; + elapsed_timer_result_ = nullptr; + timer_state_ = kTimerState_Ready; +} + +void GPUTimer::QueryTimeStamp() { + DCHECK(gpu_timing_client_->gpu_timing_); + Reset(); + time_stamp_result_ = gpu_timing_client_->gpu_timing_->DoTimeStampQuery(); + timer_state_ = kTimerState_WaitingForResult; +} + void GPUTimer::Start() { DCHECK(gpu_timing_client_->gpu_timing_); + Reset(); if (!use_elapsed_timer_) time_stamp_result_ = gpu_timing_client_->gpu_timing_->DoTimeStampQuery(); elapsed_timer_result_ = gpu_timing_client_->gpu_timing_->BeginElapsedTimeQuery(); + timer_state_ = kTimerState_WaitingForEnd; } void GPUTimer::End() { + DCHECK(timer_state_ == kTimerState_WaitingForEnd); DCHECK(elapsed_timer_result_.get()); - end_requested_ = true; gpu_timing_client_->gpu_timing_->EndElapsedTimeQuery(elapsed_timer_result_); + timer_state_ = kTimerState_WaitingForResult; } bool GPUTimer::IsAvailable() { - if (!end_requested_) - return false; - if (!end_available_) { - DCHECK(elapsed_timer_result_.get()); - if (elapsed_timer_result_->IsAvailable()) { - end_available_ = true; + if (timer_state_ == kTimerState_WaitingForResult) { + // Elapsed timer are only used during start/end queries and always after + // the timestamp query. Otherwise only the timestamp is used. + scoped_refptr<QueryResult> result = + elapsed_timer_result_.get() ? + elapsed_timer_result_ : + time_stamp_result_; + + DCHECK(result.get()); + if (result->IsAvailable()) { + timer_state_ = kTimerState_ResultAvailable; } else { gpu_timing_client_->gpu_timing_->UpdateQueryResults(); - end_available_ = elapsed_timer_result_->IsAvailable(); + if (result->IsAvailable()) + timer_state_ = kTimerState_ResultAvailable; } } - return end_available_; + + return (timer_state_ == kTimerState_ResultAvailable); } void GPUTimer::GetStartEndTimestamps(int64* start, int64* end) { DCHECK(start && end); - DCHECK(elapsed_timer_result_.get()); + DCHECK(elapsed_timer_result_.get() || time_stamp_result_.get()); DCHECK(IsAvailable()); - if (time_stamp_result_.get()) { - DCHECK(time_stamp_result_->IsAvailable()); - const int64_t time_stamp = time_stamp_result_->GetStartValue(); - *start = time_stamp; - *end = time_stamp + elapsed_timer_result_->GetDelta(); - } else { - // Use estimation from elasped timer results. - *start = elapsed_timer_result_->GetStartValue(); - *end = elapsed_timer_result_->GetEndValue(); - } + const int64_t time_stamp = time_stamp_result_.get() ? + time_stamp_result_->GetStartValue() : + elapsed_timer_result_->GetStartValue(); + const int64_t elapsed_time = elapsed_timer_result_.get() ? + elapsed_timer_result_->GetDelta() : + 0; + + *start = time_stamp; + *end = time_stamp + elapsed_time; } int64 GPUTimer::GetDeltaElapsed() { - DCHECK(elapsed_timer_result_.get()); DCHECK(IsAvailable()); - return elapsed_timer_result_->GetDelta(); + if (elapsed_timer_result_.get()) + return elapsed_timer_result_->GetDelta(); + return 0; } GPUTimer::GPUTimer(scoped_refptr<GPUTimingClient> gpu_timing_client, diff --git a/ui/gl/gpu_timing.h b/ui/gl/gpu_timing.h index 08f9c03..5d72d7c 100644 --- a/ui/gl/gpu_timing.h +++ b/ui/gl/gpu_timing.h @@ -43,12 +43,9 @@ namespace gfx { class GLContextReal; -class GPUTiming; class GPUTimingClient; class GPUTimingImpl; class QueryResult; -class TimeElapsedTimerQuery; -class TimerQuery; class GPUTiming { public: @@ -86,6 +83,12 @@ class GL_EXPORT GPUTimer { // this object. void Destroy(bool have_context); + // Clears current queries. + void Reset(); + + // Start an instant timer, start and end will be equal. + void QueryTimeStamp(); + // Start a timer range. void Start(); void End(); @@ -102,8 +105,12 @@ class GL_EXPORT GPUTimer { bool use_elapsed_timer); bool use_elapsed_timer_ = false; - bool end_requested_ = false; - bool end_available_ = false; + enum TimerState { + kTimerState_Ready, + kTimerState_WaitingForEnd, + kTimerState_WaitingForResult, + kTimerState_ResultAvailable + } timer_state_ = kTimerState_Ready; scoped_refptr<GPUTimingClient> gpu_timing_client_; scoped_refptr<QueryResult> time_stamp_result_; scoped_refptr<QueryResult> elapsed_timer_result_; diff --git a/ui/gl/gpu_timing_fake.cc b/ui/gl/gpu_timing_fake.cc index e7c1df9..e329979 100644 --- a/ui/gl/gpu_timing_fake.cc +++ b/ui/gl/gpu_timing_fake.cc @@ -53,9 +53,48 @@ void GPUTimingFake::ExpectNoDisjointCalls(MockGLInterface& gl) { EXPECT_CALL(gl, GetIntegerv(GL_GPU_DISJOINT_EXT, _)).Times(Exactly(0)); } +void GPUTimingFake::ExpectGPUTimeStampQuery( + MockGLInterface& gl, bool elapsed_query) { + EXPECT_CALL(gl, GenQueries(1, NotNull())).Times(Exactly(1)) + .WillRepeatedly(Invoke(this, &GPUTimingFake::FakeGLGenQueries)); + + if (!elapsed_query) { + // Time Stamp based queries. + EXPECT_CALL(gl, GetInteger64v(GL_TIMESTAMP, _)) + .WillRepeatedly( + Invoke(this, &GPUTimingFake::FakeGLGetInteger64v)); + + EXPECT_CALL(gl, QueryCounter(_, GL_TIMESTAMP)).Times(Exactly(1)) + .WillRepeatedly( + Invoke(this, &GPUTimingFake::FakeGLQueryCounter)); + } else { + // Time Elapsed based queries. + EXPECT_CALL(gl, BeginQuery(GL_TIME_ELAPSED, _)).Times(Exactly(1)) + .WillRepeatedly( + Invoke(this, &GPUTimingFake::FakeGLBeginQuery)); + + EXPECT_CALL(gl, EndQuery(GL_TIME_ELAPSED)).Times(Exactly(1)) + .WillRepeatedly(Invoke(this, &GPUTimingFake::FakeGLEndQuery)); + } + + EXPECT_CALL(gl, GetQueryObjectuiv(_, GL_QUERY_RESULT_AVAILABLE, + NotNull())) + .WillRepeatedly( + Invoke(this, &GPUTimingFake::FakeGLGetQueryObjectuiv)); + + EXPECT_CALL(gl, GetQueryObjectui64v(_, GL_QUERY_RESULT, NotNull())) + .WillRepeatedly( + Invoke(this, &GPUTimingFake::FakeGLGetQueryObjectui64v)); + + EXPECT_CALL(gl, DeleteQueries(1, NotNull())).Times(AtLeast(1)) + .WillRepeatedly( + Invoke(this, &GPUTimingFake::FakeGLDeleteQueries)); +} + void GPUTimingFake::ExpectGPUTimerQuery( MockGLInterface& gl, bool elapsed_query) { - EXPECT_CALL(gl, GenQueries(1, NotNull())).Times(AtLeast(2)) + EXPECT_CALL(gl, GenQueries(1, NotNull())) + .Times(AtLeast(elapsed_query ? 1 : 2)) .WillRepeatedly(Invoke(this, &GPUTimingFake::FakeGLGenQueries)); if (!elapsed_query) { @@ -86,7 +125,8 @@ void GPUTimingFake::ExpectGPUTimerQuery( .WillRepeatedly( Invoke(this, &GPUTimingFake::FakeGLGetQueryObjectui64v)); - EXPECT_CALL(gl, DeleteQueries(1, NotNull())).Times(AtLeast(2)) + EXPECT_CALL(gl, DeleteQueries(1, NotNull())) + .Times(AtLeast(elapsed_query ? 1 : 2)) .WillRepeatedly( Invoke(this, &GPUTimingFake::FakeGLDeleteQueries)); } diff --git a/ui/gl/gpu_timing_fake.h b/ui/gl/gpu_timing_fake.h index 40c93f4..82deea9 100644 --- a/ui/gl/gpu_timing_fake.h +++ b/ui/gl/gpu_timing_fake.h @@ -32,6 +32,7 @@ class GPUTimingFake { void ExpectNoDisjointCalls(MockGLInterface& gl); // GPUTimer fake queries which can only be called once per setup. + void ExpectGPUTimeStampQuery(MockGLInterface& gl, bool elapsed_query); void ExpectGPUTimerQuery(MockGLInterface& gl, bool elapsed_query); void ExpectOffsetCalculationQuery(MockGLInterface& gl); void ExpectNoOffsetCalculationQuery(MockGLInterface& gl); diff --git a/ui/gl/gpu_timing_unittest.cc b/ui/gl/gpu_timing_unittest.cc index 9044394..946d231 100644 --- a/ui/gl/gpu_timing_unittest.cc +++ b/ui/gl/gpu_timing_unittest.cc @@ -11,6 +11,7 @@ #include "ui/gl/gl_surface.h" #include "ui/gl/gpu_preference.h" #include "ui/gl/gpu_timing.h" +#include "ui/gl/gpu_timing_fake.h" namespace gfx { @@ -19,24 +20,33 @@ class GPUTimingTest : public testing::Test { void SetUp() override { setup_ = false; fake_cpu_time_ = 0; - - CreateGPUTimingClient()->SetCpuTimeForTesting(base::Bind(&GetFakeCPUTime)); + cpu_time_bounded_ = false; } void TearDown() override { + if (setup_) { + MockGLInterface::SetGLInterface(NULL); + gfx::ClearGLBindings(); + } + setup_ = false; + cpu_time_bounded_ = false; context_ = nullptr; + gl_.reset(); + gpu_timing_fake_queries_.Reset(); } void SetupGLContext(const char* gl_version, const char* gl_extensions) { ASSERT_FALSE(setup_) << "Cannot setup GL context twice."; - gfx::SetGLGetProcAddressProc(gfx::MockGLInterface::GetGLProcAddress); - gfx::GLSurface::InitializeOneOffWithMockBindingsForTests(); - gl_.reset(new ::testing::StrictMock< ::gfx::MockGLInterface>()); - ::gfx::MockGLInterface::SetGLInterface(gl_.get()); + SetGLGetProcAddressProc(MockGLInterface::GetGLProcAddress); + GLSurface::InitializeOneOffWithMockBindingsForTests(); + gl_.reset(new ::testing::StrictMock<MockGLInterface>()); + MockGLInterface::SetGLInterface(gl_.get()); - context_ = new gfx::GLContextStubWithExtensions; + context_ = new GLContextStubWithExtensions; context_->AddExtensionsString(gl_extensions); context_->SetGLVersionString(gl_version); + gpu_timing_fake_queries_.Reset(); + GLSurface::InitializeDynamicMockBindingsForTests(context_.get()); setup_ = true; } @@ -45,7 +55,13 @@ class GPUTimingTest : public testing::Test { if (!setup_) { SetupGLContext("2.0", ""); } - return context_->CreateGPUTimingClient(); + + scoped_refptr<GPUTimingClient> client = context_->CreateGPUTimingClient(); + if (!cpu_time_bounded_) { + client->SetCpuTimeForTesting(base::Bind(&GetFakeCPUTime)); + cpu_time_bounded_ = true; + } + return client; } void SetFakeCPUTime(int64_t fake_cpu_time) { @@ -57,12 +73,13 @@ class GPUTimingTest : public testing::Test { return fake_cpu_time_; } - private: static int64_t fake_cpu_time_; bool setup_ = false; - scoped_ptr< ::testing::StrictMock< ::gfx::MockGLInterface> > gl_; - scoped_refptr<gfx::GLContextStubWithExtensions> context_; + bool cpu_time_bounded_ = false; + scoped_ptr< ::testing::StrictMock<MockGLInterface> > gl_; + scoped_refptr<GLContextStubWithExtensions> context_; + GPUTimingFake gpu_timing_fake_queries_; }; int64_t GPUTimingTest::fake_cpu_time_ = 0; @@ -95,4 +112,65 @@ TEST_F(GPUTimingTest, ForceTimeElapsedQuery) { EXPECT_TRUE(client2->IsForceTimeElapsedQuery()); } +TEST_F(GPUTimingTest, QueryTimeStampTest) { + SetupGLContext("3.2", "GL_ARB_timer_query"); + scoped_refptr<GPUTimingClient> client = CreateGPUTimingClient(); + scoped_ptr<GPUTimer> gpu_timer = client->CreateGPUTimer(false); + + SetFakeCPUTime(123); + gpu_timing_fake_queries_.SetCurrentGLTime( + 10 * base::Time::kNanosecondsPerMicrosecond); + gpu_timing_fake_queries_.ExpectGPUTimeStampQuery(*gl_, false); + + gpu_timer->QueryTimeStamp(); + + SetFakeCPUTime(122); + gpu_timing_fake_queries_.SetCurrentGLTime( + 9 * base::Time::kNanosecondsPerMicrosecond); + EXPECT_FALSE(gpu_timer->IsAvailable()); + + SetFakeCPUTime(124); + gpu_timing_fake_queries_.SetCurrentGLTime( + 11 * base::Time::kNanosecondsPerMicrosecond); + EXPECT_TRUE(gpu_timer->IsAvailable()); + EXPECT_EQ(0, gpu_timer->GetDeltaElapsed()); + + int64 start, end; + gpu_timer->GetStartEndTimestamps(&start, &end); + EXPECT_EQ(123, start); + EXPECT_EQ(123, end); +} + +TEST_F(GPUTimingTest, QueryTimeStampUsingElapsedTest) { + // Test timestamp queries using GL_EXT_timer_query which does not support + // timestamp queries. Internally we fall back to time elapsed queries. + SetupGLContext("3.2", "GL_EXT_timer_query"); + scoped_refptr<GPUTimingClient> client = CreateGPUTimingClient(); + scoped_ptr<GPUTimer> gpu_timer = client->CreateGPUTimer(false); + ASSERT_TRUE(client->IsForceTimeElapsedQuery()); + + SetFakeCPUTime(123); + gpu_timing_fake_queries_.SetCurrentGLTime( + 10 * base::Time::kNanosecondsPerMicrosecond); + gpu_timing_fake_queries_.ExpectGPUTimeStampQuery(*gl_, true); + + gpu_timer->QueryTimeStamp(); + + SetFakeCPUTime(122); + gpu_timing_fake_queries_.SetCurrentGLTime( + 9 * base::Time::kNanosecondsPerMicrosecond); + EXPECT_FALSE(gpu_timer->IsAvailable()); + + SetFakeCPUTime(124); + gpu_timing_fake_queries_.SetCurrentGLTime( + 11 * base::Time::kNanosecondsPerMicrosecond); + EXPECT_TRUE(gpu_timer->IsAvailable()); + EXPECT_EQ(0, gpu_timer->GetDeltaElapsed()); + + int64 start, end; + gpu_timer->GetStartEndTimestamps(&start, &end); + EXPECT_EQ(123, start); + EXPECT_EQ(123, end); +} + } // namespace gpu |