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

Side by Side Diff: ash/laser/laser_pointer_view.cc

Issue 2745953002: ash: Add basic prediction code to the laser pointer. (Closed)
Patch Set: fix typo Created 3 years, 9 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
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 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 "ash/laser/laser_pointer_view.h" 5 #include "ash/laser/laser_pointer_view.h"
6 6
7 #include <GLES2/gl2.h> 7 #include <GLES2/gl2.h>
8 #include <GLES2/gl2ext.h> 8 #include <GLES2/gl2ext.h>
9 #include <GLES2/gl2extchromium.h> 9 #include <GLES2/gl2extchromium.h>
10 10
11 #include <algorithm>
12 #include <array>
13 #include <cmath>
11 #include <memory> 14 #include <memory>
12 15
13 #include "ash/laser/laser_pointer_points.h" 16 #include "ash/laser/laser_pointer_points.h"
14 #include "ash/laser/laser_segment_utils.h" 17 #include "ash/laser/laser_segment_utils.h"
15 #include "ash/public/cpp/shell_window_ids.h" 18 #include "ash/public/cpp/shell_window_ids.h"
16 #include "ash/shell.h" 19 #include "ash/shell.h"
20 #include "base/containers/adapters.h"
17 #include "base/threading/thread_task_runner_handle.h" 21 #include "base/threading/thread_task_runner_handle.h"
18 #include "base/timer/timer.h" 22 #include "base/timer/timer.h"
19 #include "base/trace_event/trace_event.h" 23 #include "base/trace_event/trace_event.h"
20 #include "cc/output/context_provider.h" 24 #include "cc/output/context_provider.h"
21 #include "cc/quads/texture_draw_quad.h" 25 #include "cc/quads/texture_draw_quad.h"
22 #include "cc/resources/transferable_resource.h" 26 #include "cc/resources/transferable_resource.h"
23 #include "cc/surfaces/surface.h" 27 #include "cc/surfaces/surface.h"
24 #include "cc/surfaces/surface_manager.h" 28 #include "cc/surfaces/surface_manager.h"
25 #include "gpu/command_buffer/client/context_support.h" 29 #include "gpu/command_buffer/client/context_support.h"
26 #include "gpu/command_buffer/client/gles2_interface.h" 30 #include "gpu/command_buffer/client/gles2_interface.h"
(...skipping 11 matching lines...) Expand all
38 42
39 namespace ash { 43 namespace ash {
40 namespace { 44 namespace {
41 45
42 // Variables for rendering the laser. Radius in DIP. 46 // Variables for rendering the laser. Radius in DIP.
43 const float kPointInitialRadius = 5.0f; 47 const float kPointInitialRadius = 5.0f;
44 const float kPointFinalRadius = 0.25f; 48 const float kPointFinalRadius = 0.25f;
45 const int kPointInitialOpacity = 200; 49 const int kPointInitialOpacity = 200;
46 const int kPointFinalOpacity = 10; 50 const int kPointFinalOpacity = 10;
47 const SkColor kPointColor = SkColorSetRGB(255, 0, 0); 51 const SkColor kPointColor = SkColorSetRGB(255, 0, 0);
52 // Change this to a debug prediction code.
53 const SkColor kPredictionPointColor = SkColorSetRGB(255, 0, 0);
Daniele Castagna 2017/03/15 19:07:28 nit: you could initialize this to kPointColor to m
reveman 2017/03/16 12:57:28 Done.
48 54
49 float DistanceBetweenPoints(const gfx::PointF& point1, 55 float DistanceBetweenPoints(const gfx::PointF& point1,
50 const gfx::PointF& point2) { 56 const gfx::PointF& point2) {
51 return (point1 - point2).Length(); 57 return (point1 - point2).Length();
52 } 58 }
53 59
54 float LinearInterpolate(float initial_value, 60 float LinearInterpolate(float initial_value,
55 float final_value, 61 float final_value,
56 float progress) { 62 float progress) {
57 return initial_value + (final_value - initial_value) * progress; 63 return initial_value + (final_value - initial_value) * progress;
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after
189 } 195 }
190 } 196 }
191 scoped_refptr<cc::ContextProvider> context_provider; 197 scoped_refptr<cc::ContextProvider> context_provider;
192 uint32_t texture = 0; 198 uint32_t texture = 0;
193 uint32_t image = 0; 199 uint32_t image = 0;
194 gpu::Mailbox mailbox; 200 gpu::Mailbox mailbox;
195 }; 201 };
196 202
197 // LaserPointerView 203 // LaserPointerView
198 LaserPointerView::LaserPointerView(base::TimeDelta life_duration, 204 LaserPointerView::LaserPointerView(base::TimeDelta life_duration,
205 base::TimeDelta presentation_delay,
199 aura::Window* root_window) 206 aura::Window* root_window)
200 : laser_points_(life_duration), 207 : laser_points_(life_duration),
208 predicted_laser_points_(life_duration),
209 presentation_delay_(presentation_delay),
201 frame_sink_id_(aura::Env::GetInstance() 210 frame_sink_id_(aura::Env::GetInstance()
202 ->context_factory_private() 211 ->context_factory_private()
203 ->AllocateFrameSinkId()), 212 ->AllocateFrameSinkId()),
204 frame_sink_support_(this, 213 frame_sink_support_(this,
205 aura::Env::GetInstance() 214 aura::Env::GetInstance()
206 ->context_factory_private() 215 ->context_factory_private()
207 ->GetSurfaceManager(), 216 ->GetSurfaceManager(),
208 frame_sink_id_, 217 frame_sink_id_,
209 false /* is_root */, 218 false /* is_root */,
210 true /* handles_frame_sink_id_invalidation */, 219 true /* handles_frame_sink_id_invalidation */,
(...skipping 24 matching lines...) Expand all
235 244
236 LaserPointerView::~LaserPointerView() { 245 LaserPointerView::~LaserPointerView() {
237 // Make sure GPU memory buffer is unmapped before being destroyed. 246 // Make sure GPU memory buffer is unmapped before being destroyed.
238 if (gpu_memory_buffer_) 247 if (gpu_memory_buffer_)
239 gpu_memory_buffer_->Unmap(); 248 gpu_memory_buffer_->Unmap();
240 } 249 }
241 250
242 void LaserPointerView::Stop() { 251 void LaserPointerView::Stop() {
243 buffer_damage_rect_.Union(GetBoundingBox()); 252 buffer_damage_rect_.Union(GetBoundingBox());
244 laser_points_.Clear(); 253 laser_points_.Clear();
254 predicted_laser_points_.Clear();
245 OnPointsUpdated(); 255 OnPointsUpdated();
246 } 256 }
247 257
248 void LaserPointerView::AddNewPoint(const gfx::PointF& new_point) { 258 void LaserPointerView::AddNewPoint(const gfx::PointF& new_point,
259 const base::TimeTicks& new_time) {
260 TRACE_EVENT1("ui", "LaserPointerView::AddNewPoint", "new_point",
261 new_point.ToString());
262 TRACE_COUNTER1(
263 "ui", "LaserPointerPredictionError",
264 predicted_laser_points_.GetNumberOfPoints()
265 ? std::round((new_point -
266 predicted_laser_points_.laser_points().front().location)
267 .Length())
268 : 0);
269
249 buffer_damage_rect_.Union(GetBoundingBox()); 270 buffer_damage_rect_.Union(GetBoundingBox());
250 laser_points_.AddPoint(new_point); 271 laser_points_.AddPoint(new_point, new_time);
272
273 // Current time is needed to determine presentation time and the number of
274 // predicted points to add.
275 base::TimeTicks current_time = base::TimeTicks::Now();
276
277 // Create a new set of predicted points based on the last four points added.
278 // We add enough predicted points to fill the time between the new point and
279 // the expected presentation time. Note that estimated presentation time is
280 // based on current time and inefficient rendering of points can result in an
281 // actual presentation time that is later.
282 predicted_laser_points_.Clear();
Daniele Castagna 2017/03/15 19:07:28 This probably creates a lot of discontinuity, sinc
reveman 2017/03/16 12:57:29 Yes, there's definitely room for improvement here.
283
284 // Normalize all coordinates to screen size.
285 gfx::Size screen_size = widget_->GetNativeView()->GetBoundsInScreen().size();
286 gfx::Vector2dF scale(1.0f / screen_size.width(), 1.0f / screen_size.height());
287
288 // TODO(reveman): Determine interval based on history when event time stamps
289 // are accurate. b/36137953
290 const float kPredictionIntervalMs = 5.0f;
291 const float kMaxPointIntervalMs = 10.0f;
292 base::TimeDelta prediction_interval =
293 base::TimeDelta::FromMilliseconds(kPredictionIntervalMs);
294 base::TimeDelta max_point_interval =
295 base::TimeDelta::FromMilliseconds(kMaxPointIntervalMs);
296 base::TimeTicks last_point_time = current_time;
297
298 // Use the last four points for prediction.
299 using PositionArray = std::array<gfx::PointF, 4>;
300 PositionArray position;
Daniele Castagna 2017/03/15 19:07:28 nit: positions?
reveman 2017/03/16 12:57:28 then it should be velocities and accelerations too
301 PositionArray::iterator it = position.begin();
302 for (auto& point : base::Reversed(laser_points_.laser_points())) {
303 // Stop adding positons if interval between points is too large to provide
304 // an accurate history for prediction.
305 if ((last_point_time - point.time) > max_point_interval)
306 break;
307
308 *it++ = gfx::ScalePoint(point.location, scale.x(), scale.y());
309 last_point_time = point.time;
310
311 // Stop when no more positions are needed.
312 if (it == position.end())
313 break;
314 }
315 // Pad with last point if needed.
316 std::fill(it, position.end(), *(it - 1));
317
318 // Calculate velocity.
319 gfx::Vector2dF velocity[3];
320 for (size_t i = 0; i < arraysize(velocity); ++i)
321 velocity[i] = position[i] - position[i + 1];
Daniele Castagna 2017/03/15 19:07:28 It's really weird to see a velocity defined as del
reveman 2017/03/16 12:57:28 I agree. Added a comment and about this for now. W
322
323 // Calculate acceleration.
324 gfx::Vector2dF acceleration[2];
325 for (size_t i = 0; i < arraysize(acceleration); ++i)
326 acceleration[i] = velocity[i] - velocity[i + 1];
327
328 // Calculate jerk.
329 gfx::Vector2dF jerk = acceleration[0] - acceleration[1];
330
331 // Adjust max prediction time based on speed as prediction data is not great
332 // at lower speeds.
333 const float kMaxPredictionScaleSpeed = 1e-5;
334 double speed = velocity[0].LengthSquared();
335 base::TimeTicks max_prediction_time =
336 current_time +
337 std::min(presentation_delay_ * (speed / kMaxPredictionScaleSpeed),
338 presentation_delay_);
339
340 // Add predicted points until we reach the max prediction time.
341 gfx::PointF location = position[0];
342 for (base::TimeTicks time = new_time + prediction_interval;
343 time < max_prediction_time; time += prediction_interval) {
344 velocity[0] += acceleration[0];
Daniele Castagna 2017/03/15 19:07:28 Again, kinda weird to see a velocity incremented b
reveman 2017/03/16 12:57:28 Added a comment for now.
345 acceleration[0] += jerk;
Daniele Castagna 2017/03/15 19:07:28 Have you tried dampening jerk and acceleration at
reveman 2017/03/16 12:57:29 I considered that and suspect that it can help mak
346 location += velocity[0];
347
348 predicted_laser_points_.AddPoint(
349 gfx::ScalePoint(location, screen_size.width(), screen_size.height()),
350 time);
351
352 // Always stop at three predicted points as a four point history doesn't
353 // provide accurate prediction of more points.
354 if (predicted_laser_points_.GetNumberOfPoints() == 3)
355 break;
356 }
357
358 // Move forward to next presentation time.
359 base::TimeTicks next_presentation_time = current_time + presentation_delay_;
360 laser_points_.MoveForwardToTime(next_presentation_time);
361 predicted_laser_points_.MoveForwardToTime(next_presentation_time);
362
251 buffer_damage_rect_.Union(GetBoundingBox()); 363 buffer_damage_rect_.Union(GetBoundingBox());
252 OnPointsUpdated(); 364 OnPointsUpdated();
253 } 365 }
254 366
255 void LaserPointerView::UpdateTime() { 367 void LaserPointerView::UpdateTime() {
256 buffer_damage_rect_.Union(GetBoundingBox()); 368 buffer_damage_rect_.Union(GetBoundingBox());
257 // Do not add the point but advance the time if the view is in process of 369 // Do not add the point but advance the time if the view is in process of
258 // fading away. 370 // fading away.
259 laser_points_.MoveForwardToTime(base::Time::Now()); 371 base::TimeTicks next_presentation_time =
372 base::TimeTicks::Now() + presentation_delay_;
373 laser_points_.MoveForwardToTime(next_presentation_time);
374 predicted_laser_points_.MoveForwardToTime(next_presentation_time);
260 buffer_damage_rect_.Union(GetBoundingBox()); 375 buffer_damage_rect_.Union(GetBoundingBox());
261 OnPointsUpdated(); 376 OnPointsUpdated();
262 } 377 }
263 378
264 void LaserPointerView::SetNeedsBeginFrame(bool needs_begin_frame) { 379 void LaserPointerView::SetNeedsBeginFrame(bool needs_begin_frame) {
265 frame_sink_support_.SetNeedsBeginFrame(needs_begin_frame); 380 frame_sink_support_.SetNeedsBeginFrame(needs_begin_frame);
266 } 381 }
267 382
268 void LaserPointerView::SubmitCompositorFrame( 383 void LaserPointerView::SubmitCompositorFrame(
269 const cc::LocalSurfaceId& local_surface_id, 384 const cc::LocalSurfaceId& local_surface_id,
(...skipping 25 matching lines...) Expand all
295 gles2->WaitSyncTokenCHROMIUM(resources.front().sync_token.GetConstData()); 410 gles2->WaitSyncTokenCHROMIUM(resources.front().sync_token.GetConstData());
296 411
297 if (!resources.front().lost) 412 if (!resources.front().lost)
298 returned_resources_.push_back(std::move(resource)); 413 returned_resources_.push_back(std::move(resource));
299 } 414 }
300 415
301 gfx::Rect LaserPointerView::GetBoundingBox() { 416 gfx::Rect LaserPointerView::GetBoundingBox() {
302 // Expand the bounding box so that it includes the radius of the points on the 417 // Expand the bounding box so that it includes the radius of the points on the
303 // edges and antialiasing. 418 // edges and antialiasing.
304 gfx::Rect bounding_box = laser_points_.GetBoundingBox(); 419 gfx::Rect bounding_box = laser_points_.GetBoundingBox();
420 bounding_box.Union(predicted_laser_points_.GetBoundingBox());
305 const int kOutsetForAntialiasing = 1; 421 const int kOutsetForAntialiasing = 1;
306 int outset = kPointInitialRadius + kOutsetForAntialiasing; 422 int outset = kPointInitialRadius + kOutsetForAntialiasing;
307 bounding_box.Inset(-outset, -outset); 423 bounding_box.Inset(-outset, -outset);
308 return bounding_box; 424 return bounding_box;
309 } 425 }
310 426
311 void LaserPointerView::OnPointsUpdated() { 427 void LaserPointerView::OnPointsUpdated() {
312 if (pending_update_buffer_) 428 if (pending_update_buffer_)
313 return; 429 return;
314 430
315 pending_update_buffer_ = true; 431 pending_update_buffer_ = true;
316 base::ThreadTaskRunnerHandle::Get()->PostTask( 432 base::ThreadTaskRunnerHandle::Get()->PostTask(
317 FROM_HERE, base::Bind(&LaserPointerView::UpdateBuffer, 433 FROM_HERE, base::Bind(&LaserPointerView::UpdateBuffer,
318 weak_ptr_factory_.GetWeakPtr())); 434 weak_ptr_factory_.GetWeakPtr()));
319 } 435 }
320 436
321 void LaserPointerView::UpdateBuffer() { 437 void LaserPointerView::UpdateBuffer() {
322 TRACE_EVENT2("ui", "LaserPointerView::UpdatedBuffer", "damage", 438 TRACE_EVENT1("ui", "LaserPointerView::UpdatedBuffer", "damage",
323 buffer_damage_rect_.ToString(), "points", 439 buffer_damage_rect_.ToString());
324 laser_points_.GetNumberOfPoints());
325 440
326 DCHECK(pending_update_buffer_); 441 DCHECK(pending_update_buffer_);
327 pending_update_buffer_ = false; 442 pending_update_buffer_ = false;
328 443
329 gfx::Rect screen_bounds = widget_->GetNativeView()->GetBoundsInScreen(); 444 gfx::Rect screen_bounds = widget_->GetNativeView()->GetBoundsInScreen();
330 gfx::Rect update_rect = buffer_damage_rect_; 445 gfx::Rect update_rect = buffer_damage_rect_;
331 buffer_damage_rect_ = gfx::Rect(); 446 buffer_damage_rect_ = gfx::Rect();
332 447
333 // Create and map a single GPU memory buffer. The laser pointer will be 448 // Create and map a single GPU memory buffer. The laser pointer will be
334 // written into this buffer without any buffering. The result is that we 449 // written into this buffer without any buffering. The result is that we
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
372 487
373 cc::PaintFlags flags; 488 cc::PaintFlags flags;
374 flags.setStyle(cc::PaintFlags::kFill_Style); 489 flags.setStyle(cc::PaintFlags::kFill_Style);
375 flags.setAntiAlias(true); 490 flags.setAntiAlias(true);
376 491
377 // Compute the offset of the current widget. 492 // Compute the offset of the current widget.
378 gfx::Vector2d widget_offset( 493 gfx::Vector2d widget_offset(
379 widget_->GetNativeView()->GetBoundsInRootWindow().origin().x(), 494 widget_->GetNativeView()->GetBoundsInRootWindow().origin().x(),
380 widget_->GetNativeView()->GetBoundsInRootWindow().origin().y()); 495 widget_->GetNativeView()->GetBoundsInRootWindow().origin().y());
381 496
382 int num_points = laser_points_.GetNumberOfPoints(); 497 int num_points = laser_points_.GetNumberOfPoints() +
498 predicted_laser_points_.GetNumberOfPoints();
383 if (num_points) { 499 if (num_points) {
384 LaserPointerPoints::LaserPoint previous_point = laser_points_.GetOldest(); 500 LaserPointerPoints::LaserPoint previous_point = laser_points_.GetOldest();
385 previous_point.location -= widget_offset + update_rect.OffsetFromOrigin(); 501 previous_point.location -= widget_offset + update_rect.OffsetFromOrigin();
386 LaserPointerPoints::LaserPoint current_point; 502 LaserPointerPoints::LaserPoint current_point;
387 std::vector<gfx::PointF> previous_segment_points; 503 std::vector<gfx::PointF> previous_segment_points;
388 float previous_radius; 504 float previous_radius;
389 int current_opacity; 505 int current_opacity;
390 506
391 for (int i = 0; i < num_points; ++i) { 507 for (int i = 0; i < num_points; ++i) {
392 current_point = laser_points_.laser_points()[i]; 508 if (i < laser_points_.GetNumberOfPoints()) {
509 current_point = laser_points_.laser_points()[i];
510 } else {
511 current_point =
512 predicted_laser_points_
513 .laser_points()[i - laser_points_.GetNumberOfPoints()];
514 }
393 current_point.location -= widget_offset + update_rect.OffsetFromOrigin(); 515 current_point.location -= widget_offset + update_rect.OffsetFromOrigin();
394 516
395 // Set the radius and opacity based on the distance. 517 // Set the radius and opacity based on the distance.
396 float current_radius = LinearInterpolate( 518 float current_radius = LinearInterpolate(
397 kPointInitialRadius, kPointFinalRadius, current_point.age); 519 kPointInitialRadius, kPointFinalRadius, current_point.age);
398 current_opacity = int{LinearInterpolate( 520 current_opacity = int{LinearInterpolate(
399 kPointInitialOpacity, kPointFinalOpacity, current_point.age)}; 521 kPointInitialOpacity, kPointFinalOpacity, current_point.age)};
400 522
401 // If we draw laser_points_ that are within a stroke width of each other, 523 // If we draw laser_points_ that are within a stroke width of each other,
402 // the result will be very jagged, unless we are on the last point, then 524 // the result will be very jagged, unless we are on the last point, then
403 // we draw regardless. 525 // we draw regardless.
404 float distance_threshold = current_radius * 2.0f; 526 float distance_threshold = current_radius * 2.0f;
405 if (DistanceBetweenPoints(previous_point.location, 527 if (DistanceBetweenPoints(previous_point.location,
406 current_point.location) <= distance_threshold && 528 current_point.location) <= distance_threshold &&
407 i != num_points - 1) { 529 i != num_points - 1) {
408 continue; 530 continue;
409 } 531 }
410 532
411 LaserSegment current_segment( 533 LaserSegment current_segment(
412 previous_segment_points, gfx::PointF(previous_point.location), 534 previous_segment_points, gfx::PointF(previous_point.location),
413 gfx::PointF(current_point.location), previous_radius, current_radius, 535 gfx::PointF(current_point.location), previous_radius, current_radius,
414 i == num_points - 1); 536 i == num_points - 1);
415 537
416 SkPath path = current_segment.path(); 538 SkPath path = current_segment.path();
417 flags.setColor(SkColorSetA(kPointColor, current_opacity)); 539 if (i < laser_points_.GetNumberOfPoints())
540 flags.setColor(SkColorSetA(kPointColor, current_opacity));
541 else
542 flags.setColor(SkColorSetA(kPredictionPointColor, current_opacity));
418 canvas.DrawPath(path, flags); 543 canvas.DrawPath(path, flags);
419 544
420 previous_segment_points = current_segment.path_points(); 545 previous_segment_points = current_segment.path_points();
421 previous_radius = current_radius; 546 previous_radius = current_radius;
422 previous_point = current_point; 547 previous_point = current_point;
423 } 548 }
424 549
425 // Draw the last point as a circle. 550 // Draw the last point as a circle.
426 flags.setColor(SkColorSetA(kPointColor, current_opacity));
427 flags.setStyle(cc::PaintFlags::kFill_Style); 551 flags.setStyle(cc::PaintFlags::kFill_Style);
428 canvas.DrawCircle(current_point.location, kPointInitialRadius, flags); 552 canvas.DrawCircle(current_point.location, kPointInitialRadius, flags);
429 } 553 }
430 554
431 // Copy result to GPU memory buffer. This is effectiely a memcpy and unlike 555 // Copy result to GPU memory buffer. This is effectiely a memcpy and unlike
432 // drawing to the buffer directly this ensures that the buffer is never in a 556 // drawing to the buffer directly this ensures that the buffer is never in a
433 // state that would result in flicker. 557 // state that would result in flicker.
434 { 558 {
435 TRACE_EVENT0("ui", "LaserPointerView::OnPointsUpdated::Copy"); 559 TRACE_EVENT0("ui", "LaserPointerView::OnPointsUpdated::Copy");
436 560
(...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after
584 pending_draw_surface_ = true; 708 pending_draw_surface_ = true;
585 } 709 }
586 710
587 void LaserPointerView::OnDidDrawSurface() { 711 void LaserPointerView::OnDidDrawSurface() {
588 pending_draw_surface_ = false; 712 pending_draw_surface_ = false;
589 if (needs_update_surface_) 713 if (needs_update_surface_)
590 UpdateSurface(); 714 UpdateSurface();
591 } 715 }
592 716
593 } // namespace ash 717 } // namespace ash
OLDNEW
« ash/laser/laser_pointer_points_unittest.cc ('K') | « ash/laser/laser_pointer_view.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698