OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 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 // TODO: Need to generate and check EGL_KHR_gl_texture_2D_image |
| 124 if (!gfx::g_driver_egl.ext.b_EGL_KHR_image_base || |
| 125 !gfx::g_driver_gl.ext.b_GL_OES_EGL_image) { |
| 126 return NULL; |
| 127 } |
| 128 |
| 129 const EGLint egl_attrib_list[] = { |
| 130 EGL_GL_TEXTURE_LEVEL_KHR, 0, EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE}; |
| 131 EGLClientBuffer egl_buffer = reinterpret_cast<EGLClientBuffer>(texture_id); |
| 132 EGLenum egl_target = EGL_GL_TEXTURE_2D_KHR; // TODO |
| 133 |
| 134 EGLImageKHR egl_image = eglCreateImageKHR( |
| 135 egl_display, egl_context, egl_target, egl_buffer, egl_attrib_list); |
| 136 |
| 137 if (egl_image == EGL_NO_IMAGE_KHR) |
| 138 return NULL; |
| 139 |
| 140 return new NativeImageBufferEGL(egl_display, egl_image); |
| 141 } |
| 142 |
| 143 NativeImageBufferEGL::NativeImageBufferEGL(EGLDisplay display, |
| 144 EGLImageKHR image) |
| 145 : egl_display_(display), egl_image_(image) { |
| 146 DCHECK(egl_display_ != EGL_NO_DISPLAY); |
| 147 DCHECK(egl_image_ != EGL_NO_IMAGE_KHR); |
| 148 } |
| 149 |
| 150 NativeImageBufferEGL::~NativeImageBufferEGL() { |
| 151 if (egl_image_ != EGL_NO_IMAGE_KHR) |
| 152 eglDestroyImageKHR(egl_display_, egl_image_); |
| 153 } |
| 154 |
| 155 void NativeImageBufferEGL::BindToTexture(GLenum target) { |
| 156 DCHECK(egl_image_ != EGL_NO_IMAGE_KHR); |
| 157 glEGLImageTargetTexture2DOES(target, egl_image_); |
| 158 DCHECK_EQ(static_cast<EGLint>(EGL_SUCCESS), eglGetError()); |
| 159 DCHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); |
| 160 } |
| 161 #endif |
| 162 |
| 163 class NativeImageBufferStub : public NativeImageBuffer { |
| 164 public: |
| 165 NativeImageBufferStub() {} |
| 166 |
| 167 private: |
| 168 virtual ~NativeImageBufferStub() {} |
| 169 virtual void BindToTexture(GLenum target) OVERRIDE {} |
| 170 |
| 171 DISALLOW_COPY_AND_ASSIGN(NativeImageBufferStub); |
| 172 }; |
| 173 |
| 174 } // anonymous namespace |
| 175 |
| 176 // static |
| 177 scoped_refptr<NativeImageBuffer> NativeImageBuffer::Create(GLuint texture_id) { |
| 178 switch (gfx::GetGLImplementation()) { |
| 179 #if !defined(OS_MACOSX) |
| 180 case gfx::kGLImplementationEGLGLES2: |
| 181 return NativeImageBufferEGL::Create(texture_id); |
| 182 #endif |
| 183 case gfx::kGLImplementationMockGL: |
| 184 return new NativeImageBufferStub; |
| 185 default: |
| 186 NOTREACHED(); |
| 187 return NULL; |
| 188 } |
| 189 } |
| 190 |
| 191 NativeImageBuffer::ClientInfo::ClientInfo(gfx::GLImage* client) |
| 192 : client(client), needs_wait_before_read(true) {} |
| 193 |
| 194 NativeImageBuffer::ClientInfo::~ClientInfo() {} |
| 195 |
| 196 NativeImageBuffer::NativeImageBuffer() : write_client_(NULL) { |
| 197 write_fence_.reset(gfx::GLFence::Create()); |
| 198 } |
| 199 |
| 200 NativeImageBuffer::~NativeImageBuffer() { |
| 201 DCHECK(client_infos_.empty()); |
| 202 } |
| 203 |
| 204 void NativeImageBuffer::AddClient(gfx::GLImage* client) { |
| 205 base::AutoLock lock(lock_); |
| 206 client_infos_.push_back(ClientInfo(client)); |
| 207 } |
| 208 |
| 209 void NativeImageBuffer::RemoveClient(gfx::GLImage* client) { |
| 210 base::AutoLock lock(lock_); |
| 211 if (write_client_ == client) |
| 212 write_client_ = NULL; |
| 213 for (std::list<ClientInfo>::iterator it = client_infos_.begin(); |
| 214 it != client_infos_.end(); |
| 215 it++) { |
| 216 if (it->client == client) { |
| 217 client_infos_.erase(it); |
| 218 return; |
| 219 } |
| 220 } |
| 221 NOTREACHED(); |
| 222 } |
| 223 |
| 224 bool NativeImageBuffer::IsClient(gfx::GLImage* client) { |
| 225 base::AutoLock lock(lock_); |
| 226 for (std::list<ClientInfo>::iterator it = client_infos_.begin(); |
| 227 it != client_infos_.end(); |
| 228 it++) { |
| 229 if (it->client == client) |
| 230 return true; |
| 231 } |
| 232 return false; |
| 233 } |
| 234 |
| 235 void NativeImageBuffer::WillRead(gfx::GLImage* client) { |
| 236 base::AutoLock lock(lock_); |
| 237 if (!write_fence_.get() || write_client_ == client) |
| 238 return; |
| 239 |
| 240 for (std::list<ClientInfo>::iterator it = client_infos_.begin(); |
| 241 it != client_infos_.end(); |
| 242 it++) { |
| 243 if (it->client == client) { |
| 244 if (it->needs_wait_before_read) { |
| 245 it->needs_wait_before_read = false; |
| 246 write_fence_->ServerWait(); |
| 247 } |
| 248 return; |
| 249 } |
| 250 } |
| 251 NOTREACHED(); |
| 252 } |
| 253 |
| 254 void NativeImageBuffer::WillWrite(gfx::GLImage* client) { |
| 255 base::AutoLock lock(lock_); |
| 256 if (write_client_ != client) |
| 257 write_fence_->ServerWait(); |
| 258 |
| 259 for (std::list<ClientInfo>::iterator it = client_infos_.begin(); |
| 260 it != client_infos_.end(); |
| 261 it++) { |
| 262 if (it->read_fence.get() && it->client != client) |
| 263 it->read_fence->ServerWait(); |
| 264 } |
| 265 } |
| 266 |
| 267 void NativeImageBuffer::DidRead(gfx::GLImage* client) { |
| 268 base::AutoLock lock(lock_); |
| 269 for (std::list<ClientInfo>::iterator it = client_infos_.begin(); |
| 270 it != client_infos_.end(); |
| 271 it++) { |
| 272 if (it->client == client) { |
| 273 it->read_fence = make_linked_ptr(gfx::GLFence::Create()); |
| 274 return; |
| 275 } |
| 276 } |
| 277 NOTREACHED(); |
| 278 } |
| 279 |
| 280 void NativeImageBuffer::DidWrite(gfx::GLImage* client) { |
| 281 base::AutoLock lock(lock_); |
| 282 // TODO(sievers): crbug.com/352419 |
| 283 // This is super-risky. We need to somehow find out about when the current |
| 284 // context gets flushed, so that we will only ever wait on the write fence |
| 285 // (esp. from another context) if it was flushed and is guaranteed to clear. |
| 286 // On the other hand, proactively flushing here is not feasible in terms |
| 287 // of perf when there are multiple draw calls per frame. |
| 288 write_fence_.reset(gfx::GLFence::CreateWithoutFlush()); |
| 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 TextureDefinition::LevelInfo::LevelInfo(GLenum target, |
| 298 GLenum internal_format, |
| 299 GLsizei width, |
| 300 GLsizei height, |
| 301 GLsizei depth, |
| 302 GLint border, |
| 303 GLenum format, |
| 304 GLenum type, |
| 305 bool cleared) |
| 306 : target(target), |
| 307 internal_format(internal_format), |
| 308 width(width), |
| 309 height(height), |
| 310 depth(depth), |
| 311 border(border), |
| 312 format(format), |
| 313 type(type), |
| 314 cleared(cleared) {} |
| 315 |
| 316 TextureDefinition::LevelInfo::~LevelInfo() {} |
| 317 |
| 318 TextureDefinition::TextureDefinition( |
| 319 GLenum target, |
| 320 Texture* texture, |
| 321 unsigned int version, |
| 322 const scoped_refptr<NativeImageBuffer>& image_buffer) |
| 323 : version_(version), |
| 324 target_(target), |
| 325 image_buffer_(image_buffer ? image_buffer : NativeImageBuffer::Create( |
| 326 texture->service_id())), |
| 327 min_filter_(texture->min_filter()), |
| 328 mag_filter_(texture->mag_filter()), |
| 329 wrap_s_(texture->wrap_s()), |
| 330 wrap_t_(texture->wrap_t()), |
| 331 usage_(texture->usage()), |
| 332 immutable_(texture->IsImmutable()) { |
| 333 |
| 334 // TODO |
| 335 DCHECK(!texture->level_infos_.empty()); |
| 336 DCHECK(!texture->level_infos_[0].empty()); |
| 337 DCHECK(!texture->NeedsMips()); |
| 338 DCHECK(texture->level_infos_[0][0].width); |
| 339 DCHECK(texture->level_infos_[0][0].height); |
| 340 |
| 341 scoped_refptr<gfx::GLImage> gl_image(new GLImageSync(image_buffer_)); |
| 342 texture->SetLevelImage(NULL, target, 0, gl_image); |
| 343 |
| 344 // TODO: all levels |
| 345 level_infos_.clear(); |
| 346 const Texture::LevelInfo& level = texture->level_infos_[0][0]; |
| 347 LevelInfo info(level.target, |
| 348 level.internal_format, |
| 349 level.width, |
| 350 level.height, |
| 351 level.depth, |
| 352 level.border, |
| 353 level.format, |
| 354 level.type, |
| 355 level.cleared); |
| 356 std::vector<LevelInfo> infos; |
| 357 infos.push_back(info); |
| 358 level_infos_.push_back(infos); |
| 359 |
| 360 } |
| 361 |
| 362 TextureDefinition::~TextureDefinition() { |
| 363 } |
| 364 |
| 365 Texture* TextureDefinition::CreateTexture() const { |
| 366 if (!image_buffer_) |
| 367 return NULL; |
| 368 |
| 369 GLuint texture_id; |
| 370 glGenTextures(1, &texture_id); |
| 371 |
| 372 Texture* texture(new Texture(texture_id)); |
| 373 UpdateTexture(texture); |
| 374 |
| 375 return texture; |
| 376 } |
| 377 |
| 378 void TextureDefinition::UpdateTexture(Texture* texture) const { |
| 379 gfx::ScopedTextureBinder texture_binder(target_, texture->service_id()); |
| 380 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter_); |
| 381 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter_); |
| 382 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s_); |
| 383 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t_); |
| 384 if (image_buffer_) |
| 385 image_buffer_->BindToTexture(target_); |
| 386 // We have to make sure the changes are visible to other clients in this share |
| 387 // group. As far as the clients are concerned, the mailbox semantics only |
| 388 // demand a single flush from the client after changes are first made, |
| 389 // and it is not visible to them when another share group boundary is crossed. |
| 390 // We could probably track this and be a bit smarter about when to flush |
| 391 // though. |
| 392 glFlush(); |
| 393 |
| 394 texture->level_infos_.resize(1); |
| 395 for (size_t i = 0; i < level_infos_.size(); i++) { |
| 396 const LevelInfo& base_info = level_infos_[i][0]; |
| 397 const size_t levels_needed = TextureManager::ComputeMipMapCount( |
| 398 base_info.target, base_info.width, base_info.height, base_info.depth); |
| 399 DCHECK(level_infos_.size() <= levels_needed); |
| 400 texture->level_infos_[0].resize(levels_needed); |
| 401 for (size_t n = 0; n < level_infos_.size(); n++) { |
| 402 const LevelInfo& info = level_infos_[i][n]; |
| 403 texture->SetLevelInfo(NULL, |
| 404 info.target, |
| 405 i, |
| 406 info.internal_format, |
| 407 info.width, |
| 408 info.height, |
| 409 info.depth, |
| 410 info.border, |
| 411 info.format, |
| 412 info.type, |
| 413 info.cleared); |
| 414 } |
| 415 } |
| 416 if (image_buffer_) |
| 417 texture->SetLevelImage(NULL, target_, 0, new GLImageSync(image_buffer_)); |
| 418 |
| 419 texture->target_ = target_; |
| 420 texture->SetImmutable(immutable_); |
| 421 texture->min_filter_ = min_filter_; |
| 422 texture->mag_filter_ = mag_filter_; |
| 423 texture->wrap_s_ = wrap_s_; |
| 424 texture->wrap_t_ = wrap_t_; |
| 425 texture->usage_ = usage_; |
| 426 } |
| 427 |
| 428 bool TextureDefinition::Matches(const Texture* texture) const { |
| 429 DCHECK(target_ == texture->target()); |
| 430 if (texture->min_filter_ != min_filter_ || |
| 431 texture->mag_filter_ != mag_filter_ || |
| 432 texture->wrap_s_ != wrap_s_ || |
| 433 texture->wrap_t_ != wrap_t_) { |
| 434 return false; |
| 435 } |
| 436 |
| 437 // All structural changes should have orphaned the texture. |
| 438 if (image_buffer_ && !texture->GetLevelImage(texture->target(), 0)) |
| 439 return false; |
| 440 |
| 441 return true; |
| 442 } |
| 443 |
| 444 } // namespace gles2 |
| 445 } // namespace gpu |
OLD | NEW |