| // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/renderer/gpu/compositor_software_output_device.h" |
| |
| #include "base/logging.h" |
| #include "cc/output/software_frame_data.h" |
| #include "content/child/child_shared_bitmap_manager.h" |
| #include "content/renderer/render_process.h" |
| #include "content/renderer/render_thread_impl.h" |
| #include "third_party/skia/include/core/SkCanvas.h" |
| #include "third_party/skia/include/core/SkPixelRef.h" |
| #include "third_party/skia/include/core/SkRegion.h" |
| #include "ui/gfx/skia_util.h" |
| |
| namespace { |
| |
| const size_t kInvalidIndex = static_cast<size_t>(-1); |
| |
| } // namespace |
| |
| namespace content { |
| |
| CompositorSoftwareOutputDevice::Buffer::Buffer( |
| unsigned id, |
| scoped_ptr<cc::SharedBitmap> bitmap) |
| : id_(id), shared_bitmap_(bitmap.Pass()), free_(true), parent_(NULL) {} |
| |
| CompositorSoftwareOutputDevice::Buffer::~Buffer() { |
| } |
| |
| void CompositorSoftwareOutputDevice::Buffer::SetParent( |
| Buffer* parent, const gfx::Rect& damage) { |
| parent_ = parent; |
| damage_ = damage; |
| } |
| |
| bool CompositorSoftwareOutputDevice::Buffer::FindDamageDifferenceFrom( |
| Buffer* buffer, SkRegion* result) const { |
| if (!buffer) |
| return false; |
| |
| if (buffer == this) { |
| *result = SkRegion(); |
| return true; |
| } |
| |
| SkRegion damage; |
| const Buffer* current = this; |
| while (current->parent_) { |
| damage.op(RectToSkIRect(current->damage_), SkRegion::kUnion_Op); |
| if (current->parent_ == buffer) { |
| *result = damage; |
| return true; |
| } |
| current = current->parent_; |
| } |
| |
| return false; |
| } |
| |
| CompositorSoftwareOutputDevice::CompositorSoftwareOutputDevice() |
| : current_index_(kInvalidIndex), |
| next_buffer_id_(1), |
| shared_bitmap_manager_( |
| RenderThreadImpl::current()->shared_bitmap_manager()) { |
| DetachFromThread(); |
| } |
| |
| CompositorSoftwareOutputDevice::~CompositorSoftwareOutputDevice() { |
| DCHECK(CalledOnValidThread()); |
| } |
| |
| unsigned CompositorSoftwareOutputDevice::GetNextId() { |
| unsigned id = next_buffer_id_++; |
| // Zero is reserved to label invalid frame id. |
| if (id == 0) |
| id = next_buffer_id_++; |
| return id; |
| } |
| |
| CompositorSoftwareOutputDevice::Buffer* |
| CompositorSoftwareOutputDevice::CreateBuffer() { |
| scoped_ptr<cc::SharedBitmap> shared_bitmap = |
| shared_bitmap_manager_->AllocateSharedBitmap(viewport_pixel_size_); |
| CHECK(shared_bitmap); |
| return new Buffer(GetNextId(), shared_bitmap.Pass()); |
| } |
| |
| size_t CompositorSoftwareOutputDevice::FindFreeBuffer(size_t hint) { |
| for (size_t i = 0; i < buffers_.size(); ++i) { |
| size_t index = (hint + i) % buffers_.size(); |
| if (buffers_[index]->free()) |
| return index; |
| } |
| |
| buffers_.push_back(CreateBuffer()); |
| return buffers_.size() - 1; |
| } |
| |
| void CompositorSoftwareOutputDevice::Resize( |
| const gfx::Size& viewport_pixel_size, |
| float scale_factor) { |
| DCHECK(CalledOnValidThread()); |
| |
| scale_factor_ = scale_factor; |
| |
| if (viewport_pixel_size_ == viewport_pixel_size) |
| return; |
| |
| // Keep non-ACKed buffers in awaiting_ack_ until they get acknowledged. |
| for (size_t i = 0; i < buffers_.size(); ++i) { |
| if (!buffers_[i]->free()) { |
| awaiting_ack_.push_back(buffers_[i]); |
| buffers_[i] = NULL; |
| } |
| } |
| |
| buffers_.clear(); |
| current_index_ = kInvalidIndex; |
| viewport_pixel_size_ = viewport_pixel_size; |
| } |
| |
| void CompositorSoftwareOutputDevice::DiscardBackbuffer() { |
| // Keep non-ACKed buffers in awaiting_ack_ until they get acknowledged. |
| for (size_t i = 0; i < buffers_.size(); ++i) { |
| if (!buffers_[i]->free()) { |
| awaiting_ack_.push_back(buffers_[i]); |
| buffers_[i] = NULL; |
| } |
| } |
| buffers_.clear(); |
| current_index_ = kInvalidIndex; |
| } |
| |
| void CompositorSoftwareOutputDevice::EnsureBackbuffer() { |
| } |
| |
| SkCanvas* CompositorSoftwareOutputDevice::BeginPaint( |
| const gfx::Rect& damage_rect) { |
| DCHECK(CalledOnValidThread()); |
| |
| Buffer* previous = NULL; |
| if (current_index_ != kInvalidIndex) |
| previous = buffers_[current_index_]; |
| current_index_ = FindFreeBuffer(current_index_ + 1); |
| Buffer* current = buffers_[current_index_]; |
| DCHECK(current->free()); |
| current->SetFree(false); |
| |
| // Set up a canvas for the current front buffer. |
| SkImageInfo info = SkImageInfo::MakeN32Premul(viewport_pixel_size_.width(), |
| viewport_pixel_size_.height()); |
| surface_ = skia::AdoptRef(SkSurface::NewRasterDirect(info, current->memory(), |
| info.minRowBytes())); |
| |
| if (!previous) { |
| DCHECK(damage_rect == gfx::Rect(viewport_pixel_size_)); |
| } else { |
| // Find the smallest damage region that needs |
| // to be copied from the |previous| buffer. |
| SkRegion region; |
| bool found = |
| current->FindDamageDifferenceFrom(previous, ®ion) || |
| previous->FindDamageDifferenceFrom(current, ®ion); |
| if (!found) |
| region = SkRegion(RectToSkIRect(gfx::Rect(viewport_pixel_size_))); |
| region.op(RectToSkIRect(damage_rect), SkRegion::kDifference_Op); |
| |
| // Copy over the damage region. |
| if (!region.isEmpty()) { |
| SkImageInfo info = SkImageInfo::MakeN32Premul( |
| viewport_pixel_size_.width(), viewport_pixel_size_.height()); |
| SkBitmap back_bitmap; |
| back_bitmap.installPixels(info, previous->memory(), info.minRowBytes()); |
| |
| for (SkRegion::Iterator it(region); !it.done(); it.next()) { |
| const SkIRect& src_rect = it.rect(); |
| SkRect dst_rect = SkRect::Make(src_rect); |
| surface_->getCanvas()->drawBitmapRect(back_bitmap, &src_rect, dst_rect); |
| } |
| } |
| } |
| |
| // Make |current| child of |previous| and orphan all of |current|'s children. |
| current->SetParent(previous, damage_rect); |
| for (size_t i = 0; i < buffers_.size(); ++i) { |
| Buffer* buffer = buffers_[i]; |
| if (buffer->parent() == current) |
| buffer->SetParent(NULL, gfx::Rect(viewport_pixel_size_)); |
| } |
| damage_rect_ = damage_rect; |
| |
| return surface_->getCanvas(); |
| } |
| |
| void CompositorSoftwareOutputDevice::EndPaint( |
| cc::SoftwareFrameData* frame_data) { |
| DCHECK(CalledOnValidThread()); |
| DCHECK(frame_data); |
| |
| Buffer* buffer = buffers_[current_index_]; |
| frame_data->id = buffer->id(); |
| frame_data->size = viewport_pixel_size_; |
| frame_data->damage_rect = damage_rect_; |
| frame_data->bitmap_id = buffer->shared_bitmap_id(); |
| } |
| |
| void CompositorSoftwareOutputDevice::ReclaimSoftwareFrame(unsigned id) { |
| DCHECK(CalledOnValidThread()); |
| |
| if (!id) |
| return; |
| |
| // The reclaimed buffer id might not be among the currently |
| // active buffers if we got a resize event in the mean time. |
| ScopedVector<Buffer>::iterator it = |
| std::find_if(buffers_.begin(), buffers_.end(), CompareById(id)); |
| if (it != buffers_.end()) { |
| DCHECK(!(*it)->free()); |
| (*it)->SetFree(true); |
| return; |
| } else { |
| it = std::find_if(awaiting_ack_.begin(), awaiting_ack_.end(), |
| CompareById(id)); |
| DCHECK(it != awaiting_ack_.end()); |
| awaiting_ack_.erase(it); |
| } |
| } |
| |
| } // namespace content |