1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "ui/gl/gpu_timing_fake.h"
6 
7 #include "base/time/time.h"
8 #include "testing/gtest/include/gtest/gtest.h"
9 #include "ui/gl/gl_mock.h"
10 
11 namespace gl {
12 
13 using ::testing::_;
14 using ::testing::AtLeast;
15 using ::testing::AtMost;
16 using ::testing::Exactly;
17 using ::testing::Invoke;
18 using ::testing::NotNull;
19 using ::testing::DoAll;
20 using ::testing::Return;
21 using ::testing::SetArgPointee;
22 
23 int64_t GPUTimingFake::fake_cpu_time_ = 0;
24 
GPUTimingFake()25 GPUTimingFake::GPUTimingFake() {
26   Reset();
27 }
28 
~GPUTimingFake()29 GPUTimingFake::~GPUTimingFake() {
30 }
31 
Reset()32 void GPUTimingFake::Reset() {
33   current_gl_time_ = 0;
34   gl_cpu_time_offset_ = 0;
35   next_query_id_ = 23;
36   allocated_queries_.clear();
37   query_results_.clear();
38   current_elapsed_query_.Reset();
39 
40   fake_cpu_time_ = 0;
41 }
42 
GetFakeCPUTime()43 int64_t GPUTimingFake::GetFakeCPUTime() {
44   return fake_cpu_time_;
45 }
46 
SetCPUGLOffset(int64_t offset)47 void GPUTimingFake::SetCPUGLOffset(int64_t offset) {
48   gl_cpu_time_offset_ = offset;
49 }
50 
SetCurrentCPUTime(int64_t current_time)51 void GPUTimingFake::SetCurrentCPUTime(int64_t current_time) {
52   fake_cpu_time_ = current_time;
53   current_gl_time_ = (fake_cpu_time_ + gl_cpu_time_offset_) *
54                      base::Time::kNanosecondsPerMicrosecond;
55 }
56 
SetCurrentGLTime(GLint64 current_time)57 void GPUTimingFake::SetCurrentGLTime(GLint64 current_time) {
58   current_gl_time_ = current_time;
59   fake_cpu_time_ = (current_gl_time_ / base::Time::kNanosecondsPerMicrosecond) -
60                    gl_cpu_time_offset_;
61 }
62 
SetDisjoint()63 void GPUTimingFake::SetDisjoint() {
64   disjointed_ = true;
65 }
66 
ExpectGetErrorCalls(MockGLInterface & gl)67 void GPUTimingFake::ExpectGetErrorCalls(MockGLInterface& gl) {
68   EXPECT_CALL(gl, GetError()).Times(AtLeast(0))
69       .WillRepeatedly(Invoke(this, &GPUTimingFake::FakeGLGetError));
70 }
71 
ExpectDisjointCalls(MockGLInterface & gl)72 void GPUTimingFake::ExpectDisjointCalls(MockGLInterface& gl) {
73   EXPECT_CALL(gl, GetIntegerv(GL_GPU_DISJOINT_EXT, _)).Times(AtLeast(1))
74       .WillRepeatedly(Invoke(this, &GPUTimingFake::FakeGLGetIntegerv));
75 }
76 
ExpectNoDisjointCalls(MockGLInterface & gl)77 void GPUTimingFake::ExpectNoDisjointCalls(MockGLInterface& gl) {
78   EXPECT_CALL(gl, GetIntegerv(GL_GPU_DISJOINT_EXT, _)).Times(Exactly(0));
79 }
80 
ExpectGPUTimeStampQuery(MockGLInterface & gl,bool elapsed_query)81 void GPUTimingFake::ExpectGPUTimeStampQuery(MockGLInterface& gl,
82                                             bool elapsed_query) {
83   EXPECT_CALL(gl, GenQueries(1, NotNull()))
84       .WillOnce(Invoke(this, &GPUTimingFake::FakeGLGenQueries));
85 
86   EXPECT_CALL(gl, GetQueryiv(GL_TIMESTAMP, GL_QUERY_COUNTER_BITS, NotNull()))
87       .WillRepeatedly(DoAll(SetArgPointee<2>(64), Return()));
88   if (!elapsed_query) {
89     // Time Stamp based queries.
90     EXPECT_CALL(gl, GetInteger64v(GL_TIMESTAMP, _))
91         .WillRepeatedly(
92             Invoke(this, &GPUTimingFake::FakeGLGetInteger64v));
93 
94     EXPECT_CALL(gl, QueryCounter(_, GL_TIMESTAMP)).Times(Exactly(1))
95         .WillRepeatedly(
96              Invoke(this, &GPUTimingFake::FakeGLQueryCounter));
97   } else {
98     // Time Elapsed based queries.
99     EXPECT_CALL(gl, BeginQuery(GL_TIME_ELAPSED, _)).Times(Exactly(1))
100         .WillRepeatedly(
101             Invoke(this, &GPUTimingFake::FakeGLBeginQuery));
102 
103     EXPECT_CALL(gl, EndQuery(GL_TIME_ELAPSED)).Times(Exactly(1))
104       .WillRepeatedly(Invoke(this, &GPUTimingFake::FakeGLEndQuery));
105   }
106 
107   EXPECT_CALL(gl, GetQueryObjectuiv(_, GL_QUERY_RESULT_AVAILABLE,
108                                     NotNull()))
109       .WillRepeatedly(
110           Invoke(this, &GPUTimingFake::FakeGLGetQueryObjectuiv));
111 
112   EXPECT_CALL(gl, GetQueryObjectui64v(_, GL_QUERY_RESULT, NotNull()))
113       .WillRepeatedly(
114            Invoke(this, &GPUTimingFake::FakeGLGetQueryObjectui64v));
115 
116   EXPECT_CALL(gl, DeleteQueries(1, NotNull()))
117       .WillOnce(Invoke(this, &GPUTimingFake::FakeGLDeleteQueries))
118       .RetiresOnSaturation();
119 }
120 
ExpectGPUTimerQuery(MockGLInterface & gl,bool elapsed_query)121 void GPUTimingFake::ExpectGPUTimerQuery(
122     MockGLInterface& gl, bool elapsed_query) {
123   EXPECT_CALL(gl, GenQueries(1, NotNull()))
124       .Times(AtLeast(elapsed_query ? 1 : 2))
125       .WillRepeatedly(Invoke(this, &GPUTimingFake::FakeGLGenQueries));
126 
127   if (!elapsed_query) {
128     // Time Stamp based queries.
129     EXPECT_CALL(gl, GetQueryiv(GL_TIMESTAMP, GL_QUERY_COUNTER_BITS, NotNull()))
130         .WillRepeatedly(DoAll(SetArgPointee<2>(64), Return()));
131 
132     EXPECT_CALL(gl, GetInteger64v(GL_TIMESTAMP, _))
133         .WillRepeatedly(
134             Invoke(this, &GPUTimingFake::FakeGLGetInteger64v));
135 
136     EXPECT_CALL(gl, QueryCounter(_, GL_TIMESTAMP)).Times(AtLeast(1))
137         .WillRepeatedly(
138              Invoke(this, &GPUTimingFake::FakeGLQueryCounter));
139   }
140 
141   // Time Elapsed based queries.
142   EXPECT_CALL(gl, BeginQuery(GL_TIME_ELAPSED, _))
143       .WillRepeatedly(
144           Invoke(this, &GPUTimingFake::FakeGLBeginQuery));
145 
146   EXPECT_CALL(gl, EndQuery(GL_TIME_ELAPSED))
147       .WillRepeatedly(Invoke(this, &GPUTimingFake::FakeGLEndQuery));
148 
149   EXPECT_CALL(gl, GetQueryObjectuiv(_, GL_QUERY_RESULT_AVAILABLE,
150                                     NotNull()))
151       .WillRepeatedly(
152           Invoke(this, &GPUTimingFake::FakeGLGetQueryObjectuiv));
153 
154   EXPECT_CALL(gl, GetQueryObjectui64v(_, GL_QUERY_RESULT, NotNull()))
155       .WillRepeatedly(
156            Invoke(this, &GPUTimingFake::FakeGLGetQueryObjectui64v));
157 
158   EXPECT_CALL(gl, DeleteQueries(1, NotNull()))
159       .Times(AtLeast(elapsed_query ? 1 : 2))
160       .WillRepeatedly(
161            Invoke(this, &GPUTimingFake::FakeGLDeleteQueries));
162 }
163 
ExpectOffsetCalculationQuery(MockGLInterface & gl)164 void GPUTimingFake::ExpectOffsetCalculationQuery(
165     MockGLInterface& gl) {
166   EXPECT_CALL(gl, GetInteger64v(GL_TIMESTAMP, NotNull()))
167       .Times(AtMost(1))
168       .WillRepeatedly(
169           Invoke(this, &GPUTimingFake::FakeGLGetInteger64v));
170 }
171 
ExpectNoOffsetCalculationQuery(MockGLInterface & gl)172 void GPUTimingFake::ExpectNoOffsetCalculationQuery(
173     MockGLInterface& gl) {
174   EXPECT_CALL(gl, GetInteger64v(GL_TIMESTAMP, NotNull())).Times(Exactly(0));
175 }
176 
FakeGLGenQueries(GLsizei n,GLuint * ids)177 void GPUTimingFake::FakeGLGenQueries(GLsizei n, GLuint* ids) {
178   for (GLsizei i = 0; i < n; i++) {
179     ids[i] = next_query_id_++;
180     allocated_queries_.insert(ids[i]);
181   }
182 }
183 
FakeGLDeleteQueries(GLsizei n,const GLuint * ids)184 void GPUTimingFake::FakeGLDeleteQueries(GLsizei n, const GLuint* ids) {
185   for (GLsizei i = 0; i < n; i++) {
186     allocated_queries_.erase(ids[i]);
187     query_results_.erase(ids[i]);
188     if (current_elapsed_query_.query_id_ == ids[i])
189       current_elapsed_query_.Reset();
190   }
191 }
192 
FakeGLBeginQuery(GLenum target,GLuint id)193 void GPUTimingFake::FakeGLBeginQuery(GLenum target, GLuint id) {
194   switch(target) {
195     case GL_TIME_ELAPSED:
196       ASSERT_FALSE(current_elapsed_query_.active_);
197       current_elapsed_query_.Reset();
198       current_elapsed_query_.active_ = true;
199       current_elapsed_query_.query_id_ = id;
200       current_elapsed_query_.begin_time_ = current_gl_time_;
201       break;
202     default:
203       FAIL() << "Invalid target passed to BeginQuery: " << target;
204   }
205 }
206 
FakeGLEndQuery(GLenum target)207 void GPUTimingFake::FakeGLEndQuery(GLenum target) {
208   switch(target) {
209     case GL_TIME_ELAPSED: {
210       ASSERT_TRUE(current_elapsed_query_.active_);
211       QueryResult& query = query_results_[current_elapsed_query_.query_id_];
212       query.type_ = QueryResult::kQueryResultType_Elapsed;
213       query.begin_time_ = current_elapsed_query_.begin_time_;
214       query.value_ = current_gl_time_;
215       current_elapsed_query_.active_ = false;
216     } break;
217     default:
218       FAIL() << "Invalid target passed to BeginQuery: " << target;
219   }
220 }
221 
FakeGLGetQueryObjectuiv(GLuint id,GLenum pname,GLuint * params)222 void GPUTimingFake::FakeGLGetQueryObjectuiv(GLuint id, GLenum pname,
223                                             GLuint* params) {
224   switch (pname) {
225     case GL_QUERY_RESULT_AVAILABLE: {
226       auto it = query_results_.find(id);
227       if (it != query_results_.end() && it->second.value_ <= current_gl_time_)
228         *params = 1;
229       else
230         *params = 0;
231     } break;
232     default:
233       FAIL() << "Invalid variable passed to GetQueryObjectuiv: " << pname;
234   }
235 }
236 
FakeGLQueryCounter(GLuint id,GLenum target)237 void GPUTimingFake::FakeGLQueryCounter(GLuint id, GLenum target) {
238   switch (target) {
239     case GL_TIMESTAMP: {
240       ASSERT_TRUE(allocated_queries_.find(id) != allocated_queries_.end());
241       QueryResult& query = query_results_[id];
242       query.type_ = QueryResult::kQueryResultType_TimeStamp;
243       query.value_ = current_gl_time_;
244     } break;
245 
246     default:
247       FAIL() << "Invalid variable passed to QueryCounter: " << target;
248   }
249 }
250 
FakeGLGetInteger64v(GLenum pname,GLint64 * data)251 void GPUTimingFake::FakeGLGetInteger64v(GLenum pname, GLint64 * data) {
252   switch (pname) {
253     case GL_TIMESTAMP:
254       *data = current_gl_time_;
255       break;
256     default:
257       FAIL() << "Invalid variable passed to GetInteger64v: " << pname;
258   }
259 }
260 
FakeGLGetQueryObjectui64v(GLuint id,GLenum pname,GLuint64 * params)261 void GPUTimingFake::FakeGLGetQueryObjectui64v(GLuint id, GLenum pname,
262                                               GLuint64* params) {
263   switch (pname) {
264     case GL_QUERY_RESULT: {
265       auto it = query_results_.find(id);
266       ASSERT_TRUE(it != query_results_.end());
267       switch (it->second.type_) {
268         case QueryResult::kQueryResultType_TimeStamp:
269           *params = it->second.value_;
270           break;
271         case QueryResult::kQueryResultType_Elapsed:
272           *params = it->second.value_ - it->second.begin_time_;
273           break;
274         default:
275           FAIL() << "Invalid Query Result Type: " << it->second.type_;
276       }
277     } break;
278     default:
279       FAIL() << "Invalid variable passed to GetQueryObjectui64v: " << pname;
280   }
281 }
282 
FakeGLGetIntegerv(GLenum pname,GLint * params)283 void GPUTimingFake::FakeGLGetIntegerv(GLenum pname, GLint* params) {
284   switch (pname) {
285     case GL_GPU_DISJOINT_EXT:
286       *params = static_cast<GLint>(disjointed_);
287       disjointed_ = false;
288       break;
289     default:
290       FAIL() << "Invalid variable passed to GetIntegerv: " << pname;
291   }
292 }
293 
FakeGLGetError()294 GLenum GPUTimingFake::FakeGLGetError() {
295   return GL_NO_ERROR;
296 }
297 
298 }  // namespace gl
299