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.
5 #include "content/renderer/gpu/compositor_software_output_device.h"
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/SkBitmapDevice.h"
13 #include "third_party/skia/include/core/SkCanvas.h"
14 #include "third_party/skia/include/core/SkPixelRef.h"
15 #include "third_party/skia/include/core/SkRegion.h"
16 #include "ui/gfx/skia_util.h"
20 const size_t kInvalidIndex
= static_cast<size_t>(-1);
26 CompositorSoftwareOutputDevice::Buffer::Buffer(
28 scoped_ptr
<cc::SharedBitmap
> bitmap
)
29 : id_(id
), shared_bitmap_(bitmap
.Pass()), free_(true), parent_(NULL
) {}
31 CompositorSoftwareOutputDevice::Buffer::~Buffer() {
34 void CompositorSoftwareOutputDevice::Buffer::SetParent(
35 Buffer
* parent
, const gfx::Rect
& damage
) {
40 bool CompositorSoftwareOutputDevice::Buffer::FindDamageDifferenceFrom(
41 Buffer
* buffer
, SkRegion
* result
) const {
51 const Buffer
* current
= this;
52 while (current
->parent_
) {
53 damage
.op(RectToSkIRect(current
->damage_
), SkRegion::kUnion_Op
);
54 if (current
->parent_
== buffer
) {
58 current
= current
->parent_
;
64 CompositorSoftwareOutputDevice::CompositorSoftwareOutputDevice()
65 : current_index_(kInvalidIndex
),
67 shared_bitmap_manager_(
68 RenderThreadImpl::current()->shared_bitmap_manager()) {
72 CompositorSoftwareOutputDevice::~CompositorSoftwareOutputDevice() {
73 DCHECK(CalledOnValidThread());
76 unsigned CompositorSoftwareOutputDevice::GetNextId() {
77 unsigned id
= next_buffer_id_
++;
78 // Zero is reserved to label invalid frame id.
80 id
= next_buffer_id_
++;
84 CompositorSoftwareOutputDevice::Buffer
*
85 CompositorSoftwareOutputDevice::CreateBuffer() {
86 scoped_ptr
<cc::SharedBitmap
> shared_bitmap
=
87 shared_bitmap_manager_
->AllocateSharedBitmap(viewport_pixel_size_
);
89 return new Buffer(GetNextId(), shared_bitmap
.Pass());
92 size_t CompositorSoftwareOutputDevice::FindFreeBuffer(size_t hint
) {
93 for (size_t i
= 0; i
< buffers_
.size(); ++i
) {
94 size_t index
= (hint
+ i
) % buffers_
.size();
95 if (buffers_
[index
]->free())
99 buffers_
.push_back(CreateBuffer());
100 return buffers_
.size() - 1;
103 void CompositorSoftwareOutputDevice::Resize(
104 const gfx::Size
& viewport_pixel_size
,
105 float scale_factor
) {
106 DCHECK(CalledOnValidThread());
108 scale_factor_
= scale_factor
;
110 if (viewport_pixel_size_
== viewport_pixel_size
)
113 // Keep non-ACKed buffers in awaiting_ack_ until they get acknowledged.
114 for (size_t i
= 0; i
< buffers_
.size(); ++i
) {
115 if (!buffers_
[i
]->free()) {
116 awaiting_ack_
.push_back(buffers_
[i
]);
122 current_index_
= kInvalidIndex
;
123 viewport_pixel_size_
= viewport_pixel_size
;
126 void CompositorSoftwareOutputDevice::DiscardBackbuffer() {
127 // Keep non-ACKed buffers in awaiting_ack_ until they get acknowledged.
128 for (size_t i
= 0; i
< buffers_
.size(); ++i
) {
129 if (!buffers_
[i
]->free()) {
130 awaiting_ack_
.push_back(buffers_
[i
]);
135 current_index_
= kInvalidIndex
;
138 void CompositorSoftwareOutputDevice::EnsureBackbuffer() {
141 SkCanvas
* CompositorSoftwareOutputDevice::BeginPaint(
142 const gfx::Rect
& damage_rect
) {
143 DCHECK(CalledOnValidThread());
145 Buffer
* previous
= NULL
;
146 if (current_index_
!= kInvalidIndex
)
147 previous
= buffers_
[current_index_
];
148 current_index_
= FindFreeBuffer(current_index_
+ 1);
149 Buffer
* current
= buffers_
[current_index_
];
150 DCHECK(current
->free());
151 current
->SetFree(false);
153 // Set up a canvas for the current front buffer.
154 SkImageInfo info
= SkImageInfo::MakeN32Premul(viewport_pixel_size_
.width(),
155 viewport_pixel_size_
.height());
157 bitmap
.installPixels(info
, current
->memory(), info
.minRowBytes());
158 canvas_
= skia::AdoptRef(new SkCanvas(bitmap
));
161 DCHECK(damage_rect
== gfx::Rect(viewport_pixel_size_
));
163 // Find the smallest damage region that needs
164 // to be copied from the |previous| buffer.
167 current
->FindDamageDifferenceFrom(previous
, ®ion
) ||
168 previous
->FindDamageDifferenceFrom(current
, ®ion
);
170 region
= SkRegion(RectToSkIRect(gfx::Rect(viewport_pixel_size_
)));
171 region
.op(RectToSkIRect(damage_rect
), SkRegion::kDifference_Op
);
173 // Copy over the damage region.
174 if (!region
.isEmpty()) {
175 SkImageInfo info
= SkImageInfo::MakeN32Premul(
176 viewport_pixel_size_
.width(), viewport_pixel_size_
.height());
177 SkBitmap back_bitmap
;
178 back_bitmap
.installPixels(info
, previous
->memory(), info
.minRowBytes());
180 for (SkRegion::Iterator
it(region
); !it
.done(); it
.next()) {
181 const SkIRect
& src_rect
= it
.rect();
182 SkRect dst_rect
= SkRect::Make(src_rect
);
183 canvas_
->drawBitmapRect(back_bitmap
, &src_rect
, dst_rect
, NULL
);
188 // Make |current| child of |previous| and orphan all of |current|'s children.
189 current
->SetParent(previous
, damage_rect
);
190 for (size_t i
= 0; i
< buffers_
.size(); ++i
) {
191 Buffer
* buffer
= buffers_
[i
];
192 if (buffer
->parent() == current
)
193 buffer
->SetParent(NULL
, gfx::Rect(viewport_pixel_size_
));
195 damage_rect_
= damage_rect
;
197 return canvas_
.get();
200 void CompositorSoftwareOutputDevice::EndPaint(
201 cc::SoftwareFrameData
* frame_data
) {
202 DCHECK(CalledOnValidThread());
205 Buffer
* buffer
= buffers_
[current_index_
];
206 frame_data
->id
= buffer
->id();
207 frame_data
->size
= viewport_pixel_size_
;
208 frame_data
->damage_rect
= damage_rect_
;
209 frame_data
->bitmap_id
= buffer
->shared_bitmap_id();
212 void CompositorSoftwareOutputDevice::ReclaimSoftwareFrame(unsigned id
) {
213 DCHECK(CalledOnValidThread());
218 // The reclaimed buffer id might not be among the currently
219 // active buffers if we got a resize event in the mean time.
220 ScopedVector
<Buffer
>::iterator it
=
221 std::find_if(buffers_
.begin(), buffers_
.end(), CompareById(id
));
222 if (it
!= buffers_
.end()) {
223 DCHECK(!(*it
)->free());
224 (*it
)->SetFree(true);
227 it
= std::find_if(awaiting_ack_
.begin(), awaiting_ack_
.end(),
229 DCHECK(it
!= awaiting_ack_
.end());
230 awaiting_ack_
.erase(it
);
234 } // namespace content