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