Skip to content
This repository has been archived by the owner on Jul 22, 2024. It is now read-only.

Commit

Permalink
Merge pull request #2 from MozillaReality/android_egl
Browse files Browse the repository at this point in the history
Implement native EGL render loop
  • Loading branch information
bluemarvin authored Feb 13, 2018
2 parents 316cffa + 1b53c2a commit 01b8c4d
Show file tree
Hide file tree
Showing 12 changed files with 517 additions and 237 deletions.
7 changes: 7 additions & 0 deletions app/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,15 @@ target_sources(
native-lib
PUBLIC
src/main/cpp/native-lib.cpp
src/main/cpp/BrowserEGLContext.cpp
)

include(AndroidNdkModules)
android_ndk_import_module_native_app_glue()
target_link_libraries(native-lib native_app_glue)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate")
endif()

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
Expand Down
12 changes: 12 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,23 @@ android {
]
}

googlevr {
java.srcDirs = [
'src/common/nativeactivity'
]
}

oculusvr {
java.srcDirs = [
'src/common/nativeactivity'
]
jniLibs.srcDirs = ["${project.rootDir}/third_party/ovr_mobile/VrApi/Libs"]
}

svr {
java.srcDirs = [
'src/common/nativeactivity'
]
jniLibs.srcDirs = ["${project.rootDir}/third_party/svr/libs"]
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/* -*- 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;

import android.app.NativeActivity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.graphics.SurfaceTexture;
import android.view.Surface;
import android.view.SurfaceView;

import org.mozilla.gecko.GeckoSession;

public class VRBrowserActivity extends NativeActivity {
static String LOGTAG = "VRBrowser";
static final String DEFAULT_URL = "https://mozvr.com";
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}

private SurfaceView mSurfaceView;
private static GeckoSession mSession;
private static Surface mBrowserSurface;

@Override
protected void onCreate(Bundle savedInstanceState) {
Log.e(LOGTAG,"in onCreate");
super.onCreate(savedInstanceState);
if (mSession == null) {
mSession = new GeckoSession();
}
getWindow().takeSurface(null);
SurfaceView surfaceView = new SurfaceView(this);
surfaceView.getHolder().addCallback(this);

setContentView(surfaceView);
loadFromIntent(getIntent());
}

@Override
protected void onNewIntent(final Intent intent) {
Log.e(LOGTAG,"In onNewIntent");
super.onNewIntent(intent);
setIntent(intent);
final String action = intent.getAction();
if (Intent.ACTION_VIEW.equals(action)) {
if (intent.getData() != null) {
loadFromIntent(intent);
}
}
}

private void loadFromIntent(final Intent intent) {
final Uri uri = intent.getData();
Log.e(LOGTAG, "Load URI from intent: " + (uri != null ? uri.toString() : DEFAULT_URL));
String uriValue = (uri != null ? uri.toString() : DEFAULT_URL);
mSession.loadUri(uriValue);
}

@Override
protected void onPause() {
Log.e(LOGTAG, "In onPause");
super.onPause();
}

@Override
protected void onResume() {
Log.e(LOGTAG, "in onResume");
super.onResume();
}

@Override
protected void onDestroy() {
super.onDestroy();
}

private void setSurfaceTexture(String aName, final SurfaceTexture aTexture, final int aWidth, final int aHeight) {
runOnUiThread(new Runnable() {
public void run() {
createBrowser(aTexture, aWidth, aHeight);
}
});
}

private void createBrowser(SurfaceTexture aTexture, int aWidth, int aHeight) {
if (aTexture != null) {
Log.e(LOGTAG,"In createBrowser");
aTexture.setDefaultBufferSize(aWidth, aHeight);
mBrowserSurface = new Surface(aTexture);
mSession.acquireDisplay().surfaceChanged(mBrowserSurface, aWidth, aHeight);
mSession.openWindow(this);
}
}
}
1 change: 1 addition & 0 deletions app/src/googlevr/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
android:screenOrientation="landscape"
android:enableVrMode="@string/gvr_vr_mode_component"
android:resizeableActivity="false">
<meta-data android:name="android.app.lib_name" android:value="native-lib" />
<!-- Intent filter that enables this app to be launched from the
Daydream Home menu. -->
<intent-filter>
Expand Down
244 changes: 244 additions & 0 deletions app/src/main/cpp/BrowserEGLContext.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* 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/. */

#include "BrowserEGLContext.h"
#include "vrb/Logger.h"
#include <EGL/eglext.h>
#include <android_native_app_glue.h>

BrowserEGLContext::BrowserEGLContext()
: mMajorVersion(0), mMinorVersion(0), mDisplay(0), mConfig(0), mSurface(0), mContext(0),
mNativeWindow(nullptr) {
}

BrowserEGLContextPtr
BrowserEGLContext::Create() {
return std::make_shared<BrowserEGLContext>();
}

bool
BrowserEGLContext::Initialize(ANativeWindow *aNativeWindow) {
mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (eglInitialize(mDisplay, &mMajorVersion, &mMinorVersion) == EGL_FALSE) {
VRB_LOG("eglInitialize() failed: %s", ErrorToString(eglGetError()));
return false;
}

// Do NOT use eglChooseConfig, because the Android EGL code pushes in multisample
// flags in eglChooseConfig if the user has selected the "force 4x MSAA" option in
// settings, and that is completely wasted for the time warp renderer.
const int MAX_CONFIGS = 1024;

EGLConfig configs[MAX_CONFIGS];
EGLint numConfigs = 0;
if (eglGetConfigs(mDisplay, configs, MAX_CONFIGS, &numConfigs) == EGL_FALSE) {
VRB_LOG("eglGetConfigs() failed: %s", ErrorToString(eglGetError()));
return false;
}

const EGLint configAttribs[] = {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_DEPTH_SIZE, 0,
EGL_STENCIL_SIZE, 0,
EGL_SAMPLES, 0,
EGL_NONE
};

for (int i = 0; i < numConfigs; ++i) {
EGLint value = 0;

eglGetConfigAttrib(mDisplay, configs[i], EGL_RENDERABLE_TYPE, &value);

// Check EGL Client Version
if ((value & EGL_OPENGL_ES3_BIT_KHR) != EGL_OPENGL_ES3_BIT_KHR) {
continue;
}

// EGL_WINDOW_BIT is required so it can share textures with the window context.
eglGetConfigAttrib(mDisplay, configs[i], EGL_SURFACE_TYPE, &value);
if ((value & (EGL_WINDOW_BIT | EGL_PBUFFER_BIT)) != (EGL_WINDOW_BIT | EGL_PBUFFER_BIT)) {
continue;
}

int j = 0;
for (; configAttribs[j] != EGL_NONE; j += 2) {
eglGetConfigAttrib(mDisplay, configs[i], configAttribs[j], &value);
if (value != configAttribs[j + 1]) {
break;
}
}
if (configAttribs[j] == EGL_NONE) {
mConfig = configs[i];
break;
}
}

if (mConfig == 0) {
VRB_LOG("eglChooseConfig() failed: %s", ErrorToString(eglGetError()));
return false;
}


//Reconfigure the ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID.
mNativeWindow = aNativeWindow;
EGLint format;
eglGetConfigAttrib(mDisplay, mConfig, EGL_NATIVE_VISUAL_ID, &format);
ANativeWindow_setBuffersGeometry(aNativeWindow, 0, 0, format);

EGLint contextAttribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 3,
EGL_NONE
};

mContext = eglCreateContext(mDisplay, mConfig, EGL_NO_CONTEXT, contextAttribs);
if (mContext == EGL_NO_CONTEXT) {
VRB_LOG("eglCreateContext() failed: %s", ErrorToString(eglGetError()));
return false;
}

mSurface = eglCreateWindowSurface(mDisplay, mConfig, mNativeWindow, nullptr);

if (mSurface == EGL_NO_SURFACE) {
VRB_LOG("eglCreateWindowSurface() failed: %s", ErrorToString(eglGetError()));
eglDestroyContext(mDisplay, mContext);
mContext = EGL_NO_CONTEXT;
return false;
}

if (eglMakeCurrent(mDisplay, mSurface, mSurface, mContext) == EGL_FALSE) {
VRB_LOG("eglMakeCurrent() failed: %s", ErrorToString(eglGetError()));
eglDestroySurface(mDisplay, mSurface);
eglDestroyContext(mDisplay, mContext);
mContext = EGL_NO_CONTEXT;
return false;
}

return true;
}

