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

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: 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 "base/message_loop.h" 7 #include "base/message_loop.h"
8 #include "base/string_util.h" 8 #include "base/string_util.h"
9 #include "ppapi/cpp/completion_callback.h" 9 #include "ppapi/cpp/completion_callback.h"
10 #include "ppapi/cpp/graphics_2d.h" 10 #include "ppapi/cpp/graphics_2d.h"
(...skipping 28 matching lines...) Expand all
39 DLOG(FATAL) << "Unknown error code" << error; 39 DLOG(FATAL) << "Unknown error code" << error;
40 return ChromotingScriptableObject::ERROR_NONE; 40 return ChromotingScriptableObject::ERROR_NONE;
41 } 41 }
42 42
43 } // namespace 43 } // namespace
44 44
45 PepperView::PepperView(ChromotingInstance* instance, ClientContext* context) 45 PepperView::PepperView(ChromotingInstance* instance, ClientContext* context)
46 : instance_(instance), 46 : instance_(instance),
47 context_(context), 47 context_(context),
48 flush_blocked_(false), 48 flush_blocked_(false),
49 view_changed_(false),
50 view_size_(SkISize::Make(0, 0)),
51 clip_area_(SkIRect::MakeEmpty()),
52 screen_size_(SkISize::Make(0, 0)),
49 is_static_fill_(false), 53 is_static_fill_(false),
50 static_fill_color_(0), 54 static_fill_color_(0),
51 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { 55 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
52 } 56 }
53 57
54 PepperView::~PepperView() { 58 PepperView::~PepperView() {
55 } 59 }
56 60
57 bool PepperView::Initialize() { 61 bool PepperView::Initialize() {
58 return true; 62 return true;
59 } 63 }
60 64
61 void PepperView::TearDown() { 65 void PepperView::TearDown() {
62 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); 66 DCHECK(context_->main_message_loop()->BelongsToCurrentThread());
63 67
64 weak_factory_.InvalidateWeakPtrs(); 68 weak_factory_.InvalidateWeakPtrs();
65 } 69 }
66 70
67 void PepperView::Paint() { 71 void PepperView::Paint() {
68 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); 72 DCHECK(context_->main_message_loop()->BelongsToCurrentThread());
69 73
70 if (is_static_fill_) { 74 if (is_static_fill_ && !clip_area_.isEmpty()) {
71 VLOG(1) << "Static filling " << static_fill_color_; 75 VLOG(1) << "Static filling " << static_fill_color_;
72 pp::ImageData image(instance_, pp::ImageData::GetNativeImageDataFormat(), 76 pp::ImageData image(instance_, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
73 pp::Size(graphics2d_.size().width(), 77 pp::Size(clip_area_.width(),
74 graphics2d_.size().height()), 78 clip_area_.height()),
75 false); 79 false);
76 if (image.is_null()) { 80 if (image.is_null()) {
77 LOG(ERROR) << "Unable to allocate image of size: " 81 LOG(ERROR) << "Unable to allocate image of size: "
78 << graphics2d_.size().width() << " x " 82 << clip_area_.width() << " x "
79 << graphics2d_.size().height(); 83 << clip_area_.height();
80 return; 84 return;
81 } 85 }
82 86
83 for (int y = 0; y < image.size().height(); y++) { 87 for (int y = 0; y < image.size().height(); y++) {
84 for (int x = 0; x < image.size().width(); x++) { 88 for (int x = 0; x < image.size().width(); x++) {
85 *image.GetAddr32(pp::Point(x, y)) = static_fill_color_; 89 *image.GetAddr32(pp::Point(x, y)) = static_fill_color_;
86 } 90 }
87 } 91 }
88 92
89 // For ReplaceContents, make sure the image size matches the device context 93 graphics2d_.PaintImageData(
90 // size! Otherwise, this will just silently do nothing. 94 image,
91 graphics2d_.ReplaceContents(&image); 95 pp::Point(clip_area_.left(), clip_area_.top()));
92 FlushGraphics(base::Time::Now()); 96 FlushGraphics(base::Time::Now());
93 } else { 97 } else {
94 // TODO(ajwong): We need to keep a backing store image of the host screen 98 // TODO(ajwong): We need to keep a backing store image of the host screen
95 // that has the data here which can be redrawn. 99 // that has the data here which can be redrawn.
96 return; 100 return;
97 } 101 }
98 } 102 }
99 103
100 void PepperView::SetHostSize(const SkISize& host_size) { 104 void PepperView::SetScreenSize(const SkISize& screen_size) {
101 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); 105 DCHECK(context_->main_message_loop()->BelongsToCurrentThread());
102 106
103 if (host_size_ == host_size) 107 if (screen_size_ == screen_size)
104 return; 108 return;
105 109
106 host_size_ = host_size; 110 screen_size_ = screen_size;
107 111
108 // Submit an update of desktop size to Javascript. 112 // Submit an update of desktop size to Javascript.
109 instance_->GetScriptableObject()->SetDesktopSize( 113 instance_->GetScriptableObject()->SetDesktopSize(
110 host_size.width(), host_size.height()); 114 screen_size.width(), screen_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 } 115 }
179 116
180 void PepperView::FlushGraphics(base::Time paint_start) { 117 void PepperView::FlushGraphics(base::Time paint_start) {
181 scoped_ptr<base::Closure> task( 118 scoped_ptr<base::Closure> task(
182 new base::Closure( 119 new base::Closure(
183 base::Bind(&PepperView::OnPaintDone, weak_factory_.GetWeakPtr(), 120 base::Bind(&PepperView::OnFlushDone, weak_factory_.GetWeakPtr(),
184 paint_start))); 121 paint_start)));
185 122
186 // Flag needs to be set here in order to get a proper error code for Flush(). 123 // 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 124 // Otherwise Flush() will always return PP_OK_COMPLETIONPENDING and the error
188 // would be hidden. 125 // would be hidden.
189 // 126 //
190 // Note that we can also handle this by providing an actual callback which 127 // 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 128 // takes the result code. Right now everything goes to the task that doesn't
192 // result value. 129 // result value.
193 pp::CompletionCallback pp_callback(&CompletionCallbackClosureAdapter, 130 pp::CompletionCallback pp_callback(&CompletionCallbackClosureAdapter,
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
255 192
256 case protocol::ConnectionToHost::FAILED: 193 case protocol::ConnectionToHost::FAILED:
257 SetSolidFill(kFailedColor); 194 SetSolidFill(kFailedColor);
258 scriptable_obj->SetConnectionStatus( 195 scriptable_obj->SetConnectionStatus(
259 ChromotingScriptableObject::STATUS_FAILED, 196 ChromotingScriptableObject::STATUS_FAILED,
260 ConvertConnectionError(error)); 197 ConvertConnectionError(error));
261 break; 198 break;
262 } 199 }
263 } 200 }
264 201
265 bool PepperView::SetViewSize(const SkISize& view_size) { 202 bool PepperView::SetView(const SkISize& view_size,
266 if (view_size_ == view_size) 203 const SkIRect& clip_area) {
267 return false; 204 // Make sure that the clip area is entirely contained within
268 view_size_ = view_size; 205 // the view area.
206 DCHECK(!view_size.isEmpty() || clip_area.isEmpty());
207 DCHECK(view_size.isEmpty() ||
208 SkIRect::MakeSize(view_size).contains(clip_area));
269 209
270 pp::Size pp_size = pp::Size(view_size.width(), view_size.height()); 210 bool updated = false;
211 if (clip_area_ != clip_area) {
212 updated = true;
271 213
272 graphics2d_ = pp::Graphics2D(instance_, pp_size, true); 214 // YUV to RGB conversion may require even X and Y coordinates for
273 if (!instance_->BindGraphics(graphics2d_)) { 215 // the top left corner of the clipping area.
274 LOG(ERROR) << "Couldn't bind the device context."; 216 clip_area_ = AlignRect(clip_area);
275 return false; 217 clip_area_.intersect(SkIRect::MakeSize(view_size));
276 } 218 }
277 219
278 if (view_size.isEmpty()) 220 if (view_size_ != view_size) {
279 return false; 221 updated = true;
222 view_size_ = view_size;
280 223
281 // Allocate the backing store to save the desktop image. 224 pp::Size pp_size = pp::Size(view_size.width(), view_size.height());
282 if ((backing_store_.get() == NULL) || 225 graphics2d_ = pp::Graphics2D(instance_, pp_size, true);
283 (backing_store_->size() != pp_size)) { 226 bool result = instance_->BindGraphics(graphics2d_);
284 VLOG(1) << "Allocate backing store: " 227
285 << view_size.width() << " x " << view_size.height(); 228 // There is no good way to handle this error currently.
286 backing_store_.reset( 229 DCHECK(result) << "Couldn't bind the device context.";
287 new pp::ImageData(instance_, pp::ImageData::GetNativeImageDataFormat(),
288 pp_size, false));
289 DCHECK(backing_store_.get() && !backing_store_->is_null())
290 << "Not enough memory for backing store.";
291 } 230 }
292 return true; 231
232 view_changed_ = view_changed_ || updated;
233 return updated;
293 } 234 }
294 235
295 void PepperView::AllocateFrame(media::VideoFrame::Format format, 236 void PepperView::OnFrameReady(const SkISize& screen_size,
296 const SkISize& size, 237 SkISize* view_size_out,
297 scoped_refptr<media::VideoFrame>* frame_out, 238 SkIRect* clip_area_out,
298 const base::Closure& done) { 239 scoped_ptr<pp::ImageData>* backing_store_out,
240 const base::Closure& done) {
299 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); 241 DCHECK(context_->main_message_loop()->BelongsToCurrentThread());
300 242
301 *frame_out = media::VideoFrame::CreateFrame( 243 SetScreenSize(screen_size);
302 media::VideoFrame::RGB32, size.width(), size.height(), 244
303 base::TimeDelta(), base::TimeDelta()); 245 // Adjust the backing buffer size accordingly to the clipping area size:
304 (*frame_out)->AddRef(); 246 // - no buffer is needed when the clipping area is empty.
247 // - previously allocated buffer can be reused if its size matches.
248 if (clip_area_.isEmpty()) {
249 backing_store_.reset(NULL);
250 } else {
251 pp::Size pp_size = pp::Size(clip_area_.width(), clip_area_.height());
252 if ((backing_store_.get() == NULL) ||
253 (backing_store_->size() != pp_size)) {
254 VLOG(1) << "Allocate backing store: "
255 << clip_area_.width() << " x " << clip_area_.height();
256 backing_store_.reset(
257 new pp::ImageData(instance_,
258 PP_IMAGEDATAFORMAT_BGRA_PREMUL,
259 pp_size, false));
260 DCHECK(backing_store_.get() && !backing_store_->is_null())
261 << "Not enough memory for backing store.";
262 }
263 }
264
265 // Watch for any changes to the view size or clipping area while drawing.
266 view_changed_ = false;
267
268 // Capture the drawing parameters and pass them to the caller.
269 *view_size_out = view_size_;
270 *clip_area_out = clip_area_;
271 *backing_store_out = backing_store_.Pass();
305 done.Run(); 272 done.Run();
306 } 273 }
307 274
308 void PepperView::ReleaseFrame(media::VideoFrame* frame) { 275 void PepperView::OnPaintDone(scoped_ptr<pp::ImageData> backing_store,
276 scoped_ptr<SkRegion> updated_region) {
309 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); 277 DCHECK(context_->main_message_loop()->BelongsToCurrentThread());
310 278
311 if (frame) 279 // Discard the updated backing store entirely in case if
312 frame->Release(); 280 // the clipping area has been changed while the drawing
281 // operation was pending. Next painting operation has been
282 // scheduled in response to the change and it will update
283 // the screen properly.
284 if (view_changed_) {
285 instance_->RefreshFullFrame();
286 return;
287 }
288
289 backing_store_ = backing_store.Pass();
290
291 // Notify Pepper API about the updated areas and flush pixels to the screen.
292 base::Time start_time = base::Time::Now();
293
294 for (SkRegion::Iterator i(*updated_region); !i.done(); i.next()) {
295 SkIRect rect = i.rect();
296 if (!rect.intersect(clip_area_))
297 continue;
298
299 // Specify the rectangle coordinates relative to the clipping area.
300 rect.offset(- clip_area_.left(), - clip_area_.top());
301
302 // Pepper Graphics 2D has a strange and badly documented API that the
303 // point here is the offset from the source rect. Why?
304 graphics2d_.PaintImageData(
305 *backing_store_.get(),
306 pp::Point(clip_area_.left(), clip_area_.top()),
307 pp::Rect(rect.left(), rect.top(), rect.width(), rect.height()));
308 }
309
310 FlushGraphics(start_time);
311 return;
313 } 312 }
314 313
315 void PepperView::OnPartialFrameOutput(media::VideoFrame* frame, 314 void PepperView::OnFlushDone(base::Time paint_start) {
316 SkRegion* region,
317 const base::Closure& done) {
318 DCHECK(context_->main_message_loop()->BelongsToCurrentThread());
319
320 // TODO(ajwong): Clean up this API to be async so we don't need to use a
321 // member variable as a hack.
322 PaintFrame(frame, *region);
323 done.Run();
324 }
325
326 void PepperView::OnPaintDone(base::Time paint_start) {
327 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); 315 DCHECK(context_->main_message_loop()->BelongsToCurrentThread());
328 instance_->GetStats()->video_paint_ms()->Record( 316 instance_->GetStats()->video_paint_ms()->Record(
329 (base::Time::Now() - paint_start).InMilliseconds()); 317 (base::Time::Now() - paint_start).InMilliseconds());
330 318
331 // If the last flush failed because there was already another one in progress 319 // If the last flush failed because there was already another one in progress
332 // then we perform the flush now. 320 // then we perform the flush now.
333 if (flush_blocked_) 321 if (flush_blocked_)
334 FlushGraphics(base::Time::Now()); 322 FlushGraphics(base::Time::Now()); // TODO: why is it not |paint_start|?
335 return; 323 return;
336 } 324 }
337 325
338 } // namespace remoting 326 } // namespace remoting
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698