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_; |
103 return nullptr; | 168 |
| 169 // Creating a new texture is relatively expensive so we reuse the last |
| 170 // texture whenever possible. |
| 171 scoped_ptr<Texture> texture = last_texture_.Pass(); |
| 172 |
| 173 // If texture is lost, destroy it to ensure that we create a new one below. |
| 174 if (texture && texture->IsLost()) |
| 175 texture.reset(); |
| 176 |
| 177 // Create a new texture if one doesn't already exist. The contents of this |
| 178 // buffer can be bound to the texture using a call to BindTexImage and must |
| 179 // be released using a matching ReleaseTexImage call before it can be reused |
| 180 // or destroyed. |
| 181 if (!texture) { |
| 182 // Note: This can fail if GPU acceleration has been disabled. |
| 183 scoped_refptr<cc::ContextProvider> context_provider = |
| 184 aura::Env::GetInstance() |
| 185 ->context_factory() |
| 186 ->SharedMainThreadContextProvider(); |
| 187 if (!context_provider) { |
| 188 DLOG(WARNING) << "Failed to acquire a context provider"; |
| 189 Release(); // Decrements the use count |
| 190 return nullptr; |
| 191 } |
| 192 texture = make_scoped_ptr(new Texture( |
| 193 gpu_memory_buffer_.get(), context_provider.get(), texture_target_)); |
104 } | 194 } |
105 | 195 |
106 // Take ownerhsip of image and texture ids. | 196 // This binds the latest contents of this buffer to the texture. |
107 unsigned texture_id = 0; | 197 gpu::SyncToken sync_token = texture->BindTexImage(); |
108 unsigned image_id = 0; | |
109 std::swap(texture_id, texture_id_); | |
110 DCHECK_NE(image_id_, 0u); | |
111 std::swap(image_id, image_id_); | |
112 | |
113 // Bind texture to |texture_target_|. | |
114 gpu::gles2::GLES2Interface* gles2 = GetContextGL(); | |
115 gles2->ActiveTexture(GL_TEXTURE0); | |
116 gles2->BindTexture(texture_target_, texture_id); | |
117 | |
118 // Bind the image to texture. | |
119 gles2->BindTexImage2DCHROMIUM(texture_target_, image_id); | |
120 | |
121 // Create a sync token to ensure that the BindTexImage2DCHROMIUM call is | |
122 // processed before issuing any commands that will read from texture. | |
123 uint64 fence_sync = gles2->InsertFenceSyncCHROMIUM(); | |
124 gles2->ShallowFlushCHROMIUM(); | |
125 gpu::SyncToken sync_token; | |
126 gles2->GenUnverifiedSyncTokenCHROMIUM(fence_sync, sync_token.GetData()); | |
127 | 198 |
128 bool is_overlay_candidate = false; | 199 bool is_overlay_candidate = false; |
129 *texture_mailbox = | 200 *texture_mailbox = |
130 cc::TextureMailbox(mailbox_, sync_token, texture_target_, | 201 cc::TextureMailbox(texture->mailbox(), sync_token, texture_target_, |
131 gpu_memory_buffer_->GetSize(), is_overlay_candidate); | 202 gpu_memory_buffer_->GetSize(), is_overlay_candidate); |
132 return cc::SingleReleaseCallback::Create( | 203 return cc::SingleReleaseCallback::Create( |
133 base::Bind(&Buffer::Release, AsWeakPtr(), texture_target_, | 204 base::Bind(&Buffer::ReleaseTexture, AsWeakPtr(), base::Passed(&texture))); |
134 texture_id, image_id)) | |
135 .Pass(); | |
136 } | 205 } |
137 | 206 |
138 gfx::Size Buffer::GetSize() const { | 207 gfx::Size Buffer::GetSize() const { |
139 return gpu_memory_buffer_->GetSize(); | 208 return gpu_memory_buffer_->GetSize(); |
140 } | 209 } |
141 | 210 |
142 scoped_refptr<base::trace_event::TracedValue> Buffer::AsTracedValue() const { | 211 scoped_refptr<base::trace_event::TracedValue> Buffer::AsTracedValue() const { |
143 scoped_refptr<base::trace_event::TracedValue> value = | 212 scoped_refptr<base::trace_event::TracedValue> value = |
144 new base::trace_event::TracedValue; | 213 new base::trace_event::TracedValue; |
145 value->SetInteger("width", GetSize().width()); | 214 gfx::Size size = gpu_memory_buffer_->GetSize(); |
146 value->SetInteger("height", GetSize().height()); | 215 value->SetInteger("width", size.width()); |
| 216 value->SetInteger("height", size.height()); |
147 value->SetInteger("format", | 217 value->SetInteger("format", |
148 static_cast<int>(gpu_memory_buffer_->GetFormat())); | 218 static_cast<int>(gpu_memory_buffer_->GetFormat())); |
149 return value; | 219 return value; |
150 } | 220 } |
151 | 221 |
152 //////////////////////////////////////////////////////////////////////////////// | 222 //////////////////////////////////////////////////////////////////////////////// |
153 // Buffer, private: | 223 // Buffer, private: |
154 | 224 |
| 225 void Buffer::Release() { |
| 226 DCHECK_GT(use_count_, 0u); |
| 227 if (--use_count_) |
| 228 return; |
| 229 |
| 230 // Run release callback to notify the client that buffer has been released. |
| 231 if (!release_callback_.is_null()) |
| 232 release_callback_.Run(); |
| 233 } |
| 234 |
155 // static | 235 // static |
156 void Buffer::Release(base::WeakPtr<Buffer> buffer, | 236 void Buffer::ReleaseTexture(base::WeakPtr<Buffer> buffer, |
157 unsigned texture_target, | 237 scoped_ptr<Texture> texture, |
158 unsigned texture_id, | 238 const gpu::SyncToken& sync_token, |
159 unsigned image_id, | 239 bool is_lost) { |
160 const gpu::SyncToken& sync_token, | 240 TRACE_EVENT1("exo", "Buffer::ReleaseTexture", "is_lost", is_lost); |
161 bool is_lost) { | |
162 TRACE_EVENT1("exo", "Buffer::Release", "is_lost", is_lost); | |
163 | 241 |
164 gpu::gles2::GLES2Interface* gles2 = GetContextGL(); | 242 // Release image so it can safely be reused or destroyed. |
165 if (sync_token.HasData()) | 243 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 | 244 |
171 // Delete resources and return if buffer is gone. | 245 // Early out if buffer is gone. This can happen when the client destroyed the |
172 if (!buffer) { | 246 // buffer before receiving a release callback. |
173 gles2->DeleteTextures(1, &texture_id); | 247 if (!buffer) |
174 gles2->DestroyImageCHROMIUM(image_id); | |
175 return; | 248 return; |
176 } | |
177 | 249 |
178 DCHECK_EQ(buffer->texture_id_, 0u); | 250 // Allow buffer to reused texture if it's not lost. |
179 buffer->texture_id_ = texture_id; | 251 if (!is_lost) |
180 DCHECK_EQ(buffer->image_id_, 0u); | 252 buffer->last_texture_ = texture.Pass(); |
181 buffer->image_id_ = image_id; | |
182 | 253 |
183 if (!buffer->release_callback_.is_null()) | 254 buffer->Release(); |
184 buffer->release_callback_.Run(); | |
185 } | 255 } |
186 | 256 |
187 } // namespace exo | 257 } // namespace exo |
OLD | NEW |