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; |