1 // Copyright 2014 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/texture_definition.h" 6 7 #include <list> 8 9 #include "base/memory/linked_ptr.h" 10 #include "base/memory/scoped_ptr.h" 11 #include "base/synchronization/lock.h" 12 #include "gpu/command_buffer/service/texture_manager.h" 13 #include "ui/gl/gl_image.h" 14 #include "ui/gl/gl_implementation.h" 15 #include "ui/gl/scoped_binders.h" 16 17 #if !defined(OS_MACOSX) 18 #include "ui/gl/gl_fence_egl.h" 19 #include "ui/gl/gl_surface_egl.h" 20 #endif 21 22 namespace gpu { 23 namespace gles2 { 24 25 namespace { 26 27 class GLImageSync : public gfx::GLImage { 28 public: 29 explicit GLImageSync(const scoped_refptr<NativeImageBuffer>& buffer, 30 const gfx::Size& size); 31 32 // Implement GLImage. 33 virtual void Destroy() OVERRIDE; 34 virtual gfx::Size GetSize() OVERRIDE; 35 virtual bool BindTexImage(unsigned target) OVERRIDE; 36 virtual void ReleaseTexImage(unsigned target) OVERRIDE; 37 virtual void WillUseTexImage() OVERRIDE; 38 virtual void WillModifyTexImage() OVERRIDE; 39 virtual void DidModifyTexImage() OVERRIDE; 40 41 virtual void DidUseTexImage() OVERRIDE; 42 virtual void SetReleaseAfterUse() OVERRIDE; 43 44 protected: 45 virtual ~GLImageSync(); 46 47 private: 48 scoped_refptr<NativeImageBuffer> buffer_; 49 gfx::Size size_; 50 51 DISALLOW_COPY_AND_ASSIGN(GLImageSync); 52 }; 53 54 GLImageSync::GLImageSync(const scoped_refptr<NativeImageBuffer>& buffer, 55 const gfx::Size& size) 56 : buffer_(buffer), size_(size) { 57 if (buffer) 58 buffer->AddClient(this); 59 } 60 61 GLImageSync::~GLImageSync() { 62 if (buffer_) 63 buffer_->RemoveClient(this); 64 } 65 66 void GLImageSync::Destroy() {} 67 68 gfx::Size GLImageSync::GetSize() { 69 return size_; 70 } 71 72 bool GLImageSync::BindTexImage(unsigned target) { 73 NOTREACHED(); 74 return false; 75 } 76 77 void GLImageSync::ReleaseTexImage(unsigned target) { 78 NOTREACHED(); 79 } 80 81 void GLImageSync::WillUseTexImage() { 82 if (buffer_) 83 buffer_->WillRead(this); 84 } 85 86 void GLImageSync::DidUseTexImage() { 87 if (buffer_) 88 buffer_->DidRead(this); 89 } 90 91 void GLImageSync::WillModifyTexImage() { 92 if (buffer_) 93 buffer_->WillWrite(this); 94 } 95 96 void GLImageSync::DidModifyTexImage() { 97 if (buffer_) 98 buffer_->DidWrite(this); 99 } 100 101 void GLImageSync::SetReleaseAfterUse() { 102 NOTREACHED(); 103 } 104 105 #if !defined(OS_MACOSX) 106 class NativeImageBufferEGL : public NativeImageBuffer { 107 public: 108 static scoped_refptr<NativeImageBufferEGL> Create(GLuint texture_id); 109 110 private: 111 NativeImageBufferEGL(EGLDisplay display, EGLImageKHR image); 112 virtual ~NativeImageBufferEGL(); 113 virtual void AddClient(gfx::GLImage* client) OVERRIDE; 114 virtual void RemoveClient(gfx::GLImage* client) OVERRIDE; 115 virtual bool IsClient(gfx::GLImage* client) OVERRIDE; 116 virtual void BindToTexture(GLenum target) OVERRIDE; 117 virtual void WillRead(gfx::GLImage* client) OVERRIDE; 118 virtual void WillWrite(gfx::GLImage* client) OVERRIDE; 119 virtual void DidRead(gfx::GLImage* client) OVERRIDE; 120 virtual void DidWrite(gfx::GLImage* client) OVERRIDE; 121 122 void ClearCompletedReadFencesLocked(); 123 124 EGLDisplay egl_display_; 125 EGLImageKHR egl_image_; 126 127 base::Lock lock_; 128 129 struct ClientInfo { 130 ClientInfo(gfx::GLImage* client); 131 ~ClientInfo(); 132 133 gfx::GLImage* client; 134 bool needs_wait_before_read; 135 linked_ptr<gfx::GLFence> read_fence; 136 }; 137 std::list<ClientInfo> client_infos_; 138 scoped_ptr<gfx::GLFence> write_fence_; 139 gfx::GLImage* write_client_; 140 141 DISALLOW_COPY_AND_ASSIGN(NativeImageBufferEGL); 142 }; 143 144 scoped_refptr<NativeImageBufferEGL> NativeImageBufferEGL::Create( 145 GLuint texture_id) { 146 EGLDisplay egl_display = gfx::GLSurfaceEGL::GetHardwareDisplay(); 147 EGLContext egl_context = eglGetCurrentContext(); 148 149 DCHECK_NE(EGL_NO_CONTEXT, egl_context); 150 DCHECK_NE(EGL_NO_DISPLAY, egl_display); 151 DCHECK(glIsTexture(texture_id)); 152 153 DCHECK(gfx::g_driver_egl.ext.b_EGL_KHR_image_base && 154 gfx::g_driver_egl.ext.b_EGL_KHR_gl_texture_2D_image && 155 gfx::g_driver_gl.ext.b_GL_OES_EGL_image && 156 gfx::g_driver_egl.ext.b_EGL_KHR_fence_sync); 157 158 const EGLint egl_attrib_list[] = { 159 EGL_GL_TEXTURE_LEVEL_KHR, 0, EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE}; 160 EGLClientBuffer egl_buffer = reinterpret_cast<EGLClientBuffer>(texture_id); 161 EGLenum egl_target = EGL_GL_TEXTURE_2D_KHR; // TODO 162 163 EGLImageKHR egl_image = eglCreateImageKHR( 164 egl_display, egl_context, egl_target, egl_buffer, egl_attrib_list); 165 166 if (egl_image == EGL_NO_IMAGE_KHR) 167 return NULL; 168 169 return new NativeImageBufferEGL(egl_display, egl_image); 170 } 171 172 NativeImageBufferEGL::ClientInfo::ClientInfo(gfx::GLImage* client) 173 : client(client), needs_wait_before_read(true) {} 174 175 NativeImageBufferEGL::ClientInfo::~ClientInfo() {} 176 177 NativeImageBufferEGL::NativeImageBufferEGL(EGLDisplay display, 178 EGLImageKHR image) 179 : NativeImageBuffer(), 180 egl_display_(display), 181 egl_image_(image), 182 write_fence_(new gfx::GLFenceEGL(true)), 183 write_client_(NULL) { 184 DCHECK(egl_display_ != EGL_NO_DISPLAY); 185 DCHECK(egl_image_ != EGL_NO_IMAGE_KHR); 186 } 187 188 NativeImageBufferEGL::~NativeImageBufferEGL() { 189 DCHECK(client_infos_.empty()); 190 if (egl_image_ != EGL_NO_IMAGE_KHR) 191 eglDestroyImageKHR(egl_display_, egl_image_); 192 } 193 194 void NativeImageBufferEGL::AddClient(gfx::GLImage* client) { 195 base::AutoLock lock(lock_); 196 client_infos_.push_back(ClientInfo(client)); 197 } 198 199 void NativeImageBufferEGL::RemoveClient(gfx::GLImage* client) { 200 base::AutoLock lock(lock_); 201 if (write_client_ == client) 202 write_client_ = NULL; 203 for (std::list<ClientInfo>::iterator it = client_infos_.begin(); 204 it != client_infos_.end(); 205 it++) { 206 if (it->client == client) { 207 if (it->read_fence.get()) { 208 it->client = NULL; 209 } else { 210 client_infos_.erase(it); 211 } 212 ClearCompletedReadFencesLocked(); 213 return; 214 } 215 } 216 NOTREACHED(); 217 } 218 219 bool NativeImageBufferEGL::IsClient(gfx::GLImage* client) { 220 base::AutoLock lock(lock_); 221 for (std::list<ClientInfo>::iterator it = client_infos_.begin(); 222 it != client_infos_.end(); 223 it++) { 224 if (it->client == client) 225 return true; 226 } 227 return false; 228 } 229 230 void NativeImageBufferEGL::BindToTexture(GLenum target) { 231 DCHECK(egl_image_ != EGL_NO_IMAGE_KHR); 232 glEGLImageTargetTexture2DOES(target, egl_image_); 233 DCHECK_EQ(static_cast<EGLint>(EGL_SUCCESS), eglGetError()); 234 DCHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); 235 } 236 237 void NativeImageBufferEGL::WillRead(gfx::GLImage* client) { 238 base::AutoLock lock(lock_); 239 if (!write_fence_.get() || write_client_ == client) 240 return; 241 242 for (std::list<ClientInfo>::iterator it = client_infos_.begin(); 243 it != client_infos_.end(); 244 it++) { 245 if (it->client == client) { 246 if (it->needs_wait_before_read) { 247 it->needs_wait_before_read = false; 248 write_fence_->ServerWait(); 249 } 250 return; 251 } 252 } 253 NOTREACHED(); 254 } 255 256 void NativeImageBufferEGL::WillWrite(gfx::GLImage* client) { 257 base::AutoLock lock(lock_); 258 if (write_client_ != client) 259 write_fence_->ServerWait(); 260 261 ClearCompletedReadFencesLocked(); 262 for (std::list<ClientInfo>::iterator it = client_infos_.begin(); 263 it != client_infos_.end(); 264 it++) { 265 if (it->read_fence.get() && it->client != client) 266 it->read_fence->ServerWait(); 267 } 268 } 269 270 void NativeImageBufferEGL::DidRead(gfx::GLImage* client) { 271 base::AutoLock lock(lock_); 272 ClearCompletedReadFencesLocked(); 273 for (std::list<ClientInfo>::iterator it = client_infos_.begin(); 274 it != client_infos_.end(); 275 it++) { 276 if (it->client == client) { 277 it->read_fence = make_linked_ptr(new gfx::GLFenceEGL(true)); 278 return; 279 } 280 } 281 NOTREACHED(); 282 } 283 284 void NativeImageBufferEGL::DidWrite(gfx::GLImage* client) { 285 base::AutoLock lock(lock_); 286 // Sharing semantics require the client to flush in order to make changes 287 // visible to other clients. 288 write_fence_.reset(new gfx::GLFenceEGL(false)); 289 write_client_ = client; 290 for (std::list<ClientInfo>::iterator it = client_infos_.begin(); 291 it != client_infos_.end(); 292 it++) { 293 it->needs_wait_before_read = true; 294 } 295 } 296 297 void NativeImageBufferEGL::ClearCompletedReadFencesLocked() { 298 lock_.AssertAcquired(); 299 std::list<ClientInfo>::iterator it = client_infos_.begin(); 300 while (it != client_infos_.end()) { 301 if (!it->client && it->read_fence->HasCompleted()) { 302 it = client_infos_.erase(it); 303 } else { 304 it++; 305 } 306 } 307 } 308 309 #endif 310 311 class NativeImageBufferStub : public NativeImageBuffer { 312 public: 313 NativeImageBufferStub() : NativeImageBuffer() {} 314 315 private: 316 virtual ~NativeImageBufferStub() {} 317 virtual void AddClient(gfx::GLImage* client) OVERRIDE {} 318 virtual void RemoveClient(gfx::GLImage* client) OVERRIDE {} 319 virtual bool IsClient(gfx::GLImage* client) OVERRIDE { return true; } 320 virtual void BindToTexture(GLenum target) OVERRIDE {} 321 virtual void WillRead(gfx::GLImage* client) OVERRIDE {} 322 virtual void WillWrite(gfx::GLImage* client) OVERRIDE {} 323 virtual void DidRead(gfx::GLImage* client) OVERRIDE {} 324 virtual void DidWrite(gfx::GLImage* client) OVERRIDE {} 325 326 DISALLOW_COPY_AND_ASSIGN(NativeImageBufferStub); 327 }; 328 329 } // anonymous namespace 330 331 // static 332 scoped_refptr<NativeImageBuffer> NativeImageBuffer::Create(GLuint texture_id) { 333 switch (gfx::GetGLImplementation()) { 334 #if !defined(OS_MACOSX) 335 case gfx::kGLImplementationEGLGLES2: 336 return NativeImageBufferEGL::Create(texture_id); 337 #endif 338 case gfx::kGLImplementationMockGL: 339 return new NativeImageBufferStub; 340 default: 341 NOTREACHED(); 342 return NULL; 343 } 344 } 345 346 TextureDefinition::LevelInfo::LevelInfo(GLenum target, 347 GLenum internal_format, 348 GLsizei width, 349 GLsizei height, 350 GLsizei depth, 351 GLint border, 352 GLenum format, 353 GLenum type, 354 bool cleared) 355 : target(target), 356 internal_format(internal_format), 357 width(width), 358 height(height), 359 depth(depth), 360 border(border), 361 format(format), 362 type(type), 363 cleared(cleared) {} 364 365 TextureDefinition::LevelInfo::~LevelInfo() {} 366 367 TextureDefinition::TextureDefinition( 368 GLenum target, 369 Texture* texture, 370 unsigned int version, 371 const scoped_refptr<NativeImageBuffer>& image_buffer) 372 : version_(version), 373 target_(target), 374 image_buffer_(image_buffer ? image_buffer : NativeImageBuffer::Create( 375 texture->service_id())), 376 min_filter_(texture->min_filter()), 377 mag_filter_(texture->mag_filter()), 378 wrap_s_(texture->wrap_s()), 379 wrap_t_(texture->wrap_t()), 380 usage_(texture->usage()), 381 immutable_(texture->IsImmutable()) { 382 383 // TODO 384 DCHECK(!texture->level_infos_.empty()); 385 DCHECK(!texture->level_infos_[0].empty()); 386 DCHECK(!texture->NeedsMips()); 387 DCHECK(texture->level_infos_[0][0].width); 388 DCHECK(texture->level_infos_[0][0].height); 389 390 scoped_refptr<gfx::GLImage> gl_image( 391 new GLImageSync(image_buffer_, 392 gfx::Size(texture->level_infos_[0][0].width, 393 texture->level_infos_[0][0].height))); 394 texture->SetLevelImage(NULL, target, 0, gl_image); 395 396 // TODO: all levels 397 level_infos_.clear(); 398 const Texture::LevelInfo& level = texture->level_infos_[0][0]; 399 LevelInfo info(level.target, 400 level.internal_format, 401 level.width, 402 level.height, 403 level.depth, 404 level.border, 405 level.format, 406 level.type, 407 level.cleared); 408 std::vector<LevelInfo> infos; 409 infos.push_back(info); 410 level_infos_.push_back(infos); 411 } 412 413 TextureDefinition::~TextureDefinition() { 414 } 415 416 Texture* TextureDefinition::CreateTexture() const { 417 if (!image_buffer_) 418 return NULL; 419 420 GLuint texture_id; 421 glGenTextures(1, &texture_id); 422 423 Texture* texture(new Texture(texture_id)); 424 UpdateTexture(texture); 425 426 return texture; 427 } 428 429 void TextureDefinition::UpdateTexture(Texture* texture) const { 430 gfx::ScopedTextureBinder texture_binder(target_, texture->service_id()); 431 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter_); 432 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter_); 433 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s_); 434 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t_); 435 if (image_buffer_) 436 image_buffer_->BindToTexture(target_); 437 // We have to make sure the changes are visible to other clients in this share 438 // group. As far as the clients are concerned, the mailbox semantics only 439 // demand a single flush from the client after changes are first made, 440 // and it is not visible to them when another share group boundary is crossed. 441 // We could probably track this and be a bit smarter about when to flush 442 // though. 443 glFlush(); 444 445 texture->level_infos_.resize(1); 446 for (size_t i = 0; i < level_infos_.size(); i++) { 447 const LevelInfo& base_info = level_infos_[i][0]; 448 const size_t levels_needed = TextureManager::ComputeMipMapCount( 449 base_info.target, base_info.width, base_info.height, base_info.depth); 450 DCHECK(level_infos_.size() <= levels_needed); 451 texture->level_infos_[0].resize(levels_needed); 452 for (size_t n = 0; n < level_infos_.size(); n++) { 453 const LevelInfo& info = level_infos_[i][n]; 454 texture->SetLevelInfo(NULL, 455 info.target, 456 i, 457 info.internal_format, 458 info.width, 459 info.height, 460 info.depth, 461 info.border, 462 info.format, 463 info.type, 464 info.cleared); 465 } 466 } 467 if (image_buffer_) { 468 texture->SetLevelImage( 469 NULL, 470 target_, 471 0, 472 new GLImageSync( 473 image_buffer_, 474 gfx::Size(level_infos_[0][0].width, level_infos_[0][0].height))); 475 } 476 477 texture->target_ = target_; 478 texture->SetImmutable(immutable_); 479 texture->min_filter_ = min_filter_; 480 texture->mag_filter_ = mag_filter_; 481 texture->wrap_s_ = wrap_s_; 482 texture->wrap_t_ = wrap_t_; 483 texture->usage_ = usage_; 484 } 485 486 bool TextureDefinition::Matches(const Texture* texture) const { 487 DCHECK(target_ == texture->target()); 488 if (texture->min_filter_ != min_filter_ || 489 texture->mag_filter_ != mag_filter_ || 490 texture->wrap_s_ != wrap_s_ || 491 texture->wrap_t_ != wrap_t_) { 492 return false; 493 } 494 495 // All structural changes should have orphaned the texture. 496 if (image_buffer_ && !texture->GetLevelImage(texture->target(), 0)) 497 return false; 498 499 return true; 500 } 501 502 } // namespace gles2 503 } // namespace gpu 504