OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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 "content/common/gpu/texture_image_transport_surface.h" | |
6 | |
7 #include <string> | |
8 #include <vector> | |
9 | |
10 #include "base/command_line.h" | |
11 #include "content/common/gpu/gpu_channel.h" | |
12 #include "content/common/gpu/gpu_channel_manager.h" | |
13 #include "content/common/gpu/gpu_messages.h" | |
14 #include "content/common/gpu/sync_point_manager.h" | |
15 #include "content/public/common/content_switches.h" | |
16 #include "gpu/command_buffer/service/context_group.h" | |
17 #include "gpu/command_buffer/service/gpu_scheduler.h" | |
18 #include "gpu/command_buffer/service/mailbox_manager.h" | |
19 #include "ui/gl/scoped_binders.h" | |
20 | |
21 using gpu::gles2::ContextGroup; | |
22 using gpu::gles2::GLES2Decoder; | |
23 using gpu::gles2::MailboxManager; | |
24 using gpu::gles2::Texture; | |
25 using gpu::gles2::TextureManager; | |
26 using gpu::gles2::TextureRef; | |
27 using gpu::Mailbox; | |
28 | |
29 namespace content { | |
30 namespace { | |
31 | |
32 bool IsContextValid(ImageTransportHelper* helper) { | |
33 return helper->stub()->decoder()->GetGLContext()->IsCurrent(NULL); | |
34 } | |
35 | |
36 } // namespace | |
37 | |
38 TextureImageTransportSurface::TextureImageTransportSurface( | |
39 GpuChannelManager* manager, | |
40 GpuCommandBufferStub* stub, | |
41 const gfx::GLSurfaceHandle& handle) | |
42 : fbo_id_(0), | |
43 current_size_(1, 1), | |
44 scale_factor_(1.f), | |
45 stub_destroyed_(false), | |
46 backbuffer_suggested_allocation_(true), | |
47 frontbuffer_suggested_allocation_(true), | |
48 handle_(handle), | |
49 is_swap_buffers_pending_(false), | |
50 did_unschedule_(false) { | |
51 helper_.reset(new ImageTransportHelper(this, | |
52 manager, | |
53 stub, | |
54 gfx::kNullPluginWindow)); | |
55 } | |
56 | |
57 TextureImageTransportSurface::~TextureImageTransportSurface() { | |
58 DCHECK(stub_destroyed_); | |
59 Destroy(); | |
60 } | |
61 | |
62 bool TextureImageTransportSurface::Initialize() { | |
63 mailbox_manager_ = | |
64 helper_->stub()->decoder()->GetContextGroup()->mailbox_manager(); | |
65 | |
66 GpuChannelManager* manager = helper_->manager(); | |
67 surface_ = manager->GetDefaultOffscreenSurface(); | |
68 if (!surface_.get()) | |
69 return false; | |
70 | |
71 if (!helper_->Initialize()) | |
72 return false; | |
73 | |
74 GpuChannel* parent_channel = manager->LookupChannel(handle_.parent_client_id); | |
75 if (parent_channel) { | |
76 const base::CommandLine* command_line = | |
77 base::CommandLine::ForCurrentProcess(); | |
78 if (command_line->HasSwitch(switches::kUIPrioritizeInGpuProcess)) | |
79 helper_->SetPreemptByFlag(parent_channel->GetPreemptionFlag()); | |
80 } | |
81 | |
82 return true; | |
83 } | |
84 | |
85 void TextureImageTransportSurface::Destroy() { | |
86 if (surface_.get()) | |
87 surface_ = NULL; | |
88 | |
89 helper_->Destroy(); | |
90 } | |
91 | |
92 bool TextureImageTransportSurface::DeferDraws() { | |
93 // The command buffer hit a draw/clear command that could clobber the | |
94 // texture in use by the UI compositor. If a Swap is pending, abort | |
95 // processing of the command by returning true and unschedule until the Swap | |
96 // Ack arrives. | |
97 DCHECK(!did_unschedule_); | |
98 if (is_swap_buffers_pending_) { | |
99 did_unschedule_ = true; | |
100 helper_->SetScheduled(false); | |
101 return true; | |
102 } | |
103 return false; | |
104 } | |
105 | |
106 bool TextureImageTransportSurface::IsOffscreen() { | |
107 return true; | |
108 } | |
109 | |
110 unsigned int TextureImageTransportSurface::GetBackingFrameBufferObject() { | |
111 DCHECK(IsContextValid(helper_.get())); | |
112 if (!fbo_id_) { | |
113 glGenFramebuffersEXT(1, &fbo_id_); | |
114 glBindFramebufferEXT(GL_FRAMEBUFFER, fbo_id_); | |
115 helper_->stub()->AddDestructionObserver(this); | |
116 CreateBackTexture(); | |
117 } | |
118 | |
119 return fbo_id_; | |
120 } | |
121 | |
122 bool TextureImageTransportSurface::SetBackbufferAllocation(bool allocation) { | |
123 DCHECK(!is_swap_buffers_pending_); | |
124 if (backbuffer_suggested_allocation_ == allocation) | |
125 return true; | |
126 backbuffer_suggested_allocation_ = allocation; | |
127 | |
128 if (backbuffer_suggested_allocation_) { | |
129 DCHECK(!backbuffer_.get()); | |
130 CreateBackTexture(); | |
131 } else { | |
132 ReleaseBackTexture(); | |
133 } | |
134 | |
135 return true; | |
136 } | |
137 | |
138 void TextureImageTransportSurface::SetFrontbufferAllocation(bool allocation) { | |
139 if (frontbuffer_suggested_allocation_ == allocation) | |
140 return; | |
141 frontbuffer_suggested_allocation_ = allocation; | |
142 | |
143 // If a swapbuffers is in flight, wait for the ack before releasing the front | |
144 // buffer: | |
145 // - we don't know yet which texture the browser will want to keep | |
146 // - we want to ensure we don't destroy a texture that is in flight before the | |
147 // browser got a reference on it. | |
148 if (!frontbuffer_suggested_allocation_ && | |
149 !is_swap_buffers_pending_ && | |
150 helper_->MakeCurrent()) { | |
151 ReleaseFrontTexture(); | |
152 } | |
153 } | |
154 | |
155 void* TextureImageTransportSurface::GetShareHandle() { | |
156 return GetHandle(); | |
157 } | |
158 | |
159 void* TextureImageTransportSurface::GetDisplay() { | |
160 return surface_.get() ? surface_->GetDisplay() : NULL; | |
161 } | |
162 | |
163 void* TextureImageTransportSurface::GetConfig() { | |
164 return surface_.get() ? surface_->GetConfig() : NULL; | |
165 } | |
166 | |
167 void TextureImageTransportSurface::OnResize(gfx::Size size, | |
168 float scale_factor) { | |
169 DCHECK_GE(size.width(), 1); | |
170 DCHECK_GE(size.height(), 1); | |
171 current_size_ = size; | |
172 scale_factor_ = scale_factor; | |
173 if (backbuffer_suggested_allocation_) | |
174 CreateBackTexture(); | |
175 } | |
176 | |
177 void TextureImageTransportSurface::OnWillDestroyStub() { | |
178 bool have_context = IsContextValid(helper_.get()); | |
179 helper_->stub()->RemoveDestructionObserver(this); | |
180 | |
181 // We are losing the stub owning us, this is our last chance to clean up the | |
182 // resources we allocated in the stub's context. | |
183 if (have_context) { | |
184 ReleaseBackTexture(); | |
185 ReleaseFrontTexture(); | |
186 } else { | |
187 backbuffer_ = NULL; | |
188 back_mailbox_ = Mailbox(); | |
189 frontbuffer_ = NULL; | |
190 front_mailbox_ = Mailbox(); | |
191 } | |
192 | |
193 if (fbo_id_ && have_context) { | |
194 glDeleteFramebuffersEXT(1, &fbo_id_); | |
195 CHECK_GL_ERROR(); | |
196 } | |
197 fbo_id_ = 0; | |
198 | |
199 stub_destroyed_ = true; | |
200 } | |
201 | |
202 void TextureImageTransportSurface::SetLatencyInfo( | |
203 const std::vector<ui::LatencyInfo>& latency_info) { | |
204 for (size_t i = 0; i < latency_info.size(); i++) | |
205 latency_info_.push_back(latency_info[i]); | |
206 } | |
207 | |
208 void TextureImageTransportSurface::WakeUpGpu() { | |
209 NOTIMPLEMENTED(); | |
210 } | |
211 | |
212 bool TextureImageTransportSurface::SwapBuffers() { | |
213 DCHECK(IsContextValid(helper_.get())); | |
214 DCHECK(backbuffer_suggested_allocation_); | |
215 | |
216 if (!frontbuffer_suggested_allocation_) | |
217 return true; | |
218 | |
219 if (!backbuffer_.get()) { | |
220 LOG(ERROR) << "Swap without valid backing."; | |
221 return true; | |
222 } | |
223 | |
224 DCHECK(backbuffer_size() == current_size_); | |
225 GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params; | |
226 params.size = backbuffer_size(); | |
227 params.scale_factor = scale_factor_; | |
228 params.mailbox = back_mailbox_; | |
229 | |
230 glFlush(); | |
231 | |
232 params.latency_info.swap(latency_info_); | |
233 helper_->SendAcceleratedSurfaceBuffersSwapped(params); | |
234 | |
235 DCHECK(!is_swap_buffers_pending_); | |
236 is_swap_buffers_pending_ = true; | |
237 return true; | |
238 } | |
239 | |
240 bool TextureImageTransportSurface::PostSubBuffer( | |
241 int x, int y, int width, int height) { | |
242 DCHECK(IsContextValid(helper_.get())); | |
243 DCHECK(backbuffer_suggested_allocation_); | |
244 if (!frontbuffer_suggested_allocation_) | |
245 return true; | |
246 const gfx::Rect new_damage_rect(x, y, width, height); | |
247 DCHECK(gfx::Rect(gfx::Point(), current_size_).Contains(new_damage_rect)); | |
248 | |
249 // An empty damage rect is a successful no-op. | |
250 if (new_damage_rect.IsEmpty()) | |
251 return true; | |
252 | |
253 if (!backbuffer_.get()) { | |
254 LOG(ERROR) << "Swap without valid backing."; | |
255 return true; | |
256 } | |
257 | |
258 DCHECK(current_size_ == backbuffer_size()); | |
259 GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params params; | |
260 params.surface_size = backbuffer_size(); | |
261 params.surface_scale_factor = scale_factor_; | |
262 params.x = x; | |
263 params.y = y; | |
264 params.width = width; | |
265 params.height = height; | |
266 params.mailbox = back_mailbox_; | |
267 | |
268 glFlush(); | |
269 | |
270 params.latency_info.swap(latency_info_); | |
271 helper_->SendAcceleratedSurfacePostSubBuffer(params); | |
272 | |
273 DCHECK(!is_swap_buffers_pending_); | |
274 is_swap_buffers_pending_ = true; | |
275 return true; | |
276 } | |
277 | |
278 bool TextureImageTransportSurface::SupportsPostSubBuffer() { | |
279 return true; | |
280 } | |
281 | |
282 gfx::Size TextureImageTransportSurface::GetSize() { | |
283 return current_size_; | |
284 } | |
285 | |
286 void* TextureImageTransportSurface::GetHandle() { | |
287 return surface_.get() ? surface_->GetHandle() : NULL; | |
288 } | |
289 | |
290 unsigned TextureImageTransportSurface::GetFormat() { | |
291 return surface_.get() ? surface_->GetFormat() : 0; | |
292 } | |
293 | |
294 void TextureImageTransportSurface::OnBufferPresented( | |
295 const AcceleratedSurfaceMsg_BufferPresented_Params& params) { | |
296 if (params.sync_point == 0) { | |
297 BufferPresentedImpl(params.mailbox); | |
298 } else { | |
299 helper_->manager()->sync_point_manager()->AddSyncPointCallback( | |
300 params.sync_point, | |
301 base::Bind(&TextureImageTransportSurface::BufferPresentedImpl, | |
302 this, | |
303 params.mailbox)); | |
304 } | |
305 } | |
306 | |
307 void TextureImageTransportSurface::BufferPresentedImpl(const Mailbox& mailbox) { | |
308 DCHECK(is_swap_buffers_pending_); | |
309 is_swap_buffers_pending_ = false; | |
310 | |
311 // When we wait for a sync point, we may get called back after the stub is | |
312 // destroyed. In that case there's no need to do anything with the returned | |
313 // mailbox. | |
314 if (stub_destroyed_) | |
315 return; | |
316 | |
317 // We should not have allowed the backbuffer to be discarded while the ack | |
318 // was pending. | |
319 DCHECK(backbuffer_suggested_allocation_); | |
320 DCHECK(backbuffer_.get()); | |
321 | |
322 bool swap = true; | |
323 if (!mailbox.IsZero()) { | |
324 if (mailbox == back_mailbox_) { | |
325 // The browser has skipped the frame to unblock the GPU process, waiting | |
326 // for one of the right size, and returned the back buffer, so don't swap. | |
327 swap = false; | |
328 } | |
329 } | |
330 if (swap) { | |
331 std::swap(backbuffer_, frontbuffer_); | |
332 std::swap(back_mailbox_, front_mailbox_); | |
333 } | |
334 | |
335 // We're relying on the fact that the parent context is | |
336 // finished with its context when it inserts the sync point that | |
337 // triggers this callback. | |
338 if (helper_->MakeCurrent()) { | |
339 if (frontbuffer_.get() && !frontbuffer_suggested_allocation_) | |
340 ReleaseFrontTexture(); | |
341 if (!backbuffer_.get() || backbuffer_size() != current_size_) | |
342 CreateBackTexture(); | |
343 else | |
344 AttachBackTextureToFBO(); | |
345 } | |
346 | |
347 // Even if MakeCurrent fails, schedule anyway, to trigger the lost context | |
348 // logic. | |
349 if (did_unschedule_) { | |
350 did_unschedule_ = false; | |
351 helper_->SetScheduled(true); | |
352 } | |
353 } | |
354 | |
355 void TextureImageTransportSurface::ReleaseBackTexture() { | |
356 DCHECK(IsContextValid(helper_.get())); | |
357 backbuffer_ = NULL; | |
358 back_mailbox_ = Mailbox(); | |
359 glFlush(); | |
360 CHECK_GL_ERROR(); | |
361 } | |
362 | |
363 void TextureImageTransportSurface::ReleaseFrontTexture() { | |
364 DCHECK(IsContextValid(helper_.get())); | |
365 frontbuffer_ = NULL; | |
366 front_mailbox_ = Mailbox(); | |
367 glFlush(); | |
368 CHECK_GL_ERROR(); | |
369 helper_->SendAcceleratedSurfaceRelease(); | |
370 } | |
371 | |
372 void TextureImageTransportSurface::CreateBackTexture() { | |
373 DCHECK(IsContextValid(helper_.get())); | |
374 // If |is_swap_buffers_pending| we are waiting for our backbuffer | |
375 // in the mailbox, so we shouldn't be reallocating it now. | |
376 DCHECK(!is_swap_buffers_pending_); | |
377 | |
378 if (backbuffer_.get() && backbuffer_size() == current_size_) | |
379 return; | |
380 | |
381 VLOG(1) << "Allocating new backbuffer texture"; | |
382 | |
383 GLES2Decoder* decoder = helper_->stub()->decoder(); | |
384 TextureManager* texture_manager = | |
385 decoder->GetContextGroup()->texture_manager(); | |
386 if (!backbuffer_.get()) { | |
387 back_mailbox_ = gpu::Mailbox::Generate(); | |
388 GLuint service_id; | |
389 glGenTextures(1, &service_id); | |
390 backbuffer_ = TextureRef::Create(texture_manager, 0, service_id); | |
391 texture_manager->SetTarget(backbuffer_.get(), GL_TEXTURE_2D); | |
392 Texture* texture = texture_manager->Produce(backbuffer_.get()); | |
393 mailbox_manager_->ProduceTexture(GL_TEXTURE_2D, back_mailbox_, texture); | |
394 } | |
395 | |
396 { | |
397 gfx::ScopedTextureBinder texture_binder(GL_TEXTURE_2D, | |
398 backbuffer_->service_id()); | |
399 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, | |
400 current_size_.width(), current_size_.height(), 0, | |
401 GL_RGBA, GL_UNSIGNED_BYTE, NULL); | |
402 gpu::gles2::ErrorState* error_state = decoder->GetErrorState(); | |
403 texture_manager->SetParameteri("Backbuffer", | |
404 error_state, | |
405 backbuffer_.get(), | |
406 GL_TEXTURE_MIN_FILTER, | |
407 GL_LINEAR); | |
408 texture_manager->SetParameteri("Backbuffer", | |
409 error_state, | |
410 backbuffer_.get(), | |
411 GL_TEXTURE_MAG_FILTER, | |
412 GL_LINEAR); | |
413 texture_manager->SetParameteri("Backbuffer", | |
414 error_state, | |
415 backbuffer_.get(), | |
416 GL_TEXTURE_WRAP_S, | |
417 GL_CLAMP_TO_EDGE); | |
418 texture_manager->SetParameteri("Backbuffer", | |
419 error_state, | |
420 backbuffer_.get(), | |
421 GL_TEXTURE_WRAP_T, | |
422 GL_CLAMP_TO_EDGE); | |
423 texture_manager->SetLevelInfo(backbuffer_.get(), | |
424 GL_TEXTURE_2D, | |
425 0, | |
426 GL_RGBA, | |
427 current_size_.width(), | |
428 current_size_.height(), | |
429 1, | |
430 0, | |
431 GL_RGBA, | |
432 GL_UNSIGNED_BYTE, | |
433 true); | |
434 DCHECK(texture_manager->CanRender(backbuffer_.get())); | |
435 CHECK_GL_ERROR(); | |
436 } | |
437 | |
438 AttachBackTextureToFBO(); | |
439 } | |
440 | |
441 void TextureImageTransportSurface::AttachBackTextureToFBO() { | |
442 DCHECK(IsContextValid(helper_.get())); | |
443 DCHECK(backbuffer_.get()); | |
444 gfx::ScopedFrameBufferBinder fbo_binder(fbo_id_); | |
445 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, | |
446 GL_COLOR_ATTACHMENT0, | |
447 GL_TEXTURE_2D, | |
448 backbuffer_->service_id(), | |
449 0); | |
450 CHECK_GL_ERROR(); | |
451 | |
452 #ifndef NDEBUG | |
453 GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER); | |
454 if (status != GL_FRAMEBUFFER_COMPLETE) { | |
455 DLOG(FATAL) << "Framebuffer incomplete: " << status; | |
456 } | |
457 #endif | |
458 } | |
459 | |
460 } // namespace content | |
OLD | NEW |