OLD | NEW |
---|---|
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 source_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. At this point, however, | |
85 // ReturnBuffer() tasks scheduled by the producer will not be delivered, | |
86 // so we free all the buffers once the producer's queue is empty. | |
87 producer_disabled_ = true; | |
88 base::WaitableEvent done_event(true, false); | |
89 producer_->RequestReturnBuffers( | |
90 base::Bind(&base::WaitableEvent::Signal, base::Unretained(&done_event))); | |
91 done_event.Wait(); | |
92 | |
93 merge_buffer_ = NULL; | |
94 while (!buffers_.empty()) { | |
95 FreeBuffer(buffers_.front()); | |
96 } | |
97 | |
64 weak_factory_.InvalidateWeakPtrs(); | 98 weak_factory_.InvalidateWeakPtrs(); |
65 } | 99 } |
66 | 100 |
67 void PepperView::Paint() { | 101 void PepperView::Paint() { |
68 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); | 102 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); |
69 | 103 |
70 if (is_static_fill_) { | 104 if (producer_disabled_ && !clip_area_.isEmpty()) { |
71 VLOG(1) << "Static filling " << static_fill_color_; | 105 VLOG(1) << "Static filling " << static_fill_color_; |
72 pp::ImageData image(instance_, pp::ImageData::GetNativeImageDataFormat(), | 106 pp::ImageData* buffer = AllocateBuffer(); |
73 pp::Size(graphics2d_.size().width(), | 107 if (buffer) { |
74 graphics2d_.size().height()), | 108 solid_fill_requested_ = false; |
75 false); | 109 for (int y = 0; y < buffer->size().height(); y++) { |
76 if (image.is_null()) { | 110 for (int x = 0; x < buffer->size().width(); x++) { |
77 LOG(ERROR) << "Unable to allocate image of size: " | 111 *buffer->GetAddr32(pp::Point(x, y)) = static_fill_color_; |
78 << graphics2d_.size().width() << " x " | 112 } |
79 << graphics2d_.size().height(); | 113 } |
80 return; | 114 |
115 SkRegion region; | |
116 region.op(clip_area_, SkRegion::kUnion_Op); | |
117 FlushBuffer(clip_area_, buffer, region); | |
81 } | 118 } |
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 { | 119 } else { |
94 // TODO(ajwong): We need to keep a backing store image of the host screen | 120 // TODO(ajwong): We need to keep a backing store image of the host screen |
95 // that has the data here which can be redrawn. | 121 // that has the data here which can be redrawn. |
96 return; | 122 return; |
97 } | 123 } |
98 } | 124 } |
99 | 125 |
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) { | 126 void PepperView::SetSolidFill(uint32 color) { |
214 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); | 127 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); |
215 | 128 |
216 is_static_fill_ = true; | 129 solid_fill_requested_ = true; |
217 static_fill_color_ = color; | 130 static_fill_color_ = color; |
218 | 131 |
132 // The producer should not draw anything while the solid fill is enabled. | |
133 producer_disabled_ = true; | |
134 producer_->RequestReturnBuffers(base::Closure()); | |
219 Paint(); | 135 Paint(); |
220 } | 136 } |
221 | 137 |
222 void PepperView::UnsetSolidFill() { | 138 void PepperView::UnsetSolidFill() { |
223 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); | 139 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); |
224 | 140 |
225 is_static_fill_ = false; | 141 solid_fill_requested_ = false; |
142 | |
143 // Now the producer may draw. | |
144 producer_disabled_ = false; | |
145 InitiateDrawing(); | |
226 } | 146 } |
227 | 147 |
228 void PepperView::SetConnectionState(protocol::ConnectionToHost::State state, | 148 void PepperView::SetConnectionState(protocol::ConnectionToHost::State state, |
229 protocol::ConnectionToHost::Error error) { | 149 protocol::ConnectionToHost::Error error) { |
230 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); | 150 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); |
231 | 151 |
232 // TODO(hclam): Re-consider the way we communicate with Javascript. | 152 // TODO(hclam): Re-consider the way we communicate with Javascript. |
233 ChromotingScriptableObject* scriptable_obj = instance_->GetScriptableObject(); | 153 ChromotingScriptableObject* scriptable_obj = instance_->GetScriptableObject(); |
234 switch (state) { | 154 switch (state) { |
235 case protocol::ConnectionToHost::CONNECTING: | 155 case protocol::ConnectionToHost::CONNECTING: |
(...skipping 19 matching lines...) Expand all Loading... | |
255 | 175 |
256 case protocol::ConnectionToHost::FAILED: | 176 case protocol::ConnectionToHost::FAILED: |
257 SetSolidFill(kFailedColor); | 177 SetSolidFill(kFailedColor); |
258 scriptable_obj->SetConnectionStatus( | 178 scriptable_obj->SetConnectionStatus( |
259 ChromotingScriptableObject::STATUS_FAILED, | 179 ChromotingScriptableObject::STATUS_FAILED, |
260 ConvertConnectionError(error)); | 180 ConvertConnectionError(error)); |
261 break; | 181 break; |
262 } | 182 } |
263 } | 183 } |
264 | 184 |
265 bool PepperView::SetViewSize(const SkISize& view_size) { | 185 void PepperView::SetView(const SkISize& view_size, |
266 if (view_size_ == view_size) | 186 const SkIRect& clip_area) { |
267 return false; | 187 bool view_changed = false; |
268 view_size_ = view_size; | 188 |
269 | 189 // Prevent accidental upscaling. |
270 pp::Size pp_size = pp::Size(view_size.width(), view_size.height()); | 190 SkISize size = SkISize::Make( |
271 | 191 std::min(view_size.width(), source_size_.width()), |
272 graphics2d_ = pp::Graphics2D(instance_, pp_size, true); | 192 std::min(view_size.height(), source_size_.height())); |
273 if (!instance_->BindGraphics(graphics2d_)) { | 193 |
274 LOG(ERROR) << "Couldn't bind the device context."; | 194 if (view_size_ != size) { |
275 return false; | 195 view_changed = true; |
276 } | 196 view_size_ = size; |
277 | 197 |
278 if (view_size.isEmpty()) | 198 pp::Size pp_size = pp::Size(view_size_.width(), view_size_.height()); |
279 return false; | 199 graphics2d_ = pp::Graphics2D(instance_, pp_size, true); |
280 | 200 bool result = instance_->BindGraphics(graphics2d_); |
281 // Allocate the backing store to save the desktop image. | 201 |
282 if ((backing_store_.get() == NULL) || | 202 // There is no good way to handle this error currently. |
283 (backing_store_->size() != pp_size)) { | 203 DCHECK(result) << "Couldn't bind the device context."; |
284 VLOG(1) << "Allocate backing store: " | 204 } |
285 << view_size.width() << " x " << view_size.height(); | 205 |
286 backing_store_.reset( | 206 if (clip_area_ != clip_area) { |
287 new pp::ImageData(instance_, pp::ImageData::GetNativeImageDataFormat(), | 207 view_changed = true; |
288 pp_size, false)); | 208 |
289 DCHECK(backing_store_.get() && !backing_store_->is_null()) | 209 // YUV to RGB conversion may require even X and Y coordinates for |
290 << "Not enough memory for backing store."; | 210 // the top left corner of the clipping area. |
291 } | 211 clip_area_ = AlignRect(clip_area); |
292 return true; | 212 clip_area_.intersect(SkIRect::MakeSize(view_size_)); |
293 } | 213 } |
294 | 214 |
295 void PepperView::AllocateFrame(media::VideoFrame::Format format, | 215 if (view_changed) { |
296 const SkISize& size, | 216 producer_->SetView(view_size_, clip_area_); |
297 scoped_refptr<media::VideoFrame>* frame_out, | 217 InitiateDrawing(); |
298 const base::Closure& done) { | 218 } |
299 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); | 219 } |
300 | 220 |
301 *frame_out = media::VideoFrame::CreateFrame( | 221 void PepperView::PaintBuffer(const SkISize& view_size, |
302 media::VideoFrame::RGB32, size.width(), size.height(), | 222 const SkIRect& clip_area, |
303 base::TimeDelta(), base::TimeDelta()); | 223 pp::ImageData* buffer, |
304 (*frame_out)->AddRef(); | 224 const SkRegion& region) { |
305 done.Run(); | 225 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); |
306 } | 226 |
307 | 227 // Currently we cannot use the data in the buffer is scale factor has changed |
308 void PepperView::ReleaseFrame(media::VideoFrame* frame) { | 228 // already. |
309 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); | 229 // TODO(alexeypa): We could rescale and draw it to reduce the perception lag |
Wez
2012/02/23 00:11:09
nit: Don't even need to re-scale; we could just dr
Wez
2012/02/23 00:11:09
typo: perception -> perceived
typo: wating -> wait
alexeypa (please no reviews)
2012/02/23 17:10:33
Done.
alexeypa (please no reviews)
2012/02/23 17:10:33
Done.
| |
310 | 230 // while we are wating for the preperly scaled data. |
311 if (frame) | 231 if (view_size_ != view_size) { |
312 frame->Release(); | 232 FreeBuffer(buffer); |
313 } | 233 InitiateDrawing(); |
314 | 234 } else { |
315 void PepperView::OnPartialFrameOutput(media::VideoFrame* frame, | 235 FlushBuffer(clip_area, buffer, region); |
316 SkRegion* region, | 236 } |
317 const base::Closure& done) { | 237 } |
318 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); | 238 |
319 | 239 void PepperView::ReturnBuffer(pp::ImageData* buffer) { |
320 // TODO(ajwong): Clean up this API to be async so we don't need to use a | 240 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); |
321 // member variable as a hack. | 241 |
322 PaintFrame(frame, *region); | 242 // Free the buffer if there is nothing to paint. |
323 done.Run(); | 243 if (producer_disabled_) { |
324 } | 244 FreeBuffer(buffer); |
325 | 245 return; |
326 void PepperView::OnPaintDone(base::Time paint_start) { | 246 } |
327 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); | 247 |
248 // Reuse the buffer if it is large enough, otherwise drop it on the floor | |
249 // and allocate a new one. | |
250 if (buffer->size().width() >= clip_area_.width() && | |
251 buffer->size().height() >= clip_area_.height()) { | |
252 producer_->AddBuffer(buffer); | |
253 } else { | |
254 FreeBuffer(buffer); | |
255 InitiateDrawing(); | |
256 } | |
257 } | |
258 | |
259 void PepperView::SetSourceSize(const SkISize& source_size) { | |
260 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); | |
261 | |
262 if (source_size_ == source_size) | |
263 return; | |
264 | |
265 source_size_ = source_size; | |
266 | |
267 // Notify JavaScript of the change in source size. | |
268 instance_->GetScriptableObject()->SetDesktopSize( | |
269 source_size.width(), source_size.height()); | |
270 } | |
271 | |
272 pp::ImageData* PepperView::AllocateBuffer() { | |
273 pp::ImageData* buffer = NULL; | |
274 if (buffers_.size() < kMaxPendingBuffersCount) { | |
275 pp::Size pp_size = pp::Size(clip_area_.width(), clip_area_.height()); | |
276 buffer = new pp::ImageData(instance_, | |
277 PP_IMAGEDATAFORMAT_BGRA_PREMUL, | |
278 pp_size, | |
279 false); | |
280 if (buffer->is_null()) { | |
281 LOG(WARNING) << "Not enough memory for frame buffers."; | |
282 delete buffer; | |
283 buffer = NULL; | |
284 } else { | |
285 buffers_.push_back(buffer); | |
286 } | |
287 } | |
288 | |
289 return buffer; | |
290 } | |
291 | |
292 void PepperView::FreeBuffer(pp::ImageData* buffer) { | |
293 DCHECK(std::find(buffers_.begin(), buffers_.end(), buffer) != buffers_.end()); | |
294 | |
295 buffers_.remove(buffer); | |
296 delete buffer; | |
297 | |
298 // Now that we can allocate new buffers retry painting of the static | |
299 // background. | |
300 if (solid_fill_requested_) | |
301 Paint(); | |
302 } | |
303 | |
304 void PepperView::InitiateDrawing() { | |
305 // Do not schedule drawing if there is nothing to paint. | |
306 if (producer_disabled_) | |
307 return; | |
308 | |
309 pp::ImageData* buffer = AllocateBuffer(); | |
310 while (buffer) { | |
311 producer_->AddBuffer(buffer); | |
312 buffer = AllocateBuffer(); | |
313 } | |
314 } | |
315 | |
316 void PepperView::FlushBuffer(const SkIRect& clip_area, | |
317 pp::ImageData* buffer, | |
318 const SkRegion& region) { | |
319 | |
320 // Defer drawing if the flush is already in progress. | |
321 if (flush_pending_) { | |
322 // |merge_buffer_| is guaranteed to be free here because we allocate only | |
323 // two buffers simultaneously. If more buffers are allowed this code should | |
324 // apply all pending changes to the screen. | |
325 DCHECK(merge_buffer_ == NULL); | |
326 | |
327 merge_clip_area_ = clip_area; | |
328 merge_buffer_ = buffer; | |
329 merge_region_ = region; | |
330 return; | |
331 } | |
332 | |
333 // Notify Pepper API about the updated areas and flush pixels to the screen. | |
334 base::Time start_time = base::Time::Now(); | |
335 | |
336 for (SkRegion::Iterator i(region); !i.done(); i.next()) { | |
337 SkIRect rect = i.rect(); | |
338 | |
339 // N.B. |clip_area_| may be different from |clip_area| meaning that | |
340 // the clipping area has changed since the time the image was drawn. | |
341 // The parts of the image that fit the new clipping area still need | |
342 // to be painted. | |
343 if (!rect.intersect(clip_area_)) | |
344 continue; | |
345 | |
346 // Specify the rectangle coordinates relative to the clipping area. | |
347 rect.offset(-clip_area.left(), -clip_area.top()); | |
348 | |
349 // Pepper Graphics 2D has a strange and badly documented API that the | |
350 // point here is the offset from the source rect. Why? | |
351 graphics2d_.PaintImageData( | |
352 *buffer, | |
353 pp::Point(clip_area.left(), clip_area.top()), | |
354 pp::Rect(rect.left(), rect.top(), rect.width(), rect.height())); | |
355 } | |
356 | |
357 // Notify the producer that some parts of the region weren't painted because | |
358 // the clipping area has changed already. | |
359 if (clip_area != clip_area_) { | |
360 SkRegion not_painted = region; | |
361 not_painted.op(clip_area_, SkRegion::kDifference_Op); | |
362 if (!not_painted.isEmpty()) { | |
363 producer_->InvalidateRegion(not_painted); | |
364 } | |
365 } | |
366 | |
367 // Flush the updated areas to the screen. | |
368 scoped_ptr<base::Closure> task( | |
369 new base::Closure( | |
370 base::Bind(&PepperView::OnFlushDone, weak_factory_.GetWeakPtr(), | |
371 start_time, buffer))); | |
372 | |
373 // Flag needs to be set here in order to get a proper error code for Flush(). | |
374 // Otherwise Flush() will always return PP_OK_COMPLETIONPENDING and the error | |
375 // would be hidden. | |
376 // | |
377 // Note that we can also handle this by providing an actual callback which | |
378 // takes the result code. Right now everything goes to the task that doesn't | |
379 // result value. | |
380 pp::CompletionCallback pp_callback(&CompletionCallbackClosureAdapter, | |
381 task.get(), | |
382 PP_COMPLETIONCALLBACK_FLAG_OPTIONAL); | |
383 int error = graphics2d_.Flush(pp_callback); | |
384 | |
385 // If Flush() returns asynchronously then release the task. | |
386 flush_pending_ = (error == PP_OK_COMPLETIONPENDING); | |
387 if (flush_pending_) { | |
388 ignore_result(task.release()); | |
389 } else { | |
390 instance_->GetStats()->video_paint_ms()->Record( | |
391 (base::Time::Now() - start_time).InMilliseconds()); | |
392 | |
393 ReturnBuffer(buffer); | |
394 | |
395 // Resume painting for the buffer that was previoulsy postponed because of | |
396 // pending flush. | |
397 if (merge_buffer_ != NULL) { | |
398 buffer = merge_buffer_; | |
399 merge_buffer_ = NULL; | |
400 FlushBuffer(merge_clip_area_, buffer, merge_region_); | |
401 } | |
402 } | |
403 } | |
404 | |
405 void PepperView::OnFlushDone(base::Time paint_start, | |
406 pp::ImageData* buffer) { | |
407 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); | |
408 DCHECK(flush_pending_); | |
409 | |
328 instance_->GetStats()->video_paint_ms()->Record( | 410 instance_->GetStats()->video_paint_ms()->Record( |
329 (base::Time::Now() - paint_start).InMilliseconds()); | 411 (base::Time::Now() - paint_start).InMilliseconds()); |
330 | 412 |
331 // If the last flush failed because there was already another one in progress | 413 flush_pending_ = false; |
332 // then we perform the flush now. | 414 ReturnBuffer(buffer); |
333 if (flush_blocked_) | 415 |
334 FlushGraphics(base::Time::Now()); | 416 // Resume painting for the buffer that was previoulsy postponed because of |
335 return; | 417 // pending flush. |
418 if (merge_buffer_ != NULL) { | |
419 buffer = merge_buffer_; | |
420 merge_buffer_ = NULL; | |
421 FlushBuffer(merge_clip_area_, buffer, merge_region_); | |
422 } | |
336 } | 423 } |
337 | 424 |
338 } // namespace remoting | 425 } // namespace remoting |
OLD | NEW |