From 489c240d3ae33e83dc62ea6f3cc864e47c0e2e3b Mon Sep 17 00:00:00 2001 From: Marius Renn Date: Tue, 12 Jul 2011 13:20:49 -0700 Subject: [PATCH] Many bugfixes and enhancements. - FieldPort bugfixes when connecting to other filters. - Added two new filterpacks: numeric and performance. - Added new type-checking modes. - CachedFrameManager now drops oldest frames first. - RoundRobinScheduler is now default scheduler. - Added method to flush all frames from a graph. - FrameFormat fixes. - Filter fixes and optimizations. Change-Id: I8b671e5e65129af7b6a4226bd9dee2f48a6fcb52 --- mca/Docs.mk | 4 +- .../java/android/filterfw/GraphEnvironment.java | 6 +- .../android/filterfw/core/CachedFrameManager.java | 58 +++++++----- .../java/android/filterfw/core/FieldPort.java | 21 +++-- .../java/android/filterfw/core/Filter.java | 8 +- .../java/android/filterfw/core/FilterGraph.java | 41 ++++++-- .../java/android/filterfw/core/FilterPort.java | 15 +++ .../java/android/filterfw/core/FinalPort.java | 6 +- .../java/android/filterfw/core/FrameFormat.java | 68 +++++++++++++- .../java/android/filterfw/core/FrameManager.java | 6 +- .../java/android/filterfw/core/InputPort.java | 4 + .../java/android/filterfw/core/OutputPort.java | 2 +- .../java/android/filterfw/core/ProgramPort.java | 1 + .../android/filterfw/core/RoundRobinScheduler.java | 13 +-- .../android/filterfw/core/SimpleFrameManager.java | 6 -- .../java/android/filterfw/core/StreamPort.java | 2 + .../java/android/filterfw/format/ObjectFormat.java | 32 ++++++- mca/filterfw/native/core/gl_frame.cpp | 10 -- mca/filterpacks/imageproc/java/BitmapSource.java | 7 +- mca/filterpacks/imageproc/java/FisheyeFilter.java | 103 ++++++++++++++------ mca/filterpacks/imageproc/native/brightness.c | 29 ++++-- mca/filterpacks/numeric/java/SinWaveFilter.java | 66 +++++++++++++ mca/filterpacks/numeric/java/package-info.java | 4 + mca/filterpacks/performance/java/Throughput.java | 57 +++++++++++ .../performance/java/ThroughputFilter.java | 104 +++++++++++++++++++++ mca/filterpacks/performance/java/package-info.java | 4 + 26 files changed, 568 insertions(+), 109 deletions(-) create mode 100644 mca/filterpacks/numeric/java/SinWaveFilter.java create mode 100644 mca/filterpacks/numeric/java/package-info.java create mode 100644 mca/filterpacks/performance/java/Throughput.java create mode 100644 mca/filterpacks/performance/java/ThroughputFilter.java create mode 100644 mca/filterpacks/performance/java/package-info.java diff --git a/mca/Docs.mk b/mca/Docs.mk index 656cb17f..99e8436f 100644 --- a/mca/Docs.mk +++ b/mca/Docs.mk @@ -26,13 +26,15 @@ $(patsubst ./%,%, \ endef # List of mobile filter framework source files to include in the public API/SDK. -# +# # # $(1): directory for search (to support use from frameworks/base) define libfilterfw_to_document $(call libfilterfw-all-java-files-under,$(1), filterfw/java \ filterpacks/imageproc/java \ + filterpacks/numeric/java \ + filterpacks/performance/java \ filterpacks/text/java \ filterpacks/ui/java \ filterpacks/videosrc/java ) diff --git a/mca/filterfw/java/android/filterfw/GraphEnvironment.java b/mca/filterfw/java/android/filterfw/GraphEnvironment.java index 0aaf23ae..5f6d45c5 100644 --- a/mca/filterfw/java/android/filterfw/GraphEnvironment.java +++ b/mca/filterfw/java/android/filterfw/GraphEnvironment.java @@ -23,7 +23,7 @@ import android.filterfw.core.FilterGraph; import android.filterfw.core.FilterContext; import android.filterfw.core.FrameManager; import android.filterfw.core.GraphRunner; -import android.filterfw.core.SimpleScheduler; +import android.filterfw.core.RoundRobinScheduler; import android.filterfw.core.SyncRunner; import android.filterfw.io.GraphIOException; import android.filterfw.io.GraphReader; @@ -66,7 +66,7 @@ public class GraphEnvironment extends MffEnvironment { public AsyncRunner getAsyncRunner(FilterContext environment) { if (mAsyncRunner == null) { - mAsyncRunner = new AsyncRunner(environment, SimpleScheduler.class); + mAsyncRunner = new AsyncRunner(environment, RoundRobinScheduler.class); mAsyncRunner.setGraph(mGraph); } return mAsyncRunner; @@ -74,7 +74,7 @@ public class GraphEnvironment extends MffEnvironment { public GraphRunner getSyncRunner(FilterContext environment) { if (mSyncRunner == null) { - mSyncRunner = new SyncRunner(environment, mGraph, SimpleScheduler.class); + mSyncRunner = new SyncRunner(environment, mGraph, RoundRobinScheduler.class); } return mSyncRunner; } diff --git a/mca/filterfw/java/android/filterfw/core/CachedFrameManager.java b/mca/filterfw/java/android/filterfw/core/CachedFrameManager.java index a2d46e4c..5e03ad65 100644 --- a/mca/filterfw/java/android/filterfw/core/CachedFrameManager.java +++ b/mca/filterfw/java/android/filterfw/core/CachedFrameManager.java @@ -21,21 +21,25 @@ import android.filterfw.core.Frame; import android.filterfw.core.FrameFormat; import android.filterfw.core.SimpleFrameManager; -import java.util.LinkedList; -import java.util.ListIterator; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +import android.util.Log; /** * @hide */ public class CachedFrameManager extends SimpleFrameManager { - private LinkedList mAvailableFrames; - private int mStorageCapacity = 32 * 1024 * 1024; // Cap default storage to 32MB + private SortedMap mAvailableFrames; + private int mStorageCapacity = 24 * 1024 * 1024; // Cap default storage to 24MB private int mStorageSize = 0; + private int mTimeStamp = 0; public CachedFrameManager() { super(); - mAvailableFrames = new LinkedList(); + mAvailableFrames = new TreeMap(); } @Override @@ -57,12 +61,6 @@ public class CachedFrameManager extends SimpleFrameManager { } @Override - public Frame duplicateFrame(Frame frame) { - // TODO - return null; - } - - @Override public Frame retainFrame(Frame frame) { return super.retainFrame(frame); } @@ -87,22 +85,40 @@ public class CachedFrameManager extends SimpleFrameManager { private boolean storeFrame(Frame frame) { synchronized(mAvailableFrames) { - int newStorageSize = mStorageSize + frame.getFormat().getSize(); - if (newStorageSize <= mStorageCapacity) { - mStorageSize = newStorageSize; - mAvailableFrames.add(frame); - return true; + // Make sure this frame alone does not exceed capacity + int frameSize = frame.getFormat().getSize(); + if (frameSize > mStorageCapacity) { + return false; } - return false; + + // Drop frames if adding this frame would exceed capacity + int newStorageSize = mStorageSize + frameSize; + while (newStorageSize > mStorageCapacity) { + dropOldestFrame(); + newStorageSize = mStorageSize + frameSize; + } + + // Store new frame + mStorageSize = newStorageSize; + mAvailableFrames.put(mTimeStamp, frame); + ++mTimeStamp; + return true; } } + private void dropOldestFrame() { + int oldest = mAvailableFrames.firstKey(); + Frame frame = mAvailableFrames.get(oldest); + mStorageSize -= frame.getFormat().getSize(); + frame.dealloc(); + mAvailableFrames.remove(oldest); + } + private Frame findAvailableFrame(FrameFormat format, int bindingType, long bindingId) { // Look for a frame that is compatible with the requested format synchronized(mAvailableFrames) { - ListIterator iter = mAvailableFrames.listIterator(); - while (iter.hasNext()) { - Frame frame = iter.next(); + for (Map.Entry entry : mAvailableFrames.entrySet()) { + Frame frame = entry.getValue(); // Check that format is compatible if (frame.getFormat().isReplaceableBy(format)) { // Check that binding is compatible (if frame is bound) @@ -112,7 +128,7 @@ public class CachedFrameManager extends SimpleFrameManager { // We found one! Take it out of the set of available frames and attach the // requested format to it. super.retainFrame(frame); - iter.remove(); + mAvailableFrames.remove(entry.getKey()); frame.reset(format); mStorageSize -= format.getSize(); return frame; diff --git a/mca/filterfw/java/android/filterfw/core/FieldPort.java b/mca/filterfw/java/android/filterfw/core/FieldPort.java index f0c51d97..8f40de09 100644 --- a/mca/filterfw/java/android/filterfw/core/FieldPort.java +++ b/mca/filterfw/java/android/filterfw/core/FieldPort.java @@ -40,16 +40,12 @@ public class FieldPort extends InputPort { @Override public void pushFrame(Frame frame) { - setFrame(frame); + setFieldFrame(frame, false); } @Override public void setFrame(Frame frame) { - assertPortIsOpen(); - mValue = frame.getObjectValue(); - if (!mHasFrame) { - mHasFrame = true; - } + setFieldFrame(frame, true); } @Override @@ -79,7 +75,20 @@ public class FieldPort extends InputPort { } @Override + public boolean acceptsFrame() { + return mValue == null; + } + + @Override public String toString() { return "field " + super.toString(); } + + protected void setFieldFrame(Frame frame, boolean isAssignment) { + assertPortIsOpen(); + checkFrameType(frame, isAssignment); + + mValue = frame.getObjectValue(); + mHasFrame = true; + } } diff --git a/mca/filterfw/java/android/filterfw/core/Filter.java b/mca/filterfw/java/android/filterfw/core/Filter.java index fc67c493..4e14d38c 100644 --- a/mca/filterfw/java/android/filterfw/core/Filter.java +++ b/mca/filterfw/java/android/filterfw/core/Filter.java @@ -544,7 +544,7 @@ public abstract class Filter { } synchronized final boolean canProcess() { - if (mLogVerbose) Log.v(TAG, "Checking if can process: " + this + "."); + if (mLogVerbose) Log.v(TAG, "Checking if can process: " + this + " (" + mStatus + ")."); if (mStatus <= STATUS_PROCESSING) { return inputConditionsMet() && outputConditionsMet(); } else { @@ -561,6 +561,12 @@ public abstract class Filter { } } + final void clearOutputs() { + for (OutputPort outputPort : mOutputPorts.values()) { + outputPort.clear(); + } + } + final void notifyFieldPortValueUpdated(String name, FilterContext context) { if (mStatus == STATUS_PROCESSING || mStatus == STATUS_PREPARED) { fieldPortValueUpdated(name, context); diff --git a/mca/filterfw/java/android/filterfw/core/FilterGraph.java b/mca/filterfw/java/android/filterfw/core/FilterGraph.java index e20f27ef..ec593872 100644 --- a/mca/filterfw/java/android/filterfw/core/FilterGraph.java +++ b/mca/filterfw/java/android/filterfw/core/FilterGraph.java @@ -46,8 +46,13 @@ public class FilterGraph { public static final int AUTOBRANCH_SYNCED = 1; public static final int AUTOBRANCH_UNSYNCED = 2; + public static final int TYPECHECK_OFF = 0; + public static final int TYPECHECK_DYNAMIC = 1; + public static final int TYPECHECK_STRICT = 2; + private boolean mIsReady = false; private int mAutoBranchMode = AUTOBRANCH_OFF; + private int mTypeCheckMode = TYPECHECK_STRICT; private boolean mDiscardUnconnectedOutputs = false; private boolean mLogVerbose; @@ -125,6 +130,12 @@ public class FilterGraph { mIsReady = true; } + public void flushFrames() { + for (Filter filter : mFilters) { + filter.clearOutputs(); + } + } + public void closeFilters(FilterContext context) { if (mLogVerbose) Log.v(TAG, "Closing all filters..."); for (Filter filter : mFilters) { @@ -145,6 +156,10 @@ public class FilterGraph { mDiscardUnconnectedOutputs = discard; } + public void setTypeCheckMode(int typeCheckMode) { + mTypeCheckMode = typeCheckMode; + } + private boolean readyForProcessing(Filter filter, Set processed) { // Check if this has been already processed if (processed.contains(filter)) { @@ -161,7 +176,7 @@ public class FilterGraph { return true; } - private void runTypeCheck(boolean strict) { + private void runTypeCheck() { Stack filterStack = new Stack(); Set processedFilters = new HashSet(); filterStack.addAll(getSourceFilters()); @@ -176,7 +191,7 @@ public class FilterGraph { // Perform type check if (mLogVerbose) Log.v(TAG, "Running type check on " + filter + "..."); - runTypeCheckOn(filter, strict); + runTypeCheckOn(filter); // Push connected filters onto stack for (OutputPort port : filter.getOutputPorts()) { @@ -209,15 +224,29 @@ public class FilterGraph { } } - private void runTypeCheckOn(Filter filter, boolean strict) { + private void runTypeCheckOn(Filter filter) { for (InputPort inputPort : filter.getInputPorts()) { if (mLogVerbose) Log.v(TAG, "Type checking port " + inputPort); FrameFormat sourceFormat = inputPort.getSourceFormat(); FrameFormat targetFormat = inputPort.getPortFormat(); if (sourceFormat != null && targetFormat != null) { if (mLogVerbose) Log.v(TAG, "Checking " + sourceFormat + " against " + targetFormat + "."); - boolean compatible = strict ? sourceFormat.isCompatibleWith(targetFormat) - : sourceFormat.mayBeCompatibleWith(targetFormat); + + boolean compatible = true; + switch (mTypeCheckMode) { + case TYPECHECK_OFF: + inputPort.setChecksType(false); + break; + case TYPECHECK_DYNAMIC: + compatible = sourceFormat.mayBeCompatibleWith(targetFormat); + inputPort.setChecksType(true); + break; + case TYPECHECK_STRICT: + compatible = sourceFormat.isCompatibleWith(targetFormat); + inputPort.setChecksType(false); + break; + } + if (!compatible) { throw new RuntimeException("Type mismatch: Filter " + filter + " expects a " + "format of type " + targetFormat + " but got a format of type " @@ -317,7 +346,7 @@ public class FilterGraph { } connectPorts(); checkConnections(); - runTypeCheck(true); // TODO: allow non-strict type-checking + runTypeCheck(); } void tearDownFilters(FilterContext context) { diff --git a/mca/filterfw/java/android/filterfw/core/FilterPort.java b/mca/filterfw/java/android/filterfw/core/FilterPort.java index 2136ad52..7dd54082 100644 --- a/mca/filterfw/java/android/filterfw/core/FilterPort.java +++ b/mca/filterfw/java/android/filterfw/core/FilterPort.java @@ -31,6 +31,7 @@ public abstract class FilterPort { protected FrameFormat mPortFormat; protected boolean mIsBlocking = true; protected boolean mIsOpen = false; + protected boolean mChecksType = false; private boolean mLogVerbose; private static final String TAG = "FilterPort"; @@ -64,6 +65,10 @@ public abstract class FilterPort { mIsBlocking = blocking; } + public void setChecksType(boolean checksType) { + mChecksType = checksType; + } + public void open() { if (!mIsOpen) { if (mLogVerbose) Log.v(TAG, "Opening " + this); @@ -109,4 +114,14 @@ public abstract class FilterPort { throw new RuntimeException("Illegal operation on closed " + this + "!"); } } + + protected void checkFrameType(Frame frame, boolean forceCheck) { + if ((mChecksType || forceCheck) + && mPortFormat != null + && !frame.getFormat().isCompatibleWith(mPortFormat)) { + throw new RuntimeException("Frame passed to " + this + " is of incorrect type! " + + "Expected " + mPortFormat + " but got " + frame.getFormat()); + } + } } + diff --git a/mca/filterfw/java/android/filterfw/core/FinalPort.java b/mca/filterfw/java/android/filterfw/core/FinalPort.java index 44686ad3..313e49d2 100644 --- a/mca/filterfw/java/android/filterfw/core/FinalPort.java +++ b/mca/filterfw/java/android/filterfw/core/FinalPort.java @@ -29,11 +29,13 @@ public class FinalPort extends FieldPort { } @Override - public void setFrame(Frame frame) { + protected void setFieldFrame(Frame frame, boolean isAssignment) { + assertPortIsOpen(); + checkFrameType(frame, isAssignment); if (mFilter.getStatus() != Filter.STATUS_PREINIT) { throw new RuntimeException("Attempting to modify " + this + "!"); } else { - super.setFrame(frame); + super.setFieldFrame(frame, isAssignment); super.transfer(null); } } diff --git a/mca/filterfw/java/android/filterfw/core/FrameFormat.java b/mca/filterfw/java/android/filterfw/core/FrameFormat.java index 6810e943..b105138c 100644 --- a/mca/filterfw/java/android/filterfw/core/FrameFormat.java +++ b/mca/filterfw/java/android/filterfw/core/FrameFormat.java @@ -247,8 +247,63 @@ public class FrameFormat { } public boolean mayBeCompatibleWith(FrameFormat specification) { - // TODO - return false; + // Check base type + if (specification.getBaseType() != TYPE_UNSPECIFIED + && getBaseType() != TYPE_UNSPECIFIED + && getBaseType() != specification.getBaseType()) { + return false; + } + + // Check target + if (specification.getTarget() != TARGET_UNSPECIFIED + && getTarget() != TARGET_UNSPECIFIED + && getTarget() != specification.getTarget()) { + return false; + } + + // Check bytes per sample + if (specification.getBytesPerSample() != BYTES_PER_SAMPLE_UNSPECIFIED + && getBytesPerSample() != BYTES_PER_SAMPLE_UNSPECIFIED + && getBytesPerSample() != specification.getBytesPerSample()) { + return false; + } + + // Check number of dimensions + if (specification.getDimensionCount() > 0 + && getDimensionCount() > 0 + && getDimensionCount() != specification.getDimensionCount()) { + return false; + } + + // Check dimensions + for (int i = 0; i < specification.getDimensionCount(); ++i) { + int specDim = specification.getDimension(i); + if (specDim != SIZE_UNSPECIFIED + && getDimension(i) != SIZE_UNSPECIFIED + && getDimension(i) != specDim) { + return false; + } + } + + // Check class + if (specification.getObjectClass() != null && getObjectClass() != null) { + if (!specification.getObjectClass().isAssignableFrom(getObjectClass())) { + return false; + } + } + + // Check meta-data + if (specification.mMetaData != null && mMetaData != null) { + for (String specKey : specification.mMetaData.keySet()) { + if (mMetaData.containsKey(specKey) + && !mMetaData.get(specKey).equals(specification.mMetaData.get(specKey))) { + return false; + } + } + } + + // Passed all the tests + return true; } public static int bytesPerSampleOf(int baseType) { @@ -348,10 +403,15 @@ public class FrameFormat { int valuesPerSample = getValuesPerSample(); String sampleCountString = valuesPerSample == 1 ? "" : String.valueOf(valuesPerSample); String targetString = mTarget == TARGET_UNSPECIFIED ? "" : (targetToString(mTarget) + " "); + String classString = mObjectClass == null + ? "" + : ("class(" + mObjectClass.getSimpleName() + ") "); + return targetString + baseTypeToString(mBaseType) + sampleCountString + dimensionsToString(mDimensions) + + classString + metaDataToString(mMetaData); } @@ -372,6 +432,8 @@ public class FrameFormat { } boolean isReplaceableBy(FrameFormat format) { - return mTarget == format.mTarget && getSize() == format.getSize(); + return mTarget == format.mTarget + && getSize() == format.getSize() + && Arrays.equals(format.mDimensions, mDimensions); } } diff --git a/mca/filterfw/java/android/filterfw/core/FrameManager.java b/mca/filterfw/java/android/filterfw/core/FrameManager.java index 92ca09c3..70fd4267 100644 --- a/mca/filterfw/java/android/filterfw/core/FrameManager.java +++ b/mca/filterfw/java/android/filterfw/core/FrameManager.java @@ -30,7 +30,11 @@ public abstract class FrameManager { public abstract Frame newBoundFrame(FrameFormat format, int bindingType, long bindingId); - public abstract Frame duplicateFrame(Frame frame); + public Frame duplicateFrame(Frame frame) { + Frame result = newFrame(frame.getFormat()); + result.setDataFromFrame(frame); + return result; + } public Frame duplicateFrameToTarget(Frame frame, int newTarget) { MutableFrameFormat newFormat = frame.getFormat().mutableCopy(); diff --git a/mca/filterfw/java/android/filterfw/core/InputPort.java b/mca/filterfw/java/android/filterfw/core/InputPort.java index 81feb2c4..12d19371 100644 --- a/mca/filterfw/java/android/filterfw/core/InputPort.java +++ b/mca/filterfw/java/android/filterfw/core/InputPort.java @@ -73,6 +73,10 @@ public abstract class InputPort extends FilterPort { return hasFrame() || !isBlocking(); } + public boolean acceptsFrame() { + return !hasFrame(); + } + public void transfer(FilterContext context) { } } diff --git a/mca/filterfw/java/android/filterfw/core/OutputPort.java b/mca/filterfw/java/android/filterfw/core/OutputPort.java index b64bccf7..872dbddc 100644 --- a/mca/filterfw/java/android/filterfw/core/OutputPort.java +++ b/mca/filterfw/java/android/filterfw/core/OutputPort.java @@ -76,7 +76,7 @@ public class OutputPort extends FilterPort { } public boolean isReady() { - return (isOpen() && !hasFrame()) || !isBlocking(); + return (isOpen() && mTargetPort.acceptsFrame()) || !isBlocking(); } @Override diff --git a/mca/filterfw/java/android/filterfw/core/ProgramPort.java b/mca/filterfw/java/android/filterfw/core/ProgramPort.java index 75319282..f3c3a3c9 100644 --- a/mca/filterfw/java/android/filterfw/core/ProgramPort.java +++ b/mca/filterfw/java/android/filterfw/core/ProgramPort.java @@ -48,6 +48,7 @@ public class ProgramPort extends FieldPort { if (fieldValue != null) { Program program = (Program)fieldValue; program.setHostValue(mVarName, mValue); + mValue = null; } } catch (IllegalAccessException e) { throw new RuntimeException( diff --git a/mca/filterfw/java/android/filterfw/core/RoundRobinScheduler.java b/mca/filterfw/java/android/filterfw/core/RoundRobinScheduler.java index 6bad86f0..12cbf19f 100644 --- a/mca/filterfw/java/android/filterfw/core/RoundRobinScheduler.java +++ b/mca/filterfw/java/android/filterfw/core/RoundRobinScheduler.java @@ -47,12 +47,13 @@ public class RoundRobinScheduler extends Scheduler { int firstNdx = -1; for (Filter filter : all_filters) { if (filter.canProcess()) { - if (pos <= mLastPos && first == null) { - // store the first available filter - first = filter; - firstNdx = pos; - } - else { + if (pos <= mLastPos) { + if (first == null) { + // store the first available filter + first = filter; + firstNdx = pos; + } + } else { // return the next available filter since last mLastPos = pos; return filter; diff --git a/mca/filterfw/java/android/filterfw/core/SimpleFrameManager.java b/mca/filterfw/java/android/filterfw/core/SimpleFrameManager.java index 6e1e03f8..5b85dd55 100644 --- a/mca/filterfw/java/android/filterfw/core/SimpleFrameManager.java +++ b/mca/filterfw/java/android/filterfw/core/SimpleFrameManager.java @@ -89,12 +89,6 @@ public class SimpleFrameManager extends FrameManager { } @Override - public Frame duplicateFrame(Frame frame) { - // TODO - return null; - } - - @Override public Frame retainFrame(Frame frame) { frame.incRefCount(); return frame; diff --git a/mca/filterfw/java/android/filterfw/core/StreamPort.java b/mca/filterfw/java/android/filterfw/core/StreamPort.java index 13d2e3b8..c6035954 100644 --- a/mca/filterfw/java/android/filterfw/core/StreamPort.java +++ b/mca/filterfw/java/android/filterfw/core/StreamPort.java @@ -49,6 +49,8 @@ public class StreamPort extends InputPort { private void assignFrame(Frame frame, boolean persistent) { assertPortIsOpen(); + checkFrameType(frame, persistent); + if (persistent) { if (mFrame != null) { mFrame.release(); diff --git a/mca/filterfw/java/android/filterfw/format/ObjectFormat.java b/mca/filterfw/java/android/filterfw/format/ObjectFormat.java index b60c9327..06468573 100644 --- a/mca/filterfw/java/android/filterfw/format/ObjectFormat.java +++ b/mca/filterfw/java/android/filterfw/format/ObjectFormat.java @@ -29,7 +29,7 @@ public class ObjectFormat { public static MutableFrameFormat fromClass(Class clazz, int count, int target) { // Create frame format MutableFrameFormat result = new MutableFrameFormat(FrameFormat.TYPE_OBJECT, target); - result.setObjectClass(clazz); + result.setObjectClass(getBoxedClass(clazz)); if (count != FrameFormat.SIZE_UNSPECIFIED) { result.setDimensions(count); } @@ -68,4 +68,34 @@ public class ObjectFormat { return FrameFormat.BYTES_PER_SAMPLE_UNSPECIFIED; } } + + private static Class getBoxedClass(Class type) { + // Check if type is primitive + if (type.isPrimitive()) { + // Yes -> box it + if (type == boolean.class) { + return java.lang.Boolean.class; + } else if (type == byte.class) { + return java.lang.Byte.class; + } else if (type == char.class) { + return java.lang.Character.class; + } else if (type == short.class) { + return java.lang.Short.class; + } else if (type == int.class) { + return java.lang.Integer.class; + } else if (type == long.class) { + return java.lang.Long.class; + } else if (type == float.class) { + return java.lang.Float.class; + } else if (type == double.class) { + return java.lang.Double.class; + } else { + throw new IllegalArgumentException( + "Unknown primitive type: " + type.getSimpleName() + "!"); + } + } else { + // No -> return it + return type; + } + } } diff --git a/mca/filterfw/native/core/gl_frame.cpp b/mca/filterfw/native/core/gl_frame.cpp index 9b658174..66e1640e 100644 --- a/mca/filterfw/native/core/gl_frame.cpp +++ b/mca/filterfw/native/core/gl_frame.cpp @@ -316,7 +316,6 @@ bool GLFrame::BindTextureToFBO() { return true; // Otherwise check if the texture and fbo states are acceptable - // TODO: Is it okay to bind an FBO to a texture that has been allocated with pixels??? if (texture_state_ != kStateGenerated && texture_state_ != kStateAllocated && fbo_state_ != kStateGenerated) @@ -362,15 +361,6 @@ bool GLFrame::BindTextureToFBO() { } bool GLFrame::UploadTexturePixels(const uint8_t* pixels) { - if (fbo_state_ == kStateBound) { - // Get rid of FBO - // TODO: Is this necessary? Can we just leave the FBO? - glDeleteFramebuffers(1, &fbo_id_); - fbo_state_ = kStateUninitialized; - if (GLEnv::CheckGLError("Releasing FBO")) - return false; - } - // Bind the texture object FocusTexture(); diff --git a/mca/filterpacks/imageproc/java/BitmapSource.java b/mca/filterpacks/imageproc/java/BitmapSource.java index 76b2efde..e62214d7 100644 --- a/mca/filterpacks/imageproc/java/BitmapSource.java +++ b/mca/filterpacks/imageproc/java/BitmapSource.java @@ -36,7 +36,7 @@ import android.graphics.Bitmap; */ public class BitmapSource extends Filter { - @GenerateFinalPort(name = "target") + @GenerateFieldPort(name = "target") String mTargetString; @GenerateFieldPort(name = "bitmap") @@ -56,14 +56,15 @@ public class BitmapSource extends Filter { @Override public void setupPorts() { // Setup output format - mTarget = FrameFormat.readTargetString(mTargetString); - FrameFormat outputFormat = ImageFormat.create(ImageFormat.COLORSPACE_RGBA, mTarget); + FrameFormat outputFormat = ImageFormat.create(ImageFormat.COLORSPACE_RGBA, + FrameFormat.TARGET_UNSPECIFIED); // Add output port addOutputPort("image", outputFormat); } public void loadImage(FilterContext filterContext) { + mTarget = FrameFormat.readTargetString(mTargetString); FrameFormat outputFormat = ImageFormat.create(mBitmap.getWidth(), mBitmap.getHeight(), ImageFormat.COLORSPACE_RGBA, diff --git a/mca/filterpacks/imageproc/java/FisheyeFilter.java b/mca/filterpacks/imageproc/java/FisheyeFilter.java index b4e1ad08..b2ddf577 100644 --- a/mca/filterpacks/imageproc/java/FisheyeFilter.java +++ b/mca/filterpacks/imageproc/java/FisheyeFilter.java @@ -27,6 +27,7 @@ import android.filterfw.core.NativeProgram; import android.filterfw.core.NativeFrame; import android.filterfw.core.Program; import android.filterfw.core.ShaderProgram; +import android.filterfw.format.ImageFormat; import android.util.Log; @@ -36,7 +37,7 @@ import java.util.Set; /** * @hide */ -public class FisheyeFilter extends SimpleImageFilter { +public class FisheyeFilter extends Filter { private static final String TAG = "FisheyeFilter"; // This parameter has range between 0 and 1. It controls the effect of radial distortion. @@ -45,8 +46,11 @@ public class FisheyeFilter extends SimpleImageFilter { @GenerateFieldPort(name = "scale") private float mScale; - private float mWidth; - private float mHeight; + private Program mProgram; + + private int mWidth = 0; + private int mHeight = 0; + private int mTarget = FrameFormat.TARGET_UNSPECIFIED; private static final String mFisheyeShader = "precision mediump float;\n" + @@ -72,33 +76,84 @@ public class FisheyeFilter extends SimpleImageFilter { "}\n"; public FisheyeFilter(String name) { - super(name, null); + super(name); } @Override - protected Program getNativeProgram() { - Program result = new NativeProgram("filterpack_imageproc", "fisheye"); - initProgram(result); - return result; + public void setupPorts() { + addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA)); + addOutputBasedOnInput("image", "image"); } @Override - protected Program getShaderProgram() { - Program result = new ShaderProgram(mFisheyeShader); - initProgram(result); + public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) { + return inputFormat; + } - return result; + public void initProgram(int target) { + switch (target) { + case FrameFormat.TARGET_GPU: + mProgram = new ShaderProgram(mFisheyeShader); + break; + + default: + throw new RuntimeException("Filter FisheyeFilter does not support frames of " + + "target " + target + "!"); + } + mTarget = target; + } + + @Override + public void process(FilterContext context) { + // Get input frame + Frame input = pullInput("image"); + FrameFormat inputFormat = input.getFormat(); + + // Create output frame + Frame output = context.getFrameManager().newFrame(inputFormat); + + // Create program if not created already + if (mProgram == null || inputFormat.getTarget() != mTarget) { + initProgram(inputFormat.getTarget()); + } + + // Check if the frame size has changed + if (inputFormat.getWidth() != mWidth || inputFormat.getHeight() != mHeight) { + updateFrameSize(inputFormat.getWidth(), inputFormat.getHeight()); + } + + // Process + mProgram.process(input, output); + + // Push output + pushOutput("image", output); + + // Release pushed frame + output.release(); } @Override public void fieldPortValueUpdated(String name, FilterContext context) { if (mProgram != null) { - updateProgram(mProgram); + updateProgramParams(); } } - private void updateProgram(Program program) { - float pi = 3.14159265f; + private void updateFrameSize(int width, int height) { + float center[] = {0.5f * width, 0.5f * height}; + + mProgram.setHostValue("center", center); + mProgram.setHostValue("inv_width", 1.0f / width); + mProgram.setHostValue("inv_height", 1.0f / height); + + mWidth = width; + mHeight = height; + + updateProgramParams(); + } + + private void updateProgramParams() { + final float pi = 3.14159265f; float alpha = mScale * 2.0f + 0.75f; float bound2 = 0.25f * (mWidth * mWidth + mHeight * mHeight); @@ -109,22 +164,10 @@ public class FisheyeFilter extends SimpleImageFilter { (float) Math.atan(alpha / bound * (float) Math.sqrt(radius2 - bound2)); float factor = bound / max_radian; - program.setHostValue("radius2",radius2); - program.setHostValue("factor", factor); - program.setHostValue("alpha", (float) (mScale * 2.0 + 0.75)); + mProgram.setHostValue("radius2",radius2); + mProgram.setHostValue("factor", factor); + mProgram.setHostValue("alpha", (float) (mScale * 2.0 + 0.75)); } - private void initProgram(Program program) { - Frame input = pullInput("image"); - mWidth = (float) input.getFormat().getWidth(); - mHeight = (float) input.getFormat().getHeight(); - float center[] = {0.5f * mWidth, 0.5f * mHeight}; - - program.setHostValue("center", center); - program.setHostValue("inv_width", 1.0f / mWidth); - program.setHostValue("inv_height", 1.0f / mHeight); - - updateProgram(program); - } } diff --git a/mca/filterpacks/imageproc/native/brightness.c b/mca/filterpacks/imageproc/native/brightness.c index f712427c..f4addf1e 100644 --- a/mca/filterpacks/imageproc/native/brightness.c +++ b/mca/filterpacks/imageproc/native/brightness.c @@ -25,6 +25,11 @@ typedef struct { float brightness; } BrightnessParameters; +typedef union { + int value; + char rgba[4]; +} Pixel; + void brightness_init(void** user_data) { (*user_data) = malloc(sizeof(BrightnessParameters)); } @@ -59,8 +64,9 @@ int brightness_process(const char** inputs, } // Get the input and output pointers - const char* input_ptr = inputs[0]; - char* output_ptr = output; + const int* input_ptr = (int*)inputs[0]; + int* output_ptr = (int*)output; + const int* end_ptr = input_ptr + (output_size / 4); if (!input_ptr || !output_ptr) { LOGE("Brightness: No input or output pointer found!"); return 0; @@ -71,12 +77,19 @@ int brightness_process(const char** inputs, const float brightness = params->brightness; // Run the brightness adjustment - int i; - const int b = (int)(brightness * 255.0f); - int val = 0; - for (i = 0; i < output_size; ++i) { - val = (*(input_ptr++) * b) / 255; - *(output_ptr++) = val > 255 ? 255 : val; + const int factor = (int)(brightness * 255.0f); + Pixel pixel; + while (input_ptr < end_ptr) { + pixel.value = *(input_ptr++); + + const short r = (pixel.rgba[0] * factor) / 255; + const short g = (pixel.rgba[1] * factor) / 255; + const short b = (pixel.rgba[2] * factor) / 255; + + *(output_ptr++) = (r > 255 ? 255 : r) + | ((g > 255 ? 255 : g) << 8) + | ((b > 255 ? 255 : b) << 16) + | (pixel.rgba[3] << 24); } return 1; diff --git a/mca/filterpacks/numeric/java/SinWaveFilter.java b/mca/filterpacks/numeric/java/SinWaveFilter.java new file mode 100644 index 00000000..30beb354 --- /dev/null +++ b/mca/filterpacks/numeric/java/SinWaveFilter.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package android.filterpacks.numeric; + +import android.filterfw.core.Filter; +import android.filterfw.core.FilterContext; +import android.filterfw.core.Frame; +import android.filterfw.core.FrameFormat; +import android.filterfw.core.GenerateFieldPort; +import android.filterfw.core.JavaFrame; +import android.filterfw.format.ObjectFormat; + +import java.lang.Math; + +/** + * @hide + */ +public class SinWaveFilter extends Filter { + + @GenerateFieldPort(name = "stepSize", hasDefault = true) + private float mStepSize = 0.05f; + + private float mValue = 0.0f; + + private FrameFormat mOutputFormat; + + public SinWaveFilter(String name) { + super(name); + } + + @Override + public void setupPorts() { + mOutputFormat = ObjectFormat.fromClass(Float.class, FrameFormat.TARGET_JAVA); + addOutputPort("value", mOutputFormat); + } + + @Override + public void open(FilterContext env) { + mValue = 0.0f; + } + + @Override + public void process(FilterContext env) { + Frame output = env.getFrameManager().newFrame(mOutputFormat); + output.setObjectValue(((float)Math.sin(mValue) + 1.0f) / 2.0f); + pushOutput("value", output); + mValue += mStepSize; + } + + +} diff --git a/mca/filterpacks/numeric/java/package-info.java b/mca/filterpacks/numeric/java/package-info.java new file mode 100644 index 00000000..55088eb6 --- /dev/null +++ b/mca/filterpacks/numeric/java/package-info.java @@ -0,0 +1,4 @@ +/** + * @hide + */ +package android.filterpacks.numeric; diff --git a/mca/filterpacks/performance/java/Throughput.java b/mca/filterpacks/performance/java/Throughput.java new file mode 100644 index 00000000..7b064cea --- /dev/null +++ b/mca/filterpacks/performance/java/Throughput.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package android.filterpacks.performance; + +/** + * @hide + */ +public class Throughput { + + private final int mTotalFrames; + private final int mPeriodFrames; + private final int mPeriodTime; + private final int mPixels; + + public Throughput(int totalFrames, int periodFrames, int periodTime, int pixels) { + mTotalFrames = totalFrames; + mPeriodFrames = periodFrames; + mPeriodTime = periodTime; + mPixels = pixels; + } + + public int getTotalFrameCount() { + return mTotalFrames; + } + + public int getPeriodFrameCount() { + return mPeriodFrames; + } + + public int getPeriodTime() { + return mPeriodTime; + } + + public float getFramesPerSecond() { + return mPeriodFrames / (float)mPeriodTime; + } + + public float getNanosPerPixel() { + double frameTimeInNanos = (mPeriodTime / (double)mPeriodFrames) * 1000000.0; + return (float)(frameTimeInNanos / mPixels); + } +} diff --git a/mca/filterpacks/performance/java/ThroughputFilter.java b/mca/filterpacks/performance/java/ThroughputFilter.java new file mode 100644 index 00000000..9e707662 --- /dev/null +++ b/mca/filterpacks/performance/java/ThroughputFilter.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package android.filterpacks.performance; + +import android.filterfw.core.Filter; +import android.filterfw.core.FilterContext; +import android.filterfw.core.Frame; +import android.filterfw.core.FrameFormat; +import android.filterfw.core.GenerateFieldPort; +import android.filterfw.core.JavaFrame; +import android.filterfw.format.ObjectFormat; +import android.os.SystemClock; + +/** + * @hide + */ +public class ThroughputFilter extends Filter { + + @GenerateFieldPort(name = "period", hasDefault = true) + private int mPeriod = 5; + + private long mLastTime = 0; + + private int mTotalFrameCount = 0; + private int mPeriodFrameCount = 0; + + private FrameFormat mOutputFormat; + + public ThroughputFilter(String name) { + super(name); + } + + @Override + public void setupPorts() { + // Add input ports + addInputPort("frame"); + + // Add output ports + mOutputFormat = ObjectFormat.fromClass(Throughput.class, FrameFormat.TARGET_JAVA); + addOutputBasedOnInput("frame", "frame"); + addOutputPort("throughput", mOutputFormat); + } + + @Override + public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) { + return inputFormat; + } + + @Override + public void open(FilterContext env) { + mTotalFrameCount = 0; + mPeriodFrameCount = 0; + mLastTime = 0; + } + + @Override + public void process(FilterContext context) { + // Pass through input frame + Frame input = pullInput("frame"); + pushOutput("frame", input); + + // Update stats + ++mTotalFrameCount; + ++mPeriodFrameCount; + + // Check clock + if (mLastTime == 0) { + mLastTime = SystemClock.elapsedRealtime(); + } + long curTime = SystemClock.elapsedRealtime(); + + // Output throughput info if time period is up + if ((curTime - mLastTime) >= (mPeriod * 1000)) { + FrameFormat inputFormat = input.getFormat(); + int pixelCount = inputFormat.getWidth() * inputFormat.getHeight(); + Throughput throughput = new Throughput(mTotalFrameCount, + mPeriodFrameCount, + mPeriod, + pixelCount); + Frame throughputFrame = context.getFrameManager().newFrame(mOutputFormat); + throughputFrame.setObjectValue(throughput); + pushOutput("throughput", throughputFrame); + mLastTime = curTime; + mPeriodFrameCount = 0; + } + } + + +} diff --git a/mca/filterpacks/performance/java/package-info.java b/mca/filterpacks/performance/java/package-info.java new file mode 100644 index 00000000..8b77bbb3 --- /dev/null +++ b/mca/filterpacks/performance/java/package-info.java @@ -0,0 +1,4 @@ +/** + * @hide + */ +package android.filterpacks.performance; -- 2.11.0