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