void
BrowserEGLContext::Destroy() {
if (mDisplay) {
if (eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) == EGL_FALSE) {
VRB_LOG("eglMakeCurrent() failed: %s", ErrorToString(eglGetError()));
}
}
if (mContext != EGL_NO_CONTEXT) {
if (eglDestroyContext(mDisplay, mContext) == EGL_FALSE) {
VRB_LOG("eglDestroyContext() failed: %s", ErrorToString(eglGetError()));
}
mContext = EGL_NO_CONTEXT;
}
if (mSurface != EGL_NO_SURFACE) {
if (eglDestroySurface(mDisplay, mSurface) == EGL_FALSE) {
VRB_LOG("eglDestroySurface() failed: %s", ErrorToString(eglGetError()));
}
mSurface = EGL_NO_SURFACE;
}
if (mDisplay) {
if (eglTerminate(mDisplay) == EGL_FALSE) {
VRB_LOG("eglTerminate() failed: %s", ErrorToString(eglGetError()));
}
mDisplay = 0;
}
}


// Handle Android Life Cycle.
// Android has started the activity or sent it to foreground.
// Create a new surface and attach it to the recreated ANativeWindow.
// Restore the EGLContext.
void
BrowserEGLContext::SurfaceChanged(ANativeWindow *aWindow) {
if (mSurface != EGL_NO_SURFACE) {
return;
}
mNativeWindow = aWindow;
mSurface = eglCreateWindowSurface(mDisplay, mConfig, aWindow, nullptr);
if (mSurface == EGL_NO_SURFACE) {
VRB_LOG("eglCreateWindowSurface() failed: %s", ErrorToString(eglGetError()));
}
}

