1 /* 2 * Copyright (C) 2023 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 package com.android.internal.widget.remotecompose.core.operations; 17 18 import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.BYTE; 19 import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT_ARRAY; 20 import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT; 21 import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT_ARRAY; 22 import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.SHORT; 23 import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.UTF8; 24 25 import android.annotation.NonNull; 26 import android.annotation.Nullable; 27 28 import com.android.internal.widget.remotecompose.core.Operation; 29 import com.android.internal.widget.remotecompose.core.Operations; 30 import com.android.internal.widget.remotecompose.core.RemoteContext; 31 import com.android.internal.widget.remotecompose.core.VariableSupport; 32 import com.android.internal.widget.remotecompose.core.WireBuffer; 33 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; 34 import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation; 35 import com.android.internal.widget.remotecompose.core.serialize.MapSerializer; 36 import com.android.internal.widget.remotecompose.core.serialize.Serializable; 37 38 import java.util.Arrays; 39 import java.util.HashMap; 40 import java.util.List; 41 42 /** 43 * Operation to deal with bitmap data On getting an Image during a draw call the bitmap is 44 * compressed and saved in playback the image is decompressed 45 */ 46 public class ShaderData extends Operation implements VariableSupport, Serializable { 47 private static final int OP_CODE = Operations.DATA_SHADER; 48 private static final String CLASS_NAME = "ShaderData"; 49 int mShaderTextId; // the actual text of a shader 50 int mShaderID; // allows shaders to be referenced by number 51 @Nullable HashMap<String, float[]> mUniformRawFloatMap = null; 52 @Nullable HashMap<String, float[]> mUniformFloatMap = null; 53 @Nullable HashMap<String, int[]> mUniformIntMap; 54 @Nullable HashMap<String, Integer> mUniformBitmapMap = null; 55 private boolean mShaderValid = false; 56 ShaderData( int shaderID, int shaderTextId, @Nullable HashMap<String, float[]> floatMap, @Nullable HashMap<String, int[]> intMap, @Nullable HashMap<String, Integer> bitmapMap)57 public ShaderData( 58 int shaderID, 59 int shaderTextId, 60 @Nullable HashMap<String, float[]> floatMap, 61 @Nullable HashMap<String, int[]> intMap, 62 @Nullable HashMap<String, Integer> bitmapMap) { 63 mShaderID = shaderID; 64 mShaderTextId = shaderTextId; 65 if (floatMap != null) { 66 mUniformFloatMap = new HashMap<>(); 67 mUniformRawFloatMap = new HashMap<>(); 68 69 for (String name : floatMap.keySet()) { 70 mUniformRawFloatMap.put(name, floatMap.get(name)); 71 mUniformFloatMap.put(name, floatMap.get(name)); 72 } 73 } 74 75 if (intMap != null) { 76 mUniformIntMap = new HashMap<>(); 77 for (String name : intMap.keySet()) { 78 mUniformIntMap.put(name, intMap.get(name)); 79 } 80 } 81 if (bitmapMap != null) { 82 mUniformBitmapMap = new HashMap<>(); 83 for (String name : bitmapMap.keySet()) { 84 mUniformBitmapMap.put(name, bitmapMap.get(name)); 85 } 86 } 87 } 88 getShaderTextId()89 public int getShaderTextId() { 90 return mShaderTextId; 91 } 92 93 /** 94 * get names of all known floats 95 * 96 * @return Names of all uniform floats or empty array 97 */ 98 @NonNull getUniformFloatNames()99 public String[] getUniformFloatNames() { 100 if (mUniformFloatMap == null) return new String[0]; 101 return mUniformFloatMap.keySet().toArray(new String[0]); 102 } 103 104 /** 105 * Get float values associated with the name 106 * 107 * @param name name of uniform 108 * @return value of uniform 109 */ getUniformFloats(@onNull String name)110 public @NonNull float[] getUniformFloats(@NonNull String name) { 111 return mUniformFloatMap != null ? mUniformFloatMap.get(name) : new float[0]; 112 } 113 114 /** 115 * get the name of all know uniform integers 116 * 117 * @return Name of all integer uniforms 118 */ 119 @NonNull getUniformIntegerNames()120 public String[] getUniformIntegerNames() { 121 if (mUniformIntMap == null) return new String[0]; 122 return mUniformIntMap.keySet().toArray(new String[0]); 123 } 124 125 /** 126 * Get Int value associated with the name 127 * 128 * @param name Name of uniform 129 * @return value of uniform 130 */ getUniformInts(@onNull String name)131 public @NonNull int[] getUniformInts(@NonNull String name) { 132 return mUniformIntMap != null ? mUniformIntMap.get(name) : new int[0]; 133 } 134 135 /** 136 * get list of uniform Bitmaps 137 * 138 * @return Name of all bitmap uniforms 139 */ 140 @NonNull getUniformBitmapNames()141 public String[] getUniformBitmapNames() { 142 if (mUniformBitmapMap == null) return new String[0]; 143 return mUniformBitmapMap.keySet().toArray(new String[0]); 144 } 145 146 /** 147 * Get a bitmap stored under that name 148 * 149 * @param name Name of bitmap uniform 150 * @return Bitmap ID 151 */ getUniformBitmapId(@onNull String name)152 public int getUniformBitmapId(@NonNull String name) { 153 return mUniformBitmapMap != null 154 ? mUniformBitmapMap.get(name) 155 : -1; // TODO: what is the proper return value here? -- bbade@ 156 } 157 158 @Override write(@onNull WireBuffer buffer)159 public void write(@NonNull WireBuffer buffer) { 160 apply( 161 buffer, 162 mShaderID, 163 mShaderTextId, 164 mUniformFloatMap, 165 mUniformIntMap, 166 mUniformBitmapMap); 167 } 168 169 @NonNull 170 @Override toString()171 public String toString() { 172 return "SHADER DATA " + mShaderID; 173 } 174 175 @Override updateVariables(@onNull RemoteContext context)176 public void updateVariables(@NonNull RemoteContext context) { 177 if (mUniformRawFloatMap == null) { 178 return; 179 } 180 for (String name : mUniformRawFloatMap.keySet()) { // TODO: potential npe 181 float[] value = mUniformRawFloatMap.get(name); 182 float[] out = null; 183 for (int i = 0; i < value.length; i++) { 184 if (Float.isNaN(value[i])) { 185 if (out == null) { // need to copy 186 out = Arrays.copyOf(value, value.length); 187 } 188 out[i] = context.getFloat(Utils.idFromNan(value[i])); 189 } 190 } 191 mUniformFloatMap.put(name, out == null ? value : out); 192 } 193 } 194 195 @Override registerListening(@onNull RemoteContext context)196 public void registerListening(@NonNull RemoteContext context) { 197 if (mUniformFloatMap == null) { 198 return; 199 } 200 for (String name : mUniformRawFloatMap.keySet()) { // TODO: potential npe 201 float[] value = mUniformRawFloatMap.get(name); 202 for (float v : value) { 203 if (Float.isNaN(v)) { 204 context.listensTo(Utils.idFromNan(v), this); 205 } 206 } 207 } 208 } 209 210 /** 211 * The name of the class 212 * 213 * @return the name 214 */ 215 @NonNull name()216 public static String name() { 217 return CLASS_NAME; 218 } 219 220 /** 221 * The OP_CODE for this command 222 * 223 * @return the opcode 224 */ id()225 public static int id() { 226 return OP_CODE; 227 } 228 229 /** 230 * Writes out the operation to the buffer 231 * 232 * @param buffer buffer to write into 233 * @param shaderID id of shader 234 * @param shaderTextId id of text of shader 235 * @param floatMap the map of float uniforms 236 * @param intMap the map of int uniforms 237 * @param bitmapMap the map of bitmap uniforms 238 */ apply( @onNull WireBuffer buffer, int shaderID, int shaderTextId, @Nullable HashMap<String, float[]> floatMap, @Nullable HashMap<String, int[]> intMap, @Nullable HashMap<String, Integer> bitmapMap)239 public static void apply( 240 @NonNull WireBuffer buffer, 241 int shaderID, 242 int shaderTextId, 243 @Nullable HashMap<String, float[]> floatMap, 244 @Nullable HashMap<String, int[]> intMap, 245 @Nullable HashMap<String, Integer> bitmapMap) { 246 buffer.start(OP_CODE); 247 buffer.writeInt(shaderID); 248 249 buffer.writeInt(shaderTextId); 250 int floatSize = (floatMap == null) ? 0 : floatMap.size(); 251 int intSize = (intMap == null) ? 0 : intMap.size(); 252 int bitmapSize = (bitmapMap == null) ? 0 : bitmapMap.size(); 253 int sizes = floatSize | (intSize << 8) | (bitmapSize << 16); 254 buffer.writeInt(sizes); 255 256 if (floatSize > 0) { 257 258 for (String name : floatMap.keySet()) { 259 buffer.writeUTF8(name); 260 float[] values = floatMap.get(name); 261 buffer.writeInt(values.length); 262 263 for (float value : values) { 264 buffer.writeFloat(value); 265 } 266 } 267 } 268 269 if (intSize > 0) { 270 for (String name : intMap.keySet()) { 271 buffer.writeUTF8(name); 272 int[] values = intMap.get(name); 273 buffer.writeInt(values.length); 274 for (int value : values) { 275 buffer.writeInt(value); 276 } 277 } 278 } 279 if (bitmapSize > 0) { 280 for (String name : bitmapMap.keySet()) { 281 buffer.writeUTF8(name); 282 int value = bitmapMap.get(name); 283 buffer.writeInt(value); 284 } 285 } 286 } 287 288 /** 289 * Read this operation and add it to the list of operations 290 * 291 * @param buffer the buffer to read 292 * @param operations the list of operations that will be added to 293 */ read(@onNull WireBuffer buffer, @NonNull List<Operation> operations)294 public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { 295 int shaderID = buffer.readInt(); 296 int shaderTextId = buffer.readInt(); 297 HashMap<String, float[]> floatMap = null; 298 HashMap<String, int[]> intMap = null; 299 HashMap<String, Integer> bitmapMap = null; 300 301 int sizes = buffer.readInt(); 302 303 int floatMapSize = sizes & 0xFF; 304 if (floatMapSize > 0) { 305 floatMap = new HashMap<>(); 306 for (int i = 0; i < floatMapSize; i++) { 307 String name = buffer.readUTF8(); 308 int len = buffer.readInt(); 309 float[] val = new float[len]; 310 311 for (int j = 0; j < len; j++) { 312 val[j] = buffer.readFloat(); 313 } 314 315 floatMap.put(name, val); 316 } 317 } 318 int intMapSize = (sizes >> 8) & 0xFF; 319 320 if (intMapSize > 0) { 321 322 intMap = new HashMap<>(); 323 for (int i = 0; i < intMapSize; i++) { 324 String name = buffer.readUTF8(); 325 int len = buffer.readInt(); 326 int[] val = new int[len]; 327 for (int j = 0; j < len; j++) { 328 val[j] = buffer.readInt(); 329 } 330 intMap.put(name, val); 331 } 332 } 333 int bitmapMapSize = (sizes >> 16) & 0xFF; 334 335 if (bitmapMapSize > 0) { 336 bitmapMap = new HashMap<>(); 337 for (int i = 0; i < bitmapMapSize; i++) { 338 String name = buffer.readUTF8(); 339 int val = buffer.readInt(); 340 bitmapMap.put(name, val); 341 } 342 } 343 operations.add(new ShaderData(shaderID, shaderTextId, floatMap, intMap, bitmapMap)); 344 } 345 346 /** 347 * Populate the documentation with a description of this operation 348 * 349 * @param doc to append the description to. 350 */ documentation(@onNull DocumentationBuilder doc)351 public static void documentation(@NonNull DocumentationBuilder doc) { 352 doc.operation("Data Operations", OP_CODE, CLASS_NAME) 353 .description("Shader") 354 .field(DocumentedOperation.INT, "shaderID", "id of shader") 355 .field(BYTE, " floatSize", "number of float uniforms") 356 .field(BYTE, " intSize", "number of int uniform") 357 .field(SHORT, " intSize", "number of int uniform") 358 .field(UTF8, "floatName", "name of float uniform") 359 .field(INT, "length", "length") 360 .field(FLOAT_ARRAY, "VALUE", "float uniform (max 4)") 361 .field(UTF8, "IntName", "id of shader text") 362 .field(INT, "length", "length of uniform") 363 .field(INT_ARRAY, "VALUE", "int uniform (max 4)") 364 .field(UTF8, "bitmapName", "name of bitmap") 365 .field(INT, "VALUE", "id of bitmap"); 366 } 367 368 @Override apply(@onNull RemoteContext context)369 public void apply(@NonNull RemoteContext context) { 370 if (mShaderValid) { 371 context.loadShader(mShaderID, this); 372 } 373 } 374 375 @NonNull 376 @Override deepToString(@onNull String indent)377 public String deepToString(@NonNull String indent) { 378 return indent + toString(); 379 } 380 381 /** 382 * Enable or disable the shader 383 * 384 * @param shaderValid if true shader can be used 385 */ enable(boolean shaderValid)386 public void enable(boolean shaderValid) { 387 mShaderValid = shaderValid; 388 } 389 390 @Override serialize(MapSerializer serializer)391 public void serialize(MapSerializer serializer) { 392 serializer 393 .addType(CLASS_NAME) 394 .add("shaderTextId", mShaderTextId) 395 .add("shaderID", mShaderID) 396 .add("uniformRawFloatMap", mUniformRawFloatMap) 397 .add("uniformFloatMap", mUniformFloatMap) 398 .add("uniformBitmapMap", mUniformBitmapMap); 399 } 400 } 401