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

Unified Diff: chrome/browser/android/vr_shell/vr_shell_gl.cc

Issue 2584343002: WIP: working copy-no-compositor path
Patch Set: StatTracker destructor, delete old magic numbers, mojo export Created 3 years, 11 months 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 side-by-side diff with in-line comments
Download patch
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 55a1c7ee9d4ff363953955bc80793b53791daf34..73291b9c5e3fd393cd210ce3e98f8a5284134cba 100644
--- a/chrome/browser/android/vr_shell/vr_shell_gl.cc
+++ b/chrome/browser/android/vr_shell/vr_shell_gl.cc
@@ -4,6 +4,7 @@
#include "chrome/browser/android/vr_shell/vr_shell_gl.h"
+#include "base/android/jni_android.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/threading/thread_task_runner_handle.h"
@@ -23,6 +24,8 @@
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_surface.h"
#include "ui/gl/init/gl_factory.h"
+#include "gpu/ipc/common/gpu_surface_tracker.h"
+#include "gpu/ipc/common/surface_handle.h"
namespace vr_shell {
@@ -32,6 +35,11 @@ static constexpr long kPredictionTimeWithoutVsyncNanos = 50000000;
static constexpr float kZNear = 0.1f;
static constexpr float kZFar = 1000.0f;
+#include <android/native_window.h>
+#include <android/native_window_jni.h>
+
+#include <iomanip>
+
// Screen angle in degrees. 0 = vertical, positive = top closer.
static constexpr float kDesktopScreenTiltDefault = 0;
@@ -81,10 +89,6 @@ static constexpr gvr::Rectf kHeadlockedBufferFov = {20.f, 20.f, 20.f, 20.f};
static constexpr int kViewportListPrimaryOffset = 0;
static constexpr int kViewportListHeadlockedOffset = 2;
-// 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}};
-
float Distance(const gvr::Vec3f& vec1, const gvr::Vec3f& vec2) {
float xdiff = (vec1.x - vec2.x);
float ydiff = (vec1.y - vec2.y);
@@ -141,39 +145,26 @@ enum class ViewerType {
VIEWER_TYPE_MAX,
};
-int GetPixelEncodedPoseIndexByte() {
- TRACE_EVENT0("gpu", "VrShellGl::GetPixelEncodedPoseIndex");
- // Read the pose index encoded in a bottom left pixel as color values.
- // See also third_party/WebKit/Source/modules/vr/VRDisplay.cpp which
- // encodes the pose index, and device/vr/android/gvr/gvr_device.cc
- // which tracks poses. Returns the low byte (0..255) if valid, or -1
- // if not valid due to bad magic number.
- uint8_t pixels[4];
- // Assume we're reading from the framebuffer we just wrote to.
- // That's true currently, we may need to use glReadBuffer(GL_BACK)
- // or equivalent if the rendering setup changes in the future.
- glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
-
- // Check for the magic number written by VRDevice.cpp on submit.
- // This helps avoid glitches from garbage data in the render
- // buffer that can appear during initialization or resizing. These
- // often appear as flashes of all-black or all-white pixels.
- if (pixels[1] == kWebVrPosePixelMagicNumbers[0] &&
- pixels[2] == kWebVrPosePixelMagicNumbers[1]) {
- // Pose is good.
- return pixels[0];
- }
- VLOG(1) << "WebVR: reject decoded pose index " << (int)pixels[0] <<
- ", bad magic number " << (int)pixels[1] << ", " << (int)pixels[2];
- return -1;
-}
+} // namespace
int64_t TimeInMicroseconds() {
return std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::steady_clock::now().time_since_epoch()).count();
}
-} // namespace
+uint32_t GetPixelEncodedPoseIndex() {
+ TRACE_EVENT0("gpu", "VrShell::GetPixelEncodedPoseIndex");
+ // Read the pose index encoded in a bottom left pixel as color values.
+ // See also third_party/WebKit/Source/modules/vr/VRDisplay.cpp which
+ // encodes the pose index, and device/vr/android/gvr/gvr_device.cc
+ // which tracks poses.
+ uint8_t pixels[4];
+ // Assume we're reading from the framebuffer we just wrote to.
+ // That's true currently, we may need to use glReadBuffer(GL_BACK)
+ // or equivalent if the rendering setup changes in the future.
+ glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+ return pixels[0] | (pixels[1] << 8) | (pixels[2] << 16);
+}
VrShellGl::VrShellGl(
const base::WeakPtr<VrShell>& weak_vr_shell,
@@ -202,13 +193,19 @@ bool VrShellGl::Initialize() {
gvr::Mat4f identity;
SetIdentityM(identity);
webvr_head_pose_.resize(kPoseRingBufferSize, identity);
- webvr_head_pose_valid_.resize(kPoseRingBufferSize, false);
+ webvr_time_frame_start_.resize(kPoseRingBufferSize, 0.0);
+ webvr_time_get_pose_.resize(kPoseRingBufferSize, 0.0);
+ webvr_time_got_pose_.resize(kPoseRingBufferSize, 0.0);
+ webvr_time_submit_.resize(kPoseRingBufferSize, 0.0);
+ webvr_time_surfaced_.resize(kPoseRingBufferSize, 0.0);
+ webvr_time_acquired_.resize(kPoseRingBufferSize, 0.0);
- draw_task_.Reset(base::Bind(&VrShellGl::DrawFrame, base::Unretained(this)));
+ draw_task_.Reset(base::Bind(&VrShellGl::DrawFrame, base::Unretained(this), 0));
scene_.reset(new UiScene);
InitializeRenderer();
+ VLOG(1) << __FUNCTION__ << ": Destructor for presenting delegate and its gvr_api";
ScheduleNextDrawFrame();
return true;
@@ -250,24 +247,36 @@ bool VrShellGl::InitializeGl() {
LOG(ERROR) << "No VSync Provider";
}
- 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_);
+
ui_surface_.reset(new gl::ScopedJavaSurface(ui_surface_texture_.get()));
content_surface_.reset(new gl::ScopedJavaSurface(
content_surface_texture_.get()));
+ webvr_surface_.reset(new gl::ScopedJavaSurface(
+ webvr_surface_texture_.get()));
+
ui_surface_texture_->SetFrameAvailableCallback(base::Bind(
&VrShellGl::OnUIFrameAvailable, weak_ptr_factory_.GetWeakPtr()));
content_surface_texture_->SetFrameAvailableCallback(base::Bind(
&VrShellGl::OnContentFrameAvailable, weak_ptr_factory_.GetWeakPtr()));
+ webvr_surface_texture_->SetFrameAvailableCallback(base::Bind(
+ &VrShellGl::OnWebVrFrameAvailable, weak_ptr_factory_.GetWeakPtr()));
content_surface_texture_->SetDefaultBufferSize(
content_tex_physical_size_.width, content_tex_physical_size_.height);
ui_surface_texture_->SetDefaultBufferSize(ui_tex_physical_size_.width,
ui_tex_physical_size_.height);
+ // Set a small default surface size for WebVR since we may not need it.
+ // Will be resized as needed.
+ webvr_surface_texture_->SetDefaultBufferSize(1, 1);
main_thread_task_runner_->PostTask(FROM_HERE, base::Bind(
&VrShell::SurfacesChanged, weak_vr_shell_,
@@ -284,6 +293,28 @@ void VrShellGl::OnContentFrameAvailable() {
content_surface_texture_->UpdateTexImage();
}
+void VrShellGl::OnWebVrFrameAvailable() {
+ if (!webvr_pending_poses_.size()) {
+ VLOG(2) << __FUNCTION__ << ": No pending pose, saving this frame for later";
+ ++webvr_already_available_frames_;
+ return;
+ }
+ uint32_t pose_index = webvr_pending_poses_.front();
+ webvr_pending_poses_.pop_front();
+ VLOG(2) << __FUNCTION__ << ": receiving WebVR frame " << pose_index;
+ webvr_surface_texture_->UpdateTexImage();
+ auto remaining_poses = webvr_pending_poses_.size();
+ if (remaining_poses > 0) {
+ VLOG(2) << __FUNCTION__ << ": still have " << remaining_poses << " pose(s) queued up.";
+ }
+ //long frameTimestamp = mWebVrSurfaceTexture.getTimestamp();
+ //if (frameTimestamp == 0) {
+ // Log.w(TAG, "Invalid timestamp for frame on WebVR waiting list. This should not happen.");
+ // return;
+ //}
+ DrawFrame(pose_index);
+}
+
void VrShellGl::GvrInit(gvr_context* gvr_api) {
gvr_api_ = gvr::GvrApi::WrapNonOwned(gvr_api);
controller_.reset(new VrController(gvr_api));
@@ -306,21 +337,12 @@ 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_;
- // Out of paranoia, explicitly reset the "pose valid" flags to false
- // from the GL thread. The constructor ran in the UI thread.
- // TODO(klausw,crbug.com/655722): remove this.
- webvr_head_pose_valid_.assign(kPoseRingBufferSize, false);
-
gvr_api_->InitializeGl();
std::vector<gvr::BufferSpec> specs;
// For kFramePrimaryBuffer (primary VrShell and WebVR content)
specs.push_back(gvr_api_->CreateBufferSpec());
render_size_primary_ = specs[kFramePrimaryBuffer].GetSize();
+ render_size_primary_vrshell_ = render_size_primary_;
// For kFrameHeadlockedBuffer (for WebVR insecure content warning).
// Set this up at fixed resolution, the (smaller) FOV gets set below.
@@ -400,6 +422,7 @@ void VrShellGl::UpdateController(const gvr::Vec3f& forward_vector) {
// Also send tap events for controller "touchpad click" events.
if (touch_pending_ || controller_->ButtonUpHappened(
gvr::ControllerButton::GVR_CONTROLLER_BUTTON_CLICK)) {
+ VLOG(1) << __FUNCTION__ << ": WebVR emulating Cardboard button";
touch_pending_ = false;
std::unique_ptr<WebGestureEvent> gesture(new WebGestureEvent());
gesture->sourceDevice = blink::WebGestureDeviceTouchpad;
@@ -565,6 +588,21 @@ void VrShellGl::SendEventsToTarget(InputTarget input_target,
}
}
+static double getMonotonicTimestampMs() {
+ // TODO(klausw): replace with TimeInMicroseconds()?
+#if defined(OS_ANDROID)
+ // Android surfacetexture timestamp compatible timer? See:
+ // http://androidxref.com/7.0.0_r1/xref/frameworks/native/libs/gui/Surface.cpp#370
+ // http://androidxref.com/7.0.0_r1/xref/frameworks/rs/rsCppUtils.h#162
+ struct timespec t;
+ t.tv_sec = t.tv_nsec = 0;
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ return t.tv_sec * 1e3 + t.tv_nsec * 1e-6;
+#else
+ return 0.0;
+#endif
+}
+
void VrShellGl::SendGesture(InputTarget input_target,
std::unique_ptr<blink::WebInputEvent> event) {
DCHECK(input_target != InputTarget::NONE);
@@ -577,45 +615,74 @@ void VrShellGl::SendGesture(InputTarget input_target,
base::Passed(std::move(event))));
}
-void VrShellGl::SetGvrPoseForWebVr(const gvr::Mat4f& pose, uint32_t pose_num) {
- webvr_head_pose_[pose_num % kPoseRingBufferSize] = pose;
- webvr_head_pose_valid_[pose_num % kPoseRingBufferSize] = true;
+void VrShellGl::SetWebVRGvrPose(
+ const gvr::Mat4f& pose, uint32_t pose_index, int64_t pose_time_nanos) {
+ webvr_head_pose_[pose_index % kPoseRingBufferSize] = pose;
+ webvr_time_submit_[pose_index % kPoseRingBufferSize] = 0.0;
}
-bool VrShellGl::WebVrPoseByteIsValid(int pose_index_byte) {
- if (pose_index_byte < 0) {
- return false;
- }
- if (!webvr_head_pose_valid_[pose_index_byte % kPoseRingBufferSize]) {
- VLOG(1) << "WebVR: reject decoded pose index " << pose_index_byte <<
- ", not a valid pose";
- return false;
- }
- return true;
-}
-
-void VrShellGl::DrawFrame() {
- TRACE_EVENT0("gpu", "VrShellGl::DrawFrame");
+void VrShellGl::DrawFrame(uint32_t pose_index) {
+ TRACE_EVENT1("gpu", "VrShellGl::DrawFrame", "frame", pose_index);
// 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();
- gvr::Frame frame = swap_chain_->AcquireFrame();
- gvr::ClockTimePoint target_time = gvr::GvrApi::GetTimePointNow();
- target_time.monotonic_system_time_nanos += kPredictionTimeWithoutVsyncNanos;
+ if (web_vr_mode_) {
+ if (!pose_index) {
+ LOG(INFO) << "klausw:DrawFrame: no pose index, not drawing WebVR.";
+ return;
+ }
- gvr::Mat4f head_pose =
- gvr_api_->GetHeadSpaceFromStartSpaceRotation(target_time);
+ webvr_time_surfaced_[pose_index % kPoseRingBufferSize] = getMonotonicTimestampMs();
- gvr::Vec3f position = GetTranslation(head_pose);
- if (position.x == 0.0f && position.y == 0.0f && position.z == 0.0f) {
- // This appears to be a 3DOF pose without a neck model. Add one.
- // The head pose has redundant data. Assume we're only using the
- // object_from_reference_matrix, we're not updating position_external.
- // TODO: Not sure what object_from_reference_matrix is. The new api removed
- // it. For now, removing it seems working fine.
- gvr_api_->ApplyNeckModel(head_pose, 1.0f);
+ // If needed, resize the primary buffer for use with WebVR.
+ if (render_size_primary_ != render_size_primary_webvr_) {
+ if (!render_size_primary_webvr_.width) {
+ VLOG(2) << "WebVR rendering size not known yet, dropping frame";
+ return;
+ }
+ VLOG(1) << "WebVR set size " << render_size_primary_webvr_.width << "x" << render_size_primary_webvr_.height;
+ render_size_primary_ = render_size_primary_webvr_;
+ swap_chain_->ResizeBuffer(kFramePrimaryBuffer, render_size_primary_);
+ }
+ } else {
+ if (render_size_primary_ != render_size_primary_vrshell_) {
+ VLOG(1) << "WebVR restore size " << render_size_primary_vrshell_.width << "x" << render_size_primary_vrshell_.height;
+ render_size_primary_ = render_size_primary_vrshell_;
+ swap_chain_->ResizeBuffer(kFramePrimaryBuffer, render_size_primary_);
+ }
+ }
+
+ gvr::Mat4f head_pose;
+ gvr::Frame frame = gvr::Frame(nullptr);
+ {
+ TRACE_EVENT0("gpu", "AcquireFrame");
+ frame = swap_chain_->AcquireFrame();
+ }
+
+ if (web_vr_mode_) {
+ int idx = pose_index % kPoseRingBufferSize;
+ webvr_time_acquired_[idx] = getMonotonicTimestampMs();
+ // TODO(klausw): report acquire time back to JS? If it blocks,
+ // completion time == VSYNC time or a close approximation which
+ // may be useful. But not helpful if it doesn't block.
+ head_pose = webvr_head_pose_[idx];
+ } else {
+ gvr::ClockTimePoint target_time = gvr::GvrApi::GetTimePointNow();
+ target_time.monotonic_system_time_nanos += kPredictionTimeWithoutVsyncNanos;
+
+ head_pose = gvr_api_->GetHeadSpaceFromStartSpaceRotation(target_time);
+
+ gvr::Vec3f position = GetTranslation(head_pose);
+ if (position.x == 0.0f && position.y == 0.0f && position.z == 0.0f) {
+ // This appears to be a 3DOF pose without a neck model. Add one.
+ // The head pose has redundant data. Assume we're only using the
+ // object_from_reference_matrix, we're not updating position_external.
+ // TODO: Not sure what object_from_reference_matrix is. The new api
+ // removed it. For now, removing it seems working fine.
+ gvr_api_->ApplyNeckModel(head_pose, 1.0f);
+ }
}
frame.BindBuffer(kFramePrimaryBuffer);
@@ -627,45 +694,86 @@ void VrShellGl::DrawFrame() {
UpdateController(GetForwardVector(head_pose));
if (web_vr_mode_) {
- DrawWebVr();
-
- // When using async reprojection, we need to know which pose was used in
- // the WebVR app for drawing this frame. Due to unknown amounts of
- // 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,crbug.com/655722): stop
- // doing this once we have working no-compositor rendering for WebVR.
- if (gvr_api_->GetAsyncReprojectionEnabled()) {
- int pose_index_byte = GetPixelEncodedPoseIndexByte();
- if (WebVrPoseByteIsValid(pose_index_byte)) {
- // We have a valid pose, use it for reprojection.
- webvr_left_viewport_->SetReprojection(GVR_REPROJECTION_FULL);
- webvr_right_viewport_->SetReprojection(GVR_REPROJECTION_FULL);
- head_pose = webvr_head_pose_[pose_index_byte % kPoseRingBufferSize];
- // We can't mark the used pose as invalid since unfortunately
- // we have to reuse them. The compositor will re-submit stale
- // frames on vsync, and we can't tell that this has happened
- // until we've read the pose index from it, and at that point
- // it's too late to skip rendering.
- } else {
- // If we don't get a valid frame ID back we shouldn't attempt
- // to reproject by an invalid matrix, so turn off reprojection
- // instead. Invalid poses can permanently break reprojection
- // for this GVR instance: http://crbug.com/667327
- webvr_left_viewport_->SetReprojection(GVR_REPROJECTION_NONE);
- webvr_right_viewport_->SetReprojection(GVR_REPROJECTION_NONE);
- }
+ DrawWebVr(pose_index);
+#define CHECK_FRAME_COUNTER_PIXEL_FOR_DEBUGGING 0
+#if CHECK_FRAME_COUNTER_PIXEL_FOR_DEBUGGING
+ uint32_t pixel_pose = GetPixelEncodedPoseIndex();
+ if (pixel_pose != pose_index) {
+ LOG(ERROR) << __FUNCTION__ << ": poses got out of sync, pixel=" << pixel_pose << " != pose_index=" << pose_index;
}
+#endif
}
DrawVrShell(head_pose, frame);
frame.Unbind();
+ glFlush();
frame.Submit(*buffer_viewport_list_, head_pose);
// No need to SwapBuffers for an offscreen surface.
- ScheduleNextDrawFrame();
+ // TODO(klausw): is this true? Test with async reprojection off.
+#if 0
+ if (web_vr_mode_ && !gvr_api_->GetAsyncReprojectionEnabled()) {
+ // WebVR uses RENDERMODE_WHEN_DIRTY for the Java glSurfaceView,
+ // and never actually marks frames as dirty. We need to manually
+ // swap buffers if not using reprojection since the GvrLayout
+ // won't do it for us.
+ eglSwapBuffers(eglGetDisplay(EGL_DEFAULT_DISPLAY),
+ eglGetCurrentSurface(EGL_DRAW));
+ }
+#endif
+
+ if (web_vr_mode_) {
+ double submit_time = webvr_time_submit_[pose_index % kPoseRingBufferSize];
+
+ double prev_submit_time = 0.0;
+ for (int i = 1; i < kPoseRingBufferSize; ++i) {
+ int offset = kPoseRingBufferSize - i;
+ prev_submit_time = webvr_time_submit_[
+ (pose_index + offset) % kPoseRingBufferSize];
+ if (prev_submit_time != 0.0)
+ break;
+ }
+
+ int frameI = 1;
+ if (prev_submit_time != 0.0) {
+ // Rounded integer "frames taken" assuming 60Hz base rate.
+ frameI = ((submit_time - prev_submit_time) * 60 / 1000 + 0.5);
+ if (!frameI) frameI = 1;
+ }
+
+ int idx = pose_index % kPoseRingBufferSize;
+ double frame_start_time = webvr_time_frame_start_[idx];
+ double get_pose_time = webvr_time_get_pose_[idx];
+ double got_pose_time = webvr_time_got_pose_[idx];
+ double surfaced_time = webvr_time_surfaced_[idx];
+ double acquired_time = webvr_time_acquired_[idx];
+ double drawn_time = getMonotonicTimestampMs();
+
+ LOG(INFO) << "timing for frame " << pose_index <<
+ ", frameI " << frameI <<
+ ", rAF " << std::fixed << std::setprecision(1) <<
+ (get_pose_time - frame_start_time) << " getPose " <<
+ (got_pose_time - get_pose_time) << " gotPose " <<
+ (submit_time - got_pose_time) << " submit " <<
+ (surfaced_time - submit_time) << " surfaced " <<
+ (acquired_time - surfaced_time) << " acquired " <<
+ (drawn_time - acquired_time) << " drawn ";
+
+ // TODO(klausw): can completion reporting be moved earlier? I
+ // tried doing so right after AcquireFrame, but then framerate was
+ // very wobbly. Try moving the callback after DrawWebVr +
+ // glFlush() (not finish) to see if that helps?
+ main_thread_task_runner_->PostTask(FROM_HERE, base::Bind(
+ &VrShell::OnWebVRFrameSubmitted, weak_vr_shell_,
+ webvr_surface_handle_,
+ pose_index,
+ acquired_time - submit_time));
+ } else {
+ // Only request a new scheduled frame in non-WebVR mode. In WebVR mode,
+ // the next frame will be drawn in response to SubmitFrame.
+ ScheduleNextDrawFrame();
+ }
}
void VrShellGl::DrawVrShell(const gvr::Mat4f& head_pose,
@@ -727,11 +835,152 @@ void VrShellGl::DrawVrShell(const gvr::Mat4f& head_pose,
}
}
-gvr::Sizei VrShellGl::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_physical_size_;
+void VrShellGl::GetWebVRSurfaceHandle(int32_t width, int32_t height, const device::mojom::VRDisplay::GetSurfaceHandleCallback& callback) {
+ VLOG(2) << __FUNCTION__ << ": size=" << width << "x" << height;
+
+ if (!webvr_surface_texture_.get()) {
+ // We can't set up a surface due to not having a SurfaceTexture.
+ VLOG(1) << __FUNCTION__ << ": Failed, don't have a SurfaceTexture";
+ callback.Run(0);
artem.bolgar 2017/02/14 05:04:24 You can't do this here. The callback may be execut
+ return;
+ }
+
+ if (webvr_surface_handle_) {
+ // We have a surface, resize if needed.
+ if (render_size_primary_webvr_.width == width && render_size_primary_webvr_.height == height) {
+ VLOG(1) << __FUNCTION__ << ": Ignoring redundant call, this matches the current size.";
+ } else {
+ render_size_primary_webvr_.width = width;
+ render_size_primary_webvr_.height = height;
+ // The size is a bit tricky to change after the fact, see
+ // SurfaceTexture.setDefaultBufferSize documentation:
+ //
+ // For OpenGL ES, the EGLSurface should be destroyed
+ // (via eglDestroySurface), made not-current (via
+ // eglMakeCurrent), and then recreated (via
+ // eglCreateWindowSurface) to ensure that the new
+ // default size has taken effect.
+ webvr_surface_texture_->SetDefaultBufferSize(width, height);
+ }
+ } else {
+ // Create a new surface.
+ render_size_primary_webvr_.width = width;
+ render_size_primary_webvr_.height = height;
+ webvr_surface_texture_->SetDefaultBufferSize(width, height);
+ // Assume we've already created the Surface.
+ //Java_VrShellImpl_createWebVrRenderSurface(env, j_vr_shell_.obj(), width, height);
+ SetWebVrSurface();
+ }
+ callback.Run(webvr_surface_handle_);
artem.bolgar 2017/02/14 05:04:24 You can't do this here. The callback may be execut
+}
+
+void VrShellGl::SetWebVrSurface() {
+ VLOG(1) << __FUNCTION__ << ": size=" << render_size_primary_webvr_.width << "x" << render_size_primary_webvr_.height << " webvr_texture_id_=" << webvr_texture_id_ << " webvr_surface_handle_=" << webvr_surface_handle_;
+
+ if (webvr_surface_handle_) {
+ VLOG(1) << __FUNCTION__ << ": ignoring redundant call, already have webvr_surface_handle_=" << webvr_surface_handle_;
+ return;
+ }
+
+ // Note: This ensures that any local references used by
+ // ANativeWindow_fromSurface are released immediately. This is needed as a
+ // workaround for https://code.google.com/p/android/issues/detail?id=68174
+ JNIEnv* env = base::android::AttachCurrentThread();
+ base::android::ScopedJavaLocalFrame scoped_local_reference_frame(env);
+ ANativeWindow* window = ANativeWindow_fromSurface(
+ env, webvr_surface_->j_surface().obj());
+
+ // This variant doesn't seem to be working - lookup fails?!
+ //ANativeWindow* window = webvr_surface_texture_->CreateSurface();
+
+ gpu::GpuSurfaceTracker* tracker = gpu::GpuSurfaceTracker::Get();
+ ANativeWindow_acquire(window);
+
+ // TODO(klausw): is setBuffersGeometry necessary>
+ ANativeWindow_setBuffersGeometry(window, render_size_primary_webvr_.width, render_size_primary_webvr_.height, WINDOW_FORMAT_RGBA_8888);
+
+ auto handle = tracker->AddSurfaceForNativeWidget(window);
+
+ tracker->RegisterViewSurface(handle, webvr_surface_->j_surface().obj());
+
+ webvr_surface_handle_ = handle;
+
+ // Now we're ready for child_process_service_impl.cc to fetch it via
+ // AIDL/Binder by ID.
+
+ ANativeWindow_release(window);
+
+ VLOG(1) << __FUNCTION__ << ": size=" << render_size_primary_webvr_.width << "x" << render_size_primary_webvr_.height << ", webvr_surface_handle_=" << handle;
+ // TODO(klausw): add cleanup to avoid leaking surfaces:
+ //
+ // The caller must release the underlying reference when done with the handle
+ // by calling ANativeWindow_release().
+}
+
+void VrShellGl::SubmitWebVRFrame(int32_t surface_handle, const device::mojom::VRPosePtr& pose) {
+ // uint32_t pose_index, double frameStart, double serviceStart, double getPose, double gotPose, double submit) {
+ uint32_t pose_index = pose->poseIndex;
+
+ if (surface_handle != webvr_surface_handle_) {
+ VLOG(2) << __FUNCTION__ << ": ignoring submitted frame for surface " << surface_handle << ", ours is " << webvr_surface_handle_;
+ }
+
+ TRACE_EVENT1("media", "klausw:VrShell SubmitWebVRFrame", "frame", pose_index);
+ VLOG(2) << __FUNCTION__ << ": frame " << pose_index;
+
+ webvr_last_submitted_ = pose_index;
+
+ int idx = pose_index % kPoseRingBufferSize;
+ double submit_time = getMonotonicTimestampMs();
+ webvr_time_submit_[idx] = submit_time;
+
+#if 0
+ // Align clocks. TODO(klausw): this assumes submit times being equal, this ignores RPC lag.
+ double submitTs = pose->ts_submit;
+ auto fromJS = [=](double t) { return t - submitTs + submit_time; };
+ webvr_time_frame_start_[idx] = fromJS(pose->ts_frameStart);
+ webvr_time_get_pose_[idx] = fromJS(pose->ts_getPose);
+ webvr_time_got_pose_[idx] = fromJS(pose->ts_gotPose);
+#else
+ webvr_time_frame_start_[idx] = pose->ts_frameStart;
+ webvr_time_get_pose_[idx] = pose->ts_getPose;
+ webvr_time_got_pose_[idx] = pose->ts_gotPose;
+#endif
+
+ //Java_VrShellImpl_expectWebVrFrame(env, j_vr_shell_.obj(), static_cast<jlong>(pose_index));
+ VLOG(1) << __FUNCTION__ << ": expecting WebVR frame " << pose_index;
+ webvr_pending_poses_.push_back(pose_index);
+ if (webvr_already_available_frames_ > 0) {
+ // If the "frame available" was already triggered, draw now.
+ VLOG(2) << __FUNCTION__ << ": Drawing saved frame now";
+ --webvr_already_available_frames_;
+ OnWebVrFrameAvailable();
+ }
+
+ {
+ TRACE_EVENT1("gpu", "glFinish", "before frame", pose_index);
+ // This is a load-bearing glFinish. I'm not entirely sure what's
+ // going on since we haven't actually emitted any GL commands on
+ // this context since the glFlush at the end of the previous
+ // frame, but this measurably reduces stalls in AcquireFrame and
+ // steadies the framerate, at the cost of reducing throughput. It
+ // effectively aligns rAF calls to be in sync with frame
+ // completion.
+ //
+ // Without the glFinish here, rAF calls stay aligned to vsync,
+ // with dropped frames to catch up as needed. This looks jankier.
+ //
+ // Putting the glFinish at the end of vr_shell's DrawFrame causes
+ // a larger latency gap than doing it here.
+ //
+ // TODO(klausw): try adjusting rAF timing offsets to keep timing
+ // steady? Or is it possible to tweak pose prediction to handle
+ // this better? May need cooperation from the JS app to handle
+ // uneven timing.
+ //
+ //if (pose_index % 20 >= 10)
+ glFinish();
+ }
}
void VrShellGl::DrawUiView(const gvr::Mat4f* head_pose,
@@ -863,8 +1112,8 @@ void VrShellGl::DrawCursor(const gvr::Mat4f& render_matrix) {
}
}
-void VrShellGl::DrawWebVr() {
- TRACE_EVENT0("gpu", "VrShellGl::DrawWebVr");
+void VrShellGl::DrawWebVr(uint32_t pose_index) {
+ TRACE_EVENT1("gpu", "VrShellGl::DrawWebVr", "frame", pose_index);
// Don't need face culling, depth testing, blending, etc. Turn it all off.
glDisable(GL_CULL_FACE);
glDepthMask(GL_FALSE);
@@ -876,6 +1125,21 @@ void VrShellGl::DrawWebVr() {
glViewport(0, 0, render_size_primary_.width, render_size_primary_.height);
vr_shell_renderer_->GetWebVrRenderer()->Draw(webvr_texture_id_);
+ if (!webvr_texture_bounds_need_update_at_.empty()) {
+ uint32_t next_at = webvr_texture_bounds_need_update_at_.front();
+ VLOG(2) << __FUNCTION__ << ": bounds update at " << next_at;
+ if (next_at <= pose_index && pose_index - next_at < 0x40000000) {
+ auto left_bounds = webvr_texture_bounds_left_.front();
+ auto right_bounds = webvr_texture_bounds_right_.front();
+ webvr_texture_bounds_need_update_at_.pop_front();
+ webvr_texture_bounds_left_.pop_front();
+ webvr_texture_bounds_right_.pop_front();
+ VLOG(2) << __FUNCTION__ << ": Update texture bounds, left l=" << left_bounds.left << ",r=" << left_bounds.right << ",t=" << left_bounds.top << ",b=" << left_bounds.bottom;
+ webvr_left_viewport_->SetSourceUv(left_bounds);
+ webvr_right_viewport_->SetSourceUv(right_bounds);
+ }
+ }
+
buffer_viewport_list_->SetBufferViewport(GVR_LEFT_EYE,
*webvr_left_viewport_);
buffer_viewport_list_->SetBufferViewport(GVR_RIGHT_EYE,
@@ -897,18 +1161,22 @@ void VrShellGl::OnResume() {
gvr_api_->RefreshViewerProfile();
gvr_api_->ResumeTracking();
controller_->OnResume();
- draw_task_.Reset(base::Bind(&VrShellGl::DrawFrame, base::Unretained(this)));
+ draw_task_.Reset(base::Bind(&VrShellGl::DrawFrame, base::Unretained(this), 0));
ScheduleNextDrawFrame();
}
void VrShellGl::SetWebVrMode(bool enabled) {
+ VLOG(1) << __FUNCTION__ << ": enabled=" << enabled;
web_vr_mode_ = enabled;
}
-void VrShellGl::UpdateWebVRTextureBounds(const gvr::Rectf& left_bounds,
+void VrShellGl::UpdateWebVRTextureBounds(uint32_t for_pose_index,
+ const gvr::Rectf& left_bounds,
const gvr::Rectf& right_bounds) {
- webvr_left_viewport_->SetSourceUv(left_bounds);
- webvr_right_viewport_->SetSourceUv(right_bounds);
+ VLOG(2) << __FUNCTION__ << ": for_pose_index=" << for_pose_index << " left_bounds l=" << left_bounds.left << ",r=" << left_bounds.right << ",t=" << left_bounds.top << ",b=" << left_bounds.bottom;
+ webvr_texture_bounds_need_update_at_.push_back(for_pose_index);
+ webvr_texture_bounds_left_.push_back(left_bounds);
+ webvr_texture_bounds_right_.push_back(right_bounds);
}
gvr::GvrApi* VrShellGl::gvr_api() {

Powered by Google App Engine
This is Rietveld 408576698