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

Implement native EGL render loop #2

Merged
merged 3 commits into from
Feb 13, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions app/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,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