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/browser/gpu/compositor_util.h"
16#include "content/public/browser/browser_thread.h"
17#include "content/renderer/gpu/frame_swap_message_queue.h"
18#include "gpu/command_buffer/client/gles2_interface.h"
19#include "gpu/command_buffer/common/gpu_memory_allocation.h"
20#include "third_party/skia/include/core/SkCanvas.h"
21#include "ui/gfx/rect_conversions.h"
22#include "ui/gfx/skia_util.h"
23#include "ui/gfx/transform.h"
24
25namespace content {
26
27namespace {
28
29void DidActivatePendingTree(int routing_id) {
30  SynchronousCompositorOutputSurfaceDelegate* delegate =
31      SynchronousCompositorImpl::FromRoutingID(routing_id);
32  if (delegate)
33    delegate->DidActivatePendingTree();
34}
35
36} // namespace
37
38class SynchronousCompositorOutputSurface::SoftwareDevice
39  : public cc::SoftwareOutputDevice {
40 public:
41  SoftwareDevice(SynchronousCompositorOutputSurface* surface)
42    : surface_(surface) {
43  }
44  virtual void Resize(const gfx::Size& pixel_size,
45                      float scale_factor) OVERRIDE {
46    // Intentional no-op: canvas size is controlled by the embedder.
47  }
48  virtual SkCanvas* BeginPaint(const gfx::Rect& damage_rect) OVERRIDE {
49    if (!surface_->current_sw_canvas_) {
50      NOTREACHED() << "BeginPaint with no canvas set";
51      return &null_canvas_;
52    }
53    LOG_IF(WARNING, surface_->frame_holder_.get())
54        << "Mutliple calls to BeginPaint per frame";
55    return surface_->current_sw_canvas_;
56  }
57  virtual void EndPaint(cc::SoftwareFrameData* frame_data) OVERRIDE {
58  }
59  virtual void CopyToPixels(const gfx::Rect& rect, void* pixels) OVERRIDE {
60    NOTIMPLEMENTED();
61  }
62
63 private:
64  SynchronousCompositorOutputSurface* surface_;
65  SkCanvas null_canvas_;
66
67  DISALLOW_COPY_AND_ASSIGN(SoftwareDevice);
68};
69
70SynchronousCompositorOutputSurface::SynchronousCompositorOutputSurface(
71    int routing_id,
72    scoped_refptr<FrameSwapMessageQueue> frame_swap_message_queue)
73    : cc::OutputSurface(
74          scoped_ptr<cc::SoftwareOutputDevice>(new SoftwareDevice(this))),
75      routing_id_(routing_id),
76      needs_begin_frame_(false),
77      invoking_composite_(false),
78      current_sw_canvas_(NULL),
79      memory_policy_(0),
80      output_surface_client_(NULL),
81      frame_swap_message_queue_(frame_swap_message_queue) {
82  capabilities_.deferred_gl_initialization = true;
83  capabilities_.draw_and_swap_full_viewport_every_frame = true;
84  capabilities_.adjust_deadline_for_parent = false;
85  capabilities_.delegated_rendering = true;
86  capabilities_.max_frames_pending = 1;
87  // Cannot call out to GetDelegate() here as the output surface is not
88  // constructed on the correct thread.
89
90  memory_policy_.priority_cutoff_when_visible =
91      gpu::MemoryAllocation::CUTOFF_ALLOW_NICE_TO_HAVE;
92}
93
94SynchronousCompositorOutputSurface::~SynchronousCompositorOutputSurface() {
95  DCHECK(CalledOnValidThread());
96  SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
97  if (delegate)
98    delegate->DidDestroySynchronousOutputSurface(this);
99}
100
101bool SynchronousCompositorOutputSurface::BindToClient(
102    cc::OutputSurfaceClient* surface_client) {
103  DCHECK(CalledOnValidThread());
104  if (!cc::OutputSurface::BindToClient(surface_client))
105    return false;
106
107  output_surface_client_ = surface_client;
108  output_surface_client_->SetTreeActivationCallback(
109      base::Bind(&DidActivatePendingTree, routing_id_));
110  output_surface_client_->SetMemoryPolicy(memory_policy_);
111
112  SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
113  if (delegate)
114    delegate->DidBindOutputSurface(this);
115
116  return true;
117}
118
119void SynchronousCompositorOutputSurface::Reshape(
120    const gfx::Size& size, float scale_factor) {
121  // Intentional no-op: surface size is controlled by the embedder.
122}
123
124void SynchronousCompositorOutputSurface::SetNeedsBeginFrame(bool enable) {
125  DCHECK(CalledOnValidThread());
126  needs_begin_frame_ = enable;
127  SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
128  if (delegate && !invoking_composite_)
129    delegate->SetContinuousInvalidate(needs_begin_frame_);
130}
131
132void SynchronousCompositorOutputSurface::SwapBuffers(
133    cc::CompositorFrame* frame) {
134  DCHECK(CalledOnValidThread());
135
136  frame_holder_.reset(new cc::CompositorFrame);
137  frame->AssignTo(frame_holder_.get());
138
139  client_->DidSwapBuffers();
140}
141
142namespace {
143void AdjustTransform(gfx::Transform* transform, gfx::Rect viewport) {
144  // CC's draw origin starts at the viewport.
145  transform->matrix().postTranslate(-viewport.x(), -viewport.y(), 0);
146}
147} // namespace
148
149bool SynchronousCompositorOutputSurface::InitializeHwDraw(
150    scoped_refptr<cc::ContextProvider> onscreen_context_provider) {
151  DCHECK(CalledOnValidThread());
152  DCHECK(HasClient());
153  DCHECK(!context_provider_);
154
155  return InitializeAndSetContext3d(onscreen_context_provider);
156}
157
158void SynchronousCompositorOutputSurface::ReleaseHwDraw() {
159  DCHECK(CalledOnValidThread());
160  cc::OutputSurface::ReleaseGL();
161}
162
163scoped_ptr<cc::CompositorFrame>
164SynchronousCompositorOutputSurface::DemandDrawHw(
165    gfx::Size surface_size,
166    const gfx::Transform& transform,
167    gfx::Rect viewport,
168    gfx::Rect clip,
169    gfx::Rect viewport_rect_for_tile_priority,
170    const gfx::Transform& transform_for_tile_priority) {
171  DCHECK(CalledOnValidThread());
172  DCHECK(HasClient());
173  DCHECK(context_provider_);
174
175  surface_size_ = surface_size;
176  InvokeComposite(transform,
177                  viewport,
178                  clip,
179                  viewport_rect_for_tile_priority,
180                  transform_for_tile_priority,
181                  true);
182
183  return frame_holder_.Pass();
184}
185
186scoped_ptr<cc::CompositorFrame>
187SynchronousCompositorOutputSurface::DemandDrawSw(SkCanvas* canvas) {
188  DCHECK(CalledOnValidThread());
189  DCHECK(canvas);
190  DCHECK(!current_sw_canvas_);
191  base::AutoReset<SkCanvas*> canvas_resetter(&current_sw_canvas_, canvas);
192
193  SkIRect canvas_clip;
194  canvas->getClipDeviceBounds(&canvas_clip);
195  gfx::Rect clip = gfx::SkIRectToRect(canvas_clip);
196
197  gfx::Transform transform(gfx::Transform::kSkipInitialization);
198  transform.matrix() = canvas->getTotalMatrix();  // Converts 3x3 matrix to 4x4.
199
200  surface_size_ = gfx::Size(canvas->getDeviceSize().width(),
201                            canvas->getDeviceSize().height());
202
203  // Pass in the cached hw viewport and transform for tile priority to avoid
204  // tile thrashing when the WebView is alternating between hardware and
205  // software draws.
206  InvokeComposite(transform,
207                  clip,
208                  clip,
209                  cached_hw_viewport_rect_for_tile_priority_,
210                  cached_hw_transform_for_tile_priority_,
211                  false);
212
213  return frame_holder_.Pass();
214}
215
216void SynchronousCompositorOutputSurface::InvokeComposite(
217    const gfx::Transform& transform,
218    gfx::Rect viewport,
219    gfx::Rect clip,
220    gfx::Rect viewport_rect_for_tile_priority,
221    gfx::Transform transform_for_tile_priority,
222    bool hardware_draw) {
223  DCHECK(!invoking_composite_);
224  DCHECK(!frame_holder_.get());
225  base::AutoReset<bool> invoking_composite_resetter(&invoking_composite_, true);
226
227  gfx::Transform adjusted_transform = transform;
228  AdjustTransform(&adjusted_transform, viewport);
229  SetExternalDrawConstraints(adjusted_transform,
230                             viewport,
231                             clip,
232                             viewport_rect_for_tile_priority,
233                             transform_for_tile_priority,
234                             !hardware_draw);
235  SetNeedsRedrawRect(gfx::Rect(viewport.size()));
236  client_->BeginFrame(cc::BeginFrameArgs::CreateForSynchronousCompositor());
237
238  // After software draws (which might move the viewport arbitrarily), restore
239  // the previous hardware viewport to allow CC's tile manager to prioritize
240  // properly.
241  if (hardware_draw) {
242    cached_hw_transform_ = adjusted_transform;
243    cached_hw_viewport_ = viewport;
244    cached_hw_clip_ = clip;
245    cached_hw_viewport_rect_for_tile_priority_ =
246        viewport_rect_for_tile_priority;
247    cached_hw_transform_for_tile_priority_ = transform_for_tile_priority;
248  } else {
249    bool resourceless_software_draw = false;
250    SetExternalDrawConstraints(cached_hw_transform_,
251                               cached_hw_viewport_,
252                               cached_hw_clip_,
253                               cached_hw_viewport_rect_for_tile_priority_,
254                               cached_hw_transform_for_tile_priority_,
255                               resourceless_software_draw);
256  }
257
258  if (frame_holder_.get())
259    client_->DidSwapBuffersComplete();
260
261  SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
262  if (delegate)
263    delegate->SetContinuousInvalidate(needs_begin_frame_);
264}
265
266void SynchronousCompositorOutputSurface::ReturnResources(
267    const cc::CompositorFrameAck& frame_ack) {
268  ReclaimResources(&frame_ack);
269}
270
271void SynchronousCompositorOutputSurface::SetMemoryPolicy(
272    const SynchronousCompositorMemoryPolicy& policy) {
273  DCHECK(CalledOnValidThread());
274  memory_policy_.bytes_limit_when_visible = policy.bytes_limit;
275  memory_policy_.num_resources_limit = policy.num_resources_limit;
276
277  if (output_surface_client_)
278    output_surface_client_->SetMemoryPolicy(memory_policy_);
279}
280
281void SynchronousCompositorOutputSurface::GetMessagesToDeliver(
282    ScopedVector<IPC::Message>* messages) {
283  DCHECK(CalledOnValidThread());
284  scoped_ptr<FrameSwapMessageQueue::SendMessageScope> send_message_scope =
285      frame_swap_message_queue_->AcquireSendMessageScope();
286  frame_swap_message_queue_->DrainMessages(messages);
287}
288
289// Not using base::NonThreadSafe as we want to enforce a more exacting threading
290// requirement: SynchronousCompositorOutputSurface() must only be used on the UI
291// thread.
292bool SynchronousCompositorOutputSurface::CalledOnValidThread() const {
293  return BrowserThread::CurrentlyOn(BrowserThread::UI);
294}
295
296SynchronousCompositorOutputSurfaceDelegate*
297SynchronousCompositorOutputSurface::GetDelegate() {
298  return SynchronousCompositorImpl::FromRoutingID(routing_id_);
299}
300
301}  // namespace content
302