Chromium Code Reviews| 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 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 NativeImageBufferEGL* NativeImageBufferEGL::Create(GLuint texture_id) { | |
|
piman
2014/03/13 04:41:58
nit: return a scoped_refptr<NativeImageBufferEGL>
no sievers
2014/03/13 20:50:15
Done.
| |
| 115 EGLDisplay egl_display = gfx::GLSurfaceEGL::GetHardwareDisplay(); | |
| 116 EGLContext egl_context = eglGetCurrentContext(); | |
| 117 | |
| 118 if (egl_context == EGL_NO_CONTEXT || egl_display == EGL_NO_DISPLAY || | |
| 119 !glIsTexture(texture_id)) { | |
| 120 return NULL; | |
| 121 } | |
|
piman
2014/03/13 04:41:58
Can these conditions happen? Should they just be D
no sievers
2014/03/13 20:50:15
You mean no context or display, right?
piman
2014/03/13 23:29:06
Yes, thanks.
| |
| 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(EGL_SUCCESS, (EGLint)eglGetError()); | |
| 159 DCHECK_EQ(GL_NO_ERROR, (GLint)glGetError()); | |
|
piman
2014/03/13 04:41:58
nit: no c-style casts. Prefer static_cast or reint
no sievers
2014/03/13 20:50:15
Done.
| |
| 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 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): This is super-risky. We need to somehow find out | |
| 283 // about when the current context gets flushed, so that we will only | |
| 284 // ever wait on the write fence (esp. from another context) if it was | |
| 285 // 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. | |
|
piman
2014/03/13 04:41:58
So, the risk is that if we're missing a sync point
no sievers
2014/03/13 20:50:15
Can I owe this as an immediate follow-up?
I was al
piman
2014/03/13 23:29:06
Maybe file a bug and reference here... It's not so
no sievers
2014/03/13 23:51:13
Done.
| |
| 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(GLenum target, | |
| 319 Texture* texture, | |
| 320 unsigned int version, | |
| 321 NativeImageBuffer* image) | |
| 322 : version_(version), | |
| 323 target_(target), | |
| 324 image_(image ? image : NativeImageBuffer::Create(texture->service_id())), | |
| 325 min_filter_(texture->min_filter()), | |
| 326 mag_filter_(texture->mag_filter()), | |
| 327 wrap_s_(texture->wrap_s()), | |
| 328 wrap_t_(texture->wrap_t()), | |
| 329 usage_(texture->usage()), | |
| 330 immutable_(texture->IsImmutable()) { | |
| 331 | |
| 332 // TODO | |
| 333 DCHECK(!texture->level_infos_.empty()); | |
| 334 DCHECK(!texture->level_infos_[0].empty()); | |
| 335 DCHECK(!texture->NeedsMips()); | |
| 336 DCHECK(texture->level_infos_[0][0].width); | |
| 337 DCHECK(texture->level_infos_[0][0].height); | |
| 338 | |
| 339 scoped_refptr<gfx::GLImage> gl_image(new GLImageSync(image_)); | |
| 340 texture->SetLevelImage(NULL, target, 0, gl_image); | |
| 341 | |
| 342 // TODO: all levels | |
| 343 level_infos_.clear(); | |
| 344 const Texture::LevelInfo& level = texture->level_infos_[0][0]; | |
| 345 LevelInfo info(level.target, | |
| 346 level.internal_format, | |
| 347 level.width, | |
| 348 level.height, | |
| 349 level.depth, | |
| 350 level.border, | |
| 351 level.format, | |
| 352 level.type, | |
| 353 level.cleared); | |
| 354 std::vector<LevelInfo> infos; | |
| 355 infos.push_back(info); | |
| 356 level_infos_.push_back(infos); | |
| 357 | |
| 358 } | |
| 359 | |
| 360 TextureDefinition::~TextureDefinition() { | |
| 361 } | |
| 362 | |
| 363 Texture* TextureDefinition::CreateTexture() const { | |
| 364 if (!image_) | |
| 365 return NULL; | |
| 366 | |
| 367 GLuint texture_id; | |
| 368 glGenTextures(1, &texture_id); | |
| 369 | |
| 370 Texture* texture(new Texture(texture_id)); | |
| 371 UpdateTexture(texture); | |
| 372 | |
| 373 return texture; | |
| 374 } | |
| 375 | |
| 376 void TextureDefinition::UpdateTexture(Texture* texture) const { | |
| 377 gfx::ScopedTextureBinder texture_binder(target_, texture->service_id()); | |
| 378 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter_); | |
| 379 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter_); | |
| 380 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s_); | |
| 381 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t_); | |
| 382 DCHECK(image_); | |
| 383 if (image_) | |
|
piman
2014/03/13 04:41:58
DCHECK, or if? Chose one.
no sievers
2014/03/13 20:50:15
Done. Should eventually handle incomplete textures
| |
| 384 image_->BindToTexture(target_); | |
| 385 glFlush(); | |
|
piman
2014/03/13 04:41:58
DO you need this glFlush?
no sievers
2014/03/13 20:50:15
Most of the time probably not. There is one case I
piman
2014/03/13 23:29:06
I see... arguably, ctx2a needs to flush after the
no sievers
2014/03/13 23:51:13
Done. Maybe we could be smarter about when the flu
| |
| 386 | |
| 387 texture->level_infos_.resize(1); | |
| 388 for (size_t i = 0; i < level_infos_.size(); i++) { | |
| 389 const LevelInfo& base_info = level_infos_[i][0]; | |
| 390 const size_t levels_needed = TextureManager::ComputeMipMapCount( | |
| 391 base_info.target, base_info.width, base_info.height, base_info.depth); | |
| 392 DCHECK(level_infos_.size() <= levels_needed); | |
| 393 texture->level_infos_[0].resize(levels_needed); | |
| 394 for (size_t n = 0; n < level_infos_.size(); n++) { | |
| 395 const LevelInfo& info = level_infos_[i][n]; | |
| 396 texture->SetLevelInfo(NULL, | |
| 397 info.target, | |
| 398 i, | |
| 399 info.internal_format, | |
| 400 info.width, | |
| 401 info.height, | |
| 402 info.depth, | |
| 403 info.border, | |
| 404 info.format, | |
| 405 info.type, | |
| 406 info.cleared); | |
| 407 } | |
| 408 } | |
| 409 if (image_) | |
| 410 texture->SetLevelImage(NULL, target_, 0, new GLImageSync(image_)); | |
| 411 | |
| 412 texture->target_ = target_; | |
| 413 texture->SetImmutable(immutable_); | |
| 414 texture->min_filter_ = min_filter_; | |
| 415 texture->mag_filter_ = mag_filter_; | |
| 416 texture->wrap_s_ = wrap_s_; | |
| 417 texture->wrap_t_ = wrap_t_; | |
| 418 texture->usage_ = usage_; | |
| 419 } | |
| 420 | |
| 421 bool TextureDefinition::Matches(const Texture* texture) const { | |
| 422 DCHECK(target_ == texture->target()); | |
| 423 if (texture->min_filter_ != min_filter_ || | |
| 424 texture->mag_filter_ != mag_filter_ || | |
| 425 texture->wrap_s_ != wrap_s_ || | |
| 426 texture->wrap_t_ != wrap_t_) { | |
| 427 return false; | |
| 428 } | |
| 429 | |
| 430 // All structural changes should have orphaned the texture. | |
| 431 if (image_ && !texture->GetLevelImage(texture->target(), 0)) | |
| 432 return false; | |
| 433 | |
| 434 return true; | |
| 435 } | |
| 436 | |
| 437 } // namespace gles2 | |
| 438 } // namespace gpu | |
| OLD | NEW |