Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/exo/buffer.h" | 5 #include "components/exo/buffer.h" |
| 6 | 6 |
| 7 #include <GLES2/gl2.h> | 7 #include <GLES2/gl2.h> |
| 8 #include <GLES2/gl2ext.h> | 8 #include <GLES2/gl2ext.h> |
| 9 #include <GLES2/gl2extchromium.h> | 9 #include <GLES2/gl2extchromium.h> |
| 10 | 10 |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 42 GL_RGB_YCBCR_422_CHROMIUM, // UYVY_422 | 42 GL_RGB_YCBCR_422_CHROMIUM, // UYVY_422 |
| 43 }; | 43 }; |
| 44 static_assert(arraysize(kGLInternalFormats) == | 44 static_assert(arraysize(kGLInternalFormats) == |
| 45 (static_cast<int>(gfx::BufferFormat::LAST) + 1), | 45 (static_cast<int>(gfx::BufferFormat::LAST) + 1), |
| 46 "BufferFormat::LAST must be last value of kGLInternalFormats"); | 46 "BufferFormat::LAST must be last value of kGLInternalFormats"); |
| 47 | 47 |
| 48 DCHECK(format <= gfx::BufferFormat::LAST); | 48 DCHECK(format <= gfx::BufferFormat::LAST); |
| 49 return kGLInternalFormats[static_cast<int>(format)]; | 49 return kGLInternalFormats[static_cast<int>(format)]; |
| 50 } | 50 } |
| 51 | 51 |
| 52 gpu::gles2::GLES2Interface* GetContextGL() { | |
| 53 ui::ContextFactory* context_factory = | |
| 54 aura::Env::GetInstance()->context_factory(); | |
| 55 return context_factory->SharedMainThreadContextProvider()->ContextGL(); | |
| 56 } | |
| 57 | |
| 58 } // namespace | 52 } // namespace |
| 59 | 53 |
| 60 //////////////////////////////////////////////////////////////////////////////// | 54 //////////////////////////////////////////////////////////////////////////////// |
| 61 // Buffer, public: | 55 // Buffer::Texture |
| 62 | 56 |
| 63 Buffer::Buffer(scoped_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer, | 57 // Encapsulates the state and logic needed to bind a buffer to a GLES2 texture. |
| 64 unsigned texture_target) | 58 class Buffer::Texture { |
| 65 : gpu_memory_buffer_(gpu_memory_buffer.Pass()), | 59 public: |
| 60 Texture(gfx::GpuMemoryBuffer* gpu_memory_buffer, | |
| 61 cc::ContextProvider* context_provider, | |
| 62 unsigned texture_target); | |
| 63 ~Texture(); | |
| 64 | |
| 65 // Returns true if GLES2 resources for texture have been lost. | |
| 66 bool IsLost(); | |
| 67 | |
| 68 // Binds the content of gpu memory buffer to the texture returned by | |
| 69 // mailbox(). Returns a sync token that can be used to when accessing texture | |
| 70 // from a different context. | |
| 71 gpu::SyncToken BindTexImage(); | |
| 72 | |
| 73 // Releases the content of gpu memory buffer after |sync_token| has passed. | |
| 74 void ReleaseTexImage(const gpu::SyncToken& sync_token); | |
| 75 | |
| 76 // Returns the mailbox for this texture. | |
| 77 gpu::Mailbox mailbox() const { return mailbox_; } | |
| 78 | |
| 79 private: | |
| 80 scoped_refptr<cc::ContextProvider> context_provider_; | |
| 81 const unsigned texture_target_; | |
| 82 const gfx::Size size_; | |
| 83 unsigned image_id_; | |
| 84 unsigned texture_id_; | |
| 85 gpu::Mailbox mailbox_; | |
| 86 | |
| 87 DISALLOW_COPY_AND_ASSIGN(Texture); | |
| 88 }; | |
| 89 | |
| 90 Buffer::Texture::Texture(gfx::GpuMemoryBuffer* gpu_memory_buffer, | |
| 91 cc::ContextProvider* context_provider, | |
| 92 unsigned texture_target) | |
| 93 : context_provider_(context_provider), | |
| 66 texture_target_(texture_target), | 94 texture_target_(texture_target), |
| 67 texture_id_(0), | 95 size_(gpu_memory_buffer->GetSize()), |
| 68 image_id_(0) { | 96 image_id_(0), |
| 69 gpu::gles2::GLES2Interface* gles2 = GetContextGL(); | 97 texture_id_(0) { |
| 70 // Create an image for |gpu_memory_buffer_|. | 98 gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL(); |
| 71 gfx::Size size = gpu_memory_buffer_->GetSize(); | |
| 72 image_id_ = gles2->CreateImageCHROMIUM( | 99 image_id_ = gles2->CreateImageCHROMIUM( |
| 73 gpu_memory_buffer_->AsClientBuffer(), size.width(), size.height(), | 100 gpu_memory_buffer->AsClientBuffer(), size_.width(), size_.height(), |
| 74 GLInternalFormat(gpu_memory_buffer_->GetFormat())); | 101 GLInternalFormat(gpu_memory_buffer->GetFormat())); |
| 75 // Create a texture with |texture_target_|. | 102 gles2->GenTextures(1, &texture_id_); |
| 76 gles2->ActiveTexture(GL_TEXTURE0); | 103 gles2->ActiveTexture(GL_TEXTURE0); |
| 77 gles2->GenTextures(1, &texture_id_); | |
| 78 gles2->BindTexture(texture_target_, texture_id_); | 104 gles2->BindTexture(texture_target_, texture_id_); |
| 79 gles2->TexParameteri(texture_target_, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | 105 gles2->TexParameteri(texture_target_, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| 80 gles2->TexParameteri(texture_target_, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | 106 gles2->TexParameteri(texture_target_, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| 81 gles2->TexParameteri(texture_target_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | 107 gles2->TexParameteri(texture_target_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| 82 gles2->TexParameteri(texture_target_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | 108 gles2->TexParameteri(texture_target_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| 83 // Generate a crypto-secure random mailbox name. | 109 // Generate a crypto-secure random mailbox name. |
| 84 gles2->GenMailboxCHROMIUM(mailbox_.name); | 110 gles2->GenMailboxCHROMIUM(mailbox_.name); |
| 85 gles2->ProduceTextureCHROMIUM(texture_target_, mailbox_.name); | 111 gles2->ProduceTextureCHROMIUM(texture_target_, mailbox_.name); |
| 86 } | 112 } |
| 87 | 113 |
| 88 Buffer::~Buffer() { | 114 Buffer::Texture::~Texture() { |
| 89 gpu::gles2::GLES2Interface* gles2 = GetContextGL(); | 115 gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL(); |
| 90 if (texture_id_) | 116 gles2->ActiveTexture(GL_TEXTURE0); |
| 91 gles2->DeleteTextures(1, &texture_id_); | 117 gles2->DeleteTextures(1, &texture_id_); |
| 92 if (image_id_) | 118 gles2->DestroyImageCHROMIUM(image_id_); |
| 93 gles2->DestroyImageCHROMIUM(image_id_); | |
| 94 } | 119 } |
| 95 | 120 |
| 96 scoped_ptr<cc::SingleReleaseCallback> Buffer::AcquireTextureMailbox( | 121 bool Buffer::Texture::IsLost() { |
| 122 gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL(); | |
| 123 return gles2->GetGraphicsResetStatusKHR() != GL_NO_ERROR; | |
| 124 } | |
| 125 | |
| 126 gpu::SyncToken Buffer::Texture::BindTexImage() { | |
| 127 gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL(); | |
| 128 gles2->ActiveTexture(GL_TEXTURE0); | |
| 129 gles2->BindTexture(texture_target_, texture_id_); | |
| 130 gles2->BindTexImage2DCHROMIUM(texture_target_, image_id_); | |
| 131 // Create and return a sync token that can be used to ensure that the | |
| 132 // BindTexImage2DCHROMIUM call is processed before issuing any commands | |
| 133 // that will read from the texture on a different context. | |
| 134 uint64 fence_sync = gles2->InsertFenceSyncCHROMIUM(); | |
| 135 gles2->OrderingBarrierCHROMIUM(); | |
| 136 gpu::SyncToken sync_token; | |
| 137 gles2->GenUnverifiedSyncTokenCHROMIUM(fence_sync, sync_token.GetData()); | |
| 138 return sync_token; | |
| 139 } | |
| 140 | |
| 141 void Buffer::Texture::ReleaseTexImage(const gpu::SyncToken& sync_token) { | |
| 142 gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL(); | |
| 143 if (sync_token.HasData()) | |
| 144 gles2->WaitSyncTokenCHROMIUM(sync_token.GetConstData()); | |
| 145 gles2->ActiveTexture(GL_TEXTURE0); | |
| 146 gles2->BindTexture(texture_target_, texture_id_); | |
| 147 gles2->ReleaseTexImage2DCHROMIUM(texture_target_, image_id_); | |
| 148 } | |
| 149 | |
| 150 //////////////////////////////////////////////////////////////////////////////// | |
| 151 // Buffer, public: | |
| 152 | |
| 153 Buffer::Buffer(scoped_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer, | |
| 154 unsigned texture_target) | |
| 155 : gpu_memory_buffer_(gpu_memory_buffer.Pass()), | |
| 156 texture_target_(texture_target), | |
| 157 use_count_(0) {} | |
| 158 | |
| 159 Buffer::~Buffer() {} | |
| 160 | |
| 161 scoped_ptr<cc::SingleReleaseCallback> Buffer::ProduceTextureMailbox( | |
| 97 cc::TextureMailbox* texture_mailbox) { | 162 cc::TextureMailbox* texture_mailbox) { |
| 98 // Buffer can only be used by one client at a time. If texture id is 0, then a | 163 DLOG_IF(WARNING, use_count_) |
| 99 // previous call to AcquireTextureMailbox() is using this buffer and it has | 164 << "Producing a texture mailbox for a buffer that has not been released"; |
| 100 // not been released yet. | 165 |
| 101 if (!texture_id_) { | 166 // Increment the use count for this buffer. |
| 102 DLOG(WARNING) << "Client tried to use a buffer that has not been released"; | 167 ++use_count_; |
| 168 | |
| 169 // Note: This can fail if GPU acceleration has been disabled. | |
| 170 scoped_refptr<cc::ContextProvider> context_provider = | |
| 171 aura::Env::GetInstance() | |
| 172 ->context_factory() | |
| 173 ->SharedMainThreadContextProvider(); | |
| 174 if (!context_provider) { | |
| 175 DLOG(WARNING) << "Failed to acquire a context provider"; | |
| 176 Release(); // Decrements the use count | |
| 103 return nullptr; | 177 return nullptr; |
| 104 } | 178 } |
| 105 | 179 |
| 106 // Take ownerhsip of image and texture ids. | 180 // Creating a new texture is relatively expensive so we reuse the last |
| 107 unsigned texture_id = 0; | 181 // texture whenever possible. |
| 108 unsigned image_id = 0; | 182 scoped_ptr<Texture> texture = last_texture_.Pass(); |
| 109 std::swap(texture_id, texture_id_); | |
| 110 DCHECK_NE(image_id_, 0u); | |
| 111 std::swap(image_id, image_id_); | |
| 112 | 183 |
| 113 // Bind texture to |texture_target_|. | 184 // If texture is lost, destroy it to ensure that we create a new one below. |
| 114 gpu::gles2::GLES2Interface* gles2 = GetContextGL(); | 185 if (texture && texture->IsLost()) |
| 115 gles2->ActiveTexture(GL_TEXTURE0); | 186 texture.reset(); |
|
piman
2015/11/23 21:04:42
I suggest doing this before getting the context_pr
reveman
2015/11/23 23:25:29
Makes sense. Done.
| |
| 116 gles2->BindTexture(texture_target_, texture_id); | |
| 117 | 187 |
| 118 // Bind the image to texture. | 188 // Create a new texture if one doesn't already exist. The contents of this |
| 119 gles2->BindTexImage2DCHROMIUM(texture_target_, image_id); | 189 // buffer can be bound to the texture using a call to BindTexImage and must |
| 190 // be released using a matching ReleaseTexImage call before it can be reused | |
| 191 // or destroyed. | |
| 192 if (!texture) { | |
| 193 texture = make_scoped_ptr(new Texture( | |
| 194 gpu_memory_buffer_.get(), context_provider.get(), texture_target_)); | |
| 195 } | |
| 120 | 196 |
| 121 // Create a sync token to ensure that the BindTexImage2DCHROMIUM call is | 197 // This binds the latest contents of this buffer to the texture. |
| 122 // processed before issuing any commands that will read from texture. | 198 gpu::SyncToken sync_token = texture->BindTexImage(); |
| 123 uint64 fence_sync = gles2->InsertFenceSyncCHROMIUM(); | |
| 124 gles2->ShallowFlushCHROMIUM(); | |
| 125 gpu::SyncToken sync_token; | |
| 126 gles2->GenUnverifiedSyncTokenCHROMIUM(fence_sync, sync_token.GetData()); | |
| 127 | 199 |
| 128 bool is_overlay_candidate = false; | 200 bool is_overlay_candidate = false; |
| 129 *texture_mailbox = | 201 *texture_mailbox = |
| 130 cc::TextureMailbox(mailbox_, sync_token, texture_target_, | 202 cc::TextureMailbox(texture->mailbox(), sync_token, texture_target_, |
| 131 gpu_memory_buffer_->GetSize(), is_overlay_candidate); | 203 gpu_memory_buffer_->GetSize(), is_overlay_candidate); |
| 132 return cc::SingleReleaseCallback::Create( | 204 return cc::SingleReleaseCallback::Create( |
| 133 base::Bind(&Buffer::Release, AsWeakPtr(), texture_target_, | 205 base::Bind(&Buffer::ReleaseTexture, AsWeakPtr(), base::Passed(&texture))); |
| 134 texture_id, image_id)) | |
| 135 .Pass(); | |
| 136 } | 206 } |
| 137 | 207 |
| 138 gfx::Size Buffer::GetSize() const { | 208 gfx::Size Buffer::GetSize() const { |
| 139 return gpu_memory_buffer_->GetSize(); | 209 return gpu_memory_buffer_->GetSize(); |
| 140 } | 210 } |
| 141 | 211 |
| 142 scoped_refptr<base::trace_event::TracedValue> Buffer::AsTracedValue() const { | 212 scoped_refptr<base::trace_event::TracedValue> Buffer::AsTracedValue() const { |
| 143 scoped_refptr<base::trace_event::TracedValue> value = | 213 scoped_refptr<base::trace_event::TracedValue> value = |
| 144 new base::trace_event::TracedValue; | 214 new base::trace_event::TracedValue; |
| 145 value->SetInteger("width", GetSize().width()); | 215 gfx::Size size = gpu_memory_buffer_->GetSize(); |
| 146 value->SetInteger("height", GetSize().height()); | 216 value->SetInteger("width", size.width()); |
| 217 value->SetInteger("height", size.height()); | |
| 147 value->SetInteger("format", | 218 value->SetInteger("format", |
| 148 static_cast<int>(gpu_memory_buffer_->GetFormat())); | 219 static_cast<int>(gpu_memory_buffer_->GetFormat())); |
| 149 return value; | 220 return value; |
| 150 } | 221 } |
| 151 | 222 |
| 152 //////////////////////////////////////////////////////////////////////////////// | 223 //////////////////////////////////////////////////////////////////////////////// |
| 153 // Buffer, private: | 224 // Buffer, private: |
| 154 | 225 |
| 226 void Buffer::Release() { | |
| 227 DCHECK_GT(use_count_, 0u); | |
| 228 if (--use_count_) | |
| 229 return; | |
| 230 | |
| 231 // Run release callback to notify the client that buffer has been released. | |
| 232 if (!release_callback_.is_null()) | |
| 233 release_callback_.Run(); | |
| 234 } | |
| 235 | |
| 155 // static | 236 // static |
| 156 void Buffer::Release(base::WeakPtr<Buffer> buffer, | 237 void Buffer::ReleaseTexture(base::WeakPtr<Buffer> buffer, |
| 157 unsigned texture_target, | 238 scoped_ptr<Texture> texture, |
| 158 unsigned texture_id, | 239 const gpu::SyncToken& sync_token, |
| 159 unsigned image_id, | 240 bool is_lost) { |
| 160 const gpu::SyncToken& sync_token, | 241 TRACE_EVENT1("exo", "Buffer::ReleaseTexture", "is_lost", is_lost); |
| 161 bool is_lost) { | |
| 162 TRACE_EVENT1("exo", "Buffer::Release", "is_lost", is_lost); | |
| 163 | 242 |
| 164 gpu::gles2::GLES2Interface* gles2 = GetContextGL(); | 243 // Release image so it can safely be reused or destroyed. |
| 165 if (sync_token.HasData()) | 244 texture->ReleaseTexImage(sync_token); |
| 166 gles2->WaitSyncTokenCHROMIUM(sync_token.GetConstData()); | |
| 167 gles2->ActiveTexture(GL_TEXTURE0); | |
| 168 gles2->BindTexture(texture_target, texture_id); | |
| 169 gles2->ReleaseTexImage2DCHROMIUM(texture_target, image_id); | |
| 170 | 245 |
| 171 // Delete resources and return if buffer is gone. | 246 // Early out if buffer is gone. This can happen when the client destroyed the |
| 172 if (!buffer) { | 247 // buffer before receiving a release callback. |
| 173 gles2->DeleteTextures(1, &texture_id); | 248 if (!buffer) |
| 174 gles2->DestroyImageCHROMIUM(image_id); | |
| 175 return; | 249 return; |
| 176 } | |
| 177 | 250 |
| 178 DCHECK_EQ(buffer->texture_id_, 0u); | 251 // Allow buffer to reused texture if it's not lost. |
| 179 buffer->texture_id_ = texture_id; | 252 if (!is_lost) |
| 180 DCHECK_EQ(buffer->image_id_, 0u); | 253 buffer->last_texture_ = texture.Pass(); |
| 181 buffer->image_id_ = image_id; | |
| 182 | 254 |
| 183 if (!buffer->release_callback_.is_null()) | 255 buffer->Release(); |
| 184 buffer->release_callback_.Run(); | |
| 185 } | 256 } |
| 186 | 257 |
| 187 } // namespace exo | 258 } // namespace exo |
| OLD | NEW |