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 |