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

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 bshe's 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, base::WeakPtr<VrShell> weak_vr_shell,
105 float zdiff = (vec1.z - vec2.z); 46 base::WeakPtr<VrInputManager> content_input_manager,
106 float scale = xdiff * xdiff + ydiff * ydiff + zdiff * zdiff; 47 base::WeakPtr<VrInputManager> ui_input_manager,
107 return std::sqrt(scale); 48 scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner,
108 } 49 gvr_context* gvr_api)
50 : base::Thread("VrShellGL"),
51 vr_shell_(vr_shell),
52 weak_vr_shell_(weak_vr_shell),
53 content_input_manager_(content_input_manager),
54 ui_input_manager_(ui_input_manager),
55 main_thread_task_runner_(std::move(main_thread_task_runner)),
56 gvr_api_(gvr_api) {}
109 57
110 // Generate a quaternion representing the rotation from the negative Z axis 58 base::WeakPtr<VrShellGl> GetVrShellGl() { return weak_vr_shell_gl_; }
111 // (0, 0, -1) to a specified vector. This is an optimized version of a more 59 VrShellGl* GetVrShellGlUnsafe() { return vr_shell_gl_.get(); }
112 // general vector-to-vector calculation. 60 void StopRenderLoop() {
113 gvr::Quatf GetRotationFromZAxis(gvr::Vec3f vec) { 61 vr_shell_gl_.reset();
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 } 62 }
130 return quat;
131 }
132 63
133 std::unique_ptr<blink::WebMouseEvent> MakeMouseEvent(WebInputEvent::Type type, 64 protected:
134 double timestamp, 65 void Init() override {
135 float x, 66 vr_shell_gl_.reset(new VrShellGl(vr_shell_,
136 float y) { 67 std::move(weak_vr_shell_),
137 std::unique_ptr<blink::WebMouseEvent> mouse_event(new blink::WebMouseEvent); 68 std::move(content_input_manager_),
138 mouse_event->type = type; 69 std::move(ui_input_manager_),
139 mouse_event->pointerType = blink::WebPointerProperties::PointerType::Mouse; 70 std::move(main_thread_task_runner_),
140 mouse_event->x = x; 71 gvr_api_));
141 mouse_event->y = y; 72 weak_vr_shell_gl_ = vr_shell_gl_->GetWeakPtr();
142 mouse_event->windowX = x; 73 if (!vr_shell_gl_->Initialize()) {
143 mouse_event->windowY = y; 74 vr_shell_gl_.reset();
144 mouse_event->timeStampSeconds = timestamp; 75 }
145 mouse_event->clickCount = 1; 76 }
146 mouse_event->modifiers = 0;
147 77
148 return mouse_event; 78 private:
149 } 79 std::unique_ptr<VrShellGl> vr_shell_gl_;
80 base::WeakPtr<VrShellGl> weak_vr_shell_gl_;
81
82 // TODO(mthiesse): Remove vr_shell_.
83 VrShell* vr_shell_;
84 base::WeakPtr<VrShell> weak_vr_shell_;
85 base::WeakPtr<VrInputManager> content_input_manager_;
86 base::WeakPtr<VrInputManager> ui_input_manager_;
87 scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
88 gvr_context* gvr_api_;
89 };
90
150 } // namespace 91 } // namespace
151 92
152 VrShell::VrShell(JNIEnv* env, 93 VrShell::VrShell(JNIEnv* env,
153 jobject obj, 94 jobject obj,
154 content::WebContents* main_contents, 95 content::WebContents* main_contents,
155 ui::WindowAndroid* content_window, 96 ui::WindowAndroid* content_window,
156 content::WebContents* ui_contents, 97 content::WebContents* ui_contents,
157 ui::WindowAndroid* ui_window, 98 ui::WindowAndroid* ui_window,
158 bool for_web_vr) 99 bool for_web_vr,
100 VrShellDelegate* delegate,
101 gvr_context* gvr_api)
159 : WebContentsObserver(ui_contents), 102 : WebContentsObserver(ui_contents),
160 main_contents_(main_contents), 103 main_contents_(main_contents),
161 ui_contents_(ui_contents), 104 ui_contents_(ui_contents),
105 delegate_(delegate),
162 metrics_helper_(new VrMetricsHelper(main_contents)), 106 metrics_helper_(new VrMetricsHelper(main_contents)),
163 main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()), 107 main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
164 weak_ptr_factory_(this) { 108 weak_ptr_factory_(this) {
165 DCHECK(g_instance == nullptr); 109 DCHECK(g_instance == nullptr);
166 g_instance = this; 110 g_instance = this;
167 j_vr_shell_.Reset(env, obj); 111 j_vr_shell_.Reset(env, obj);
168 scene_.reset(new UiScene); 112
113 content_input_manager_.reset(new VrInputManager(main_contents_));
114 ui_input_manager_.reset(new VrInputManager(ui_contents_));
115
116 gl_thread_.reset(new GLThread(this, weak_ptr_factory_.GetWeakPtr(),
117 content_input_manager_->GetWeakPtr(),
118 ui_input_manager_->GetWeakPtr(),
119 main_thread_task_runner_,
120 gvr_api));
121
122 base::Thread::Options options(base::MessageLoop::TYPE_DEFAULT, 0);
123 options.priority = base::ThreadPriority::DISPLAY;
124 gl_thread_->StartWithOptions(options);
169 125
170 if (for_web_vr) 126 if (for_web_vr)
171 metrics_helper_->SetWebVREnabled(true); 127 metrics_helper_->SetWebVREnabled(true);
172 html_interface_.reset(new UiInterface( 128 html_interface_.reset(new UiInterface(
173 for_web_vr ? UiInterface::Mode::WEB_VR : UiInterface::Mode::STANDARD, 129 for_web_vr ? UiInterface::Mode::WEB_VR : UiInterface::Mode::STANDARD,
174 main_contents_->IsFullscreen())); 130 main_contents_->IsFullscreen()));
175 content_compositor_.reset(new VrCompositor(content_window, false)); 131 content_compositor_.reset(new VrCompositor(content_window, false));
132 content_compositor_->SetLayer(main_contents_);
176 ui_compositor_.reset(new VrCompositor(ui_window, true)); 133 ui_compositor_.reset(new VrCompositor(ui_window, true));
134 ui_compositor_->SetLayer(ui_contents_);
177 vr_web_contents_observer_.reset(new VrWebContentsObserver( 135 vr_web_contents_observer_.reset(new VrWebContentsObserver(
178 main_contents, html_interface_.get(), this)); 136 main_contents, html_interface_.get(), this));
179 137
180 LoadUIContentOnUI(); 138 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 } 139 }
194 140
195 void VrShell::UpdateCompositorLayersOnUI(JNIEnv* env, 141 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; 142 delete this;
203 } 143 }
204 144
205 void VrShell::LoadUIContentOnUI() { 145 void VrShell::LoadUIContent(JNIEnv* env, const JavaParamRef<jobject>& obj) {
206 GURL url(kVrShellUIURL); 146 GURL url(kVrShellUIURL);
207 ui_contents_->GetController().LoadURL( 147 ui_contents_->GetController().LoadURL(
208 url, content::Referrer(), 148 url, content::Referrer(),
209 ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string("")); 149 ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string(""));
210 } 150 }
211 151
212 bool RegisterVrShell(JNIEnv* env) { 152 bool RegisterVrShell(JNIEnv* env) {
213 return RegisterNativesImpl(env); 153 return RegisterNativesImpl(env);
214 } 154 }
215 155
216 VrShell::~VrShell() { 156 VrShell::~VrShell() {
217 if (delegate_ && delegate_->GetDeviceProvider()) { 157 GLThread* thread = static_cast<GLThread*>(gl_thread_.get());
218 delegate_->GetDeviceProvider()->OnGvrDelegateRemoved(); 158 thread->task_runner()->PostTask(
159 FROM_HERE,
160 base::Bind(&GLThread::StopRenderLoop, base::Unretained(thread)));
161 {
162 // Unfortunately stopping threads asserts that IO is allowed, and on the
163 // Android UI thread, IO is not allowed by default. However, we can't allow
164 // the thread to keep running, because the GVR instance is about to be
165 // deleted and we'll segfault if the thread keeps running for any amount of
166 // time.
167 // Our options are:
brettw 2016/12/12 19:12:40 Can you replace this "options" section with the in
mthiesse 2016/12/12 19:49:30 Done.
168 // 1. Use the *heavily* discouraged ScopedAllowIO.
169 // 2. a. Post a task to the GLThread telling it to stop delete vr_shell_gl.
170 // b. Wait for a signal that this has been completed.
171 // c. Call thread->StopSoon().
172 // d. Post a task to the current thread to check if GLThread has stopped
173 // yet.
174 // e. If the thread has stopped, delete it, otherwise post delayed to
175 // the current thread all over again. Repeat as needed.
176 //
177 // TODO(mthiesse): I've gone with option 1. Are there better options?
178 base::ThreadRestrictions::ScopedAllowIO allow_io;
179 thread->Stop();
219 } 180 }
181 delegate_->RemoveDelegate();
220 g_instance = nullptr; 182 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 } 183 }
572 184
573 void VrShell::SetGvrPoseForWebVr(const gvr::Mat4f& pose, uint32_t pose_num) { 185 void VrShell::SetGvrPoseForWebVr(const gvr::Mat4f& pose, uint32_t pose_num) {
574 webvr_head_pose_[pose_num % kPoseRingBufferSize] = pose; 186 GLThread* thread = static_cast<GLThread*>(gl_thread_.get());
575 webvr_head_pose_valid_[pose_num % kPoseRingBufferSize] = true; 187 if (thread->GetVrShellGlUnsafe()) {
bshe 2016/12/12 20:30:20 why do you use Unsafe here instead of post to GL t
576 } 188 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 } 189 }
764 } 190 }
765 191
766 void VrShell::SetWebVRRenderSurfaceSize(int width, int height) { 192 void VrShell::SetWebVRRenderSurfaceSize(int width, int height) {
767 render_size_primary_webvr_.width = width; 193 GLThread* thread = static_cast<GLThread*>(gl_thread_.get());
768 render_size_primary_webvr_.height = height; 194 if (thread->GetVrShellGlUnsafe()) {
769 // TODO(klausw,crbug.com/655722): set the WebVR render surface size here once 195 thread->GetVrShellGlUnsafe()->SetWebVRRenderSurfaceSize(width, height);
770 // we have that. 196 }
771 } 197 }
772 198
773 gvr::Sizei VrShell::GetWebVRCompositorSurfaceSize() { 199 gvr::Sizei VrShell::GetWebVRCompositorSurfaceSize() {
774 // This is a stopgap while we're using the WebVR compositor rendering path. 200 GLThread* thread = static_cast<GLThread*>(gl_thread_.get());
775 // TODO(klausw,crbug.com/655722): Remove this method and member once we're 201 if (thread->GetVrShellGlUnsafe()) {
776 // using a separate WebVR render surface. 202 return thread->GetVrShellGlUnsafe()->GetWebVRCompositorSurfaceSize();
777 return content_tex_physical_size_; 203 }
204 return gvr::Sizei();
778 } 205 }
779 206
780 207 void VrShell::OnTriggerEvent(JNIEnv* env,
781 void VrShell::DrawUiViewOnGL(const gvr::Mat4f* head_pose, 208 const JavaParamRef<jobject>& obj) {
782 const std::vector<const ContentRectangle*>& elements, 209 GLThread* thread = static_cast<GLThread*>(gl_thread_.get());
783 const gvr::Sizei& render_size, int viewport_offset) { 210 thread->task_runner()->PostTask(FROM_HERE,
784 TRACE_EVENT0("gpu", "VrShell::DrawUiView"); 211 base::Bind(&VrShellGl::OnTriggerEvent,
785 for (auto eye : {GVR_LEFT_EYE, GVR_RIGHT_EYE}) { 212 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 } 213 }
812 214
813 void VrShell::DrawElementsOnGL( 215 void VrShell::OnPause(JNIEnv* env, const JavaParamRef<jobject>& obj) {
814 const gvr::Mat4f& render_matrix, 216 GLThread* thread = static_cast<GLThread*>(gl_thread_.get());
815 const std::vector<const ContentRectangle*>& elements) { 217 thread->task_runner()->PostTask(
816 for (const auto& rect : elements) { 218 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 219
944 // exit vr session 220 // exit vr session
945 metrics_helper_->SetVRActive(false); 221 metrics_helper_->SetVRActive(false);
222 SetShowingOverscrollGlow(true);
946 } 223 }
947 224
948 void VrShell::OnResumeOnUI(JNIEnv* env, const JavaParamRef<jobject>& obj) { 225 void VrShell::OnResume(JNIEnv* env, const JavaParamRef<jobject>& obj) {
949 if (gvr_api_ == nullptr) 226 GLThread* thread = static_cast<GLThread*>(gl_thread_.get());
950 return; 227 thread->task_runner()->PostTask(
951 228 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 229
958 // exit vr session 230 // exit vr session
959 metrics_helper_->SetVRActive(true); 231 metrics_helper_->SetVRActive(true);
232 SetShowingOverscrollGlow(false);
960 } 233 }
961 234
962 void VrShell::SetShowingOverscrollGlowOnUI(bool showing_glow) { 235 void VrShell::SetShowingOverscrollGlow(bool showing_glow) {
963 main_contents_->GetRenderWidgetHostView()->SetShowingOverscrollGlow( 236 main_contents_->GetRenderWidgetHostView()->SetShowingOverscrollGlow(
964 showing_glow); 237 showing_glow);
965 } 238 }
966 239
967 base::WeakPtr<VrShell> VrShell::GetWeakPtrOnUI( 240 base::WeakPtr<VrShell> VrShell::GetWeakPtr(
968 const content::WebContents* web_contents) { 241 const content::WebContents* web_contents) {
969 // Ensure that the WebContents requesting the VrShell instance is the one 242 // Ensure that the WebContents requesting the VrShell instance is the one
970 // we created. 243 // we created.
971 if (g_instance != nullptr && g_instance->ui_contents_ == web_contents) 244 if (g_instance != nullptr && g_instance->ui_contents_ == web_contents)
972 return g_instance->weak_ptr_factory_.GetWeakPtr(); 245 return g_instance->weak_ptr_factory_.GetWeakPtr();
973 return base::WeakPtr<VrShell>(nullptr); 246 return base::WeakPtr<VrShell>(nullptr);
974 } 247 }
975 248
976 void VrShell::OnDomContentsLoadedOnUI() { 249 void VrShell::OnDomContentsLoaded() {
977 html_interface_->SetURL(main_contents_->GetVisibleURL()); 250 html_interface_->SetURL(main_contents_->GetVisibleURL());
978 html_interface_->SetLoading(main_contents_->IsLoading()); 251 html_interface_->SetLoading(main_contents_->IsLoading());
979 html_interface_->OnDomContentsLoaded(); 252 html_interface_->OnDomContentsLoaded();
980 } 253 }
981 254
982 void VrShell::SetWebVrModeOnUI(JNIEnv* env, 255 void VrShell::SetWebVrMode(JNIEnv* env,
983 const base::android::JavaParamRef<jobject>& obj, 256 const base::android::JavaParamRef<jobject>& obj,
984 bool enabled) { 257 bool enabled) {
985 metrics_helper_->SetWebVREnabled(enabled); 258 metrics_helper_->SetWebVREnabled(enabled);
986 if (enabled) { 259 if (enabled) {
987 html_interface_->SetMode(UiInterface::Mode::WEB_VR); 260 html_interface_->SetMode(UiInterface::Mode::WEB_VR);
988 } else { 261 } else {
989 html_interface_->SetMode(UiInterface::Mode::STANDARD); 262 html_interface_->SetMode(UiInterface::Mode::STANDARD);
990 } 263 }
991 } 264 }
992 265
993 void VrShell::SetWebVRSecureOrigin(bool secure_origin) { 266 void VrShell::SetWebVRSecureOrigin(bool secure_origin) {
994 // TODO(cjgrant): Align this state with the logic that drives the omnibox. 267 // TODO(cjgrant): Align this state with the logic that drives the omnibox.
995 html_interface_->SetWebVRSecureOrigin(secure_origin); 268 html_interface_->SetWebVRSecureOrigin(secure_origin);
996 } 269 }
997 270
998 void VrShell::SubmitWebVRFrame() {} 271 void VrShell::SubmitWebVRFrame() {}
999 272
1000 void VrShell::UpdateWebVRTextureBounds(const gvr::Rectf& left_bounds, 273 void VrShell::UpdateWebVRTextureBounds(const gvr::Rectf& left_bounds,
1001 const gvr::Rectf& right_bounds) { 274 const gvr::Rectf& right_bounds) {
1002 webvr_left_viewport_->SetSourceUv(left_bounds); 275 GLThread* thread = static_cast<GLThread*>(gl_thread_.get());
1003 webvr_right_viewport_->SetSourceUv(right_bounds); 276 if (thread->GetVrShellGlUnsafe()) {
277 thread->GetVrShellGlUnsafe()->UpdateWebVRTextureBounds(left_bounds,
278 right_bounds);
279 }
1004 } 280 }
1005 281
282 // TODO(mthiesse): Do not expose GVR API outside of GL thread.
283 // It's not thread-safe.
1006 gvr::GvrApi* VrShell::gvr_api() { 284 gvr::GvrApi* VrShell::gvr_api() {
1007 return gvr_api_.get(); 285 GLThread* thread = static_cast<GLThread*>(gl_thread_.get());
286 if (thread->GetVrShellGlUnsafe()) {
287 return thread->GetVrShellGlUnsafe()->gvr_api();
288 }
289 CHECK(false);
290 return nullptr;
1008 } 291 }
1009 292
1010 void VrShell::SurfacesChangedOnUI(JNIEnv* env, 293 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); 294 content_compositor_->SurfaceChanged(content_surface);
1015 ui_compositor_->SurfaceChanged(ui_surface); 295 ui_compositor_->SurfaceChanged(ui_surface);
1016 } 296 }
1017 297
1018 void VrShell::ContentBoundsChangedOnUI(JNIEnv* env, 298 void VrShell::GvrDelegateReady() {
299 delegate_->SetDelegate(weak_ptr_factory_.GetWeakPtr());
300 }
301
302 void VrShell::ContentBoundsChanged(JNIEnv* env,
1019 const JavaParamRef<jobject>& object, 303 const JavaParamRef<jobject>& object,
1020 jint width, jint height, jfloat dpr) { 304 jint width, jint height, jfloat dpr) {
1021 TRACE_EVENT0("gpu", "VrShell::ContentBoundsChanged"); 305 TRACE_EVENT0("gpu", "VrShell::ContentBoundsChanged");
1022 content_tex_physical_size_.width = width; 306 GLThread* thread = static_cast<GLThread*>(gl_thread_.get());
1023 content_tex_physical_size_.height = height; 307 // 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 308 // finished starting?
1025 // response to MainFrameWasResized, not here. 309 thread->WaitUntilThreadStarted();
1026 content_tex_css_width_ = width / dpr; 310 CHECK(thread->task_runner()->PostTask(
1027 content_tex_css_height_ = height / dpr; 311 FROM_HERE, base::Bind(&VrShellGl::ContentPhysicalBoundsChanged,
1028 312 thread->GetVrShellGl(),
313 width, height)));
1029 content_compositor_->SetWindowBounds(width, height); 314 content_compositor_->SetWindowBounds(width, height);
1030 } 315 }
1031 316
1032 void VrShell::UIBoundsChangedOnUI(JNIEnv* env, 317 void VrShell::UIBoundsChanged(JNIEnv* env,
1033 const JavaParamRef<jobject>& object, 318 const JavaParamRef<jobject>& object,
1034 jint width, jint height, jfloat dpr) { 319 jint width, jint height, jfloat dpr) {
320 GLThread* thread = static_cast<GLThread*>(gl_thread_.get());
321 // TODO(mthiesse): Remove this blocking wait. Queue up events if thread isn't
322 // finished starting?
323 thread->WaitUntilThreadStarted();
324 thread->task_runner()->PostTask(
325 FROM_HERE, base::Bind(&VrShellGl::UIPhysicalBoundsChanged,
326 thread->GetVrShellGl(),
327 width, height));
1035 ui_compositor_->SetWindowBounds(width, height); 328 ui_compositor_->SetWindowBounds(width, height);
1036 } 329 }
1037 330
1038 UiScene* VrShell::GetSceneOnGL() { 331 UiScene* VrShell::GetScene() {
1039 return scene_.get(); 332 GLThread* thread = static_cast<GLThread*>(gl_thread_.get());
333 // TODO(mthiesse): Remove this blocking wait. Queue up events if thread isn't
334 // finished starting?
335 thread->WaitUntilThreadStarted();
336 if (thread->GetVrShellGlUnsafe()) {
337 return thread->GetVrShellGlUnsafe()->GetScene();
338 }
339 return nullptr;
1040 } 340 }
1041 341
1042 UiInterface* VrShell::GetUiInterfaceOnGL() { 342 UiInterface* VrShell::GetUiInterface() {
1043 return html_interface_.get(); 343 return html_interface_.get();
1044 } 344 }
1045 345
1046 void VrShell::QueueTaskOnUI(base::Callback<void()>& callback) { 346 void VrShell::QueueTask(base::Callback<void()>& callback) {
1047 base::AutoLock lock(task_queue_lock_); 347 gl_thread_->task_runner()->PostTask(FROM_HERE, callback);
1048 task_queue_.push(callback);
1049 } 348 }
1050 349
1051 void VrShell::HandleQueuedTasksOnGL() { 350 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(); 351 content::NavigationController& controller = main_contents_->GetController();
1070 switch (action) { 352 switch (action) {
1071 case HISTORY_BACK: 353 case HISTORY_BACK:
1072 if (main_contents_->IsFullscreen()) { 354 if (main_contents_->IsFullscreen()) {
1073 main_contents_->ExitFullscreen(true /* will_cause_resize */); 355 main_contents_->ExitFullscreen(true /* will_cause_resize */);
1074 } else if (controller.CanGoBack()) { 356 } else if (controller.CanGoBack()) {
1075 controller.GoBack(); 357 controller.GoBack();
1076 } 358 }
1077 break; 359 break;
1078 case HISTORY_FORWARD: 360 case HISTORY_FORWARD:
(...skipping 20 matching lines...) Expand all
1099 } 381 }
1100 382
1101 void VrShell::RenderViewHostChanged(content::RenderViewHost* old_host, 383 void VrShell::RenderViewHostChanged(content::RenderViewHost* old_host,
1102 content::RenderViewHost* new_host) { 384 content::RenderViewHost* new_host) {
1103 new_host->GetWidget()->GetView()->SetBackgroundColor(SK_ColorTRANSPARENT); 385 new_host->GetWidget()->GetView()->SetBackgroundColor(SK_ColorTRANSPARENT);
1104 } 386 }
1105 387
1106 void VrShell::MainFrameWasResized(bool width_changed) { 388 void VrShell::MainFrameWasResized(bool width_changed) {
1107 display::Display display = display::Screen::GetScreen() 389 display::Display display = display::Screen::GetScreen()
1108 ->GetDisplayNearestWindow(ui_contents_->GetNativeView()); 390 ->GetDisplayNearestWindow(ui_contents_->GetNativeView());
1109 // TODO(mthiesse): Synchronize with GL thread. 391 GLThread* thread = static_cast<GLThread*>(gl_thread_.get());
1110 ui_tex_css_width_ = display.size().width(); 392 // TODO(mthiesse): Remove this blocking wait. Queue up events if thread isn't
1111 ui_tex_css_height_ = display.size().height(); 393 // finished starting?
394 thread->WaitUntilThreadStarted();
395 thread->task_runner()->PostTask(
396 FROM_HERE, base::Bind(&VrShellGl::UIBoundsChanged,
397 thread->GetVrShellGl(),
398 display.size().width(), display.size().height()));
399 }
400
401 void VrShell::ContentFrameWasResized(bool width_changed) {
402 display::Display display = display::Screen::GetScreen()
403 ->GetDisplayNearestWindow(main_contents_->GetNativeView());
404 GLThread* thread = static_cast<GLThread*>(gl_thread_.get());
405 // TODO(mthiesse): Remove this blocking wait. Queue up events if thread isn't
406 // finished starting?
407 thread->WaitUntilThreadStarted();
408 thread->task_runner()->PostTask(
409 FROM_HERE, base::Bind(&VrShellGl::ContentBoundsChanged,
410 thread->GetVrShellGl(),
411 display.size().width(), display.size().height()));
1112 } 412 }
1113 413
1114 void VrShell::WebContentsDestroyed() { 414 void VrShell::WebContentsDestroyed() {
1115 ui_input_manager_.reset(); 415 ui_input_manager_.reset();
1116 ui_contents_ = nullptr; 416 ui_contents_ = nullptr;
1117 // TODO(mthiesse): Handle web contents being destroyed. 417 // TODO(mthiesse): Handle web contents being destroyed.
418 ForceExitVR();
419 }
420
421 void VrShell::ContentWebContentsDestroyed() {
422 content_input_manager_.reset();
423 main_contents_ = nullptr;
424 // TODO(mthiesse): Handle web contents being destroyed.
425 ForceExitVR();
426 }
427
428 void VrShell::ContentWasHidden() {
429 // Ensure we don't continue sending input to it.
430 content_input_manager_.reset();
431 // TODO(mthiesse): Handle web contents being hidden.
432 ForceExitVR();
433 }
434
435 void VrShell::ForceExitVR() {
bshe 2016/12/12 20:30:20 nit: VR/Vr to be consistent
1118 delegate_->ForceExitVr(); 436 delegate_->ForceExitVr();
1119 } 437 }
1120 438
1121 void VrShell::ContentWebContentsDestroyedOnUI() { 439 void VrShell::SetContentCssSize(float width, float height, float dpr) {
1122 content_input_manager_.reset(); 440 JNIEnv* env = base::android::AttachCurrentThread();
1123 main_contents_ = nullptr; 441 Java_VrShellImpl_setContentCssSize(env, j_vr_shell_.obj(), width, height,
1124 // TODO(mthiesse): Handle web contents being destroyed. 442 dpr);
1125 delegate_->ForceExitVr();
1126 } 443 }
1127 444
1128 void VrShell::ContentWasHiddenOnUI() { 445 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(); 446 JNIEnv* env = base::android::AttachCurrentThread();
1137 Java_VrShellImpl_setContentCssSizeOnUI(env, j_vr_shell_.obj(), width, height, 447 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 } 448 }
1145 449
1146 // ---------------------------------------------------------------------------- 450 // ----------------------------------------------------------------------------
1147 // Native JNI methods 451 // Native JNI methods
1148 // ---------------------------------------------------------------------------- 452 // ----------------------------------------------------------------------------
1149 453
1150 jlong InitOnUI(JNIEnv* env, 454 jlong Init(JNIEnv* env, const JavaParamRef<jobject>& obj,
1151 const JavaParamRef<jobject>& obj, 455 const JavaParamRef<jobject>& content_web_contents,
1152 const JavaParamRef<jobject>& content_web_contents, 456 jlong content_window_android,
1153 jlong content_window_android, 457 const JavaParamRef<jobject>& ui_web_contents,
1154 const JavaParamRef<jobject>& ui_web_contents, 458 jlong ui_window_android, jboolean for_web_vr,
1155 jlong ui_window_android, 459 const base::android::JavaParamRef<jobject>& delegate,
1156 jboolean for_web_vr) { 460 jlong gvr_api) {
1157 return reinterpret_cast<intptr_t>(new VrShell( 461 return reinterpret_cast<intptr_t>(new VrShell(
1158 env, obj, content::WebContents::FromJavaWebContents(content_web_contents), 462 env, obj, content::WebContents::FromJavaWebContents(content_web_contents),
1159 reinterpret_cast<ui::WindowAndroid*>(content_window_android), 463 reinterpret_cast<ui::WindowAndroid*>(content_window_android),
1160 content::WebContents::FromJavaWebContents(ui_web_contents), 464 content::WebContents::FromJavaWebContents(ui_web_contents),
1161 reinterpret_cast<ui::WindowAndroid*>(ui_window_android), 465 reinterpret_cast<ui::WindowAndroid*>(ui_window_android),
1162 for_web_vr)); 466 for_web_vr, VrShellDelegate::GetNativeDelegate(env, delegate),
467 reinterpret_cast<gvr_context*>(gvr_api)));
1163 } 468 }
1164 469
1165 } // namespace vr_shell 470 } // namespace vr_shell
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698