Commit 8e41e5ab authored by mthiesse's avatar mthiesse Committed by Commit bot

Fix WebVR support for devices without Async Reprojection.

Adds a java Surface to render into for devices that don't support async reprojection.

BUG=674233

Review-Url: https://codereview.chromium.org/2616583002
Cr-Commit-Position: refs/heads/master@{#441443}
parent 1ccc5796
......@@ -9,6 +9,9 @@ import android.app.Activity;
import android.graphics.Point;
import android.os.StrictMode;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.FrameLayout;
......@@ -37,7 +40,7 @@ import org.chromium.ui.display.VirtualDisplayAndroid;
* This view extends from GvrLayout which wraps a GLSurfaceView that renders VR shell.
*/
@JNINamespace("vr_shell")
public class VrShellImpl extends GvrLayout implements VrShell {
public class VrShellImpl extends GvrLayout implements VrShell, SurfaceHolder.Callback {
private static final String TAG = "VrShellImpl";
// TODO(mthiesse): These values work well for Pixel/Pixel XL in VR, but we need to come up with
......@@ -63,7 +66,7 @@ public class VrShellImpl extends GvrLayout implements VrShell {
private long mNativeVrShell;
private FrameLayout mUiCVCContainer;
private FrameLayout mPresentationView;
private View mPresentationView;
// The tab that holds the main ContentViewCore.
private Tab mTab;
......@@ -77,6 +80,8 @@ public class VrShellImpl extends GvrLayout implements VrShell {
private ContentViewCore mUiCVC;
private VrWindowAndroid mUiVrWindowAndroid;
private boolean mReprojectedRendering;
public VrShellImpl(Activity activity) {
super(activity);
mActivity = activity;
......@@ -88,12 +93,23 @@ public class VrShellImpl extends GvrLayout implements VrShell {
};
addView(mUiCVCContainer, 0, new FrameLayout.LayoutParams(0, 0));
mPresentationView = new FrameLayout(mActivity);
setPresentationView(mPresentationView);
mReprojectedRendering = setAsyncReprojectionEnabled(true);
if (mReprojectedRendering) {
// No need render to a Surface if we're reprojected. We'll be rendering with surfaceless
// EGL.
mPresentationView = new FrameLayout(mActivity);
if (setAsyncReprojectionEnabled(true)) {
// Only enable sustained performance mode when Async reprojection decouples the app
// framerate from the display framerate.
AndroidCompat.setSustainedPerformanceMode(mActivity, true);
} else {
SurfaceView surfaceView = new SurfaceView(mActivity);
surfaceView.getHolder().addCallback(this);
mPresentationView = surfaceView;
}
setPresentationView(mPresentationView);
DisplayAndroid primaryDisplay = DisplayAndroid.getNonMultiDisplay(activity);
mContentVirtualDisplay = VirtualDisplayAndroid.createVirtualDisplay();
mContentVirtualDisplay.setTo(primaryDisplay);
......@@ -118,7 +134,7 @@ public class VrShellImpl extends GvrLayout implements VrShell {
mNativeVrShell = nativeInit(mContentCVC.getWebContents(),
mContentVrWindowAndroid.getNativePointer(), mUiContents,
mUiVrWindowAndroid.getNativePointer(), forWebVR, delegate,
getGvrApi().getNativeGvrContext());
getGvrApi().getNativeGvrContext(), mReprojectedRendering);
// Set the UI and content sizes before we load the UI.
setUiCssSize(DEFAULT_UI_WIDTH, DEFAULT_UI_HEIGHT, DEFAULT_DPR);
......@@ -277,9 +293,28 @@ public class VrShellImpl extends GvrLayout implements VrShell {
getUiLayout().setCloseButtonListener(runner);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
nativeSetSurface(mNativeVrShell, holder.getSurface());
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// No need to do anything here, we don't care about surface width/height.
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO(mthiesse): For now we don't need to handle this because we exit VR on activity pause
// (which destroys the surface). If in the future we don't destroy VR Shell on exiting,
// we will need to handle this, or at least properly handle surfaceCreated being called
// multiple times.
}
private native long nativeInit(WebContents contentWebContents,
long nativeContentWindowAndroid, WebContents uiWebContents, long nativeUiWindowAndroid,
boolean forWebVR, VrShellDelegate delegate, long gvrApi);
boolean forWebVR, VrShellDelegate delegate, long gvrApi, boolean reprojectedRendering);
private native void nativeSetSurface(long nativeVrShell, Surface surface);
private native void nativeLoadUIContent(long nativeVrShell);
private native void nativeDestroy(long nativeVrShell);
private native void nativeOnTriggerEvent(long nativeVrShell);
......
......@@ -72,8 +72,10 @@ if (current_cpu == "arm" || current_cpu == "arm64") {
"//ui/gl/init",
]
libs =
[ "//third_party/gvr-android-sdk/libgvr_shim_static_${current_cpu}.a" ]
libs = [
"//third_party/gvr-android-sdk/libgvr_shim_static_${current_cpu}.a",
"android",
]
configs += [ "//third_party/gvr-android-sdk:libgvr_config" ]
}
......
......@@ -4,6 +4,8 @@
#include "chrome/browser/android/vr_shell/vr_shell.h"
#include <android/native_window_jni.h>
#include "base/metrics/histogram_macros.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread.h"
......@@ -48,14 +50,16 @@ class GLThread : public base::Thread {
const base::WeakPtr<VrInputManager>& ui_input_manager,
scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner,
gvr_context* gvr_api,
bool initially_web_vr)
bool initially_web_vr,
bool reprojected_rendering)
: base::Thread("VrShellGL"),
weak_vr_shell_(weak_vr_shell),
content_input_manager_(content_input_manager),
ui_input_manager_(ui_input_manager),
main_thread_task_runner_(std::move(main_thread_task_runner)),
gvr_api_(gvr_api),
initially_web_vr_(initially_web_vr) {}
initially_web_vr_(initially_web_vr),
reprojected_rendering_(reprojected_rendering) {}
~GLThread() override {
Stop();
......@@ -70,11 +74,10 @@ class GLThread : public base::Thread {
std::move(ui_input_manager_),
std::move(main_thread_task_runner_),
gvr_api_,
initially_web_vr_));
initially_web_vr_,
reprojected_rendering_));
weak_vr_shell_gl_ = vr_shell_gl_->GetWeakPtr();
if (!vr_shell_gl_->Initialize()) {
vr_shell_gl_.reset();
}
vr_shell_gl_->Initialize();
}
void CleanUp() override {
vr_shell_gl_.reset();
......@@ -91,6 +94,7 @@ class GLThread : public base::Thread {
scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
gvr_context* gvr_api_;
bool initially_web_vr_;
bool reprojected_rendering_;
};
} // namespace
......@@ -103,7 +107,8 @@ VrShell::VrShell(JNIEnv* env,
ui::WindowAndroid* ui_window,
bool for_web_vr,
VrShellDelegate* delegate,
gvr_context* gvr_api)
gvr_context* gvr_api,
bool reprojected_rendering)
: WebContentsObserver(ui_contents),
main_contents_(main_contents),
content_compositor_(new VrCompositor(content_window, false)),
......@@ -112,6 +117,7 @@ VrShell::VrShell(JNIEnv* env,
delegate_(delegate),
metrics_helper_(new VrMetricsHelper(main_contents_)),
main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
reprojected_rendering_(reprojected_rendering),
weak_ptr_factory_(this) {
DCHECK(g_instance == nullptr);
g_instance = this;
......@@ -128,7 +134,8 @@ VrShell::VrShell(JNIEnv* env,
ui_input_manager_->GetWeakPtr(),
main_thread_task_runner_,
gvr_api,
for_web_vr));
for_web_vr,
reprojected_rendering_));
base::Thread::Options options(base::MessageLoop::TYPE_DEFAULT, 0);
options.priority = base::ThreadPriority::DISPLAY;
......@@ -215,6 +222,18 @@ void VrShell::OnResume(JNIEnv* env, const JavaParamRef<jobject>& obj) {
SetShowingOverscrollGlow(false);
}
void VrShell::SetSurface(JNIEnv* env,
const JavaParamRef<jobject>& obj,
const JavaParamRef<jobject>& surface) {
CHECK(!reprojected_rendering_);
GLThread* thread = static_cast<GLThread*>(gl_thread_.get());
gfx::AcceleratedWidget window =
ANativeWindow_fromSurface(base::android::AttachCurrentThread(), surface);
PostToGlThreadWhenReady(base::Bind(&VrShellGl::InitializeGl,
thread->GetVrShellGl(),
base::Unretained(window)));
}
void VrShell::SetShowingOverscrollGlow(bool showing_glow) {
main_contents_->GetRenderWidgetHostView()->SetShowingOverscrollGlow(
showing_glow);
......@@ -453,14 +472,14 @@ jlong Init(JNIEnv* env, const JavaParamRef<jobject>& obj,
const JavaParamRef<jobject>& ui_web_contents,
jlong ui_window_android, jboolean for_web_vr,
const base::android::JavaParamRef<jobject>& delegate,
jlong gvr_api) {
jlong gvr_api, jboolean reprojected_rendering) {
return reinterpret_cast<intptr_t>(new VrShell(
env, obj, content::WebContents::FromJavaWebContents(content_web_contents),
reinterpret_cast<ui::WindowAndroid*>(content_window_android),
content::WebContents::FromJavaWebContents(ui_web_contents),
reinterpret_cast<ui::WindowAndroid*>(ui_window_android),
for_web_vr, VrShellDelegate::GetNativeDelegate(env, delegate),
reinterpret_cast<gvr_context*>(gvr_api)));
reinterpret_cast<gvr_context*>(gvr_api), reprojected_rendering));
}
} // namespace vr_shell
......@@ -63,7 +63,8 @@ class VrShell : public device::GvrDelegate, content::WebContentsObserver {
ui::WindowAndroid* ui_window,
bool for_web_vr,
VrShellDelegate* delegate,
gvr_context* gvr_api);
gvr_context* gvr_api,
bool reprojected_rendering);
void LoadUIContent(JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj);
......@@ -74,6 +75,9 @@ class VrShell : public device::GvrDelegate, content::WebContentsObserver {
const base::android::JavaParamRef<jobject>& obj);
void OnResume(JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj);
void SetSurface(JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
const base::android::JavaParamRef<jobject>& surface);
void SetWebVrMode(JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
bool enabled);
......@@ -158,6 +162,7 @@ class VrShell : public device::GvrDelegate, content::WebContentsObserver {
scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
std::unique_ptr<base::Thread> gl_thread_;
bool reprojected_rendering_;
base::WeakPtrFactory<VrShell> weak_ptr_factory_;
......
......@@ -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"
......@@ -173,6 +174,10 @@ int64_t TimeInMicroseconds() {
std::chrono::steady_clock::now().time_since_epoch()).count();
}
void WaitForSwapAck(const base::Closure& callback, gfx::SwapResult result) {
callback.Run();
}
} // namespace
VrShellGl::VrShellGl(
......@@ -181,8 +186,10 @@ VrShellGl::VrShellGl(
const base::WeakPtr<VrInputManager>& ui_input_manager,
scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner,
gvr_context* gvr_api,
bool initially_web_vr)
bool initially_web_vr,
bool reprojected_rendering)
: web_vr_mode_(initially_web_vr),
surfaceless_rendering_(reprojected_rendering),
task_runner_(base::ThreadTaskRunnerHandle::Get()),
weak_vr_shell_(weak_vr_shell),
content_input_manager_(content_input_manager),
......@@ -196,48 +203,52 @@ VrShellGl::~VrShellGl() {
draw_task_.Cancel();
}
bool VrShellGl::Initialize() {
if (!InitializeGl()) return false;
void VrShellGl::Initialize() {
gvr::Mat4f identity;
SetIdentityM(identity);
webvr_head_pose_.resize(kPoseRingBufferSize, identity);
webvr_head_pose_valid_.resize(kPoseRingBufferSize, false);
draw_task_.Reset(base::Bind(&VrShellGl::DrawFrame, base::Unretained(this)));
scene_.reset(new UiScene);
InitializeRenderer();
ScheduleNextDrawFrame();
return true;
if (surfaceless_rendering_) {
// If we're rendering surfaceless, we'll never get a java surface to render
// into, so we can initialize GL right away.
InitializeGl(nullptr);
}
}
bool VrShellGl::InitializeGl() {
void VrShellGl::InitializeGl(gfx::AcceleratedWidget window) {
CHECK(!ready_to_draw_);
if (gl::GetGLImplementation() == gl::kGLImplementationNone &&
!gl::init::InitializeGLOneOff()) {
LOG(ERROR) << "gl::init::InitializeGLOneOff failed";
ForceExitVr();
return false;
return;
}
if (window) {
CHECK(!surfaceless_rendering_);
surface_ = gl::init::CreateViewGLSurface(window);
} else {
CHECK(surfaceless_rendering_);
surface_ = gl::init::CreateOffscreenGLSurface(gfx::Size());
}
surface_ = gl::init::CreateOffscreenGLSurface(gfx::Size());
if (!surface_.get()) {
LOG(ERROR) << "gl::init::CreateOffscreenGLSurface failed";
ForceExitVr();
return false;
return;
}
context_ = gl::init::CreateGLContext(nullptr, surface_.get(),
gl::GLContextAttribs());
if (!context_.get()) {
LOG(ERROR) << "gl::init::CreateGLContext failed";
ForceExitVr();
return false;
return;
}
if (!context_->MakeCurrent(surface_.get())) {
LOG(ERROR) << "gl::GLContext::MakeCurrent() failed";
ForceExitVr();
return false;
return;
}
// TODO(mthiesse): We don't appear to have a VSync provider ever here. This is
......@@ -273,7 +284,13 @@ bool VrShellGl::InitializeGl() {
&VrShell::SurfacesChanged, weak_vr_shell_,
content_surface_->j_surface().obj(),
ui_surface_->j_surface().obj()));
return true;
InitializeRenderer();
draw_task_.Reset(base::Bind(&VrShellGl::DrawFrame, base::Unretained(this)));
ScheduleNextDrawFrame();
ready_to_draw_ = true;
}
void VrShellGl::OnUIFrameAvailable() {
......@@ -386,7 +403,6 @@ void VrShellGl::InitializeRenderer() {
void VrShellGl::UpdateController(const gvr::Vec3f& forward_vector) {
controller_->UpdateState();
// Note that button up/down state is transient, so ButtonUpHappened only
// returns true for a single frame (and we're guaranteed not to miss it).
if (controller_->ButtonUpHappened(
......@@ -664,8 +680,19 @@ void VrShellGl::DrawFrame() {
frame.Unbind();
frame.Submit(*buffer_viewport_list_, head_pose);
// No need to SwapBuffers for an offscreen surface.
ScheduleNextDrawFrame();
// No need to swap buffers for surfaceless rendering.
if (surfaceless_rendering_) {
ScheduleNextDrawFrame();
return;
}
if (surface_->SupportsAsyncSwap()) {
surface_->SwapBuffersAsync(base::Bind(&WaitForSwapAck, base::Bind(
&VrShellGl::ScheduleNextDrawFrame, weak_ptr_factory_.GetWeakPtr())));
} else {
surface_->SwapBuffers();
ScheduleNextDrawFrame();
}
}
void VrShellGl::DrawVrShell(const gvr::Mat4f& head_pose,
......@@ -897,8 +924,10 @@ void VrShellGl::OnResume() {
gvr_api_->RefreshViewerProfile();
gvr_api_->ResumeTracking();
controller_->OnResume();
draw_task_.Reset(base::Bind(&VrShellGl::DrawFrame, base::Unretained(this)));
ScheduleNextDrawFrame();
if (ready_to_draw_) {
draw_task_.Reset(base::Bind(&VrShellGl::DrawFrame, base::Unretained(this)));
ScheduleNextDrawFrame();
}
}
void VrShellGl::SetWebVrMode(bool enabled) {
......
......@@ -57,10 +57,12 @@ class VrShellGl {
const base::WeakPtr<VrInputManager>& ui_input_manager,
scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner,
gvr_context* gvr_api,
bool initially_web_vr);
bool initially_web_vr,
bool reprojected_rendering);
~VrShellGl();
bool Initialize();
void Initialize();
void InitializeGl(gfx::AcceleratedWidget window);
void DrawFrame();
......@@ -84,7 +86,6 @@ class VrShellGl {
void UpdateScene(std::unique_ptr<base::ListValue> commands);
private:
bool InitializeGl();
void GvrInit(gvr_context* gvr_api);
void InitializeRenderer();
void DrawVrShell(const gvr::Mat4f& head_pose, gvr::Frame &frame);
......@@ -163,6 +164,8 @@ class VrShellGl {
std::vector<bool> webvr_head_pose_valid_;
int webvr_texture_id_ = 0;
bool web_vr_mode_;
bool ready_to_draw_ = false;
bool surfaceless_rendering_;
std::unique_ptr<VrController> controller_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment