Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(277)

Side by Side Diff: remoting/client/plugin/pepper_view.cc

Issue 9331003: Improving the decoder pipeline. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebased Created 8 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "remoting/client/plugin/pepper_view.h" 5 #include "remoting/client/plugin/pepper_view.h"
6 6
7 #include <functional>
8
7 #include "base/message_loop.h" 9 #include "base/message_loop.h"
8 #include "base/string_util.h" 10 #include "base/string_util.h"
11 #include "base/synchronization/waitable_event.h"
9 #include "ppapi/cpp/completion_callback.h" 12 #include "ppapi/cpp/completion_callback.h"
10 #include "ppapi/cpp/graphics_2d.h" 13 #include "ppapi/cpp/graphics_2d.h"
11 #include "ppapi/cpp/image_data.h" 14 #include "ppapi/cpp/image_data.h"
12 #include "ppapi/cpp/point.h" 15 #include "ppapi/cpp/point.h"
13 #include "ppapi/cpp/rect.h" 16 #include "ppapi/cpp/rect.h"
14 #include "ppapi/cpp/size.h" 17 #include "ppapi/cpp/size.h"
15 #include "remoting/base/util.h" 18 #include "remoting/base/util.h"
16 #include "remoting/client/chromoting_stats.h" 19 #include "remoting/client/chromoting_stats.h"
17 #include "remoting/client/client_context.h" 20 #include "remoting/client/client_context.h"
21 #include "remoting/client/frame_producer.h"
18 #include "remoting/client/plugin/chromoting_instance.h" 22 #include "remoting/client/plugin/chromoting_instance.h"
19 #include "remoting/client/plugin/pepper_util.h" 23 #include "remoting/client/plugin/pepper_util.h"
20 24
25 using base::Passed;
26
21 namespace remoting { 27 namespace remoting {
22 28
23 namespace { 29 namespace {
24 30
31 // The maximum number of image buffers to be allocated at any point of time.
32 const size_t kMaxPendingBuffersCount = 2;
33
25 ChromotingScriptableObject::ConnectionError ConvertConnectionError( 34 ChromotingScriptableObject::ConnectionError ConvertConnectionError(
26 protocol::ConnectionToHost::Error error) { 35 protocol::ConnectionToHost::Error error) {
27 switch (error) { 36 switch (error) {
28 case protocol::ConnectionToHost::OK: 37 case protocol::ConnectionToHost::OK:
29 return ChromotingScriptableObject::ERROR_NONE; 38 return ChromotingScriptableObject::ERROR_NONE;
30 case protocol::ConnectionToHost::HOST_IS_OFFLINE: 39 case protocol::ConnectionToHost::HOST_IS_OFFLINE:
31 return ChromotingScriptableObject::ERROR_HOST_IS_OFFLINE; 40 return ChromotingScriptableObject::ERROR_HOST_IS_OFFLINE;
32 case protocol::ConnectionToHost::SESSION_REJECTED: 41 case protocol::ConnectionToHost::SESSION_REJECTED:
33 return ChromotingScriptableObject::ERROR_SESSION_REJECTED; 42 return ChromotingScriptableObject::ERROR_SESSION_REJECTED;
34 case protocol::ConnectionToHost::INCOMPATIBLE_PROTOCOL: 43 case protocol::ConnectionToHost::INCOMPATIBLE_PROTOCOL:
35 return ChromotingScriptableObject::ERROR_INCOMPATIBLE_PROTOCOL; 44 return ChromotingScriptableObject::ERROR_INCOMPATIBLE_PROTOCOL;
36 case protocol::ConnectionToHost::NETWORK_FAILURE: 45 case protocol::ConnectionToHost::NETWORK_FAILURE:
37 return ChromotingScriptableObject::ERROR_NETWORK_FAILURE; 46 return ChromotingScriptableObject::ERROR_NETWORK_FAILURE;
38 } 47 }
39 DLOG(FATAL) << "Unknown error code" << error; 48 DLOG(FATAL) << "Unknown error code" << error;
40 return ChromotingScriptableObject::ERROR_NONE; 49 return ChromotingScriptableObject::ERROR_NONE;
41 } 50 }
42 51
43 } // namespace 52 } // namespace
44 53
45 PepperView::PepperView(ChromotingInstance* instance, ClientContext* context) 54 PepperView::PepperView(ChromotingInstance* instance,
55 ClientContext* context,
56 FrameProducer* producer)
46 : instance_(instance), 57 : instance_(instance),
47 context_(context), 58 context_(context),
48 flush_blocked_(false), 59 producer_(producer),
49 is_static_fill_(false), 60 merge_buffer_(NULL),
61 merge_clip_area_(SkIRect::MakeEmpty()),
62 view_size_(SkISize::Make(0, 0)),
63 clip_area_(SkIRect::MakeEmpty()),
64 screen_size_(SkISize::Make(0, 0)),
50 static_fill_color_(0), 65 static_fill_color_(0),
66 flush_pending_(false),
67 producer_disabled_(true),
68 solid_fill_requested_(false),
51 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { 69 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
52 } 70 }
53 71
54 PepperView::~PepperView() { 72 PepperView::~PepperView() {
73 DCHECK(merge_buffer_ == NULL);
74 DCHECK(buffers_.empty());
55 } 75 }
56 76
57 bool PepperView::Initialize() { 77 bool PepperView::Initialize() {
58 return true; 78 return true;
59 } 79 }
60 80
61 void PepperView::TearDown() { 81 void PepperView::TearDown() {
62 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); 82 DCHECK(context_->main_message_loop()->BelongsToCurrentThread());
63 83
84 // The producer should now return any pending buffers.
Wez 2012/02/17 23:42:17 This comment doesn't seem to describe the line tha
alexeypa (please no reviews) 2012/02/21 23:00:44 Done.
85 producer_disabled_ = true;
86
87 // At this point ReturnBuffer() tasks scheduled by the producer will not be
88 // delivered, so instead we free all the buffers once the producer's queu is
89 // empty.
90 base::WaitableEvent done_event(true, false);
91 producer_->DrainQueue(base::Bind(&base::WaitableEvent::Signal,
92 base::Unretained(&done_event)));
93 done_event.Wait();
Wez 2012/02/17 23:42:17 Hmmm. It's a real shame to have to block here...
alexeypa (please no reviews) 2012/02/21 23:00:44 Yes. The same approach is used when tearing down t
94
95 merge_buffer_ = NULL;
96 while (!buffers_.empty()) {
97 FreeBuffer(buffers_.front());
98 }
99
64 weak_factory_.InvalidateWeakPtrs(); 100 weak_factory_.InvalidateWeakPtrs();
65 } 101 }
66 102
67 void PepperView::Paint() { 103 void PepperView::Paint() {
68 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); 104 DCHECK(context_->main_message_loop()->BelongsToCurrentThread());
69 105
70 if (is_static_fill_) { 106 if (producer_disabled_ && !clip_area_.isEmpty()) {
Wez 2012/02/17 23:42:17 We should just strip out the static-fill code, I t
alexeypa (please no reviews) 2012/02/21 23:00:44 Probably, but not in this CL. :-)
Wez 2012/02/23 00:11:09 Unless it simplifies the CL...?
alexeypa (please no reviews) 2012/02/23 17:10:33 OK.
71 VLOG(1) << "Static filling " << static_fill_color_; 107 VLOG(1) << "Static filling " << static_fill_color_;
72 pp::ImageData image(instance_, pp::ImageData::GetNativeImageDataFormat(), 108 pp::ImageData* buffer = AllocateBuffer();
73 pp::Size(graphics2d_.size().width(), 109 if (buffer) {
74 graphics2d_.size().height()), 110 solid_fill_requested_ = false;
75 false); 111 for (int y = 0; y < buffer->size().height(); y++) {
76 if (image.is_null()) { 112 for (int x = 0; x < buffer->size().width(); x++) {
77 LOG(ERROR) << "Unable to allocate image of size: " 113 *buffer->GetAddr32(pp::Point(x, y)) = static_fill_color_;
78 << graphics2d_.size().width() << " x " 114 }
79 << graphics2d_.size().height(); 115 }
80 return; 116
117 SkRegion region;
118 region.op(clip_area_, SkRegion::kUnion_Op);
119 FlushBuffer(clip_area_, buffer, region);
81 } 120 }
82
83 for (int y = 0; y < image.size().height(); y++) {
84 for (int x = 0; x < image.size().width(); x++) {
85 *image.GetAddr32(pp::Point(x, y)) = static_fill_color_;
86 }
87 }
88
89 // For ReplaceContents, make sure the image size matches the device context
90 // size! Otherwise, this will just silently do nothing.
91 graphics2d_.ReplaceContents(&image);
92 FlushGraphics(base::Time::Now());
93 } else { 121 } else {
94 // TODO(ajwong): We need to keep a backing store image of the host screen 122 // TODO(ajwong): We need to keep a backing store image of the host screen
95 // that has the data here which can be redrawn. 123 // that has the data here which can be redrawn.
96 return; 124 return;
97 } 125 }
98 } 126 }
99 127
100 void PepperView::SetHostSize(const SkISize& host_size) {
101 DCHECK(context_->main_message_loop()->BelongsToCurrentThread());
102
103 if (host_size_ == host_size)
104 return;
105
106 host_size_ = host_size;
107
108 // Submit an update of desktop size to Javascript.
109 instance_->GetScriptableObject()->SetDesktopSize(
110 host_size.width(), host_size.height());
111 }
112
113 void PepperView::PaintFrame(media::VideoFrame* frame, const SkRegion& region) {
114 DCHECK(context_->main_message_loop()->BelongsToCurrentThread());
115
116 SetHostSize(SkISize::Make(frame->width(), frame->height()));
117
118 if (!backing_store_.get() || backing_store_->is_null()) {
119 LOG(ERROR) << "Backing store is not available.";
120 return;
121 }
122
123 base::Time start_time = base::Time::Now();
124
125 // Copy updated regions to the backing store and then paint the regions.
126 bool changes_made = false;
127 for (SkRegion::Iterator i(region); !i.done(); i.next())
128 changes_made |= PaintRect(frame, i.rect());
129
130 if (changes_made)
131 FlushGraphics(start_time);
132 }
133
134 bool PepperView::PaintRect(media::VideoFrame* frame, const SkIRect& r) {
135 const uint8* frame_data = frame->data(media::VideoFrame::kRGBPlane);
136 const int kFrameStride = frame->stride(media::VideoFrame::kRGBPlane);
137 const int kBytesPerPixel = GetBytesPerPixel(media::VideoFrame::RGB32);
138
139 pp::Size backing_store_size = backing_store_->size();
140 SkIRect rect(r);
141 if (!rect.intersect(SkIRect::MakeWH(backing_store_size.width(),
142 backing_store_size.height()))) {
143 return false;
144 }
145
146 const uint8* in =
147 frame_data +
148 kFrameStride * rect.fTop + // Y offset.
149 kBytesPerPixel * rect.fLeft; // X offset.
150 uint8* out =
151 reinterpret_cast<uint8*>(backing_store_->data()) +
152 backing_store_->stride() * rect.fTop + // Y offset.
153 kBytesPerPixel * rect.fLeft; // X offset.
154
155 // TODO(hclam): We really should eliminate this memory copy.
156 for (int j = 0; j < rect.height(); ++j) {
157 memcpy(out, in, rect.width() * kBytesPerPixel);
158 in += kFrameStride;
159 out += backing_store_->stride();
160 }
161
162 // Pepper Graphics 2D has a strange and badly documented API that the
163 // point here is the offset from the source rect. Why?
164 graphics2d_.PaintImageData(
165 *backing_store_.get(),
166 pp::Point(0, 0),
167 pp::Rect(rect.fLeft, rect.fTop, rect.width(), rect.height()));
168 return true;
169 }
170
171 void PepperView::BlankRect(pp::ImageData& image_data, const pp::Rect& rect) {
172 const int kBytesPerPixel = GetBytesPerPixel(media::VideoFrame::RGB32);
173 for (int y = rect.y(); y < rect.bottom(); y++) {
174 uint8* to = reinterpret_cast<uint8*>(image_data.data()) +
175 (y * image_data.stride()) + (rect.x() * kBytesPerPixel);
176 memset(to, 0xff, rect.width() * kBytesPerPixel);
177 }
178 }
179
180 void PepperView::FlushGraphics(base::Time paint_start) {
181 scoped_ptr<base::Closure> task(
182 new base::Closure(
183 base::Bind(&PepperView::OnPaintDone, weak_factory_.GetWeakPtr(),
184 paint_start)));
185
186 // Flag needs to be set here in order to get a proper error code for Flush().
187 // Otherwise Flush() will always return PP_OK_COMPLETIONPENDING and the error
188 // would be hidden.
189 //
190 // Note that we can also handle this by providing an actual callback which
191 // takes the result code. Right now everything goes to the task that doesn't
192 // result value.
193 pp::CompletionCallback pp_callback(&CompletionCallbackClosureAdapter,
194 task.get(),
195 PP_COMPLETIONCALLBACK_FLAG_OPTIONAL);
196 int error = graphics2d_.Flush(pp_callback);
197
198 // There is already a flush in progress so set this flag to true so that we
199 // can flush again later.
200 // |paint_start| is then discarded but this is fine because we're not aiming
201 // for precise measurement of timing, otherwise we need to keep a list of
202 // queued start time(s).
203 if (error == PP_ERROR_INPROGRESS)
204 flush_blocked_ = true;
205 else
206 flush_blocked_ = false;
207
208 // If Flush() returns asynchronously then release the task.
209 if (error == PP_OK_COMPLETIONPENDING)
210 ignore_result(task.release());
211 }
212
213 void PepperView::SetSolidFill(uint32 color) { 128 void PepperView::SetSolidFill(uint32 color) {
214 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); 129 DCHECK(context_->main_message_loop()->BelongsToCurrentThread());
215 130
216 is_static_fill_ = true; 131 solid_fill_requested_ = true;
217 static_fill_color_ = color; 132 static_fill_color_ = color;
218 133
134 // The producer should not draw anything while the solid fill is enabled.
135 producer_disabled_ = true;
136 producer_->DrainQueue(base::Closure());
219 Paint(); 137 Paint();
220 } 138 }
221 139
222 void PepperView::UnsetSolidFill() { 140 void PepperView::UnsetSolidFill() {
223 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); 141 DCHECK(context_->main_message_loop()->BelongsToCurrentThread());
224 142
225 is_static_fill_ = false; 143 solid_fill_requested_ = false;
144
145 // Now the producer may draw.
146 producer_disabled_ = false;
147 InitiateDrawing();
226 } 148 }
227 149
228 void PepperView::SetConnectionState(protocol::ConnectionToHost::State state, 150 void PepperView::SetConnectionState(protocol::ConnectionToHost::State state,
229 protocol::ConnectionToHost::Error error) { 151 protocol::ConnectionToHost::Error error) {
230 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); 152 DCHECK(context_->main_message_loop()->BelongsToCurrentThread());
231 153
232 // TODO(hclam): Re-consider the way we communicate with Javascript. 154 // TODO(hclam): Re-consider the way we communicate with Javascript.
233 ChromotingScriptableObject* scriptable_obj = instance_->GetScriptableObject(); 155 ChromotingScriptableObject* scriptable_obj = instance_->GetScriptableObject();
234 switch (state) { 156 switch (state) {
235 case protocol::ConnectionToHost::CONNECTING: 157 case protocol::ConnectionToHost::CONNECTING:
(...skipping 19 matching lines...) Expand all
255 177
256 case protocol::ConnectionToHost::FAILED: 178 case protocol::ConnectionToHost::FAILED:
257 SetSolidFill(kFailedColor); 179 SetSolidFill(kFailedColor);
258 scriptable_obj->SetConnectionStatus( 180 scriptable_obj->SetConnectionStatus(
259 ChromotingScriptableObject::STATUS_FAILED, 181 ChromotingScriptableObject::STATUS_FAILED,
260 ConvertConnectionError(error)); 182 ConvertConnectionError(error));
261 break; 183 break;
262 } 184 }
263 } 185 }
264 186
265 bool PepperView::SetViewSize(const SkISize& view_size) { 187 bool PepperView::SetView(const SkISize& view_size,
266 if (view_size_ == view_size) 188 const SkIRect& clip_area) {
267 return false; 189 bool updated = false;
Wez 2012/02/17 23:42:17 Can you give this a more helpful name, e.g. size_o
alexeypa (please no reviews) 2012/02/21 23:00:44 "Size and Clip" constitute the view, so a called i
268 view_size_ = view_size; 190
269 191 // Prevent accidental upscaling.
Wez 2012/02/17 23:42:17 nit: It's more that we don't currently support up-
alexeypa (please no reviews) 2012/02/21 23:00:44 It is more than that. Currently it is the only way
Wez 2012/02/23 00:11:09 Which wouldn't be a problem if we supported up-sca
alexeypa (please no reviews) 2012/02/23 17:10:33 Done as per our discussion.
270 pp::Size pp_size = pp::Size(view_size.width(), view_size.height()); 192 SkISize size = SkISize::Make(
271 193 std::min(view_size.width(), screen_size_.width()),
272 graphics2d_ = pp::Graphics2D(instance_, pp_size, true); 194 std::min(view_size.height(), screen_size_.height()));
273 if (!instance_->BindGraphics(graphics2d_)) { 195
274 LOG(ERROR) << "Couldn't bind the device context."; 196 if (view_size_ != size) {
275 return false; 197 updated = true;
276 } 198 view_size_ = size;
277 199
278 if (view_size.isEmpty()) 200 pp::Size pp_size = pp::Size(view_size_.width(), view_size_.height());
279 return false; 201 graphics2d_ = pp::Graphics2D(instance_, pp_size, true);
280 202 bool result = instance_->BindGraphics(graphics2d_);
281 // Allocate the backing store to save the desktop image. 203
282 if ((backing_store_.get() == NULL) || 204 // There is no good way to handle this error currently.
283 (backing_store_->size() != pp_size)) { 205 DCHECK(result) << "Couldn't bind the device context.";
284 VLOG(1) << "Allocate backing store: " 206 }
285 << view_size.width() << " x " << view_size.height(); 207
286 backing_store_.reset( 208 if (clip_area_ != clip_area) {
287 new pp::ImageData(instance_, pp::ImageData::GetNativeImageDataFormat(), 209 updated = true;
288 pp_size, false)); 210
289 DCHECK(backing_store_.get() && !backing_store_->is_null()) 211 // YUV to RGB conversion may require even X and Y coordinates for
290 << "Not enough memory for backing store."; 212 // the top left corner of the clipping area.
291 } 213 clip_area_ = AlignRect(clip_area);
292 return true; 214 clip_area_.intersect(SkIRect::MakeSize(view_size_));
293 } 215 }
294 216
295 void PepperView::AllocateFrame(media::VideoFrame::Format format, 217 if (updated) {
296 const SkISize& size, 218 producer_->SetView(view_size_, clip_area_);
297 scoped_refptr<media::VideoFrame>* frame_out, 219 InitiateDrawing();
298 const base::Closure& done) { 220 }
299 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); 221
300 222 return updated;
301 *frame_out = media::VideoFrame::CreateFrame( 223 }
302 media::VideoFrame::RGB32, size.width(), size.height(), 224
303 base::TimeDelta(), base::TimeDelta()); 225 void PepperView::PaintBuffer(const SkISize& view_size,
304 (*frame_out)->AddRef(); 226 const SkIRect& clip_area,
305 done.Run(); 227 pp::ImageData* buffer,
306 } 228 const SkRegion& region) {
307 229 DCHECK(context_->main_message_loop()->BelongsToCurrentThread());
308 void PepperView::ReleaseFrame(media::VideoFrame* frame) { 230
309 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); 231 // The data in the buffer is useless if the scale factor has changed.
Wez 2012/02/17 23:42:17 Not strictly true; we could render what we have an
alexeypa (please no reviews) 2012/02/21 23:00:44 I added a TODO comment.
310 232 if (view_size_ != view_size) {
311 if (frame) 233 FreeBuffer(buffer);
312 frame->Release(); 234 InitiateDrawing();
313 } 235 } else {
314 236 FlushBuffer(clip_area, buffer, region);
315 void PepperView::OnPartialFrameOutput(media::VideoFrame* frame, 237 }
316 SkRegion* region, 238 }
317 const base::Closure& done) { 239
318 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); 240 void PepperView::ReturnBuffer(pp::ImageData* buffer) {
319 241 DCHECK(context_->main_message_loop()->BelongsToCurrentThread());
320 // TODO(ajwong): Clean up this API to be async so we don't need to use a 242
321 // member variable as a hack. 243 // Free the buffer if there is nothing to paint.
322 PaintFrame(frame, *region); 244 if (producer_disabled_) {
323 done.Run(); 245 FreeBuffer(buffer);
324 } 246 return;
325 247 }
326 void PepperView::OnPaintDone(base::Time paint_start) { 248
327 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); 249 // Reuse the buffer if it is large enough, otherwise drop it on the floor
250 // and allocate a new one.
251 if (buffer->size().width() >= clip_area_.width() &&
252 buffer->size().height() >= clip_area_.height()) {
253 producer_->EnqueueBuffer(buffer);
254 } else {
255 FreeBuffer(buffer);
256 InitiateDrawing();
257 }
258 }
259
260 void PepperView::SetScreenSize(const SkISize& screen_size) {
261 DCHECK(context_->main_message_loop()->BelongsToCurrentThread());
262
263 if (screen_size_ == screen_size)
264 return;
265
266 screen_size_ = screen_size;
267
268 // Submit an update of desktop size to Javascript.
Wez 2012/02/17 23:42:17 nit: Suggest "Notify JavaScript of the change in s
alexeypa (please no reviews) 2012/02/21 23:00:44 Done.
269 instance_->GetScriptableObject()->SetDesktopSize(
270 screen_size.width(), screen_size.height());
271 }
272
273 pp::ImageData* PepperView::AllocateBuffer() {
274 pp::ImageData* buffer = NULL;
275 if (buffers_.size() < kMaxPendingBuffersCount) {
276 pp::Size pp_size = pp::Size(clip_area_.width(), clip_area_.height());
277 buffer = new pp::ImageData(instance_,
278 PP_IMAGEDATAFORMAT_BGRA_PREMUL,
279 pp_size,
280 false);
281 if (buffer->is_null()) {
282 LOG(WARNING) << "Not enough memory for backing store.";
Wez 2012/02/17 23:42:17 nit: "backing store" -> "frame buffers"
alexeypa (please no reviews) 2012/02/21 23:00:44 Done.
283 delete buffer;
284 buffer = NULL;
285 } else {
286 buffers_.push_back(buffer);
287 }
288 }
289
290 return buffer;
291 }
292
293 void PepperView::FreeBuffer(pp::ImageData* buffer) {
294 DCHECK(std::find(buffers_.begin(), buffers_.end(), buffer) != buffers_.end());
295
296 buffers_.remove(buffer);
297 delete buffer;
298
299 // Now that we can allocate new buffers retry painting of the static
300 // background.
301 if (solid_fill_requested_)
302 Paint();
303 }
304
305 void PepperView::InitiateDrawing() {
306 // Do not schedule drawing if there is nothing to paint.
307 if (producer_disabled_)
308 return;
309
310 pp::ImageData* buffer = AllocateBuffer();
311 while (buffer) {
312 producer_->EnqueueBuffer(buffer);
313 buffer = AllocateBuffer();
314 }
315 }
316
317 void PepperView::FlushBuffer(const SkIRect& clip_area,
318 pp::ImageData* buffer,
319 const SkRegion& region) {
320
321 // Defer drawing if the flush is already in progress.
322 if (flush_pending_) {
323 // |merge_buffer_| is guaranteed to be free here because we allocate only
324 // two buffers simultaneously. If more buffers are allowed this code should
325 // merge all pending changes into a single buffer and then apply it to
Wez 2012/02/17 23:42:17 nit: Suggest "this code should apply all pending c
alexeypa (please no reviews) 2012/02/21 23:00:44 Done.
326 // the screen.
327 DCHECK(merge_buffer_ == NULL);
328
329 merge_clip_area_ = clip_area;
330 merge_buffer_ = buffer;
331 merge_region_ = region;
332 return;
333 }
334
335 // Notify Pepper API about the updated areas and flush pixels to the screen.
336 base::Time start_time = base::Time::Now();
Wez 2012/02/17 23:42:17 Gahhh. We should update these stats gathering poi
alexeypa (please no reviews) 2012/02/21 23:00:44 ... in a separate CL.
337
338 for (SkRegion::Iterator i(region); !i.done(); i.next()) {
339 SkIRect rect = i.rect();
340
341 // N.B. |clip_area_| may be different from |clip_area| meaning that
342 // the clipping area has changed since the time the image was drawn.
343 // The parts of the image that fit the new clipping area still need
344 // to be painted.
Wez 2012/02/17 23:42:17 How does this comment relate to the intersect() ca
alexeypa (please no reviews) 2012/02/21 23:00:44 Well, it explains why the intersect() call below i
Wez 2012/02/23 00:11:09 The comment refers to |clip_area_| and |clip_area|
alexeypa (please no reviews) 2012/02/23 17:10:33 I've reworded the comment.
345 if (!rect.intersect(clip_area_))
346 continue;
347
348 // Specify the rectangle coordinates relative to the clipping area.
349 rect.offset(- clip_area.left(), - clip_area.top());
Wez 2012/02/17 23:42:17 nit: Not 100% sure, but I think the '-' should not
alexeypa (please no reviews) 2012/02/21 23:00:44 Done.
350
351 // Pepper Graphics 2D has a strange and badly documented API that the
352 // point here is the offset from the source rect. Why?
Wez 2012/02/17 23:42:17 It is within our power to fix the Graphics2D comme
alexeypa (please no reviews) 2012/02/21 23:00:44 As you can imagine I didn't write this comment. WE
Wez 2012/02/23 00:11:09 Indeed. Not sure where my comment on this sprang
353 graphics2d_.PaintImageData(
354 *buffer,
355 pp::Point(clip_area.left(), clip_area.top()),
356 pp::Rect(rect.left(), rect.top(), rect.width(), rect.height()));
357 }
358
359 // Flush the updated areas ro the screen.
Wez 2012/02/17 23:42:17 typo: ro -> to
alexeypa (please no reviews) 2012/02/21 23:00:44 Done.
360 scoped_ptr<base::Closure> task(
361 new base::Closure(
362 base::Bind(&PepperView::OnFlushDone, weak_factory_.GetWeakPtr(),
363 start_time, buffer)));
364
365 // Flag needs to be set here in order to get a proper error code for Flush().
366 // Otherwise Flush() will always return PP_OK_COMPLETIONPENDING and the error
367 // would be hidden.
368 //
369 // Note that we can also handle this by providing an actual callback which
370 // takes the result code. Right now everything goes to the task that doesn't
371 // result value.
Wez 2012/02/17 23:42:17 This is ugly. We should be able to clean this up
alexeypa (please no reviews) 2012/02/21 23:00:44 I inherited this code. I'd like to defer any such
Wez 2012/02/23 00:11:09 SGTM
372 pp::CompletionCallback pp_callback(&CompletionCallbackClosureAdapter,
373 task.get(),
374 PP_COMPLETIONCALLBACK_FLAG_OPTIONAL);
375 int error = graphics2d_.Flush(pp_callback);
376
377 // If Flush() returns asynchronously then release the task.
378 flush_pending_ = (error == PP_OK_COMPLETIONPENDING);
379 if (flush_pending_) {
380 ignore_result(task.release());
Wez 2012/02/17 23:42:17 This doesn't look right. Shouldn't we be release(
alexeypa (please no reviews) 2012/02/21 23:00:44 Looks OK to me. If completion is pending, Completi
Wez 2012/02/23 00:11:09 Yes, you're right!
381 } else {
382 instance_->GetStats()->video_paint_ms()->Record(
383 (base::Time::Now() - start_time).InMilliseconds());
384
385 ReturnBuffer(buffer);
386
387 // Resume painting for the buffer that was previoulsy postponed because of
388 // pending flush.
389 if (merge_buffer_ != NULL) {
390 buffer = merge_buffer_;
391 merge_buffer_ = NULL;
392 FlushBuffer(merge_clip_area_, buffer, merge_region_);
393 }
394 }
395 }
396
397 void PepperView::OnFlushDone(base::Time paint_start,
398 pp::ImageData* buffer) {
399 DCHECK(context_->main_message_loop()->BelongsToCurrentThread());
400 DCHECK(flush_pending_);
401
328 instance_->GetStats()->video_paint_ms()->Record( 402 instance_->GetStats()->video_paint_ms()->Record(
329 (base::Time::Now() - paint_start).InMilliseconds()); 403 (base::Time::Now() - paint_start).InMilliseconds());
330 404
331 // If the last flush failed because there was already another one in progress 405 flush_pending_ = false;
332 // then we perform the flush now. 406 ReturnBuffer(buffer);
333 if (flush_blocked_) 407
334 FlushGraphics(base::Time::Now()); 408 // Resume painting for the buffer that was previoulsy postponed because of
335 return; 409 // pending flush.
410 if (merge_buffer_ != NULL) {
411 buffer = merge_buffer_;
412 merge_buffer_ = NULL;
413 FlushBuffer(merge_clip_area_, buffer, merge_region_);
414 }
336 } 415 }
337 416
338 } // namespace remoting 417 } // namespace remoting
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698