Home | History | Annotate | Download | only in rs
      1 /*
      2  * Copyright 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.hardware.camera2.cts.rs;
     18 
     19 import static android.hardware.camera2.cts.helpers.Preconditions.*;
     20 
     21 import android.hardware.camera2.cts.helpers.UncheckedCloseable;
     22 import android.renderscript.Allocation;
     23 import android.renderscript.RenderScript;
     24 import android.util.Log;
     25 
     26 import java.util.HashMap;
     27 
     28 /**
     29  * Base class for all renderscript script abstractions.
     30  *
     31  * <p>Each script has exactly one input and one output allocation, and is able to execute
     32  * one {@link android.renderscript.Script} script file.</p>
     33  *
     34  * <p>Each script owns it's input allocation, but not the output allocation.</p>
     35  *
     36  * <p>Subclasses of this class must implement exactly one of two constructors:
     37  * <ul>
     38  * <li>{@code ScriptSubclass(AllocationInfo inputInfo)}
     39  *  - if it expects 0 parameters
     40  * <li>{@code ScriptSubclass(AllocationInfo inputInfo, ParameterMap<T> parameterMap))}
     41  *  - if it expects 1 or more parameters
     42  * </ul>
     43  *
     44  * @param <T> A concrete subclass of {@link android.renderscript.Script}
     45  */
     46 public abstract class Script<T extends android.renderscript.Script> implements UncheckedCloseable {
     47 
     48     /**
     49      * A type-safe heterogenous parameter map for script parameters.
     50      *
     51      * @param  A concrete subclass of {@link Script}.
     52      */
     53     public static class ParameterMap<ScriptT extends Script<?>> {
     54         private final HashMap<Script.ScriptParameter<ScriptT, ?>, Object> mParameterMap =
     55                 new HashMap<Script.ScriptParameter<ScriptT, ?>, Object>();
     56 
     57         /**
     58          * Create a new parameter map with 0 parameters.</p>
     59          */
     60         public ParameterMap() {}
     61 
     62         /**
     63          * Get the value associated with the given parameter key.
     64          *
     65          * @param parameter A type-safe key corresponding to a parameter.
     66          *
     67          * @return The value, or {@code null} if none was set.
     68          *
     69          * @param <T> The type of the value
     70          *
     71          * @throws NullPointerException if parameter was {@code null}
     72          */
     73         @SuppressWarnings("unchecked")
     74         public <T> T get(Script.ScriptParameter<ScriptT, T> parameter) {
     75             checkNotNull("parameter", parameter);
     76 
     77             return (T) mParameterMap.get(parameter);
     78         }
     79 
     80         /**
     81          * Sets the value associated with the given parameter key.
     82          *
     83          * @param parameter A type-safe key corresponding to a parameter.
     84          * @param value The value
     85          *
     86          * @param <T> The type of the value
     87          *
     88          * @throws NullPointerException if parameter was {@code null}
     89          * @throws NullPointerException if value was {@code null}
     90          */
     91         public <T> void set(Script.ScriptParameter<ScriptT, T> parameter, T value) {
     92             checkNotNull("parameter", parameter);
     93             checkNotNull("value", value);
     94 
     95             if (!parameter.getValueClass().isInstance(value)) {
     96                 throw new IllegalArgumentException(
     97                         "Runtime type mismatch between " + parameter + " and value " + value);
     98             }
     99 
    100             mParameterMap.put(parameter, value);
    101         }
    102 
    103         /**
    104          * Whether or not at least one parameter has been {@link #set}.
    105          *
    106          * @return true if there is at least one element in the map
    107          */
    108         public boolean isEmpty() {
    109             return mParameterMap.isEmpty();
    110         }
    111 
    112         /**
    113          * Check if the parameter has been {@link #set} to a value.
    114          *
    115          * @param parameter A type-safe key corresponding to a parameter.
    116          * @return true if there is a value corresponding to this parameter, false otherwise.
    117          */
    118         public boolean contains(Script.ScriptParameter<ScriptT, ?> parameter) {
    119             checkNotNull("parameter", parameter);
    120 
    121             return mParameterMap.containsKey(parameter);
    122         }
    123     }
    124 
    125     /**
    126      * A type-safe parameter key to be used with {@link ParameterMap}.
    127      *
    128      * @param <J> A concrete subclass of {@link Script}.
    129      * @param <K> The type of the value that the parameter holds.
    130      */
    131     public static class ScriptParameter<J extends Script<?>, K> {
    132         private final Class<J> mScriptClass;
    133         private final Class<K> mValueClass;
    134 
    135         ScriptParameter(Class<J> jClass, Class<K> kClass) {
    136             checkNotNull("jClass", jClass);
    137             checkNotNull("kClass", kClass);
    138 
    139             mScriptClass = jClass;
    140             mValueClass = kClass;
    141         }
    142 
    143         /**
    144          * Get the runtime class associated with the value.
    145          */
    146         public Class<K> getValueClass() {
    147             return mValueClass;
    148         }
    149 
    150         /**
    151          * Compare with another object.
    152          *
    153          * <p>Two script parameters are considered equal only if their script class and value
    154          * class are both equal.</p>
    155          */
    156         @SuppressWarnings("unchecked")
    157         @Override
    158         public boolean equals(Object other) {
    159             if (other instanceof ScriptParameter) {
    160                 ScriptParameter<J, K> otherParam = (ScriptParameter<J,K>) other;
    161 
    162                 return mScriptClass.equals(otherParam.mScriptClass) &&
    163                         mValueClass.equals(otherParam.mValueClass);
    164             }
    165 
    166             return false;
    167         }
    168 
    169         /**
    170          * Gets the hash code for this object.
    171          */
    172         @Override
    173         public int hashCode() {
    174             return mScriptClass.hashCode() ^ mValueClass.hashCode();
    175         }
    176     }
    177 
    178     private static final String TAG = "Script";
    179     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
    180 
    181     protected final AllocationCache mCache = RenderScriptSingleton.getCache();
    182     protected final RenderScript mRS = RenderScriptSingleton.getRS();
    183 
    184     protected final AllocationInfo mInputInfo;
    185     protected final AllocationInfo mOutputInfo;
    186 
    187     protected Allocation mOutputAllocation;
    188     protected Allocation mInputAllocation;
    189 
    190     protected final T mScript;
    191     private boolean mClosed = false;
    192 
    193     /**
    194      * Gets the {@link AllocationInfo info} associated with this script's input.
    195      *
    196      * @return A non-{@code null} {@link AllocationInfo} object.
    197      *
    198      * @throws IllegalStateException If the script has already been {@link #close closed}.
    199      */
    200     public AllocationInfo getInputInfo() {
    201         checkNotClosed();
    202 
    203         return mInputInfo;
    204     }
    205     /**
    206      * Gets the {@link AllocationInfo info} associated with this script's output.
    207      *
    208      * @return A non-{@code null} {@link AllocationInfo} object.
    209      *
    210      * @throws IllegalStateException If the script has already been {@link #close closed}.
    211      */
    212     public AllocationInfo getOutputInfo() {
    213         checkNotClosed();
    214 
    215         return mOutputInfo;
    216     }
    217 
    218     /**
    219      * Set the input.
    220      *
    221      * <p>Must be called before executing any scripts.</p>
    222      *
    223      * @throws IllegalStateException If the script has already been {@link #close closed}.
    224      */
    225     void setInput(Allocation allocation) {
    226         checkNotClosed();
    227         checkNotNull("allocation", allocation);
    228         checkEquals("allocation info", AllocationInfo.newInstance(allocation),
    229                 "input info", mInputInfo);
    230 
    231         // Scripts own the input, so return old input to cache if the input changes
    232         if (mInputAllocation != allocation) {
    233             mCache.returnToCacheIfNotNull(mInputAllocation);
    234         }
    235 
    236         mInputAllocation = allocation;
    237         updateScriptInput();
    238     }
    239 
    240     protected abstract void updateScriptInput();
    241 
    242     /**
    243      * Set the output.
    244      *
    245      * <p>Must be called before executing any scripts.</p>
    246      *
    247      * @throws IllegalStateException If the script has already been {@link #close closed}.
    248      */
    249     void setOutput(Allocation allocation) {
    250         checkNotClosed();
    251         checkNotNull("allocation", allocation);
    252         checkEquals("allocation info", AllocationInfo.newInstance(allocation),
    253                 "output info", mOutputInfo);
    254 
    255         // Scripts do not own the output, simply set a reference to the new one.
    256         mOutputAllocation = allocation;
    257     }
    258 
    259     protected Script(AllocationInfo inputInfo, AllocationInfo outputInfo, T rsScript) {
    260         checkNotNull("inputInfo", inputInfo);
    261         checkNotNull("outputInfo", outputInfo);
    262         checkNotNull("rsScript", rsScript);
    263 
    264         mInputInfo = inputInfo;
    265         mOutputInfo = outputInfo;
    266         mScript = rsScript;
    267 
    268         if (VERBOSE) {
    269             Log.v(TAG, String.format("%s - inputInfo = %s, outputInfo = %s, rsScript = %s",
    270                     getName(), inputInfo, outputInfo, rsScript));
    271         }
    272     }
    273 
    274     /**
    275      * Get the {@link Allocation} associated with this script's input.</p>
    276      *
    277      * @return The input {@link Allocation}, which is never {@code null}.
    278      *
    279      * @throws IllegalStateException If the script has already been {@link #close closed}.
    280      */
    281     public Allocation getInput() {
    282         checkNotClosed();
    283 
    284         return mInputAllocation;
    285     }
    286     /**
    287      * Get the {@link Allocation} associated with this script's output.</p>
    288      *
    289      * @return The output {@link Allocation}, which is never {@code null}.
    290      *
    291      * @throws IllegalStateException If the script has already been {@link #close closed}.
    292      */
    293     public Allocation getOutput() {
    294         checkNotClosed();
    295 
    296         return mOutputAllocation;
    297     }
    298 
    299     /**
    300      * Execute the script's kernel against the input/output {@link Allocation allocations}.
    301      *
    302      * <p>Once this is complete, the output will have the new data available (for either
    303      * the next script, or to read out with a copy).</p>
    304      *
    305      * @throws IllegalStateException If the script has already been {@link #close closed}.
    306      */
    307     public void execute() {
    308         checkNotClosed();
    309 
    310         if (mInputAllocation == null || mOutputAllocation == null) {
    311             throw new IllegalStateException("Both inputs and outputs must have been set");
    312         }
    313 
    314         executeUnchecked();
    315     }
    316 
    317     /**
    318      * Get the name of this script.
    319      *
    320      * <p>The name is the short hand name of the concrete class backing this script.</p>
    321      *
    322      * <p>This method works even if the script has already been {@link #close closed}.</p>
    323      *
    324      * @return A string representing the script name.
    325      */
    326     public String getName() {
    327         return getClass().getSimpleName();
    328     }
    329 
    330     protected abstract void executeUnchecked();
    331 
    332     protected void checkNotClosed() {
    333         if (mClosed) {
    334             throw new IllegalStateException("Script has been closed");
    335         }
    336     }
    337 
    338     /**
    339      * Destroy the underlying script object and return the input allocation back to the
    340      * {@link AllocationCache cache}.
    341      *
    342      * <p>This method has no effect if called more than once.</p>
    343      */
    344     @Override
    345     public void close() {
    346         if (mClosed) return;
    347 
    348         // Scripts own the input allocation. They do NOT own outputs.
    349         mCache.returnToCacheIfNotNull(mInputAllocation);
    350 
    351         mScript.destroy();
    352 
    353         mClosed = true;
    354     }
    355 
    356     @Override
    357     protected void finalize() throws Throwable {
    358         try {
    359             close();
    360         } finally {
    361             super.finalize();
    362         }
    363     }
    364 
    365     protected static RenderScript getRS() {
    366         return RenderScriptSingleton.getRS();
    367     }
    368 }
    369