• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
IsContextValid(ImageTransportHelper * helper)32 bool IsContextValid(ImageTransportHelper* helper) {
33   return helper->stub()->decoder()->GetGLContext()->IsCurrent(NULL);
34 }
35 
36 }  // namespace
37 
TextureImageTransportSurface(GpuChannelManager * manager,GpuCommandBufferStub * stub,const gfx::GLSurfaceHandle & handle)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 
~TextureImageTransportSurface()57 TextureImageTransportSurface::~TextureImageTransportSurface() {
58   DCHECK(stub_destroyed_);
59   Destroy();
60 }
61 
Initialize()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 
Destroy()85 void TextureImageTransportSurface::Destroy() {
86   if (surface_.get())
87     surface_ = NULL;
88 
89   helper_->Destroy();
90 }
91 
DeferDraws()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 
IsOffscreen()106 bool TextureImageTransportSurface::IsOffscreen() {
107   return true;
108 }
109 
GetBackingFrameBufferObject()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 
SetBackbufferAllocation(bool allocation)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 
SetFrontbufferAllocation(bool allocation)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 
GetShareHandle()155 void* TextureImageTransportSurface::GetShareHandle() {
156   return GetHandle();
157 }
158 
GetDisplay()159 void* TextureImageTransportSurface::GetDisplay() {
160   return surface_.get() ? surface_->GetDisplay() : NULL;
161 }
162 
GetConfig()163 void* TextureImageTransportSurface::GetConfig() {
164   return surface_.get() ? surface_->GetConfig() : NULL;
165 }
166 
OnResize(gfx::Size size,float scale_factor)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 
OnWillDestroyStub()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 
SetLatencyInfo(const std::vector<ui::LatencyInfo> & latency_info)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 
WakeUpGpu()208 void TextureImageTransportSurface::WakeUpGpu() {
209   NOTIMPLEMENTED();
210 }
211 
SwapBuffers()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 
PostSubBuffer(int x,int y,int width,int height)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 
SupportsPostSubBuffer()278 bool TextureImageTransportSurface::SupportsPostSubBuffer() {
279   return true;
280 }
281 
GetSize()282 gfx::Size TextureImageTransportSurface::GetSize() {
283   return current_size_;
284 }
285 
GetHandle()286 void* TextureImageTransportSurface::GetHandle() {
287   return surface_.get() ? surface_->GetHandle() : NULL;
288 }
289 
GetFormat()290 unsigned TextureImageTransportSurface::GetFormat() {
291   return surface_.get() ? surface_->GetFormat() : 0;
292 }
293 
OnBufferPresented(const AcceleratedSurfaceMsg_BufferPresented_Params & params)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 
BufferPresentedImpl(const Mailbox & mailbox)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 
ReleaseBackTexture()355 void TextureImageTransportSurface::ReleaseBackTexture() {
356   DCHECK(IsContextValid(helper_.get()));
357   backbuffer_ = NULL;
358   back_mailbox_ = Mailbox();
359   glFlush();
360   CHECK_GL_ERROR();
361 }
362 
ReleaseFrontTexture()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 
CreateBackTexture()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 
AttachBackTextureToFBO()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
461