| Index: chrome/browser/android/vr_shell/vr_shell.cc
|
| diff --git a/chrome/browser/android/vr_shell/vr_shell.cc b/chrome/browser/android/vr_shell/vr_shell.cc
|
| index 5e4189a97aa92570b67b4a403b9e7e21ff6fb0e0..48a19ba7428bd18e1c01cffffc3b08cfa2120c9f 100644
|
| --- a/chrome/browser/android/vr_shell/vr_shell.cc
|
| +++ b/chrome/browser/android/vr_shell/vr_shell.cc
|
| @@ -73,9 +73,24 @@ static constexpr float kReticleOffset = 0.99f;
|
| // adjust according to content quad placement.
|
| static constexpr float kReticleDistanceMultiplier = 1.5f;
|
|
|
| +// GVR buffer indices for use with viewport->SetSourceBufferIndex
|
| +// or frame.BindBuffer. We use one for world content (with reprojection)
|
| +// including main VrShell and WebVR content plus world-space UI.
|
| +// The headlocked buffer is for UI that should not use reprojection.
|
| static constexpr int kFramePrimaryBuffer = 0;
|
| static constexpr int kFrameHeadlockedBuffer = 1;
|
|
|
| +// Pixel dimensions and field of view for the head-locked content. This
|
| +// is currently sized to fit the WebVR "insecure transport" warnings,
|
| +// adjust it as needed if there is additional content.
|
| +static constexpr gvr::Sizei kHeadlockedBufferDimensions = {1024, 1024};
|
| +static constexpr gvr::Rectf kHeadlockedBufferFov = {20.f, 20.f, 20.f, 20.f};
|
| +
|
| +// The GVR viewport list has two entries (left eye and right eye) for each
|
| +// GVR buffer.
|
| +static constexpr int kViewportListPrimaryOffset = 0;
|
| +static constexpr int kViewportListHeadlockedOffset = 2;
|
| +
|
| vr_shell::VrShell* g_instance;
|
|
|
| static const char kVrShellUIURL[] = "chrome://vr-shell-ui";
|
| @@ -222,13 +237,10 @@ void VrShell::GvrInit(JNIEnv* env,
|
|
|
| gvr_api_ =
|
| gvr::GvrApi::WrapNonOwned(reinterpret_cast<gvr_context*>(native_gvr_api));
|
| -
|
| - if (delegate_) {
|
| - main_thread_task_runner_->PostTask(
|
| - FROM_HERE, base::Bind(&device::GvrDeviceProvider::OnGvrDelegateReady,
|
| - delegate_->GetDeviceProvider(),
|
| - weak_ptr_factory_.GetWeakPtr()));
|
| - }
|
| + // TODO(klausw,crbug.com/655722): should report OnGvrDelegateReady here once
|
| + // we switch to using a WebVR render surface. We currently need to wait for
|
| + // the compositor window's size to be known first. See also
|
| + // ContentSurfaceChanged.
|
| controller_.reset(
|
| new VrController(reinterpret_cast<gvr_context*>(native_gvr_api)));
|
| content_input_manager_ = new VrInputManager(main_contents_);
|
| @@ -261,30 +273,53 @@ void VrShell::InitializeGl(JNIEnv* env,
|
| content_texture_id_ = content_texture_handle;
|
| ui_texture_id_ = ui_texture_handle;
|
|
|
| + // While WebVR is going through the compositor path, it shares
|
| + // the same texture ID. This will change once it gets its own
|
| + // surface, but store it separately to avoid future confusion.
|
| + // TODO(klausw,crbug.com/655722): remove this.
|
| + webvr_texture_id_ = content_texture_id_;
|
| +
|
| gvr_api_->InitializeGl();
|
| std::vector<gvr::BufferSpec> specs;
|
| + // For kFramePrimaryBuffer (primary VrShell and WebVR content)
|
| specs.push_back(gvr_api_->CreateBufferSpec());
|
| - render_size_ = specs[0].GetSize();
|
| + render_size_primary_ = specs[kFramePrimaryBuffer].GetSize();
|
| + render_size_primary_vrshell_ = render_size_primary_;
|
|
|
| - // For WebVR content
|
| + // For kFrameHeadlockedBuffer (for WebVR insecure content warning).
|
| + // Set this up at fixed resolution, the (smaller) FOV gets set below.
|
| specs.push_back(gvr_api_->CreateBufferSpec());
|
| + specs.back().SetSize(kHeadlockedBufferDimensions);
|
| + render_size_headlocked_ = specs[kFrameHeadlockedBuffer].GetSize();
|
|
|
| swap_chain_.reset(new gvr::SwapChain(gvr_api_->CreateSwapChain(specs)));
|
|
|
| vr_shell_renderer_.reset(new VrShellRenderer());
|
| +
|
| + // Allocate a buffer viewport for use in UI drawing. This isn't
|
| + // initialized at this point, it'll be set from other viewport list
|
| + // entries as needed.
|
| + buffer_viewport_.reset(
|
| + new gvr::BufferViewport(gvr_api_->CreateBufferViewport()));
|
| +
|
| + // Set up main content viewports. The list has two elements, 0=left
|
| + // eye and 1=right eye.
|
| buffer_viewport_list_.reset(
|
| new gvr::BufferViewportList(gvr_api_->CreateEmptyBufferViewportList()));
|
| buffer_viewport_list_->SetToRecommendedBufferViewports();
|
|
|
| - buffer_viewport_.reset(
|
| - new gvr::BufferViewport(gvr_api_->CreateBufferViewport()));
|
| -
|
| + // Set up head-locked UI viewports, these will be elements 2=left eye
|
| + // and 3=right eye. For now, use a hardcoded 20-degree-from-center FOV
|
| + // frustum to reduce rendering cost for this overlay. This fits the
|
| + // current content, but will need to be adjusted once there's more dynamic
|
| + // head-locked content that could be larger.
|
| headlocked_left_viewport_.reset(
|
| new gvr::BufferViewport(gvr_api_->CreateBufferViewport()));
|
| buffer_viewport_list_->GetBufferViewport(GVR_LEFT_EYE,
|
| headlocked_left_viewport_.get());
|
| headlocked_left_viewport_->SetSourceBufferIndex(kFrameHeadlockedBuffer);
|
| headlocked_left_viewport_->SetReprojection(GVR_REPROJECTION_NONE);
|
| + headlocked_left_viewport_->SetSourceFov(kHeadlockedBufferFov);
|
|
|
| headlocked_right_viewport_.reset(
|
| new gvr::BufferViewport(gvr_api_->CreateBufferViewport()));
|
| @@ -292,7 +327,10 @@ void VrShell::InitializeGl(JNIEnv* env,
|
| headlocked_right_viewport_.get());
|
| headlocked_right_viewport_->SetSourceBufferIndex(kFrameHeadlockedBuffer);
|
| headlocked_right_viewport_->SetReprojection(GVR_REPROJECTION_NONE);
|
| + headlocked_right_viewport_->SetSourceFov(kHeadlockedBufferFov);
|
|
|
| + // Save copies of the first two viewport items for use by WebVR, it
|
| + // sets its own UV bounds.
|
| webvr_left_viewport_.reset(
|
| new gvr::BufferViewport(gvr_api_->CreateBufferViewport()));
|
| buffer_viewport_list_->GetBufferViewport(GVR_LEFT_EYE,
|
| @@ -506,8 +544,24 @@ uint32_t GetPixelEncodedPoseIndex() {
|
| }
|
|
|
| void VrShell::DrawFrame(JNIEnv* env, const JavaParamRef<jobject>& obj) {
|
| + // Reset the viewport list to just the pair of viewports for the
|
| + // primary buffer each frame. Head-locked viewports get added by
|
| + // DrawVrShell if needed.
|
| buffer_viewport_list_->SetToRecommendedBufferViewports();
|
|
|
| + if (html_interface_->GetMode() == UiInterface::Mode::WEB_VR) {
|
| + // If needed, resize the primary buffer for use with WebVR.
|
| + if (render_size_primary_ != render_size_primary_webvr_) {
|
| + render_size_primary_ = render_size_primary_webvr_;
|
| + swap_chain_->ResizeBuffer(kFramePrimaryBuffer, render_size_primary_);
|
| + }
|
| + } else {
|
| + if (render_size_primary_ != render_size_primary_vrshell_) {
|
| + render_size_primary_ = render_size_primary_vrshell_;
|
| + swap_chain_->ResizeBuffer(kFramePrimaryBuffer, render_size_primary_);
|
| + }
|
| + }
|
| +
|
| gvr::Frame frame = swap_chain_->AcquireFrame();
|
| gvr::ClockTimePoint target_time = gvr::GvrApi::GetTimePointNow();
|
| target_time.monotonic_system_time_nanos += kPredictionTimeWithoutVsyncNanos;
|
| @@ -544,8 +598,8 @@ void VrShell::DrawFrame(JNIEnv* env, const JavaParamRef<jobject>& obj) {
|
| // buffering in the compositor and SurfaceTexture, we read the pose number
|
| // from a corner pixel. There's no point in doing this for legacy
|
| // distortion rendering since that doesn't need a pose, and reading back
|
| - // pixels is an expensive operation. TODO(klausw): stop doing this once we
|
| - // have working no-compositor rendering for WebVR.
|
| + // pixels is an expensive operation. TODO(klausw,crbug.com/655722): stop
|
| + // doing this once we have working no-compositor rendering for WebVR.
|
| if (gvr_api_->GetAsyncReprojectionEnabled()) {
|
| uint32_t webvr_pose_frame = GetPixelEncodedPoseIndex();
|
| // If we don't get a valid frame ID back we shouldn't attempt to reproject
|
| @@ -582,39 +636,69 @@ void VrShell::DrawVrShell(const gvr::Mat4f& head_pose,
|
| }
|
| }
|
|
|
| - bool not_web_vr = html_interface_->GetMode() != UiInterface::Mode::WEB_VR;
|
| + if (html_interface_->GetMode() == UiInterface::Mode::WEB_VR) {
|
| + // WebVR is incompatible with 3D world compositing since the
|
| + // depth buffer was already populated with unknown scaling - the
|
| + // WebVR app has full control over zNear/zFar. Just leave the
|
| + // existing content in place in the primary buffer without
|
| + // clearing. Currently, there aren't any world elements in WebVR
|
| + // mode, this will need further testing if those get added
|
| + // later.
|
| + } else {
|
| + // Non-WebVR mode, enable depth testing and clear the primary buffers.
|
| + glEnable(GL_CULL_FACE);
|
| + glEnable(GL_DEPTH_TEST);
|
| + glDepthMask(GL_TRUE);
|
|
|
| - glEnable(GL_CULL_FACE);
|
| - glEnable(GL_DEPTH_TEST);
|
| - glDepthMask(GL_TRUE);
|
| -
|
| - if (not_web_vr) {
|
| glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
|
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
| }
|
|
|
| - DrawUiView(&head_pose, world_elements);
|
| + if (!world_elements.empty()) {
|
| + DrawUiView(&head_pose, world_elements, render_size_primary_,
|
| + kViewportListPrimaryOffset);
|
| + }
|
|
|
| if (!head_locked_elements.empty()) {
|
| - // Switch to head-locked viewports.
|
| - size_t last_viewport = buffer_viewport_list_->GetSize();
|
| - buffer_viewport_list_->SetBufferViewport(last_viewport++,
|
| + // Add head-locked viewports. The list gets reset to just
|
| + // the recommended viewports (for the primary buffer) each frame.
|
| + buffer_viewport_list_->SetBufferViewport(
|
| + kViewportListHeadlockedOffset + GVR_LEFT_EYE,
|
| *headlocked_left_viewport_);
|
| - buffer_viewport_list_->SetBufferViewport(last_viewport++,
|
| + buffer_viewport_list_->SetBufferViewport(
|
| + kViewportListHeadlockedOffset + GVR_RIGHT_EYE,
|
| *headlocked_right_viewport_);
|
|
|
| // Bind the headlocked framebuffer.
|
| frame.BindBuffer(kFrameHeadlockedBuffer);
|
| glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
| - DrawUiView(nullptr, head_locked_elements);
|
| + DrawUiView(nullptr, head_locked_elements, render_size_headlocked_,
|
| + kViewportListHeadlockedOffset);
|
| }
|
| }
|
|
|
| +void VrShell::SetWebVRRenderSurfaceSize(int width, int height) {
|
| + render_size_primary_webvr_.width = width;
|
| + render_size_primary_webvr_.height = height;
|
| + // TODO(klausw,crbug.com/655722): set the WebVR render surface size here once
|
| + // we have that.
|
| +}
|
| +
|
| +gvr::Sizei VrShell::GetWebVRCompositorSurfaceSize() {
|
| + // This is a stopgap while we're using the WebVR compositor rendering path.
|
| + // TODO(klausw,crbug.com/655722): Remove this method and member once we're
|
| + // using a separate WebVR render surface.
|
| + return content_tex_pixels_for_webvr_;
|
| +}
|
| +
|
| +
|
| void VrShell::DrawUiView(const gvr::Mat4f* head_pose,
|
| - const std::vector<const ContentRectangle*>& elements) {
|
| + const std::vector<const ContentRectangle*>& elements,
|
| + const gvr::Sizei& render_size, int viewport_offset) {
|
| for (auto eye : {GVR_LEFT_EYE, GVR_RIGHT_EYE}) {
|
| - buffer_viewport_list_->GetBufferViewport(eye, buffer_viewport_.get());
|
| + buffer_viewport_list_->GetBufferViewport(
|
| + eye + viewport_offset, buffer_viewport_.get());
|
|
|
| gvr::Mat4f view_matrix = gvr_api_->GetEyeFromHeadMatrix(eye);
|
| if (head_pose != nullptr) {
|
| @@ -622,7 +706,7 @@ void VrShell::DrawUiView(const gvr::Mat4f* head_pose,
|
| }
|
|
|
| gvr::Recti pixel_rect =
|
| - CalculatePixelSpaceRect(render_size_, buffer_viewport_->GetSourceUv());
|
| + CalculatePixelSpaceRect(render_size, buffer_viewport_->GetSourceUv());
|
| glViewport(pixel_rect.left, pixel_rect.bottom,
|
| pixel_rect.right - pixel_rect.left,
|
| pixel_rect.top - pixel_rect.bottom);
|
| @@ -747,11 +831,13 @@ void VrShell::DrawWebVr() {
|
| glDisable(GL_BLEND);
|
| glDisable(GL_POLYGON_OFFSET_FILL);
|
|
|
| - glViewport(0, 0, render_size_.width, render_size_.height);
|
| - vr_shell_renderer_->GetWebVrRenderer()->Draw(content_texture_id_);
|
| + glViewport(0, 0, render_size_primary_.width, render_size_primary_.height);
|
| + vr_shell_renderer_->GetWebVrRenderer()->Draw(webvr_texture_id_);
|
|
|
| - buffer_viewport_list_->SetBufferViewport(0, *webvr_left_viewport_);
|
| - buffer_viewport_list_->SetBufferViewport(1, *webvr_right_viewport_);
|
| + buffer_viewport_list_->SetBufferViewport(GVR_LEFT_EYE,
|
| + *webvr_left_viewport_);
|
| + buffer_viewport_list_->SetBufferViewport(GVR_RIGHT_EYE,
|
| + *webvr_right_viewport_);
|
| }
|
|
|
| void VrShell::OnTriggerEvent(JNIEnv* env, const JavaParamRef<jobject>& obj) {
|
| @@ -830,11 +916,28 @@ void VrShell::ContentSurfaceChanged(JNIEnv* env,
|
| jint width,
|
| jint height,
|
| const JavaParamRef<jobject>& surface) {
|
| + // If we have a delegate, must trigger "ready" callback one time only.
|
| + // Do so the first time we got a nonzero size. (This assumes it doesn't
|
| + // change, but once we get resize ability we'll no longer need this hack.)
|
| + // TODO(klausw,crbug.com/655722): remove when we have surface support.
|
| + bool delegate_not_ready = delegate_ && !content_tex_pixels_for_webvr_.width;
|
| +
|
| content_compositor_->SurfaceChanged((int)width, (int)height, surface);
|
| + content_tex_pixels_for_webvr_.width = width;
|
| + content_tex_pixels_for_webvr_.height = height;
|
| float scale_factor = display::Screen::GetScreen()
|
| ->GetPrimaryDisplay().device_scale_factor();
|
| content_tex_width_ = width / scale_factor;
|
| content_tex_height_ = height / scale_factor;
|
| +
|
| + // TODO(klausw,crbug.com/655722): move this back to GvrInit once we have
|
| + // our own WebVR surface.
|
| + if (delegate_ && delegate_not_ready) {
|
| + main_thread_task_runner_->PostTask(
|
| + FROM_HERE, base::Bind(&device::GvrDeviceProvider::OnGvrDelegateReady,
|
| + delegate_->GetDeviceProvider(),
|
| + weak_ptr_factory_.GetWeakPtr()));
|
| + }
|
| }
|
|
|
| void VrShell::UiSurfaceChanged(JNIEnv* env,
|
|
|