OLD | NEW |
| (Empty) |
1 // Copyright 2013 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 "gpu/command_buffer/service/async_pixel_transfer_manager_egl.h" | |
6 | |
7 #include <list> | |
8 #include <string> | |
9 | |
10 #include "base/bind.h" | |
11 #include "base/lazy_instance.h" | |
12 #include "base/location.h" | |
13 #include "base/logging.h" | |
14 #include "base/memory/ref_counted.h" | |
15 #include "base/single_thread_task_runner.h" | |
16 #include "base/synchronization/waitable_event.h" | |
17 #include "base/threading/thread.h" | |
18 #include "base/trace_event/trace_event.h" | |
19 #include "base/trace_event/trace_event_synthetic_delay.h" | |
20 #include "gpu/command_buffer/service/async_pixel_transfer_delegate.h" | |
21 #include "ui/gl/gl_context.h" | |
22 #include "ui/gl/gl_surface_egl.h" | |
23 #include "ui/gl/scoped_binders.h" | |
24 | |
25 namespace gpu { | |
26 | |
27 namespace { | |
28 | |
29 bool CheckErrors(const char* file, int line) { | |
30 EGLint eglerror; | |
31 GLenum glerror; | |
32 bool success = true; | |
33 while ((eglerror = eglGetError()) != EGL_SUCCESS) { | |
34 LOG(ERROR) << "Async transfer EGL error at " | |
35 << file << ":" << line << " " << eglerror; | |
36 success = false; | |
37 } | |
38 while ((glerror = glGetError()) != GL_NO_ERROR) { | |
39 LOG(ERROR) << "Async transfer OpenGL error at " | |
40 << file << ":" << line << " " << glerror; | |
41 success = false; | |
42 } | |
43 return success; | |
44 } | |
45 #define CHECK_GL() CheckErrors(__FILE__, __LINE__) | |
46 | |
47 const char kAsyncTransferThreadName[] = "AsyncTransferThread"; | |
48 | |
49 // Regular glTexImage2D call. | |
50 void DoTexImage2D(const AsyncTexImage2DParams& tex_params, void* data) { | |
51 glTexImage2D( | |
52 GL_TEXTURE_2D, tex_params.level, tex_params.internal_format, | |
53 tex_params.width, tex_params.height, | |
54 tex_params.border, tex_params.format, tex_params.type, data); | |
55 } | |
56 | |
57 // Regular glTexSubImage2D call. | |
58 void DoTexSubImage2D(const AsyncTexSubImage2DParams& tex_params, void* data) { | |
59 glTexSubImage2D( | |
60 GL_TEXTURE_2D, tex_params.level, | |
61 tex_params.xoffset, tex_params.yoffset, | |
62 tex_params.width, tex_params.height, | |
63 tex_params.format, tex_params.type, data); | |
64 } | |
65 | |
66 // Full glTexSubImage2D call, from glTexImage2D params. | |
67 void DoFullTexSubImage2D(const AsyncTexImage2DParams& tex_params, void* data) { | |
68 glTexSubImage2D( | |
69 GL_TEXTURE_2D, tex_params.level, | |
70 0, 0, tex_params.width, tex_params.height, | |
71 tex_params.format, tex_params.type, data); | |
72 } | |
73 | |
74 void SetGlParametersForEglImageTexture() { | |
75 // These params are needed for EGLImage creation to succeed on several | |
76 // Android devices. I couldn't find this requirement in the EGLImage | |
77 // extension spec, but several devices fail without it. | |
78 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
79 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
80 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
81 } | |
82 | |
83 void PerformNotifyCompletion( | |
84 AsyncMemoryParams mem_params, | |
85 scoped_refptr<AsyncPixelTransferCompletionObserver> observer) { | |
86 TRACE_EVENT0("gpu", "PerformNotifyCompletion"); | |
87 observer->DidComplete(mem_params); | |
88 } | |
89 | |
90 class TransferThread : public base::Thread { | |
91 public: | |
92 TransferThread() : base::Thread(kAsyncTransferThreadName) { | |
93 base::Thread::Options options; | |
94 #if defined(OS_ANDROID) | |
95 options.priority = base::ThreadPriority::BACKGROUND; | |
96 #endif | |
97 StartWithOptions(options); | |
98 } | |
99 ~TransferThread() override { Stop(); } | |
100 | |
101 void Init() override { | |
102 gfx::GLShareGroup* share_group = NULL; | |
103 surface_ = new gfx::PbufferGLSurfaceEGL(gfx::Size(1, 1)); | |
104 surface_->Initialize(); | |
105 context_ = gfx::GLContext::CreateGLContext( | |
106 share_group, surface_.get(), gfx::PreferDiscreteGpu); | |
107 bool is_current = context_->MakeCurrent(surface_.get()); | |
108 DCHECK(is_current); | |
109 } | |
110 | |
111 void CleanUp() override { | |
112 surface_ = NULL; | |
113 context_->ReleaseCurrent(surface_.get()); | |
114 context_ = NULL; | |
115 } | |
116 | |
117 private: | |
118 scoped_refptr<gfx::GLContext> context_; | |
119 scoped_refptr<gfx::GLSurface> surface_; | |
120 | |
121 DISALLOW_COPY_AND_ASSIGN(TransferThread); | |
122 }; | |
123 | |
124 base::LazyInstance<TransferThread> | |
125 g_transfer_thread = LAZY_INSTANCE_INITIALIZER; | |
126 | |
127 base::SingleThreadTaskRunner* transfer_task_runner() { | |
128 return g_transfer_thread.Pointer()->task_runner().get(); | |
129 } | |
130 | |
131 // Class which holds async pixel transfers state (EGLImage). | |
132 // The EGLImage is accessed by either thread, but everything | |
133 // else accessed only on the main thread. | |
134 class TransferStateInternal | |
135 : public base::RefCountedThreadSafe<TransferStateInternal> { | |
136 public: | |
137 TransferStateInternal(GLuint texture_id, | |
138 const AsyncTexImage2DParams& define_params, | |
139 bool wait_for_uploads, | |
140 bool wait_for_creation, | |
141 bool use_image_preserved) | |
142 : texture_id_(texture_id), | |
143 thread_texture_id_(0), | |
144 transfer_completion_(true, true), | |
145 egl_image_(EGL_NO_IMAGE_KHR), | |
146 wait_for_uploads_(wait_for_uploads), | |
147 wait_for_creation_(wait_for_creation), | |
148 use_image_preserved_(use_image_preserved) { | |
149 define_params_ = define_params; | |
150 } | |
151 | |
152 bool TransferIsInProgress() { | |
153 return !transfer_completion_.IsSignaled(); | |
154 } | |
155 | |
156 void BindTransfer() { | |
157 TRACE_EVENT2("gpu", "BindAsyncTransfer glEGLImageTargetTexture2DOES", | |
158 "width", define_params_.width, | |
159 "height", define_params_.height); | |
160 DCHECK(texture_id_); | |
161 if (EGL_NO_IMAGE_KHR == egl_image_) | |
162 return; | |
163 | |
164 glBindTexture(GL_TEXTURE_2D, texture_id_); | |
165 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image_); | |
166 bind_callback_.Run(); | |
167 | |
168 DCHECK(CHECK_GL()); | |
169 } | |
170 | |
171 void CreateEglImage(GLuint texture_id) { | |
172 TRACE_EVENT0("gpu", "eglCreateImageKHR"); | |
173 DCHECK(texture_id); | |
174 DCHECK_EQ(egl_image_, EGL_NO_IMAGE_KHR); | |
175 | |
176 EGLDisplay egl_display = eglGetCurrentDisplay(); | |
177 EGLContext egl_context = eglGetCurrentContext(); | |
178 EGLenum egl_target = EGL_GL_TEXTURE_2D_KHR; | |
179 EGLClientBuffer egl_buffer = | |
180 reinterpret_cast<EGLClientBuffer>(texture_id); | |
181 | |
182 EGLint image_preserved = use_image_preserved_ ? EGL_TRUE : EGL_FALSE; | |
183 EGLint egl_attrib_list[] = { | |
184 EGL_GL_TEXTURE_LEVEL_KHR, 0, // mip-level. | |
185 EGL_IMAGE_PRESERVED_KHR, image_preserved, | |
186 EGL_NONE | |
187 }; | |
188 egl_image_ = eglCreateImageKHR( | |
189 egl_display, | |
190 egl_context, | |
191 egl_target, | |
192 egl_buffer, | |
193 egl_attrib_list); | |
194 | |
195 DLOG_IF(ERROR, EGL_NO_IMAGE_KHR == egl_image_) | |
196 << "eglCreateImageKHR failed"; | |
197 } | |
198 | |
199 void CreateEglImageOnUploadThread() { | |
200 CreateEglImage(thread_texture_id_); | |
201 } | |
202 | |
203 void CreateEglImageOnMainThreadIfNeeded() { | |
204 if (egl_image_ == EGL_NO_IMAGE_KHR) { | |
205 CreateEglImage(texture_id_); | |
206 if (wait_for_creation_) { | |
207 TRACE_EVENT0("gpu", "glFinish creation"); | |
208 glFinish(); | |
209 } | |
210 } | |
211 } | |
212 | |
213 void WaitForLastUpload() { | |
214 // This glFinish is just a safe-guard for if uploads have some | |
215 // GPU action that needs to occur. We could use fences and try | |
216 // to do this less often. However, on older drivers fences are | |
217 // not always reliable (eg. Mali-400 just blocks forever). | |
218 if (wait_for_uploads_) { | |
219 TRACE_EVENT0("gpu", "glFinish"); | |
220 glFinish(); | |
221 } | |
222 } | |
223 | |
224 void MarkAsTransferIsInProgress() { | |
225 TRACE_EVENT_SYNTHETIC_DELAY_BEGIN("gpu.AsyncTexImage"); | |
226 transfer_completion_.Reset(); | |
227 } | |
228 | |
229 void MarkAsCompleted() { | |
230 TRACE_EVENT_SYNTHETIC_DELAY_END("gpu.AsyncTexImage"); | |
231 transfer_completion_.Signal(); | |
232 } | |
233 | |
234 void WaitForTransferCompletion() { | |
235 TRACE_EVENT0("gpu", "WaitForTransferCompletion"); | |
236 // TODO(backer): Deschedule the channel rather than blocking the main GPU | |
237 // thread (crbug.com/240265). | |
238 transfer_completion_.Wait(); | |
239 } | |
240 | |
241 void PerformAsyncTexImage2D( | |
242 AsyncTexImage2DParams tex_params, | |
243 AsyncMemoryParams mem_params, | |
244 scoped_refptr<AsyncPixelTransferUploadStats> texture_upload_stats) { | |
245 TRACE_EVENT2("gpu", | |
246 "PerformAsyncTexImage", | |
247 "width", | |
248 tex_params.width, | |
249 "height", | |
250 tex_params.height); | |
251 DCHECK(!thread_texture_id_); | |
252 DCHECK_EQ(0, tex_params.level); | |
253 if (EGL_NO_IMAGE_KHR != egl_image_) { | |
254 MarkAsCompleted(); | |
255 return; | |
256 } | |
257 | |
258 void* data = mem_params.GetDataAddress(); | |
259 | |
260 base::TimeTicks begin_time; | |
261 if (texture_upload_stats.get()) | |
262 begin_time = base::TimeTicks::Now(); | |
263 | |
264 { | |
265 TRACE_EVENT0("gpu", "glTexImage2D no data"); | |
266 glGenTextures(1, &thread_texture_id_); | |
267 glActiveTexture(GL_TEXTURE0); | |
268 glBindTexture(GL_TEXTURE_2D, thread_texture_id_); | |
269 | |
270 SetGlParametersForEglImageTexture(); | |
271 | |
272 // If we need to use image_preserved, we pass the data with | |
273 // the allocation. Otherwise we use a NULL allocation to | |
274 // try to avoid any costs associated with creating the EGLImage. | |
275 if (use_image_preserved_) | |
276 DoTexImage2D(tex_params, data); | |
277 else | |
278 DoTexImage2D(tex_params, NULL); | |
279 } | |
280 | |
281 CreateEglImageOnUploadThread(); | |
282 | |
283 { | |
284 TRACE_EVENT0("gpu", "glTexSubImage2D with data"); | |
285 | |
286 // If we didn't use image_preserved, we haven't uploaded | |
287 // the data yet, so we do this with a full texSubImage. | |
288 if (!use_image_preserved_) | |
289 DoFullTexSubImage2D(tex_params, data); | |
290 } | |
291 | |
292 WaitForLastUpload(); | |
293 MarkAsCompleted(); | |
294 | |
295 DCHECK(CHECK_GL()); | |
296 if (texture_upload_stats.get()) { | |
297 texture_upload_stats->AddUpload(base::TimeTicks::Now() - begin_time); | |
298 } | |
299 } | |
300 | |
301 void PerformAsyncTexSubImage2D( | |
302 AsyncTexSubImage2DParams tex_params, | |
303 AsyncMemoryParams mem_params, | |
304 scoped_refptr<AsyncPixelTransferUploadStats> texture_upload_stats) { | |
305 TRACE_EVENT2("gpu", | |
306 "PerformAsyncTexSubImage2D", | |
307 "width", | |
308 tex_params.width, | |
309 "height", | |
310 tex_params.height); | |
311 | |
312 DCHECK_NE(EGL_NO_IMAGE_KHR, egl_image_); | |
313 DCHECK_EQ(0, tex_params.level); | |
314 | |
315 void* data = mem_params.GetDataAddress(); | |
316 | |
317 base::TimeTicks begin_time; | |
318 if (texture_upload_stats.get()) | |
319 begin_time = base::TimeTicks::Now(); | |
320 | |
321 if (!thread_texture_id_) { | |
322 TRACE_EVENT0("gpu", "glEGLImageTargetTexture2DOES"); | |
323 glGenTextures(1, &thread_texture_id_); | |
324 glActiveTexture(GL_TEXTURE0); | |
325 glBindTexture(GL_TEXTURE_2D, thread_texture_id_); | |
326 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image_); | |
327 } else { | |
328 glActiveTexture(GL_TEXTURE0); | |
329 glBindTexture(GL_TEXTURE_2D, thread_texture_id_); | |
330 } | |
331 { | |
332 TRACE_EVENT0("gpu", "glTexSubImage2D"); | |
333 DoTexSubImage2D(tex_params, data); | |
334 } | |
335 WaitForLastUpload(); | |
336 MarkAsCompleted(); | |
337 | |
338 DCHECK(CHECK_GL()); | |
339 if (texture_upload_stats.get()) { | |
340 texture_upload_stats->AddUpload(base::TimeTicks::Now() - begin_time); | |
341 } | |
342 } | |
343 | |
344 protected: | |
345 friend class base::RefCountedThreadSafe<TransferStateInternal>; | |
346 friend class gpu::AsyncPixelTransferDelegateEGL; | |
347 | |
348 static void DeleteTexture(GLuint id) { | |
349 glDeleteTextures(1, &id); | |
350 } | |
351 | |
352 virtual ~TransferStateInternal() { | |
353 if (egl_image_ != EGL_NO_IMAGE_KHR) { | |
354 EGLDisplay display = eglGetCurrentDisplay(); | |
355 eglDestroyImageKHR(display, egl_image_); | |
356 } | |
357 if (thread_texture_id_) { | |
358 transfer_task_runner()->PostTask( | |
359 FROM_HERE, base::Bind(&DeleteTexture, thread_texture_id_)); | |
360 } | |
361 } | |
362 | |
363 // The 'real' texture. | |
364 GLuint texture_id_; | |
365 | |
366 // The EGLImage sibling on the upload thread. | |
367 GLuint thread_texture_id_; | |
368 | |
369 // Definition params for texture that needs binding. | |
370 AsyncTexImage2DParams define_params_; | |
371 | |
372 // Indicates that an async transfer is in progress. | |
373 base::WaitableEvent transfer_completion_; | |
374 | |
375 // It would be nice if we could just create a new EGLImage for | |
376 // every upload, but I found that didn't work, so this stores | |
377 // one for the lifetime of the texture. | |
378 EGLImageKHR egl_image_; | |
379 | |
380 // Callback to invoke when AsyncTexImage2D is complete | |
381 // and the client can safely use the texture. This occurs | |
382 // during BindCompletedAsyncTransfers(). | |
383 base::Closure bind_callback_; | |
384 | |
385 // Customize when we block on fences (these are work-arounds). | |
386 bool wait_for_uploads_; | |
387 bool wait_for_creation_; | |
388 bool use_image_preserved_; | |
389 }; | |
390 | |
391 } // namespace | |
392 | |
393 // Class which handles async pixel transfers using EGLImageKHR and another | |
394 // upload thread | |
395 class AsyncPixelTransferDelegateEGL | |
396 : public AsyncPixelTransferDelegate, | |
397 public base::SupportsWeakPtr<AsyncPixelTransferDelegateEGL> { | |
398 public: | |
399 AsyncPixelTransferDelegateEGL( | |
400 AsyncPixelTransferManagerEGL::SharedState* shared_state, | |
401 GLuint texture_id, | |
402 const AsyncTexImage2DParams& define_params); | |
403 ~AsyncPixelTransferDelegateEGL() override; | |
404 | |
405 void BindTransfer() { state_->BindTransfer(); } | |
406 | |
407 // Implement AsyncPixelTransferDelegate: | |
408 void AsyncTexImage2D(const AsyncTexImage2DParams& tex_params, | |
409 const AsyncMemoryParams& mem_params, | |
410 const base::Closure& bind_callback) override; | |
411 void AsyncTexSubImage2D(const AsyncTexSubImage2DParams& tex_params, | |
412 const AsyncMemoryParams& mem_params) override; | |
413 bool TransferIsInProgress() override; | |
414 void WaitForTransferCompletion() override; | |
415 | |
416 private: | |
417 // Returns true if a work-around was used. | |
418 bool WorkAroundAsyncTexImage2D( | |
419 const AsyncTexImage2DParams& tex_params, | |
420 const AsyncMemoryParams& mem_params, | |
421 const base::Closure& bind_callback); | |
422 bool WorkAroundAsyncTexSubImage2D( | |
423 const AsyncTexSubImage2DParams& tex_params, | |
424 const AsyncMemoryParams& mem_params); | |
425 | |
426 // A raw pointer is safe because the SharedState is owned by the Manager, | |
427 // which owns this Delegate. | |
428 AsyncPixelTransferManagerEGL::SharedState* shared_state_; | |
429 scoped_refptr<TransferStateInternal> state_; | |
430 | |
431 DISALLOW_COPY_AND_ASSIGN(AsyncPixelTransferDelegateEGL); | |
432 }; | |
433 | |
434 AsyncPixelTransferDelegateEGL::AsyncPixelTransferDelegateEGL( | |
435 AsyncPixelTransferManagerEGL::SharedState* shared_state, | |
436 GLuint texture_id, | |
437 const AsyncTexImage2DParams& define_params) | |
438 : shared_state_(shared_state) { | |
439 // We can't wait on uploads on imagination (it can take 200ms+). | |
440 // In practice, they are complete when the CPU glTexSubImage2D completes. | |
441 bool wait_for_uploads = !shared_state_->is_imagination; | |
442 | |
443 // Qualcomm runs into texture corruption problems if the same texture is | |
444 // uploaded to with both async and normal uploads. Synchronize after EGLImage | |
445 // creation on the main thread as a work-around. | |
446 bool wait_for_creation = shared_state_->is_qualcomm; | |
447 | |
448 // Qualcomm has a race when using image_preserved=FALSE, | |
449 // which can result in black textures even after the first upload. | |
450 // Since using FALSE is mainly for performance (to avoid layout changes), | |
451 // but Qualcomm itself doesn't seem to get any performance benefit, | |
452 // we just using image_preservedd=TRUE on Qualcomm as a work-around. | |
453 bool use_image_preserved = | |
454 shared_state_->is_qualcomm || shared_state_->is_imagination; | |
455 | |
456 state_ = new TransferStateInternal(texture_id, | |
457 define_params, | |
458 wait_for_uploads, | |
459 wait_for_creation, | |
460 use_image_preserved); | |
461 } | |
462 | |
463 AsyncPixelTransferDelegateEGL::~AsyncPixelTransferDelegateEGL() {} | |
464 | |
465 bool AsyncPixelTransferDelegateEGL::TransferIsInProgress() { | |
466 return state_->TransferIsInProgress(); | |
467 } | |
468 | |
469 void AsyncPixelTransferDelegateEGL::WaitForTransferCompletion() { | |
470 if (state_->TransferIsInProgress()) { | |
471 state_->WaitForTransferCompletion(); | |
472 DCHECK(!state_->TransferIsInProgress()); | |
473 } | |
474 } | |
475 | |
476 void AsyncPixelTransferDelegateEGL::AsyncTexImage2D( | |
477 const AsyncTexImage2DParams& tex_params, | |
478 const AsyncMemoryParams& mem_params, | |
479 const base::Closure& bind_callback) { | |
480 if (WorkAroundAsyncTexImage2D(tex_params, mem_params, bind_callback)) | |
481 return; | |
482 | |
483 DCHECK(!state_->TransferIsInProgress()); | |
484 DCHECK_EQ(state_->egl_image_, EGL_NO_IMAGE_KHR); | |
485 DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D), tex_params.target); | |
486 DCHECK_EQ(tex_params.level, 0); | |
487 | |
488 // Mark the transfer in progress and save the late bind | |
489 // callback, so we can notify the client when it is bound. | |
490 shared_state_->pending_allocations.push_back(AsWeakPtr()); | |
491 state_->bind_callback_ = bind_callback; | |
492 | |
493 // Mark the transfer in progress. | |
494 state_->MarkAsTransferIsInProgress(); | |
495 | |
496 // Duplicate the shared memory so there is no way we can get | |
497 // a use-after-free of the raw pixels. | |
498 transfer_task_runner()->PostTask( | |
499 FROM_HERE, | |
500 base::Bind(&TransferStateInternal::PerformAsyncTexImage2D, state_, | |
501 tex_params, mem_params, shared_state_->texture_upload_stats)); | |
502 | |
503 DCHECK(CHECK_GL()); | |
504 } | |
505 | |
506 void AsyncPixelTransferDelegateEGL::AsyncTexSubImage2D( | |
507 const AsyncTexSubImage2DParams& tex_params, | |
508 const AsyncMemoryParams& mem_params) { | |
509 TRACE_EVENT2("gpu", "AsyncTexSubImage2D", | |
510 "width", tex_params.width, | |
511 "height", tex_params.height); | |
512 if (WorkAroundAsyncTexSubImage2D(tex_params, mem_params)) | |
513 return; | |
514 DCHECK(!state_->TransferIsInProgress()); | |
515 DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D), tex_params.target); | |
516 DCHECK_EQ(tex_params.level, 0); | |
517 | |
518 // Mark the transfer in progress. | |
519 state_->MarkAsTransferIsInProgress(); | |
520 | |
521 // If this wasn't async allocated, we don't have an EGLImage yet. | |
522 // Create the EGLImage if it hasn't already been created. | |
523 state_->CreateEglImageOnMainThreadIfNeeded(); | |
524 | |
525 // Duplicate the shared memory so there are no way we can get | |
526 // a use-after-free of the raw pixels. | |
527 transfer_task_runner()->PostTask( | |
528 FROM_HERE, | |
529 base::Bind(&TransferStateInternal::PerformAsyncTexSubImage2D, state_, | |
530 tex_params, mem_params, shared_state_->texture_upload_stats)); | |
531 | |
532 DCHECK(CHECK_GL()); | |
533 } | |
534 | |
535 namespace { | |
536 bool IsPowerOfTwo (unsigned int x) { | |
537 return ((x != 0) && !(x & (x - 1))); | |
538 } | |
539 | |
540 bool IsMultipleOfEight(unsigned int x) { | |
541 return (x & 7) == 0; | |
542 } | |
543 | |
544 bool DimensionsSupportImgFastPath(int width, int height) { | |
545 // Multiple of eight, but not a power of two. | |
546 return IsMultipleOfEight(width) && | |
547 IsMultipleOfEight(height) && | |
548 !(IsPowerOfTwo(width) && | |
549 IsPowerOfTwo(height)); | |
550 } | |
551 } // namespace | |
552 | |
553 // It is very difficult to stream uploads on Imagination GPUs: | |
554 // - glTexImage2D defers a swizzle/stall until draw-time | |
555 // - glTexSubImage2D will sleep for 16ms on a good day, and 100ms | |
556 // or longer if OpenGL is in heavy use by another thread. | |
557 // The one combination that avoids these problems requires: | |
558 // a.) Allocations/Uploads must occur on different threads/contexts. | |
559 // b.) Texture size must be non-power-of-two. | |
560 // When using a+b, uploads will be incorrect/corrupt unless: | |
561 // c.) Texture size must be a multiple-of-eight. | |
562 // | |
563 // To achieve a.) we allocate synchronously on the main thread followed | |
564 // by uploading on the upload thread. When b/c are not true we fall back | |
565 // on purely synchronous allocation/upload on the main thread. | |
566 | |
567 bool AsyncPixelTransferDelegateEGL::WorkAroundAsyncTexImage2D( | |
568 const AsyncTexImage2DParams& tex_params, | |
569 const AsyncMemoryParams& mem_params, | |
570 const base::Closure& bind_callback) { | |
571 if (!shared_state_->is_imagination) | |
572 return false; | |
573 | |
574 // On imagination we allocate synchronously all the time, even | |
575 // if the dimensions support fast uploads. This is for part a.) | |
576 // above, so allocations occur on a different thread/context as uploads. | |
577 void* data = mem_params.GetDataAddress(); | |
578 SetGlParametersForEglImageTexture(); | |
579 | |
580 { | |
581 TRACE_EVENT0("gpu", "glTexImage2D with data"); | |
582 DoTexImage2D(tex_params, data); | |
583 } | |
584 | |
585 // The allocation has already occured, so mark it as finished | |
586 // and ready for binding. | |
587 CHECK(!state_->TransferIsInProgress()); | |
588 | |
589 // If the dimensions support fast async uploads, create the | |
590 // EGLImage for future uploads. The late bind should not | |
591 // be needed since the EGLImage was created from the main thread | |
592 // texture, but this is required to prevent an imagination driver crash. | |
593 if (DimensionsSupportImgFastPath(tex_params.width, tex_params.height)) { | |
594 state_->CreateEglImageOnMainThreadIfNeeded(); | |
595 shared_state_->pending_allocations.push_back(AsWeakPtr()); | |
596 state_->bind_callback_ = bind_callback; | |
597 } | |
598 | |
599 DCHECK(CHECK_GL()); | |
600 return true; | |
601 } | |
602 | |
603 bool AsyncPixelTransferDelegateEGL::WorkAroundAsyncTexSubImage2D( | |
604 const AsyncTexSubImage2DParams& tex_params, | |
605 const AsyncMemoryParams& mem_params) { | |
606 if (!shared_state_->is_imagination) | |
607 return false; | |
608 | |
609 // If the dimensions support fast async uploads, we can use the | |
610 // normal async upload path for uploads. | |
611 if (DimensionsSupportImgFastPath(tex_params.width, tex_params.height)) | |
612 return false; | |
613 | |
614 // Fall back on a synchronous stub as we don't have a known fast path. | |
615 // Also, older ICS drivers crash when we do any glTexSubImage2D on the | |
616 // same thread. To work around this we do glTexImage2D instead. Since | |
617 // we didn't create an EGLImage for this texture (see above), this is | |
618 // okay, but it limits this API to full updates for now. | |
619 DCHECK(!state_->egl_image_); | |
620 DCHECK_EQ(tex_params.xoffset, 0); | |
621 DCHECK_EQ(tex_params.yoffset, 0); | |
622 DCHECK_EQ(state_->define_params_.width, tex_params.width); | |
623 DCHECK_EQ(state_->define_params_.height, tex_params.height); | |
624 DCHECK_EQ(state_->define_params_.level, tex_params.level); | |
625 DCHECK_EQ(state_->define_params_.format, tex_params.format); | |
626 DCHECK_EQ(state_->define_params_.type, tex_params.type); | |
627 | |
628 void* data = mem_params.GetDataAddress(); | |
629 base::TimeTicks begin_time; | |
630 if (shared_state_->texture_upload_stats.get()) | |
631 begin_time = base::TimeTicks::Now(); | |
632 { | |
633 TRACE_EVENT0("gpu", "glTexSubImage2D"); | |
634 // Note we use define_params_ instead of tex_params. | |
635 // The DCHECKs above verify this is always the same. | |
636 DoTexImage2D(state_->define_params_, data); | |
637 } | |
638 if (shared_state_->texture_upload_stats.get()) { | |
639 shared_state_->texture_upload_stats | |
640 ->AddUpload(base::TimeTicks::Now() - begin_time); | |
641 } | |
642 | |
643 DCHECK(CHECK_GL()); | |
644 return true; | |
645 } | |
646 | |
647 AsyncPixelTransferManagerEGL::SharedState::SharedState() | |
648 // TODO(reveman): Skip this if --enable-gpu-benchmarking is not present. | |
649 : texture_upload_stats(new AsyncPixelTransferUploadStats) { | |
650 const char* vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR)); | |
651 if (vendor) { | |
652 is_imagination = | |
653 std::string(vendor).find("Imagination") != std::string::npos; | |
654 is_qualcomm = std::string(vendor).find("Qualcomm") != std::string::npos; | |
655 } | |
656 } | |
657 | |
658 AsyncPixelTransferManagerEGL::SharedState::~SharedState() {} | |
659 | |
660 AsyncPixelTransferManagerEGL::AsyncPixelTransferManagerEGL() {} | |
661 | |
662 AsyncPixelTransferManagerEGL::~AsyncPixelTransferManagerEGL() {} | |
663 | |
664 void AsyncPixelTransferManagerEGL::BindCompletedAsyncTransfers() { | |
665 scoped_ptr<gfx::ScopedTextureBinder> texture_binder; | |
666 | |
667 while(!shared_state_.pending_allocations.empty()) { | |
668 if (!shared_state_.pending_allocations.front().get()) { | |
669 shared_state_.pending_allocations.pop_front(); | |
670 continue; | |
671 } | |
672 AsyncPixelTransferDelegateEGL* delegate = | |
673 shared_state_.pending_allocations.front().get(); | |
674 // Terminate early, as all transfers finish in order, currently. | |
675 if (delegate->TransferIsInProgress()) | |
676 break; | |
677 | |
678 if (!texture_binder) | |
679 texture_binder.reset(new gfx::ScopedTextureBinder(GL_TEXTURE_2D, 0)); | |
680 | |
681 // If the transfer is finished, bind it to the texture | |
682 // and remove it from pending list. | |
683 delegate->BindTransfer(); | |
684 shared_state_.pending_allocations.pop_front(); | |
685 } | |
686 } | |
687 | |
688 void AsyncPixelTransferManagerEGL::AsyncNotifyCompletion( | |
689 const AsyncMemoryParams& mem_params, | |
690 AsyncPixelTransferCompletionObserver* observer) { | |
691 // Post a PerformNotifyCompletion task to the upload thread. This task | |
692 // will run after all async transfers are complete. | |
693 transfer_task_runner()->PostTask( | |
694 FROM_HERE, base::Bind(&PerformNotifyCompletion, mem_params, | |
695 make_scoped_refptr(observer))); | |
696 } | |
697 | |
698 uint32 AsyncPixelTransferManagerEGL::GetTextureUploadCount() { | |
699 return shared_state_.texture_upload_stats->GetStats(NULL); | |
700 } | |
701 | |
702 base::TimeDelta AsyncPixelTransferManagerEGL::GetTotalTextureUploadTime() { | |
703 base::TimeDelta total_texture_upload_time; | |
704 shared_state_.texture_upload_stats->GetStats(&total_texture_upload_time); | |
705 return total_texture_upload_time; | |
706 } | |
707 | |
708 void AsyncPixelTransferManagerEGL::ProcessMorePendingTransfers() { | |
709 } | |
710 | |
711 bool AsyncPixelTransferManagerEGL::NeedsProcessMorePendingTransfers() { | |
712 return false; | |
713 } | |
714 | |
715 void AsyncPixelTransferManagerEGL::WaitAllAsyncTexImage2D() { | |
716 if (shared_state_.pending_allocations.empty()) | |
717 return; | |
718 | |
719 AsyncPixelTransferDelegateEGL* delegate = | |
720 shared_state_.pending_allocations.back().get(); | |
721 if (delegate) | |
722 delegate->WaitForTransferCompletion(); | |
723 } | |
724 | |
725 AsyncPixelTransferDelegate* | |
726 AsyncPixelTransferManagerEGL::CreatePixelTransferDelegateImpl( | |
727 gles2::TextureRef* ref, | |
728 const AsyncTexImage2DParams& define_params) { | |
729 return new AsyncPixelTransferDelegateEGL( | |
730 &shared_state_, ref->service_id(), define_params); | |
731 } | |
732 | |
733 } // namespace gpu | |
OLD | NEW |