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