Home | History | Annotate | Download | only in in_process
      1 // Copyright 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 "content/browser/android/in_process/synchronous_compositor_output_surface.h"
      6 
      7 #include "base/auto_reset.h"
      8 #include "base/logging.h"
      9 #include "cc/output/begin_frame_args.h"
     10 #include "cc/output/compositor_frame.h"
     11 #include "cc/output/context_provider.h"
     12 #include "cc/output/output_surface_client.h"
     13 #include "cc/output/software_output_device.h"
     14 #include "content/browser/android/in_process/synchronous_compositor_impl.h"
     15 #include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h"
     16 #include "content/public/browser/browser_thread.h"
     17 #include "gpu/command_buffer/client/gl_in_process_context.h"
     18 #include "third_party/skia/include/core/SkCanvas.h"
     19 #include "third_party/skia/include/core/SkDevice.h"
     20 #include "ui/gfx/rect_conversions.h"
     21 #include "ui/gfx/skia_util.h"
     22 #include "ui/gfx/transform.h"
     23 #include "ui/gl/gl_surface.h"
     24 #include "webkit/common/gpu/webgraphicscontext3d_in_process_command_buffer_impl.h"
     25 
     26 
     27 namespace content {
     28 
     29 namespace {
     30 
     31 scoped_ptr<WebKit::WebGraphicsContext3D> CreateWebGraphicsContext3D(
     32     scoped_refptr<gfx::GLSurface> surface) {
     33   using webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl;
     34   if (!gfx::GLSurface::InitializeOneOff())
     35     return scoped_ptr<WebKit::WebGraphicsContext3D>();
     36 
     37   const char* allowed_extensions = "*";
     38   const gfx::GpuPreference gpu_preference = gfx::PreferDiscreteGpu;
     39 
     40   WebKit::WebGraphicsContext3D::Attributes attributes;
     41   attributes.antialias = false;
     42   attributes.shareResources = true;
     43   attributes.noAutomaticFlushes = true;
     44 
     45   gpu::GLInProcessContextAttribs in_process_attribs;
     46   WebGraphicsContext3DInProcessCommandBufferImpl::ConvertAttributes(
     47       attributes, &in_process_attribs);
     48   scoped_ptr<gpu::GLInProcessContext> context(
     49       gpu::GLInProcessContext::CreateWithSurface(surface,
     50                                                  attributes.shareResources,
     51                                                  allowed_extensions,
     52                                                  in_process_attribs,
     53                                                  gpu_preference));
     54 
     55   if (!context.get())
     56     return scoped_ptr<WebKit::WebGraphicsContext3D>();
     57 
     58   return scoped_ptr<WebKit::WebGraphicsContext3D>(
     59       WebGraphicsContext3DInProcessCommandBufferImpl::WrapContext(
     60           context.Pass(), attributes));
     61 }
     62 
     63 void DidActivatePendingTree(int routing_id) {
     64   SynchronousCompositorOutputSurfaceDelegate* delegate =
     65       SynchronousCompositorImpl::FromRoutingID(routing_id);
     66   if (delegate)
     67     delegate->DidActivatePendingTree();
     68 }
     69 
     70 } // namespace
     71 
     72 class SynchronousCompositorOutputSurface::SoftwareDevice
     73   : public cc::SoftwareOutputDevice {
     74  public:
     75   SoftwareDevice(SynchronousCompositorOutputSurface* surface)
     76     : surface_(surface),
     77       null_device_(SkBitmap::kARGB_8888_Config, 1, 1),
     78       null_canvas_(&null_device_) {
     79   }
     80   virtual void Resize(gfx::Size size) OVERRIDE {
     81     // Intentional no-op: canvas size is controlled by the embedder.
     82   }
     83   virtual SkCanvas* BeginPaint(gfx::Rect damage_rect) OVERRIDE {
     84     if (!surface_->current_sw_canvas_) {
     85       NOTREACHED() << "BeginPaint with no canvas set";
     86       return &null_canvas_;
     87     }
     88     LOG_IF(WARNING, surface_->did_swap_buffer_)
     89         << "Mutliple calls to BeginPaint per frame";
     90     return surface_->current_sw_canvas_;
     91   }
     92   virtual void EndPaint(cc::SoftwareFrameData* frame_data) OVERRIDE {
     93   }
     94   virtual void CopyToBitmap(gfx::Rect rect, SkBitmap* output) OVERRIDE {
     95     NOTIMPLEMENTED();
     96   }
     97 
     98  private:
     99   SynchronousCompositorOutputSurface* surface_;
    100   SkDevice null_device_;
    101   SkCanvas null_canvas_;
    102 
    103   DISALLOW_COPY_AND_ASSIGN(SoftwareDevice);
    104 };
    105 
    106 SynchronousCompositorOutputSurface::SynchronousCompositorOutputSurface(
    107     int routing_id)
    108     : cc::OutputSurface(
    109           scoped_ptr<cc::SoftwareOutputDevice>(new SoftwareDevice(this))),
    110       routing_id_(routing_id),
    111       needs_begin_frame_(false),
    112       invoking_composite_(false),
    113       did_swap_buffer_(false),
    114       current_sw_canvas_(NULL),
    115       memory_policy_(0),
    116       output_surface_client_(NULL) {
    117   capabilities_.deferred_gl_initialization = true;
    118   capabilities_.draw_and_swap_full_viewport_every_frame = true;
    119   capabilities_.adjust_deadline_for_parent = false;
    120   // Cannot call out to GetDelegate() here as the output surface is not
    121   // constructed on the correct thread.
    122 
    123   memory_policy_.priority_cutoff_when_visible =
    124       cc::ManagedMemoryPolicy::CUTOFF_ALLOW_NICE_TO_HAVE;
    125 }
    126 
    127 SynchronousCompositorOutputSurface::~SynchronousCompositorOutputSurface() {
    128   DCHECK(CalledOnValidThread());
    129   SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
    130   if (delegate)
    131     delegate->DidDestroySynchronousOutputSurface(this);
    132 }
    133 
    134 bool SynchronousCompositorOutputSurface::ForcedDrawToSoftwareDevice() const {
    135   // |current_sw_canvas_| indicates we're in a DemandDrawSw call. In addition
    136   // |invoking_composite_| == false indicates an attempt to draw outside of
    137   // the synchronous compositor's control: force it into SW path and hence to
    138   // the null canvas (and will log a warning there).
    139   return current_sw_canvas_ != NULL || !invoking_composite_;
    140 }
    141 
    142 bool SynchronousCompositorOutputSurface::BindToClient(
    143     cc::OutputSurfaceClient* surface_client) {
    144   DCHECK(CalledOnValidThread());
    145   if (!cc::OutputSurface::BindToClient(surface_client))
    146     return false;
    147 
    148   output_surface_client_ = surface_client;
    149   output_surface_client_->SetTreeActivationCallback(
    150       base::Bind(&DidActivatePendingTree, routing_id_));
    151   output_surface_client_->SetMemoryPolicy(memory_policy_);
    152 
    153   SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
    154   if (delegate)
    155     delegate->DidBindOutputSurface(this);
    156 
    157   return true;
    158 }
    159 
    160 void SynchronousCompositorOutputSurface::Reshape(
    161     gfx::Size size, float scale_factor) {
    162   // Intentional no-op: surface size is controlled by the embedder.
    163 }
    164 
    165 void SynchronousCompositorOutputSurface::SetNeedsBeginFrame(
    166     bool enable) {
    167   DCHECK(CalledOnValidThread());
    168   cc::OutputSurface::SetNeedsBeginFrame(enable);
    169   needs_begin_frame_ = enable;
    170   SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
    171   if (delegate)
    172     delegate->SetContinuousInvalidate(needs_begin_frame_);
    173 }
    174 
    175 void SynchronousCompositorOutputSurface::SwapBuffers(
    176     cc::CompositorFrame* frame) {
    177   DCHECK(CalledOnValidThread());
    178   if (!ForcedDrawToSoftwareDevice()) {
    179     DCHECK(context3d());
    180     context3d()->shallowFlushCHROMIUM();
    181   }
    182   SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
    183   if (delegate)
    184     delegate->UpdateFrameMetaData(frame->metadata);
    185 
    186   did_swap_buffer_ = true;
    187   DidSwapBuffers();
    188 }
    189 
    190 namespace {
    191 void AdjustTransform(gfx::Transform* transform,
    192                      gfx::Rect viewport,
    193                      gfx::Rect clip) {
    194   // CC's draw origin starts at the viewport.
    195   transform->matrix().postTranslate(-viewport.x(), -viewport.y(), 0);
    196 }
    197 } // namespace
    198 
    199 bool SynchronousCompositorOutputSurface::InitializeHwDraw(
    200     scoped_refptr<gfx::GLSurface> surface,
    201     scoped_refptr<cc::ContextProvider> offscreen_context) {
    202   DCHECK(CalledOnValidThread());
    203   DCHECK(HasClient());
    204   DCHECK(!context3d_);
    205   DCHECK(surface);
    206 
    207   return InitializeAndSetContext3D(
    208       CreateWebGraphicsContext3D(surface).Pass(), offscreen_context);
    209 }
    210 
    211 void SynchronousCompositorOutputSurface::ReleaseHwDraw() {
    212   DCHECK(CalledOnValidThread());
    213   cc::OutputSurface::ReleaseGL();
    214 }
    215 
    216 bool SynchronousCompositorOutputSurface::DemandDrawHw(
    217     gfx::Size surface_size,
    218     const gfx::Transform& transform,
    219     gfx::Rect viewport,
    220     gfx::Rect clip,
    221     bool stencil_enabled) {
    222   DCHECK(CalledOnValidThread());
    223   DCHECK(HasClient());
    224   DCHECK(context3d());
    225 
    226   surface_size_ = surface_size;
    227   SetExternalStencilTest(stencil_enabled);
    228   InvokeComposite(transform, viewport, clip, true);
    229 
    230   return did_swap_buffer_;
    231 }
    232 
    233 bool SynchronousCompositorOutputSurface::DemandDrawSw(SkCanvas* canvas) {
    234   DCHECK(CalledOnValidThread());
    235   DCHECK(canvas);
    236   DCHECK(!current_sw_canvas_);
    237   base::AutoReset<SkCanvas*> canvas_resetter(&current_sw_canvas_, canvas);
    238 
    239   SkIRect canvas_clip;
    240   canvas->getClipDeviceBounds(&canvas_clip);
    241   gfx::Rect clip = gfx::SkIRectToRect(canvas_clip);
    242 
    243   gfx::Transform transform(gfx::Transform::kSkipInitialization);
    244   transform.matrix() = canvas->getTotalMatrix();  // Converts 3x3 matrix to 4x4.
    245 
    246   surface_size_ = gfx::Size(canvas->getDeviceSize().width(),
    247                             canvas->getDeviceSize().height());
    248   SetExternalStencilTest(false);
    249 
    250   InvokeComposite(transform, clip, clip, false);
    251 
    252   return did_swap_buffer_;
    253 }
    254 
    255 void SynchronousCompositorOutputSurface::InvokeComposite(
    256     const gfx::Transform& transform,
    257     gfx::Rect viewport,
    258     gfx::Rect clip,
    259     bool hardware) {
    260   DCHECK(!invoking_composite_);
    261   base::AutoReset<bool> invoking_composite_resetter(&invoking_composite_, true);
    262   did_swap_buffer_ = false;
    263 
    264   gfx::Transform adjusted_transform = transform;
    265   AdjustTransform(&adjusted_transform, viewport, clip);
    266   SetExternalDrawConstraints(adjusted_transform, viewport, clip, hardware);
    267   SetNeedsRedrawRect(gfx::Rect(viewport.size()));
    268 
    269   if (needs_begin_frame_)
    270     BeginFrame(cc::BeginFrameArgs::CreateForSynchronousCompositor());
    271 
    272   if (hardware) {
    273     cached_hw_transform_ = adjusted_transform;
    274     cached_hw_viewport_ = viewport;
    275     cached_hw_clip_ = clip;
    276   } else {
    277     SetExternalDrawConstraints(
    278         cached_hw_transform_, cached_hw_viewport_, cached_hw_clip_, true);
    279   }
    280 
    281   if (did_swap_buffer_)
    282     OnSwapBuffersComplete(NULL);
    283 }
    284 
    285 void SynchronousCompositorOutputSurface::PostCheckForRetroactiveBeginFrame() {
    286   // Synchronous compositor cannot perform retroactive begin frames, so
    287   // intentionally no-op here.
    288 }
    289 
    290 void SynchronousCompositorOutputSurface::SetMemoryPolicy(
    291     const SynchronousCompositorMemoryPolicy& policy) {
    292   DCHECK(CalledOnValidThread());
    293   memory_policy_.bytes_limit_when_visible = policy.bytes_limit;
    294   memory_policy_.num_resources_limit = policy.num_resources_limit;
    295 
    296   if (output_surface_client_)
    297     output_surface_client_->SetMemoryPolicy(memory_policy_);
    298 }
    299 
    300 // Not using base::NonThreadSafe as we want to enforce a more exacting threading
    301 // requirement: SynchronousCompositorOutputSurface() must only be used on the UI
    302 // thread.
    303 bool SynchronousCompositorOutputSurface::CalledOnValidThread() const {
    304   return BrowserThread::CurrentlyOn(BrowserThread::UI);
    305 }
    306 
    307 SynchronousCompositorOutputSurfaceDelegate*
    308 SynchronousCompositorOutputSurface::GetDelegate() {
    309   return SynchronousCompositorImpl::FromRoutingID(routing_id_);
    310 }
    311 
    312 }  // namespace content
    313