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