Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(767)

Side by Side Diff: chrome/browser/android/vr_shell/vr_shell.cc

Issue 2562733002: Implement our own GLThread for VR Shell. (Closed)
Patch Set: Address comments Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/android/vr_shell/vr_shell.h" 5 #include "chrome/browser/android/vr_shell/vr_shell.h"
6 6
7 #include "base/metrics/histogram_macros.h" 7 #include "base/metrics/histogram_macros.h"
8 #include "chrome/browser/android/vr_shell/ui_elements.h" 8 #include "base/threading/platform_thread.h"
9 #include "base/threading/thread.h"
10 #include "base/threading/thread_restrictions.h"
11 #include "base/threading/thread_task_runner_handle.h"
9 #include "chrome/browser/android/vr_shell/ui_interface.h" 12 #include "chrome/browser/android/vr_shell/ui_interface.h"
10 #include "chrome/browser/android/vr_shell/ui_scene.h"
11 #include "chrome/browser/android/vr_shell/vr_compositor.h" 13 #include "chrome/browser/android/vr_shell/vr_compositor.h"
12 #include "chrome/browser/android/vr_shell/vr_controller.h"
13 #include "chrome/browser/android/vr_shell/vr_gl_util.h"
14 #include "chrome/browser/android/vr_shell/vr_input_manager.h" 14 #include "chrome/browser/android/vr_shell/vr_input_manager.h"
15 #include "chrome/browser/android/vr_shell/vr_shell_delegate.h" 15 #include "chrome/browser/android/vr_shell/vr_shell_delegate.h"
16 #include "chrome/browser/android/vr_shell/vr_shell_renderer.h" 16 #include "chrome/browser/android/vr_shell/vr_shell_gl.h"
17 #include "chrome/browser/android/vr_shell/vr_usage_monitor.h" 17 #include "chrome/browser/android/vr_shell/vr_usage_monitor.h"
18 #include "chrome/browser/android/vr_shell/vr_web_contents_observer.h" 18 #include "chrome/browser/android/vr_shell/vr_web_contents_observer.h"
19 #include "content/public/browser/navigation_controller.h" 19 #include "content/public/browser/navigation_controller.h"
20 #include "content/public/browser/render_view_host.h" 20 #include "content/public/browser/render_view_host.h"
21 #include "content/public/browser/render_widget_host.h" 21 #include "content/public/browser/render_widget_host.h"
22 #include "content/public/browser/render_widget_host_view.h" 22 #include "content/public/browser/render_widget_host_view.h"
23 #include "content/public/browser/web_contents.h" 23 #include "content/public/browser/web_contents.h"
24 #include "content/public/common/referrer.h" 24 #include "content/public/common/referrer.h"
25 #include "device/vr/android/gvr/gvr_device_provider.h" 25 #include "device/vr/android/gvr/gvr_device_provider.h"
26 #include "jni/VrShellImpl_jni.h" 26 #include "jni/VrShellImpl_jni.h"
27 #include "ui/android/view_android.h" 27 #include "ui/android/view_android.h"
28 #include "ui/android/window_android.h" 28 #include "ui/android/window_android.h"
29 #include "ui/base/page_transition_types.h" 29 #include "ui/base/page_transition_types.h"
30 #include "ui/display/display.h" 30 #include "ui/display/display.h"
31 #include "ui/display/screen.h" 31 #include "ui/display/screen.h"
32 #include "ui/gl/gl_bindings.h"
33 #include "ui/gl/init/gl_factory.h"
34 32
35 using base::android::JavaParamRef; 33 using base::android::JavaParamRef;
34 using base::android::JavaRef;
36 35
37 namespace vr_shell { 36 namespace vr_shell {
38 37
39 namespace { 38 namespace {
40 // Constant taken from treasure_hunt demo.
41 static constexpr long kPredictionTimeWithoutVsyncNanos = 50000000;
42
43 static constexpr float kZNear = 0.1f;
44 static constexpr float kZFar = 1000.0f;
45
46 // Screen angle in degrees. 0 = vertical, positive = top closer.
47 static constexpr float kDesktopScreenTiltDefault = 0;
48
49 static constexpr float kReticleWidth = 0.025f;
50 static constexpr float kReticleHeight = 0.025f;
51
52 static constexpr float kLaserWidth = 0.01f;
53
54 // Angle (radians) the beam down from the controller axis, for wrist comfort.
55 static constexpr float kErgoAngleOffset = 0.26f;
56
57 static constexpr gvr::Vec3f kOrigin = {0.0f, 0.0f, 0.0f};
58
59 // In lieu of an elbow model, we assume a position for the user's hand.
60 // TODO(mthiesse): Handedness options.
61 static constexpr gvr::Vec3f kHandPosition = {0.2f, -0.5f, -0.2f};
62
63 // If there is no content quad, and the reticle isn't hitting another element,
64 // draw the reticle at this distance.
65 static constexpr float kDefaultReticleDistance = 2.0f;
66
67 // Fraction of the distance to the object the cursor is drawn at to avoid
68 // rounding errors drawing the cursor behind the object.
69 static constexpr float kReticleOffset = 0.99f;
70
71 // Limit the rendering distance of the reticle to the distance to a corner of
72 // the content quad, times this value. This lets the rendering distance
73 // adjust according to content quad placement.
74 static constexpr float kReticleDistanceMultiplier = 1.5f;
75
76 // GVR buffer indices for use with viewport->SetSourceBufferIndex
77 // or frame.BindBuffer. We use one for world content (with reprojection)
78 // including main VrShell and WebVR content plus world-space UI.
79 // The headlocked buffer is for UI that should not use reprojection.
80 static constexpr int kFramePrimaryBuffer = 0;
81 static constexpr int kFrameHeadlockedBuffer = 1;
82
83 // Pixel dimensions and field of view for the head-locked content. This
84 // is currently sized to fit the WebVR "insecure transport" warnings,
85 // adjust it as needed if there is additional content.
86 static constexpr gvr::Sizei kHeadlockedBufferDimensions = {1024, 1024};
87 static constexpr gvr::Rectf kHeadlockedBufferFov = {20.f, 20.f, 20.f, 20.f};
88
89 // The GVR viewport list has two entries (left eye and right eye) for each
90 // GVR buffer.
91 static constexpr int kViewportListPrimaryOffset = 0;
92 static constexpr int kViewportListHeadlockedOffset = 2;
93
94 // Magic numbers used to mark valid pose index values encoded in frame
95 // data. Must match the magic numbers used in blink's VRDisplay.cpp.
96 static constexpr std::array<uint8_t, 2> kWebVrPosePixelMagicNumbers{{42, 142}};
97
98 vr_shell::VrShell* g_instance; 39 vr_shell::VrShell* g_instance;
99 40
100 static const char kVrShellUIURL[] = "chrome://vr-shell-ui"; 41 static const char kVrShellUIURL[] = "chrome://vr-shell-ui";
101 42
102 float Distance(const gvr::Vec3f& vec1, const gvr::Vec3f& vec2) { 43 class GLThread : public base::Thread {
103 float xdiff = (vec1.x - vec2.x); 44 public:
104 float ydiff = (vec1.y - vec2.y); 45 GLThread(VrShell* vr_shell, const base::WeakPtr<VrShell>& weak_vr_shell,
105 float zdiff = (vec1.z - vec2.z); 46 const base::WeakPtr<VrInputManager>& content_input_manager,
106 float scale = xdiff * xdiff + ydiff * ydiff + zdiff * zdiff; 47 const base::WeakPtr<VrInputManager>& ui_input_manager,
107 return std::sqrt(scale); 48 const scoped_refptr<base::SingleThreadTaskRunner>&
dcheng 2016/12/13 00:59:11 Sorry for the confusion, but it's actually the exp
mthiesse 2016/12/13 15:13:53 Done.
108 } 49 main_thread_task_runner,
50 gvr_context* gvr_api)
51 : base::Thread("VrShellGL"),
52 vr_shell_(vr_shell),
53 weak_vr_shell_(weak_vr_shell),
54 content_input_manager_(content_input_manager),
55 ui_input_manager_(ui_input_manager),
56 main_thread_task_runner_(main_thread_task_runner),
57 gvr_api_(gvr_api) {}
109 58
110 // Generate a quaternion representing the rotation from the negative Z axis 59 ~GLThread() override {
111 // (0, 0, -1) to a specified vector. This is an optimized version of a more 60 Stop();
112 // general vector-to-vector calculation.
113 gvr::Quatf GetRotationFromZAxis(gvr::Vec3f vec) {
114 vr_shell::NormalizeVector(vec);
115 gvr::Quatf quat;
116 quat.qw = 1.0f - vec.z;
117 if (quat.qw < 1e-6f) {
118 // Degenerate case: vectors are exactly opposite. Replace by an
119 // arbitrary 180 degree rotation to avoid invalid normalization.
120 quat.qx = 1.0f;
121 quat.qy = 0.0f;
122 quat.qz = 0.0f;
123 quat.qw = 0.0f;
124 } else {
125 quat.qx = vec.y;
126 quat.qy = -vec.x;
127 quat.qz = 0.0f;
128 vr_shell::NormalizeQuat(quat);
129 } 61 }
130 return quat; 62 base::WeakPtr<VrShellGl> GetVrShellGl() { return weak_vr_shell_gl_; }
131 } 63 VrShellGl* GetVrShellGlUnsafe() { return vr_shell_gl_.get(); }
132 64
133 std::unique_ptr<blink::WebMouseEvent> MakeMouseEvent(WebInputEvent::Type type, 65 protected:
134 double timestamp, 66 void Init() override {
135 float x, 67 vr_shell_gl_.reset(new VrShellGl(vr_shell_,
136 float y) { 68 std::move(weak_vr_shell_),
137 std::unique_ptr<blink::WebMouseEvent> mouse_event(new blink::WebMouseEvent); 69 std::move(content_input_manager_),
138 mouse_event->type = type; 70 std::move(ui_input_manager_),
139 mouse_event->pointerType = blink::WebPointerProperties::PointerType::Mouse; 71 std::move(main_thread_task_runner_),
140 mouse_event->x = x; 72 gvr_api_));
141 mouse_event->y = y; 73 weak_vr_shell_gl_ = vr_shell_gl_->GetWeakPtr();
142 mouse_event->windowX = x; 74 if (!vr_shell_gl_->Initialize()) {
143 mouse_event->windowY = y; 75 vr_shell_gl_.reset();
144 mouse_event->timeStampSeconds = timestamp; 76 }
145 mouse_event->clickCount = 1; 77 }
146 mouse_event->modifiers = 0; 78 void CleanUp() override {
79 vr_shell_gl_.reset();
80 }
147 81
148 return mouse_event; 82 private:
149 } 83 // Created on GL thread.
84 std::unique_ptr<VrShellGl> vr_shell_gl_;
85 base::WeakPtr<VrShellGl> weak_vr_shell_gl_;
86
87 // Created on main thread.
88 // TODO(mthiesse): Remove vr_shell_.
89 VrShell* vr_shell_;
90 base::WeakPtr<VrShell> weak_vr_shell_;
91 base::WeakPtr<VrInputManager> content_input_manager_;
92 base::WeakPtr<VrInputManager> ui_input_manager_;
93 scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
94 gvr_context* gvr_api_;
95 };
96
150 } // namespace 97 } // namespace
151 98
152 VrShell::VrShell(JNIEnv* env, 99 VrShell::VrShell(JNIEnv* env,
153 jobject obj, 100 jobject obj,
154 content::WebContents* main_contents, 101 content::WebContents* main_contents,
155 ui::WindowAndroid* content_window, 102 ui::WindowAndroid* content_window,
156 content::WebContents* ui_contents, 103 content::WebContents* ui_contents,
157 ui::WindowAndroid* ui_window, 104 ui::WindowAndroid* ui_window,
158 bool for_web_vr) 105 bool for_web_vr,
106 VrShellDelegate* delegate,
107 gvr_context* gvr_api)
159 : WebContentsObserver(ui_contents), 108 : WebContentsObserver(ui_contents),
160 main_contents_(main_contents), 109 main_contents_(main_contents),
161 ui_contents_(ui_contents), 110 ui_contents_(ui_contents),
111 delegate_(delegate),
162 metrics_helper_(new VrMetricsHelper(main_contents)), 112 metrics_helper_(new VrMetricsHelper(main_contents)),
163 main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()), 113 main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
164 weak_ptr_factory_(this) { 114 weak_ptr_factory_(this) {
165 DCHECK(g_instance == nullptr); 115 DCHECK(g_instance == nullptr);
166 g_instance = this; 116 g_instance = this;
167 j_vr_shell_.Reset(env, obj); 117 j_vr_shell_.Reset(env, obj);
168 scene_.reset(new UiScene); 118
119 content_input_manager_.reset(new VrInputManager(main_contents_));
120 ui_input_manager_.reset(new VrInputManager(ui_contents_));
121
122 gl_thread_.reset(new GLThread(this, weak_ptr_factory_.GetWeakPtr(),
123 content_input_manager_->GetWeakPtr(),
124 ui_input_manager_->GetWeakPtr(),
125 main_thread_task_runner_,
126 gvr_api));
127
128 base::Thread::Options options(base::MessageLoop::TYPE_DEFAULT, 0);
129 options.priority = base::ThreadPriority::DISPLAY;
130 gl_thread_->StartWithOptions(options);
169 131
170 if (for_web_vr) 132 if (for_web_vr)
171 metrics_helper_->SetWebVREnabled(true); 133 metrics_helper_->SetWebVREnabled(true);
172 html_interface_.reset(new UiInterface( 134 html_interface_.reset(new UiInterface(
173 for_web_vr ? UiInterface::Mode::WEB_VR : UiInterface::Mode::STANDARD, 135 for_web_vr ? UiInterface::Mode::WEB_VR : UiInterface::Mode::STANDARD,
174 main_contents_->IsFullscreen())); 136 main_contents_->IsFullscreen()));
175 content_compositor_.reset(new VrCompositor(content_window, false)); 137 content_compositor_.reset(new VrCompositor(content_window, false));
138 content_compositor_->SetLayer(main_contents_);
176 ui_compositor_.reset(new VrCompositor(ui_window, true)); 139 ui_compositor_.reset(new VrCompositor(ui_window, true));
140 ui_compositor_->SetLayer(ui_contents_);
177 vr_web_contents_observer_.reset(new VrWebContentsObserver( 141 vr_web_contents_observer_.reset(new VrWebContentsObserver(
178 main_contents, html_interface_.get(), this)); 142 main_contents, html_interface_.get(), this));
179 143
180 LoadUIContentOnUI(); 144 SetShowingOverscrollGlow(false);
181
182 gvr::Mat4f identity;
183 SetIdentityM(identity);
184 webvr_head_pose_.resize(kPoseRingBufferSize, identity);
185 webvr_head_pose_valid_.resize(kPoseRingBufferSize, false);
186
187 content_input_manager_.reset(new VrInputManager(main_contents_));
188 ui_input_manager_.reset(new VrInputManager(ui_contents_));
189 weak_content_input_manager_ = content_input_manager_->GetWeakPtr();
190 weak_ui_input_manager_ = ui_input_manager_->GetWeakPtr();
191
192 SetShowingOverscrollGlowOnUI(false);
193 } 145 }
194 146
195 void VrShell::UpdateCompositorLayersOnUI(JNIEnv* env, 147 void VrShell::Destroy(JNIEnv* env, const JavaParamRef<jobject>& obj) {
196 const JavaParamRef<jobject>& obj) {
197 content_compositor_->SetLayer(main_contents_);
198 ui_compositor_->SetLayer(ui_contents_);
199 }
200
201 void VrShell::DestroyOnUI(JNIEnv* env, const JavaParamRef<jobject>& obj) {
202 delete this; 148 delete this;
203 } 149 }
204 150
205 void VrShell::LoadUIContentOnUI() { 151 void VrShell::LoadUIContent(JNIEnv* env, const JavaParamRef<jobject>& obj) {
206 GURL url(kVrShellUIURL); 152 GURL url(kVrShellUIURL);
207 ui_contents_->GetController().LoadURL( 153 ui_contents_->GetController().LoadURL(
208 url, content::Referrer(), 154 url, content::Referrer(),
209 ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string("")); 155 ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string(""));
210 } 156 }
211 157
212 bool RegisterVrShell(JNIEnv* env) { 158 bool RegisterVrShell(JNIEnv* env) {
213 return RegisterNativesImpl(env); 159 return RegisterNativesImpl(env);
214 } 160 }
215 161
216 VrShell::~VrShell() { 162 VrShell::~VrShell() {
217 if (delegate_ && delegate_->GetDeviceProvider()) { 163 {
218 delegate_->GetDeviceProvider()->OnGvrDelegateRemoved(); 164 // The GvrLayout is, and must always be, used only on the UI thread, and the
165 // GvrApi used for rendering should only be used from the GL thread as it's
166 // not thread safe. However, the GvrLayout owns the GvrApi instance, and
167 // when it gets shut down it deletes the GvrApi instance with it. Therefore,
168 // we need to block shutting down the GvrLayout on stopping our GL thread
169 // from using the GvrApi instance.
170 // base::Thread::Stop, which is called when destroying the thread, asserts
171 // that IO is allowed to prevent jank, but there shouldn't be any concerns
172 // regarding jank in this case, because we're switching from 3D to 2D,
173 // adding/removing a bunch of Java views, and probably changing device
174 // orientation here.
175 base::ThreadRestrictions::ScopedAllowIO allow_io;
176 gl_thread_.reset();
219 } 177 }
178 delegate_->RemoveDelegate();
220 g_instance = nullptr; 179 g_instance = nullptr;
221 gl::init::ShutdownGL();
222 }
223
224 void VrShell::SetDelegateOnUI(JNIEnv* env,
225 const base::android::JavaParamRef<jobject>& obj,
226 const base::android::JavaParamRef<jobject>& delegate) {
227 base::AutoLock lock(gvr_init_lock_);
228 delegate_ = VrShellDelegate::GetNativeDelegate(env, delegate);
229 if (swap_chain_.get()) {
230 delegate_->GetDeviceProvider()->OnGvrDelegateReady(
231 weak_ptr_factory_.GetWeakPtr());
232 }
233 }
234
235 enum class ViewerType {
236 UNKNOWN_TYPE = 0,
237 CARDBOARD = 1,
238 DAYDREAM = 2,
239 VIEWER_TYPE_MAX,
240 };
241
242 void VrShell::GvrInitOnGL(JNIEnv* env,
243 const JavaParamRef<jobject>& obj,
244 jlong native_gvr_api) {
245 // set the initial webvr state
246 metrics_helper_->SetVRActive(true);
247
248 gvr_api_ =
249 gvr::GvrApi::WrapNonOwned(reinterpret_cast<gvr_context*>(native_gvr_api));
250 // TODO(klausw,crbug.com/655722): should report OnGvrDelegateReady here once
251 // we switch to using a WebVR render surface. We currently need to wait for
252 // the compositor window's size to be known first. See also
253 // ContentSurfaceChanged.
254 controller_.reset(
255 new VrController(reinterpret_cast<gvr_context*>(native_gvr_api)));
256
257
258 ViewerType viewerType;
259 switch (gvr_api_->GetViewerType()) {
260 case gvr::ViewerType::GVR_VIEWER_TYPE_DAYDREAM:
261 viewerType = ViewerType::DAYDREAM;
262 break;
263 case gvr::ViewerType::GVR_VIEWER_TYPE_CARDBOARD:
264 viewerType = ViewerType::CARDBOARD;
265 break;
266 default:
267 NOTREACHED();
268 viewerType = ViewerType::UNKNOWN_TYPE;
269 break;
270 }
271 UMA_HISTOGRAM_ENUMERATION("VRViewerType", static_cast<int>(viewerType),
272 static_cast<int>(ViewerType::VIEWER_TYPE_MAX));
273 }
274
275 void VrShell::InitializeGlOnGL(JNIEnv* env,
276 const JavaParamRef<jobject>& obj,
277 jint content_texture_handle,
278 jint ui_texture_handle) {
279 base::AutoLock lock(gvr_init_lock_);
280 CHECK(gl::GetGLImplementation() != gl::kGLImplementationNone ||
281 gl::init::InitializeGLOneOff());
282
283 content_texture_id_ = content_texture_handle;
284 ui_texture_id_ = ui_texture_handle;
285
286 // While WebVR is going through the compositor path, it shares
287 // the same texture ID. This will change once it gets its own
288 // surface, but store it separately to avoid future confusion.
289 // TODO(klausw,crbug.com/655722): remove this.
290 webvr_texture_id_ = content_texture_id_;
291 // Out of paranoia, explicitly reset the "pose valid" flags to false
292 // from the GL thread. The constructor ran in the UI thread.
293 // TODO(klausw,crbug.com/655722): remove this.
294 webvr_head_pose_valid_.assign(kPoseRingBufferSize, false);
295
296 gvr_api_->InitializeGl();
297 std::vector<gvr::BufferSpec> specs;
298 // For kFramePrimaryBuffer (primary VrShell and WebVR content)
299 specs.push_back(gvr_api_->CreateBufferSpec());
300 render_size_primary_ = specs[kFramePrimaryBuffer].GetSize();
301 render_size_primary_vrshell_ = render_size_primary_;
302
303 // For kFrameHeadlockedBuffer (for WebVR insecure content warning).
304 // Set this up at fixed resolution, the (smaller) FOV gets set below.
305 specs.push_back(gvr_api_->CreateBufferSpec());
306 specs.back().SetSize(kHeadlockedBufferDimensions);
307 render_size_headlocked_ = specs[kFrameHeadlockedBuffer].GetSize();
308
309 swap_chain_.reset(new gvr::SwapChain(gvr_api_->CreateSwapChain(specs)));
310
311 vr_shell_renderer_.reset(new VrShellRenderer());
312
313 // Allocate a buffer viewport for use in UI drawing. This isn't
314 // initialized at this point, it'll be set from other viewport list
315 // entries as needed.
316 buffer_viewport_.reset(
317 new gvr::BufferViewport(gvr_api_->CreateBufferViewport()));
318
319 // Set up main content viewports. The list has two elements, 0=left
320 // eye and 1=right eye.
321 buffer_viewport_list_.reset(
322 new gvr::BufferViewportList(gvr_api_->CreateEmptyBufferViewportList()));
323 buffer_viewport_list_->SetToRecommendedBufferViewports();
324
325 // Set up head-locked UI viewports, these will be elements 2=left eye
326 // and 3=right eye. For now, use a hardcoded 20-degree-from-center FOV
327 // frustum to reduce rendering cost for this overlay. This fits the
328 // current content, but will need to be adjusted once there's more dynamic
329 // head-locked content that could be larger.
330 headlocked_left_viewport_.reset(
331 new gvr::BufferViewport(gvr_api_->CreateBufferViewport()));
332 buffer_viewport_list_->GetBufferViewport(GVR_LEFT_EYE,
333 headlocked_left_viewport_.get());
334 headlocked_left_viewport_->SetSourceBufferIndex(kFrameHeadlockedBuffer);
335 headlocked_left_viewport_->SetReprojection(GVR_REPROJECTION_NONE);
336 headlocked_left_viewport_->SetSourceFov(kHeadlockedBufferFov);
337
338 headlocked_right_viewport_.reset(
339 new gvr::BufferViewport(gvr_api_->CreateBufferViewport()));
340 buffer_viewport_list_->GetBufferViewport(GVR_RIGHT_EYE,
341 headlocked_right_viewport_.get());
342 headlocked_right_viewport_->SetSourceBufferIndex(kFrameHeadlockedBuffer);
343 headlocked_right_viewport_->SetReprojection(GVR_REPROJECTION_NONE);
344 headlocked_right_viewport_->SetSourceFov(kHeadlockedBufferFov);
345
346 // Save copies of the first two viewport items for use by WebVR, it
347 // sets its own UV bounds.
348 webvr_left_viewport_.reset(
349 new gvr::BufferViewport(gvr_api_->CreateBufferViewport()));
350 buffer_viewport_list_->GetBufferViewport(GVR_LEFT_EYE,
351 webvr_left_viewport_.get());
352 webvr_left_viewport_->SetSourceBufferIndex(kFramePrimaryBuffer);
353
354 webvr_right_viewport_.reset(
355 new gvr::BufferViewport(gvr_api_->CreateBufferViewport()));
356 buffer_viewport_list_->GetBufferViewport(GVR_RIGHT_EYE,
357 webvr_right_viewport_.get());
358 webvr_right_viewport_->SetSourceBufferIndex(kFramePrimaryBuffer);
359
360 if (delegate_) {
361 main_thread_task_runner_->PostTask(
362 FROM_HERE, base::Bind(&device::GvrDeviceProvider::OnGvrDelegateReady,
363 delegate_->GetDeviceProvider(),
364 weak_ptr_factory_.GetWeakPtr()));
365 }
366 }
367
368 void VrShell::UpdateControllerOnGL(const gvr::Vec3f& forward_vector) {
369 controller_->UpdateState();
370
371 #if defined(ENABLE_VR_SHELL)
372 // Note that button up/down state is transient, so ButtonUpHappened only
373 // returns
374 // true for a single frame (and we're guaranteed not to miss it).
375 if (controller_->ButtonUpHappened(
376 gvr::ControllerButton::GVR_CONTROLLER_BUTTON_APP)) {
377 html_interface_->SetMenuMode(!html_interface_->GetMenuMode());
378
379 // TODO(mthiesse): The page is no longer visible when in menu mode. We
380 // should unfocus or otherwise let it know it's hidden.
381 if (html_interface_->GetMode() == UiInterface::Mode::WEB_VR) {
382 const auto&& task = html_interface_->GetMenuMode() ?
383 &device::GvrDeviceProvider::OnDisplayBlur :
384 &device::GvrDeviceProvider::OnDisplayFocus;
385 main_thread_task_runner_->PostTask(
386 FROM_HERE, base::Bind(task, delegate_->GetDeviceProvider()));
387 }
388 }
389 #endif
390 if (html_interface_->GetMode() == UiInterface::Mode::WEB_VR) {
391 // Process screen touch events for Cardboard button compatibility.
392 // Also send tap events for controller "touchpad click" events.
393 if (touch_pending_ ||
394 controller_->ButtonUpHappened(
395 gvr::ControllerButton::GVR_CONTROLLER_BUTTON_CLICK)) {
396 touch_pending_ = false;
397 std::unique_ptr<WebGestureEvent> gesture(new WebGestureEvent());
398 gesture->sourceDevice = blink::WebGestureDeviceTouchpad;
399 gesture->timeStampSeconds =
400 (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF();
401 gesture->type = WebInputEvent::GestureTapDown;
402 gesture->x = 0;
403 gesture->y = 0;
404 SendGestureOnGL(CONTENT, std::move(gesture));
405 }
406
407 return;
408 }
409
410 gvr::Vec3f ergo_neutral_pose;
411 if (!controller_->IsConnected()) {
412 // No controller detected, set up a gaze cursor that tracks the
413 // forward direction.
414 ergo_neutral_pose = {0.0f, 0.0f, -1.0f};
415 controller_quat_ = GetRotationFromZAxis(forward_vector);
416 } else {
417 ergo_neutral_pose = {0.0f, -sin(kErgoAngleOffset), -cos(kErgoAngleOffset)};
418 controller_quat_ = controller_->Orientation();
419 }
420
421 gvr::Mat4f mat = QuatToMatrix(controller_quat_);
422 gvr::Vec3f forward = MatrixVectorMul(mat, ergo_neutral_pose);
423 gvr::Vec3f origin = kHandPosition;
424
425 // If we place the reticle based on elements intersecting the controller beam,
426 // we can end up with the reticle hiding behind elements, or jumping laterally
427 // in the field of view. This is physically correct, but hard to use. For
428 // usability, do the following instead:
429 //
430 // - Project the controller laser onto an outer surface, which is the
431 // closer of the desktop plane, or a distance-limiting sphere.
432 // - Create a vector between the eyes and the outer surface point.
433 // - If any UI elements intersect this vector, choose the closest to the eyes,
434 // and place the reticle at the intersection point.
435
436 // Find distance to a corner of the content quad, and limit the cursor
437 // distance to a multiple of that distance. This lets us keep the reticle on
438 // the content plane near the content window, and on the surface of a sphere
439 // in other directions. Note that this approach uses distance from controller,
440 // rather than eye, for simplicity. This will make the sphere slightly
441 // off-center.
442 float distance = kDefaultReticleDistance;
443 ContentRectangle* content_plane = scene_->GetContentQuad();
444 if (content_plane) {
445 distance = content_plane->GetRayDistance(origin, forward);
446 gvr::Vec3f corner = {0.5f, 0.5f, 0.0f};
447 corner = MatrixVectorMul(content_plane->transform.to_world, corner);
448 float max_distance = Distance(origin, corner) * kReticleDistanceMultiplier;
449 if (distance > max_distance || distance <= 0.0f) {
450 distance = max_distance;
451 }
452 }
453
454 target_point_ = GetRayPoint(origin, forward, distance);
455 gvr::Vec3f eye_to_target = target_point_;
456 NormalizeVector(eye_to_target);
457
458 // Determine which UI element (if any) intersects the line between the eyes
459 // and the controller target position.
460 float closest_element_distance = std::numeric_limits<float>::infinity();
461 int pixel_x = 0;
462 int pixel_y = 0;
463 target_element_ = nullptr;
464 InputTarget input_target = NONE;
465
466 for (const auto& plane : scene_->GetUiElements()) {
467 if (!plane->visible || !plane->hit_testable) {
468 continue;
469 }
470 float distance_to_plane = plane->GetRayDistance(kOrigin, eye_to_target);
471 gvr::Vec3f plane_intersection_point =
472 GetRayPoint(kOrigin, eye_to_target, distance_to_plane);
473
474 gvr::Vec3f rect_2d_point =
475 MatrixVectorMul(plane->transform.from_world, plane_intersection_point);
476 if (distance_to_plane > 0 && distance_to_plane < closest_element_distance) {
477 float x = rect_2d_point.x + 0.5f;
478 float y = 0.5f - rect_2d_point.y;
479 bool is_inside = x >= 0.0f && x < 1.0f && y >= 0.0f && y < 1.0f;
480 if (!is_inside)
481 continue;
482
483 closest_element_distance = distance_to_plane;
484 Rectf pixel_rect;
485 if (plane->content_quad) {
486 pixel_rect = {0, 0, content_tex_css_width_, content_tex_css_height_};
487 } else {
488 pixel_rect = {plane->copy_rect.x, plane->copy_rect.y,
489 plane->copy_rect.width, plane->copy_rect.height};
490 }
491 pixel_x = pixel_rect.width * x + pixel_rect.x;
492 pixel_y = pixel_rect.height * y + pixel_rect.y;
493
494 target_point_ = plane_intersection_point;
495 target_element_ = plane.get();
496 input_target = plane->content_quad ? CONTENT : UI;
497 }
498 }
499 SendEventsToTargetOnGL(input_target, pixel_x, pixel_y);
500 }
501
502 void VrShell::SendEventsToTargetOnGL(InputTarget input_target,
503 int pixel_x,
504 int pixel_y) {
505 std::vector<std::unique_ptr<WebGestureEvent>> gesture_list =
506 controller_->DetectGestures();
507 double timestamp = gesture_list.front()->timeStampSeconds;
508
509 if (touch_pending_) {
510 touch_pending_ = false;
511 std::unique_ptr<WebGestureEvent> event(new WebGestureEvent());
512 event->type = WebInputEvent::GestureTapDown;
513 event->sourceDevice = blink::WebGestureDeviceTouchpad;
514 event->timeStampSeconds = timestamp;
515 event->x = pixel_x;
516 event->y = pixel_y;
517 gesture_list.push_back(std::move(event));
518 }
519
520 for (const auto& gesture : gesture_list) {
521 switch (gesture->type) {
522 case WebInputEvent::GestureScrollBegin:
523 case WebInputEvent::GestureScrollUpdate:
524 case WebInputEvent::GestureScrollEnd:
525 case WebInputEvent::GestureFlingCancel:
526 case WebInputEvent::GestureFlingStart:
527 SendGestureOnGL(CONTENT,
528 base::WrapUnique(new WebGestureEvent(*gesture)));
529 break;
530 case WebInputEvent::GestureTapDown:
531 gesture->x = pixel_x;
532 gesture->y = pixel_y;
533 if (input_target != NONE)
534 SendGestureOnGL(input_target,
535 base::WrapUnique(new WebGestureEvent(*gesture)));
536 break;
537 case WebInputEvent::Undefined:
538 break;
539 default:
540 NOTREACHED();
541 }
542 }
543
544 // Hover support
545 bool new_target = input_target != current_input_target_;
546 if (new_target && current_input_target_ != NONE) {
547 // Send a move event indicating that the pointer moved off of an element.
548 SendGestureOnGL(current_input_target_,
549 MakeMouseEvent(WebInputEvent::MouseLeave, timestamp, 0, 0));
550 }
551
552 current_input_target_ = input_target;
553 if (current_input_target_ != NONE) {
554 WebInputEvent::Type type =
555 new_target ? WebInputEvent::MouseEnter : WebInputEvent::MouseMove;
556 SendGestureOnGL(input_target,
557 MakeMouseEvent(type, timestamp, pixel_x, pixel_y));
558 }
559 }
560
561 void VrShell::SendGestureOnGL(InputTarget input_target,
562 std::unique_ptr<blink::WebInputEvent> event) {
563 DCHECK(input_target != NONE);
564 const base::WeakPtr<VrInputManager>& weak_ptr =
565 input_target == CONTENT ? weak_content_input_manager_
566 : weak_ui_input_manager_;
567 main_thread_task_runner_->PostTask(
568 FROM_HERE,
569 base::Bind(&VrInputManager::ProcessUpdatedGesture, weak_ptr,
570 base::Passed(std::move(event))));
571 } 180 }
572 181
573 void VrShell::SetGvrPoseForWebVr(const gvr::Mat4f& pose, uint32_t pose_num) { 182 void VrShell::SetGvrPoseForWebVr(const gvr::Mat4f& pose, uint32_t pose_num) {
574 webvr_head_pose_[pose_num % kPoseRingBufferSize] = pose; 183 GLThread* thread = static_cast<GLThread*>(gl_thread_.get());
575 webvr_head_pose_valid_[pose_num % kPoseRingBufferSize] = true; 184 if (thread->GetVrShellGlUnsafe()) {
576 } 185 thread->GetVrShellGlUnsafe()->SetGvrPoseForWebVr(pose, pose_num);
577
578 int GetPixelEncodedPoseIndexByte() {
579 TRACE_EVENT0("gpu", "VrShell::GetPixelEncodedPoseIndex");
580 // Read the pose index encoded in a bottom left pixel as color values.
581 // See also third_party/WebKit/Source/modules/vr/VRDisplay.cpp which
582 // encodes the pose index, and device/vr/android/gvr/gvr_device.cc
583 // which tracks poses. Returns the low byte (0..255) if valid, or -1
584 // if not valid due to bad magic number.
585 uint8_t pixels[4];
586 // Assume we're reading from the framebuffer we just wrote to.
587 // That's true currently, we may need to use glReadBuffer(GL_BACK)
588 // or equivalent if the rendering setup changes in the future.
589 glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
590
591 // Check for the magic number written by VRDevice.cpp on submit.
592 // This helps avoid glitches from garbage data in the render
593 // buffer that can appear during initialization or resizing. These
594 // often appear as flashes of all-black or all-white pixels.
595 if (pixels[1] == kWebVrPosePixelMagicNumbers[0] &&
596 pixels[2] == kWebVrPosePixelMagicNumbers[1]) {
597 // Pose is good.
598 return pixels[0];
599 }
600 VLOG(1) << "WebVR: reject decoded pose index " << (int)pixels[0] <<
601 ", bad magic number " << (int)pixels[1] << ", " << (int)pixels[2];
602 return -1;
603 }
604
605 bool VrShell::WebVrPoseByteIsValidOnGL(int pose_index_byte) {
606 if (pose_index_byte < 0) {
607 return false;
608 }
609 if (!webvr_head_pose_valid_[pose_index_byte % kPoseRingBufferSize]) {
610 VLOG(1) << "WebVR: reject decoded pose index " << pose_index_byte <<
611 ", not a valid pose";
612 return false;
613 }
614 return true;
615 }
616
617 void VrShell::DrawFrameOnGL(JNIEnv* env, const JavaParamRef<jobject>& obj) {
618 TRACE_EVENT0("gpu", "VrShell::DrawFrame");
619 // Reset the viewport list to just the pair of viewports for the
620 // primary buffer each frame. Head-locked viewports get added by
621 // DrawVrShell if needed.
622 buffer_viewport_list_->SetToRecommendedBufferViewports();
623
624 if (html_interface_->GetMode() == UiInterface::Mode::WEB_VR) {
625 // If needed, resize the primary buffer for use with WebVR.
626 if (render_size_primary_ != render_size_primary_webvr_) {
627 if (!render_size_primary_webvr_.width) {
628 VLOG(2) << "WebVR rendering size not known yet, dropping frame";
629 return;
630 }
631 render_size_primary_ = render_size_primary_webvr_;
632 swap_chain_->ResizeBuffer(kFramePrimaryBuffer, render_size_primary_);
633 }
634 } else {
635 if (render_size_primary_ != render_size_primary_vrshell_) {
636 render_size_primary_ = render_size_primary_vrshell_;
637 swap_chain_->ResizeBuffer(kFramePrimaryBuffer, render_size_primary_);
638 }
639 }
640
641 gvr::Frame frame = swap_chain_->AcquireFrame();
642 gvr::ClockTimePoint target_time = gvr::GvrApi::GetTimePointNow();
643 target_time.monotonic_system_time_nanos += kPredictionTimeWithoutVsyncNanos;
644
645 gvr::Mat4f head_pose =
646 gvr_api_->GetHeadSpaceFromStartSpaceRotation(target_time);
647
648 gvr::Vec3f position = GetTranslation(head_pose);
649 if (position.x == 0.0f && position.y == 0.0f && position.z == 0.0f) {
650 // This appears to be a 3DOF pose without a neck model. Add one.
651 // The head pose has redundant data. Assume we're only using the
652 // object_from_reference_matrix, we're not updating position_external.
653 // TODO: Not sure what object_from_reference_matrix is. The new api removed
654 // it. For now, removing it seems working fine.
655 gvr_api_->ApplyNeckModel(head_pose, 1.0f);
656 }
657
658 // Bind the primary framebuffer.
659 frame.BindBuffer(kFramePrimaryBuffer);
660
661 HandleQueuedTasksOnGL();
662
663 // Update the render position of all UI elements (including desktop).
664 const float screen_tilt = kDesktopScreenTiltDefault * M_PI / 180.0f;
665 scene_->UpdateTransforms(screen_tilt, UiScene::TimeInMicroseconds());
666
667 UpdateControllerOnGL(GetForwardVector(head_pose));
668
669 if (html_interface_->GetMode() == UiInterface::Mode::WEB_VR) {
670 DrawWebVrOnGL();
671
672 // When using async reprojection, we need to know which pose was used in
673 // the WebVR app for drawing this frame. Due to unknown amounts of
674 // buffering in the compositor and SurfaceTexture, we read the pose number
675 // from a corner pixel. There's no point in doing this for legacy
676 // distortion rendering since that doesn't need a pose, and reading back
677 // pixels is an expensive operation. TODO(klausw,crbug.com/655722): stop
678 // doing this once we have working no-compositor rendering for WebVR.
679 if (gvr_api_->GetAsyncReprojectionEnabled()) {
680 int pose_index_byte = GetPixelEncodedPoseIndexByte();
681 if (WebVrPoseByteIsValidOnGL(pose_index_byte)) {
682 // We have a valid pose, use it for reprojection.
683 webvr_left_viewport_->SetReprojection(GVR_REPROJECTION_FULL);
684 webvr_right_viewport_->SetReprojection(GVR_REPROJECTION_FULL);
685 head_pose = webvr_head_pose_[pose_index_byte % kPoseRingBufferSize];
686 // We can't mark the used pose as invalid since unfortunately
687 // we have to reuse them. The compositor will re-submit stale
688 // frames on vsync, and we can't tell that this has happened
689 // until we've read the pose index from it, and at that point
690 // it's too late to skip rendering.
691 } else {
692 // If we don't get a valid frame ID back we shouldn't attempt
693 // to reproject by an invalid matrix, so turn off reprojection
694 // instead. Invalid poses can permanently break reprojection
695 // for this GVR instance: http://crbug.com/667327
696 webvr_left_viewport_->SetReprojection(GVR_REPROJECTION_NONE);
697 webvr_right_viewport_->SetReprojection(GVR_REPROJECTION_NONE);
698 }
699 }
700 }
701
702 DrawVrShellOnGL(head_pose, frame);
703
704 frame.Unbind();
705 frame.Submit(*buffer_viewport_list_, head_pose);
706 }
707
708 void VrShell::DrawVrShellOnGL(const gvr::Mat4f& head_pose,
709 gvr::Frame &frame) {
710 TRACE_EVENT0("gpu", "VrShell::DrawVrShell");
711 std::vector<const ContentRectangle*> head_locked_elements;
712 std::vector<const ContentRectangle*> world_elements;
713 for (const auto& rect : scene_->GetUiElements()) {
714 if (!rect->visible) {
715 continue;
716 }
717 if (rect->lock_to_fov) {
718 head_locked_elements.push_back(rect.get());
719 } else {
720 world_elements.push_back(rect.get());
721 }
722 }
723
724 if (html_interface_->GetMode() == UiInterface::Mode::WEB_VR) {
725 // WebVR is incompatible with 3D world compositing since the
726 // depth buffer was already populated with unknown scaling - the
727 // WebVR app has full control over zNear/zFar. Just leave the
728 // existing content in place in the primary buffer without
729 // clearing. Currently, there aren't any world elements in WebVR
730 // mode, this will need further testing if those get added
731 // later.
732 } else {
733 // Non-WebVR mode, enable depth testing and clear the primary buffers.
734 glEnable(GL_CULL_FACE);
735 glEnable(GL_DEPTH_TEST);
736 glDepthMask(GL_TRUE);
737
738 glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
739 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
740 }
741
742 if (!world_elements.empty()) {
743 DrawUiViewOnGL(&head_pose, world_elements, render_size_primary_,
744 kViewportListPrimaryOffset);
745 }
746
747 if (!head_locked_elements.empty()) {
748 // Add head-locked viewports. The list gets reset to just
749 // the recommended viewports (for the primary buffer) each frame.
750 buffer_viewport_list_->SetBufferViewport(
751 kViewportListHeadlockedOffset + GVR_LEFT_EYE,
752 *headlocked_left_viewport_);
753 buffer_viewport_list_->SetBufferViewport(
754 kViewportListHeadlockedOffset + GVR_RIGHT_EYE,
755 *headlocked_right_viewport_);
756
757 // Bind the headlocked framebuffer.
758 frame.BindBuffer(kFrameHeadlockedBuffer);
759 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
760 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
761 DrawUiViewOnGL(nullptr, head_locked_elements, render_size_headlocked_,
762 kViewportListHeadlockedOffset);
763 } 186 }
764 } 187 }
765 188
766 void VrShell::SetWebVRRenderSurfaceSize(int width, int height) { 189 void VrShell::SetWebVRRenderSurfaceSize(int width, int height) {
767 render_size_primary_webvr_.width = width; 190 GLThread* thread = static_cast<GLThread*>(gl_thread_.get());
768 render_size_primary_webvr_.height = height; 191 if (thread->GetVrShellGlUnsafe()) {
769 // TODO(klausw,crbug.com/655722): set the WebVR render surface size here once 192 thread->GetVrShellGlUnsafe()->SetWebVRRenderSurfaceSize(width, height);
770 // we have that. 193 }
771 } 194 }
772 195
773 gvr::Sizei VrShell::GetWebVRCompositorSurfaceSize() { 196 gvr::Sizei VrShell::GetWebVRCompositorSurfaceSize() {
774 // This is a stopgap while we're using the WebVR compositor rendering path. 197 GLThread* thread = static_cast<GLThread*>(gl_thread_.get());
775 // TODO(klausw,crbug.com/655722): Remove this method and member once we're 198 if (thread->GetVrShellGlUnsafe()) {
776 // using a separate WebVR render surface. 199 return thread->GetVrShellGlUnsafe()->GetWebVRCompositorSurfaceSize();
777 return content_tex_physical_size_; 200 }
201 return gvr::Sizei();
778 } 202 }
779 203
780 204 void VrShell::OnTriggerEvent(JNIEnv* env,
781 void VrShell::DrawUiViewOnGL(const gvr::Mat4f* head_pose, 205 const JavaParamRef<jobject>& obj) {
782 const std::vector<const ContentRectangle*>& elements, 206 GLThread* thread = static_cast<GLThread*>(gl_thread_.get());
783 const gvr::Sizei& render_size, int viewport_offset) { 207 thread->task_runner()->PostTask(FROM_HERE,
784 TRACE_EVENT0("gpu", "VrShell::DrawUiView"); 208 base::Bind(&VrShellGl::OnTriggerEvent,
785 for (auto eye : {GVR_LEFT_EYE, GVR_RIGHT_EYE}) { 209 thread->GetVrShellGl()));
786 buffer_viewport_list_->GetBufferViewport(
787 eye + viewport_offset, buffer_viewport_.get());
788
789 gvr::Mat4f view_matrix = gvr_api_->GetEyeFromHeadMatrix(eye);
790 if (head_pose != nullptr) {
791 view_matrix = MatrixMul(view_matrix, *head_pose);
792 }
793
794 gvr::Recti pixel_rect =
795 CalculatePixelSpaceRect(render_size, buffer_viewport_->GetSourceUv());
796 glViewport(pixel_rect.left, pixel_rect.bottom,
797 pixel_rect.right - pixel_rect.left,
798 pixel_rect.top - pixel_rect.bottom);
799
800 const gvr::Mat4f render_matrix = MatrixMul(
801 PerspectiveMatrixFromView(
802 buffer_viewport_->GetSourceFov(), kZNear, kZFar),
803 view_matrix);
804
805 DrawElementsOnGL(render_matrix, elements);
806 if (head_pose != nullptr &&
807 html_interface_->GetMode() != UiInterface::Mode::WEB_VR) {
808 DrawCursorOnGL(render_matrix);
809 }
810 }
811 } 210 }
812 211
813 void VrShell::DrawElementsOnGL( 212 void VrShell::OnPause(JNIEnv* env, const JavaParamRef<jobject>& obj) {
814 const gvr::Mat4f& render_matrix, 213 GLThread* thread = static_cast<GLThread*>(gl_thread_.get());
815 const std::vector<const ContentRectangle*>& elements) { 214 thread->task_runner()->PostTask(
816 for (const auto& rect : elements) { 215 FROM_HERE, base::Bind(&VrShellGl::OnPause, thread->GetVrShellGl()));
817 Rectf copy_rect;
818 jint texture_handle;
819 if (rect->content_quad) {
820 copy_rect = {0, 0, 1, 1};
821 texture_handle = content_texture_id_;
822 } else {
823 copy_rect.x = static_cast<float>(rect->copy_rect.x) / ui_tex_css_width_;
824 copy_rect.y = static_cast<float>(rect->copy_rect.y) / ui_tex_css_height_;
825 copy_rect.width = static_cast<float>(rect->copy_rect.width) /
826 ui_tex_css_width_;
827 copy_rect.height = static_cast<float>(rect->copy_rect.height) /
828 ui_tex_css_height_;
829 texture_handle = ui_texture_id_;
830 }
831 gvr::Mat4f transform = MatrixMul(render_matrix, rect->transform.to_world);
832 vr_shell_renderer_->GetTexturedQuadRenderer()->Draw(
833 texture_handle, transform, copy_rect);
834 }
835 }
836
837 void VrShell::DrawCursorOnGL(const gvr::Mat4f& render_matrix) {
838 gvr::Mat4f mat;
839 SetIdentityM(mat);
840
841 // Draw the reticle.
842
843 // Scale the pointer to have a fixed FOV size at any distance.
844 const float eye_to_target = Distance(target_point_, kOrigin);
845 ScaleM(mat, mat, kReticleWidth * eye_to_target,
846 kReticleHeight * eye_to_target, 1.0f);
847
848 gvr::Quatf rotation;
849 if (target_element_ != nullptr) {
850 // Make the reticle planar to the element it's hitting.
851 rotation = GetRotationFromZAxis(target_element_->GetNormal());
852 } else {
853 // Rotate the cursor to directly face the eyes.
854 rotation = GetRotationFromZAxis(target_point_);
855 }
856 mat = MatrixMul(QuatToMatrix(rotation), mat);
857
858 // Place the pointer slightly in front of the plane intersection point.
859 TranslateM(mat, mat, target_point_.x * kReticleOffset,
860 target_point_.y * kReticleOffset,
861 target_point_.z * kReticleOffset);
862
863 gvr::Mat4f transform = MatrixMul(render_matrix, mat);
864 vr_shell_renderer_->GetReticleRenderer()->Draw(transform);
865
866 // Draw the laser.
867
868 // Find the length of the beam (from hand to target).
869 const float laser_length = Distance(kHandPosition, target_point_);
870
871 // Build a beam, originating from the origin.
872 SetIdentityM(mat);
873
874 // Move the beam half its height so that its end sits on the origin.
875 TranslateM(mat, mat, 0.0f, 0.5f, 0.0f);
876 ScaleM(mat, mat, kLaserWidth, laser_length, 1);
877
878 // Tip back 90 degrees to flat, pointing at the scene.
879 const gvr::Quatf q = QuatFromAxisAngle({1.0f, 0.0f, 0.0f}, -M_PI / 2);
880 mat = MatrixMul(QuatToMatrix(q), mat);
881
882 const gvr::Vec3f beam_direction = {
883 target_point_.x - kHandPosition.x,
884 target_point_.y - kHandPosition.y,
885 target_point_.z - kHandPosition.z
886 };
887 const gvr::Mat4f beam_direction_mat =
888 QuatToMatrix(GetRotationFromZAxis(beam_direction));
889
890 // Render multiple faces to make the laser appear cylindrical.
891 const int faces = 4;
892 for (int i = 0; i < faces; i++) {
893 // Rotate around Z.
894 const float angle = M_PI * 2 * i / faces;
895 const gvr::Quatf rot = QuatFromAxisAngle({0.0f, 0.0f, 1.0f}, angle);
896 gvr::Mat4f face_transform = MatrixMul(QuatToMatrix(rot), mat);
897
898 // Orient according to target direction.
899 face_transform = MatrixMul(beam_direction_mat, face_transform);
900
901 // Move the beam origin to the hand.
902 TranslateM(face_transform, face_transform, kHandPosition.x, kHandPosition.y,
903 kHandPosition.z);
904
905 transform = MatrixMul(render_matrix, face_transform);
906 vr_shell_renderer_->GetLaserRenderer()->Draw(transform);
907 }
908 }
909
910 void VrShell::DrawWebVrOnGL() {
911 TRACE_EVENT0("gpu", "VrShell::DrawWebVr");
912 // Don't need face culling, depth testing, blending, etc. Turn it all off.
913 glDisable(GL_CULL_FACE);
914 glDepthMask(GL_FALSE);
915 glDisable(GL_DEPTH_TEST);
916 glDisable(GL_SCISSOR_TEST);
917 glDisable(GL_BLEND);
918 glDisable(GL_POLYGON_OFFSET_FILL);
919
920 glViewport(0, 0, render_size_primary_.width, render_size_primary_.height);
921 vr_shell_renderer_->GetWebVrRenderer()->Draw(webvr_texture_id_);
922
923 buffer_viewport_list_->SetBufferViewport(GVR_LEFT_EYE,
924 *webvr_left_viewport_);
925 buffer_viewport_list_->SetBufferViewport(GVR_RIGHT_EYE,
926 *webvr_right_viewport_);
927 }
928
929 void VrShell::OnTriggerEventOnUI(JNIEnv* env,
930 const JavaParamRef<jobject>& obj) {
931 // Set a flag to handle this on the render thread at the next frame.
932 touch_pending_ = true;
933 }
934
935 void VrShell::OnPauseOnUI(JNIEnv* env, const JavaParamRef<jobject>& obj) {
936 if (gvr_api_ == nullptr)
937 return;
938
939 // TODO(mthiesse): Clean up threading here.
940 controller_->OnPause();
941 gvr_api_->PauseTracking();
942 SetShowingOverscrollGlowOnUI(true);
943 216
944 // exit vr session 217 // exit vr session
945 metrics_helper_->SetVRActive(false); 218 metrics_helper_->SetVRActive(false);
219 SetShowingOverscrollGlow(true);
946 } 220 }
947 221
948 void VrShell::OnResumeOnUI(JNIEnv* env, const JavaParamRef<jobject>& obj) { 222 void VrShell::OnResume(JNIEnv* env, const JavaParamRef<jobject>& obj) {
949 if (gvr_api_ == nullptr) 223 GLThread* thread = static_cast<GLThread*>(gl_thread_.get());
950 return; 224 thread->task_runner()->PostTask(
951 225 FROM_HERE, base::Bind(&VrShellGl::OnResume, thread->GetVrShellGl()));
952 // TODO(mthiesse): Clean up threading here.
953 gvr_api_->RefreshViewerProfile();
954 gvr_api_->ResumeTracking();
955 controller_->OnResume();
956 SetShowingOverscrollGlowOnUI(false);
957 226
958 // exit vr session 227 // exit vr session
959 metrics_helper_->SetVRActive(true); 228 metrics_helper_->SetVRActive(true);
229 SetShowingOverscrollGlow(false);
960 } 230 }
961 231
962 void VrShell::SetShowingOverscrollGlowOnUI(bool showing_glow) { 232 void VrShell::SetShowingOverscrollGlow(bool showing_glow) {
963 main_contents_->GetRenderWidgetHostView()->SetShowingOverscrollGlow( 233 main_contents_->GetRenderWidgetHostView()->SetShowingOverscrollGlow(
964 showing_glow); 234 showing_glow);
965 } 235 }
966 236
967 base::WeakPtr<VrShell> VrShell::GetWeakPtrOnUI( 237 base::WeakPtr<VrShell> VrShell::GetWeakPtr(
968 const content::WebContents* web_contents) { 238 const content::WebContents* web_contents) {
969 // Ensure that the WebContents requesting the VrShell instance is the one 239 // Ensure that the WebContents requesting the VrShell instance is the one
970 // we created. 240 // we created.
971 if (g_instance != nullptr && g_instance->ui_contents_ == web_contents) 241 if (g_instance != nullptr && g_instance->ui_contents_ == web_contents)
972 return g_instance->weak_ptr_factory_.GetWeakPtr(); 242 return g_instance->weak_ptr_factory_.GetWeakPtr();
973 return base::WeakPtr<VrShell>(nullptr); 243 return base::WeakPtr<VrShell>(nullptr);
974 } 244 }
975 245
976 void VrShell::OnDomContentsLoadedOnUI() { 246 void VrShell::OnDomContentsLoaded() {
977 html_interface_->SetURL(main_contents_->GetVisibleURL()); 247 html_interface_->SetURL(main_contents_->GetVisibleURL());
978 html_interface_->SetLoading(main_contents_->IsLoading()); 248 html_interface_->SetLoading(main_contents_->IsLoading());
979 html_interface_->OnDomContentsLoaded(); 249 html_interface_->OnDomContentsLoaded();
980 } 250 }
981 251
982 void VrShell::SetWebVrModeOnUI(JNIEnv* env, 252 void VrShell::SetWebVrMode(JNIEnv* env,
983 const base::android::JavaParamRef<jobject>& obj, 253 const base::android::JavaParamRef<jobject>& obj,
984 bool enabled) { 254 bool enabled) {
985 metrics_helper_->SetWebVREnabled(enabled); 255 metrics_helper_->SetWebVREnabled(enabled);
986 if (enabled) { 256 if (enabled) {
987 html_interface_->SetMode(UiInterface::Mode::WEB_VR); 257 html_interface_->SetMode(UiInterface::Mode::WEB_VR);
988 } else { 258 } else {
989 html_interface_->SetMode(UiInterface::Mode::STANDARD); 259 html_interface_->SetMode(UiInterface::Mode::STANDARD);
990 } 260 }
991 } 261 }
992 262
993 void VrShell::SetWebVRSecureOrigin(bool secure_origin) { 263 void VrShell::SetWebVRSecureOrigin(bool secure_origin) {
994 // TODO(cjgrant): Align this state with the logic that drives the omnibox. 264 // TODO(cjgrant): Align this state with the logic that drives the omnibox.
995 html_interface_->SetWebVRSecureOrigin(secure_origin); 265 html_interface_->SetWebVRSecureOrigin(secure_origin);
996 } 266 }
997 267
998 void VrShell::SubmitWebVRFrame() {} 268 void VrShell::SubmitWebVRFrame() {}
999 269
1000 void VrShell::UpdateWebVRTextureBounds(const gvr::Rectf& left_bounds, 270 void VrShell::UpdateWebVRTextureBounds(const gvr::Rectf& left_bounds,
1001 const gvr::Rectf& right_bounds) { 271 const gvr::Rectf& right_bounds) {
1002 webvr_left_viewport_->SetSourceUv(left_bounds); 272 GLThread* thread = static_cast<GLThread*>(gl_thread_.get());
1003 webvr_right_viewport_->SetSourceUv(right_bounds); 273 if (thread->GetVrShellGlUnsafe()) {
274 thread->GetVrShellGlUnsafe()->UpdateWebVRTextureBounds(left_bounds,
275 right_bounds);
276 }
1004 } 277 }
1005 278
279 // TODO(mthiesse): Do not expose GVR API outside of GL thread.
280 // It's not thread-safe.
1006 gvr::GvrApi* VrShell::gvr_api() { 281 gvr::GvrApi* VrShell::gvr_api() {
1007 return gvr_api_.get(); 282 GLThread* thread = static_cast<GLThread*>(gl_thread_.get());
283 if (thread->GetVrShellGlUnsafe()) {
284 return thread->GetVrShellGlUnsafe()->gvr_api();
285 }
286 CHECK(false);
287 return nullptr;
1008 } 288 }
1009 289
1010 void VrShell::SurfacesChangedOnUI(JNIEnv* env, 290 void VrShell::SurfacesChanged(jobject content_surface, jobject ui_surface) {
1011 const JavaParamRef<jobject>& object,
1012 const JavaParamRef<jobject>& content_surface,
1013 const JavaParamRef<jobject>& ui_surface) {
1014 content_compositor_->SurfaceChanged(content_surface); 291 content_compositor_->SurfaceChanged(content_surface);
1015 ui_compositor_->SurfaceChanged(ui_surface); 292 ui_compositor_->SurfaceChanged(ui_surface);
1016 } 293 }
1017 294
1018 void VrShell::ContentBoundsChangedOnUI(JNIEnv* env, 295 void VrShell::GvrDelegateReady() {
296 delegate_->SetDelegate(weak_ptr_factory_.GetWeakPtr());
297 }
298
299 void VrShell::ContentBoundsChanged(JNIEnv* env,
1019 const JavaParamRef<jobject>& object, 300 const JavaParamRef<jobject>& object,
1020 jint width, jint height, jfloat dpr) { 301 jint width, jint height, jfloat dpr) {
1021 TRACE_EVENT0("gpu", "VrShell::ContentBoundsChanged"); 302 TRACE_EVENT0("gpu", "VrShell::ContentBoundsChanged");
1022 content_tex_physical_size_.width = width; 303 GLThread* thread = static_cast<GLThread*>(gl_thread_.get());
1023 content_tex_physical_size_.height = height; 304 // TODO(mthiesse): Remove this blocking wait. Queue up events if thread isn't
1024 // TODO(mthiesse): Synchronize with GL thread, and update tex css size in 305 // finished starting?
1025 // response to MainFrameWasResized, not here. 306 thread->WaitUntilThreadStarted();
1026 content_tex_css_width_ = width / dpr; 307 CHECK(thread->task_runner()->PostTask(
1027 content_tex_css_height_ = height / dpr; 308 FROM_HERE, base::Bind(&VrShellGl::ContentPhysicalBoundsChanged,
1028 309 thread->GetVrShellGl(),
310 width, height)));
1029 content_compositor_->SetWindowBounds(width, height); 311 content_compositor_->SetWindowBounds(width, height);
1030 } 312 }
1031 313
1032 void VrShell::UIBoundsChangedOnUI(JNIEnv* env, 314 void VrShell::UIBoundsChanged(JNIEnv* env,
1033 const JavaParamRef<jobject>& object, 315 const JavaParamRef<jobject>& object,
1034 jint width, jint height, jfloat dpr) { 316 jint width, jint height, jfloat dpr) {
317 GLThread* thread = static_cast<GLThread*>(gl_thread_.get());
318 // TODO(mthiesse): Remove this blocking wait. Queue up events if thread isn't
319 // finished starting?
320 thread->WaitUntilThreadStarted();
321 thread->task_runner()->PostTask(
322 FROM_HERE, base::Bind(&VrShellGl::UIPhysicalBoundsChanged,
323 thread->GetVrShellGl(),
324 width, height));
1035 ui_compositor_->SetWindowBounds(width, height); 325 ui_compositor_->SetWindowBounds(width, height);
1036 } 326 }
1037 327
1038 UiScene* VrShell::GetSceneOnGL() { 328 UiScene* VrShell::GetScene() {
1039 return scene_.get(); 329 GLThread* thread = static_cast<GLThread*>(gl_thread_.get());
330 // TODO(mthiesse): Remove this blocking wait. Queue up events if thread isn't
331 // finished starting?
332 thread->WaitUntilThreadStarted();
333 if (thread->GetVrShellGlUnsafe()) {
334 return thread->GetVrShellGlUnsafe()->GetScene();
335 }
336 return nullptr;
1040 } 337 }
1041 338
1042 UiInterface* VrShell::GetUiInterfaceOnGL() { 339 UiInterface* VrShell::GetUiInterface() {
1043 return html_interface_.get(); 340 return html_interface_.get();
1044 } 341 }
1045 342
1046 void VrShell::QueueTaskOnUI(base::Callback<void()>& callback) { 343 void VrShell::QueueTask(base::Callback<void()>& callback) {
1047 base::AutoLock lock(task_queue_lock_); 344 gl_thread_->task_runner()->PostTask(FROM_HERE, callback);
1048 task_queue_.push(callback);
1049 } 345 }
1050 346
1051 void VrShell::HandleQueuedTasksOnGL() { 347 void VrShell::DoUiAction(const UiAction action) {
1052 // To protect a stream of tasks from blocking rendering indefinitely,
1053 // process only the number of tasks present when first checked.
1054 std::vector<base::Callback<void()>> tasks;
1055 {
1056 base::AutoLock lock(task_queue_lock_);
1057 const size_t count = task_queue_.size();
1058 for (size_t i = 0; i < count; i++) {
1059 tasks.push_back(task_queue_.front());
1060 task_queue_.pop();
1061 }
1062 }
1063 for (auto &task : tasks) {
1064 task.Run();
1065 }
1066 }
1067
1068 void VrShell::DoUiActionOnUI(const UiAction action) {
1069 content::NavigationController& controller = main_contents_->GetController(); 348 content::NavigationController& controller = main_contents_->GetController();
1070 switch (action) { 349 switch (action) {
1071 case HISTORY_BACK: 350 case HISTORY_BACK:
1072 if (main_contents_->IsFullscreen()) { 351 if (main_contents_->IsFullscreen()) {
1073 main_contents_->ExitFullscreen(true /* will_cause_resize */); 352 main_contents_->ExitFullscreen(true /* will_cause_resize */);
1074 } else if (controller.CanGoBack()) { 353 } else if (controller.CanGoBack()) {
1075 controller.GoBack(); 354 controller.GoBack();
1076 } 355 }
1077 break; 356 break;
1078 case HISTORY_FORWARD: 357 case HISTORY_FORWARD:
(...skipping 20 matching lines...) Expand all
1099 } 378 }
1100 379
1101 void VrShell::RenderViewHostChanged(content::RenderViewHost* old_host, 380 void VrShell::RenderViewHostChanged(content::RenderViewHost* old_host,
1102 content::RenderViewHost* new_host) { 381 content::RenderViewHost* new_host) {
1103 new_host->GetWidget()->GetView()->SetBackgroundColor(SK_ColorTRANSPARENT); 382 new_host->GetWidget()->GetView()->SetBackgroundColor(SK_ColorTRANSPARENT);
1104 } 383 }
1105 384
1106 void VrShell::MainFrameWasResized(bool width_changed) { 385 void VrShell::MainFrameWasResized(bool width_changed) {
1107 display::Display display = display::Screen::GetScreen() 386 display::Display display = display::Screen::GetScreen()
1108 ->GetDisplayNearestWindow(ui_contents_->GetNativeView()); 387 ->GetDisplayNearestWindow(ui_contents_->GetNativeView());
1109 // TODO(mthiesse): Synchronize with GL thread. 388 GLThread* thread = static_cast<GLThread*>(gl_thread_.get());
1110 ui_tex_css_width_ = display.size().width(); 389 // TODO(mthiesse): Remove this blocking wait. Queue up events if thread isn't
1111 ui_tex_css_height_ = display.size().height(); 390 // finished starting?
391 thread->WaitUntilThreadStarted();
392 thread->task_runner()->PostTask(
393 FROM_HERE, base::Bind(&VrShellGl::UIBoundsChanged,
394 thread->GetVrShellGl(),
395 display.size().width(), display.size().height()));
396 }
397
398 void VrShell::ContentFrameWasResized(bool width_changed) {
399 display::Display display = display::Screen::GetScreen()
400 ->GetDisplayNearestWindow(main_contents_->GetNativeView());
401 GLThread* thread = static_cast<GLThread*>(gl_thread_.get());
402 // TODO(mthiesse): Remove this blocking wait. Queue up events if thread isn't
403 // finished starting?
404 thread->WaitUntilThreadStarted();
405 thread->task_runner()->PostTask(
406 FROM_HERE, base::Bind(&VrShellGl::ContentBoundsChanged,
407 thread->GetVrShellGl(),
408 display.size().width(), display.size().height()));
1112 } 409 }
1113 410
1114 void VrShell::WebContentsDestroyed() { 411 void VrShell::WebContentsDestroyed() {
1115 ui_input_manager_.reset(); 412 ui_input_manager_.reset();
1116 ui_contents_ = nullptr; 413 ui_contents_ = nullptr;
1117 // TODO(mthiesse): Handle web contents being destroyed. 414 // TODO(mthiesse): Handle web contents being destroyed.
415 ForceExitVR();
416 }
417
418 void VrShell::ContentWebContentsDestroyed() {
419 content_input_manager_.reset();
420 main_contents_ = nullptr;
421 // TODO(mthiesse): Handle web contents being destroyed.
422 ForceExitVR();
423 }
424
425 void VrShell::ContentWasHidden() {
426 // Ensure we don't continue sending input to it.
427 content_input_manager_.reset();
428 // TODO(mthiesse): Handle web contents being hidden.
429 ForceExitVR();
430 }
431
432 void VrShell::ForceExitVR() {
1118 delegate_->ForceExitVr(); 433 delegate_->ForceExitVr();
1119 } 434 }
1120 435
1121 void VrShell::ContentWebContentsDestroyedOnUI() { 436 void VrShell::SetContentCssSize(float width, float height, float dpr) {
1122 content_input_manager_.reset(); 437 JNIEnv* env = base::android::AttachCurrentThread();
1123 main_contents_ = nullptr; 438 Java_VrShellImpl_setContentCssSize(env, j_vr_shell_.obj(), width, height,
1124 // TODO(mthiesse): Handle web contents being destroyed. 439 dpr);
1125 delegate_->ForceExitVr();
1126 } 440 }
1127 441
1128 void VrShell::ContentWasHiddenOnUI() { 442 void VrShell::SetUiCssSize(float width, float height, float dpr) {
1129 // Ensure we don't continue sending input to it.
1130 content_input_manager_.reset();
1131 // TODO(mthiesse): Handle web contents being hidden.
1132 delegate_->ForceExitVr();
1133 }
1134
1135 void VrShell::SetContentCssSizeOnUI(float width, float height, float dpr) {
1136 JNIEnv* env = base::android::AttachCurrentThread(); 443 JNIEnv* env = base::android::AttachCurrentThread();
1137 Java_VrShellImpl_setContentCssSizeOnUI(env, j_vr_shell_.obj(), width, height, 444 Java_VrShellImpl_setUiCssSize(env, j_vr_shell_.obj(), width, height, dpr);
1138 dpr);
1139 }
1140
1141 void VrShell::SetUiCssSizeOnUI(float width, float height, float dpr) {
1142 JNIEnv* env = base::android::AttachCurrentThread();
1143 Java_VrShellImpl_setUiCssSizeOnUI(env, j_vr_shell_.obj(), width, height, dpr);
1144 } 445 }
1145 446
1146 // ---------------------------------------------------------------------------- 447 // ----------------------------------------------------------------------------
1147 // Native JNI methods 448 // Native JNI methods
1148 // ---------------------------------------------------------------------------- 449 // ----------------------------------------------------------------------------
1149 450
1150 jlong InitOnUI(JNIEnv* env, 451 jlong Init(JNIEnv* env, const JavaParamRef<jobject>& obj,
1151 const JavaParamRef<jobject>& obj, 452 const JavaParamRef<jobject>& content_web_contents,
1152 const JavaParamRef<jobject>& content_web_contents, 453 jlong content_window_android,
1153 jlong content_window_android, 454 const JavaParamRef<jobject>& ui_web_contents,
1154 const JavaParamRef<jobject>& ui_web_contents, 455 jlong ui_window_android, jboolean for_web_vr,
1155 jlong ui_window_android, 456 const base::android::JavaParamRef<jobject>& delegate,
1156 jboolean for_web_vr) { 457 jlong gvr_api) {
1157 return reinterpret_cast<intptr_t>(new VrShell( 458 return reinterpret_cast<intptr_t>(new VrShell(
1158 env, obj, content::WebContents::FromJavaWebContents(content_web_contents), 459 env, obj, content::WebContents::FromJavaWebContents(content_web_contents),
1159 reinterpret_cast<ui::WindowAndroid*>(content_window_android), 460 reinterpret_cast<ui::WindowAndroid*>(content_window_android),
1160 content::WebContents::FromJavaWebContents(ui_web_contents), 461 content::WebContents::FromJavaWebContents(ui_web_contents),
1161 reinterpret_cast<ui::WindowAndroid*>(ui_window_android), 462 reinterpret_cast<ui::WindowAndroid*>(ui_window_android),
1162 for_web_vr)); 463 for_web_vr, VrShellDelegate::GetNativeDelegate(env, delegate),
464 reinterpret_cast<gvr_context*>(gvr_api)));
1163 } 465 }
1164 466
1165 } // namespace vr_shell 467 } // namespace vr_shell
OLDNEW
« no previous file with comments | « chrome/browser/android/vr_shell/vr_shell.h ('k') | chrome/browser/android/vr_shell/vr_shell_delegate.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698