Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "ui/gl/async_pixel_transfer_delegate_android.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/cancelable_callback.h" | |
| 9 #include "base/debug/trace_event.h" | |
| 10 #include "base/lazy_instance.h" | |
| 11 #include "base/logging.h" | |
| 12 #include "base/memory/scoped_ptr.h" | |
| 13 #include "base/threading/thread.h" | |
| 14 #include "build/build_config.h" | |
| 15 #include "third_party/angle/include/EGL/egl.h" | |
| 16 #include "third_party/angle/include/EGL/eglext.h" | |
| 17 #include "ui/gfx/point.h" | |
| 18 #include "ui/gfx/size.h" | |
| 19 #include "ui/gl/async_pixel_transfer_delegate.h" | |
| 20 #include "ui/gl/egl_util.h" | |
| 21 #include "ui/gl/gl_bindings.h" | |
| 22 #include "ui/gl/gl_context.h" | |
| 23 #include "ui/gl/gl_surface_egl.h" | |
| 24 | |
| 25 namespace gfx { | |
| 26 | |
| 27 namespace { | |
| 28 bool CheckErrors(const char* file, int line) { | |
| 29 EGLint eglerror; | |
| 30 GLenum glerror; | |
| 31 bool success = true; | |
| 32 while ((eglerror = eglGetError()) != EGL_SUCCESS) { | |
| 33 LOG(ERROR) << "Async transfer eglerror at " | |
| 34 << file << ":" << line << " " << eglerror; | |
| 35 success = false; | |
| 36 } | |
| 37 while ((glerror = glGetError()) != GL_NO_ERROR) { | |
| 38 LOG(ERROR) << "Async transfer openglerror at " | |
| 39 << file << ":" << line << " " << glerror; | |
| 40 success = false; | |
| 41 } | |
| 42 return success; | |
| 43 } | |
| 44 #define CHK() CheckErrors(__FILE__, __LINE__) | |
| 45 } // namespace | |
| 46 | |
| 47 // Class which holds async pixel transfers state (EGLImage). | |
| 48 // The EGLImage is accessed by either thread, but the everything | |
|
apatrick_chromium
2012/12/04 20:59:22
nit: fix comment
epenner
2012/12/08 03:15:04
Is this regarding thread-safe ref counting? Let me
| |
| 49 // else (and the lifetime of this object) is access only by the main | |
| 50 // GPU thread. | |
| 51 class AsyncTransferStateAndroid : public AsyncPixelTransferState { | |
| 52 public: | |
| 53 AsyncTransferStateAndroid(GLuint texture_id) | |
| 54 : texture_id_(texture_id), | |
| 55 needs_bind_(false), | |
| 56 transfer_in_progress_(false), | |
| 57 egl_image_(0) {} | |
| 58 | |
| 59 virtual ~AsyncTransferStateAndroid() { | |
| 60 if (egl_image_) { | |
|
apatrick_chromium
2012/12/04 20:59:22
nit: indentation out by 1 space
epenner
2012/12/08 03:15:04
Done.
| |
| 61 EGLDisplay display = eglGetCurrentDisplay(); | |
| 62 eglDestroyImageKHR(display, egl_image_); | |
| 63 } | |
| 64 } | |
| 65 | |
| 66 // implement AsyncPixelTransferState: | |
| 67 virtual bool TransferIsInProgress() { | |
| 68 return transfer_in_progress_; | |
| 69 } | |
| 70 | |
| 71 virtual void BindTexture(GLenum target) { | |
| 72 glBindTexture(target, texture_id_); | |
| 73 if (needs_bind_) | |
| 74 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image_); | |
| 75 needs_bind_ = false; | |
| 76 } | |
| 77 | |
| 78 // Completion callbacks. | |
| 79 void TexImage2DCompleted() { | |
| 80 needs_bind_ = true; | |
| 81 transfer_in_progress_ = false; | |
| 82 } | |
| 83 void TexSubImage2DCompleted() { | |
| 84 transfer_in_progress_ = false; | |
| 85 } | |
| 86 | |
| 87 // The 'real' texture. | |
| 88 GLuint texture_id_; | |
| 89 | |
| 90 // Indicates there is a new EGLImage and the 'real' | |
| 91 // texture needs to be bound to it. | |
| 92 bool needs_bind_; | |
| 93 | |
| 94 // Indicates that an async transfer is in progress. | |
| 95 bool transfer_in_progress_; | |
| 96 | |
| 97 // It would be nice if we could just create a new EGLImage for | |
| 98 // every upload, but I found that didn't work, so this stores | |
| 99 // one for the lifetime of the texture. | |
| 100 EGLImageKHR egl_image_; | |
| 101 }; | |
| 102 | |
| 103 // Class which handles async pixel transfers on Android (using | |
| 104 // EGLImageKHR and another upload thread) | |
| 105 class AsyncPixelTransferDelegateAndroid : public AsyncPixelTransferDelegate { | |
| 106 public: | |
| 107 AsyncPixelTransferDelegateAndroid(); | |
| 108 virtual ~AsyncPixelTransferDelegateAndroid(); | |
| 109 | |
| 110 // implement AsyncPixelTransferDelegate: | |
| 111 virtual scoped_refptr<AsyncPixelTransferState> | |
| 112 CreatePixelTransferState(GLuint); | |
| 113 | |
| 114 virtual void AsyncNotifyCompletion( | |
| 115 const base::Closure& task); | |
| 116 | |
| 117 virtual void AsyncTexImage2D( | |
| 118 AsyncPixelTransferState*, | |
| 119 GLenum target, | |
| 120 GLint level, | |
| 121 GLenum internal_format, | |
| 122 GLsizei width, | |
| 123 GLsizei height, | |
| 124 GLint border, | |
| 125 GLenum format, | |
| 126 GLenum type, | |
| 127 const void* data); | |
| 128 | |
| 129 virtual void AsyncTexSubImage2D( | |
| 130 AsyncPixelTransferState*, | |
| 131 GLenum target, | |
| 132 GLint level, | |
| 133 GLint xoffset, | |
| 134 GLint yoffset, | |
| 135 GLsizei width, | |
| 136 GLsizei height, | |
| 137 GLenum format, | |
| 138 GLenum type, | |
| 139 const void* data); | |
| 140 | |
| 141 private: | |
| 142 void Initialize(); | |
| 143 void Shutdown(); | |
| 144 void PerformInitialize(); | |
| 145 void PerformShutdown(); | |
| 146 | |
| 147 void PerformAsyncTexImage2D( | |
| 148 EGLImageKHR* egl_image, | |
| 149 std::pair<GLenum, GLenum> formats, | |
| 150 gfx::Size size, | |
| 151 GLint border, | |
| 152 GLenum type, | |
| 153 const void* data); | |
| 154 | |
| 155 void PerformAsyncTexSubImage2D( | |
| 156 EGLImageKHR egl_image, | |
| 157 gfx::Point offset, | |
| 158 gfx::Size size, | |
| 159 GLenum format, | |
| 160 GLenum type, | |
| 161 const void* data); | |
| 162 | |
| 163 scoped_ptr<base::Thread> thread_; | |
| 164 scoped_refptr<gfx::GLContext> thread_context_; | |
| 165 scoped_refptr<gfx::GLSurface> thread_surface_; | |
| 166 | |
| 167 DISALLOW_COPY_AND_ASSIGN(AsyncPixelTransferDelegateAndroid); | |
| 168 }; | |
| 169 | |
| 170 // Lazy instance creation. | |
| 171 base::LazyInstance<AsyncPixelTransferDelegateAndroid> | |
| 172 g_async_pixel_transfer_delegate_ = LAZY_INSTANCE_INITIALIZER; | |
| 173 | |
| 174 AsyncPixelTransferDelegate* AsyncPixelTransferDelegate::Get() { | |
| 175 return g_async_pixel_transfer_delegate_.Pointer(); | |
| 176 } | |
| 177 | |
| 178 scoped_refptr<AsyncPixelTransferState> | |
| 179 AsyncPixelTransferDelegateAndroid:: | |
| 180 CreatePixelTransferState(GLuint texture_id) { | |
| 181 return make_scoped_refptr(static_cast<AsyncPixelTransferState*>( | |
| 182 new AsyncTransferStateAndroid(texture_id))); | |
| 183 } | |
| 184 | |
| 185 AsyncPixelTransferDelegateAndroid::AsyncPixelTransferDelegateAndroid() | |
| 186 : thread_(new base::Thread("GPUAsyncTransferThread")) | |
| 187 { | |
| 188 Initialize(); | |
| 189 } | |
| 190 | |
| 191 AsyncPixelTransferDelegateAndroid::~AsyncPixelTransferDelegateAndroid() | |
| 192 { | |
| 193 Shutdown(); | |
| 194 } | |
| 195 | |
| 196 void AsyncPixelTransferDelegateAndroid::Initialize() { | |
| 197 // Start the thread and initialize on the thread. | |
| 198 thread_->Start(); | |
| 199 thread_->message_loop()->PostTask(FROM_HERE, base::Bind( | |
| 200 &AsyncPixelTransferDelegateAndroid::PerformInitialize, | |
| 201 base::Unretained(this))); | |
| 202 } | |
| 203 | |
| 204 void AsyncPixelTransferDelegateAndroid::Shutdown() { | |
| 205 // Shutdown and wait for the thread to finish. | |
| 206 thread_->message_loop()->PostTask(FROM_HERE, base::Bind( | |
| 207 &AsyncPixelTransferDelegateAndroid::PerformShutdown, | |
| 208 base::Unretained(this))); | |
| 209 thread_->Stop(); | |
| 210 } | |
| 211 | |
| 212 | |
| 213 namespace { | |
| 214 // Dummy function to measure completion on | |
| 215 // the upload thread. | |
| 216 void NoOp() {} | |
| 217 } // namespace | |
| 218 | |
| 219 void AsyncPixelTransferDelegateAndroid::AsyncNotifyCompletion( | |
| 220 const base::Closure& task) { | |
| 221 // Post a no-op task to the upload thread followed | |
| 222 // by a reply to the callback. The reply will then occur after | |
| 223 // all async transfers are complete. | |
| 224 thread_->message_loop_proxy()->PostTaskAndReply(FROM_HERE, | |
| 225 base::Bind(&NoOp), task); | |
| 226 } | |
| 227 | |
| 228 void AsyncPixelTransferDelegateAndroid::AsyncTexImage2D( | |
| 229 AsyncPixelTransferState* transfer_state, | |
| 230 GLenum target, | |
| 231 GLint level, | |
| 232 GLenum internal_format, | |
| 233 GLsizei width, | |
| 234 GLsizei height, | |
| 235 GLint border, | |
| 236 GLenum format, | |
| 237 GLenum type, | |
| 238 const void* data) { | |
| 239 AsyncTransferStateAndroid* state = | |
| 240 static_cast<AsyncTransferStateAndroid*>(transfer_state); | |
| 241 DCHECK(state); | |
| 242 DCHECK(state->texture_id_); | |
| 243 DCHECK(!state->transfer_in_progress_); | |
| 244 | |
| 245 // TODO: Implement other targets/levels if needed. | |
| 246 DCHECK(target == GL_TEXTURE_2D); | |
| 247 DCHECK(level == 0); | |
| 248 | |
| 249 state->transfer_in_progress_ = true; | |
| 250 | |
| 251 // Any existing EGLImage is made an 'orphan' by a call to | |
| 252 // texImage2D. We can delete the existing one safely since | |
| 253 // the client texture is not effected. | |
|
apatrick_chromium
2012/12/04 20:59:22
nit: effected -> affected
epenner
2012/12/08 03:15:04
Done.
| |
| 254 if (state->egl_image_) { | |
| 255 EGLDisplay display = eglGetCurrentDisplay(); | |
| 256 eglDestroyImageKHR(display, state->egl_image_); | |
| 257 state->egl_image_ = 0; | |
| 258 } | |
| 259 | |
| 260 // Post the upload task. The reply also keeps the texture | |
| 261 // state referenced until the reply executes on the main thread. | |
| 262 thread_->message_loop_proxy()->PostTaskAndReply(FROM_HERE, | |
| 263 base::Bind( | |
| 264 &AsyncPixelTransferDelegateAndroid::PerformAsyncTexImage2D, | |
| 265 base::Unretained(this), | |
| 266 &state->egl_image_, | |
| 267 std::pair<GLenum, GLenum>(internal_format, format), | |
| 268 gfx::Size(width, height), | |
| 269 border, type, data), | |
| 270 base::Bind( | |
| 271 &AsyncTransferStateAndroid::TexImage2DCompleted, | |
| 272 state)); | |
| 273 } | |
| 274 | |
| 275 void AsyncPixelTransferDelegateAndroid::AsyncTexSubImage2D( | |
| 276 AsyncPixelTransferState* transfer_state, | |
| 277 GLenum target, | |
| 278 GLint level, | |
| 279 GLint xoffset, | |
| 280 GLint yoffset, | |
| 281 GLsizei width, | |
| 282 GLsizei height, | |
| 283 GLenum format, | |
| 284 GLenum type, | |
| 285 const void* data) { | |
| 286 TRACE_EVENT2("gpu", "AsyncTexSubImage2D", | |
| 287 "width", width, | |
| 288 "height", height); | |
| 289 AsyncTransferStateAndroid* state = | |
| 290 static_cast<AsyncTransferStateAndroid*>(transfer_state); | |
| 291 DCHECK(state); | |
| 292 DCHECK(state->texture_id_); | |
| 293 DCHECK(!state->transfer_in_progress_); | |
| 294 | |
| 295 // TODO: Implement other targets/levels if needed. | |
| 296 DCHECK(target == GL_TEXTURE_2D); | |
| 297 DCHECK(level == 0); | |
| 298 | |
| 299 state->transfer_in_progress_ = true; | |
| 300 | |
| 301 // Create the EGLImage if it hasn't already been created. | |
| 302 if (!state->egl_image_) { | |
| 303 EGLDisplay egl_display = eglGetCurrentDisplay(); | |
| 304 EGLContext egl_context = eglGetCurrentContext(); | |
| 305 EGLenum egl_target = EGL_GL_TEXTURE_2D_KHR; | |
| 306 EGLClientBuffer egl_buffer = | |
| 307 reinterpret_cast<EGLClientBuffer>(state->texture_id_); | |
| 308 EGLint egl_attrib_list[] = { | |
| 309 EGL_GL_TEXTURE_LEVEL_KHR, level, // mip-map level to reference. | |
| 310 EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, // preserve the data in the texture. | |
| 311 EGL_NONE | |
| 312 }; | |
| 313 state->egl_image_ = eglCreateImageKHR( | |
| 314 egl_display, | |
| 315 egl_context, | |
| 316 egl_target, | |
| 317 egl_buffer, | |
| 318 egl_attrib_list); | |
| 319 } | |
| 320 | |
| 321 // Post the upload task. The reply also keeps the texture | |
| 322 // state referenced until the reply executes on the main thread. | |
| 323 thread_->message_loop_proxy()->PostTaskAndReply(FROM_HERE, | |
| 324 base::Bind( | |
| 325 &AsyncPixelTransferDelegateAndroid::PerformAsyncTexSubImage2D, | |
| 326 base::Unretained(this), | |
| 327 state->egl_image_, | |
| 328 gfx::Point(xoffset, yoffset), | |
| 329 gfx::Size(width, height), | |
| 330 format, type, data), | |
| 331 base::Bind( | |
| 332 &AsyncTransferStateAndroid::TexSubImage2DCompleted, | |
| 333 state)); | |
| 334 } | |
| 335 | |
| 336 void AsyncPixelTransferDelegateAndroid::PerformInitialize() { | |
| 337 DCHECK(!thread_surface_); | |
| 338 DCHECK(!thread_context_); | |
| 339 GLShareGroup* share_group = NULL; | |
| 340 bool software = false; | |
| 341 thread_surface_ = new gfx::PbufferGLSurfaceEGL(software, gfx::Size(1,1)); | |
| 342 thread_surface_->Initialize(); | |
|
apatrick_chromium
2012/12/04 20:59:22
What if this fails?
epenner
2012/12/08 03:15:04
Hmm, is this normally treated as a recoverable err
| |
| 343 thread_context_ = gfx::GLContext::CreateGLContext(share_group, | |
|
apatrick_chromium
2012/12/04 20:59:22
And this.
epenner
2012/12/08 03:15:04
See above.
| |
| 344 thread_surface_, | |
| 345 gfx::PreferDiscreteGpu); | |
| 346 bool is_current = thread_context_->MakeCurrent(thread_surface_); | |
|
apatrick_chromium
2012/12/04 20:59:22
Ditto.
epenner
2012/12/08 03:15:04
See above.
| |
| 347 DCHECK(thread_surface_); | |
| 348 DCHECK(thread_context_); | |
| 349 DCHECK(is_current); | |
| 350 } | |
| 351 | |
| 352 void AsyncPixelTransferDelegateAndroid::PerformShutdown() { | |
| 353 DCHECK(thread_surface_); | |
| 354 DCHECK(thread_context_); | |
| 355 thread_surface_ = NULL; | |
| 356 thread_context_->ReleaseCurrent(thread_surface_); | |
| 357 thread_context_ = NULL; | |
| 358 } | |
| 359 | |
| 360 namespace { | |
| 361 void WaitForGlFence() { | |
| 362 // TODO: Fix bindings (link errors) to enable the code below. | |
| 363 // TODO: Should we only sync just before we report completion? | |
| 364 | |
| 365 // Uploads usually finish on the CPU, but just in case add a fence | |
| 366 // and guarantee the upload has completed. Flush bit is set to | |
| 367 // insure we don't wait forever. | |
| 368 // EGLSyncKHR fence = eglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, NULL); | |
| 369 // EGLint flags = EGL_SYNC_FLUSH_COMMANDS_BIT_KHR; | |
| 370 // EGLTimeKHR time = EGL_FOREVER_KHR; | |
| 371 // eglClientWaitSyncKHR(display, fence, flags, time); | |
| 372 glFinish(); | |
| 373 } | |
| 374 } // namespace | |
| 375 | |
| 376 void AsyncPixelTransferDelegateAndroid::PerformAsyncTexImage2D( | |
| 377 EGLImageKHR* egl_image, | |
| 378 std::pair<GLenum, GLenum> formats, | |
| 379 gfx::Size size, | |
| 380 GLint border, | |
| 381 GLenum type, | |
| 382 const void* data) { | |
| 383 // In texImage2D, we do everything on the upload thread. | |
| 384 // This is because texImage2D can incur the allocation cost, and | |
| 385 // it also 'orphans' any previous EGLImage bound to the texture. | |
| 386 DCHECK(egl_image); | |
| 387 DCHECK(!(*egl_image)); | |
| 388 TRACE_EVENT2("gpu", "performAsyncTexImage2D", | |
| 389 "width", size.width(), | |
| 390 "height", size.height()); | |
| 391 | |
| 392 // Create a texture from the image and upload to it. | |
| 393 GLenum internal_format = formats.first; | |
| 394 GLenum format = formats.second; | |
| 395 GLuint temp_texture = 0; | |
| 396 glGenTextures(1, &temp_texture); | |
| 397 glActiveTexture(GL_TEXTURE0); | |
| 398 glBindTexture(GL_TEXTURE_2D, temp_texture); | |
| 399 { | |
| 400 TRACE_EVENT0("gpu", "performAsyncTexSubImage2D glTexImage2D"); | |
| 401 glTexImage2D(GL_TEXTURE_2D, 0, | |
| 402 internal_format, | |
| 403 size.width(), size.height(), | |
| 404 border, format, type, data); | |
| 405 } | |
| 406 | |
| 407 // Create the EGLImage, as texSubImage always 'orphan's a previous EGLImage. | |
| 408 GLuint level = 0; | |
| 409 EGLDisplay egl_display = eglGetCurrentDisplay(); | |
| 410 EGLContext egl_context = eglGetCurrentContext(); | |
| 411 EGLenum egl_target = EGL_GL_TEXTURE_2D_KHR; | |
| 412 EGLClientBuffer egl_buffer = (EGLClientBuffer) temp_texture; | |
| 413 EGLint egl_attrib_list[] = { | |
| 414 EGL_GL_TEXTURE_LEVEL_KHR, level, // mip-map level to reference. | |
| 415 EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, // preserve the data in the texture. | |
| 416 EGL_NONE | |
| 417 }; | |
| 418 (*egl_image) = eglCreateImageKHR( | |
| 419 egl_display, | |
| 420 egl_context, | |
| 421 egl_target, | |
| 422 egl_buffer, | |
| 423 egl_attrib_list); | |
| 424 | |
| 425 WaitForGlFence(); | |
| 426 | |
| 427 // We can delete this thread's texture as the real texture | |
| 428 // now contains the data. | |
| 429 glDeleteTextures(1, &temp_texture); | |
| 430 } | |
| 431 | |
| 432 void AsyncPixelTransferDelegateAndroid::PerformAsyncTexSubImage2D( | |
| 433 EGLImageKHR egl_image, | |
| 434 gfx::Point offset, | |
| 435 gfx::Size size, | |
| 436 GLenum format, | |
| 437 GLenum type, | |
| 438 const void* data) { | |
| 439 // For a texSubImage, the texture must already have been | |
| 440 // created on the main thread, along with EGLImageKHR. | |
| 441 DCHECK(egl_image); | |
| 442 TRACE_EVENT2("gpu", "performAsyncTexSubImage2D", | |
| 443 "width", size.width(), | |
| 444 "height", size.height()); | |
| 445 | |
| 446 // Create a texture from the image and upload to it. | |
| 447 GLuint temp_texture = 0; | |
| 448 glGenTextures(1, &temp_texture); | |
| 449 glActiveTexture(GL_TEXTURE0); | |
| 450 glBindTexture(GL_TEXTURE_2D, temp_texture); | |
| 451 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image); | |
| 452 { | |
| 453 TRACE_EVENT0("gpu", "performAsyncTexSubImage2D glTexSubImage2D"); | |
| 454 glTexSubImage2D(GL_TEXTURE_2D, 0, | |
| 455 offset.x(), offset.y(), | |
| 456 size.width(), size.height(), | |
| 457 format, type, data); | |
| 458 } | |
| 459 | |
| 460 WaitForGlFence(); | |
| 461 | |
| 462 // We can delete this thread's texture as the real texture | |
| 463 // now contains the data. | |
| 464 glDeleteTextures(1, &temp_texture); | |
| 465 } | |
| 466 | |
| 467 } // namespace gfx | |
| OLD | NEW |