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