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 com.android.internal.widget.remotecompose.core.CompanionOperation;
19 import com.android.internal.widget.remotecompose.core.Operation;
20 import com.android.internal.widget.remotecompose.core.Operations;
21 import com.android.internal.widget.remotecompose.core.RemoteContext;
22 import com.android.internal.widget.remotecompose.core.VariableSupport;
23 import com.android.internal.widget.remotecompose.core.WireBuffer;
24 
25 import java.util.Arrays;
26 import java.util.HashMap;
27 import java.util.List;
28 
29 /**
30  * Operation to deal with bitmap data
31  * On getting an Image during a draw call the bitmap is compressed and saved
32  * in playback the image is decompressed
33  */
34 public class ShaderData implements Operation, VariableSupport {
35     int mShaderTextId; // the actual text of a shader
36     int mShaderID; // allows shaders to be referenced by number
37     HashMap<String, float[]> mUniformRawFloatMap = null;
38     HashMap<String, float[]> mUniformFloatMap = null;
39     HashMap<String, int[]> mUniformIntMap = null;
40     HashMap<String, Integer> mUniformBitmapMap = null;
41 
42     public static final int MAX_IMAGE_DIMENSION = 8000;
43 
44     public static final Companion COMPANION = new Companion();
45 
ShaderData(int shaderID, int shaderTextId, HashMap<String, float[]> floatMap, HashMap<String, int[]> intMap, HashMap<String, Integer> bitmapMap)46     public ShaderData(int shaderID,
47                       int shaderTextId,
48                       HashMap<String, float[]> floatMap,
49                       HashMap<String, int[]> intMap,
50                       HashMap<String, Integer> bitmapMap) {
51         mShaderID = shaderID;
52         mShaderTextId = shaderTextId;
53         if (floatMap != null) {
54             mUniformFloatMap = new HashMap<>();
55             mUniformRawFloatMap = new HashMap<>();
56 
57             for (String name : floatMap.keySet()) {
58                 mUniformRawFloatMap.put(name, floatMap.get(name));
59                 mUniformFloatMap.put(name, floatMap.get(name));
60             }
61         }
62 
63         if (intMap != null) {
64             mUniformIntMap = new HashMap<>();
65             for (String name : intMap.keySet()) {
66                 mUniformIntMap.put(name, intMap.get(name));
67             }
68         }
69         if (bitmapMap != null) {
70             mUniformBitmapMap = new HashMap<>();
71             for (String name : bitmapMap.keySet()) {
72                 mUniformBitmapMap.put(name, bitmapMap.get(name));
73             }
74         }
75 
76     }
77 
getShaderTextId()78     public int getShaderTextId() {
79         return mShaderTextId;
80     }
81 
82     /**
83      * get names of all known floats
84      * @return
85      */
getUniformFloatNames()86     public String[] getUniformFloatNames() {
87         if (mUniformFloatMap == null) return new String[0];
88         return mUniformFloatMap.keySet().toArray(new String[0]);
89     }
90 
91     /**
92      * Get float values associated with the name
93      * @param name
94      * @return
95      */
getUniformFloats(String name)96     public float[] getUniformFloats(String name) {
97         return mUniformFloatMap.get(name);
98     }
99 
100     /**
101      * get the name of all know uniform integers
102      * @return
103      */
getUniformIntegerNames()104     public String[] getUniformIntegerNames() {
105         if (mUniformIntMap == null) return new String[0];
106         return mUniformIntMap.keySet().toArray(new String[0]);
107     }
108 
109     /**
110      * Get Int value associated with the name
111      * @param name
112      * @return
113      */
getUniformInts(String name)114     public int[] getUniformInts(String name) {
115         return mUniformIntMap.get(name);
116     }
117 
118     /**
119      * get list of uniform Bitmaps
120      * @return
121      */
getUniformBitmapNames()122     public String[] getUniformBitmapNames() {
123         if (mUniformBitmapMap == null) return new String[0];
124         return mUniformBitmapMap.keySet().toArray(new String[0]);
125     }
126 
127     /**
128      * Get a bitmap stored under that name
129      * @param name
130      * @return
131      */
getUniformBitmapId(String name)132     public int getUniformBitmapId(String name) {
133         return mUniformBitmapMap.get(name);
134     }
135 
136     @Override
write(WireBuffer buffer)137     public void write(WireBuffer buffer) {
138         COMPANION.apply(buffer, mShaderID, mShaderTextId,
139                 mUniformFloatMap, mUniformIntMap, mUniformBitmapMap);
140     }
141 
142     @Override
toString()143     public String toString() {
144         return "SHADER DATA " + mShaderID;
145     }
146 
147     @Override
updateVariables(RemoteContext context)148     public void updateVariables(RemoteContext context) {
149         for (String name : mUniformRawFloatMap.keySet()) {
150             float[] value = mUniformRawFloatMap.get(name);
151             float[] out = null;
152             for (int i = 0; i < value.length; i++) {
153                 if (Float.isNaN(value[i])) {
154                     if (out == null) { // need to copy
155                         out = Arrays.copyOf(value, value.length);
156                     }
157                     out[i] = context.getFloat(Utils.idFromNan(value[i]));
158                 }
159             }
160             mUniformFloatMap.put(name, out == null ? value : out);
161         }
162     }
163 
164     @Override
registerListening(RemoteContext context)165     public void registerListening(RemoteContext context) {
166         for (String name : mUniformRawFloatMap.keySet()) {
167             float[] value = mUniformRawFloatMap.get(name);
168             for (int i = 0; i < value.length; i++) {
169                 if (Float.isNaN(value[i])) {
170                     context.listensTo(Utils.idFromNan(value[i]), this);
171                 }
172             }
173         }
174     }
175 
176     public static class Companion implements CompanionOperation {
Companion()177         private Companion() {
178         }
179 
180         @Override
name()181         public String name() {
182             return "BitmapData";
183         }
184 
185         @Override
id()186         public int id() {
187             return Operations.DATA_SHADER;
188         }
189 
190         /**
191          * Writes out the operation to the buffer
192          * @param buffer
193          * @param shaderID
194          * @param shaderTextId
195          * @param floatMap
196          * @param intMap
197          * @param bitmapMap
198          */
apply(WireBuffer buffer, int shaderID, int shaderTextId, HashMap<String, float[]> floatMap, HashMap<String, int[]> intMap, HashMap<String, Integer> bitmapMap)199         public void apply(WireBuffer buffer, int shaderID, int shaderTextId,
200                           HashMap<String, float[]> floatMap,
201                           HashMap<String, int[]> intMap,
202                           HashMap<String, Integer> bitmapMap) {
203             buffer.start(Operations.DATA_SHADER);
204             buffer.writeInt(shaderID);
205 
206             buffer.writeInt(shaderTextId);
207             int floatSize = (floatMap == null) ? 0 : floatMap.size();
208             int intSize = (intMap == null) ? 0 : intMap.size();
209             int bitmapSize = (bitmapMap == null) ? 0 : bitmapMap.size();
210             int sizes = floatSize | (intSize << 8) | (bitmapSize << 16);
211             buffer.writeInt(sizes);
212 
213             if (floatSize > 0) {
214 
215                 for (String name : floatMap.keySet()) {
216                     buffer.writeUTF8(name);
217                     float[] values = floatMap.get(name);
218                     buffer.writeInt(values.length);
219 
220                     for (int i = 0; i < values.length; i++) {
221                         buffer.writeFloat(values[i]);
222                     }
223                 }
224             }
225 
226             if (intSize > 0) {
227                 for (String name : intMap.keySet()) {
228                     buffer.writeUTF8(name);
229                     int[] values = intMap.get(name);
230                     buffer.writeInt(values.length);
231                     for (int i = 0; i < values.length; i++) {
232                         buffer.writeInt(values[i]);
233                     }
234                 }
235             }
236             if (bitmapSize > 0) {
237                 for (String name : bitmapMap.keySet()) {
238                     buffer.writeUTF8(name);
239                     int value = bitmapMap.get(name);
240                     buffer.writeInt(value);
241                 }
242             }
243         }
244 
245         @Override
read(WireBuffer buffer, List<Operation> operations)246         public void read(WireBuffer buffer, List<Operation> operations) {
247             int shaderID = buffer.readInt();
248             int shaderTextId = buffer.readInt();
249             HashMap<String, float[]> floatMap = null;
250             HashMap<String, int[]> intMap = null;
251             HashMap<String, Integer> bitmapMap = null;
252 
253             int sizes = buffer.readInt();
254 
255             int floatMapSize = sizes & 0xFF;
256             if (floatMapSize > 0) {
257                 floatMap = new HashMap<>();
258                 for (int i = 0; i < floatMapSize; i++) {
259                     String name = buffer.readUTF8();
260                     int len = buffer.readInt();
261                     float[] val = new float[len];
262 
263                     for (int j = 0; j < len; j++) {
264                         val[j] = buffer.readFloat();
265                     }
266 
267                     floatMap.put(name, val);
268                 }
269             }
270             int intMapSize = (sizes >> 8) & 0xFF;
271 
272             if (intMapSize > 0) {
273 
274                 intMap = new HashMap<>();
275                 for (int i = 0; i < intMapSize; i++) {
276                     String name = buffer.readUTF8();
277                     int len = buffer.readInt();
278                     int[] val = new int[len];
279                     for (int j = 0; j < len; j++) {
280                         val[j] = buffer.readInt();
281                     }
282                     intMap.put(name, val);
283                 }
284             }
285             int bitmapMapSize = (sizes >> 16) & 0xFF;
286 
287             if (bitmapMapSize > 0) {
288                 bitmapMap = new HashMap<>();
289                 for (int i = 0; i < bitmapMapSize; i++) {
290                     String name = buffer.readUTF8();
291                     int val = buffer.readInt();
292                     bitmapMap.put(name, val);
293                 }
294             }
295             operations.add(new ShaderData(shaderID, shaderTextId,
296                     floatMap, intMap, bitmapMap));
297         }
298     }
299 
300     @Override
apply(RemoteContext context)301     public void apply(RemoteContext context) {
302         context.loadShader(mShaderID, this);
303     }
304 
305     @Override
deepToString(String indent)306     public String deepToString(String indent) {
307         return indent + toString();
308     }
309 }
310