* the environment.
*/
public void setGLEnvironment(GLEnvironment glEnvironment) {
- mContext.setGLEnvironment(glEnvironment);
+ mContext.initGLEnvironment(glEnvironment);
}
/**
@Override
public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
- return getRetargetedFormat(inputFormat);
- }
-
- public FrameFormat getRetargetedFormat(FrameFormat format) {
- MutableFrameFormat retargeted = format.mutableCopy();
+ MutableFrameFormat retargeted = inputFormat.mutableCopy();
retargeted.setTarget(mTarget);
return retargeted;
}
Frame input = pullInput("frame");
// Create output frame
- FrameFormat outFormat = getRetargetedFormat(input.getFormat());
- Frame output = context.getFrameManager().newFrame(outFormat);
-
- // Process
- output.setDataFromFrame(input);
+ Frame output = context.getFrameManager().duplicateFrameToTarget(input, mTarget);
// Push output
pushOutput("frame", output);
}
public void setFrameManager(FrameManager manager) {
- mFrameManager = manager;
+ if (manager == null) {
+ throw new NullPointerException("Attempting to set null FrameManager!");
+ } else if (manager.getContext() != null) {
+ throw new IllegalArgumentException("Attempting to set FrameManager which is already "
+ + "bound to another FilterContext!");
+ } else {
+ mFrameManager = manager;
+ mFrameManager.setContext(this);
+ }
}
public GLEnvironment getGLEnvironment() {
return mGLEnvironment;
}
- public void setGLEnvironment(GLEnvironment environment) {
- mGLEnvironment = environment;
+ public void initGLEnvironment(GLEnvironment environment) {
+ if (mGLEnvironment == null) {
+ mGLEnvironment = environment;
+ } else {
+ throw new RuntimeException("Attempting to re-initialize GL Environment for " +
+ "FilterContext!");
+ }
}
public interface OnFrameReceivedListener {
+ "Expected " + mPortFormat + " but got " + frame.getFormat());
}
}
+
+ protected void checkFrameManager(Frame frame, FilterContext context) {
+ if (frame.getFrameManager() != null
+ && frame.getFrameManager() != context.getFrameManager()) {
+ throw new RuntimeException("Frame " + frame + " is managed by foreign FrameManager! ");
+ }
+ }
}
}
}
+ public FrameManager getFrameManager() {
+ return mFrameManager;
+ }
+
protected void assertFrameMutable() {
if (isReadOnly()) {
throw new RuntimeException("Attempting to modify read-only frame!");
*/
public abstract class FrameManager {
+ private FilterContext mContext;
+
public abstract Frame newFrame(FrameFormat format);
public abstract Frame newBoundFrame(FrameFormat format, int bindingType, long bindingId);
public abstract Frame retainFrame(Frame frame);
public abstract Frame releaseFrame(Frame frame);
+
+ public FilterContext getContext() {
+ return mContext;
+ }
+
+ public GLEnvironment getGLEnvironment() {
+ return mContext != null ? mContext.getGLEnvironment() : null;
+ }
+
+ void setContext(FilterContext context) {
+ mContext = context;
+ }
}
import android.filterfw.core.NativeAllocatorTag;
import android.graphics.SurfaceTexture;
+import android.os.Looper;
+import android.util.Log;
import android.view.Surface;
/**
nativeDeallocate();
}
- public static GLEnvironment activeEnvironment() {
- return nativeActiveEnvironment();
- }
-
public void initWithNewContext() {
if (!nativeInitWithNewContext()) {
throw new RuntimeException("Could not initialize GLEnvironment with new context!");
}
public void activate() {
+ if (Looper.myLooper() != null && Looper.myLooper().equals(Looper.getMainLooper())) {
+ Log.e("GLEnvironment", "Activating GL context in UI thread!");
+ }
if (!nativeActivate()) {
throw new RuntimeException("Could not activate GLEnvironment!");
}
System.loadLibrary("filterfw");
}
- private native static GLEnvironment nativeActiveEnvironment();
-
private native boolean nativeInitWithNewContext();
private native boolean nativeInitWithCurrentContext();
private int glFrameId;
+ // Keep a reference to the GL environment, so that it does not get deallocated while there
+ // are still frames living in it.
+ private GLEnvironment mGLEnvironment;
+
GLFrame(FrameFormat format, FrameManager frameManager) {
super(format, frameManager);
}
super(format, frameManager, bindingType, bindingId);
}
- void init() {
+ void init(GLEnvironment glEnv) {
FrameFormat format = getFormat();
+ mGLEnvironment = glEnv;
// Check that we have a valid format
if (format.getBytesPerSample() != 4) {
private void initNew(boolean isExternal) {
if (isExternal) {
- if (!allocateExternal()) {
+ if (!allocateExternal(mGLEnvironment)) {
throw new RuntimeException("Could not allocate external GL frame!");
}
} else {
- if (!allocate(getFormat().getWidth(), getFormat().getHeight())) {
+ if (!allocate(mGLEnvironment, getFormat().getWidth(), getFormat().getHeight())) {
throw new RuntimeException("Could not allocate GL frame!");
}
}
private void initWithTexture(int texId, boolean isNew) {
int width = getFormat().getWidth();
int height = getFormat().getHeight();
- if (!allocateWithTexture(texId, width, height, isNew, isNew)) {
+ if (!allocateWithTexture(mGLEnvironment, texId, width, height, isNew, isNew)) {
throw new RuntimeException("Could not allocate texture backed GL frame!");
}
setReusable(isNew);
private void initWithFbo(int fboId, boolean isNew) {
int width = getFormat().getWidth();
int height = getFormat().getHeight();
- if (!allocateWithFbo(fboId, width, height, isNew, isNew)) {
+ if (!allocateWithFbo(mGLEnvironment, fboId, width, height, isNew, isNew)) {
throw new RuntimeException("Could not allocate FBO backed GL frame!");
}
setReusable(isNew);
deallocate();
}
+ public GLEnvironment getGLEnvironment() {
+ return mGLEnvironment;
+ }
+
@Override
public Object getObjectValue() {
return ByteBuffer.wrap(getNativeData());
System.loadLibrary("filterfw");
}
- private native boolean allocate(int width, int height);
+ private native boolean allocate(GLEnvironment env, int width, int height);
- private native boolean allocateWithTexture(int textureId,
+ private native boolean allocateWithTexture(GLEnvironment env,
+ int textureId,
int width,
int height,
boolean owns,
boolean create);
- private native boolean allocateWithFbo(int fboId,
+ private native boolean allocateWithFbo(GLEnvironment env,
+ int fboId,
int width,
int height,
boolean owns,
boolean create);
- private native boolean allocateExternal();
+ private native boolean allocateExternal(GLEnvironment env);
private native boolean deallocate();
return !hasFrame();
}
- public void transfer(FilterContext context) {
- }
+ public abstract void transfer(FilterContext context);
}
public class ShaderProgram extends Program {
private int shaderProgramId;
+
+ // Keep a reference to the GL environment, so that it does not get deallocated while there
+ // are still programs living in it.
+ private GLEnvironment mGLEnvironment;
+
private StopWatchMap mTimer = null;
private void setTimer() {
private ShaderProgram(NativeAllocatorTag tag) {
}
- public ShaderProgram(String fragmentShader) {
- allocate(null, fragmentShader);
+ public ShaderProgram(FilterContext context, String fragmentShader) {
+ mGLEnvironment = getGLEnvironment(context);
+ allocate(mGLEnvironment, null, fragmentShader);
if (!compileAndLink()) {
throw new RuntimeException("Could not compile and link shader!");
}
this.setTimer();
}
- public ShaderProgram(String vertexShader, String fragmentShader) {
- allocate(vertexShader, fragmentShader);
+ public ShaderProgram(FilterContext context, String vertexShader, String fragmentShader) {
+ mGLEnvironment = getGLEnvironment(context);
+ allocate(mGLEnvironment, vertexShader, fragmentShader);
if (!compileAndLink()) {
throw new RuntimeException("Could not compile and link shader!");
}
this.setTimer();
}
- public static ShaderProgram createIdentity() {
- ShaderProgram program = nativeCreateIdentity();
+ public static ShaderProgram createIdentity(FilterContext context) {
+ ShaderProgram program = nativeCreateIdentity(getGLEnvironment(context));
program.setTimer();
return program;
}
deallocate();
}
+ public GLEnvironment getGLEnvironment() {
+ return mGLEnvironment;
+ }
+
@Override
public void process(Frame[] inputs, Frame output) {
if (mTimer.LOG_MFF_RUNNING_TIMES) {
}
}
+ private static GLEnvironment getGLEnvironment(FilterContext context) {
+ GLEnvironment result = context != null ? context.getGLEnvironment() : null;
+ if (result == null) {
+ throw new NullPointerException("Attempting to create ShaderProgram with no GL "
+ + "environment in place!");
+ }
+ return result;
+ }
+
static {
System.loadLibrary("filterfw");
}
- private native boolean allocate(String vertexShader, String fragmentShader);
+ private native boolean allocate(GLEnvironment glEnv,
+ String vertexShader,
+ String fragmentShader);
private native boolean deallocate();
private native boolean setShaderTargetRegion(float x0, float y0, float x1, float y1,
float x2, float y2, float x3, float y3);
- private static native ShaderProgram nativeCreateIdentity();
+ private static native ShaderProgram nativeCreateIdentity(GLEnvironment glEnv);
private native boolean setShaderClearsOutput(boolean clears);
switch(format.getTarget()) {
case FrameFormat.TARGET_GPU: {
GLFrame glFrame = new GLFrame(format, this, bindingType, bindingId);
- glFrame.init();
+ glFrame.init(getGLEnvironment());
result = glFrame;
break;
}
case FrameFormat.TARGET_GPU: {
GLFrame glFrame = new GLFrame(format, this);
- glFrame.init();
+ glFrame.init(getGLEnvironment());
result = glFrame;
break;
}
public String toString() {
return "input " + super.toString();
}
+
+ @Override
+ public void transfer(FilterContext context) {
+ if (mFrame != null) {
+ checkFrameManager(mFrame, context);
+ }
+ }
}
return gl_env ? ToJBool(gl_env->Deactivate()) : JNI_FALSE;
}
-jobject Java_android_filterfw_core_GLEnvironment_nativeActiveEnvironment(JNIEnv* env,
- jclass clazz) {
- GLEnv* active = GLEnv::ActiveEnv();
- return active ? WrapNewObjectInJava(active, env, false) : NULL;
-}
-
jboolean Java_android_filterfw_core_GLEnvironment_nativeSwapBuffers(JNIEnv* env, jobject thiz) {
GLEnv* gl_env = ConvertFromJava<GLEnv>(env, thiz);
return gl_env ? ToJBool(gl_env->SwapBuffers()) : JNI_FALSE;
JNIEXPORT jboolean JNICALL
Java_android_filterfw_core_GLEnvironment_nativeDeactivate(JNIEnv* env, jobject thiz);
-JNIEXPORT jobject JNICALL
-Java_android_filterfw_core_GLEnvironment_nativeActiveEnvironment(JNIEnv* env, jclass clazz);
-
JNIEXPORT jboolean JNICALL
Java_android_filterfw_core_GLEnvironment_nativeSwapBuffers(JNIEnv* env, jobject thiz);
#include "jni/jni_gl_frame.h"
#include "jni/jni_util.h"
+#include "native/core/gl_env.h"
#include "native/core/gl_frame.h"
#include "native/core/native_frame.h"
+using android::filterfw::GLEnv;
using android::filterfw::GLFrame;
using android::filterfw::NativeFrame;
// GLFrame JNI implementation //////////////////////////////////////////////////////////////////////
jboolean Java_android_filterfw_core_GLFrame_allocate(JNIEnv* env,
jobject thiz,
+ jobject gl_env,
jint width,
jint height) {
- GLFrame* frame = new GLFrame();
+ GLEnv* gl_env_ptr = ConvertFromJava<GLEnv>(env, gl_env);
+ if (!gl_env_ptr) return JNI_FALSE;
+ GLFrame* frame = new GLFrame(gl_env_ptr);
if (frame->Init(width, height)) {
return ToJBool(WrapObjectInJava(frame, env, thiz, true));
} else {
jboolean Java_android_filterfw_core_GLFrame_allocateWithTexture(JNIEnv* env,
jobject thiz,
+ jobject gl_env,
jint tex_id,
jint width,
jint height,
jboolean owns,
jboolean create) {
- GLFrame* frame = new GLFrame();
+ GLEnv* gl_env_ptr = ConvertFromJava<GLEnv>(env, gl_env);
+ if (!gl_env_ptr) return JNI_FALSE;
+ GLFrame* frame = new GLFrame(gl_env_ptr);
if (frame->InitWithTexture(tex_id, width, height, ToCppBool(owns), ToCppBool(create))) {
return ToJBool(WrapObjectInJava(frame, env, thiz, true));
} else {
jboolean Java_android_filterfw_core_GLFrame_allocateWithFbo(JNIEnv* env,
jobject thiz,
+ jobject gl_env,
jint fbo_id,
jint width,
jint height,
jboolean owns,
jboolean create) {
- GLFrame* frame = new GLFrame();
+ GLEnv* gl_env_ptr = ConvertFromJava<GLEnv>(env, gl_env);
+ if (!gl_env_ptr) return JNI_FALSE;
+ GLFrame* frame = new GLFrame(gl_env_ptr);
if (frame->InitWithFbo(fbo_id, width, height, ToCppBool(owns), ToCppBool(create))) {
return ToJBool(WrapObjectInJava(frame, env, thiz, true));
} else {
}
}
-jboolean Java_android_filterfw_core_GLFrame_allocateExternal(JNIEnv* env, jobject thiz) {
- GLFrame* frame = new GLFrame();
+jboolean Java_android_filterfw_core_GLFrame_allocateExternal(JNIEnv* env,
+ jobject thiz,
+ jobject gl_env) {
+ GLEnv* gl_env_ptr = ConvertFromJava<GLEnv>(env, gl_env);
+ if (!gl_env_ptr) return JNI_FALSE;
+ GLFrame* frame = new GLFrame(gl_env_ptr);
if (frame->InitWithExternalTexture()) {
return ToJBool(WrapObjectInJava(frame, env, thiz, true));
} else {
#endif
JNIEXPORT jboolean JNICALL
-Java_android_filterfw_core_GLFrame_allocate(JNIEnv* env, jobject thiz, jint width, jint height);
+Java_android_filterfw_core_GLFrame_allocate(JNIEnv* env,
+ jobject thiz,
+ jobject gl_env,
+ jint width,
+ jint height);
JNIEXPORT jboolean JNICALL
Java_android_filterfw_core_GLFrame_allocateWithTexture(JNIEnv* env,
jobject thiz,
+ jobject gl_env,
jint tex_id,
jint width,
jint height,
JNIEXPORT jboolean JNICALL
Java_android_filterfw_core_GLFrame_allocateWithFbo(JNIEnv* env,
jobject thiz,
+ jobject gl_env,
jint fbo_id,
jint width,
jint height,
jboolean create);
JNIEXPORT jboolean JNICALL
-Java_android_filterfw_core_GLFrame_allocateExternal(JNIEnv* env, jobject thiz);
+Java_android_filterfw_core_GLFrame_allocateExternal(JNIEnv* env, jobject thiz, jobject gl_env);
JNIEXPORT jboolean JNICALL
Java_android_filterfw_core_GLFrame_deallocate(JNIEnv* env, jobject thiz);
#include "native/base/logging.h"
#include "native/core/geometry.h"
+#include "native/core/gl_env.h"
#include "native/core/gl_frame.h"
#include "native/core/shader_program.h"
#include "native/core/vertex_frame.h"
+using android::filterfw::GLEnv;
using android::filterfw::GLFrame;
using android::filterfw::Point;
using android::filterfw::ProgramVar;
jboolean Java_android_filterfw_core_ShaderProgram_allocate(JNIEnv* env,
jobject thiz,
+ jobject gl_env,
jstring vertex_shader,
jstring fragment_shader) {
- if (!fragment_shader)
+ // Get the GLEnv pointer
+ GLEnv* gl_env_ptr = ConvertFromJava<GLEnv>(env, gl_env);
+
+ // Create the shader
+ if (!fragment_shader || !gl_env_ptr)
return false;
else if (!vertex_shader)
- return ToJBool(WrapObjectInJava(new ShaderProgram(ToCppString(env, fragment_shader)),
- env,
- thiz,
- true));
+ return ToJBool(WrapObjectInJava(new ShaderProgram(
+ gl_env_ptr,
+ ToCppString(env, fragment_shader)),
+ env,
+ thiz,
+ true));
else
- return ToJBool(WrapObjectInJava(new ShaderProgram(ToCppString(env, vertex_shader),
- ToCppString(env, fragment_shader)),
- env,
- thiz,
- true));
+ return ToJBool(WrapObjectInJava(new ShaderProgram(
+ gl_env_ptr,
+ ToCppString(env, vertex_shader),
+ ToCppString(env, fragment_shader)),
+ env,
+ thiz,
+ true));
}
jboolean Java_android_filterfw_core_ShaderProgram_deallocate(JNIEnv* env, jobject thiz) {
return JNI_FALSE;
}
-jobject Java_android_filterfw_core_ShaderProgram_nativeCreateIdentity(JNIEnv* env, jclass) {
- ShaderProgram* program = ShaderProgram::CreateIdentity();
- return program ? WrapNewObjectInJava(program, env, true) : NULL;
+jobject Java_android_filterfw_core_ShaderProgram_nativeCreateIdentity(JNIEnv* env,
+ jclass,
+ jobject gl_env) {
+ GLEnv* gl_env_ptr = ConvertFromJava<GLEnv>(env, gl_env);
+ ShaderProgram* program = gl_env_ptr ? ShaderProgram::CreateIdentity(gl_env_ptr) : NULL;
+ return program ? WrapNewObjectInJava(program, env, false) : NULL;
}
jboolean Java_android_filterfw_core_ShaderProgram_setShaderSourceRegion(JNIEnv* env,
JNIEXPORT jboolean JNICALL
Java_android_filterfw_core_ShaderProgram_allocate(JNIEnv* env,
jobject thiz,
+ jobject gl_env,
jstring vertex_shader,
jstring fragment_shader);
jobject output);
JNIEXPORT jobject JNICALL
-Java_android_filterfw_core_ShaderProgram_nativeCreateIdentity(JNIEnv* env, jclass clazz);
+Java_android_filterfw_core_ShaderProgram_nativeCreateIdentity(JNIEnv* env,
+ jclass clazz,
+ jobject gl_env);
JNIEXPORT jboolean JNICALL
Java_android_filterfw_core_ShaderProgram_setShaderSourceRegion(JNIEnv* env,
*/
#include "base/logging.h"
+#include "base/utilities.h"
#include "core/gl_env.h"
+#include "core/shader_program.h"
+#include "core/vertex_frame.h"
#include "system/window.h"
#include <map>
namespace android {
namespace filterfw {
-GLEnv* GLEnv::active_env_ = NULL;
int GLEnv::max_id_ = 0;
GLEnv::GLEnv()
}
GLEnv::~GLEnv() {
- // We are no longer active if torn down
- if (active_env_ == this)
- active_env_ = NULL;
-
// Destroy surfaces
for (std::map<int, SurfaceWindowPair>::iterator it = surfaces_.begin();
it != surfaces_.end();
eglDestroyContext(display(), it->second);
}
+ // Destroy attached shaders and frames
+ STLDeleteValues(&attached_shaders_);
+ STLDeleteValues(&attached_vframes_);
+
// Destroy display
if (initialized_)
eglTerminate(display());
}
bool GLEnv::Deactivate() {
- active_env_ = NULL;
eglMakeCurrent(display(), EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
return !CheckEGLError("eglMakeCurrent");
}
bool GLEnv::Activate() {
- if (active_env_ != this ||
- display() != eglGetCurrentDisplay() ||
+ if (display() != eglGetCurrentDisplay() ||
context() != eglGetCurrentContext() ||
surface() != eglGetCurrentSurface(EGL_DRAW)) {
// Make sure we are initialized
// Make our context current
eglMakeCurrent(display(), surface(), surface(), context());
- // We are no longer active now
- active_env_ = NULL;
-
- // Set active env to new environment if there was no error
- if (!CheckEGLError("eglMakeCurrent")) {
- active_env_ = this;
- return true;
- }
- return false;
+ return !CheckEGLMakeCurrentError();
}
return true;
}
contexts_[0] = eglGetCurrentContext();
surfaces_[0] = SurfaceWindowPair(eglGetCurrentSurface(EGL_DRAW), NULL);
- if ((context() != EGL_NO_CONTEXT) &&
- (display() != EGL_NO_DISPLAY) &&
- (surface() != EGL_NO_SURFACE)) {
- active_env_ = this;
- return true;
- }
- return false;
+ return (context() != EGL_NO_CONTEXT) &&
+ (display() != EGL_NO_DISPLAY) &&
+ (surface() != EGL_NO_SURFACE);
}
bool GLEnv::InitWithNewContext() {
const SurfaceWindowPair* surface = FindOrNull(surfaces_, surface_id);
if (surface) {
surfaces_.erase(surface_id);
- if (surface_id_ == surface_id && ActiveEnv() == this)
+ if (surface_id_ == surface_id && IsActive())
SwitchToSurfaceId(0);
eglDestroySurface(display(), surface->first);
if (surface->second) {
const EGLContext* context = FindOrNull(contexts_, context_id);
if (context) {
contexts_.erase(context_id);
- if (context_id_ == context_id && ActiveEnv() == this)
+ if (context_id_ == context_id && IsActive())
SwitchToContextId(0);
eglDestroyContext(display(), *context);
}
return err;
}
+bool GLEnv::CheckEGLMakeCurrentError() {
+ bool err = false;
+ for (EGLint error = eglGetError();
+ error != EGL_SUCCESS;
+ error = eglGetError()) {
+ switch (error) {
+ case EGL_BAD_DISPLAY:
+ LOGE("EGL Error: Attempting to activate context with bad display!");
+ break;
+ case EGL_BAD_SURFACE:
+ LOGE("EGL Error: Attempting to activate context with bad surface!");
+ break;
+ case EGL_BAD_ACCESS:
+ LOGE("EGL Error: Attempting to activate context, which is "
+ "already active in another thread!");
+ break;
+ default:
+ LOGE("EGL Error: Making EGL rendering context current caused "
+ "error: 0x%x\n", error);
+ }
+ err = true;
+ }
+ return err;
+}
+
GLuint GLEnv::GetCurrentProgram() {
GLint result;
glGetIntegerv(GL_CURRENT_PROGRAM, &result);
}
}
+void GLEnv::AttachShader(int key, ShaderProgram* shader) {
+ ShaderProgram* existingShader = ShaderWithKey(key);
+ if (existingShader)
+ delete existingShader;
+ attached_shaders_[key] = shader;
+}
+
+void GLEnv::AttachVertexFrame(int key, VertexFrame* frame) {
+ VertexFrame* existingFrame = VertexFrameWithKey(key);
+ if (existingFrame)
+ delete existingFrame;
+ attached_vframes_[key] = frame;
+}
+
+ShaderProgram* GLEnv::ShaderWithKey(int key) {
+ return FindPtrOrNull(attached_shaders_, key);
+}
+
+VertexFrame* GLEnv::VertexFrameWithKey(int key) {
+ return FindPtrOrNull(attached_vframes_, key);
+}
+
} // namespace filterfw
} // namespace android
#include <GLES2/gl2.h>
#include <EGL/egl.h>
+#include <pthread.h>
namespace android {
namespace filterfw {
+class ShaderProgram;
+class VertexFrame;
+
class WindowHandle {
public:
virtual ~WindowHandle() {
// by this instance.
~GLEnv();
- // Returns the currently active GL environment, which was activated using
- // Activate().
- static GLEnv* ActiveEnv() {
- return active_env_;
- }
-
// Returns a globally unique id of this environment.
int id() const {
return id_;
}
// Inspecting the environment //////////////////////////////////////////////
- // Returns true if the environment is the active (current) environment.
+ // Returns true if the environment is active in the current thread.
bool IsActive() const;
+ // Attaching GL objects ////////////////////////////////////////////////////
+
+ // Attach a shader to the environment. The environment takes ownership of
+ // the shader.
+ void AttachShader(int key, ShaderProgram* shader);
+
+ // Attach a vertex frame to the environment. The environment takes ownership
+ // of the frame.
+ void AttachVertexFrame(int key, VertexFrame* frame);
+
+ // Return the shader with the specified key, or NULL if there is no such
+ // shader attached to this environment.
+ ShaderProgram* ShaderWithKey(int key);
+
+ // Return the vertex frame with the specified key, or NULL if there is no
+ // such frame attached to this environment.
+ VertexFrame* VertexFrameWithKey(int key);
+
// Static methods //////////////////////////////////////////////////////////
// These operate on the currently active environment!
// instance.
bool IsInitialized() const;
- // (Weak) pointer to active environment. Managed by the GLEnv instances.
- static GLEnv* active_env_;
+ // Outputs error messages specific to the operation eglMakeCurrent().
+ // Returns true if there was at least one error.
+ static bool CheckEGLMakeCurrentError();
// The maximum id assigned to a GLEnv instance.
static int max_id_;
bool created_surface_;
bool initialized_;
- DISALLOW_COPY_AND_ASSIGN(GLEnv);
-};
-
-// Helper class for variables that are GL related, and thus bound to the active
-// environment. When setting a value, it will be bound to the currently active
-// GL environment. Getting a value will result in whatever value was set in
-// the current environment, or NULL if no value was set in that environment.
-// Note: We rely on the unique ID of the environment to determine the
-// association with any one particular GL environment. There may be a conflict
-// of IDs when the current process relaunches (and the static max_id_ resets),
-// however, in this case all dependant variables have been deallocated anyway
-// and will be reinitialized.
-template<typename T>
-class GLBoundVariable {
- public:
- // Set the value of this variable in the context of the currently active
- // GL environment.
- void SetValue(T* value);
+ // Attachments that GL objects can add to the environment.
+ std::map<int, ShaderProgram*> attached_shaders_;
+ std::map<int, VertexFrame*> attached_vframes_;
- // Get the value of this variable in the context of the currently active
- // GL environment. Returns NULL if the variable was not set in this
- // environment.
- T* Value();
-
- private:
- typedef std::map<int, T*> GLVarMap;
- GLVarMap variables_;
+ DISALLOW_COPY_AND_ASSIGN(GLEnv);
};
-// GLBoundVariable implementation //////////////////////////////////////////////
-template<typename T>
-void GLBoundVariable<T>::SetValue(T* value) {
- if (GLEnv::ActiveEnv())
- variables_[GLEnv::ActiveEnv()->id()] = value;
- else
- LOGE("Attempting to set GL bound variable without an active context!");
-}
-
-template<typename T>
-T* GLBoundVariable<T>::Value() {
- if (GLEnv::ActiveEnv()) {
- return FindPtrOrNull(variables_, GLEnv::ActiveEnv()->id());
- } else {
- LOGE("Attempting to get GL bound variable without an active context!");
- return NULL;
- }
-}
-
} // namespace filterfw
} // namespace android
namespace android {
namespace filterfw {
+static const int kIdentityShaderKey = 1;
+
//
// A GLFrame stores pixel data on the GPU. It uses two kinds of GL data
// containers for this: Textures and Frame Buffer Objects (FBOs). Textures are
// target for shaders.
//
-GLFrame::GLFrame()
- : width_(0),
+GLFrame::GLFrame(GLEnv* gl_env)
+ : gl_env_(gl_env),
+ width_(0),
height_(0),
vp_x_(0),
vp_y_(0),
texture_target_(GL_TEXTURE_2D),
texture_state_(kStateUninitialized),
fbo_state_(kStateUninitialized),
- owns_(false),
- identity_cache_(NULL) {
+ owns_(false) {
}
bool GLFrame::Init(int width, int height) {
glDeleteTextures(1, &texture_id_);
glDeleteFramebuffers(1, &fbo_id_);
}
- delete identity_cache_;
}
bool GLFrame::GenerateMipMap() {
}
GLFrame* GLFrame::Clone() const {
- GLFrame* target = new GLFrame();
+ GLFrame* target = new GLFrame(gl_env_);
target->Init(width_, height_);
target->CopyPixelsFrom(this);
return target;
}
ShaderProgram* GLFrame::GetIdentity() const {
- if (!identity_cache_)
- identity_cache_ = ShaderProgram::CreateIdentity();
- return identity_cache_;
+ ShaderProgram* stored_shader = gl_env_->ShaderWithKey(kIdentityShaderKey);
+ if (!stored_shader) {
+ stored_shader = ShaderProgram::CreateIdentity(gl_env_);
+ gl_env_->AttachShader(kIdentityShaderKey, stored_shader);
+ }
+ return stored_shader;
}
bool GLFrame::BindFrameBuffer() const {
sources.push_back(this);
// Create target frame
- GLFrame target;
+ GLFrame target(gl_env_);
target.Init(width_, height_);
// Render the texture to the target
namespace android {
namespace filterfw {
+class GLEnv;
class ShaderProgram;
// A GLFrame stores pixel data on the GPU. While pixel data may be uploaded to
// processing from one GLFrame to another.
class GLFrame : public GLBufferHandle {
public:
- // Create an empty GL frame.
- GLFrame();
+ // Create an empty GL frame in the specified GL environment. Note, that the GLFrame does NOT
+ // take ownership. The caller must make sure the GLEnv stays valid as long as the GLFrame is
+ // alive.
+ GLFrame(GLEnv* gl_env);
// Deallocate a GL frame.
~GLFrame();
// Get the (cached) identity shader.
ShaderProgram* GetIdentity() const;
+ // The GL environment this frame belongs to
+ GLEnv* gl_env_;
+
// The width, height and format of the frame
int width_;
int height_;
// Flag whether frame owns the texture and FBO
bool owns_;
-
- // The identity shader (cache).
- mutable ShaderProgram* identity_cache_;
};
} // namespace filterfw
namespace android {
namespace filterfw {
-// Statics /////////////////////////////////////////////////////////////////////
-GLBoundVariable<VertexFrame> ShaderProgram::s_default_vbo_;
+// VBO attachment keys
+static const int kDefaultVboKey = 1;
// The default vertices for our shader program. This assumes the draw primitive
// GL_TRIANGLE_STRIP.
}
// ShaderProgram implementation ////////////////////////////////////////////////
-ShaderProgram::ShaderProgram(const std::string& fragment_shader)
+ShaderProgram::ShaderProgram(GLEnv* gl_env, const std::string& fragment_shader)
: fragment_shader_source_(fragment_shader),
vertex_shader_source_(s_default_vertex_shader_source_),
fragment_shader_(0),
vertex_shader_(0),
program_(0),
+ gl_env_(gl_env),
base_texture_unit_(GL_TEXTURE0),
vertex_data_(NULL),
vertex_count_(4),
dfactor_(GL_ONE_MINUS_SRC_ALPHA) {
}
-ShaderProgram::ShaderProgram(const std::string& vertex_shader,
+ShaderProgram::ShaderProgram(GLEnv* gl_env,
+ const std::string& vertex_shader,
const std::string& fragment_shader)
: fragment_shader_source_(fragment_shader),
vertex_shader_source_(vertex_shader),
fragment_shader_(0),
vertex_shader_(0),
program_(0),
+ gl_env_(gl_env),
base_texture_unit_(GL_TEXTURE0),
vertex_data_(NULL),
vertex_count_(4),
}
}
-ShaderProgram* ShaderProgram::CreateIdentity() {
- static const char* s_id_fragment_shader =
+ShaderProgram* ShaderProgram::CreateIdentity(GLEnv* gl_env) {
+ const char* s_id_fragment_shader =
"precision mediump float;\n"
"uniform sampler2D tex_sampler_0;\n"
"varying vec2 v_texcoord;\n"
"void main() {\n"
" gl_FragColor = texture2D(tex_sampler_0, v_texcoord);\n"
"}\n";
-
- ShaderProgram* result = new ShaderProgram(s_id_fragment_shader);
+ ShaderProgram* result = new ShaderProgram(gl_env, s_id_fragment_shader);
result->CompileAndLink();
return result;
}
}
VertexFrame* ShaderProgram::DefaultVertexBuffer() {
- LOGI("In ShaderProgram::DefaultVertexBuffer()!");
- if (!s_default_vbo_.Value()) {
+ VertexFrame* storedFrame = gl_env_->VertexFrameWithKey(kDefaultVboKey);
+ if (!storedFrame) {
LOGI("Uploading Data to VBO!");
- s_default_vbo_.SetValue(new VertexFrame(sizeof(s_default_vertices_)));
- s_default_vbo_.Value()->WriteData(
+ storedFrame = new VertexFrame(sizeof(s_default_vertices_));
+ storedFrame->WriteData(
reinterpret_cast<const uint8_t*>(s_default_vertices_),
sizeof(s_default_vertices_)
);
+ gl_env_->AttachVertexFrame(kDefaultVboKey, storedFrame);
}
- return s_default_vbo_.Value();
+ return storedFrame;
}
bool ShaderProgram::BindVertexValues(const std::string& varname,
namespace android {
namespace filterfw {
-class GLTextureHandle;
+class GLFrame;
class GLFrameBufferHandle;
+class GLTextureHandle;
class Quad;
class VertexFrame;
// (and related) functions below.
// This program will not be executable until you have compiled and linked
// it.
- explicit ShaderProgram(const std::string& fragment_shader);
+ // Note, that the ShaderProgram does NOT take ownership of the GLEnv. The
+ // caller must make sure the GLEnv stays valid as long as the GLFrame is
+ // alive.
+ explicit ShaderProgram(GLEnv* gl_env, const std::string& fragment_shader);
// Create a new shader program with the given fragment and vertex shader
// source code. This program will not be executable until you have compiled
// and linked it.
- ShaderProgram(const std::string& vertex_shader, const std::string& fragment_shader);
+ // Note, that the ShaderProgram does NOT take ownership of the GLEnv. The
+ // caller must make sure the GLEnv stays valid as long as the GLFrame is
+ // alive.
+ ShaderProgram(GLEnv* gl_env,
+ const std::string& vertex_shader,
+ const std::string& fragment_shader);
// Destructor.
~ShaderProgram();
// input to the output. Note that transformations may be applied to achieve
// effects such as cropping, scaling or rotation.
// The caller takes ownership of the result!
- static ShaderProgram* CreateIdentity();
+ static ShaderProgram* CreateIdentity(GLEnv* env);
// Geometry ////////////////////////////////////////////////////////////////
// These functions modify the source and target regions used during
};
// Gets or creates the default vertex data.
- static VertexFrame* DefaultVertexBuffer();
+ VertexFrame* DefaultVertexBuffer();
// Binds the given input textures.
bool BindInputTextures(const std::vector<GLuint>& textures,
GLuint vertex_shader_;
GLuint program_;
+ // The GL environment this shader lives in.
+ GLEnv* gl_env_;
+
// The lowest texture unit this program will use
GLuint base_texture_unit_;
bool blending_;
int sfactor_;
int dfactor_;
-
- // The default vertex data used by ShaderPrograms
- static GLBoundVariable<VertexFrame> s_default_vbo_;
-
- // Built-in shaders
- static GLBoundVariable<ShaderProgram> s_identity_;
};
} // namespace filterfw
}
@Override
- protected Program getNativeProgram() {
+ protected Program getNativeProgram(FilterContext context) {
throw new RuntimeException("TODO: Write native implementation for AlphaBlend!");
}
@Override
- protected Program getShaderProgram() {
- return new ShaderProgram(mAlphaBlendShader);
+ protected Program getShaderProgram(FilterContext context) {
+ return new ShaderProgram(context, mAlphaBlendShader);
}
}
}
@Override
- protected Program getNativeProgram() {
+ protected Program getNativeProgram(FilterContext context) {
throw new RuntimeException("TODO: Write native implementation for AlphaBlend!");
}
@Override
- protected Program getShaderProgram() {
- return new ShaderProgram(mBlendShader);
+ protected Program getShaderProgram(FilterContext context) {
+ return new ShaderProgram(context, mBlendShader);
}
}
}
@Override
- protected Program getNativeProgram() {
+ protected Program getNativeProgram(FilterContext context) {
return new NativeProgram("filterpack_imageproc", "brightness");
}
@Override
- protected Program getShaderProgram() {
- return new ShaderProgram(mBrightnessShader);
+ protected Program getShaderProgram(FilterContext context) {
+ return new ShaderProgram(context, mBrightnessShader);
}
}
}
@Override
- protected Program getNativeProgram() {
+ protected Program getNativeProgram(FilterContext context) {
return new NativeProgram("filterpack_imageproc", "contrast");
}
@Override
- protected Program getShaderProgram() {
- return new ShaderProgram(mContrastShader);
+ protected Program getShaderProgram(FilterContext context) {
+ return new ShaderProgram(context, mContrastShader);
}
}
}
@Override
- public void prepare(FilterContext env) {
+ public void prepare(FilterContext context) {
// TODO: Add CPU version
switch (getInputFormat("image").getTarget()) {
case FrameFormat.TARGET_GPU:
- mProgram = ShaderProgram.createIdentity();
+ mProgram = ShaderProgram.createIdentity(context);
break;
}
if (mProgram == null) {
}
@Override
- public void prepare(FilterContext env) {
- mProgram = ShaderProgram.createIdentity();
+ public void prepare(FilterContext context) {
+ mProgram = ShaderProgram.createIdentity(context);
}
@Override
}
@Override
- public void prepare(FilterContext env) {
- mProgram = new ShaderProgram(mVertexShader, mFixedColorFragmentShader);
+ public void prepare(FilterContext context) {
+ mProgram = new ShaderProgram(context, mVertexShader, mFixedColorFragmentShader);
}
@Override
return inputFormat;
}
- public void initProgram(int target) {
+ public void initProgram(FilterContext context, int target) {
switch (target) {
case FrameFormat.TARGET_GPU:
- mProgram = new ShaderProgram(mFisheyeShader);
+ mProgram = new ShaderProgram(context, mFisheyeShader);
break;
default:
// Create program if not created already
if (mProgram == null || inputFormat.getTarget() != mTarget) {
- initProgram(inputFormat.getTarget());
+ initProgram(context, inputFormat.getTarget());
}
// Check if the frame size has changed
if (target != mCurrentTarget) {
switch (target) {
case FrameFormat.TARGET_NATIVE:
- mProgram = getNativeProgram();
+ mProgram = getNativeProgram(context);
break;
case FrameFormat.TARGET_GPU:
- mProgram = getShaderProgram();
+ mProgram = getShaderProgram(context);
break;
default:
}
}
- protected abstract Program getNativeProgram();
+ protected abstract Program getNativeProgram(FilterContext context);
- protected abstract Program getShaderProgram();
+ protected abstract Program getShaderProgram(FilterContext context);
}
}
@Override
- protected Program getNativeProgram() {
+ protected Program getNativeProgram(FilterContext context) {
return new NativeProgram("filterpack_imageproc", "invert");
}
@Override
- protected Program getShaderProgram() {
- return new ShaderProgram(mInvertShader);
+ protected Program getShaderProgram(FilterContext context) {
+ return new ShaderProgram(context, mInvertShader);
}
}
}
@Override
- protected void prepare(FilterContext environment) {
+ protected void prepare(FilterContext context) {
switch (getInputFormat("image").getTarget()) {
case FrameFormat.TARGET_NATIVE:
throw new RuntimeException("Native ResizeFilter not implemented yet!");
case FrameFormat.TARGET_GPU:
- ShaderProgram prog = ShaderProgram.createIdentity();
+ ShaderProgram prog = ShaderProgram.createIdentity(context);
mProgram = prog;
break;
if (target != mCurrentTarget) {
switch (target) {
case FrameFormat.TARGET_NATIVE:
- mProgram = getNativeProgram();
+ mProgram = getNativeProgram(context);
break;
case FrameFormat.TARGET_GPU:
- mProgram = getShaderProgram();
+ mProgram = getShaderProgram(context);
break;
default:
}
}
- protected abstract Program getNativeProgram();
+ protected abstract Program getNativeProgram(FilterContext context);
- protected abstract Program getShaderProgram();
+ protected abstract Program getShaderProgram(FilterContext context);
}
}
@Override
- protected Program getNativeProgram() {
+ protected Program getNativeProgram(FilterContext context) {
throw new RuntimeException("Native toGray not implemented yet!");
}
@Override
- protected Program getShaderProgram() {
+ protected Program getShaderProgram(FilterContext context) {
int inputChannels = getInputFormat("image").getBytesPerSample();
if (inputChannels != 4) {
throw new RuntimeException("Unsupported GL input channels: " +
inputChannels + "! Channels must be 4!");
}
- ShaderProgram program = new ShaderProgram(mColorToGray4Shader);
+ ShaderProgram program = new ShaderProgram(context, mColorToGray4Shader);
if (mInvertSource)
program.setSourceRect(0.0f, 1.0f, 1.0f, -1.0f);
return program;
@Override
public void prepare(FilterContext context) {
- mProgram = new ShaderProgram(mColorToPackedGrayShader);
+ mProgram = new ShaderProgram(context, mColorToPackedGrayShader);
}
@Override
public void prepare(FilterContext context) {
// Create identity shader to render, and make sure to render upside-down, as textures
// are stored internally bottom-to-top.
- mProgram = ShaderProgram.createIdentity();
+ mProgram = ShaderProgram.createIdentity(context);
mProgram.setSourceRect(0, 1, 1, -1);
mProgram.setClearsOutput(true);
mProgram.setClearColor(0.0f, 0.0f, 0.0f);
public void prepare(FilterContext context) {
if (mLogVerbose) Log.v(TAG, "Preparing");
// Compile shader TODO: Move to onGLEnvSomething?
- mFrameExtractor = new ShaderProgram(mFrameShader);
+ mFrameExtractor = new ShaderProgram(context, mFrameShader);
// SurfaceTexture defines (0,0) to be bottom-left. The filter framework defines
// (0,0) as top-left, so do the flip here.
mFrameExtractor.setSourceRect(0, 1, 1, -1);
protected void prepare(FilterContext context) {
if (mLogVerbose) Log.v(TAG, "Preparing MediaSource");
- mFrameExtractor = new ShaderProgram(mFrameShader);
+ mFrameExtractor = new ShaderProgram(context, mFrameShader);
// SurfaceTexture defines (0,0) to be bottom-left. The filter framework
// defines (0,0) as top-left, so do the flip here.
mFrameExtractor.setSourceRect(0, 1, 1, -1);
0);
// Prepare output
- mFrameExtractor = new ShaderProgram(mRenderShader);
+ mFrameExtractor = new ShaderProgram(context, mRenderShader);
// SurfaceTexture defines (0,0) to be bottom-left. The filter framework
// defines (0,0) as top-left, so do the flip here.
mFrameExtractor.setSourceRect(0, 1, 1, -1);
public void prepare(FilterContext context) {
// Create identity shader to render, and make sure to render upside-down, as textures
// are stored internally bottom-to-top.
- mProgram = ShaderProgram.createIdentity();
+ mProgram = ShaderProgram.createIdentity(context);
mProgram.setSourceRect(0, 1, 1, -1);
mProgram.setClearColor(0.0f, 0.0f, 0.0f);