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 |