| 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/dom/DOMException.h" | 7 #include "core/dom/DOMException.h" |
| 8 #include "core/dom/Fullscreen.h" | 8 #include "core/dom/Fullscreen.h" |
| 9 #include "core/inspector/ConsoleMessage.h" | 9 #include "core/inspector/ConsoleMessage.h" |
| 10 #include "modules/vr/NavigatorVR.h" | 10 #include "modules/vr/NavigatorVR.h" |
| (...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 153 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState)
; | 153 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState)
; |
| 154 ScriptPromise promise = resolver->promise(); | 154 ScriptPromise promise = resolver->promise(); |
| 155 | 155 |
| 156 // If the VRDisplay does not advertise the ability to present reject the req
uest. | 156 // If the VRDisplay does not advertise the ability to present reject the req
uest. |
| 157 if (!m_capabilities->canPresent()) { | 157 if (!m_capabilities->canPresent()) { |
| 158 DOMException* exception = DOMException::create(InvalidStateError, "VRDis
play cannot present."); | 158 DOMException* exception = DOMException::create(InvalidStateError, "VRDis
play cannot present."); |
| 159 resolver->reject(exception); | 159 resolver->reject(exception); |
| 160 return promise; | 160 return promise; |
| 161 } | 161 } |
| 162 | 162 |
| 163 bool firstPresent = !m_isPresenting; |
| 164 |
| 163 // Initiating VR presentation is only allowed in response to a user gesture. | 165 // Initiating VR presentation is only allowed in response to a user gesture. |
| 164 // If the VRDisplay is already presenting, however, repeated calls are | 166 // If the VRDisplay is already presenting, however, repeated calls are |
| 165 // allowed outside a user gesture so that the presented content may be | 167 // allowed outside a user gesture so that the presented content may be |
| 166 // updated. | 168 // updated. |
| 167 if (!m_isPresenting && !UserGestureIndicator::utilizeUserGesture()) { | 169 if (firstPresent && !UserGestureIndicator::utilizeUserGesture()) { |
| 168 DOMException* exception = DOMException::create(InvalidStateError, "API c
an only be initiated by a user gesture."); | 170 DOMException* exception = DOMException::create(InvalidStateError, "API c
an only be initiated by a user gesture."); |
| 169 resolver->reject(exception); | 171 resolver->reject(exception); |
| 170 return promise; | 172 return promise; |
| 171 } | 173 } |
| 172 | 174 |
| 173 m_isPresenting = false; | 175 m_isPresenting = false; |
| 174 | 176 |
| 175 // A valid number of layers must be provided in order to present. | 177 // A valid number of layers must be provided in order to present. |
| 176 if (layers.size() == 0 || layers.size() > m_capabilities->maxLayers()) { | 178 if (layers.size() == 0 || layers.size() > m_capabilities->maxLayers()) { |
| 179 forceExitPresent(); |
| 177 DOMException* exception = DOMException::create(InvalidStateError, "Inval
id number of layers."); | 180 DOMException* exception = DOMException::create(InvalidStateError, "Inval
id number of layers."); |
| 178 if (m_isPresenting) { | |
| 179 exitPresent(scriptState); | |
| 180 } | |
| 181 resolver->reject(exception); | 181 resolver->reject(exception); |
| 182 return promise; | 182 return promise; |
| 183 } | 183 } |
| 184 | 184 |
| 185 m_layer = layers[0]; | 185 m_layer = layers[0]; |
| 186 | 186 |
| 187 if (m_layer.source()) { | 187 if (!m_layer.source()) { |
| 188 if (!m_capabilities->hasExternalDisplay()) { | 188 forceExitPresent(); |
| 189 // TODO: Need a proper VR compositor, but for the moment on mobile | |
| 190 // we'll just make the canvas fullscreen so that VrShell can pick it | |
| 191 // up through the standard (high latency) compositing path. | |
| 192 Fullscreen::requestFullscreen(*m_layer.source(), Fullscreen::Unprefi
xedRequest); | |
| 193 | |
| 194 m_isPresenting = true; | |
| 195 | |
| 196 resolver->resolve(); | |
| 197 | |
| 198 m_navigatorVR->fireVRDisplayPresentChange(this); | |
| 199 | |
| 200 // Check to see if the canvas is still the current fullscreen | |
| 201 // element once per second. | |
| 202 m_fullscreenCheckTimer.startRepeating(1.0, BLINK_FROM_HERE); | |
| 203 | |
| 204 controller()->requestPresent(m_displayId); | |
| 205 } else { | |
| 206 DOMException* exception = DOMException::create(InvalidStateError, "V
R Presentation not implemented for this VRDisplay."); | |
| 207 resolver->reject(exception); | |
| 208 } | |
| 209 | |
| 210 // Set up the texture bounds for the provided layer | |
| 211 device::blink::VRLayerBoundsPtr leftBounds = device::blink::VRLayerBound
s::New(); | |
| 212 device::blink::VRLayerBoundsPtr rightBounds = device::blink::VRLayerBoun
ds::New(); | |
| 213 | |
| 214 if (m_layer.hasLeftBounds()) { | |
| 215 leftBounds->left = m_layer.leftBounds()[0]; | |
| 216 leftBounds->top = m_layer.leftBounds()[1]; | |
| 217 leftBounds->width = m_layer.leftBounds()[2]; | |
| 218 leftBounds->height = m_layer.leftBounds()[3]; | |
| 219 } else { | |
| 220 // Left eye defaults | |
| 221 leftBounds->left = 0.0f; | |
| 222 leftBounds->top = 0.0f; | |
| 223 leftBounds->width = 0.5f; | |
| 224 leftBounds->height = 1.0f; | |
| 225 } | |
| 226 | |
| 227 if (m_layer.hasRightBounds()) { | |
| 228 rightBounds->left = m_layer.rightBounds()[0]; | |
| 229 rightBounds->top = m_layer.rightBounds()[1]; | |
| 230 rightBounds->width = m_layer.rightBounds()[2]; | |
| 231 rightBounds->height = m_layer.rightBounds()[3]; | |
| 232 } else { | |
| 233 // Right eye defaults | |
| 234 rightBounds->left = 0.5f; | |
| 235 rightBounds->top = 0.0f; | |
| 236 rightBounds->width = 0.5f; | |
| 237 rightBounds->height = 1.0f; | |
| 238 } | |
| 239 | |
| 240 controller()->updateLayerBounds(m_displayId, std::move(leftBounds), std:
:move(rightBounds)); | |
| 241 } else { | |
| 242 DOMException* exception = DOMException::create(InvalidStateError, "Inval
id layer source."); | 189 DOMException* exception = DOMException::create(InvalidStateError, "Inval
id layer source."); |
| 243 resolver->reject(exception); | 190 resolver->reject(exception); |
| 191 return promise; |
| 192 } |
| 193 |
| 194 CanvasRenderingContext* renderingContext = m_layer.source()->renderingContex
t(); |
| 195 |
| 196 if (!renderingContext || !renderingContext->is3d()) { |
| 197 forceExitPresent(); |
| 198 DOMException* exception = DOMException::create(InvalidStateError, "Layer
source must have a WebGLRenderingContext"); |
| 199 resolver->reject(exception); |
| 200 return promise; |
| 201 } |
| 202 |
| 203 if (!m_capabilities->hasExternalDisplay()) { |
| 204 // TODO: Need a proper VR compositor, but for the moment on mobile |
| 205 // we'll just make the canvas fullscreen so that VrShell can pick it |
| 206 // up through the standard (high latency) compositing path. |
| 207 Fullscreen::requestFullscreen(*m_layer.source(), Fullscreen::UnprefixedR
equest); |
| 208 |
| 209 // Check to see if the canvas is still the current fullscreen |
| 210 // element once per second. |
| 211 m_fullscreenCheckTimer.startRepeating(1.0, BLINK_FROM_HERE); |
| 212 } |
| 213 |
| 214 if (firstPresent) { |
| 215 controller()->requestPresent(resolver, m_displayId); |
| 216 } else { |
| 217 updateLayerBounds(); |
| 218 resolver->resolve(); |
| 244 } | 219 } |
| 245 | 220 |
| 246 return promise; | 221 return promise; |
| 247 } | 222 } |
| 248 | 223 |
| 249 ScriptPromise VRDisplay::exitPresent(ScriptState* scriptState) | 224 ScriptPromise VRDisplay::exitPresent(ScriptState* scriptState) |
| 250 { | 225 { |
| 251 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState)
; | 226 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState)
; |
| 252 ScriptPromise promise = resolver->promise(); | 227 ScriptPromise promise = resolver->promise(); |
| 253 | 228 |
| 254 if (!m_isPresenting) { | 229 if (!m_isPresenting) { |
| 255 // Can't stop presenting if we're not presenting. | 230 // Can't stop presenting if we're not presenting. |
| 256 DOMException* exception = DOMException::create(InvalidStateError, "VRDis
play is not presenting."); | 231 DOMException* exception = DOMException::create(InvalidStateError, "VRDis
play is not presenting."); |
| 257 resolver->reject(exception); | 232 resolver->reject(exception); |
| 258 return promise; | 233 return promise; |
| 259 } | 234 } |
| 260 | 235 |
| 261 if (!m_capabilities->hasExternalDisplay()) { | 236 controller()->exitPresent(m_displayId); |
| 262 Fullscreen::fullyExitFullscreen(m_layer.source()->document()); | |
| 263 m_fullscreenCheckTimer.stop(); | |
| 264 controller()->exitPresent(m_displayId); | |
| 265 } else { | |
| 266 // Can't get into this presentation mode, so nothing to do here. | |
| 267 } | |
| 268 | 237 |
| 269 m_isPresenting = false; | |
| 270 | |
| 271 // TODO: Resolve when exit is confirmed | |
| 272 resolver->resolve(); | 238 resolver->resolve(); |
| 273 | 239 |
| 274 m_navigatorVR->fireVRDisplayPresentChange(this); | 240 forceExitPresent(); |
| 275 | 241 |
| 276 return promise; | 242 return promise; |
| 277 } | 243 } |
| 278 | 244 |
| 245 void VRDisplay::beginPresent(ScriptPromiseResolver* resolver) |
| 246 { |
| 247 if (m_capabilities->hasExternalDisplay()) { |
| 248 forceExitPresent(); |
| 249 DOMException* exception = DOMException::create(InvalidStateError, "VR Pr
esentation not implemented for this VRDisplay."); |
| 250 resolver->reject(exception); |
| 251 return; |
| 252 } |
| 253 |
| 254 m_isPresenting = true; |
| 255 |
| 256 updateLayerBounds(); |
| 257 |
| 258 resolver->resolve(); |
| 259 m_navigatorVR->fireVRDisplayPresentChange(this); |
| 260 } |
| 261 |
| 262 void VRDisplay::forceExitPresent() |
| 263 { |
| 264 if (m_isPresenting) { |
| 265 if (!m_capabilities->hasExternalDisplay()) { |
| 266 Fullscreen::fullyExitFullscreen(m_layer.source()->document()); |
| 267 m_fullscreenCheckTimer.stop(); |
| 268 } else { |
| 269 // Can't get into this presentation mode, so nothing to do here. |
| 270 } |
| 271 m_navigatorVR->fireVRDisplayPresentChange(this); |
| 272 } |
| 273 |
| 274 m_isPresenting = false; |
| 275 } |
| 276 |
| 277 void VRDisplay::updateLayerBounds() |
| 278 { |
| 279 // Set up the texture bounds for the provided layer |
| 280 device::blink::VRLayerBoundsPtr leftBounds = device::blink::VRLayerBounds::N
ew(); |
| 281 device::blink::VRLayerBoundsPtr rightBounds = device::blink::VRLayerBounds::
New(); |
| 282 |
| 283 if (m_layer.hasLeftBounds()) { |
| 284 leftBounds->left = m_layer.leftBounds()[0]; |
| 285 leftBounds->top = m_layer.leftBounds()[1]; |
| 286 leftBounds->width = m_layer.leftBounds()[2]; |
| 287 leftBounds->height = m_layer.leftBounds()[3]; |
| 288 } else { |
| 289 // Left eye defaults |
| 290 leftBounds->left = 0.0f; |
| 291 leftBounds->top = 0.0f; |
| 292 leftBounds->width = 0.5f; |
| 293 leftBounds->height = 1.0f; |
| 294 } |
| 295 |
| 296 if (m_layer.hasRightBounds()) { |
| 297 rightBounds->left = m_layer.rightBounds()[0]; |
| 298 rightBounds->top = m_layer.rightBounds()[1]; |
| 299 rightBounds->width = m_layer.rightBounds()[2]; |
| 300 rightBounds->height = m_layer.rightBounds()[3]; |
| 301 } else { |
| 302 // Right eye defaults |
| 303 rightBounds->left = 0.5f; |
| 304 rightBounds->top = 0.0f; |
| 305 rightBounds->width = 0.5f; |
| 306 rightBounds->height = 1.0f; |
| 307 } |
| 308 |
| 309 controller()->updateLayerBounds(m_displayId, std::move(leftBounds), std::mov
e(rightBounds)); |
| 310 } |
| 311 |
| 279 HeapVector<VRLayer> VRDisplay::getLayers() | 312 HeapVector<VRLayer> VRDisplay::getLayers() |
| 280 { | 313 { |
| 281 HeapVector<VRLayer> layers; | 314 HeapVector<VRLayer> layers; |
| 282 | 315 |
| 283 if (m_isPresenting) { | 316 if (m_isPresenting) { |
| 284 layers.append(m_layer); | 317 layers.append(m_layer); |
| 285 } | 318 } |
| 286 | 319 |
| 287 return layers; | 320 return layers; |
| 288 } | 321 } |
| 289 | 322 |
| 290 void VRDisplay::submitFrame() | 323 void VRDisplay::submitFrame() |
| 291 { | 324 { |
| 292 controller()->submitFrame(m_displayId); | 325 controller()->submitFrame(m_displayId, m_framePose.Clone()); |
| 293 m_canUpdateFramePose = true; | 326 m_canUpdateFramePose = true; |
| 294 } | 327 } |
| 295 | 328 |
| 296 void VRDisplay::onFullscreenCheck(TimerBase*) | 329 void VRDisplay::onFullscreenCheck(TimerBase*) |
| 297 { | 330 { |
| 298 // TODO: This is a temporary measure to track if fullscreen mode has been | 331 // TODO: This is a temporary measure to track if fullscreen mode has been |
| 299 // exited by the UA. If so we need to end VR presentation. Soon we won't | 332 // exited by the UA. If so we need to end VR presentation. Soon we won't |
| 300 // depend on the Fullscreen API to fake VR presentation, so this will | 333 // depend on the Fullscreen API to fake VR presentation, so this will |
| 301 // become unnessecary. Until that point, though, this seems preferable to | 334 // become unnessecary. Until that point, though, this seems preferable to |
| 302 // adding a bunch of notification plumbing to Fullscreen. | 335 // adding a bunch of notification plumbing to Fullscreen. |
| 303 if (!Fullscreen::isCurrentFullScreenElement(*m_layer.source())) { | 336 if (!Fullscreen::isCurrentFullScreenElement(*m_layer.source())) { |
| 304 m_isPresenting = false; | 337 m_isPresenting = false; |
| 305 m_navigatorVR->fireVRDisplayPresentChange(this); | 338 m_navigatorVR->fireVRDisplayPresentChange(this); |
| 306 m_fullscreenCheckTimer.stop(); | 339 m_fullscreenCheckTimer.stop(); |
| 307 controller()->exitPresent(m_displayId); | 340 controller()->exitPresent(m_displayId); |
| 308 } | 341 } |
| 309 } | 342 } |
| 310 | 343 |
| 311 DEFINE_TRACE(VRDisplay) | 344 DEFINE_TRACE(VRDisplay) |
| 312 { | 345 { |
| 313 visitor->trace(m_navigatorVR); | 346 visitor->trace(m_navigatorVR); |
| 314 visitor->trace(m_capabilities); | 347 visitor->trace(m_capabilities); |
| 315 visitor->trace(m_stageParameters); | 348 visitor->trace(m_stageParameters); |
| 316 visitor->trace(m_eyeParametersLeft); | 349 visitor->trace(m_eyeParametersLeft); |
| 317 visitor->trace(m_eyeParametersRight); | 350 visitor->trace(m_eyeParametersRight); |
| 318 visitor->trace(m_layer); | 351 visitor->trace(m_layer); |
| 319 } | 352 } |
| 320 | 353 |
| 321 } // namespace blink | 354 } // namespace blink |
| OLD | NEW |