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 "modules/vr/VRDisplay.h" | 5 #include "modules/vr/VRDisplay.h" |
6 | 6 |
7 #include "core/css/StylePropertySet.h" | 7 #include "core/css/StylePropertySet.h" |
8 #include "core/dom/DOMException.h" | 8 #include "core/dom/DOMException.h" |
9 #include "core/dom/DocumentUserGestureToken.h" | 9 #include "core/dom/DocumentUserGestureToken.h" |
10 #include "core/dom/FrameRequestCallback.h" | 10 #include "core/dom/FrameRequestCallback.h" |
| 11 #include "core/dom/Fullscreen.h" |
11 #include "core/dom/ScriptedAnimationController.h" | 12 #include "core/dom/ScriptedAnimationController.h" |
12 #include "core/dom/TaskRunnerHelper.h" | 13 #include "core/dom/TaskRunnerHelper.h" |
13 #include "core/frame/FrameView.h" | |
14 #include "core/frame/ImageBitmap.h" | |
15 #include "core/frame/UseCounter.h" | 14 #include "core/frame/UseCounter.h" |
16 #include "core/inspector/ConsoleMessage.h" | 15 #include "core/inspector/ConsoleMessage.h" |
17 #include "core/layout/LayoutView.h" | |
18 #include "core/layout/compositing/PaintLayerCompositor.h" | |
19 #include "core/loader/DocumentLoader.h" | 16 #include "core/loader/DocumentLoader.h" |
20 #include "gpu/command_buffer/client/gles2_interface.h" | 17 #include "gpu/command_buffer/client/gles2_interface.h" |
21 #include "gpu/command_buffer/common/mailbox_holder.h" | |
22 #include "modules/EventTargetModules.h" | 18 #include "modules/EventTargetModules.h" |
23 #include "modules/vr/NavigatorVR.h" | 19 #include "modules/vr/NavigatorVR.h" |
24 #include "modules/vr/VRController.h" | 20 #include "modules/vr/VRController.h" |
25 #include "modules/vr/VRDisplayCapabilities.h" | 21 #include "modules/vr/VRDisplayCapabilities.h" |
26 #include "modules/vr/VREyeParameters.h" | 22 #include "modules/vr/VREyeParameters.h" |
27 #include "modules/vr/VRFrameData.h" | 23 #include "modules/vr/VRFrameData.h" |
28 #include "modules/vr/VRLayer.h" | 24 #include "modules/vr/VRLayer.h" |
29 #include "modules/vr/VRPose.h" | 25 #include "modules/vr/VRPose.h" |
30 #include "modules/vr/VRStageParameters.h" | 26 #include "modules/vr/VRStageParameters.h" |
31 #include "modules/webgl/WebGLRenderingContextBase.h" | 27 #include "modules/webgl/WebGLRenderingContextBase.h" |
32 #include "platform/Histogram.h" | 28 #include "platform/Histogram.h" |
33 #include "platform/UserGestureIndicator.h" | 29 #include "platform/UserGestureIndicator.h" |
34 #include "platform/instrumentation/tracing/TraceEvent.h" | |
35 #include "public/platform/Platform.h" | 30 #include "public/platform/Platform.h" |
36 #include "wtf/AutoReset.h" | 31 #include "wtf/AutoReset.h" |
37 #include "wtf/Time.h" | 32 #include "wtf/Time.h" |
38 | 33 |
39 #include <array> | 34 #include <array> |
40 | 35 |
41 namespace blink { | 36 namespace blink { |
42 | 37 |
43 namespace { | 38 namespace { |
44 | 39 |
| 40 // Magic numbers used to mark valid pose index values encoded in frame |
| 41 // data. Must match the magic numbers used in vr_shell.cc. |
| 42 static constexpr std::array<uint8_t, 2> kWebVrPosePixelMagicNumbers{{42, 142}}; |
| 43 |
45 VREye stringToVREye(const String& whichEye) { | 44 VREye stringToVREye(const String& whichEye) { |
46 if (whichEye == "left") | 45 if (whichEye == "left") |
47 return VREyeLeft; | 46 return VREyeLeft; |
48 if (whichEye == "right") | 47 if (whichEye == "right") |
49 return VREyeRight; | 48 return VREyeRight; |
50 return VREyeNone; | 49 return VREyeNone; |
51 } | 50 } |
52 | 51 |
53 } // namespace | 52 } // namespace |
54 | 53 |
55 VRDisplay::VRDisplay(NavigatorVR* navigatorVR, | 54 VRDisplay::VRDisplay(NavigatorVR* navigatorVR, |
56 device::mojom::blink::VRDisplayPtr display, | 55 device::mojom::blink::VRDisplayPtr display, |
57 device::mojom::blink::VRDisplayClientRequest request) | 56 device::mojom::blink::VRDisplayClientRequest request) |
58 : ContextLifecycleObserver(navigatorVR->document()), | 57 : ContextLifecycleObserver(navigatorVR->document()), |
59 m_navigatorVR(navigatorVR), | 58 m_navigatorVR(navigatorVR), |
60 m_capabilities(new VRDisplayCapabilities()), | 59 m_capabilities(new VRDisplayCapabilities()), |
61 m_eyeParametersLeft(new VREyeParameters()), | 60 m_eyeParametersLeft(new VREyeParameters()), |
62 m_eyeParametersRight(new VREyeParameters()), | 61 m_eyeParametersRight(new VREyeParameters()), |
| 62 m_fullscreenCheckTimer( |
| 63 TaskRunnerHelper::get(TaskType::UnspecedTimer, |
| 64 navigatorVR->document()->frame()), |
| 65 this, |
| 66 &VRDisplay::onFullscreenCheck), |
63 m_display(std::move(display)), | 67 m_display(std::move(display)), |
64 m_submit_frame_client_binding(this), | |
65 m_displayClientBinding(this, std::move(request)) {} | 68 m_displayClientBinding(this, std::move(request)) {} |
66 | 69 |
67 VRDisplay::~VRDisplay() {} | 70 VRDisplay::~VRDisplay() {} |
68 | 71 |
69 VRController* VRDisplay::controller() { | 72 VRController* VRDisplay::controller() { |
70 return m_navigatorVR->controller(); | 73 return m_navigatorVR->controller(); |
71 } | 74 } |
72 | 75 |
73 void VRDisplay::update(const device::mojom::blink::VRDisplayInfoPtr& display) { | 76 void VRDisplay::update(const device::mojom::blink::VRDisplayInfoPtr& display) { |
74 m_displayId = display->index; | 77 m_displayId = display->index; |
(...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
229 // allowed outside a user gesture so that the presented content may be | 232 // allowed outside a user gesture so that the presented content may be |
230 // updated. | 233 // updated. |
231 if (firstPresent && !UserGestureIndicator::utilizeUserGesture()) { | 234 if (firstPresent && !UserGestureIndicator::utilizeUserGesture()) { |
232 DOMException* exception = DOMException::create( | 235 DOMException* exception = DOMException::create( |
233 InvalidStateError, "API can only be initiated by a user gesture."); | 236 InvalidStateError, "API can only be initiated by a user gesture."); |
234 resolver->reject(exception); | 237 resolver->reject(exception); |
235 ReportPresentationResult(PresentationResult::NotInitiatedByUserGesture); | 238 ReportPresentationResult(PresentationResult::NotInitiatedByUserGesture); |
236 return promise; | 239 return promise; |
237 } | 240 } |
238 | 241 |
| 242 // TODO(mthiesse): Remove fullscreen requirement for presentation. See |
| 243 // crbug.com/687369 |
| 244 Document* doc = this->document(); |
| 245 if (!doc || !Fullscreen::fullscreenEnabled(*doc)) { |
| 246 DOMException* exception = |
| 247 DOMException::create(InvalidStateError, "Fullscreen is not enabled."); |
| 248 resolver->reject(exception); |
| 249 ReportPresentationResult(PresentationResult::FullscreenNotEnabled); |
| 250 return promise; |
| 251 } |
| 252 |
239 // A valid number of layers must be provided in order to present. | 253 // A valid number of layers must be provided in order to present. |
240 if (layers.size() == 0 || layers.size() > m_capabilities->maxLayers()) { | 254 if (layers.size() == 0 || layers.size() > m_capabilities->maxLayers()) { |
241 forceExitPresent(); | 255 forceExitPresent(); |
242 DOMException* exception = | 256 DOMException* exception = |
243 DOMException::create(InvalidStateError, "Invalid number of layers."); | 257 DOMException::create(InvalidStateError, "Invalid number of layers."); |
244 resolver->reject(exception); | 258 resolver->reject(exception); |
245 ReportPresentationResult(PresentationResult::InvalidNumberOfLayers); | 259 ReportPresentationResult(PresentationResult::InvalidNumberOfLayers); |
246 return promise; | 260 return promise; |
247 } | 261 } |
248 | 262 |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
303 bool secureContext = scriptState->getExecutionContext()->isSecureContext(); | 317 bool secureContext = scriptState->getExecutionContext()->isSecureContext(); |
304 if (!m_display) { | 318 if (!m_display) { |
305 forceExitPresent(); | 319 forceExitPresent(); |
306 DOMException* exception = DOMException::create( | 320 DOMException* exception = DOMException::create( |
307 InvalidStateError, "The service is no longer active."); | 321 InvalidStateError, "The service is no longer active."); |
308 resolver->reject(exception); | 322 resolver->reject(exception); |
309 return promise; | 323 return promise; |
310 } | 324 } |
311 | 325 |
312 m_pendingPresentResolvers.append(resolver); | 326 m_pendingPresentResolvers.append(resolver); |
313 m_submit_frame_client_binding.Close(); | 327 m_display->RequestPresent(secureContext, convertToBaseCallback(WTF::bind( |
314 m_display->RequestPresent( | 328 &VRDisplay::onPresentComplete, |
315 secureContext, | 329 wrapPersistent(this)))); |
316 m_submit_frame_client_binding.CreateInterfacePtrAndBind(), | |
317 convertToBaseCallback( | |
318 WTF::bind(&VRDisplay::onPresentComplete, wrapPersistent(this)))); | |
319 } else { | 330 } else { |
320 updateLayerBounds(); | 331 updateLayerBounds(); |
321 resolver->resolve(); | 332 resolver->resolve(); |
322 ReportPresentationResult(PresentationResult::SuccessAlreadyPresenting); | 333 ReportPresentationResult(PresentationResult::SuccessAlreadyPresenting); |
323 } | 334 } |
324 | 335 |
325 return promise; | 336 return promise; |
326 } | 337 } |
327 | 338 |
328 void VRDisplay::onPresentComplete(bool success) { | 339 void VRDisplay::onPresentComplete(bool success) { |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
377 "VR Presentation not implemented for this VRDisplay."); | 388 "VR Presentation not implemented for this VRDisplay."); |
378 while (!m_pendingPresentResolvers.isEmpty()) { | 389 while (!m_pendingPresentResolvers.isEmpty()) { |
379 ScriptPromiseResolver* resolver = m_pendingPresentResolvers.takeFirst(); | 390 ScriptPromiseResolver* resolver = m_pendingPresentResolvers.takeFirst(); |
380 resolver->reject(exception); | 391 resolver->reject(exception); |
381 } | 392 } |
382 ReportPresentationResult( | 393 ReportPresentationResult( |
383 PresentationResult::PresentationNotSupportedByDisplay); | 394 PresentationResult::PresentationNotSupportedByDisplay); |
384 return; | 395 return; |
385 } else { | 396 } else { |
386 if (m_layer.source().isHTMLCanvasElement()) { | 397 if (m_layer.source().isHTMLCanvasElement()) { |
387 // TODO(klausw,crbug.com/698923): suppress compositor updates | 398 HTMLCanvasElement* canvas = m_layer.source().getAsHTMLCanvasElement(); |
388 // since they aren't needed, they do a fair amount of extra | 399 // TODO(klausw,crbug.com/655722): Need a proper VR compositor, but |
389 // work. | 400 // for the moment on mobile we'll just make the canvas fullscreen |
| 401 // so that VrShell can pick it up through the standard (high |
| 402 // latency) compositing path. auto canvas = |
| 403 // m_layer.source().getAsHTMLCanvasElement(); |
| 404 auto inlineStyle = canvas->inlineStyle(); |
| 405 if (inlineStyle) { |
| 406 // THREE.js's VREffect sets explicit style.width/height on its rendering |
| 407 // canvas based on the non-fullscreen window dimensions, and it keeps |
| 408 // those unchanged when presenting. Unfortunately it appears that a |
| 409 // fullscreened canvas just gets centered if it has explicitly set a |
| 410 // size smaller than the fullscreen dimensions. Manually set size to |
| 411 // 100% in this case and restore it when exiting fullscreen. This is a |
| 412 // stopgap measure since THREE.js's usage appears legal according to the |
| 413 // WebVR API spec. This will no longer be necessary once we can get rid |
| 414 // of this fullscreen hack. |
| 415 m_fullscreenOrigWidth = inlineStyle->getPropertyValue(CSSPropertyWidth); |
| 416 if (!m_fullscreenOrigWidth.isNull()) { |
| 417 canvas->setInlineStyleProperty(CSSPropertyWidth, "100%"); |
| 418 } |
| 419 m_fullscreenOrigHeight = |
| 420 inlineStyle->getPropertyValue(CSSPropertyHeight); |
| 421 if (!m_fullscreenOrigHeight.isNull()) { |
| 422 canvas->setInlineStyleProperty(CSSPropertyHeight, "100%"); |
| 423 } |
| 424 } else { |
| 425 m_fullscreenOrigWidth = String(); |
| 426 m_fullscreenOrigHeight = String(); |
| 427 } |
| 428 |
| 429 if (doc) { |
| 430 // Since the callback for requestPresent is asynchronous, we've lost our |
| 431 // UserGestureToken, and need to create a new one to enter fullscreen. |
| 432 gestureIndicator = WTF::wrapUnique( |
| 433 new UserGestureIndicator(DocumentUserGestureToken::create( |
| 434 doc, UserGestureToken::Status::PossiblyExistingGesture))); |
| 435 } |
| 436 Fullscreen::requestFullscreen(*canvas); |
| 437 |
| 438 // Check to see if the canvas is still the current fullscreen |
| 439 // element once every 2 seconds. |
| 440 m_fullscreenCheckTimer.startRepeating(2.0, BLINK_FROM_HERE); |
| 441 m_reenteredFullscreen = false; |
390 } else { | 442 } else { |
391 DCHECK(m_layer.source().isOffscreenCanvas()); | 443 DCHECK(m_layer.source().isOffscreenCanvas()); |
392 // TODO(junov, crbug.com/695497): Implement OffscreenCanvas presentation | 444 // TODO(junov, crbug.com/695497): Implement OffscreenCanvas presentation |
393 forceExitPresent(); | 445 forceExitPresent(); |
394 DOMException* exception = DOMException::create( | 446 DOMException* exception = DOMException::create( |
395 InvalidStateError, "OffscreenCanvas presentation not implemented."); | 447 InvalidStateError, "OffscreenCanvas presentation not implemented."); |
396 while (!m_pendingPresentResolvers.isEmpty()) { | 448 while (!m_pendingPresentResolvers.isEmpty()) { |
397 ScriptPromiseResolver* resolver = m_pendingPresentResolvers.takeFirst(); | 449 ScriptPromiseResolver* resolver = m_pendingPresentResolvers.takeFirst(); |
398 resolver->reject(exception); | 450 resolver->reject(exception); |
399 } | 451 } |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
460 } else { | 512 } else { |
461 // Right eye defaults | 513 // Right eye defaults |
462 rightBounds->left = 0.5f; | 514 rightBounds->left = 0.5f; |
463 rightBounds->top = 0.0f; | 515 rightBounds->top = 0.0f; |
464 rightBounds->width = 0.5f; | 516 rightBounds->width = 0.5f; |
465 rightBounds->height = 1.0f; | 517 rightBounds->height = 1.0f; |
466 m_layer.setRightBounds({0.5f, 0.0f, 0.5f, 1.0f}); | 518 m_layer.setRightBounds({0.5f, 0.0f, 0.5f, 1.0f}); |
467 } | 519 } |
468 | 520 |
469 m_display->UpdateLayerBounds(m_vrFrameId, std::move(leftBounds), | 521 m_display->UpdateLayerBounds(m_vrFrameId, std::move(leftBounds), |
470 std::move(rightBounds), m_sourceWidth, | 522 std::move(rightBounds)); |
471 m_sourceHeight); | |
472 } | 523 } |
473 | 524 |
474 HeapVector<VRLayer> VRDisplay::getLayers() { | 525 HeapVector<VRLayer> VRDisplay::getLayers() { |
475 HeapVector<VRLayer> layers; | 526 HeapVector<VRLayer> layers; |
476 | 527 |
477 if (m_isPresenting) { | 528 if (m_isPresenting) { |
478 layers.push_back(m_layer); | 529 layers.push_back(m_layer); |
479 } | 530 } |
480 | 531 |
481 return layers; | 532 return layers; |
482 } | 533 } |
483 | 534 |
484 void VRDisplay::submitFrame() { | 535 void VRDisplay::submitFrame() { |
485 if (!m_display) | 536 if (!m_display) |
486 return; | 537 return; |
487 TRACE_EVENT1("gpu", "submitFrame", "frame", m_vrFrameId); | |
488 | 538 |
489 Document* doc = this->document(); | 539 Document* doc = this->document(); |
490 if (!m_isPresenting) { | 540 if (!m_isPresenting) { |
491 if (doc) { | 541 if (doc) { |
492 doc->addConsoleMessage(ConsoleMessage::create( | 542 doc->addConsoleMessage(ConsoleMessage::create( |
493 RenderingMessageSource, WarningMessageLevel, | 543 RenderingMessageSource, WarningMessageLevel, |
494 "submitFrame has no effect when the VRDisplay is not presenting.")); | 544 "submitFrame has no effect when the VRDisplay is not presenting.")); |
495 } | 545 } |
496 return; | 546 return; |
497 } | 547 } |
498 | 548 |
499 if (!m_inAnimationFrame) { | 549 if (!m_inAnimationFrame) { |
500 if (doc) { | 550 if (doc) { |
501 doc->addConsoleMessage( | 551 doc->addConsoleMessage( |
502 ConsoleMessage::create(RenderingMessageSource, WarningMessageLevel, | 552 ConsoleMessage::create(RenderingMessageSource, WarningMessageLevel, |
503 "submitFrame must be called within a " | 553 "submitFrame must be called within a " |
504 "VRDisplay.requestAnimationFrame callback.")); | 554 "VRDisplay.requestAnimationFrame callback.")); |
505 } | 555 } |
506 return; | 556 return; |
507 } | 557 } |
508 | 558 |
509 if (!m_contextGL) { | 559 if (!m_contextGL) { |
510 // Something got confused, we can't submit frames without a GL context. | 560 // Something got confused, we can't submit frames without a GL context. |
511 return; | 561 return; |
512 } | 562 } |
513 | 563 |
514 // No frame Id to write before submitting the frame. | 564 // No frame Id to write before submitting the frame. |
515 if (m_vrFrameId < 0) { | 565 if (m_vrFrameId < 0) { |
516 // TODO(klausw): There used to be a submitFrame here, but we can't | 566 m_display->SubmitFrame(m_framePose.Clone()); |
517 // submit without a frameId and associated pose data. Just drop it. | |
518 return; | 567 return; |
519 } | 568 } |
520 | 569 |
521 m_contextGL->Flush(); | 570 // Write the frame number for the pose used into a bottom left pixel block. |
522 auto elem = m_layer.source(); | 571 // It is read by chrome/browser/android/vr_shell/vr_shell.cc to associate |
| 572 // the correct corresponding pose for submission. |
| 573 auto gl = m_contextGL; |
523 | 574 |
524 // Check if the canvas got resized, if yes send a bounds update. | 575 // We must ensure that the WebGL app's GL state is preserved. We do this by |
525 int currentWidth = m_renderingContext->drawingBufferWidth(); | 576 // calling low-level GL commands directly so that the rendering context's |
526 int currentHeight = m_renderingContext->drawingBufferHeight(); | 577 // saved parameters don't get overwritten. |
527 if ((currentWidth != m_sourceWidth || currentHeight != m_sourceHeight) && | |
528 currentWidth != 0 && currentHeight != 0) { | |
529 m_sourceWidth = currentWidth; | |
530 m_sourceHeight = currentHeight; | |
531 updateLayerBounds(); | |
532 } | |
533 | 578 |
534 // There's two types of synchronization needed for submitting frames: | 579 gl->Enable(GL_SCISSOR_TEST); |
535 // | 580 // Use a few pixels to ensure we get a clean color. The resolution for the |
536 // - Before submitting, need to wait for the previous frame to be | 581 // WebGL buffer may not match the final rendered destination size, and |
537 // pulled off the transfer surface to avoid lost frames. This | 582 // texture filtering could interfere for single pixels. This isn't visible |
538 // is currently a compile-time option, normally we always want | 583 // since the final rendering hides the edges via a vignette effect. |
539 // to defer this wait to increase parallelism. | 584 gl->Scissor(0, 0, 4, 4); |
540 // | 585 gl->ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); |
541 // - After submitting, need to wait for the mailbox to be consumed, | 586 // Careful with the arithmetic here. Float color 1.f is equivalent to int 255. |
542 // and must remain inside the execution context while waiting. | 587 // Use the low byte of the index as the red component, and store an arbitrary |
543 // The waitForPreviousTransferToFinish option defers this wait | 588 // magic number in green/blue. This number must match the reading code in |
544 // until the next frame. That's more efficient, but seems to | 589 // vr_shell.cc. Avoid all-black/all-white. |
545 // cause wobbly framerates. | 590 gl->ClearColor((m_vrFrameId & 255) / 255.0f, |
546 bool waitForPreviousTransferToFinish = | 591 kWebVrPosePixelMagicNumbers[0] / 255.0f, |
547 RuntimeEnabledFeatures::webVRExperimentalRenderingEnabled(); | 592 kWebVrPosePixelMagicNumbers[1] / 255.0f, 1.0f); |
| 593 gl->Clear(GL_COLOR_BUFFER_BIT); |
548 | 594 |
549 if (waitForPreviousTransferToFinish) { | 595 // Set the GL state back to what was set by the WebVR application. |
550 TRACE_EVENT0("gpu", "VRDisplay::waitForPreviousTransferToFinish"); | 596 m_renderingContext->restoreScissorEnabled(); |
551 while (m_pendingSubmitFrame) { | 597 m_renderingContext->restoreScissorBox(); |
552 if (!m_submit_frame_client_binding.WaitForIncomingMethodCall()) { | 598 m_renderingContext->restoreColorMask(); |
553 DLOG(ERROR) << "Failed to receive SubmitFrame response"; | 599 m_renderingContext->restoreClearColor(); |
554 break; | |
555 } | |
556 } | |
557 } | |
558 | 600 |
559 RefPtr<Image> imageRef = m_renderingContext->getImage( | 601 m_display->SubmitFrame(m_framePose.Clone()); |
560 PreferAcceleration, SnapshotReasonCreateImageBitmap); | |
561 | |
562 // Hardware-accelerated rendering should always be texture backed. | |
563 // I hope nobody is trying to do WebVR with software rendering. | |
564 DCHECK(imageRef->isTextureBacked()); | |
565 | |
566 // The AcceleratedStaticBitmapImage must be kept alive until the | |
567 // mailbox is used via createAndConsumeTextureCHROMIUM, the mailbox | |
568 // itself does not keep it alive. We must keep a reference to the | |
569 // image until the mailbox was consumed. | |
570 StaticBitmapImage* staticImage = | |
571 static_cast<StaticBitmapImage*>(imageRef.get()); | |
572 staticImage->ensureMailbox(); | |
573 | |
574 if (waitForPreviousTransferToFinish) { | |
575 // Save a reference to the image to keep it alive until next frame, | |
576 // where we'll wait for the transfer to finish before overwriting | |
577 // it. | |
578 m_previousImage = std::move(imageRef); | |
579 } | |
580 | |
581 // Wait for the previous render to finish, to avoid losing frames in the | |
582 // Android Surface / GLConsumer pair. TODO(klausw): make this tunable? | |
583 // Other devices may have different preferences. | |
584 { | |
585 TRACE_EVENT0("gpu", "waitForPreviousRenderToFinish"); | |
586 while (m_pendingPreviousFrameRender) { | |
587 if (!m_submit_frame_client_binding.WaitForIncomingMethodCall()) { | |
588 DLOG(ERROR) << "Failed to receive SubmitFrame response"; | |
589 break; | |
590 } | |
591 } | |
592 } | |
593 | |
594 m_pendingPreviousFrameRender = true; | |
595 m_pendingSubmitFrame = true; | |
596 m_display->SubmitFrame( | |
597 m_vrFrameId, gpu::MailboxHolder(staticImage->mailbox(), | |
598 staticImage->syncToken(), GL_TEXTURE_2D)); | |
599 | |
600 // If we're not deferring the wait for transferring the mailbox, | |
601 // we need to wait for it now to prevent the image going out of | |
602 // scope before its mailbox is retrieved. | |
603 if (!waitForPreviousTransferToFinish) { | |
604 TRACE_EVENT0("gpu", "waitForCurrentTransferToFinish"); | |
605 while (m_pendingSubmitFrame) { | |
606 if (!m_submit_frame_client_binding.WaitForIncomingMethodCall()) { | |
607 DLOG(ERROR) << "Failed to receive SubmitFrame response"; | |
608 break; | |
609 } | |
610 } | |
611 } | |
612 } | |
613 | |
614 void VRDisplay::OnSubmitFrameTransferred() { | |
615 m_pendingSubmitFrame = false; | |
616 } | |
617 | |
618 void VRDisplay::OnSubmitFrameRendered() { | |
619 m_pendingPreviousFrameRender = false; | |
620 } | 602 } |
621 | 603 |
622 Document* VRDisplay::document() { | 604 Document* VRDisplay::document() { |
623 return m_navigatorVR->document(); | 605 return m_navigatorVR->document(); |
624 } | 606 } |
625 | 607 |
626 void VRDisplay::OnPresentChange() { | 608 void VRDisplay::OnPresentChange() { |
627 if (m_isPresenting && !m_isValidDeviceForPresenting) { | 609 if (m_isPresenting && !m_isValidDeviceForPresenting) { |
628 DVLOG(1) << __FUNCTION__ << ": device not valid, not sending event"; | 610 VLOG(1) << __FUNCTION__ << ": device not valid, not sending event"; |
629 return; | 611 return; |
630 } | 612 } |
631 m_navigatorVR->enqueueVREvent(VRDisplayEvent::create( | 613 m_navigatorVR->enqueueVREvent(VRDisplayEvent::create( |
632 EventTypeNames::vrdisplaypresentchange, true, false, this, "")); | 614 EventTypeNames::vrdisplaypresentchange, true, false, this, "")); |
633 } | 615 } |
634 | 616 |
635 void VRDisplay::OnChanged(device::mojom::blink::VRDisplayInfoPtr display) { | 617 void VRDisplay::OnChanged(device::mojom::blink::VRDisplayInfoPtr display) { |
636 update(display); | 618 update(display); |
637 } | 619 } |
638 | 620 |
639 void VRDisplay::OnExitPresent() { | 621 void VRDisplay::OnExitPresent() { |
640 stopPresenting(); | 622 stopPresenting(); |
641 } | 623 } |
642 | 624 |
643 void VRDisplay::onConnected() { | 625 void VRDisplay::onConnected() { |
644 m_navigatorVR->enqueueVREvent(VRDisplayEvent::create( | 626 m_navigatorVR->enqueueVREvent(VRDisplayEvent::create( |
645 EventTypeNames::vrdisplayconnect, true, false, this, "connect")); | 627 EventTypeNames::vrdisplayconnect, true, false, this, "connect")); |
646 } | 628 } |
647 | 629 |
648 void VRDisplay::onDisconnected() { | 630 void VRDisplay::onDisconnected() { |
649 m_navigatorVR->enqueueVREvent(VRDisplayEvent::create( | 631 m_navigatorVR->enqueueVREvent(VRDisplayEvent::create( |
650 EventTypeNames::vrdisplaydisconnect, true, false, this, "disconnect")); | 632 EventTypeNames::vrdisplaydisconnect, true, false, this, "disconnect")); |
651 } | 633 } |
652 | 634 |
653 void VRDisplay::stopPresenting() { | 635 void VRDisplay::stopPresenting() { |
654 if (m_isPresenting) { | 636 if (m_isPresenting) { |
655 if (!m_capabilities->hasExternalDisplay()) { | 637 if (!m_capabilities->hasExternalDisplay()) { |
656 if (m_layer.source().isHTMLCanvasElement()) { | 638 if (m_layer.source().isHTMLCanvasElement()) { |
657 // TODO(klausw,crbug.com/698923): If compositor updates are | 639 auto canvas = m_layer.source().getAsHTMLCanvasElement(); |
658 // suppressed, restore them here. | 640 Fullscreen::fullyExitFullscreen(canvas->document()); |
| 641 m_fullscreenCheckTimer.stop(); |
| 642 if (!m_fullscreenOrigWidth.isNull()) { |
| 643 canvas->setInlineStyleProperty(CSSPropertyWidth, |
| 644 m_fullscreenOrigWidth); |
| 645 m_fullscreenOrigWidth = String(); |
| 646 } |
| 647 if (!m_fullscreenOrigHeight.isNull()) { |
| 648 canvas->setInlineStyleProperty(CSSPropertyWidth, |
| 649 m_fullscreenOrigHeight); |
| 650 m_fullscreenOrigHeight = String(); |
| 651 } |
659 } else { | 652 } else { |
660 // TODO(junov, crbug.com/695497): Implement for OffscreenCanvas | 653 // TODO(junov, crbug.com/695497): Implement for OffscreenCanvas |
661 } | 654 } |
662 } else { | 655 } else { |
663 // Can't get into this presentation mode, so nothing to do here. | 656 // Can't get into this presentation mode, so nothing to do here. |
664 } | 657 } |
665 m_isPresenting = false; | 658 m_isPresenting = false; |
666 OnPresentChange(); | 659 OnPresentChange(); |
667 } | 660 } |
668 | 661 |
669 m_renderingContext = nullptr; | 662 m_renderingContext = nullptr; |
670 m_contextGL = nullptr; | 663 m_contextGL = nullptr; |
671 m_pendingSubmitFrame = false; | |
672 m_pendingPreviousFrameRender = false; | |
673 } | 664 } |
674 | 665 |
675 void VRDisplay::OnActivate(device::mojom::blink::VRDisplayEventReason reason) { | 666 void VRDisplay::OnActivate(device::mojom::blink::VRDisplayEventReason reason) { |
676 if (!m_navigatorVR->isFocused() || m_displayBlurred) | 667 if (!m_navigatorVR->isFocused() || m_displayBlurred) |
677 return; | 668 return; |
678 m_navigatorVR->dispatchVRGestureEvent(VRDisplayEvent::create( | 669 m_navigatorVR->dispatchVRGestureEvent(VRDisplayEvent::create( |
679 EventTypeNames::vrdisplayactivate, true, false, this, reason)); | 670 EventTypeNames::vrdisplayactivate, true, false, this, reason)); |
680 } | 671 } |
681 | 672 |
682 void VRDisplay::OnDeactivate( | 673 void VRDisplay::OnDeactivate( |
683 device::mojom::blink::VRDisplayEventReason reason) { | 674 device::mojom::blink::VRDisplayEventReason reason) { |
684 m_navigatorVR->enqueueVREvent(VRDisplayEvent::create( | 675 m_navigatorVR->enqueueVREvent(VRDisplayEvent::create( |
685 EventTypeNames::vrdisplaydeactivate, true, false, this, reason)); | 676 EventTypeNames::vrdisplaydeactivate, true, false, this, reason)); |
686 } | 677 } |
687 | 678 |
688 void VRDisplay::OnVSync(device::mojom::blink::VRPosePtr pose, | 679 void VRDisplay::OnVSync(device::mojom::blink::VRPosePtr pose, |
689 mojo::common::mojom::blink::TimeDeltaPtr time, | 680 mojo::common::mojom::blink::TimeDeltaPtr time, |
690 int16_t frameId, | 681 int16_t frameId, |
691 device::mojom::blink::VRVSyncProvider::Status error) { | 682 device::mojom::blink::VRVSyncProvider::Status error) { |
692 switch (error) { | 683 switch (error) { |
693 case device::mojom::blink::VRVSyncProvider::Status::SUCCESS: | 684 case device::mojom::blink::VRVSyncProvider::Status::SUCCESS: |
694 break; | 685 break; |
695 case device::mojom::blink::VRVSyncProvider::Status::CLOSING: | 686 case device::mojom::blink::VRVSyncProvider::Status::RETRY: |
| 687 m_vrVSyncProvider->GetVSync(convertToBaseCallback( |
| 688 WTF::bind(&VRDisplay::OnVSync, wrapWeakPersistent(this)))); |
696 return; | 689 return; |
697 } | 690 } |
698 m_pendingVsync = false; | 691 m_pendingVsync = false; |
| 692 if (m_displayBlurred) |
| 693 return; |
| 694 if (!m_scriptedAnimationController) |
| 695 return; |
699 Document* doc = this->document(); | 696 Document* doc = this->document(); |
700 if (!doc || m_displayBlurred || !m_scriptedAnimationController) | 697 if (!doc) |
701 return; | 698 return; |
702 | 699 |
703 WTF::TimeDelta timeDelta = | 700 WTF::TimeDelta timeDelta = |
704 WTF::TimeDelta::FromMicroseconds(time->microseconds); | 701 WTF::TimeDelta::FromMicroseconds(time->microseconds); |
705 // Ensure a consistent timebase with document rAF. | 702 // Ensure a consistent timebase with document rAF. |
706 if (m_timebase < 0) { | 703 if (m_timebase < 0) { |
707 m_timebase = WTF::monotonicallyIncreasingTime() - timeDelta.InSecondsF(); | 704 m_timebase = WTF::monotonicallyIncreasingTime() - timeDelta.InSecondsF(); |
708 } | 705 } |
709 | 706 |
710 AutoReset<bool> animating(&m_inAnimationFrame, true); | 707 AutoReset<bool> animating(&m_inAnimationFrame, true); |
711 m_framePose = std::move(pose); | 708 m_framePose = std::move(pose); |
712 m_vrFrameId = frameId; | 709 m_vrFrameId = frameId; |
713 m_pendingRaf = false; | 710 m_pendingRaf = false; |
714 m_scriptedAnimationController->serviceScriptedAnimations( | 711 m_scriptedAnimationController->serviceScriptedAnimations( |
715 m_timebase + timeDelta.InSecondsF()); | 712 m_timebase + timeDelta.InSecondsF()); |
716 } | 713 } |
717 | 714 |
718 void VRDisplay::ConnectVSyncProvider() { | 715 void VRDisplay::ConnectVSyncProvider() { |
719 if (!m_navigatorVR->isFocused() || m_vrVSyncProvider.is_bound()) | 716 if (!m_navigatorVR->isFocused() || m_vrVSyncProvider.is_bound()) |
720 return; | 717 return; |
721 m_display->GetVRVSyncProvider(mojo::MakeRequest(&m_vrVSyncProvider)); | 718 m_display->GetVRVSyncProvider(mojo::MakeRequest(&m_vrVSyncProvider)); |
722 m_vrVSyncProvider.set_connection_error_handler(convertToBaseCallback( | |
723 WTF::bind(&VRDisplay::OnVSyncConnectionError, wrapWeakPersistent(this)))); | |
724 if (m_pendingRaf && !m_displayBlurred) { | 719 if (m_pendingRaf && !m_displayBlurred) { |
725 m_pendingVsync = true; | 720 m_pendingVsync = true; |
726 m_vrVSyncProvider->GetVSync(convertToBaseCallback( | 721 m_vrVSyncProvider->GetVSync(convertToBaseCallback( |
727 WTF::bind(&VRDisplay::OnVSync, wrapWeakPersistent(this)))); | 722 WTF::bind(&VRDisplay::OnVSync, wrapWeakPersistent(this)))); |
728 } | 723 } |
729 } | 724 } |
730 | 725 |
731 void VRDisplay::OnVSyncConnectionError() { | 726 void VRDisplay::onFullscreenCheck(TimerBase*) { |
732 m_vrVSyncProvider.reset(); | 727 DCHECK(m_layer.source().isHTMLCanvasElement()); |
733 ConnectVSyncProvider(); | 728 if (!m_isPresenting) { |
| 729 m_fullscreenCheckTimer.stop(); |
| 730 return; |
| 731 } |
| 732 // TODO: This is a temporary measure to track if fullscreen mode has been |
| 733 // exited by the UA. If so we need to end VR presentation. Soon we won't |
| 734 // depend on the Fullscreen API to fake VR presentation, so this will |
| 735 // become unnessecary. Until that point, though, this seems preferable to |
| 736 // adding a bunch of notification plumbing to Fullscreen. |
| 737 if (!Fullscreen::isCurrentFullScreenElement( |
| 738 *m_layer.source().getAsHTMLCanvasElement())) { |
| 739 // TODO(mthiesse): Due to asynchronous resizing, we might get kicked out of |
| 740 // fullscreen when changing display parameters upon entering WebVR. So one |
| 741 // time only, we reenter fullscreen after having left it; otherwise we exit |
| 742 // presentation. |
| 743 if (m_reenteredFullscreen) { |
| 744 m_isPresenting = false; |
| 745 OnPresentChange(); |
| 746 m_fullscreenCheckTimer.stop(); |
| 747 if (m_display) |
| 748 m_display->ExitPresent(); |
| 749 return; |
| 750 } |
| 751 m_reenteredFullscreen = true; |
| 752 auto canvas = m_layer.source().getAsHTMLCanvasElement(); |
| 753 Document* doc = this->document(); |
| 754 std::unique_ptr<UserGestureIndicator> gestureIndicator; |
| 755 if (doc) { |
| 756 gestureIndicator = WTF::wrapUnique( |
| 757 new UserGestureIndicator(DocumentUserGestureToken::create( |
| 758 doc, UserGestureToken::Status::PossiblyExistingGesture))); |
| 759 } |
| 760 Fullscreen::requestFullscreen(*canvas); |
| 761 } |
734 } | 762 } |
735 | 763 |
736 ScriptedAnimationController& VRDisplay::ensureScriptedAnimationController( | 764 ScriptedAnimationController& VRDisplay::ensureScriptedAnimationController( |
737 Document* doc) { | 765 Document* doc) { |
738 if (!m_scriptedAnimationController) | 766 if (!m_scriptedAnimationController) |
739 m_scriptedAnimationController = ScriptedAnimationController::create(doc); | 767 m_scriptedAnimationController = ScriptedAnimationController::create(doc); |
740 | 768 |
741 return *m_scriptedAnimationController; | 769 return *m_scriptedAnimationController; |
742 } | 770 } |
743 | 771 |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
779 visitor->trace(m_stageParameters); | 807 visitor->trace(m_stageParameters); |
780 visitor->trace(m_eyeParametersLeft); | 808 visitor->trace(m_eyeParametersLeft); |
781 visitor->trace(m_eyeParametersRight); | 809 visitor->trace(m_eyeParametersRight); |
782 visitor->trace(m_layer); | 810 visitor->trace(m_layer); |
783 visitor->trace(m_renderingContext); | 811 visitor->trace(m_renderingContext); |
784 visitor->trace(m_scriptedAnimationController); | 812 visitor->trace(m_scriptedAnimationController); |
785 visitor->trace(m_pendingPresentResolvers); | 813 visitor->trace(m_pendingPresentResolvers); |
786 } | 814 } |
787 | 815 |
788 } // namespace blink | 816 } // namespace blink |
OLD | NEW |