diff --git a/app/src/common/nativeactivity/org/mozilla/vrbrowser/PlatformActivity.java b/app/src/common/nativeactivity/org/mozilla/vrbrowser/PlatformActivity.java index 6037740947..23fafa42b6 100644 --- a/app/src/common/nativeactivity/org/mozilla/vrbrowser/PlatformActivity.java +++ b/app/src/common/nativeactivity/org/mozilla/vrbrowser/PlatformActivity.java @@ -6,36 +6,18 @@ package org.mozilla.vrbrowser; import android.app.NativeActivity; -import android.graphics.Color; import android.os.Bundle; import android.util.Log; -import android.view.SurfaceView; -import android.view.View; -import android.widget.FrameLayout; public class PlatformActivity extends NativeActivity { static String LOGTAG = "VRBrowser"; - private FrameLayout mFrameLayout; @Override protected void onCreate(Bundle savedInstanceState) { Log.e(LOGTAG,"in onCreate"); super.onCreate(savedInstanceState); - - getWindow().takeSurface(null); getWindow().takeInputQueue(null); - - mFrameLayout = new FrameLayout(this); - SurfaceView surfaceView = new SurfaceView(this); - surfaceView.setClickable(true); - surfaceView.getHolder().addCallback(this); - surfaceView.setZOrderOnTop(true); - mFrameLayout.addView(surfaceView, new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)); - - setContentView(mFrameLayout); } - protected void addWidget(View aView, int aWidth, int aHeight) { - mFrameLayout.addView(aView, 0, new FrameLayout.LayoutParams(aWidth, aHeight)); - } + protected native void queueRunnable(Runnable aRunnable); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java b/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java index 7d99379fe0..b796f38650 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java @@ -8,12 +8,16 @@ import android.content.Intent; import android.graphics.SurfaceTexture; import android.net.Uri; +import android.opengl.GLES11Ext; +import android.opengl.GLES20; import android.os.Bundle; import android.support.annotation.Keep; import android.util.Log; import android.view.View; +import android.widget.FrameLayout; import org.mozilla.gecko.GeckoSession; +import org.mozilla.vrbrowser.ui.OffscreenDisplay; import java.util.HashMap; @@ -29,6 +33,8 @@ public class VRBrowserActivity extends PlatformActivity { String mTargetUrl; BrowserWidget mCurrentBrowser; HashMap mWidgets; + OffscreenDisplay mOffscreenDisplay; + FrameLayout mWidgetContainer; @Override protected void onCreate(Bundle savedInstanceState) { @@ -36,7 +42,14 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mWidgets = new HashMap<>(); + mWidgetContainer = new FrameLayout(this); loadFromIntent(getIntent()); + queueRunnable(new Runnable() { + @Override + public void run() { + createOffscreenDisplay(); + } + }); } @Override @@ -82,7 +95,7 @@ void createWidget(final int aType, final int aHandle, SurfaceTexture aTexture, i if (aType != Widget.Browser) { // Add hidden UI widget to the platform window for invalidation - addWidget((View) widget, aWidth, aHeight); + mWidgetContainer.addView((View) widget, new FrameLayout.LayoutParams(aWidth, aHeight)); } } @@ -107,4 +120,28 @@ public void run() { } }); } + + void createOffscreenDisplay() { + int[] ids = new int[1]; + GLES20.glGenTextures(1, ids, 0); + GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, ids[0]); + + GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); + GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); + GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); + int error = GLES20.glGetError(); + if (error != GLES20.GL_NO_ERROR) { + Log.e(LOGTAG, "OpenGL Error creating OffscreenDisplay: " + error); + } + + final SurfaceTexture texture = new SurfaceTexture(ids[0]); + runOnUiThread(new Runnable() { + @Override + public void run() { + mOffscreenDisplay = new OffscreenDisplay(VRBrowserActivity.this, texture, 16, 16); + mOffscreenDisplay.setContentView(mWidgetContainer); + } + }); + } } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/OffscreenDisplay.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/OffscreenDisplay.java new file mode 100644 index 0000000000..892e2e9dde --- /dev/null +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/OffscreenDisplay.java @@ -0,0 +1,98 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.vrbrowser.ui; + +import android.app.Presentation; +import android.content.Context; +import android.content.DialogInterface; +import android.graphics.SurfaceTexture; +import android.hardware.display.DisplayManager; +import android.hardware.display.VirtualDisplay; +import android.os.Bundle; +import android.util.DisplayMetrics; +import android.view.Display; +import android.view.LayoutInflater; +import android.view.Surface; +import android.view.View; + +public class OffscreenDisplay { + private Context mContext; + private VirtualDisplay mVirtualDisplay; + private SurfaceTexture mTexture; + private Surface mSurface; + private OffscreenPresentation mPresentation; + + private DisplayMetrics mDefaultMetrics; + + public OffscreenDisplay(Context aContext, SurfaceTexture aTexture, int aWidth, int aHeight) { + mContext = aContext; + aTexture.setDefaultBufferSize(aWidth, aHeight); + mSurface = new Surface(aTexture); + + DisplayManager manager = (DisplayManager) aContext.getSystemService(Context.DISPLAY_SERVICE); + Display defaultDisplay = manager.getDisplay(Display.DEFAULT_DISPLAY); + + mDefaultMetrics = new DisplayMetrics(); + defaultDisplay.getMetrics(mDefaultMetrics); + + int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY | + DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION | + DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC; + + mVirtualDisplay = manager.createVirtualDisplay("OffscreenViews", aWidth, aHeight, + mDefaultMetrics.densityDpi, mSurface, flags); + + mPresentation = new OffscreenPresentation(mContext, mVirtualDisplay.getDisplay()); + mPresentation.show(); + } + + public void setContentView(View aView) { + if (mPresentation == null) { + throw new IllegalStateException("No presentation!"); + } + + mPresentation.setContentView(aView); + } + + public void resize(int aWidth, int aHeight) { + if (mVirtualDisplay == null) { + throw new IllegalStateException("No virtual display!"); + } + + mVirtualDisplay.resize(aWidth, aHeight, mDefaultMetrics.densityDpi); + } + + public void release() { + if (mPresentation != null) { + mPresentation.dismiss(); + mPresentation = null; + } + + if (mVirtualDisplay != null) { + mVirtualDisplay.release(); + mVirtualDisplay = null; + } + + if (mSurface != null) { + mSurface.release(); + } + + if (mTexture != null) { + mTexture.release(); + } + } + + class OffscreenPresentation extends Presentation { + OffscreenPresentation(Context context, Display display) { + super(context, display); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + } +} diff --git a/app/src/main/cpp/native-lib.cpp b/app/src/main/cpp/native-lib.cpp index 6863319571..ca1127ec0e 100644 --- a/app/src/main/cpp/native-lib.cpp +++ b/app/src/main/cpp/native-lib.cpp @@ -21,15 +21,17 @@ #define JNI_METHOD(return_type, method_name) \ JNIEXPORT return_type JNICALL \ - Java_org_mozilla_vrbrowser_VRBrowserActivity_##method_name + Java_org_mozilla_vrbrowser_PlatformActivity_##method_name using namespace crow; -static -jobject GetAssetManager(JNIEnv * aEnv, jobject aActivity) { +namespace { + +jobject +GetAssetManager(JNIEnv *aEnv, jobject aActivity) { jclass clazz = aEnv->GetObjectClass(aActivity); jmethodID method = aEnv->GetMethodID(clazz, "getAssets", "()Landroid/content/res/AssetManager;"); - jobject result = aEnv->CallObjectMethod(aActivity, method); + jobject result = aEnv->CallObjectMethod(aActivity, method); if (!result) { VRB_LOG("Failed to get AssetManager instance!"); } @@ -43,6 +45,10 @@ struct AppContext { BrowserEGLContextPtr mEgl; DeviceDelegateOculusVRPtr mDevice; }; +typedef std::shared_ptr AppContextPtr; + +AppContextPtr sAppContext; +} void CommandCallback(android_app *aApp, int32_t aCmd) { @@ -114,6 +120,8 @@ CommandCallback(android_app *aApp, int32_t aCmd) { } } +extern "C" { + void android_main(android_app *aAppState) { @@ -126,21 +134,22 @@ android_main(android_app *aAppState) { (*aAppState->activity->vm).AttachCurrentThread(&jniEnv, NULL); // Create Browser context - auto appContext = std::make_shared(); - appContext->mQueue = vrb::RunnableQueue::Create(aAppState->activity->vm); - appContext->mWorld = BrowserWorld::Create(); + sAppContext = std::make_shared(); + sAppContext->mQueue = vrb::RunnableQueue::Create(aAppState->activity->vm); + sAppContext->mWorld = BrowserWorld::Create(); // Create device delegate - appContext->mDevice = DeviceDelegateOculusVR::Create(appContext->mWorld->GetWeakContext(), aAppState); - appContext->mWorld->RegisterDeviceDelegate(appContext->mDevice); + sAppContext->mDevice = DeviceDelegateOculusVR::Create(sAppContext->mWorld->GetWeakContext(), + aAppState); + sAppContext->mWorld->RegisterDeviceDelegate(sAppContext->mDevice); // Initialize java auto assetManager = GetAssetManager(jniEnv, aAppState->activity->clazz); - appContext->mWorld->InitializeJava(jniEnv, aAppState->activity->clazz, assetManager); + sAppContext->mWorld->InitializeJava(jniEnv, aAppState->activity->clazz, assetManager); jniEnv->DeleteLocalRef(assetManager); // Set up activity & SurfaceView life cycle callbacks - aAppState->userData = appContext.get(); + aAppState->userData = sAppContext.get(); aAppState->onAppCmd = CommandCallback; // Main render loop @@ -150,7 +159,7 @@ android_main(android_app *aAppState) { // Loop until all events are read // If the activity is paused use a blocking call to read events. - while (ALooper_pollAll(appContext->mWorld->IsPaused() ? -1 : 0, + while (ALooper_pollAll(sAppContext->mWorld->IsPaused() ? -1 : 0, NULL, &events, (void **) &pSource) >= 0) { @@ -161,19 +170,29 @@ android_main(android_app *aAppState) { // Check if we are exiting. if (aAppState->destroyRequested != 0) { - appContext->mWorld->ShutdownGL(); - appContext->mEgl->Destroy(); + sAppContext->mWorld->ShutdownGL(); + sAppContext->mEgl->Destroy(); aAppState->activity->vm->DetachCurrentThread(); + sAppContext.reset(); exit(0); } } - if (appContext->mEgl) { - appContext->mEgl->MakeCurrent(); + if (sAppContext->mEgl) { + sAppContext->mEgl->MakeCurrent(); } - appContext->mQueue->ProcessRunnables(); - if (!appContext->mWorld->IsPaused() && appContext->mDevice->IsInVRMode()) { + sAppContext->mQueue->ProcessRunnables(); + if (!sAppContext->mWorld->IsPaused() && sAppContext->mDevice->IsInVRMode()) { VRB_CHECK(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); - appContext->mWorld->Draw(); + sAppContext->mWorld->Draw(); } } } + +JNI_METHOD(void, queueRunnable) +(JNIEnv *aEnv, jobject, jobject aRunnable) { + if (sAppContext) { + sAppContext->mQueue->AddRunnable(aEnv, aRunnable); + } +} + +} // extern "C" diff --git a/app/src/wavevr/java/org/mozilla/vrbrowser/PlatformActivity.java b/app/src/wavevr/java/org/mozilla/vrbrowser/PlatformActivity.java index bfb121c8b6..d2ee808c5a 100644 --- a/app/src/wavevr/java/org/mozilla/vrbrowser/PlatformActivity.java +++ b/app/src/wavevr/java/org/mozilla/vrbrowser/PlatformActivity.java @@ -8,12 +8,7 @@ import com.htc.vr.sdk.VRActivity; import android.content.res.AssetManager; import android.os.Bundle; -import android.util.Log; -import android.view.SurfaceHolder; -import android.view.SurfaceView; -import android.view.View; import android.widget.FrameLayout; -import java.lang.reflect.Field; public class PlatformActivity extends VRActivity { static final String LOGTAG = "VRB"; @@ -27,19 +22,6 @@ public PlatformActivity() { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - // Render to our SurfaceView instead of letting VRActivity take ownership of the window. - SurfaceHolder.Callback2 callback = getVRActivitySurfaceCallback(); - if (callback != null) { - getWindow().takeSurface(null); - SurfaceView surfaceView = new SurfaceView(this); - surfaceView.setClickable(true); - surfaceView.getHolder().addCallback(callback); - surfaceView.setZOrderOnTop(true); - mFrameLayout = new FrameLayout(this); - mFrameLayout.addView(surfaceView, new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)); - setContentView(mFrameLayout); - } - queueRunnable(new Runnable() { @Override public void run() { @@ -48,29 +30,6 @@ public void run() { }); } - protected void addWidget(View aView, int aWidth, int aHeight) { - if (mFrameLayout != null) { - mFrameLayout.addView(aView, 0, new FrameLayout.LayoutParams(aWidth, aHeight)); - } - } - - private SurfaceHolder.Callback2 getVRActivitySurfaceCallback() { - try { - Field fPlatform = VRActivity.class.getDeclaredField("mVRPlatform"); - fPlatform.setAccessible(true); - Object platform = fPlatform.get(this); - Field fRenderer = platform.getClass().getDeclaredField("mSVRRenderBase"); - fRenderer.setAccessible(true); - Object renderer = fRenderer.get(platform); - if (renderer != null && renderer instanceof SurfaceHolder.Callback) { - return (SurfaceHolder.Callback2) renderer; - } - } catch (Exception e) { - Log.e(LOGTAG, "Error getting SurfaceHolder.Callback from VRActivity:" + e.toString()); - } - return null; - } - protected native void queueRunnable(Runnable aRunnable); protected native void initializeJava(AssetManager aAssets); }