summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordyen <dyen@chromium.org>2015-07-16 15:59:30 -0700
committerCommit bot <commit-bot@chromium.org>2015-07-16 23:00:45 +0000
commitda06bae00bee789d12cd6f4418018d048b04cfd2 (patch)
tree79826f0d5e5e3aa6188b8054bceacfce03f2a37c
parent65410813e3997a19f99d35f059e8d8f8b6986d35 (diff)
downloadchromium_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}
-rw-r--r--gpu/GLES2/gl2chromium_autogen.h1
-rwxr-xr-xgpu/command_buffer/build_gles2_cmd_buffer.py8
-rw-r--r--gpu/command_buffer/client/gles2_c_lib_autogen.h7
-rw-r--r--gpu/command_buffer/client/gles2_cmd_helper_autogen.h11
-rw-r--r--gpu/command_buffer/client/gles2_implementation.cc42
-rw-r--r--gpu/command_buffer/client/gles2_implementation_autogen.h2
-rw-r--r--gpu/command_buffer/client/gles2_implementation_unittest.cc107
-rw-r--r--gpu/command_buffer/client/gles2_implementation_unittest_autogen.h1
-rw-r--r--gpu/command_buffer/client/gles2_interface_autogen.h1
-rw-r--r--gpu/command_buffer/client/gles2_interface_stub_autogen.h1
-rw-r--r--gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h2
-rw-r--r--gpu/command_buffer/client/gles2_trace_implementation_autogen.h1
-rw-r--r--gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h5
-rw-r--r--gpu/command_buffer/client/query_tracker.cc30
-rw-r--r--gpu/command_buffer/client/query_tracker.h2
-rw-r--r--gpu/command_buffer/client/query_tracker_unittest.cc1
-rw-r--r--gpu/command_buffer/cmd_buffer_functions.txt1
-rw-r--r--gpu/command_buffer/common/gles2_cmd_format_autogen.h59
-rw-r--r--gpu/command_buffer/common/gles2_cmd_format_test_autogen.h16
-rw-r--r--gpu/command_buffer/common/gles2_cmd_ids_autogen.h157
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder.cc47
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc331
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_unittest_3_autogen.h2
-rw-r--r--gpu/command_buffer/service/query_manager.cc124
-rw-r--r--gpu/command_buffer/service/query_manager.h7
-rw-r--r--gpu/command_buffer/service/query_manager_unittest.cc52
-rw-r--r--mojo/gpu/mojo_gles2_impl_autogen.cc3
-rw-r--r--mojo/gpu/mojo_gles2_impl_autogen.h1
-rw-r--r--ui/gl/gpu_timing.cc78
-rw-r--r--ui/gl/gpu_timing.h17
-rw-r--r--ui/gl/gpu_timing_fake.cc44
-rw-r--r--ui/gl/gpu_timing_fake.h1
-rw-r--r--ui/gl/gpu_timing_unittest.cc100
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