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 "content/renderer/gpu/compositor_software_output_device.h"
6
7#include "base/logging.h"
8#include "cc/output/software_frame_data.h"
9#include "content/child/child_shared_bitmap_manager.h"
10#include "content/renderer/render_process.h"
11#include "content/renderer/render_thread_impl.h"
12#include "third_party/skia/include/core/SkCanvas.h"
13#include "third_party/skia/include/core/SkPixelRef.h"
14#include "third_party/skia/include/core/SkRegion.h"
15#include "ui/gfx/skia_util.h"
16
17namespace {
18
19const size_t kInvalidIndex = static_cast<size_t>(-1);
20
21}  // namespace
22
23namespace content {
24
25CompositorSoftwareOutputDevice::Buffer::Buffer(
26    unsigned id,
27    scoped_ptr<cc::SharedBitmap> bitmap)
28    : id_(id), shared_bitmap_(bitmap.Pass()), free_(true), parent_(NULL) {}
29
30CompositorSoftwareOutputDevice::Buffer::~Buffer() {
31}
32
33void CompositorSoftwareOutputDevice::Buffer::SetParent(
34    Buffer* parent, const gfx::Rect& damage) {
35  parent_ = parent;
36  damage_ = damage;
37}
38
39bool CompositorSoftwareOutputDevice::Buffer::FindDamageDifferenceFrom(
40    Buffer* buffer, SkRegion* result) const {
41  if (!buffer)
42    return false;
43
44  if (buffer == this) {
45    *result = SkRegion();
46    return true;
47  }
48
49  SkRegion damage;
50  const Buffer* current = this;
51  while (current->parent_) {
52    damage.op(RectToSkIRect(current->damage_), SkRegion::kUnion_Op);
53    if (current->parent_ == buffer) {
54      *result = damage;
55      return true;
56    }
57    current = current->parent_;
58  }
59
60  return false;
61}
62
63CompositorSoftwareOutputDevice::CompositorSoftwareOutputDevice()
64    : current_index_(kInvalidIndex),
65      next_buffer_id_(1),
66      shared_bitmap_manager_(
67          RenderThreadImpl::current()->shared_bitmap_manager()) {
68  DetachFromThread();
69}
70
71CompositorSoftwareOutputDevice::~CompositorSoftwareOutputDevice() {
72  DCHECK(CalledOnValidThread());
73}
74
75unsigned CompositorSoftwareOutputDevice::GetNextId() {
76  unsigned id = next_buffer_id_++;
77  // Zero is reserved to label invalid frame id.
78  if (id == 0)
79    id = next_buffer_id_++;
80  return id;
81}
82
83CompositorSoftwareOutputDevice::Buffer*
84CompositorSoftwareOutputDevice::CreateBuffer() {
85  scoped_ptr<cc::SharedBitmap> shared_bitmap =
86      shared_bitmap_manager_->AllocateSharedBitmap(viewport_pixel_size_);
87  CHECK(shared_bitmap);
88  return new Buffer(GetNextId(), shared_bitmap.Pass());
89}
90
91size_t CompositorSoftwareOutputDevice::FindFreeBuffer(size_t hint) {
92  for (size_t i = 0; i < buffers_.size(); ++i) {
93    size_t index = (hint + i) % buffers_.size();
94    if (buffers_[index]->free())
95      return index;
96  }
97
98  buffers_.push_back(CreateBuffer());
99  return buffers_.size() - 1;
100}
101
102void CompositorSoftwareOutputDevice::Resize(
103    const gfx::Size& viewport_pixel_size,
104    float scale_factor) {
105  DCHECK(CalledOnValidThread());
106
107  scale_factor_ = scale_factor;
108
109  if (viewport_pixel_size_ == viewport_pixel_size)
110    return;
111
112  // Keep non-ACKed buffers in awaiting_ack_ until they get acknowledged.
113  for (size_t i = 0; i < buffers_.size(); ++i) {
114    if (!buffers_[i]->free()) {
115      awaiting_ack_.push_back(buffers_[i]);
116      buffers_[i] = NULL;
117    }
118  }
119
120  buffers_.clear();
121  current_index_ = kInvalidIndex;
122  viewport_pixel_size_ = viewport_pixel_size;
123}
124
125void CompositorSoftwareOutputDevice::DiscardBackbuffer() {
126  // Keep non-ACKed buffers in awaiting_ack_ until they get acknowledged.
127  for (size_t i = 0; i < buffers_.size(); ++i) {
128    if (!buffers_[i]->free()) {
129      awaiting_ack_.push_back(buffers_[i]);
130      buffers_[i] = NULL;
131    }
132  }
133  buffers_.clear();
134  current_index_ = kInvalidIndex;
135}
136
137void CompositorSoftwareOutputDevice::EnsureBackbuffer() {
138}
139
140SkCanvas* CompositorSoftwareOutputDevice::BeginPaint(
141    const gfx::Rect& damage_rect) {
142  DCHECK(CalledOnValidThread());
143
144  Buffer* previous = NULL;
145  if (current_index_ != kInvalidIndex)
146    previous = buffers_[current_index_];
147  current_index_ = FindFreeBuffer(current_index_ + 1);
148  Buffer* current = buffers_[current_index_];
149  DCHECK(current->free());
150  current->SetFree(false);
151
152  // Set up a canvas for the current front buffer.
153  SkImageInfo info = SkImageInfo::MakeN32Premul(viewport_pixel_size_.width(),
154                                                viewport_pixel_size_.height());
155  SkBitmap bitmap;
156  bitmap.installPixels(info, current->memory(), info.minRowBytes());
157  canvas_ = skia::AdoptRef(new SkCanvas(bitmap));
158
159  if (!previous) {
160    DCHECK(damage_rect == gfx::Rect(viewport_pixel_size_));
161  } else {
162    // Find the smallest damage region that needs
163    // to be copied from the |previous| buffer.
164    SkRegion region;
165    bool found =
166        current->FindDamageDifferenceFrom(previous, &region) ||
167        previous->FindDamageDifferenceFrom(current, &region);
168    if (!found)
169      region = SkRegion(RectToSkIRect(gfx::Rect(viewport_pixel_size_)));
170    region.op(RectToSkIRect(damage_rect), SkRegion::kDifference_Op);
171
172    // Copy over the damage region.
173    if (!region.isEmpty()) {
174      SkImageInfo info = SkImageInfo::MakeN32Premul(
175          viewport_pixel_size_.width(), viewport_pixel_size_.height());
176      SkBitmap back_bitmap;
177      back_bitmap.installPixels(info, previous->memory(), info.minRowBytes());
178
179      for (SkRegion::Iterator it(region); !it.done(); it.next()) {
180        const SkIRect& src_rect = it.rect();
181        SkRect dst_rect = SkRect::Make(src_rect);
182        canvas_->drawBitmapRect(back_bitmap, &src_rect, dst_rect, NULL);
183      }
184    }
185  }
186
187  // Make |current| child of |previous| and orphan all of |current|'s children.
188  current->SetParent(previous, damage_rect);
189  for (size_t i = 0; i < buffers_.size(); ++i) {
190    Buffer* buffer = buffers_[i];
191    if (buffer->parent() == current)
192      buffer->SetParent(NULL, gfx::Rect(viewport_pixel_size_));
193  }
194  damage_rect_ = damage_rect;
195
196  return canvas_.get();
197}
198
199void CompositorSoftwareOutputDevice::EndPaint(
200    cc::SoftwareFrameData* frame_data) {
201  DCHECK(CalledOnValidThread());
202  DCHECK(frame_data);
203
204  Buffer* buffer = buffers_[current_index_];
205  frame_data->id = buffer->id();
206  frame_data->size = viewport_pixel_size_;
207  frame_data->damage_rect = damage_rect_;
208  frame_data->bitmap_id = buffer->shared_bitmap_id();
209}
210
211void CompositorSoftwareOutputDevice::ReclaimSoftwareFrame(unsigned id) {
212  DCHECK(CalledOnValidThread());
213
214  if (!id)
215    return;
216
217  // The reclaimed buffer id might not be among the currently
218  // active buffers if we got a resize event in the mean time.
219  ScopedVector<Buffer>::iterator it =
220      std::find_if(buffers_.begin(), buffers_.end(), CompareById(id));
221  if (it != buffers_.end()) {
222    DCHECK(!(*it)->free());
223    (*it)->SetFree(true);
224    return;
225  } else {
226    it = std::find_if(awaiting_ack_.begin(), awaiting_ack_.end(),
227                      CompareById(id));
228    DCHECK(it != awaiting_ack_.end());
229    awaiting_ack_.erase(it);
230  }
231}
232
233}  // namespace content
234