| 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 |