Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "gpu/command_buffer/service/gpu_tracer.h" | 5 #include "gpu/command_buffer/service/gpu_tracer.h" |
| 6 | 6 |
| 7 #include <deque> | 7 #include <deque> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/debug/trace_event.h" | 10 #include "base/debug/trace_event.h" |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 67 virtual bool IsAvailable() OVERRIDE { return true; } | 67 virtual bool IsAvailable() OVERRIDE { return true; } |
| 68 virtual bool IsProcessable() OVERRIDE { return false; } | 68 virtual bool IsProcessable() OVERRIDE { return false; } |
| 69 virtual void Process() OVERRIDE {} | 69 virtual void Process() OVERRIDE {} |
| 70 | 70 |
| 71 private: | 71 private: |
| 72 virtual ~NoopTrace() {} | 72 virtual ~NoopTrace() {} |
| 73 | 73 |
| 74 DISALLOW_COPY_AND_ASSIGN(NoopTrace); | 74 DISALLOW_COPY_AND_ASSIGN(NoopTrace); |
| 75 }; | 75 }; |
| 76 | 76 |
| 77 struct TraceMarker { | 77 GPUTracer::GPUTracer(gles2::GLES2Decoder* decoder) |
| 78 TraceMarker(const std::string& name, GpuTracerSource source) | 78 : gpu_trace_srv_category(TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED( |
| 79 : name_(name), source_(source) {} | 79 TRACE_DISABLED_BY_DEFAULT("gpu.service"))), |
| 80 | 80 gpu_trace_dev_category(TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED( |
| 81 std::string name_; | 81 TRACE_DISABLED_BY_DEFAULT("gpu.device"))), |
| 82 GpuTracerSource source_; | 82 decoder_(decoder), |
| 83 scoped_refptr<Trace> trace_; | 83 timer_offset_(0), |
| 84 }; | 84 last_tracer_source_(kTraceGroupInvalid), |
| 85 | 85 enabled_(false), |
| 86 class GPUTracerImpl | 86 gpu_timing_synced_(false), |
| 87 : public GPUTracer, | 87 gpu_executing_(false), |
| 88 public base::SupportsWeakPtr<GPUTracerImpl> { | 88 process_posted_(false) { |
| 89 public: | 89 if (gfx::g_driver_gl.ext.b_GL_ARB_timer_query) { |
| 90 GPUTracerImpl() | 90 outputter_ = TraceOutputter::Create("GL_ARB_timer_query"); |
| 91 : gpu_trace_srv_category(TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED( | 91 enabled_ = true; |
| 92 TRACE_DISABLED_BY_DEFAULT("gpu.service"))), | |
| 93 gpu_trace_dev_category(TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED( | |
| 94 TRACE_DISABLED_BY_DEFAULT("gpu.device"))), | |
| 95 gpu_executing_(false), | |
| 96 process_posted_(false) {} | |
| 97 virtual ~GPUTracerImpl() {} | |
| 98 | |
| 99 // Implementation of gpu::gles2::GPUTracer | |
| 100 virtual bool BeginDecoding() OVERRIDE; | |
| 101 virtual bool EndDecoding() OVERRIDE; | |
| 102 virtual bool Begin(const std::string& name, GpuTracerSource source) OVERRIDE; | |
| 103 virtual bool End(GpuTracerSource source) OVERRIDE; | |
| 104 virtual const std::string& CurrentName() const OVERRIDE; | |
| 105 virtual bool IsTracing() OVERRIDE { | |
| 106 return (*gpu_trace_srv_category != 0) || (*gpu_trace_dev_category != 0); | |
| 107 } | 92 } |
| 108 virtual void CalculateTimerOffset() {} | |
| 109 | |
| 110 // Process any completed traces. | |
| 111 virtual void Process(); | |
| 112 virtual void ProcessTraces(); | |
| 113 | |
| 114 protected: | |
| 115 // Create a new trace. | |
| 116 virtual scoped_refptr<Trace> CreateTrace(const std::string& name); | |
| 117 | |
| 118 const unsigned char* gpu_trace_srv_category; | |
| 119 const unsigned char* gpu_trace_dev_category; | |
| 120 | |
| 121 protected: | |
| 122 void IssueProcessTask(); | |
| 123 | |
| 124 std::vector<TraceMarker> markers_; | |
| 125 std::deque<scoped_refptr<Trace> > traces_; | |
| 126 | |
| 127 bool gpu_executing_; | |
| 128 bool process_posted_; | |
| 129 | |
| 130 DISALLOW_COPY_AND_ASSIGN(GPUTracerImpl); | |
| 131 }; | |
| 132 | |
| 133 class GPUTracerARBTimerQuery : public GPUTracerImpl { | |
| 134 public: | |
| 135 explicit GPUTracerARBTimerQuery(gles2::GLES2Decoder* decoder); | |
| 136 virtual ~GPUTracerARBTimerQuery(); | |
| 137 | |
| 138 // Implementation of GPUTracerImpl | |
| 139 virtual void ProcessTraces() OVERRIDE; | |
| 140 | |
| 141 protected: | |
| 142 // Implementation of GPUTracerImpl. | |
| 143 virtual bool BeginDecoding() OVERRIDE; | |
| 144 virtual bool EndDecoding() OVERRIDE; | |
| 145 virtual scoped_refptr<Trace> CreateTrace(const std::string& name) OVERRIDE; | |
| 146 virtual void CalculateTimerOffset() OVERRIDE; | |
| 147 | |
| 148 scoped_refptr<Outputter> outputter_; | |
| 149 | |
| 150 bool gpu_timing_synced_; | |
| 151 int64 timer_offset_; | |
| 152 | |
| 153 gles2::GLES2Decoder* decoder_; | |
| 154 | |
| 155 DISALLOW_COPY_AND_ASSIGN(GPUTracerARBTimerQuery); | |
| 156 }; | |
| 157 | |
| 158 bool Trace::IsProcessable() { return true; } | |
| 159 | |
| 160 const std::string& Trace::name() { return name_; } | |
| 161 | |
| 162 GLARBTimerTrace::GLARBTimerTrace(scoped_refptr<Outputter> outputter, | |
| 163 const std::string& name, | |
| 164 int64 offset) | |
| 165 : Trace(name), | |
| 166 outputter_(outputter), | |
| 167 offset_(offset), | |
| 168 start_time_(0), | |
| 169 end_time_(0), | |
| 170 end_requested_(false) { | |
| 171 glGenQueries(2, queries_); | |
| 172 } | 93 } |
| 173 | 94 |
| 174 GLARBTimerTrace::~GLARBTimerTrace() { glDeleteQueries(2, queries_); } | 95 GPUTracer::~GPUTracer() { |
| 175 | |
| 176 void GLARBTimerTrace::Start() { | |
| 177 TRACE_EVENT_COPY_ASYNC_BEGIN0( | |
| 178 TRACE_DISABLED_BY_DEFAULT("gpu.service"), name().c_str(), this); | |
| 179 glQueryCounter(queries_[0], GL_TIMESTAMP); | |
| 180 } | 96 } |
| 181 | 97 |
| 182 void GLARBTimerTrace::End() { | 98 bool GPUTracer::BeginDecoding() { |
| 183 glQueryCounter(queries_[1], GL_TIMESTAMP); | 99 if (enabled_) { |
| 184 end_requested_ = true; | 100 if (*gpu_trace_dev_category) { |
| 185 TRACE_EVENT_COPY_ASYNC_END0( | 101 // Make sure timing is synced before tracing |
| 186 TRACE_DISABLED_BY_DEFAULT("gpu.service"), name().c_str(), this); | 102 if (!gpu_timing_synced_) { |
| 187 } | 103 CalculateTimerOffset(); |
| 104 gpu_timing_synced_ = true; | |
| 105 } | |
| 106 } else { | |
| 107 // If GPU device category is off, invalidate timing sync | |
| 108 gpu_timing_synced_ = false; | |
| 109 } | |
| 110 } | |
| 188 | 111 |
| 189 bool GLARBTimerTrace::IsAvailable() { | |
| 190 if (!end_requested_) | |
| 191 return false; | |
| 192 | |
| 193 GLint done = 0; | |
| 194 glGetQueryObjectiv(queries_[1], GL_QUERY_RESULT_AVAILABLE, &done); | |
| 195 return !!done; | |
| 196 } | |
| 197 | |
| 198 void GLARBTimerTrace::Process() { | |
| 199 DCHECK(IsAvailable()); | |
| 200 | |
| 201 GLuint64 timestamp; | |
| 202 | |
| 203 // TODO(dsinclair): It's possible for the timer to wrap during the start/end. | |
| 204 // We need to detect if the end is less then the start and correct for the | |
| 205 // wrapping. | |
| 206 glGetQueryObjectui64v(queries_[0], GL_QUERY_RESULT, ×tamp); | |
| 207 start_time_ = (timestamp / base::Time::kNanosecondsPerMicrosecond) + offset_; | |
| 208 | |
| 209 glGetQueryObjectui64v(queries_[1], GL_QUERY_RESULT, ×tamp); | |
| 210 end_time_ = (timestamp / base::Time::kNanosecondsPerMicrosecond) + offset_; | |
| 211 | |
| 212 glDeleteQueries(2, queries_); | |
| 213 outputter_->Trace(name(), start_time_, end_time_); | |
| 214 } | |
| 215 | |
| 216 bool GPUTracerImpl::BeginDecoding() { | |
| 217 if (gpu_executing_) | 112 if (gpu_executing_) |
| 218 return false; | 113 return false; |
| 219 | 114 |
| 220 gpu_executing_ = true; | 115 gpu_executing_ = true; |
| 221 | 116 |
| 222 if (IsTracing()) { | 117 if (IsTracing()) { |
| 223 // Begin a Trace for all active markers | 118 // Begin a Trace for all active markers |
| 224 for (size_t i = 0; i < markers_.size(); i++) { | 119 for (int n = 0; n < NUM_TRACER_SOURCES; n++) { |
| 225 markers_[i].trace_ = CreateTrace(markers_[i].name_); | 120 for (size_t i = 0; i < markers_[n].size(); i++) { |
| 226 markers_[i].trace_->Start(); | 121 markers_[n][i].trace_ = CreateTrace(markers_[n][i].name_); |
| 122 markers_[n][i].trace_->Start(); | |
| 123 } | |
| 227 } | 124 } |
| 228 } | 125 } |
| 229 return true; | 126 return true; |
| 230 } | 127 } |
| 231 | 128 |
| 232 bool GPUTracerImpl::EndDecoding() { | 129 bool GPUTracer::EndDecoding() { |
| 233 if (!gpu_executing_) | 130 if (!gpu_executing_) |
| 234 return false; | 131 return false; |
| 235 | 132 |
| 236 // End Trace for all active markers | 133 // End Trace for all active markers |
| 237 if (IsTracing()) { | 134 if (IsTracing()) { |
| 238 for (size_t i = 0; i < markers_.size(); i++) { | 135 for (int n = 0; n < NUM_TRACER_SOURCES; n++) { |
| 239 if (markers_[i].trace_) { | 136 for (size_t i = 0; i < markers_[n].size(); i++) { |
| 240 markers_[i].trace_->End(); | 137 if (markers_[n][i].trace_) { |
| 241 if (markers_[i].trace_->IsProcessable()) | 138 markers_[n][i].trace_->End(); |
| 242 traces_.push_back(markers_[i].trace_); | 139 if (markers_[n][i].trace_->IsProcessable()) |
| 243 markers_[i].trace_ = 0; | 140 traces_.push_back(markers_[n][i].trace_); |
| 141 markers_[n][i].trace_ = 0; | |
| 142 } | |
| 244 } | 143 } |
| 245 } | 144 } |
| 246 IssueProcessTask(); | 145 IssueProcessTask(); |
| 247 } | 146 } |
| 248 | 147 |
| 249 gpu_executing_ = false; | 148 gpu_executing_ = false; |
| 149 | |
| 150 // NOTE(vmiura_: glFlush() here can help give better trace results, | |
|
vmiura
2014/07/29 23:45:30
nit: NOTE(vmiura)
David Yen
2014/08/04 22:20:05
Done.
| |
| 151 // but it distorts the normal device behavior. | |
| 250 return true; | 152 return true; |
| 251 } | 153 } |
| 252 | 154 |
| 253 bool GPUTracerImpl::Begin(const std::string& name, GpuTracerSource source) { | 155 bool GPUTracer::Begin(const std::string& name, GpuTracerSource source) { |
| 254 if (!gpu_executing_) | 156 if (!gpu_executing_) |
| 255 return false; | 157 return false; |
| 256 | 158 |
| 159 assert(source >= 0 && source < NUM_TRACER_SOURCES); | |
|
vmiura
2014/07/29 23:45:30
Please use DCHECK instead of assert here and below
David Yen
2014/08/04 22:20:05
Done.
| |
| 160 | |
| 257 // Push new marker from given 'source' | 161 // Push new marker from given 'source' |
| 258 markers_.push_back(TraceMarker(name, source)); | 162 last_tracer_source_ = source; |
| 163 markers_[source].push_back(TraceMarker(name)); | |
| 259 | 164 |
| 260 // Create trace | 165 // Create trace |
| 261 if (IsTracing()) { | 166 if (IsTracing()) { |
| 262 scoped_refptr<Trace> trace = CreateTrace(name); | 167 scoped_refptr<Trace> trace = CreateTrace(name); |
| 263 trace->Start(); | 168 trace->Start(); |
| 264 markers_.back().trace_ = trace; | 169 markers_[source].back().trace_ = trace; |
| 265 } | 170 } |
| 171 | |
| 266 return true; | 172 return true; |
| 267 } | 173 } |
| 268 | 174 |
| 269 bool GPUTracerImpl::End(GpuTracerSource source) { | 175 bool GPUTracer::End(GpuTracerSource source) { |
| 270 if (!gpu_executing_) | 176 if (!gpu_executing_) |
| 271 return false; | 177 return false; |
| 272 | 178 |
| 179 assert(source >= 0 && source < NUM_TRACER_SOURCES); | |
| 180 | |
| 273 // Pop last marker with matching 'source' | 181 // Pop last marker with matching 'source' |
| 274 for (int i = markers_.size() - 1; i >= 0; i--) { | 182 if (!markers_[source].empty()) { |
| 275 if (markers_[i].source_ == source) { | 183 if (IsTracing()) { |
| 276 // End trace | 184 scoped_refptr<Trace> trace = markers_[source].back().trace_; |
| 277 if (IsTracing()) { | 185 if (trace) { |
| 278 scoped_refptr<Trace> trace = markers_[i].trace_; | 186 trace->End(); |
| 279 if (trace) { | 187 if (trace->IsProcessable()) |
| 280 trace->End(); | 188 traces_.push_back(trace); |
| 281 if (trace->IsProcessable()) | 189 IssueProcessTask(); |
| 282 traces_.push_back(trace); | |
| 283 IssueProcessTask(); | |
| 284 } | |
| 285 } | 190 } |
| 191 } | |
| 286 | 192 |
| 287 markers_.erase(markers_.begin() + i); | 193 markers_[source].pop_back(); |
| 288 return true; | 194 return true; |
| 289 } | |
| 290 } | 195 } |
| 291 return false; | 196 return false; |
| 292 } | 197 } |
| 293 | 198 |
| 294 void GPUTracerImpl::Process() { | 199 bool GPUTracer::IsTracing() { |
| 200 return (*gpu_trace_srv_category != 0) || (*gpu_trace_dev_category != 0); | |
| 201 } | |
| 202 | |
| 203 const std::string& GPUTracer::CurrentName() const { | |
| 204 if (last_tracer_source_ >= 0 && | |
| 205 last_tracer_source_ < NUM_TRACER_SOURCES && | |
| 206 !markers_[last_tracer_source_].empty()) { | |
| 207 return markers_[last_tracer_source_].back().name_; | |
| 208 } | |
| 209 return base::EmptyString(); | |
| 210 } | |
| 211 | |
| 212 void GPUTracer::CalculateTimerOffset() { | |
| 213 if (enabled_) { | |
| 214 TRACE_EVENT0("gpu", "GPUTracer::CalculateTimerOffset"); | |
| 215 | |
| 216 // NOTE(vmiura): It would be better to use glGetInteger64v, however | |
| 217 // it's not available everywhere. | |
| 218 GLuint64 gl_now = 0; | |
| 219 GLuint query; | |
| 220 glFinish(); | |
| 221 glGenQueries(1, &query); | |
| 222 glQueryCounter(query, GL_TIMESTAMP); | |
| 223 glFinish(); | |
| 224 glGetQueryObjectui64v(query, GL_QUERY_RESULT, &gl_now); | |
| 225 base::TimeTicks system_now = base::TimeTicks::NowFromSystemTraceTime(); | |
| 226 | |
| 227 gl_now /= base::Time::kNanosecondsPerMicrosecond; | |
| 228 timer_offset_ = system_now.ToInternalValue() - gl_now; | |
| 229 glDeleteQueries(1, &query); | |
| 230 } | |
| 231 } | |
| 232 | |
| 233 scoped_refptr<Trace> GPUTracer::CreateTrace(const std::string& name) { | |
| 234 if (enabled_) { | |
| 235 if (*gpu_trace_dev_category) | |
| 236 return new GLARBTimerTrace(outputter_, name, timer_offset_); | |
| 237 } | |
| 238 return new NoopTrace(name); | |
| 239 } | |
| 240 | |
| 241 void GPUTracer::Process() { | |
| 295 process_posted_ = false; | 242 process_posted_ = false; |
| 296 ProcessTraces(); | 243 ProcessTraces(); |
| 297 IssueProcessTask(); | 244 IssueProcessTask(); |
| 298 } | 245 } |
| 299 | 246 |
| 300 void GPUTracerImpl::ProcessTraces() { | 247 void GPUTracer::ProcessTraces() { |
| 301 while (!traces_.empty() && traces_.front()->IsAvailable()) { | 248 if (!enabled_) { |
| 302 traces_.front()->Process(); | 249 while (!traces_.empty() && traces_.front()->IsAvailable()) { |
| 303 traces_.pop_front(); | 250 traces_.front()->Process(); |
| 304 } | 251 traces_.pop_front(); |
| 305 } | 252 } |
| 306 | |
| 307 const std::string& GPUTracerImpl::CurrentName() const { | |
| 308 if (markers_.empty()) | |
| 309 return base::EmptyString(); | |
| 310 return markers_.back().name_; | |
| 311 } | |
| 312 | |
| 313 scoped_refptr<Trace> GPUTracerImpl::CreateTrace(const std::string& name) { | |
| 314 return new NoopTrace(name); | |
| 315 } | |
| 316 | |
| 317 void GPUTracerImpl::IssueProcessTask() { | |
| 318 if (traces_.empty() || process_posted_) | |
| 319 return; | 253 return; |
| 320 | |
| 321 process_posted_ = true; | |
| 322 base::MessageLoop::current()->PostDelayedTask( | |
| 323 FROM_HERE, | |
| 324 base::Bind(&GPUTracerImpl::Process, base::AsWeakPtr(this)), | |
| 325 base::TimeDelta::FromMilliseconds(kProcessInterval)); | |
| 326 } | |
| 327 | |
| 328 GPUTracerARBTimerQuery::GPUTracerARBTimerQuery(gles2::GLES2Decoder* decoder) | |
| 329 : timer_offset_(0), decoder_(decoder) { | |
| 330 outputter_ = TraceOutputter::Create("GL_ARB_timer_query"); | |
| 331 } | |
| 332 | |
| 333 GPUTracerARBTimerQuery::~GPUTracerARBTimerQuery() { | |
| 334 } | |
| 335 | |
| 336 scoped_refptr<Trace> GPUTracerARBTimerQuery::CreateTrace( | |
| 337 const std::string& name) { | |
| 338 if (*gpu_trace_dev_category) | |
| 339 return new GLARBTimerTrace(outputter_, name, timer_offset_); | |
| 340 return GPUTracerImpl::CreateTrace(name); | |
| 341 } | |
| 342 | |
| 343 bool GPUTracerARBTimerQuery::BeginDecoding() { | |
| 344 if (*gpu_trace_dev_category) { | |
| 345 // Make sure timing is synced before tracing | |
| 346 if (!gpu_timing_synced_) { | |
| 347 CalculateTimerOffset(); | |
| 348 gpu_timing_synced_ = true; | |
| 349 } | |
| 350 } else { | |
| 351 // If GPU device category is off, invalidate timing sync | |
| 352 gpu_timing_synced_ = false; | |
| 353 } | 254 } |
| 354 | 255 |
| 355 return GPUTracerImpl::BeginDecoding(); | 256 TRACE_EVENT0("gpu", "GPUTracer::ProcessTraces"); |
| 356 } | |
| 357 | |
| 358 bool GPUTracerARBTimerQuery::EndDecoding() { | |
| 359 bool ret = GPUTracerImpl::EndDecoding(); | |
| 360 | |
| 361 // NOTE(vmiura_: glFlush() here can help give better trace results, | |
| 362 // but it distorts the normal device behavior. | |
| 363 return ret; | |
| 364 } | |
| 365 | |
| 366 void GPUTracerARBTimerQuery::ProcessTraces() { | |
| 367 TRACE_EVENT0("gpu", "GPUTracerARBTimerQuery::ProcessTraces"); | |
| 368 | 257 |
| 369 // Make owning decoder's GL context current | 258 // Make owning decoder's GL context current |
| 370 if (!decoder_->MakeCurrent()) { | 259 if (!decoder_->MakeCurrent()) { |
| 371 // Skip subsequent GL calls if MakeCurrent fails | 260 // Skip subsequent GL calls if MakeCurrent fails |
| 372 traces_.clear(); | 261 traces_.clear(); |
| 373 return; | 262 return; |
| 374 } | 263 } |
| 375 | 264 |
| 376 while (!traces_.empty() && traces_.front()->IsAvailable()) { | 265 while (!traces_.empty() && traces_.front()->IsAvailable()) { |
| 377 traces_.front()->Process(); | 266 traces_.front()->Process(); |
| 378 traces_.pop_front(); | 267 traces_.pop_front(); |
| 379 } | 268 } |
| 380 | 269 |
| 381 // Clear pending traces if there were are any errors | 270 // Clear pending traces if there were are any errors |
| 382 GLenum err = glGetError(); | 271 GLenum err = glGetError(); |
| 383 if (err != GL_NO_ERROR) | 272 if (err != GL_NO_ERROR) |
| 384 traces_.clear(); | 273 traces_.clear(); |
| 385 } | 274 } |
| 386 | 275 |
| 387 void GPUTracerARBTimerQuery::CalculateTimerOffset() { | 276 void GPUTracer::IssueProcessTask() { |
| 388 TRACE_EVENT0("gpu", "GPUTracerARBTimerQuery::CalculateTimerOffset"); | 277 if (traces_.empty() || process_posted_) |
| 278 return; | |
| 389 | 279 |
| 390 // NOTE(vmiura): It would be better to use glGetInteger64v, however | 280 process_posted_ = true; |
| 391 // it's not available everywhere. | 281 base::MessageLoop::current()->PostDelayedTask( |
| 392 GLuint64 gl_now = 0; | 282 FROM_HERE, |
| 393 GLuint query; | 283 base::Bind(&GPUTracer::Process, base::AsWeakPtr(this)), |
| 394 glFinish(); | 284 base::TimeDelta::FromMilliseconds(kProcessInterval)); |
| 395 glGenQueries(1, &query); | |
| 396 glQueryCounter(query, GL_TIMESTAMP); | |
| 397 glFinish(); | |
| 398 glGetQueryObjectui64v(query, GL_QUERY_RESULT, &gl_now); | |
| 399 base::TimeTicks system_now = base::TimeTicks::NowFromSystemTraceTime(); | |
| 400 | |
| 401 gl_now /= base::Time::kNanosecondsPerMicrosecond; | |
| 402 timer_offset_ = system_now.ToInternalValue() - gl_now; | |
| 403 glDeleteQueries(1, &query); | |
| 404 } | 285 } |
| 405 | 286 |
| 406 GPUTracer::GPUTracer() {} | 287 bool Trace::IsProcessable() { return true; } |
| 407 | 288 |
| 408 GPUTracer::~GPUTracer() {} | 289 const std::string& Trace::name() { return name_; } |
| 409 | 290 |
| 410 scoped_ptr<GPUTracer> GPUTracer::Create(gles2::GLES2Decoder* decoder) { | 291 GLARBTimerTrace::GLARBTimerTrace(scoped_refptr<Outputter> outputter, |
| 411 if (gfx::g_driver_gl.ext.b_GL_ARB_timer_query) { | 292 const std::string& name, |
| 412 return scoped_ptr<GPUTracer>(new GPUTracerARBTimerQuery(decoder)); | 293 int64 offset) |
| 413 } | 294 : Trace(name), |
| 414 return scoped_ptr<GPUTracer>(new GPUTracerImpl()); | 295 outputter_(outputter), |
| 296 offset_(offset), | |
| 297 start_time_(0), | |
| 298 end_time_(0), | |
| 299 end_requested_(false) { | |
| 300 glGenQueries(2, queries_); | |
| 301 } | |
| 302 | |
| 303 GLARBTimerTrace::~GLARBTimerTrace() { glDeleteQueries(2, queries_); } | |
| 304 | |
| 305 void GLARBTimerTrace::Start() { | |
| 306 TRACE_EVENT_COPY_ASYNC_BEGIN0( | |
| 307 TRACE_DISABLED_BY_DEFAULT("gpu.service"), name().c_str(), this); | |
| 308 glQueryCounter(queries_[0], GL_TIMESTAMP); | |
| 309 } | |
| 310 | |
| 311 void GLARBTimerTrace::End() { | |
| 312 glQueryCounter(queries_[1], GL_TIMESTAMP); | |
| 313 end_requested_ = true; | |
| 314 TRACE_EVENT_COPY_ASYNC_END0( | |
| 315 TRACE_DISABLED_BY_DEFAULT("gpu.service"), name().c_str(), this); | |
| 316 } | |
| 317 | |
| 318 bool GLARBTimerTrace::IsAvailable() { | |
| 319 if (!end_requested_) | |
| 320 return false; | |
| 321 | |
| 322 GLint done = 0; | |
| 323 glGetQueryObjectiv(queries_[1], GL_QUERY_RESULT_AVAILABLE, &done); | |
| 324 return !!done; | |
| 325 } | |
| 326 | |
| 327 void GLARBTimerTrace::Process() { | |
| 328 DCHECK(IsAvailable()); | |
| 329 | |
| 330 GLuint64 timestamp; | |
| 331 | |
| 332 // TODO(dsinclair): It's possible for the timer to wrap during the start/end. | |
| 333 // We need to detect if the end is less then the start and correct for the | |
| 334 // wrapping. | |
| 335 glGetQueryObjectui64v(queries_[0], GL_QUERY_RESULT, ×tamp); | |
| 336 start_time_ = (timestamp / base::Time::kNanosecondsPerMicrosecond) + offset_; | |
| 337 | |
| 338 glGetQueryObjectui64v(queries_[1], GL_QUERY_RESULT, ×tamp); | |
| 339 end_time_ = (timestamp / base::Time::kNanosecondsPerMicrosecond) + offset_; | |
| 340 | |
| 341 glDeleteQueries(2, queries_); | |
| 342 outputter_->Trace(name(), start_time_, end_time_); | |
| 415 } | 343 } |
| 416 | 344 |
| 417 } // namespace gles2 | 345 } // namespace gles2 |
| 418 } // namespace gpu | 346 } // namespace gpu |
| OLD | NEW |