From 1804b983a0fb03aad3a7d095625aaff9a692d41b Mon Sep 17 00:00:00 2001 From: jules Date: Thu, 23 Feb 2012 09:57:00 +0000 Subject: [PATCH] Android GL work. --- .../Builds/Android/src/com/juce/JuceDemo.java | 55 ++++-- .../native/java/JuceAppActivity.java | 112 ++++++++--- .../native/juce_android_SystemStats.cpp | 4 +- modules/juce_graphics/geometry/juce_Point.h | 2 +- .../native/juce_android_Windowing.cpp | 6 + .../native/juce_android_OpenGLComponent.cpp | 177 +++++++++++++++++- .../opengl/juce_OpenGLComponent.cpp | 4 + .../juce_opengl/opengl/juce_OpenGLComponent.h | 13 +- 8 files changed, 321 insertions(+), 52 deletions(-) diff --git a/extras/JuceDemo/Builds/Android/src/com/juce/JuceDemo.java b/extras/JuceDemo/Builds/Android/src/com/juce/JuceDemo.java index 144c4c525e..e47178e08a 100644 --- a/extras/JuceDemo/Builds/Android/src/com/juce/JuceDemo.java +++ b/extras/JuceDemo/Builds/Android/src/com/juce/JuceDemo.java @@ -114,7 +114,10 @@ public final class JuceDemo extends Activity public final void deleteView (ComponentPeerView view) { - viewHolder.removeView (view); + ViewGroup group = (ViewGroup) (view.getParent()); + + if (group != null) + group.removeView (view); } final class ViewHolder extends ViewGroup @@ -246,6 +249,7 @@ public final class JuceDemo extends Activity public ComponentPeerView (Context context, boolean opaque_) { super (context); + setWillNotDraw (false); opaque = opaque_; setFocusable (true); @@ -260,6 +264,7 @@ public final class JuceDemo extends Activity @Override public void draw (Canvas canvas) { + super.draw (canvas); handlePaint (canvas); } @@ -295,11 +300,16 @@ public final class JuceDemo extends Activity @Override protected void onSizeChanged (int w, int h, int oldw, int oldh) { + super.onSizeChanged (w, h, oldw, oldh); viewSizeChanged(); } @Override - protected void onLayout (boolean changed, int left, int top, int right, int bottom) {} + protected void onLayout (boolean changed, int left, int top, int right, int bottom) + { + for (int i = getChildCount(); --i >= 0;) + requestTransparentRegion (getChildAt (i)); + } private native void viewSizeChanged(); @@ -321,38 +331,59 @@ public final class JuceDemo extends Activity { return true; //xxx needs to check overlapping views } + + public OpenGLView createGLView() + { + OpenGLView glView = new OpenGLView (getContext()); + addView (glView); + return glView; + } } //============================================================================== - public final class OpenGLView extends GLSurfaceView - implements GLSurfaceView.Renderer + public final class OpenGLView extends GLSurfaceView + implements GLSurfaceView.Renderer { - public OpenGLView (ComponentPeerView parent) + OpenGLView (Context context) { - super (parent.getContext()); + super (context); setEGLContextClientVersion (2); + getHolder().setFormat(PixelFormat.TRANSLUCENT); + setVisibility (VISIBLE); setRenderer (this); - parent.addView (this); + //setRenderMode (RENDERMODE_CONTINUOUSLY); + //requestRender(); } + @Override public void onSurfaceCreated (GL10 unused, EGLConfig config) { - contextCreated(); + // contextCreated(); } + @Override public void onDrawFrame (GL10 unused) { - GLES20.glClearColor (1.0f, 0.5f, 0.0f, 1.0f); - GLES20.glClear (GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); + //GLES20.glClearColor (1.0f, 0.5f, 0.0f, 1.0f); + //GLES20.glClear (GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); - render(); + // render(); } + @Override public void onSurfaceChanged (GL10 unused, int width, int height) { - GLES20.glViewport (0, 0, width, height); + //GLES20.glViewport (0, 0, width, height); + } + + @Override + protected void onLayout (boolean changed, int left, int top, int right, int bottom) + { + super.onLayout (changed, left, top, right, bottom); + requestLayout(); + ((ViewGroup) getParent()).requestTransparentRegion (this); } private native void contextCreated(); diff --git a/modules/juce_core/native/java/JuceAppActivity.java b/modules/juce_core/native/java/JuceAppActivity.java index 725300d3e3..d59c53f601 100644 --- a/modules/juce_core/native/java/JuceAppActivity.java +++ b/modules/juce_core/native/java/JuceAppActivity.java @@ -32,6 +32,7 @@ import android.content.Context; import android.os.Bundle; import android.view.*; import android.graphics.*; +import android.opengl.*; import android.text.ClipboardManager; import java.io.BufferedInputStream; import java.io.IOException; @@ -39,6 +40,8 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.URL; import java.net.HttpURLConnection; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; //============================================================================== public final class JuceAppActivity extends Activity @@ -72,9 +75,9 @@ public final class JuceAppActivity extends Activity } //============================================================================== - public native void launchApp (String appFile, String appDataDir); - public native void quitApp(); - public native void setScreenSize (int screenWidth, int screenHeight); + private native void launchApp (String appFile, String appDataDir); + private native void quitApp(); + private native void setScreenSize (int screenWidth, int screenHeight); //============================================================================== public static final void printToConsole (String s) @@ -111,7 +114,10 @@ public final class JuceAppActivity extends Activity public final void deleteView (ComponentPeerView view) { - viewHolder.removeView (view); + ViewGroup group = (ViewGroup) (view.getParent()); + + if (group != null) + group.removeView (view); } final class ViewHolder extends ViewGroup @@ -237,12 +243,13 @@ public final class JuceAppActivity extends Activity public native void alertDismissed (long callback, int id); //============================================================================== - public class ComponentPeerView extends View - implements View.OnFocusChangeListener + public final class ComponentPeerView extends ViewGroup + implements View.OnFocusChangeListener { public ComponentPeerView (Context context, boolean opaque_) { super (context); + setWillNotDraw (false); opaque = opaque_; setFocusable (true); @@ -257,6 +264,7 @@ public final class JuceAppActivity extends Activity @Override public void draw (Canvas canvas) { + super.draw (canvas); handlePaint (canvas); } @@ -292,11 +300,16 @@ public final class JuceAppActivity extends Activity @Override protected void onSizeChanged (int w, int h, int oldw, int oldh) { + super.onSizeChanged (w, h, oldw, oldh); viewSizeChanged(); } @Override - protected void onLayout (boolean changed, int left, int top, int right, int bottom) {} + protected void onLayout (boolean changed, int left, int top, int right, int bottom) + { + for (int i = getChildCount(); --i >= 0;) + requestTransparentRegion (getChildAt (i)); + } private native void viewSizeChanged(); @@ -318,6 +331,63 @@ public final class JuceAppActivity extends Activity { return true; //xxx needs to check overlapping views } + + public OpenGLView createGLView() + { + OpenGLView glView = new OpenGLView (getContext()); + addView (glView); + return glView; + } + } + + //============================================================================== + public final class OpenGLView extends GLSurfaceView + implements GLSurfaceView.Renderer + { + OpenGLView (Context context) + { + super (context); + + setEGLContextClientVersion (2); + getHolder().setFormat(PixelFormat.TRANSLUCENT); + setVisibility (VISIBLE); + setRenderer (this); + + //setRenderMode (RENDERMODE_CONTINUOUSLY); + //requestRender(); + } + + @Override + public void onSurfaceCreated (GL10 unused, EGLConfig config) + { + // contextCreated(); + } + + @Override + public void onDrawFrame (GL10 unused) + { + //GLES20.glClearColor (1.0f, 0.5f, 0.0f, 1.0f); + //GLES20.glClear (GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); + + // render(); + } + + @Override + public void onSurfaceChanged (GL10 unused, int width, int height) + { + //GLES20.glViewport (0, 0, width, height); + } + + @Override + protected void onLayout (boolean changed, int left, int top, int right, int bottom) + { + super.onLayout (changed, left, top, right, bottom); + requestLayout(); + ((ViewGroup) getParent()).requestTransparentRegion (this); + } + + private native void contextCreated(); + private native void render(); } //============================================================================== @@ -344,7 +414,7 @@ public final class JuceAppActivity extends Activity c.setMatrix (matrix); c.drawPath (p, paint); - int sizeNeeded = w * h; + final int sizeNeeded = w * h; if (cachedRenderArray.length < sizeNeeded) cachedRenderArray = new int [sizeNeeded]; @@ -393,25 +463,10 @@ public final class JuceAppActivity extends Activity return num; } - public final long getPosition() - { - return position; - } - - public final long getTotalLength() - { - return -1; - } - - public final boolean isExhausted() - { - return false; - } - - public final boolean setPosition (long newPos) - { - return false; - } + public final long getPosition() { return position; } + public final long getTotalLength() { return -1; } + public final boolean isExhausted() { return false; } + public final boolean setPosition (long newPos) { return false; } private HttpURLConnection connection; private InputStream inputStream; @@ -419,7 +474,8 @@ public final class JuceAppActivity extends Activity } public static final HTTPStream createHTTPStream (String address, boolean isPost, byte[] postData, - String headers, int timeOutMs, java.lang.StringBuffer responseHeaders) + String headers, int timeOutMs, + java.lang.StringBuffer responseHeaders) { try { diff --git a/modules/juce_core/native/juce_android_SystemStats.cpp b/modules/juce_core/native/juce_android_SystemStats.cpp index 7d70e6b753..a66d228797 100644 --- a/modules/juce_core/native/juce_android_SystemStats.cpp +++ b/modules/juce_core/native/juce_android_SystemStats.cpp @@ -55,14 +55,14 @@ void JNIClassBase::release (JNIEnv* env) void JNIClassBase::initialiseAllClasses (JNIEnv* env) { - Array& classes = getClasses(); + const Array& classes = getClasses(); for (int i = classes.size(); --i >= 0;) classes.getUnchecked(i)->initialise (env); } void JNIClassBase::releaseAllClasses (JNIEnv* env) { - Array& classes = getClasses(); + const Array& classes = getClasses(); for (int i = classes.size(); --i >= 0;) classes.getUnchecked(i)->release (env); } diff --git a/modules/juce_graphics/geometry/juce_Point.h b/modules/juce_graphics/geometry/juce_Point.h index f7f80fb454..1f7b99349a 100644 --- a/modules/juce_graphics/geometry/juce_Point.h +++ b/modules/juce_graphics/geometry/juce_Point.h @@ -119,7 +119,7 @@ public: /** Returns the inverse of this point. */ Point operator-() const noexcept { return Point (-x, -y); } - /** Returns the straight-line distance between this point and another one. */ + /** Returns the straight-line distance between this point and the origin. */ ValueType getDistanceFromOrigin() const noexcept { return juce_hypot (x, y); } /** Returns the straight-line distance between this point and another one. */ diff --git a/modules/juce_gui_basics/native/juce_android_Windowing.cpp b/modules/juce_gui_basics/native/juce_android_Windowing.cpp index 995597f59e..eaa818287d 100644 --- a/modules/juce_gui_basics/native/juce_android_Windowing.cpp +++ b/modules/juce_gui_basics/native/juce_android_Windowing.cpp @@ -78,6 +78,7 @@ DECLARE_JNI_CLASS (CanvasMinimal, "android/graphics/Canvas"); METHOD (hasFocus, "hasFocus", "()Z") \ METHOD (invalidate, "invalidate", "(IIII)V") \ METHOD (containsPoint, "containsPoint", "(II)Z") \ + METHOD (createGLView, "createGLView", "()L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$OpenGLView;") \ DECLARE_JNI_CLASS (ComponentPeerView, JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView"); #undef JNI_CLASS_MEMBERS @@ -585,6 +586,11 @@ ComponentPeer* Component::createNewPeer (int styleFlags, void*) return new AndroidComponentPeer (this, styleFlags); } +jobject createOpenGLView (ComponentPeer* peer) +{ + jobject parentView = static_cast (peer->getNativeHandle()); + return getEnv()->CallObjectMethod (parentView, ComponentPeerView.createGLView); +} //============================================================================== bool Desktop::canUseSemiTransparentWindows() noexcept diff --git a/modules/juce_opengl/native/juce_android_OpenGLComponent.cpp b/modules/juce_opengl/native/juce_android_OpenGLComponent.cpp index 0e4a871f1c..9998bab2d0 100644 --- a/modules/juce_opengl/native/juce_android_OpenGLComponent.cpp +++ b/modules/juce_opengl/native/juce_android_OpenGLComponent.cpp @@ -23,22 +23,187 @@ ============================================================================== */ -// TODO + +#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ + METHOD (layout, "layout", "(IIII)V") \ + METHOD (invalidate, "invalidate", "(IIII)V") \ + +DECLARE_JNI_CLASS (OpenGLView, JUCE_ANDROID_ACTIVITY_CLASSPATH "$OpenGLView"); +#undef JNI_CLASS_MEMBERS + +jobject createOpenGLView (ComponentPeer*); + +//============================================================================== +class AndroidGLContext : public OpenGLContext +{ +public: + AndroidGLContext (OpenGLComponent* const component_, + ComponentPeer* peer, + const OpenGLPixelFormat& pixelFormat, + const AndroidGLContext* const sharedContext, + const bool isGLES2_) + : component (component_), + lastWidth (0), lastHeight (0), + isGLES2 (isGLES2_), + isInsideGLCallback (false) + { + getContextList().add (this); + + jassert (peer != nullptr); + + glView = GlobalRef (createOpenGLView (peer)); + } + + ~AndroidGLContext() + { + properties.clear(); // to release any stored programs, etc that may be held in properties. + + android.activity.callVoidMethod (JuceAppActivity.deleteView, glView.get()); + glView.clear(); + + getContextList().removeValue (this); + } + + //============================================================================== + bool makeActive() const noexcept { return isInsideGLCallback; } + bool makeInactive() const noexcept { return true; } + bool isActive() const noexcept { return isInsideGLCallback; } + + void swapBuffers() {} + + void* getRawContext() const noexcept { return 0; } + unsigned int getFrameBufferID() const { return 0; } + + int getWidth() const { return lastWidth; } + int getHeight() const { return lastHeight; } + + bool areShadersAvailable() const { return isGLES2; } + + void updateWindowPosition (const Rectangle& bounds) + { + if (lastWidth != bounds.getWidth() || lastHeight != bounds.getHeight()) + { + lastWidth = bounds.getWidth(); + lastHeight = bounds.getHeight(); + + glView.callVoidMethod (OpenGLView.layout, + bounds.getX(), bounds.getY(), + lastWidth, lastHeight); + } + } + + bool setSwapInterval (const int /*numFramesPerSwap*/) { return false; } + int getSwapInterval() const { return 0; } + + //============================================================================== + void contextCreatedCallback() + { + isInsideGLCallback = true; + + if (component != nullptr) + dynamic_cast (component.get())->newOpenGLContextCreated(); + + isInsideGLCallback = false; + } + + void renderCallback() + { + isInsideGLCallback = true; + + if (component != nullptr) + dynamic_cast (component.get())->performRender(); + + isInsideGLCallback = false; + } + + //============================================================================== + static Array& getContextList() + { + static Array contexts; + return contexts; + } + + static AndroidGLContext* findContextFor (JNIEnv* env, jobject glView) + { + const Array& contexts = getContextList(); + + for (int i = contexts.size(); --i >= 0;) + { + AndroidGLContext* const c = contexts.getUnchecked(i); + + if (env->IsSameObject (c->glView.get(), glView)) + return c; + } + + return nullptr; + } + + static bool isAnyContextActive() + { + const Array& contexts = getContextList(); + + for (int i = contexts.size(); --i >= 0;) + { + const AndroidGLContext* const c = contexts.getUnchecked(i); + + if (c->isInsideGLCallback) + return true; + } + + return false; + } + + //============================================================================== + GlobalRef glView; + +private: + WeakReference component; + int lastWidth, lastHeight; + bool isGLES2; + bool isInsideGLCallback; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidGLContext); +}; + +//============================================================================== OpenGLContext* OpenGLComponent::createContext() { + ComponentPeer* peer = getTopLevelComponent()->getPeer(); + + if (peer != nullptr) + return new AndroidGLContext (this, peer, preferredPixelFormat, + dynamic_cast (contextToShareListsWith), + (flags & openGLES2) != 0); + return nullptr; } -void* OpenGLComponent::getNativeWindowHandle() const +void OpenGLComponent::updateEmbeddedPosition (const Rectangle& bounds) { - return nullptr; + const ScopedLock sl (contextLock); + + if (context != nullptr) + static_cast (context.get())->updateWindowPosition (bounds); +} + +bool OpenGLHelpers::isContextActive() +{ + return AndroidGLContext::isAnyContextActive(); } -void OpenGLComponent::updateEmbeddedPosition (const Rectangle&) +//============================================================================== +JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024OpenGLView), contextCreated, void, (JNIEnv* env, jobject view)) { + AndroidGLContext* const context = AndroidGLContext::findContextFor (env, view); + + if (context != nullptr) + context->contextCreatedCallback(); } -bool OpenGLHelpers::isContextActive() +JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024OpenGLView), render, void, (JNIEnv* env, jobject view)) { - return false; + AndroidGLContext* const context = AndroidGLContext::findContextFor (env, view); + + if (context != nullptr) + context->renderCallback(); } diff --git a/modules/juce_opengl/opengl/juce_OpenGLComponent.cpp b/modules/juce_opengl/opengl/juce_OpenGLComponent.cpp index 3becef108b..15ef8fa106 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLComponent.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLComponent.cpp @@ -220,7 +220,11 @@ void OpenGLComponent::stopRenderThread() //============================================================================== OpenGLComponent::OpenGLComponent (const int flags_) + #if JUCE_ANDROID + : flags (flags_ & ~useBackgroundThread), + #else : flags (flags_), + #endif contextToShareListsWith (nullptr), needToUpdateViewport (true), needToDeleteContext (false), diff --git a/modules/juce_opengl/opengl/juce_OpenGLComponent.h b/modules/juce_opengl/opengl/juce_OpenGLComponent.h index d083081e4e..90eb1c1c40 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLComponent.h +++ b/modules/juce_opengl/opengl/juce_OpenGLComponent.h @@ -35,6 +35,9 @@ #endif class OpenGLGraphicsContext; +#if JUCE_ANDROID +class AndroidGLContext; +#endif //============================================================================== /** @@ -61,9 +64,9 @@ public: */ openGLDefault = 8, - #if JUCE_IOS - openGLES1 = 1, /**< On the iPhone, this selects openGL ES 1.0 */ - openGLES2 = 2, /**< On the iPhone, this selects openGL ES 2.0 */ + #if JUCE_IOS || JUCE_ANDROID + openGLES1 = 1, /**< This selects openGL ES 1.0 */ + openGLES2 = 2, /**< This selects openGL ES 2.0 */ #endif /** If this flag is enabled, the component will launch a background thread to @@ -290,6 +293,10 @@ private: int renderAndSwapBuffers(); // (This method has been deprecated) + #if JUCE_ANDROID + friend class AndroidGLContext; + #endif + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLComponent); };