Home | History | Annotate | Download | only in output
      1 // Copyright (c) 2013 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 "cc/output/output_surface.h"
      6 
      7 #include <set>
      8 #include <string>
      9 #include <vector>
     10 
     11 #include "base/bind.h"
     12 #include "base/debug/trace_event.h"
     13 #include "base/logging.h"
     14 #include "base/message_loop/message_loop.h"
     15 #include "base/strings/string_split.h"
     16 #include "base/strings/string_util.h"
     17 #include "cc/output/compositor_frame.h"
     18 #include "cc/output/managed_memory_policy.h"
     19 #include "cc/output/output_surface_client.h"
     20 #include "cc/scheduler/delay_based_time_source.h"
     21 #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
     22 #include "third_party/WebKit/public/platform/WebGraphicsMemoryAllocation.h"
     23 #include "third_party/khronos/GLES2/gl2.h"
     24 #include "third_party/khronos/GLES2/gl2ext.h"
     25 #include "ui/gfx/rect.h"
     26 #include "ui/gfx/size.h"
     27 
     28 using std::set;
     29 using std::string;
     30 using std::vector;
     31 
     32 namespace cc {
     33 namespace {
     34 
     35 ManagedMemoryPolicy::PriorityCutoff ConvertPriorityCutoff(
     36     WebKit::WebGraphicsMemoryAllocation::PriorityCutoff priority_cutoff) {
     37   // This is simple a 1:1 map, the names differ only because the WebKit names
     38   // should be to match the cc names.
     39   switch (priority_cutoff) {
     40     case WebKit::WebGraphicsMemoryAllocation::PriorityCutoffAllowNothing:
     41       return ManagedMemoryPolicy::CUTOFF_ALLOW_NOTHING;
     42     case WebKit::WebGraphicsMemoryAllocation::PriorityCutoffAllowVisibleOnly:
     43       return ManagedMemoryPolicy::CUTOFF_ALLOW_REQUIRED_ONLY;
     44     case WebKit::WebGraphicsMemoryAllocation::
     45         PriorityCutoffAllowVisibleAndNearby:
     46       return ManagedMemoryPolicy::CUTOFF_ALLOW_NICE_TO_HAVE;
     47     case WebKit::WebGraphicsMemoryAllocation::PriorityCutoffAllowEverything:
     48       return ManagedMemoryPolicy::CUTOFF_ALLOW_EVERYTHING;
     49   }
     50   NOTREACHED();
     51   return ManagedMemoryPolicy::CUTOFF_ALLOW_NOTHING;
     52 }
     53 
     54 }  // anonymous namespace
     55 
     56 class OutputSurfaceCallbacks
     57     : public WebKit::WebGraphicsContext3D::
     58         WebGraphicsSwapBuffersCompleteCallbackCHROMIUM,
     59       public WebKit::WebGraphicsContext3D::WebGraphicsContextLostCallback,
     60     public WebKit::WebGraphicsContext3D::
     61       WebGraphicsMemoryAllocationChangedCallbackCHROMIUM {
     62  public:
     63   explicit OutputSurfaceCallbacks(OutputSurface* client)
     64       : client_(client) {
     65     DCHECK(client_);
     66   }
     67 
     68   // WK:WGC3D::WGSwapBuffersCompleteCallbackCHROMIUM implementation.
     69   virtual void onSwapBuffersComplete() { client_->OnSwapBuffersComplete(NULL); }
     70 
     71   // WK:WGC3D::WGContextLostCallback implementation.
     72   virtual void onContextLost() { client_->DidLoseOutputSurface(); }
     73 
     74   // WK:WGC3D::WGMemoryAllocationChangedCallbackCHROMIUM implementation.
     75   virtual void onMemoryAllocationChanged(
     76       WebKit::WebGraphicsMemoryAllocation allocation) {
     77     ManagedMemoryPolicy policy(
     78         allocation.bytesLimitWhenVisible,
     79         ConvertPriorityCutoff(allocation.priorityCutoffWhenVisible),
     80         allocation.bytesLimitWhenNotVisible,
     81         ConvertPriorityCutoff(allocation.priorityCutoffWhenNotVisible),
     82         ManagedMemoryPolicy::kDefaultNumResourcesLimit);
     83     bool discard_backbuffer = !allocation.suggestHaveBackbuffer;
     84     client_->SetMemoryPolicy(policy, discard_backbuffer);
     85   }
     86 
     87  private:
     88   OutputSurface* client_;
     89 };
     90 
     91 OutputSurface::OutputSurface(
     92     scoped_ptr<WebKit::WebGraphicsContext3D> context3d)
     93     : context3d_(context3d.Pass()),
     94       has_gl_discard_backbuffer_(false),
     95       has_swap_buffers_complete_callback_(false),
     96       device_scale_factor_(-1),
     97       weak_ptr_factory_(this),
     98       max_frames_pending_(0),
     99       pending_swap_buffers_(0),
    100       needs_begin_frame_(false),
    101       begin_frame_pending_(false),
    102       client_(NULL),
    103       check_for_retroactive_begin_frame_pending_(false) {
    104 }
    105 
    106 OutputSurface::OutputSurface(
    107     scoped_ptr<cc::SoftwareOutputDevice> software_device)
    108     : software_device_(software_device.Pass()),
    109       has_gl_discard_backbuffer_(false),
    110       has_swap_buffers_complete_callback_(false),
    111       device_scale_factor_(-1),
    112       weak_ptr_factory_(this),
    113       max_frames_pending_(0),
    114       pending_swap_buffers_(0),
    115       needs_begin_frame_(false),
    116       begin_frame_pending_(false),
    117       client_(NULL),
    118       check_for_retroactive_begin_frame_pending_(false) {
    119 }
    120 
    121 OutputSurface::OutputSurface(
    122     scoped_ptr<WebKit::WebGraphicsContext3D> context3d,
    123     scoped_ptr<cc::SoftwareOutputDevice> software_device)
    124     : context3d_(context3d.Pass()),
    125       software_device_(software_device.Pass()),
    126       has_gl_discard_backbuffer_(false),
    127       has_swap_buffers_complete_callback_(false),
    128       device_scale_factor_(-1),
    129       weak_ptr_factory_(this),
    130       max_frames_pending_(0),
    131       pending_swap_buffers_(0),
    132       needs_begin_frame_(false),
    133       begin_frame_pending_(false),
    134       client_(NULL),
    135       check_for_retroactive_begin_frame_pending_(false) {
    136 }
    137 
    138 void OutputSurface::InitializeBeginFrameEmulation(
    139     base::SingleThreadTaskRunner* task_runner,
    140     bool throttle_frame_production,
    141     base::TimeDelta interval) {
    142   if (throttle_frame_production) {
    143     frame_rate_controller_.reset(
    144         new FrameRateController(
    145             DelayBasedTimeSource::Create(interval, task_runner)));
    146   } else {
    147     frame_rate_controller_.reset(new FrameRateController(task_runner));
    148   }
    149 
    150   frame_rate_controller_->SetClient(this);
    151   frame_rate_controller_->SetMaxSwapsPending(max_frames_pending_);
    152   frame_rate_controller_->SetDeadlineAdjustment(
    153       capabilities_.adjust_deadline_for_parent ?
    154           BeginFrameArgs::DefaultDeadlineAdjustment() : base::TimeDelta());
    155 
    156   // The new frame rate controller will consume the swap acks of the old
    157   // frame rate controller, so we set that expectation up here.
    158   for (int i = 0; i < pending_swap_buffers_; i++)
    159     frame_rate_controller_->DidSwapBuffers();
    160 }
    161 
    162 void OutputSurface::SetMaxFramesPending(int max_frames_pending) {
    163   if (frame_rate_controller_)
    164     frame_rate_controller_->SetMaxSwapsPending(max_frames_pending);
    165   max_frames_pending_ = max_frames_pending;
    166 }
    167 
    168 void OutputSurface::OnVSyncParametersChanged(base::TimeTicks timebase,
    169                                              base::TimeDelta interval) {
    170   TRACE_EVENT2("cc", "OutputSurface::OnVSyncParametersChanged",
    171                "timebase", (timebase - base::TimeTicks()).InSecondsF(),
    172                "interval", interval.InSecondsF());
    173   if (frame_rate_controller_)
    174     frame_rate_controller_->SetTimebaseAndInterval(timebase, interval);
    175 }
    176 
    177 void OutputSurface::FrameRateControllerTick(bool throttled,
    178                                             const BeginFrameArgs& args) {
    179   DCHECK(frame_rate_controller_);
    180   if (throttled)
    181     skipped_begin_frame_args_ = args;
    182   else
    183     BeginFrame(args);
    184 }
    185 
    186 // Forwarded to OutputSurfaceClient
    187 void OutputSurface::SetNeedsRedrawRect(gfx::Rect damage_rect) {
    188   TRACE_EVENT0("cc", "OutputSurface::SetNeedsRedrawRect");
    189   client_->SetNeedsRedrawRect(damage_rect);
    190 }
    191 
    192 void OutputSurface::SetNeedsBeginFrame(bool enable) {
    193   TRACE_EVENT1("cc", "OutputSurface::SetNeedsBeginFrame", "enable", enable);
    194   needs_begin_frame_ = enable;
    195   begin_frame_pending_ = false;
    196   if (frame_rate_controller_) {
    197     BeginFrameArgs skipped = frame_rate_controller_->SetActive(enable);
    198     if (skipped.IsValid())
    199       skipped_begin_frame_args_ = skipped;
    200   }
    201   if (needs_begin_frame_)
    202     PostCheckForRetroactiveBeginFrame();
    203 }
    204 
    205 void OutputSurface::BeginFrame(const BeginFrameArgs& args) {
    206   TRACE_EVENT2("cc", "OutputSurface::BeginFrame",
    207                "begin_frame_pending_", begin_frame_pending_,
    208                "pending_swap_buffers_", pending_swap_buffers_);
    209   if (!needs_begin_frame_ || begin_frame_pending_ ||
    210       (pending_swap_buffers_ >= max_frames_pending_ &&
    211        max_frames_pending_ > 0)) {
    212     skipped_begin_frame_args_ = args;
    213   } else {
    214     begin_frame_pending_ = true;
    215     client_->BeginFrame(args);
    216     // args might be an alias for skipped_begin_frame_args_.
    217     // Do not reset it before calling BeginFrame!
    218     skipped_begin_frame_args_ = BeginFrameArgs();
    219   }
    220 }
    221 
    222 base::TimeDelta OutputSurface::RetroactiveBeginFramePeriod() {
    223   return BeginFrameArgs::DefaultRetroactiveBeginFramePeriod();
    224 }
    225 
    226 void OutputSurface::PostCheckForRetroactiveBeginFrame() {
    227   if (!skipped_begin_frame_args_.IsValid() ||
    228       check_for_retroactive_begin_frame_pending_)
    229     return;
    230 
    231   base::MessageLoop::current()->PostTask(
    232      FROM_HERE,
    233      base::Bind(&OutputSurface::CheckForRetroactiveBeginFrame,
    234                 weak_ptr_factory_.GetWeakPtr()));
    235   check_for_retroactive_begin_frame_pending_ = true;
    236 }
    237 
    238 void OutputSurface::CheckForRetroactiveBeginFrame() {
    239   TRACE_EVENT0("cc", "OutputSurface::CheckForRetroactiveBeginFrame");
    240   check_for_retroactive_begin_frame_pending_ = false;
    241   base::TimeTicks now = base::TimeTicks::Now();
    242   base::TimeTicks alternative_deadline =
    243       skipped_begin_frame_args_.frame_time +
    244       RetroactiveBeginFramePeriod();
    245   if (now < skipped_begin_frame_args_.deadline ||
    246       now < alternative_deadline) {
    247     BeginFrame(skipped_begin_frame_args_);
    248   }
    249 }
    250 
    251 void OutputSurface::DidSwapBuffers() {
    252   begin_frame_pending_ = false;
    253   pending_swap_buffers_++;
    254   TRACE_EVENT1("cc", "OutputSurface::DidSwapBuffers",
    255                "pending_swap_buffers_", pending_swap_buffers_);
    256   if (frame_rate_controller_)
    257     frame_rate_controller_->DidSwapBuffers();
    258   PostCheckForRetroactiveBeginFrame();
    259 }
    260 
    261 void OutputSurface::OnSwapBuffersComplete(const CompositorFrameAck* ack) {
    262   pending_swap_buffers_--;
    263   TRACE_EVENT1("cc", "OutputSurface::OnSwapBuffersComplete",
    264                "pending_swap_buffers_", pending_swap_buffers_);
    265   client_->OnSwapBuffersComplete(ack);
    266   if (frame_rate_controller_)
    267     frame_rate_controller_->DidSwapBuffersComplete();
    268   PostCheckForRetroactiveBeginFrame();
    269 }
    270 
    271 void OutputSurface::DidLoseOutputSurface() {
    272   TRACE_EVENT0("cc", "OutputSurface::DidLoseOutputSurface");
    273   begin_frame_pending_ = false;
    274   pending_swap_buffers_ = 0;
    275   client_->DidLoseOutputSurface();
    276 }
    277 
    278 void OutputSurface::SetExternalStencilTest(bool enabled) {
    279   client_->SetExternalStencilTest(enabled);
    280 }
    281 
    282 void OutputSurface::SetExternalDrawConstraints(const gfx::Transform& transform,
    283                                                gfx::Rect viewport,
    284                                                gfx::Rect clip,
    285                                                bool valid_for_tile_management) {
    286   client_->SetExternalDrawConstraints(
    287       transform, viewport, clip, valid_for_tile_management);
    288 }
    289 
    290 OutputSurface::~OutputSurface() {
    291   if (frame_rate_controller_)
    292     frame_rate_controller_->SetActive(false);
    293 
    294   if (context3d_) {
    295     context3d_->setSwapBuffersCompleteCallbackCHROMIUM(NULL);
    296     context3d_->setContextLostCallback(NULL);
    297     context3d_->setMemoryAllocationChangedCallbackCHROMIUM(NULL);
    298   }
    299 }
    300 
    301 bool OutputSurface::ForcedDrawToSoftwareDevice() const {
    302   return false;
    303 }
    304 
    305 bool OutputSurface::BindToClient(cc::OutputSurfaceClient* client) {
    306   DCHECK(client);
    307   client_ = client;
    308   bool success = true;
    309 
    310   if (context3d_) {
    311     success = context3d_->makeContextCurrent();
    312     if (success)
    313       SetContext3D(context3d_.Pass());
    314   }
    315 
    316   if (!success)
    317     client_ = NULL;
    318 
    319   return success;
    320 }
    321 
    322 bool OutputSurface::InitializeAndSetContext3D(
    323     scoped_ptr<WebKit::WebGraphicsContext3D> context3d,
    324     scoped_refptr<ContextProvider> offscreen_context_provider) {
    325   DCHECK(!context3d_);
    326   DCHECK(context3d);
    327   DCHECK(client_);
    328 
    329   bool success = false;
    330   if (context3d->makeContextCurrent()) {
    331     SetContext3D(context3d.Pass());
    332     if (client_->DeferredInitialize(offscreen_context_provider))
    333       success = true;
    334   }
    335 
    336   if (!success)
    337     ResetContext3D();
    338 
    339   return success;
    340 }
    341 
    342 void OutputSurface::ReleaseGL() {
    343   DCHECK(client_);
    344   DCHECK(context3d_);
    345   client_->ReleaseGL();
    346   ResetContext3D();
    347 }
    348 
    349 void OutputSurface::SetContext3D(
    350     scoped_ptr<WebKit::WebGraphicsContext3D> context3d) {
    351   DCHECK(!context3d_);
    352   DCHECK(context3d);
    353   DCHECK(client_);
    354 
    355   string extensions_string = UTF16ToASCII(context3d->getString(GL_EXTENSIONS));
    356   vector<string> extensions_list;
    357   base::SplitString(extensions_string, ' ', &extensions_list);
    358   set<string> extensions(extensions_list.begin(), extensions_list.end());
    359   has_gl_discard_backbuffer_ =
    360       extensions.count("GL_CHROMIUM_discard_backbuffer") > 0;
    361   has_swap_buffers_complete_callback_ =
    362        extensions.count("GL_CHROMIUM_swapbuffers_complete_callback") > 0;
    363 
    364 
    365   context3d_ = context3d.Pass();
    366   callbacks_.reset(new OutputSurfaceCallbacks(this));
    367   context3d_->setSwapBuffersCompleteCallbackCHROMIUM(callbacks_.get());
    368   context3d_->setContextLostCallback(callbacks_.get());
    369   context3d_->setMemoryAllocationChangedCallbackCHROMIUM(callbacks_.get());
    370 }
    371 
    372 void OutputSurface::ResetContext3D() {
    373   context3d_.reset();
    374   callbacks_.reset();
    375 }
    376 
    377 void OutputSurface::EnsureBackbuffer() {
    378   DCHECK(context3d_);
    379   if (has_gl_discard_backbuffer_)
    380     context3d_->ensureBackbufferCHROMIUM();
    381 }
    382 
    383 void OutputSurface::DiscardBackbuffer() {
    384   DCHECK(context3d_);
    385   if (has_gl_discard_backbuffer_)
    386     context3d_->discardBackbufferCHROMIUM();
    387 }
    388 
    389 void OutputSurface::Reshape(gfx::Size size, float scale_factor) {
    390   if (size == surface_size_ && scale_factor == device_scale_factor_)
    391     return;
    392 
    393   surface_size_ = size;
    394   device_scale_factor_ = scale_factor;
    395   if (context3d_) {
    396     context3d_->reshapeWithScaleFactor(
    397         size.width(), size.height(), scale_factor);
    398   }
    399   if (software_device_)
    400     software_device_->Resize(size);
    401 }
    402 
    403 gfx::Size OutputSurface::SurfaceSize() const {
    404   return surface_size_;
    405 }
    406 
    407 void OutputSurface::BindFramebuffer() {
    408   DCHECK(context3d_);
    409   context3d_->bindFramebuffer(GL_FRAMEBUFFER, 0);
    410 }
    411 
    412 void OutputSurface::SwapBuffers(cc::CompositorFrame* frame) {
    413   if (frame->software_frame_data) {
    414     PostSwapBuffersComplete();
    415     DidSwapBuffers();
    416     return;
    417   }
    418 
    419   DCHECK(context3d_);
    420   DCHECK(frame->gl_frame_data);
    421 
    422   if (frame->gl_frame_data->sub_buffer_rect ==
    423       gfx::Rect(frame->gl_frame_data->size)) {
    424     // Note that currently this has the same effect as SwapBuffers; we should
    425     // consider exposing a different entry point on WebGraphicsContext3D.
    426     context3d()->prepareTexture();
    427   } else {
    428     gfx::Rect sub_buffer_rect = frame->gl_frame_data->sub_buffer_rect;
    429     context3d()->postSubBufferCHROMIUM(sub_buffer_rect.x(),
    430                                        sub_buffer_rect.y(),
    431                                        sub_buffer_rect.width(),
    432                                        sub_buffer_rect.height());
    433   }
    434 
    435   if (!has_swap_buffers_complete_callback_)
    436     PostSwapBuffersComplete();
    437 
    438   DidSwapBuffers();
    439 }
    440 
    441 void OutputSurface::PostSwapBuffersComplete() {
    442   base::MessageLoop::current()->PostTask(
    443        FROM_HERE,
    444        base::Bind(&OutputSurface::OnSwapBuffersComplete,
    445                   weak_ptr_factory_.GetWeakPtr(),
    446                   static_cast<CompositorFrameAck*>(NULL)));
    447 }
    448 
    449 void OutputSurface::SetMemoryPolicy(const ManagedMemoryPolicy& policy,
    450                                     bool discard_backbuffer) {
    451   TRACE_EVENT2("cc", "OutputSurface::SetMemoryPolicy",
    452                "bytes_limit_when_visible", policy.bytes_limit_when_visible,
    453                "discard_backbuffer", discard_backbuffer);
    454   // Just ignore the memory manager when it says to set the limit to zero
    455   // bytes. This will happen when the memory manager thinks that the renderer
    456   // is not visible (which the renderer knows better).
    457   if (policy.bytes_limit_when_visible)
    458     client_->SetMemoryPolicy(policy);
    459   client_->SetDiscardBackBufferWhenNotVisible(discard_backbuffer);
    460 }
    461 
    462 }  // namespace cc
    463