blob: 92ff1cb651d6d3e4a423e3925e4dd2266a4d36bf [file] [log] [blame]
/*
* Copyright 2014 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.hardware.camera2.cts.rs;
import static android.hardware.camera2.cts.helpers.Preconditions.*;
import android.hardware.camera2.cts.helpers.UncheckedCloseable;
import android.renderscript.Allocation;
import android.renderscript.RenderScript;
import android.util.Log;
import java.util.HashMap;
/**
* Base class for all renderscript script abstractions.
*
* <p>Each script has exactly one input and one output allocation, and is able to execute
* one {@link android.renderscript.Script} script file.</p>
*
* <p>Each script owns it's input allocation, but not the output allocation.</p>
*
* <p>Subclasses of this class must implement exactly one of two constructors:
* <ul>
* <li>{@code ScriptSubclass(AllocationInfo inputInfo)}
* - if it expects 0 parameters
* <li>{@code ScriptSubclass(AllocationInfo inputInfo, ParameterMap<T> parameterMap))}
* - if it expects 1 or more parameters
* </ul>
*
* @param <T> A concrete subclass of {@link android.renderscript.Script}
*/
public abstract class Script<T extends android.renderscript.Script> implements UncheckedCloseable {
/**
* A type-safe heterogenous parameter map for script parameters.
*
* @param <ScriptT> A concrete subclass of {@link Script}.
*/
public static class ParameterMap<ScriptT extends Script<?>> {
private final HashMap<Script.ScriptParameter<ScriptT, ?>, Object> mParameterMap =
new HashMap<Script.ScriptParameter<ScriptT, ?>, Object>();
/**
* Create a new parameter map with 0 parameters.</p>
*/
public ParameterMap() {}
/**
* Get the value associated with the given parameter key.
*
* @param parameter A type-safe key corresponding to a parameter.
*
* @return The value, or {@code null} if none was set.
*
* @param <T> The type of the value
*
* @throws NullPointerException if parameter was {@code null}
*/
@SuppressWarnings("unchecked")
public <T> T get(Script.ScriptParameter<ScriptT, T> parameter) {
checkNotNull("parameter", parameter);
return (T) mParameterMap.get(parameter);
}
/**
* Sets the value associated with the given parameter key.
*
* @param parameter A type-safe key corresponding to a parameter.
* @param value The value
*
* @param <T> The type of the value
*
* @throws NullPointerException if parameter was {@code null}
* @throws NullPointerException if value was {@code null}
*/
public <T> void set(Script.ScriptParameter<ScriptT, T> parameter, T value) {
checkNotNull("parameter", parameter);
checkNotNull("value", value);
if (!parameter.getValueClass().isInstance(value)) {
throw new IllegalArgumentException(
"Runtime type mismatch between " + parameter + " and value " + value);
}
mParameterMap.put(parameter, value);
}
/**
* Whether or not at least one parameter has been {@link #set}.
*
* @return true if there is at least one element in the map
*/
public boolean isEmpty() {
return mParameterMap.isEmpty();
}
/**
* Check if the parameter has been {@link #set} to a value.
*
* @param parameter A type-safe key corresponding to a parameter.
* @return true if there is a value corresponding to this parameter, false otherwise.
*/
public boolean contains(Script.ScriptParameter<ScriptT, ?> parameter) {
checkNotNull("parameter", parameter);
return mParameterMap.containsKey(parameter);
}
}
/**
* A type-safe parameter key to be used with {@link ParameterMap}.
*
* @param <J> A concrete subclass of {@link Script}.
* @param <K> The type of the value that the parameter holds.
*/
public static class ScriptParameter<J extends Script<?>, K> {
private final Class<J> mScriptClass;
private final Class<K> mValueClass;
ScriptParameter(Class<J> jClass, Class<K> kClass) {
checkNotNull("jClass", jClass);
checkNotNull("kClass", kClass);
mScriptClass = jClass;
mValueClass = kClass;
}
/**
* Get the runtime class associated with the value.
*/
public Class<K> getValueClass() {
return mValueClass;
}
/**
* Compare with another object.
*
* <p>Two script parameters are considered equal only if their script class and value
* class are both equal.</p>
*/
@SuppressWarnings("unchecked")
@Override
public boolean equals(Object other) {
if (other instanceof ScriptParameter) {
ScriptParameter<J, K> otherParam = (ScriptParameter<J,K>) other;
return mScriptClass.equals(otherParam.mScriptClass) &&
mValueClass.equals(otherParam.mValueClass);
}
return false;
}
/**
* Gets the hash code for this object.
*/
@Override
public int hashCode() {
return mScriptClass.hashCode() ^ mValueClass.hashCode();
}
}
private static final String TAG = "Script";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
protected final AllocationCache mCache = RenderScriptSingleton.getCache();
protected final RenderScript mRS = RenderScriptSingleton.getRS();
protected final AllocationInfo mInputInfo;
protected final AllocationInfo mOutputInfo;
protected Allocation mOutputAllocation;
protected Allocation mInputAllocation;
protected final T mScript;
private boolean mClosed = false;
/**
* Gets the {@link AllocationInfo info} associated with this script's input.
*
* @return A non-{@code null} {@link AllocationInfo} object.
*
* @throws IllegalStateException If the script has already been {@link #close closed}.
*/
public AllocationInfo getInputInfo() {
checkNotClosed();
return mInputInfo;
}
/**
* Gets the {@link AllocationInfo info} associated with this script's output.
*
* @return A non-{@code null} {@link AllocationInfo} object.
*
* @throws IllegalStateException If the script has already been {@link #close closed}.
*/
public AllocationInfo getOutputInfo() {
checkNotClosed();
return mOutputInfo;
}
/**
* Set the input.
*
* <p>Must be called before executing any scripts.</p>
*
* @throws IllegalStateException If the script has already been {@link #close closed}.
*/
void setInput(Allocation allocation) {
checkNotClosed();
checkNotNull("allocation", allocation);
checkEquals("allocation info", AllocationInfo.newInstance(allocation),
"input info", mInputInfo);
// Scripts own the input, so return old input to cache if the input changes
if (mInputAllocation != allocation) {
mCache.returnToCacheIfNotNull(mInputAllocation);
}
mInputAllocation = allocation;
updateScriptInput();
}
protected abstract void updateScriptInput();
/**
* Set the output.
*
* <p>Must be called before executing any scripts.</p>
*
* @throws IllegalStateException If the script has already been {@link #close closed}.
*/
void setOutput(Allocation allocation) {
checkNotClosed();
checkNotNull("allocation", allocation);
checkEquals("allocation info", AllocationInfo.newInstance(allocation),
"output info", mOutputInfo);
// Scripts do not own the output, simply set a reference to the new one.
mOutputAllocation = allocation;
}
protected Script(AllocationInfo inputInfo, AllocationInfo outputInfo, T rsScript) {
checkNotNull("inputInfo", inputInfo);
checkNotNull("outputInfo", outputInfo);
checkNotNull("rsScript", rsScript);
mInputInfo = inputInfo;
mOutputInfo = outputInfo;
mScript = rsScript;
if (VERBOSE) {
Log.v(TAG, String.format("%s - inputInfo = %s, outputInfo = %s, rsScript = %s",
getName(), inputInfo, outputInfo, rsScript));
}
}
/**
* Get the {@link Allocation} associated with this script's input.</p>
*
* @return The input {@link Allocation}, which is never {@code null}.
*
* @throws IllegalStateException If the script has already been {@link #close closed}.
*/
public Allocation getInput() {
checkNotClosed();
return mInputAllocation;
}
/**
* Get the {@link Allocation} associated with this script's output.</p>
*
* @return The output {@link Allocation}, which is never {@code null}.
*
* @throws IllegalStateException If the script has already been {@link #close closed}.
*/
public Allocation getOutput() {
checkNotClosed();
return mOutputAllocation;
}
/**
* Execute the script's kernel against the input/output {@link Allocation allocations}.
*
* <p>Once this is complete, the output will have the new data available (for either
* the next script, or to read out with a copy).</p>
*
* @throws IllegalStateException If the script has already been {@link #close closed}.
*/
public void execute() {
checkNotClosed();
if (mInputAllocation == null || mOutputAllocation == null) {
throw new IllegalStateException("Both inputs and outputs must have been set");
}
executeUnchecked();
}
/**
* Get the name of this script.
*
* <p>The name is the short hand name of the concrete class backing this script.</p>
*
* <p>This method works even if the script has already been {@link #close closed}.</p>
*
* @return A string representing the script name.
*/
public String getName() {
return getClass().getSimpleName();
}
protected abstract void executeUnchecked();
protected void checkNotClosed() {
if (mClosed) {
throw new IllegalStateException("Script has been closed");
}
}
/**
* Destroy the underlying script object and return the input allocation back to the
* {@link AllocationCache cache}.
*
* <p>This method has no effect if called more than once.</p>
*/
@Override
public void close() {
if (mClosed) return;
// Scripts own the input allocation. They do NOT own outputs.
mCache.returnToCacheIfNotNull(mInputAllocation);
mScript.destroy();
mClosed = true;
}
@Override
protected void finalize() throws Throwable {
try {
close();
} finally {
super.finalize();
}
}
protected static RenderScript getRS() {
return RenderScriptSingleton.getRS();
}
}