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