// Handle Android Life Cycle.
// Android has stopped the activity or sent it to background.
// Release the surface attached to the destroyed ANativeWindow.
// The EGLContext is not destroyed so it can be restored later.
void
BrowserEGLContext::SurfaceDestroyed() {
if (mSurface != EGL_NO_SURFACE) {
if (eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) == EGL_FALSE) {
VRB_LOG("eglMakeCurrent() failed: %s", ErrorToString(eglGetError()));
}
if (eglDestroySurface(mDisplay, mSurface) == EGL_FALSE) {
VRB_LOG("eglDestroySurface() failed: %s", ErrorToString(eglGetError()));
}
mSurface = EGL_NO_SURFACE;
}
}

bool
BrowserEGLContext::IsSurfaceReady() const {
return mSurface != EGL_NO_SURFACE;
}

bool
BrowserEGLContext::MakeCurrent() {
if (eglMakeCurrent(mDisplay, mSurface, mSurface, mContext) == EGL_FALSE) {
VRB_LOG("eglMakeCurrent() failed: %s", ErrorToString(eglGetError()));
return false;
}
return true;
}

bool
BrowserEGLContext::SwapBuffers() {
if (eglSwapBuffers(mDisplay, mSurface) == EGL_FALSE) {
VRB_LOG("SwapBuffers() failed: %s", ErrorToString(eglGetError()));
return false;
}
return true;
}

const char *
BrowserEGLContext::ErrorToString(EGLint error) {
switch (error) {
case EGL_SUCCESS:
return "EGL_SUCCESS";
case EGL_NOT_INITIALIZED:
return "EGL_NOT_INITIALIZED";
case EGL_BAD_ACCESS:
return "EGL_BAD_ACCESS";
case EGL_BAD_ALLOC:
return "EGL_BAD_ALLOC";
case EGL_BAD_ATTRIBUTE:
return "EGL_BAD_ATTRIBUTE";
case EGL_BAD_CONTEXT:
return "EGL_BAD_CONTEXT";
case EGL_BAD_CONFIG:
return "EGL_BAD_CONFIG";
case EGL_BAD_CURRENT_SURFACE:
return "EGL_BAD_CURRENT_SURFACE";
case EGL_BAD_DISPLAY:
return "EGL_BAD_DISPLAY";
case EGL_BAD_SURFACE:
return "EGL_BAD_SURFACE";
case EGL_BAD_MATCH:
return "EGL_BAD_MATCH";
case EGL_BAD_PARAMETER:
return "EGL_BAD_PARAMETER";
case EGL_BAD_NATIVE_PIXMAP:
return "EGL_BAD_NATIVE_PIXMAP";
case EGL_BAD_NATIVE_WINDOW:
return "EGL_BAD_NATIVE_WINDOW";
case EGL_CONTEXT_LOST:
return "EGL_CONTEXT_LOST";
default:
return "Unknown Error";
}
}
Loading

0 comments on commit 01b8c4d

Please sign in to comment.