| Index: chrome/browser/android/vr_shell/vr_shell_gl.cc
|
| diff --git a/chrome/browser/android/vr_shell/vr_shell_gl.cc b/chrome/browser/android/vr_shell/vr_shell_gl.cc
|
| index b7e699248d55de307307762a6a2856de73e20b19..41c83b445673c2d7ec18772da9627b22b29b0aa2 100644
|
| --- a/chrome/browser/android/vr_shell/vr_shell_gl.cc
|
| +++ b/chrome/browser/android/vr_shell/vr_shell_gl.cc
|
| @@ -30,6 +30,8 @@
|
| #include "ui/gl/gl_surface.h"
|
| #include "ui/gl/init/gl_factory.h"
|
|
|
| +#include "gpu/ipc/common/gpu_surface_tracker.h"
|
| +
|
| namespace vr_shell {
|
|
|
| namespace {
|
| @@ -83,9 +85,11 @@ static constexpr int kViewportListHeadlockedOffset = 2;
|
| // 2-3 frames.
|
| static constexpr unsigned kPoseRingBufferSize = 8;
|
|
|
| +#if 0
|
| // Magic numbers used to mark valid pose index values encoded in frame
|
| // data. Must match the magic numbers used in blink's VRDisplay.cpp.
|
| static constexpr std::array<uint8_t, 2> kWebVrPosePixelMagicNumbers{{42, 142}};
|
| +#endif
|
|
|
| float Distance(const gvr::Vec3f& vec1, const gvr::Vec3f& vec2) {
|
| float xdiff = (vec1.x - vec2.x);
|
| @@ -235,22 +239,35 @@ void VrShellGl::InitializeGl(gfx::AcceleratedWidget window) {
|
| return;
|
| }
|
|
|
| - unsigned int textures[2];
|
| - glGenTextures(2, textures);
|
| + unsigned int textures[3];
|
| + glGenTextures(3, textures);
|
| ui_texture_id_ = textures[0];
|
| content_texture_id_ = textures[1];
|
| + webvr_texture_id_ = textures[2];
|
| ui_surface_texture_ = gl::SurfaceTexture::Create(ui_texture_id_);
|
| content_surface_texture_ = gl::SurfaceTexture::Create(content_texture_id_);
|
| + webvr_surface_texture_ = gl::SurfaceTexture::Create(webvr_texture_id_);
|
| CreateUiSurface();
|
| CreateContentSurface();
|
| + CreateWebVRSurface();
|
| ui_surface_texture_->SetFrameAvailableCallback(base::Bind(
|
| &VrShellGl::OnUIFrameAvailable, weak_ptr_factory_.GetWeakPtr()));
|
| content_surface_texture_->SetFrameAvailableCallback(base::Bind(
|
| &VrShellGl::OnContentFrameAvailable, weak_ptr_factory_.GetWeakPtr()));
|
| - content_surface_texture_->SetDefaultBufferSize(
|
| - content_tex_physical_size_.width, content_tex_physical_size_.height);
|
| + webvr_surface_texture_->SetFrameAvailableCallback(base::Bind(
|
| + &VrShellGl::OnWebVRFrameAvailable, weak_ptr_factory_.GetWeakPtr()));
|
| ui_surface_texture_->SetDefaultBufferSize(ui_tex_physical_size_.width,
|
| ui_tex_physical_size_.height);
|
| + content_surface_texture_->SetDefaultBufferSize(
|
| + content_tex_physical_size_.width, content_tex_physical_size_.height);
|
| + VLOG(1) << __FUNCTION__ << ";;; content_tex_physical_size=" <<
|
| + content_tex_physical_size_.width << "x" <<
|
| + content_tex_physical_size_.height;
|
| + VLOG(1) << __FUNCTION__ << ";;; render_size_primary=" <<
|
| + render_size_primary_.width << "x" << render_size_primary_.height;
|
| + webvr_surface_texture_->SetDefaultBufferSize(
|
| + //content_tex_physical_size_.width, content_tex_physical_size_.height);
|
| + 2560, 1440); // FIXME!
|
| InitializeRenderer();
|
|
|
| vsync_task_.Reset(base::Bind(&VrShellGl::OnVSync, base::Unretained(this)));
|
| @@ -275,16 +292,78 @@ void VrShellGl::CreateUiSurface() {
|
| ui_surface_->j_surface().obj()));
|
| }
|
|
|
| +void VrShellGl::CreateWebVRSurface() {
|
| + ANativeWindow* window = webvr_surface_texture_->CreateSurface();
|
| + gpu::GpuSurfaceTracker* tracker = gpu::GpuSurfaceTracker::Get();
|
| + ANativeWindow_acquire(window);
|
| + // setBuffersGeometry seems optional, it uses the SurfaceTexture's
|
| + // default size by default?
|
| + // ANativeWindow_setBuffersGeometry(
|
| + // window, 2048, 1024, WINDOW_FORMAT_RGBA_8888); // FIXME!
|
| + webvr_surface_handle_ = tracker->AddSurfaceForNativeWidget(window);
|
| + VLOG(1) << __FUNCTION__ << ";;;: webvr_surface_handle_=" <<
|
| + webvr_surface_handle_;
|
| +
|
| + webvr_surface_ =
|
| + base::MakeUnique<gl::ScopedJavaSurface>(webvr_surface_texture_.get());
|
| + tracker->RegisterViewSurface(
|
| + webvr_surface_handle_, webvr_surface_->j_surface().obj());
|
| + // When should this be released? Does registering it keep it alive?
|
| + ANativeWindow_release(window);
|
| +
|
| + main_thread_task_runner_->PostTask(
|
| + FROM_HERE, base::Bind(&VrShell::WebVRSurfaceChanged, weak_vr_shell_,
|
| + webvr_surface_handle_));
|
| +}
|
| +
|
| void VrShellGl::OnUIFrameAvailable() {
|
| + VLOG(1) << __FUNCTION__ << ";;; UI UpdateTexImage start";
|
| ui_surface_texture_->UpdateTexImage();
|
| + VLOG(1) << __FUNCTION__ << ";;; UI UpdateTexImage end";
|
| }
|
|
|
| void VrShellGl::OnContentFrameAvailable() {
|
| + VLOG(1) << __FUNCTION__ << ";;; Content UpdateTexImage start";
|
| content_surface_texture_->UpdateTexImage();
|
| received_frame_ = true;
|
| + VLOG(1) << __FUNCTION__ << ";;; Content UpdateTexImage end";
|
| }
|
|
|
| +void VrShellGl::OnWebVRFrameAvailable() {
|
| + VLOG(1) << __FUNCTION__ << ";;; pending count=" << pending_frames_.size();
|
| + // A "while" loop here is a bad idea. It's legal to call
|
| + // UpdateTexImage repeatedly even if no frames are available, but
|
| + // that does *not* wait for a new frame, it just reuses the most
|
| + // recent one. That would mess up the count.
|
| + if (pending_frames_.empty()) {
|
| + VLOG(1) << __FUNCTION__ << ";;; no pending frames? Please retry! premature_received_frames " << premature_received_frames_ << " => " << (premature_received_frames_ + 1);
|
| + ++premature_received_frames_;
|
| + return;
|
| + }
|
| +
|
| + VLOG(1) << __FUNCTION__ << ";;; requested_frames " << requested_frames_ << " => " << (requested_frames_ - 1);
|
| + --requested_frames_;
|
| + VLOG(1) << __FUNCTION__ << ";;; WebVR UpdateTexImage start";
|
| + webvr_surface_texture_->UpdateTexImage();
|
| + int frame_index = pending_frames_.front();
|
| + pending_frames_.pop();
|
| + VLOG(1) << __FUNCTION__ << ";;; WebVR UpdateTexImage end, got frame=" << frame_index;
|
| + DrawFrame(frame_index);
|
| + main_thread_task_runner_->PostTask(
|
| + FROM_HERE, base::Bind(&VrShell::WebVRFrameCompleted, weak_vr_shell_,
|
| + frame_index));
|
| +
|
| + if (pending_frames_.empty() && requested_frames_ > 0) {
|
| + // Heuristic got confused? TODO(klausw): do proper SubmitFrame callback?
|
| + VLOG(1) << __FUNCTION__ << ";;; Resetting requested_frames " << requested_frames_ << " => " << 0;
|
| + requested_frames_ = 0;
|
| + }
|
| +}
|
| +
|
| +#if 0
|
| bool VrShellGl::GetPixelEncodedFrameIndex(uint16_t* frame_index) {
|
| + //*frame_index = 1;
|
| + //return true;
|
| TRACE_EVENT0("gpu", "VrShellGl::GetPixelEncodedFrameIndex");
|
| if (!received_frame_) {
|
| if (last_frame_index_ == (uint16_t)-1)
|
| @@ -321,6 +400,7 @@ bool VrShellGl::GetPixelEncodedFrameIndex(uint16_t* frame_index) {
|
| << static_cast<int>(pixels[2]);
|
| return false;
|
| }
|
| +#endif
|
|
|
| void VrShellGl::GvrInit(gvr_context* gvr_api) {
|
| gvr_api_ = gvr::GvrApi::WrapNonOwned(gvr_api);
|
| @@ -344,12 +424,6 @@ void VrShellGl::GvrInit(gvr_context* gvr_api) {
|
| }
|
|
|
| void VrShellGl::InitializeRenderer() {
|
| - // 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();
|
| webvr_head_pose_.assign(kPoseRingBufferSize,
|
| gvr_api_->GetHeadSpaceFromStartSpaceRotation(
|
| @@ -630,7 +704,7 @@ void VrShellGl::SendGesture(InputTarget input_target,
|
| base::Bind(target, weak_vr_shell_, base::Passed(std::move(event))));
|
| }
|
|
|
| -void VrShellGl::DrawFrame() {
|
| +void VrShellGl::DrawFrame(int frame_index) {
|
| TRACE_EVENT0("gpu", "VrShellGl::DrawFrame");
|
|
|
| // Reset the viewport list to just the pair of viewports for the
|
| @@ -638,7 +712,9 @@ void VrShellGl::DrawFrame() {
|
| // DrawVrShell if needed.
|
| buffer_viewport_list_->SetToRecommendedBufferViewports();
|
|
|
| + TRACE_EVENT_BEGIN0("gpu", "VrShellGl::AcquireFrame");
|
| gvr::Frame frame = swap_chain_->AcquireFrame();
|
| + TRACE_EVENT_END0("gpu", "VrShellGl::AcquireFrame");
|
| if (!frame.is_valid()) {
|
| return;
|
| }
|
| @@ -647,7 +723,6 @@ void VrShellGl::DrawFrame() {
|
| DrawWebVr();
|
| }
|
|
|
| - uint16_t frame_index;
|
| gvr::Mat4f head_pose;
|
|
|
| // When using async reprojection, we need to know which pose was used in
|
| @@ -657,8 +732,7 @@ void VrShellGl::DrawFrame() {
|
| // distortion rendering since that doesn't need a pose, and reading back
|
| // pixels is an expensive operation. TODO(klausw,crbug.com/655722): stop
|
| // doing this once we have working no-compositor rendering for WebVR.
|
| - if (web_vr_mode_ && gvr_api_->GetAsyncReprojectionEnabled() &&
|
| - GetPixelEncodedFrameIndex(&frame_index)) {
|
| + if (web_vr_mode_ && gvr_api_->GetAsyncReprojectionEnabled()) {
|
| static_assert(!((kPoseRingBufferSize - 1) & kPoseRingBufferSize),
|
| "kPoseRingBufferSize must be a power of 2");
|
| head_pose = webvr_head_pose_[frame_index % kPoseRingBufferSize];
|
| @@ -671,6 +745,7 @@ void VrShellGl::DrawFrame() {
|
| "than half of frame_index_ range.");
|
| while (!pending_bounds_.empty()) {
|
| uint16_t index = pending_bounds_.front().first;
|
| + VLOG(1) << __FUNCTION__ << ";;; new bounds, index=" << index;
|
| // If index is less than the frame_index it's possible we've wrapped, so
|
| // we extend the range and 'un-wrap' to account for this.
|
| if (index < frame_index)
|
| @@ -713,7 +788,9 @@ void VrShellGl::DrawFrame() {
|
|
|
| UpdateController(GetForwardVector(head_pose));
|
|
|
| - DrawVrShell(head_pose, frame);
|
| + // Drawing VrShell causes GL error 0x501 GL_INVALID_VALUE while in
|
| + // WebVR mode. FIXME.
|
| + if (!web_vr_mode_) DrawVrShell(head_pose, frame);
|
|
|
| frame.Unbind();
|
| frame.Submit(*buffer_viewport_list_, head_pose);
|
| @@ -725,6 +802,20 @@ void VrShellGl::DrawFrame() {
|
| }
|
| }
|
|
|
| +void VrShellGl::ScheduleWebVRFrame(int frame_index) {
|
| + VLOG(1) << __FUNCTION__ << ";;; schedule frame=" << frame_index;
|
| + pending_frames_.emplace(frame_index);
|
| +}
|
| +
|
| +void VrShellGl::DropWebVRFrame(int frame_index) {
|
| + VLOG(1) << __FUNCTION__ << ";;; drop frame=" << frame_index;
|
| + if (requested_frames_ > 0) {
|
| + VLOG(1) << __FUNCTION__ << ";;; requested_frames " << requested_frames_ <<
|
| + " => " << (requested_frames_ - 1);
|
| + --requested_frames_;
|
| + }
|
| +}
|
| +
|
| void VrShellGl::DrawVrShell(const gvr::Mat4f& head_pose, gvr::Frame& frame) {
|
| TRACE_EVENT0("gpu", "VrShellGl::DrawVrShell");
|
| std::vector<const ContentRectangle*> head_locked_elements;
|
| @@ -1029,6 +1120,11 @@ void VrShellGl::SetWebVrMode(bool enabled) {
|
| void VrShellGl::UpdateWebVRTextureBounds(int16_t frame_index,
|
| const gvr::Rectf& left_bounds,
|
| const gvr::Rectf& right_bounds) {
|
| + VLOG(1) << __FUNCTION__ << ";;; frame_index=" << frame_index <<
|
| + " left=" << left_bounds.left << "," << left_bounds.bottom <<
|
| + "," << left_bounds.right << "," << left_bounds.top <<
|
| + " right=" << right_bounds.left << "," << right_bounds.bottom <<
|
| + "," << right_bounds.right << "," << right_bounds.top;
|
| if (frame_index < 0) {
|
| webvr_left_viewport_->SetSourceUv(left_bounds);
|
| webvr_right_viewport_->SetSourceUv(right_bounds);
|
| @@ -1068,6 +1164,14 @@ base::WeakPtr<VrShellGl> VrShellGl::GetWeakPtr() {
|
| }
|
|
|
| void VrShellGl::OnVSync() {
|
| + while (premature_received_frames_ > 0) {
|
| + VLOG(1) << __FUNCTION__ << ";;; Retrying premature received frame " <<
|
| + premature_received_frames_ << " => " <<
|
| + (premature_received_frames_ - 1);
|
| + --premature_received_frames_;
|
| + OnWebVRFrameAvailable();
|
| + }
|
| +
|
| base::TimeTicks now = base::TimeTicks::Now();
|
| base::TimeTicks target;
|
|
|
| @@ -1081,13 +1185,24 @@ void VrShellGl::OnVSync() {
|
| target - now);
|
|
|
| base::TimeDelta time = intervals * vsync_interval_;
|
| + // Send a small rate of vsyncs even while backlogged to avoid
|
| + // locking up when the rAF loop doesn't submit a frame.
|
| + static int skip_counter = 0;
|
| if (!callback_.is_null()) {
|
| - SendVSync(time, base::ResetAndReturn(&callback_));
|
| + if (requested_frames_ < 1 || (++skip_counter % 30) == 0) {
|
| + VLOG(1) << __FUNCTION__ << ";;; vsync B, interval=" << vsync_interval_;
|
| + SendVSync(time, base::ResetAndReturn(&callback_));
|
| + } else {
|
| + VLOG(1) << __FUNCTION__ << ";;; Skip vsync B, already have frame requested";
|
| + return;
|
| + }
|
| } else {
|
| pending_vsync_ = true;
|
| pending_time_ = time;
|
| }
|
| - DrawFrame();
|
| + if (!web_vr_mode_) {
|
| + DrawFrame(-1);
|
| + }
|
| }
|
|
|
| void VrShellGl::OnRequest(device::mojom::VRVSyncProviderRequest request) {
|
| @@ -1107,6 +1222,7 @@ void VrShellGl::GetVSync(const GetVSyncCallback& callback) {
|
| return;
|
| }
|
| pending_vsync_ = false;
|
| + VLOG(1) << __FUNCTION__ << ";;; vsync A, pending time=" << pending_time_;
|
| SendVSync(pending_time_, callback);
|
| }
|
|
|
| @@ -1116,6 +1232,7 @@ void VrShellGl::UpdateVSyncInterval(int64_t timebase_nanos,
|
| vsync_timebase_ += base::TimeDelta::FromMicroseconds(timebase_nanos / 1000);
|
| vsync_interval_ = base::TimeDelta::FromSecondsD(interval_seconds);
|
| vsync_task_.Reset(base::Bind(&VrShellGl::OnVSync, base::Unretained(this)));
|
| + VLOG(1) << __FUNCTION__ << ";;; vsync_interval=" << vsync_interval_;
|
| OnVSync();
|
| }
|
|
|
| @@ -1132,7 +1249,12 @@ void VrShellGl::SendVSync(base::TimeDelta time,
|
| const GetVSyncCallback& callback) {
|
| TRACE_EVENT0("input", "VrShellGl::SendVSync");
|
|
|
| + VLOG(1) << __FUNCTION__ << ";;; requested_frames " << requested_frames_ <<
|
| + " => " << (requested_frames_ + 1);
|
| + ++requested_frames_;
|
| +
|
| uint8_t frame_index = frame_index_++;
|
| + VLOG(1) << __FUNCTION__ << ";;; vsync for frame=" << (int)frame_index;
|
|
|
| gvr::ClockTimePoint target_time = gvr::GvrApi::GetTimePointNow();
|
| target_time.monotonic_system_time_nanos += kPredictionTimeWithoutVsyncNanos;
|
|
|