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