| OLD | NEW |
| 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 "chrome/browser/android/vr_shell/vr_shell_gl.h" | 5 #include "chrome/browser/android/vr_shell/vr_shell_gl.h" |
| 6 | 6 |
| 7 #include <chrono> | 7 #include <chrono> |
| 8 #include <limits> | 8 #include <limits> |
| 9 #include <utility> | 9 #include <utility> |
| 10 | 10 |
| (...skipping 338 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 349 const gpu::MailboxHolder& mailbox) { | 349 const gpu::MailboxHolder& mailbox) { |
| 350 TRACE_EVENT0("gpu", "VrShellGl::SubmitWebVRFrame"); | 350 TRACE_EVENT0("gpu", "VrShellGl::SubmitWebVRFrame"); |
| 351 | 351 |
| 352 // submit_client_ could be null when we exit presentation, if there were | 352 // submit_client_ could be null when we exit presentation, if there were |
| 353 // pending SubmitFrame messages queued. VRDisplayClient::OnExitPresent | 353 // pending SubmitFrame messages queued. VRDisplayClient::OnExitPresent |
| 354 // will clean up state in blink, so it doesn't wait for | 354 // will clean up state in blink, so it doesn't wait for |
| 355 // OnSubmitFrameTransferred or OnSubmitFrameRendered. | 355 // OnSubmitFrameTransferred or OnSubmitFrameRendered. |
| 356 if (!submit_client_.get()) | 356 if (!submit_client_.get()) |
| 357 return; | 357 return; |
| 358 | 358 |
| 359 if (frame_index < 0 || |
| 360 !webvr_frame_oustanding_[frame_index % kPoseRingBufferSize]) { |
| 361 mojo::ReportBadMessage("SubmitFrame called with an invalid frame_index"); |
| 362 binding_.Close(); |
| 363 return; |
| 364 } |
| 365 |
| 359 webvr_time_js_submit_[frame_index % kPoseRingBufferSize] = | 366 webvr_time_js_submit_[frame_index % kPoseRingBufferSize] = |
| 360 base::TimeTicks::Now(); | 367 base::TimeTicks::Now(); |
| 361 | 368 |
| 362 // Swapping twice on a Surface without calling updateTexImage in | 369 // Swapping twice on a Surface without calling updateTexImage in |
| 363 // between can lose frames, so don't draw+swap if we already have | 370 // between can lose frames, so don't draw+swap if we already have |
| 364 // a pending frame we haven't consumed yet. | 371 // a pending frame we haven't consumed yet. |
| 365 bool swapped = false; | 372 bool swapped = false; |
| 366 if (pending_frames_.empty()) { | 373 if (pending_frames_.empty()) { |
| 367 swapped = mailbox_bridge_->CopyMailboxToSurfaceAndSwap(mailbox); | 374 swapped = mailbox_bridge_->CopyMailboxToSurfaceAndSwap(mailbox); |
| 368 if (swapped) { | 375 if (swapped) { |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 425 browser_->ToggleCardboardGamepad(true); | 432 browser_->ToggleCardboardGamepad(true); |
| 426 } | 433 } |
| 427 } | 434 } |
| 428 | 435 |
| 429 void VrShellGl::InitializeRenderer() { | 436 void VrShellGl::InitializeRenderer() { |
| 430 gvr_api_->InitializeGl(); | 437 gvr_api_->InitializeGl(); |
| 431 gfx::Transform head_pose; | 438 gfx::Transform head_pose; |
| 432 device::GvrDelegate::GetGvrPoseWithNeckModel(gvr_api_.get(), &head_pose); | 439 device::GvrDelegate::GetGvrPoseWithNeckModel(gvr_api_.get(), &head_pose); |
| 433 webvr_head_pose_.assign(kPoseRingBufferSize, head_pose); | 440 webvr_head_pose_.assign(kPoseRingBufferSize, head_pose); |
| 434 webvr_time_pose_.assign(kPoseRingBufferSize, base::TimeTicks()); | 441 webvr_time_pose_.assign(kPoseRingBufferSize, base::TimeTicks()); |
| 442 webvr_frame_oustanding_.assign(kPoseRingBufferSize, false); |
| 435 webvr_time_js_submit_.assign(kPoseRingBufferSize, base::TimeTicks()); | 443 webvr_time_js_submit_.assign(kPoseRingBufferSize, base::TimeTicks()); |
| 436 | 444 |
| 437 std::vector<gvr::BufferSpec> specs; | 445 std::vector<gvr::BufferSpec> specs; |
| 438 // For kFramePrimaryBuffer (primary VrShell and WebVR content) | 446 // For kFramePrimaryBuffer (primary VrShell and WebVR content) |
| 439 specs.push_back(gvr_api_->CreateBufferSpec()); | 447 specs.push_back(gvr_api_->CreateBufferSpec()); |
| 440 gvr::Sizei render_size_primary = specs[kFramePrimaryBuffer].GetSize(); | 448 gvr::Sizei render_size_primary = specs[kFramePrimaryBuffer].GetSize(); |
| 441 render_size_primary_ = {render_size_primary.width, | 449 render_size_primary_ = {render_size_primary.width, |
| 442 render_size_primary.height}; | 450 render_size_primary.height}; |
| 443 render_size_vrshell_ = render_size_primary_; | 451 render_size_vrshell_ = render_size_primary_; |
| 444 | 452 |
| (...skipping 385 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 830 | 838 |
| 831 // When using async reprojection, we need to know which pose was | 839 // When using async reprojection, we need to know which pose was |
| 832 // used in the WebVR app for drawing this frame and supply it when | 840 // used in the WebVR app for drawing this frame and supply it when |
| 833 // submitting. Technically we don't need a pose if not reprojecting, | 841 // submitting. Technically we don't need a pose if not reprojecting, |
| 834 // but keeping it uninitialized seems likely to cause problems down | 842 // but keeping it uninitialized seems likely to cause problems down |
| 835 // the road. Copying it is cheaper than fetching a new one. | 843 // the road. Copying it is cheaper than fetching a new one. |
| 836 if (ShouldDrawWebVr()) { | 844 if (ShouldDrawWebVr()) { |
| 837 static_assert(!((kPoseRingBufferSize - 1) & kPoseRingBufferSize), | 845 static_assert(!((kPoseRingBufferSize - 1) & kPoseRingBufferSize), |
| 838 "kPoseRingBufferSize must be a power of 2"); | 846 "kPoseRingBufferSize must be a power of 2"); |
| 839 head_pose = webvr_head_pose_[frame_index % kPoseRingBufferSize]; | 847 head_pose = webvr_head_pose_[frame_index % kPoseRingBufferSize]; |
| 848 webvr_frame_oustanding_[frame_index % kPoseRingBufferSize] = false; |
| 840 } else { | 849 } else { |
| 841 device::GvrDelegate::GetGvrPoseWithNeckModel(gvr_api_.get(), &head_pose); | 850 device::GvrDelegate::GetGvrPoseWithNeckModel(gvr_api_.get(), &head_pose); |
| 842 } | 851 } |
| 843 | 852 |
| 844 // Update the render position of all UI elements (including desktop). | 853 // Update the render position of all UI elements (including desktop). |
| 845 scene_->OnBeginFrame(current_time); | 854 scene_->OnBeginFrame(current_time); |
| 846 | 855 |
| 847 { | 856 { |
| 848 // TODO(crbug.com/704690): Acquire controller state in a way that's timely | 857 // TODO(crbug.com/704690): Acquire controller state in a way that's timely |
| 849 // for both the gamepad API and UI input handling. | 858 // for both the gamepad API and UI input handling. |
| (...skipping 496 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1346 if (needs_init) { | 1355 if (needs_init) { |
| 1347 vsync_task_.Reset(base::Bind(&VrShellGl::OnVSync, base::Unretained(this))); | 1356 vsync_task_.Reset(base::Bind(&VrShellGl::OnVSync, base::Unretained(this))); |
| 1348 OnVSync(); | 1357 OnVSync(); |
| 1349 } | 1358 } |
| 1350 } | 1359 } |
| 1351 | 1360 |
| 1352 void VrShellGl::ForceExitVr() { | 1361 void VrShellGl::ForceExitVr() { |
| 1353 browser_->ForceExitVr(); | 1362 browser_->ForceExitVr(); |
| 1354 } | 1363 } |
| 1355 | 1364 |
| 1365 namespace { |
| 1366 bool ValidateRect(const gfx::RectF& bounds) { |
| 1367 // Bounds should be between 0 and 1, with positive width/height. |
| 1368 // We simply clamp to [0,1], but still validate that the bounds are not NAN. |
| 1369 return !std::isnan(bounds.width()) && !std::isnan(bounds.height()) && |
| 1370 !std::isnan(bounds.x()) && !std::isnan(bounds.y()); |
| 1371 } |
| 1372 |
| 1373 gfx::RectF ClampRect(gfx::RectF bounds) { |
| 1374 bounds.AdjustToFit(gfx::RectF(0, 0, 1, 1)); |
| 1375 return bounds; |
| 1376 } |
| 1377 |
| 1378 } // namespace |
| 1379 |
| 1356 void VrShellGl::UpdateLayerBounds(int16_t frame_index, | 1380 void VrShellGl::UpdateLayerBounds(int16_t frame_index, |
| 1357 const gfx::RectF& left_bounds, | 1381 const gfx::RectF& left_bounds, |
| 1358 const gfx::RectF& right_bounds, | 1382 const gfx::RectF& right_bounds, |
| 1359 const gfx::Size& source_size) { | 1383 const gfx::Size& source_size) { |
| 1384 if (!ValidateRect(left_bounds) || !ValidateRect(right_bounds)) { |
| 1385 mojo::ReportBadMessage("UpdateLayerBounds called with invalid bounds"); |
| 1386 binding_.Close(); |
| 1387 return; |
| 1388 } |
| 1389 |
| 1390 if (frame_index >= 0 && |
| 1391 !webvr_frame_oustanding_[frame_index % kPoseRingBufferSize]) { |
| 1392 mojo::ReportBadMessage("UpdateLayerBounds called with invalid frame_index"); |
| 1393 binding_.Close(); |
| 1394 return; |
| 1395 } |
| 1396 |
| 1360 if (frame_index < 0) { | 1397 if (frame_index < 0) { |
| 1361 webvr_left_viewport_->SetSourceUv(UVFromGfxRect(left_bounds)); | 1398 webvr_left_viewport_->SetSourceUv(UVFromGfxRect(ClampRect(left_bounds))); |
| 1362 webvr_right_viewport_->SetSourceUv(UVFromGfxRect(right_bounds)); | 1399 webvr_right_viewport_->SetSourceUv(UVFromGfxRect(ClampRect(right_bounds))); |
| 1363 CreateOrResizeWebVRSurface(source_size); | 1400 CreateOrResizeWebVRSurface(source_size); |
| 1401 |
| 1402 // clear all pending bounds |
| 1403 pending_bounds_ = std::queue<std::pair<uint8_t, WebVrBounds>>(); |
| 1364 } else { | 1404 } else { |
| 1365 pending_bounds_.emplace( | 1405 pending_bounds_.emplace( |
| 1366 frame_index, WebVrBounds(left_bounds, right_bounds, source_size)); | 1406 frame_index, WebVrBounds(left_bounds, right_bounds, source_size)); |
| 1367 } | 1407 } |
| 1368 } | 1408 } |
| 1369 | 1409 |
| 1370 int64_t VrShellGl::GetPredictedFrameTimeNanos() { | 1410 int64_t VrShellGl::GetPredictedFrameTimeNanos() { |
| 1371 int64_t frame_time_micros = vsync_interval_.InMicroseconds(); | 1411 int64_t frame_time_micros = vsync_interval_.InMicroseconds(); |
| 1372 // If we aim to submit at vsync, that frame will start scanning out | 1412 // If we aim to submit at vsync, that frame will start scanning out |
| 1373 // one vsync later. Add a half frame to split the difference between | 1413 // one vsync later. Add a half frame to split the difference between |
| (...skipping 16 matching lines...) Expand all Loading... |
| 1390 TRACE_EVENT1("input", "VrShellGl::SendVSync", "frame", frame_index); | 1430 TRACE_EVENT1("input", "VrShellGl::SendVSync", "frame", frame_index); |
| 1391 | 1431 |
| 1392 int64_t prediction_nanos = GetPredictedFrameTimeNanos(); | 1432 int64_t prediction_nanos = GetPredictedFrameTimeNanos(); |
| 1393 | 1433 |
| 1394 gfx::Transform head_mat; | 1434 gfx::Transform head_mat; |
| 1395 device::mojom::VRPosePtr pose = | 1435 device::mojom::VRPosePtr pose = |
| 1396 device::GvrDelegate::GetVRPosePtrWithNeckModel(gvr_api_.get(), &head_mat, | 1436 device::GvrDelegate::GetVRPosePtrWithNeckModel(gvr_api_.get(), &head_mat, |
| 1397 prediction_nanos); | 1437 prediction_nanos); |
| 1398 | 1438 |
| 1399 webvr_head_pose_[frame_index % kPoseRingBufferSize] = head_mat; | 1439 webvr_head_pose_[frame_index % kPoseRingBufferSize] = head_mat; |
| 1440 webvr_frame_oustanding_[frame_index % kPoseRingBufferSize] = true; |
| 1400 webvr_time_pose_[frame_index % kPoseRingBufferSize] = base::TimeTicks::Now(); | 1441 webvr_time_pose_[frame_index % kPoseRingBufferSize] = base::TimeTicks::Now(); |
| 1401 | 1442 |
| 1402 std::move(callback).Run( | 1443 std::move(callback).Run( |
| 1403 std::move(pose), time, frame_index, | 1444 std::move(pose), time, frame_index, |
| 1404 device::mojom::VRPresentationProvider::VSyncStatus::SUCCESS); | 1445 device::mojom::VRPresentationProvider::VSyncStatus::SUCCESS); |
| 1405 } | 1446 } |
| 1406 | 1447 |
| 1407 void VrShellGl::CreateVRDisplayInfo( | 1448 void VrShellGl::CreateVRDisplayInfo( |
| 1408 const base::Callback<void(device::mojom::VRDisplayInfoPtr)>& callback, | 1449 const base::Callback<void(device::mojom::VRDisplayInfoPtr)>& callback, |
| 1409 uint32_t device_id) { | 1450 uint32_t device_id) { |
| (...skipping 13 matching lines...) Expand all Loading... |
| 1423 // pending callbacks, so instead of providing a VSync, tell the requester | 1464 // pending callbacks, so instead of providing a VSync, tell the requester |
| 1424 // the connection is closing. | 1465 // the connection is closing. |
| 1425 base::ResetAndReturn(&callback_) | 1466 base::ResetAndReturn(&callback_) |
| 1426 .Run(nullptr, base::TimeDelta(), -1, | 1467 .Run(nullptr, base::TimeDelta(), -1, |
| 1427 device::mojom::VRPresentationProvider::VSyncStatus::CLOSING); | 1468 device::mojom::VRPresentationProvider::VSyncStatus::CLOSING); |
| 1428 } | 1469 } |
| 1429 binding_.Close(); | 1470 binding_.Close(); |
| 1430 } | 1471 } |
| 1431 | 1472 |
| 1432 } // namespace vr_shell | 1473 } // namespace vr_shell |
| OLD | NEW |