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