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 <ScriptT> 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          */
ParameterMap()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")
get(Script.ScriptParameter<ScriptT, T> parameter)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          */
set(Script.ScriptParameter<ScriptT, T> parameter, T value)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          */
isEmpty()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          */
contains(Script.ScriptParameter<ScriptT, ?> parameter)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 
ScriptParameter(Class<J> jClass, Class<K> kClass)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          */
getValueClass()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
equals(Object other)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
hashCode()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      */
getInputInfo()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      */
getOutputInfo()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      */
setInput(Allocation allocation)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 
updateScriptInput()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      */
setOutput(Allocation allocation)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 
Script(AllocationInfo inputInfo, AllocationInfo outputInfo, T rsScript)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      */
getInput()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      */
getOutput()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      */
execute()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      */
getName()326     public String getName() {
327         return getClass().getSimpleName();
328     }
329 
executeUnchecked()330     protected abstract void executeUnchecked();
331 
checkNotClosed()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
close()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
finalize()357     protected void finalize() throws Throwable {
358         try {
359             close();
360         } finally {
361             super.finalize();
362         }
363     }
364 
getRS()365     protected static RenderScript getRS() {
366         return RenderScriptSingleton.getRS();
367     }
368 }
369