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 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 Loading... | |
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 |
OLD | NEW |