OLD | NEW |
| (Empty) |
1 // Copyright 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 "components/display_compositor/buffer_queue.h" | |
6 | |
7 #include "base/containers/adapters.h" | |
8 #include "base/memory/ptr_util.h" | |
9 #include "build/build_config.h" | |
10 #include "components/display_compositor/gl_helper.h" | |
11 #include "gpu/GLES2/gl2extchromium.h" | |
12 #include "gpu/command_buffer/client/gles2_interface.h" | |
13 #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h" | |
14 #include "gpu/command_buffer/common/gpu_memory_buffer_support.h" | |
15 #include "third_party/skia/include/core/SkRect.h" | |
16 #include "third_party/skia/include/core/SkRegion.h" | |
17 #include "ui/display/types/display_snapshot.h" | |
18 #include "ui/gfx/gpu_memory_buffer.h" | |
19 #include "ui/gfx/skia_util.h" | |
20 | |
21 namespace display_compositor { | |
22 | |
23 BufferQueue::BufferQueue(gpu::gles2::GLES2Interface* gl, | |
24 uint32_t texture_target, | |
25 uint32_t internal_format, | |
26 gfx::BufferFormat format, | |
27 GLHelper* gl_helper, | |
28 gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager, | |
29 gpu::SurfaceHandle surface_handle) | |
30 : gl_(gl), | |
31 fbo_(0), | |
32 allocated_count_(0), | |
33 texture_target_(texture_target), | |
34 internal_format_(internal_format), | |
35 format_(format), | |
36 gl_helper_(gl_helper), | |
37 gpu_memory_buffer_manager_(gpu_memory_buffer_manager), | |
38 surface_handle_(surface_handle) { | |
39 DCHECK(gpu::IsImageFormatCompatibleWithGpuMemoryBufferFormat(internal_format, | |
40 format_)); | |
41 } | |
42 | |
43 BufferQueue::~BufferQueue() { | |
44 FreeAllSurfaces(); | |
45 | |
46 if (fbo_) | |
47 gl_->DeleteFramebuffers(1, &fbo_); | |
48 } | |
49 | |
50 void BufferQueue::Initialize() { | |
51 gl_->GenFramebuffers(1, &fbo_); | |
52 } | |
53 | |
54 void BufferQueue::BindFramebuffer() { | |
55 gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_); | |
56 | |
57 if (!current_surface_) | |
58 current_surface_ = GetNextSurface(); | |
59 | |
60 if (current_surface_) { | |
61 gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, | |
62 texture_target_, current_surface_->texture, 0); | |
63 if (current_surface_->stencil) { | |
64 gl_->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, | |
65 GL_RENDERBUFFER, current_surface_->stencil); | |
66 } | |
67 } | |
68 } | |
69 | |
70 void BufferQueue::CopyBufferDamage(int texture, | |
71 int source_texture, | |
72 const gfx::Rect& new_damage, | |
73 const gfx::Rect& old_damage) { | |
74 gl_helper_->CopySubBufferDamage(texture_target_, texture, source_texture, | |
75 SkRegion(gfx::RectToSkIRect(new_damage)), | |
76 SkRegion(gfx::RectToSkIRect(old_damage))); | |
77 } | |
78 | |
79 void BufferQueue::UpdateBufferDamage(const gfx::Rect& damage) { | |
80 if (displayed_surface_) | |
81 displayed_surface_->damage.Union(damage); | |
82 for (auto& surface : available_surfaces_) | |
83 surface->damage.Union(damage); | |
84 for (auto& surface : in_flight_surfaces_) { | |
85 if (surface) | |
86 surface->damage.Union(damage); | |
87 } | |
88 } | |
89 | |
90 void BufferQueue::SwapBuffers(const gfx::Rect& damage) { | |
91 if (current_surface_) { | |
92 if (damage != gfx::Rect(size_)) { | |
93 // Copy damage from the most recently swapped buffer. In the event that | |
94 // the buffer was destroyed and failed to recreate, pick from the most | |
95 // recently available buffer. | |
96 uint32_t texture_id = 0; | |
97 for (auto& surface : base::Reversed(in_flight_surfaces_)) { | |
98 if (surface) { | |
99 texture_id = surface->texture; | |
100 break; | |
101 } | |
102 } | |
103 if (!texture_id && displayed_surface_) | |
104 texture_id = displayed_surface_->texture; | |
105 | |
106 if (texture_id) { | |
107 CopyBufferDamage(current_surface_->texture, texture_id, damage, | |
108 current_surface_->damage); | |
109 } | |
110 } | |
111 current_surface_->damage = gfx::Rect(); | |
112 } | |
113 UpdateBufferDamage(damage); | |
114 in_flight_surfaces_.push_back(std::move(current_surface_)); | |
115 // Some things reset the framebuffer (CopySubBufferDamage, some GLRenderer | |
116 // paths), so ensure we restore it here. | |
117 gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_); | |
118 } | |
119 | |
120 void BufferQueue::Reshape(const gfx::Size& size, | |
121 float scale_factor, | |
122 const gfx::ColorSpace& color_space, | |
123 bool use_stencil) { | |
124 if (size == size_ && color_space == color_space_ && | |
125 use_stencil == use_stencil_) | |
126 return; | |
127 #if !defined(OS_MACOSX) | |
128 // TODO(ccameron): This assert is being hit on Mac try jobs. Determine if that | |
129 // is cause for concern or if it is benign. | |
130 // http://crbug.com/524624 | |
131 DCHECK(!current_surface_); | |
132 #endif | |
133 size_ = size; | |
134 color_space_ = color_space; | |
135 use_stencil_ = use_stencil; | |
136 | |
137 gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_); | |
138 gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, | |
139 texture_target_, 0, 0); | |
140 gl_->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, | |
141 GL_RENDERBUFFER, 0); | |
142 | |
143 FreeAllSurfaces(); | |
144 } | |
145 | |
146 void BufferQueue::RecreateBuffers() { | |
147 // We need to recreate the buffers, for whatever reason the old ones are not | |
148 // presentable on the device anymore. | |
149 // Unused buffers can be freed directly, they will be re-allocated as needed. | |
150 // Any in flight, current or displayed surface must be replaced. | |
151 available_surfaces_.clear(); | |
152 | |
153 // Recreate all in-flight surfaces and put the recreated copies in the queue. | |
154 for (auto& surface : in_flight_surfaces_) | |
155 surface = RecreateBuffer(std::move(surface)); | |
156 | |
157 current_surface_ = RecreateBuffer(std::move(current_surface_)); | |
158 displayed_surface_ = RecreateBuffer(std::move(displayed_surface_)); | |
159 | |
160 if (current_surface_) { | |
161 // If we have a texture bound, we will need to re-bind it. | |
162 gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_); | |
163 gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, | |
164 texture_target_, current_surface_->texture, 0); | |
165 } | |
166 } | |
167 | |
168 std::unique_ptr<BufferQueue::AllocatedSurface> BufferQueue::RecreateBuffer( | |
169 std::unique_ptr<AllocatedSurface> surface) { | |
170 if (!surface) | |
171 return nullptr; | |
172 | |
173 std::unique_ptr<AllocatedSurface> new_surface(GetNextSurface()); | |
174 if (!new_surface) | |
175 return nullptr; | |
176 | |
177 new_surface->damage = surface->damage; | |
178 | |
179 // Copy the entire texture. | |
180 CopyBufferDamage(new_surface->texture, surface->texture, gfx::Rect(), | |
181 gfx::Rect(size_)); | |
182 return new_surface; | |
183 } | |
184 | |
185 void BufferQueue::PageFlipComplete() { | |
186 // Early out when no surface is in-flight. This can happen when using | |
187 // overlays and page flipping without changing the primary plane. | |
188 if (in_flight_surfaces_.empty()) | |
189 return; | |
190 if (displayed_surface_) | |
191 available_surfaces_.push_back(std::move(displayed_surface_)); | |
192 displayed_surface_ = std::move(in_flight_surfaces_.front()); | |
193 in_flight_surfaces_.pop_front(); | |
194 } | |
195 | |
196 uint32_t BufferQueue::GetCurrentTextureId() const { | |
197 // Return current surface texture if bound. | |
198 if (current_surface_) | |
199 return current_surface_->texture; | |
200 | |
201 // Return in-flight or displayed surface texture if no surface is | |
202 // currently bound. This can happen when using overlays and surface | |
203 // damage is empty. Note: |in_flight_surfaces_| entries can be null | |
204 // as a result of calling FreeAllSurfaces(). | |
205 if (!in_flight_surfaces_.empty() && in_flight_surfaces_.back()) | |
206 return in_flight_surfaces_.back()->texture; | |
207 if (displayed_surface_) | |
208 return displayed_surface_->texture; | |
209 | |
210 return 0; | |
211 } | |
212 | |
213 void BufferQueue::FreeAllSurfaces() { | |
214 displayed_surface_.reset(); | |
215 current_surface_.reset(); | |
216 // This is intentionally not emptied since the swap buffers acks are still | |
217 // expected to arrive. | |
218 for (auto& surface : in_flight_surfaces_) | |
219 surface = nullptr; | |
220 available_surfaces_.clear(); | |
221 } | |
222 | |
223 void BufferQueue::FreeSurfaceResources(AllocatedSurface* surface) { | |
224 if (!surface->texture) | |
225 return; | |
226 | |
227 gl_->BindTexture(texture_target_, surface->texture); | |
228 gl_->ReleaseTexImage2DCHROMIUM(texture_target_, surface->image); | |
229 gl_->DeleteTextures(1, &surface->texture); | |
230 gl_->DestroyImageCHROMIUM(surface->image); | |
231 if (surface->stencil) | |
232 gl_->DeleteRenderbuffers(1, &surface->stencil); | |
233 surface->buffer.reset(); | |
234 allocated_count_--; | |
235 } | |
236 | |
237 std::unique_ptr<BufferQueue::AllocatedSurface> BufferQueue::GetNextSurface() { | |
238 if (!available_surfaces_.empty()) { | |
239 std::unique_ptr<AllocatedSurface> surface = | |
240 std::move(available_surfaces_.back()); | |
241 available_surfaces_.pop_back(); | |
242 return surface; | |
243 } | |
244 | |
245 GLuint texture; | |
246 gl_->GenTextures(1, &texture); | |
247 | |
248 GLuint stencil = 0; | |
249 if (use_stencil_) { | |
250 gl_->GenRenderbuffers(1, &stencil); | |
251 gl_->BindRenderbuffer(GL_RENDERBUFFER, stencil); | |
252 gl_->RenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, size_.width(), | |
253 size_.height()); | |
254 gl_->BindRenderbuffer(GL_RENDERBUFFER, 0); | |
255 } | |
256 | |
257 // We don't want to allow anything more than triple buffering. | |
258 DCHECK_LT(allocated_count_, 4U); | |
259 std::unique_ptr<gfx::GpuMemoryBuffer> buffer( | |
260 gpu_memory_buffer_manager_->CreateGpuMemoryBuffer( | |
261 size_, format_, gfx::BufferUsage::SCANOUT, surface_handle_)); | |
262 if (!buffer.get()) { | |
263 gl_->DeleteTextures(1, &texture); | |
264 DLOG(ERROR) << "Failed to allocate GPU memory buffer"; | |
265 return nullptr; | |
266 } | |
267 buffer->SetColorSpaceForScanout(color_space_); | |
268 | |
269 uint32_t id = | |
270 gl_->CreateImageCHROMIUM(buffer->AsClientBuffer(), size_.width(), | |
271 size_.height(), internal_format_); | |
272 if (!id) { | |
273 LOG(ERROR) << "Failed to allocate backing image surface"; | |
274 gl_->DeleteTextures(1, &texture); | |
275 return nullptr; | |
276 } | |
277 | |
278 allocated_count_++; | |
279 gl_->BindTexture(texture_target_, texture); | |
280 gl_->BindTexImage2DCHROMIUM(texture_target_, id); | |
281 return base::MakeUnique<AllocatedSurface>(this, std::move(buffer), texture, | |
282 id, stencil, gfx::Rect(size_)); | |
283 } | |
284 | |
285 BufferQueue::AllocatedSurface::AllocatedSurface( | |
286 BufferQueue* buffer_queue, | |
287 std::unique_ptr<gfx::GpuMemoryBuffer> buffer, | |
288 uint32_t texture, | |
289 uint32_t image, | |
290 uint32_t stencil, | |
291 const gfx::Rect& rect) | |
292 : buffer_queue(buffer_queue), | |
293 buffer(buffer.release()), | |
294 texture(texture), | |
295 image(image), | |
296 stencil(stencil), | |
297 damage(rect) {} | |
298 | |
299 BufferQueue::AllocatedSurface::~AllocatedSurface() { | |
300 buffer_queue->FreeSurfaceResources(this); | |
301 } | |
302 | |
303 } // namespace display_compositor | |
OLD | NEW |