texture_image_transport_surface.cc revision a93a17c8d99d686bd4a1511e5504e5e6cc9fcadf
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
20using gpu::gles2::ContextGroup;
21using gpu::gles2::MailboxManager;
22using gpu::gles2::MailboxName;
23using gpu::gles2::TextureDefinition;
24using gpu::gles2::TextureManager;
25
26namespace content {
27
28TextureImageTransportSurface::TextureImageTransportSurface(
29    GpuChannelManager* manager,
30    GpuCommandBufferStub* stub,
31    const gfx::GLSurfaceHandle& handle)
32      : fbo_id_(0),
33        backbuffer_(CreateTextureDefinition(gfx::Size(), 0)),
34        stub_destroyed_(false),
35        backbuffer_suggested_allocation_(true),
36        frontbuffer_suggested_allocation_(true),
37        handle_(handle),
38        is_swap_buffers_pending_(false),
39        did_unschedule_(false) {
40  helper_.reset(new ImageTransportHelper(this,
41                                         manager,
42                                         stub,
43                                         gfx::kNullPluginWindow));
44}
45
46TextureImageTransportSurface::~TextureImageTransportSurface() {
47  DCHECK(stub_destroyed_);
48  Destroy();
49}
50
51bool TextureImageTransportSurface::Initialize() {
52  mailbox_manager_ =
53      helper_->stub()->decoder()->GetContextGroup()->mailbox_manager();
54
55  GpuChannelManager* manager = helper_->manager();
56  surface_ = manager->GetDefaultOffscreenSurface();
57  if (!surface_)
58    return false;
59
60  if (!helper_->Initialize())
61    return false;
62
63  GpuChannel* parent_channel = manager->LookupChannel(handle_.parent_client_id);
64  if (parent_channel) {
65    const CommandLine* command_line = CommandLine::ForCurrentProcess();
66    if (command_line->HasSwitch(switches::kUIPrioritizeInGpuProcess))
67      helper_->SetPreemptByFlag(parent_channel->GetPreemptionFlag());
68  }
69
70  return true;
71}
72
73void TextureImageTransportSurface::Destroy() {
74  if (surface_)
75    surface_ = NULL;
76
77  helper_->Destroy();
78}
79
80bool TextureImageTransportSurface::DeferDraws() {
81  // The command buffer hit a draw/clear command that could clobber the
82  // texture in use by the UI compositor. If a Swap is pending, abort
83  // processing of the command by returning true and unschedule until the Swap
84  // Ack arrives.
85  DCHECK(!did_unschedule_);
86  if (is_swap_buffers_pending_) {
87    did_unschedule_ = true;
88    helper_->SetScheduled(false);
89    return true;
90  }
91  return false;
92}
93
94bool TextureImageTransportSurface::Resize(const gfx::Size&) {
95  return true;
96}
97
98bool TextureImageTransportSurface::IsOffscreen() {
99  return true;
100}
101
102bool TextureImageTransportSurface::OnMakeCurrent(gfx::GLContext* context) {
103  if (stub_destroyed_) {
104    // Early-exit so that we don't recreate the fbo. We still want to return
105    // true, so that the context is made current and the GLES2DecoderImpl can
106    // release its own resources.
107    return true;
108  }
109
110  context_ = context;
111
112  if (!fbo_id_) {
113    glGenFramebuffersEXT(1, &fbo_id_);
114    glBindFramebufferEXT(GL_FRAMEBUFFER, fbo_id_);
115    current_size_ = gfx::Size(1, 1);
116    helper_->stub()->AddDestructionObserver(this);
117  }
118
119  // We could be receiving non-deferred GL commands, that is anything that does
120  // not need a framebuffer.
121  if (!backbuffer_->service_id() && !is_swap_buffers_pending_ &&
122      backbuffer_suggested_allocation_) {
123    CreateBackTexture();
124  }
125  return true;
126}
127
128unsigned int TextureImageTransportSurface::GetBackingFrameBufferObject() {
129  return fbo_id_;
130}
131
132bool TextureImageTransportSurface::SetBackbufferAllocation(bool allocation) {
133  DCHECK(!is_swap_buffers_pending_);
134  if (backbuffer_suggested_allocation_ == allocation)
135     return true;
136  backbuffer_suggested_allocation_ = allocation;
137
138  if (backbuffer_suggested_allocation_) {
139    DCHECK(!backbuffer_->service_id());
140    CreateBackTexture();
141  } else {
142    ReleaseBackTexture();
143  }
144
145  return true;
146}
147
148void TextureImageTransportSurface::SetFrontbufferAllocation(bool allocation) {
149  if (frontbuffer_suggested_allocation_ == allocation)
150    return;
151  frontbuffer_suggested_allocation_ = allocation;
152
153  if (!frontbuffer_suggested_allocation_) {
154    GpuHostMsg_AcceleratedSurfaceRelease_Params params;
155    helper_->SendAcceleratedSurfaceRelease(params);
156  }
157}
158
159void* TextureImageTransportSurface::GetShareHandle() {
160  return GetHandle();
161}
162
163void* TextureImageTransportSurface::GetDisplay() {
164  return surface_.get() ? surface_->GetDisplay() : NULL;
165}
166
167void* TextureImageTransportSurface::GetConfig() {
168  return surface_.get() ? surface_->GetConfig() : NULL;
169}
170
171void TextureImageTransportSurface::OnResize(gfx::Size size) {
172  current_size_ = size;
173  CreateBackTexture();
174}
175
176void TextureImageTransportSurface::OnWillDestroyStub() {
177  helper_->stub()->RemoveDestructionObserver(this);
178
179  GpuHostMsg_AcceleratedSurfaceRelease_Params params;
180  helper_->SendAcceleratedSurfaceRelease(params);
181
182  ReleaseBackTexture();
183
184  // We are losing the stub owning us, this is our last chance to clean up the
185  // resources we allocated in the stub's context.
186  if (fbo_id_) {
187    glDeleteFramebuffersEXT(1, &fbo_id_);
188    CHECK_GL_ERROR();
189    fbo_id_ = 0;
190  }
191
192  stub_destroyed_ = true;
193}
194
195void TextureImageTransportSurface::SetLatencyInfo(
196    const cc::LatencyInfo& latency_info) {
197  latency_info_ = latency_info;
198}
199
200bool TextureImageTransportSurface::SwapBuffers() {
201  DCHECK(backbuffer_suggested_allocation_);
202
203  if (!frontbuffer_suggested_allocation_)
204    return true;
205
206  if (!backbuffer_->service_id()) {
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.mailbox_name.assign(
215      reinterpret_cast<const char*>(&mailbox_name_), sizeof(mailbox_name_));
216
217  glFlush();
218  ProduceTexture();
219
220  // Do not allow destruction while we are still waiting for a swap ACK,
221  // so we do not leak a texture in the mailbox.
222  AddRef();
223
224  params.latency_info = latency_info_;
225  helper_->SendAcceleratedSurfaceBuffersSwapped(params);
226
227  DCHECK(!is_swap_buffers_pending_);
228  is_swap_buffers_pending_ = true;
229  return true;
230}
231
232bool TextureImageTransportSurface::PostSubBuffer(
233    int x, int y, int width, int height) {
234  DCHECK(backbuffer_suggested_allocation_);
235  if (!frontbuffer_suggested_allocation_)
236    return true;
237  const gfx::Rect new_damage_rect(x, y, width, height);
238  DCHECK(gfx::Rect(gfx::Point(), current_size_).Contains(new_damage_rect));
239
240  // An empty damage rect is a successful no-op.
241  if (new_damage_rect.IsEmpty())
242    return true;
243
244  if (!backbuffer_->service_id()) {
245    LOG(ERROR) << "Swap without valid backing.";
246    return true;
247  }
248
249  DCHECK(current_size_ == backbuffer_size());
250  GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params params;
251  params.surface_size = backbuffer_size();
252  params.x = x;
253  params.y = y;
254  params.width = width;
255  params.height = height;
256  params.mailbox_name.assign(
257      reinterpret_cast<const char*>(&mailbox_name_), sizeof(mailbox_name_));
258
259  glFlush();
260  ProduceTexture();
261
262  // Do not allow destruction while we are still waiting for a swap ACK,
263  // so we do not leak a texture in the mailbox.
264  AddRef();
265
266  params.latency_info = latency_info_;
267  helper_->SendAcceleratedSurfacePostSubBuffer(params);
268
269  DCHECK(!is_swap_buffers_pending_);
270  is_swap_buffers_pending_ = true;
271  return true;
272}
273
274std::string TextureImageTransportSurface::GetExtensions() {
275  std::string extensions = gfx::GLSurface::GetExtensions();
276  extensions += extensions.empty() ? "" : " ";
277  extensions += "GL_CHROMIUM_front_buffer_cached ";
278  extensions += "GL_CHROMIUM_post_sub_buffer";
279  return extensions;
280}
281
282gfx::Size TextureImageTransportSurface::GetSize() {
283  gfx::Size size = current_size_;
284
285  // OSMesa expects a non-zero size.
286  return gfx::Size(size.width() == 0 ? 1 : size.width(),
287                   size.height() == 0 ? 1 : size.height());
288}
289
290void* TextureImageTransportSurface::GetHandle() {
291  return surface_.get() ? surface_->GetHandle() : NULL;
292}
293
294unsigned TextureImageTransportSurface::GetFormat() {
295  return surface_.get() ? surface_->GetFormat() : 0;
296}
297
298void TextureImageTransportSurface::OnBufferPresented(
299    const AcceleratedSurfaceMsg_BufferPresented_Params& params) {
300  if (params.sync_point == 0) {
301    BufferPresentedImpl(params.mailbox_name);
302  } else {
303    helper_->manager()->sync_point_manager()->AddSyncPointCallback(
304        params.sync_point,
305        base::Bind(&TextureImageTransportSurface::BufferPresentedImpl,
306                   this,
307                   params.mailbox_name));
308  }
309
310  // Careful, we might get deleted now if we were only waiting for
311  // a final swap ACK.
312  Release();
313}
314
315void TextureImageTransportSurface::BufferPresentedImpl(
316    const std::string& mailbox_name) {
317  DCHECK(!backbuffer_->service_id());
318  if (!mailbox_name.empty()) {
319    DCHECK(mailbox_name.length() == GL_MAILBOX_SIZE_CHROMIUM);
320    mailbox_name.copy(reinterpret_cast<char *>(&mailbox_name_),
321                      sizeof(MailboxName));
322    ConsumeTexture();
323  }
324
325  if (stub_destroyed_ && backbuffer_->service_id()) {
326    // TODO(sievers): Remove this after changes to the mailbox to take ownership
327    // of the service ids.
328    DCHECK(context_.get() && surface_.get());
329    uint32 service_id = backbuffer_->ReleaseServiceId();
330    if (context_->MakeCurrent(surface_))
331      glDeleteTextures(1, &service_id);
332
333    return;
334  }
335
336  DCHECK(is_swap_buffers_pending_);
337  is_swap_buffers_pending_ = false;
338
339  // We should not have allowed the backbuffer to be discarded while the ack
340  // was pending.
341  DCHECK(backbuffer_suggested_allocation_);
342
343  // We're relying on the fact that the parent context is
344  // finished with it's context when it inserts the sync point that
345  // triggers this callback.
346  if (helper_->MakeCurrent()) {
347    if (backbuffer_size() != current_size_ || !backbuffer_->service_id())
348      CreateBackTexture();
349    else
350      AttachBackTextureToFBO();
351  }
352
353  // Even if MakeCurrent fails, schedule anyway, to trigger the lost context
354  // logic.
355  if (did_unschedule_) {
356    did_unschedule_ = false;
357    helper_->SetScheduled(true);
358  }
359}
360
361void TextureImageTransportSurface::OnResizeViewACK() {
362  NOTREACHED();
363}
364
365void TextureImageTransportSurface::ReleaseBackTexture() {
366  if (!backbuffer_->service_id())
367    return;
368
369  uint32 service_id = backbuffer_->ReleaseServiceId();
370  glDeleteTextures(1, &service_id);
371  backbuffer_.reset(CreateTextureDefinition(gfx::Size(), 0));
372  mailbox_name_ = MailboxName();
373  glFlush();
374  CHECK_GL_ERROR();
375}
376
377void TextureImageTransportSurface::CreateBackTexture() {
378  // If |is_swap_buffers_pending| we are waiting for our backbuffer
379  // in the mailbox, so we shouldn't be reallocating it now.
380  DCHECK(!is_swap_buffers_pending_);
381
382  if (backbuffer_->service_id() && backbuffer_size() == current_size_)
383    return;
384
385  uint32 service_id = backbuffer_->ReleaseServiceId();
386
387  VLOG(1) << "Allocating new backbuffer texture";
388
389  // On Qualcomm we couldn't resize an FBO texture past a certain
390  // size, after we allocated it as 1x1. So here we simply delete
391  // the previous texture on resize, to insure we don't 'run out of
392  // memory'.
393  if (service_id &&
394      helper_->stub()
395             ->decoder()
396             ->GetContextGroup()
397             ->feature_info()
398             ->workarounds()
399             .delete_instead_of_resize_fbo) {
400    glDeleteTextures(1, &service_id);
401    service_id = 0;
402    mailbox_name_ = MailboxName();
403  }
404
405  if (!service_id) {
406    MailboxName new_mailbox_name;
407    MailboxName& name = mailbox_name_;
408    // This slot should be uninitialized.
409    DCHECK(!memcmp(&name, &new_mailbox_name, sizeof(MailboxName)));
410    mailbox_manager_->GenerateMailboxName(&new_mailbox_name);
411    name = new_mailbox_name;
412    glGenTextures(1, &service_id);
413  }
414
415  backbuffer_.reset(
416      CreateTextureDefinition(current_size_, service_id));
417
418  {
419    gfx::ScopedTextureBinder texture_binder(GL_TEXTURE_2D, service_id);
420    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
421    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
422    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
423    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
424    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
425        current_size_.width(), current_size_.height(), 0,
426        GL_RGBA, GL_UNSIGNED_BYTE, NULL);
427    CHECK_GL_ERROR();
428  }
429
430  AttachBackTextureToFBO();
431}
432
433void TextureImageTransportSurface::AttachBackTextureToFBO() {
434  DCHECK(backbuffer_->service_id());
435  gfx::ScopedFrameBufferBinder fbo_binder(fbo_id_);
436  glFramebufferTexture2DEXT(GL_FRAMEBUFFER,
437      GL_COLOR_ATTACHMENT0,
438      GL_TEXTURE_2D,
439      backbuffer_->service_id(),
440      0);
441  CHECK_GL_ERROR();
442
443#ifndef NDEBUG
444  GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER);
445  if (status != GL_FRAMEBUFFER_COMPLETE) {
446    DLOG(FATAL) << "Framebuffer incomplete: " << status;
447  }
448#endif
449}
450
451TextureDefinition* TextureImageTransportSurface::CreateTextureDefinition(
452    gfx::Size size, int service_id) {
453  TextureDefinition::LevelInfo info(
454      GL_TEXTURE_2D, GL_RGBA, size.width(), size.height(), 1,
455      0, GL_RGBA, GL_UNSIGNED_BYTE, true);
456
457  TextureDefinition::LevelInfos level_infos;
458  level_infos.resize(1);
459  level_infos[0].resize(1);
460  level_infos[0][0] = info;
461  return new TextureDefinition(
462      GL_TEXTURE_2D,
463      service_id,
464      GL_LINEAR,
465      GL_LINEAR,
466      GL_CLAMP_TO_EDGE,
467      GL_CLAMP_TO_EDGE,
468      GL_NONE,
469      true,
470      false,
471      level_infos);
472}
473
474void TextureImageTransportSurface::ConsumeTexture() {
475  DCHECK(!backbuffer_->service_id());
476
477  backbuffer_.reset(mailbox_manager_->ConsumeTexture(
478      GL_TEXTURE_2D, mailbox_name_));
479  if (!backbuffer_) {
480    mailbox_name_ = MailboxName();
481    backbuffer_.reset(CreateTextureDefinition(gfx::Size(), 0));
482  }
483}
484
485void TextureImageTransportSurface::ProduceTexture() {
486  DCHECK(backbuffer_->service_id());
487  DCHECK(!backbuffer_size().IsEmpty());
488
489  // Pass NULL as |owner| here to avoid errors from glConsumeTextureCHROMIUM()
490  // when the renderer context group goes away before the RWHV handles a pending
491  // ACK. We avoid leaking a texture in the mailbox by waiting for the final ACK
492  // at which point we consume the correct texture back.
493  bool success = mailbox_manager_->ProduceTexture(
494      GL_TEXTURE_2D,
495      mailbox_name_,
496      backbuffer_.release(),
497      NULL);
498  DCHECK(success);
499  mailbox_name_ = MailboxName();
500  backbuffer_.reset(CreateTextureDefinition(gfx::Size(), 0));
501}
502
503}  // namespace content
504