blob: 83084591f1c5920ec0aa48a5da78146f2db5b822 [file] [log] [blame]
Romain Guy2d614592010-06-09 18:21:37 -07001/*
Romain Guy735738c2012-12-03 12:34:51 -08002 * Copyright (C) 2013 The Android Open Source Project
Romain Guy2d614592010-06-09 18:21:37 -07003 *
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
Romain Guy2d614592010-06-09 18:21:37 -070017package android.view;
18
Dianne Hackbornc68c9132011-07-29 01:25:18 -070019import android.content.ComponentCallbacks2;
Romain Guy7d7b5492011-01-24 16:33:45 -080020import android.graphics.Paint;
21import android.graphics.Rect;
Romain Guyaa6c24c2011-04-28 18:40:04 -070022import android.graphics.SurfaceTexture;
Romain Guybd431522012-09-26 13:31:25 -070023import android.opengl.EGL14;
Romain Guy407ec782011-08-24 17:06:58 -070024import android.opengl.GLUtils;
Dianne Hackborn717a25d2011-11-15 18:59:59 -080025import android.opengl.ManagedEGLContext;
26import android.os.Handler;
27import android.os.Looper;
Romain Guy02ccac62011-06-24 13:20:23 -070028import android.os.SystemClock;
29import android.os.SystemProperties;
Romain Guy77e67cf2012-06-19 16:38:50 -070030import android.os.Trace;
Romain Guy98e4a522013-01-07 10:58:34 -080031import android.util.DisplayMetrics;
Romain Guye3924992010-06-10 18:51:21 -070032import android.util.Log;
Romain Guy8ff6b9e2011-11-09 20:10:18 -080033import com.google.android.gles_jni.EGLImpl;
Romain Guy2d614592010-06-09 18:21:37 -070034
35import javax.microedition.khronos.egl.EGL10;
36import javax.microedition.khronos.egl.EGL11;
37import javax.microedition.khronos.egl.EGLConfig;
38import javax.microedition.khronos.egl.EGLContext;
39import javax.microedition.khronos.egl.EGLDisplay;
40import javax.microedition.khronos.egl.EGLSurface;
Romain Guye3924992010-06-10 18:51:21 -070041import javax.microedition.khronos.opengles.GL;
Romain Guy2d614592010-06-09 18:21:37 -070042
Romain Guya9582652011-11-10 14:20:10 -080043import java.io.File;
Romain Guya676ad72012-02-13 17:47:10 -080044import java.io.PrintWriter;
Romain Guy48ef4a92013-01-10 18:38:46 -080045import java.util.Arrays;
Romain Guy77e67cf2012-06-19 16:38:50 -070046import java.util.concurrent.locks.ReentrantLock;
Romain Guya9582652011-11-10 14:20:10 -080047
Romain Guy484c7192011-07-25 11:56:33 -070048import static javax.microedition.khronos.egl.EGL10.*;
49
Romain Guy2d614592010-06-09 18:21:37 -070050/**
Romain Guy8d4aeb72013-02-12 16:08:55 -080051 * Interface for rendering a view hierarchy using hardware acceleration.
Romain Guy52036b12013-02-14 18:03:37 -080052 *
Romain Guy2d614592010-06-09 18:21:37 -070053 * @hide
54 */
Romain Guy61c8c9c2010-08-09 20:48:09 -070055public abstract class HardwareRenderer {
Romain Guy4f6aff32011-01-12 16:21:41 -080056 static final String LOG_TAG = "HardwareRenderer";
Romain Guyfb8b7632010-08-23 21:05:08 -070057
Romain Guy52339202010-09-03 16:04:46 -070058 /**
Romain Guya9582652011-11-10 14:20:10 -080059 * Name of the file that holds the shaders cache.
60 */
61 private static final String CACHE_PATH_SHADERS = "com.android.opengl.shaders_cache";
62
63 /**
Romain Guy7d7b5492011-01-24 16:33:45 -080064 * Turn on to only refresh the parts of the screen that need updating.
Romain Guy069ea0e2011-02-08 12:24:52 -080065 * When turned on the property defined by {@link #RENDER_DIRTY_REGIONS_PROPERTY}
Romain Guy52036b12013-02-14 18:03:37 -080066 * must also have the value "true".
Romain Guy7d7b5492011-01-24 16:33:45 -080067 */
Romain Guy52036b12013-02-14 18:03:37 -080068 static final boolean RENDER_DIRTY_REGIONS = true;
Romain Guy7d7b5492011-01-24 16:33:45 -080069
70 /**
Romain Guy069ea0e2011-02-08 12:24:52 -080071 * System property used to enable or disable dirty regions invalidation.
72 * This property is only queried if {@link #RENDER_DIRTY_REGIONS} is true.
73 * The default value of this property is assumed to be true.
74 *
75 * Possible values:
76 * "true", to enable partial invalidates
77 * "false", to disable partial invalidates
78 */
Romain Guy4b8c4f82012-04-27 15:48:35 -070079 static final String RENDER_DIRTY_REGIONS_PROPERTY = "debug.hwui.render_dirty_regions";
Romain Guy069ea0e2011-02-08 12:24:52 -080080
81 /**
Romain Guya676ad72012-02-13 17:47:10 -080082 * System property used to enable or disable hardware rendering profiling.
83 * The default value of this property is assumed to be false.
Chet Haase09280602012-04-03 16:15:34 -070084 *
Romain Guya676ad72012-02-13 17:47:10 -080085 * When profiling is enabled, the adb shell dumpsys gfxinfo command will
86 * output extra information about the time taken to execute by the last
87 * frames.
88 *
89 * Possible values:
90 * "true", to enable profiling
Romain Guy48ef4a92013-01-10 18:38:46 -080091 * "visual_bars", to enable profiling and visualize the results on screen
92 * "visual_lines", to enable profiling and visualize the results on screen
Romain Guya676ad72012-02-13 17:47:10 -080093 * "false", to disable profiling
Romain Guy672433d2013-01-04 19:05:13 -080094 *
Romain Guy48ef4a92013-01-10 18:38:46 -080095 * @see #PROFILE_PROPERTY_VISUALIZE_BARS
96 * @see #PROFILE_PROPERTY_VISUALIZE_LINES
Romain Guya4fef022013-01-07 11:18:38 -080097 *
Romain Guy4b8c4f82012-04-27 15:48:35 -070098 * @hide
Romain Guya676ad72012-02-13 17:47:10 -080099 */
Romain Guy4b8c4f82012-04-27 15:48:35 -0700100 public static final String PROFILE_PROPERTY = "debug.hwui.profile";
Romain Guya676ad72012-02-13 17:47:10 -0800101
102 /**
Romain Guya4fef022013-01-07 11:18:38 -0800103 * Value for {@link #PROFILE_PROPERTY}. When the property is set to this
Romain Guy48ef4a92013-01-10 18:38:46 -0800104 * value, profiling data will be visualized on screen as a bar chart.
Romain Guy672433d2013-01-04 19:05:13 -0800105 *
106 * @hide
107 */
Romain Guy48ef4a92013-01-10 18:38:46 -0800108 public static final String PROFILE_PROPERTY_VISUALIZE_BARS = "visual_bars";
109
110 /**
111 * Value for {@link #PROFILE_PROPERTY}. When the property is set to this
112 * value, profiling data will be visualized on screen as a line chart.
113 *
114 * @hide
115 */
116 public static final String PROFILE_PROPERTY_VISUALIZE_LINES = "visual_lines";
Romain Guy672433d2013-01-04 19:05:13 -0800117
118 /**
Chet Haase09280602012-04-03 16:15:34 -0700119 * System property used to specify the number of frames to be used
120 * when doing hardware rendering profiling.
121 * The default value of this property is #PROFILE_MAX_FRAMES.
122 *
123 * When profiling is enabled, the adb shell dumpsys gfxinfo command will
124 * output extra information about the time taken to execute by the last
125 * frames.
126 *
127 * Possible values:
128 * "60", to set the limit of frames to 60
129 */
Romain Guy4b8c4f82012-04-27 15:48:35 -0700130 static final String PROFILE_MAXFRAMES_PROPERTY = "debug.hwui.profile.maxframes";
Chet Haase09280602012-04-03 16:15:34 -0700131
132 /**
Romain Guy484c7192011-07-25 11:56:33 -0700133 * System property used to debug EGL configuration choice.
134 *
135 * Possible values:
Romain Guy484c7192011-07-25 11:56:33 -0700136 * "choice", print the chosen configuration only
137 * "all", print all possible configurations
138 */
Romain Guy4b8c4f82012-04-27 15:48:35 -0700139 static final String PRINT_CONFIG_PROPERTY = "debug.hwui.print_config";
Romain Guy484c7192011-07-25 11:56:33 -0700140
141 /**
Romain Guy7d7b5492011-01-24 16:33:45 -0800142 * Turn on to draw dirty regions every other frame.
Romain Guyb04f7e92012-02-15 12:36:54 -0800143 *
144 * Possible values:
145 * "true", to enable dirty regions debugging
146 * "false", to disable dirty regions debugging
Romain Guy4b8c4f82012-04-27 15:48:35 -0700147 *
148 * @hide
Romain Guy7d7b5492011-01-24 16:33:45 -0800149 */
Romain Guy4b8c4f82012-04-27 15:48:35 -0700150 public static final String DEBUG_DIRTY_REGIONS_PROPERTY = "debug.hwui.show_dirty_regions";
Romain Guy4ff0cf42012-08-06 14:51:10 -0700151
152 /**
153 * Turn on to flash hardware layers when they update.
154 *
155 * Possible values:
156 * "true", to enable hardware layers updates debugging
157 * "false", to disable hardware layers updates debugging
158 *
159 * @hide
160 */
161 public static final String DEBUG_SHOW_LAYERS_UPDATES_PROPERTY =
162 "debug.hwui.show_layers_updates";
163
Romain Guy7d7b5492011-01-24 16:33:45 -0800164 /**
Romain Guy7c450aa2012-09-21 19:15:00 -0700165 * Turn on to show overdraw level.
166 *
167 * Possible values:
168 * "true", to enable overdraw debugging
169 * "false", to disable overdraw debugging
170 *
171 * @hide
172 */
173 public static final String DEBUG_SHOW_OVERDRAW_PROPERTY = "debug.hwui.show_overdraw";
174
175 /**
Romain Guy08bca882013-02-25 16:21:30 -0800176 * Turn on to debug non-rectangular clip operations.
177 *
178 * Possible values:
179 * "hide", to disable this debug mode
180 * "highlight", highlight drawing commands tested against a non-rectangular clip
181 * "stencil", renders the clip region on screen when set
182 *
183 * @hide
184 */
185 public static final String DEBUG_SHOW_NON_RECTANGULAR_CLIP_PROPERTY =
186 "debug.hwui.show_non_rect_clip";
187
188 /**
Romain Guy52339202010-09-03 16:04:46 -0700189 * A process can set this flag to false to prevent the use of hardware
190 * rendering.
191 *
192 * @hide
193 */
194 public static boolean sRendererDisabled = false;
195
Dianne Hackborn5d927c22011-09-02 12:22:18 -0700196 /**
197 * Further hardware renderer disabling for the system process.
198 *
199 * @hide
200 */
201 public static boolean sSystemRendererDisabled = false;
202
Romain Guya676ad72012-02-13 17:47:10 -0800203 /**
204 * Number of frames to profile.
205 */
Romain Guya21f8772012-05-07 10:20:52 -0700206 private static final int PROFILE_MAX_FRAMES = 128;
Romain Guya676ad72012-02-13 17:47:10 -0800207
208 /**
209 * Number of floats per profiled frame.
210 */
211 private static final int PROFILE_FRAME_DATA_COUNT = 3;
212
Romain Guy2d614592010-06-09 18:21:37 -0700213 private boolean mEnabled;
214 private boolean mRequested = true;
215
216 /**
Romain Guy67f27952010-12-07 20:09:23 -0800217 * Invoke this method to disable hardware rendering in the current process.
Romain Guy52339202010-09-03 16:04:46 -0700218 *
219 * @hide
220 */
Dianne Hackborn5d927c22011-09-02 12:22:18 -0700221 public static void disable(boolean system) {
Romain Guy52339202010-09-03 16:04:46 -0700222 sRendererDisabled = true;
Dianne Hackborn5d927c22011-09-02 12:22:18 -0700223 if (system) {
224 sSystemRendererDisabled = true;
225 }
Romain Guy52339202010-09-03 16:04:46 -0700226 }
227
228 /**
Romain Guy61c8c9c2010-08-09 20:48:09 -0700229 * Indicates whether hardware acceleration is available under any form for
230 * the view hierarchy.
231 *
232 * @return True if the view hierarchy can potentially be hardware accelerated,
233 * false otherwise
234 */
235 public static boolean isAvailable() {
236 return GLES20Canvas.isAvailable();
237 }
238
239 /**
Romain Guy2d614592010-06-09 18:21:37 -0700240 * Destroys the hardware rendering context.
Romain Guy4caa4ed2010-08-25 14:46:24 -0700241 *
242 * @param full If true, destroys all associated resources.
Romain Guy2d614592010-06-09 18:21:37 -0700243 */
Romain Guy4caa4ed2010-08-25 14:46:24 -0700244 abstract void destroy(boolean full);
Romain Guy2d614592010-06-09 18:21:37 -0700245
246 /**
247 * Initializes the hardware renderer for the specified surface.
248 *
Romain Guy786fc932012-07-24 16:24:56 -0700249 * @param surface The surface to hardware accelerate
Romain Guy2d614592010-06-09 18:21:37 -0700250 *
251 * @return True if the initialization was successful, false otherwise.
252 */
Romain Guy786fc932012-07-24 16:24:56 -0700253 abstract boolean initialize(Surface surface) throws Surface.OutOfResourcesException;
Romain Guy2a83f002011-01-18 18:28:21 -0800254
255 /**
256 * Updates the hardware renderer for the specified surface.
Romain Guy786fc932012-07-24 16:24:56 -0700257 *
258 * @param surface The surface to hardware accelerate
Romain Guy2a83f002011-01-18 18:28:21 -0800259 */
Romain Guy786fc932012-07-24 16:24:56 -0700260 abstract void updateSurface(Surface surface) throws Surface.OutOfResourcesException;
Romain Guy2d614592010-06-09 18:21:37 -0700261
262 /**
Romain Guy31f2c2e2011-11-21 10:55:41 -0800263 * Destroys the layers used by the specified view hierarchy.
Romain Guy6d7475d2011-07-27 16:28:21 -0700264 *
265 * @param view The root of the view hierarchy
266 */
267 abstract void destroyLayers(View view);
268
269 /**
Romain Guy31f2c2e2011-11-21 10:55:41 -0800270 * Destroys all hardware rendering resources associated with the specified
271 * view hierarchy.
272 *
273 * @param view The root of the view hierarchy
274 */
275 abstract void destroyHardwareResources(View view);
276
277 /**
Romain Guy03985752011-07-11 15:33:51 -0700278 * This method should be invoked whenever the current hardware renderer
Romain Guycf15efb2011-08-02 12:59:32 -0700279 * context should be reset.
Romain Guy786fc932012-07-24 16:24:56 -0700280 *
281 * @param surface The surface to hardware accelerate
Romain Guy7e1160e2011-07-08 15:49:50 -0700282 */
Romain Guy786fc932012-07-24 16:24:56 -0700283 abstract void invalidate(Surface surface);
Romain Guy03985752011-07-11 15:33:51 -0700284
285 /**
286 * This method should be invoked to ensure the hardware renderer is in
287 * valid state (for instance, to ensure the correct EGL context is bound
288 * to the current thread.)
289 *
290 * @return true if the renderer is now valid, false otherwise
291 */
292 abstract boolean validate();
Romain Guy7e1160e2011-07-08 15:49:50 -0700293
294 /**
Romain Guy1ac47652012-04-11 18:15:20 -0700295 * This method ensures the hardware renderer is in a valid state
296 * before executing the specified action.
297 *
298 * This method will attempt to set a valid state even if the window
299 * the renderer is attached to was destroyed.
300 *
301 * @return true if the action was run
302 */
303 abstract boolean safelyRun(Runnable action);
304
305 /**
Romain Guy7e1160e2011-07-08 15:49:50 -0700306 * Setup the hardware renderer for drawing. This is called whenever the
307 * size of the target surface changes or when the surface is first created.
Romain Guy2d614592010-06-09 18:21:37 -0700308 *
309 * @param width Width of the drawing surface.
310 * @param height Height of the drawing surface.
Romain Guy2d614592010-06-09 18:21:37 -0700311 */
Romain Guyfb8b7632010-08-23 21:05:08 -0700312 abstract void setup(int width, int height);
Romain Guy2d614592010-06-09 18:21:37 -0700313
Romain Guy069ea0e2011-02-08 12:24:52 -0800314 /**
Chet Haase40e03832011-10-06 08:34:13 -0700315 * Gets the current width of the surface. This is the width that the surface
316 * was last set to in a call to {@link #setup(int, int)}.
317 *
318 * @return the current width of the surface
319 */
320 abstract int getWidth();
321
322 /**
323 * Gets the current height of the surface. This is the height that the surface
324 * was last set to in a call to {@link #setup(int, int)}.
325 *
326 * @return the current width of the surface
327 */
328 abstract int getHeight();
329
330 /**
Chet Haase08837c22011-11-28 11:53:21 -0800331 * Gets the current canvas associated with this HardwareRenderer.
332 *
333 * @return the current HardwareCanvas
334 */
335 abstract HardwareCanvas getCanvas();
336
337 /**
Romain Guya676ad72012-02-13 17:47:10 -0800338 * Outputs extra debugging information in the specified file descriptor.
339 * @param pw
340 */
341 abstract void dumpGfxInfo(PrintWriter pw);
Michael Jurkaa3fabff2012-03-28 17:22:29 +0200342
343 /**
344 * Outputs the total number of frames rendered (used for fps calculations)
345 *
346 * @return the number of frames rendered
347 */
348 abstract long getFrameCount();
349
Romain Guya676ad72012-02-13 17:47:10 -0800350 /**
Romain Guy5bb3c732012-11-29 17:52:58 -0800351 * Loads system properties used by the renderer. This method is invoked
352 * whenever system properties are modified. Implementations can use this
353 * to trigger live updates of the renderer based on properties.
354 *
355 * @param surface The surface to update with the new properties.
356 * Can be null.
357 *
358 * @return True if a property has changed.
359 */
360 abstract boolean loadSystemProperties(Surface surface);
361
362 private static native boolean nLoadProperties();
363
364 /**
Romain Guya9582652011-11-10 14:20:10 -0800365 * Sets the directory to use as a persistent storage for hardware rendering
366 * resources.
367 *
368 * @param cacheDir A directory the current process can write to
Romain Guy52036b12013-02-14 18:03:37 -0800369 *
370 * @hide
Romain Guya9582652011-11-10 14:20:10 -0800371 */
372 public static void setupDiskCache(File cacheDir) {
373 nSetupShadersDiskCache(new File(cacheDir, CACHE_PATH_SHADERS).getAbsolutePath());
374 }
375
376 private static native void nSetupShadersDiskCache(String cacheFile);
377
378 /**
Jamie Gennisb335fad2012-01-15 18:54:57 -0800379 * Notifies EGL that the frame is about to be rendered.
Romain Guy76878822012-03-30 14:54:22 -0700380 * @param size
Jamie Gennisb335fad2012-01-15 18:54:57 -0800381 */
Romain Guy672433d2013-01-04 19:05:13 -0800382 static void beginFrame(int[] size) {
Romain Guy76878822012-03-30 14:54:22 -0700383 nBeginFrame(size);
Jamie Gennisb335fad2012-01-15 18:54:57 -0800384 }
385
Romain Guy76878822012-03-30 14:54:22 -0700386 private static native void nBeginFrame(int[] size);
Jamie Gennisb335fad2012-01-15 18:54:57 -0800387
388 /**
Romain Guy244ada12012-03-28 16:41:26 -0700389 * Preserves the back buffer of the current surface after a buffer swap.
390 * Calling this method sets the EGL_SWAP_BEHAVIOR attribute of the current
391 * surface to EGL_BUFFER_PRESERVED. Calling this method requires an EGL
392 * config that supports EGL_SWAP_BEHAVIOR_PRESERVED_BIT.
393 *
394 * @return True if the swap behavior was successfully changed,
395 * false otherwise.
396 */
397 static boolean preserveBackBuffer() {
398 return nPreserveBackBuffer();
399 }
400
401 private static native boolean nPreserveBackBuffer();
402
403 /**
404 * Indicates whether the current surface preserves its back buffer
405 * after a buffer swap.
406 *
407 * @return True, if the surface's EGL_SWAP_BEHAVIOR is EGL_BUFFER_PRESERVED,
408 * false otherwise
409 */
410 static boolean isBackBufferPreserved() {
411 return nIsBackBufferPreserved();
412 }
413
414 private static native boolean nIsBackBufferPreserved();
415
416 /**
Romain Guy11cb6422012-09-21 00:39:43 -0700417 * Indicates that the specified hardware layer needs to be updated
418 * as soon as possible.
419 *
420 * @param layer The hardware layer that needs an update
421 */
422 abstract void pushLayerUpdate(HardwareLayer layer);
423
424 /**
Romain Guy069ea0e2011-02-08 12:24:52 -0800425 * Interface used to receive callbacks whenever a view is drawn by
426 * a hardware renderer instance.
427 */
Dianne Hackborn0f761d62010-11-30 22:06:10 -0800428 interface HardwareDrawCallbacks {
Romain Guy069ea0e2011-02-08 12:24:52 -0800429 /**
430 * Invoked before a view is drawn by a hardware renderer.
Romain Guy96885eb2013-03-26 15:05:58 -0700431 * This method can be used to apply transformations to the
432 * canvas but no drawing command should be issued.
Romain Guy069ea0e2011-02-08 12:24:52 -0800433 *
434 * @param canvas The Canvas used to render the view.
435 */
Romain Guy7d70fbf2011-05-24 17:40:25 -0700436 void onHardwarePreDraw(HardwareCanvas canvas);
Romain Guy069ea0e2011-02-08 12:24:52 -0800437
438 /**
439 * Invoked after a view is drawn by a hardware renderer.
Romain Guy96885eb2013-03-26 15:05:58 -0700440 * It is safe to invoke drawing commands from this method.
Romain Guy069ea0e2011-02-08 12:24:52 -0800441 *
442 * @param canvas The Canvas used to render the view.
443 */
Romain Guy7d70fbf2011-05-24 17:40:25 -0700444 void onHardwarePostDraw(HardwareCanvas canvas);
Dianne Hackborn0f761d62010-11-30 22:06:10 -0800445 }
446
Romain Guy2d614592010-06-09 18:21:37 -0700447 /**
448 * Draws the specified view.
Romain Guy7d7b5492011-01-24 16:33:45 -0800449 *
Romain Guy2d614592010-06-09 18:21:37 -0700450 * @param view The view to draw.
451 * @param attachInfo AttachInfo tied to the specified view.
Romain Guy7d7b5492011-01-24 16:33:45 -0800452 * @param callbacks Callbacks invoked when drawing happens.
453 * @param dirty The dirty rectangle to update, can be null.
Romain Guy2d614592010-06-09 18:21:37 -0700454 */
Romain Guye55945e2013-04-04 15:26:04 -0700455 abstract void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
Romain Guy7d7b5492011-01-24 16:33:45 -0800456 Rect dirty);
Romain Guy2d614592010-06-09 18:21:37 -0700457
458 /**
Romain Guy53ca03d2010-10-08 18:55:27 -0700459 * Creates a new display list that can be used to record batches of
460 * drawing operations.
Romain Guyb051e892010-09-28 19:09:36 -0700461 *
Romain Guy52036b12013-02-14 18:03:37 -0800462 * @param name The name of the display list, used for debugging purpose. May be null.
Romain Guy13631f32012-01-30 17:41:55 -0800463 *
Romain Guy53ca03d2010-10-08 18:55:27 -0700464 * @return A new display list.
Romain Guy52036b12013-02-14 18:03:37 -0800465 *
466 * @hide
Romain Guyb051e892010-09-28 19:09:36 -0700467 */
Romain Guy13631f32012-01-30 17:41:55 -0800468 public abstract DisplayList createDisplayList(String name);
Romain Guyb051e892010-09-28 19:09:36 -0700469
470 /**
Romain Guyaa6c24c2011-04-28 18:40:04 -0700471 * Creates a new hardware layer. A hardware layer built by calling this
472 * method will be treated as a texture layer, instead of as a render target.
473 *
Romain Guya9489272011-06-22 20:58:11 -0700474 * @param isOpaque Whether the layer should be opaque or not
475 *
Romain Guyaa6c24c2011-04-28 18:40:04 -0700476 * @return A hardware layer
Jamie Gennis2af35242012-04-05 11:44:30 -0700477 */
Romain Guya9489272011-06-22 20:58:11 -0700478 abstract HardwareLayer createHardwareLayer(boolean isOpaque);
Jamie Gennis2af35242012-04-05 11:44:30 -0700479
Romain Guyaa6c24c2011-04-28 18:40:04 -0700480 /**
Romain Guy6c319ca2011-01-11 14:29:25 -0800481 * Creates a new hardware layer.
482 *
483 * @param width The minimum width of the layer
484 * @param height The minimum height of the layer
485 * @param isOpaque Whether the layer should be opaque or not
486 *
487 * @return A hardware layer
488 */
489 abstract HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque);
Romain Guyaa6c24c2011-04-28 18:40:04 -0700490
491 /**
492 * Creates a new {@link SurfaceTexture} that can be used to render into the
493 * specified hardware layer.
Romain Guyaa6c24c2011-04-28 18:40:04 -0700494 *
495 * @param layer The layer to render into using a {@link android.graphics.SurfaceTexture}
496 *
497 * @return A {@link SurfaceTexture}
498 */
Romain Guye5e0c502011-06-15 15:18:31 -0700499 abstract SurfaceTexture createSurfaceTexture(HardwareLayer layer);
Romain Guyaa6c24c2011-04-28 18:40:04 -0700500
501 /**
Jamie Gennis2af35242012-04-05 11:44:30 -0700502 * Sets the {@link android.graphics.SurfaceTexture} that will be used to
503 * render into the specified hardware layer.
504 *
505 * @param layer The layer to render into using a {@link android.graphics.SurfaceTexture}
506 * @param surfaceTexture The {@link android.graphics.SurfaceTexture} to use for the layer
507 */
508 abstract void setSurfaceTexture(HardwareLayer layer, SurfaceTexture surfaceTexture);
509
510 /**
Romain Guyba6be8a2012-04-23 18:22:09 -0700511 * Detaches the specified functor from the current functor execution queue.
512 *
513 * @param functor The native functor to remove from the execution queue.
514 *
515 * @see HardwareCanvas#callDrawGLFunction(int)
516 * @see #attachFunctor(android.view.View.AttachInfo, int)
517 */
518 abstract void detachFunctor(int functor);
519
520 /**
521 * Schedules the specified functor in the functors execution queue.
522 *
523 * @param attachInfo AttachInfo tied to this renderer.
524 * @param functor The native functor to insert in the execution queue.
525 *
526 * @see HardwareCanvas#callDrawGLFunction(int)
Chris Craik41ee4652012-05-31 15:05:57 -0700527 * @see #detachFunctor(int)
528 *
529 * @return true if the functor was attached successfully
Romain Guyba6be8a2012-04-23 18:22:09 -0700530 */
Chris Craik41ee4652012-05-31 15:05:57 -0700531 abstract boolean attachFunctor(View.AttachInfo attachInfo, int functor);
Romain Guyba6be8a2012-04-23 18:22:09 -0700532
533 /**
Romain Guy2d614592010-06-09 18:21:37 -0700534 * Initializes the hardware renderer for the specified surface and setup the
Joe Onoratoc6cc0f82011-04-12 11:53:13 -0700535 * renderer for drawing, if needed. This is invoked when the ViewAncestor has
Romain Guy2d614592010-06-09 18:21:37 -0700536 * potentially lost the hardware renderer. The hardware renderer should be
537 * reinitialized and setup when the render {@link #isRequested()} and
538 * {@link #isEnabled()}.
Romain Guy44d79742012-01-13 12:12:09 -0800539 *
Romain Guy2d614592010-06-09 18:21:37 -0700540 * @param width The width of the drawing surface.
541 * @param height The height of the drawing surface.
Romain Guy786fc932012-07-24 16:24:56 -0700542 * @param surface The surface to hardware accelerate
Romain Guydfab3632012-10-03 14:53:25 -0700543 *
544 * @return true if the surface was initialized, false otherwise. Returning
545 * false might mean that the surface was already initialized.
Romain Guy2d614592010-06-09 18:21:37 -0700546 */
Romain Guydfab3632012-10-03 14:53:25 -0700547 boolean initializeIfNeeded(int width, int height, Surface surface)
Romain Guy44d79742012-01-13 12:12:09 -0800548 throws Surface.OutOfResourcesException {
Romain Guy2d614592010-06-09 18:21:37 -0700549 if (isRequested()) {
550 // We lost the gl context, so recreate it.
551 if (!isEnabled()) {
Romain Guy786fc932012-07-24 16:24:56 -0700552 if (initialize(surface)) {
Romain Guyfb8b7632010-08-23 21:05:08 -0700553 setup(width, height);
Romain Guydfab3632012-10-03 14:53:25 -0700554 return true;
Romain Guy2d614592010-06-09 18:21:37 -0700555 }
556 }
Romain Guydfab3632012-10-03 14:53:25 -0700557 }
558 return false;
Romain Guy2d614592010-06-09 18:21:37 -0700559 }
560
561 /**
Romain Guyef359272013-01-31 19:07:29 -0800562 * Optional, sets the name of the renderer. Useful for debugging purposes.
563 *
564 * @param name The name of this renderer, can be null
565 */
566 abstract void setName(String name);
567
568 /**
Romain Guy2d614592010-06-09 18:21:37 -0700569 * Creates a hardware renderer using OpenGL.
570 *
571 * @param glVersion The version of OpenGL to use (1 for OpenGL 1, 11 for OpenGL 1.1, etc.)
Romain Guye4d01122010-06-16 18:44:05 -0700572 * @param translucent True if the surface is translucent, false otherwise
Romain Guy2d614592010-06-09 18:21:37 -0700573 *
574 * @return A hardware renderer backed by OpenGL.
575 */
Romain Guye4d01122010-06-16 18:44:05 -0700576 static HardwareRenderer createGlRenderer(int glVersion, boolean translucent) {
Romain Guy2d614592010-06-09 18:21:37 -0700577 switch (glVersion) {
Romain Guye3924992010-06-10 18:51:21 -0700578 case 2:
Romain Guy16393512010-08-08 00:14:31 -0700579 return Gl20Renderer.create(translucent);
Romain Guy2d614592010-06-09 18:21:37 -0700580 }
581 throw new IllegalArgumentException("Unknown GL version: " + glVersion);
582 }
583
584 /**
Romain Guybdf76092011-07-18 15:00:43 -0700585 * Invoke this method when the system is running out of memory. This
586 * method will attempt to recover as much memory as possible, based on
587 * the specified hint.
588 *
589 * @param level Hint about the amount of memory that should be trimmed,
590 * see {@link android.content.ComponentCallbacks}
591 */
592 static void trimMemory(int level) {
Romain Guy19f86e82012-04-23 15:19:07 -0700593 startTrimMemory(level);
594 endTrimMemory();
595 }
596
597 /**
598 * Starts the process of trimming memory. Usually this call will setup
599 * hardware rendering context and reclaim memory.Extra cleanup might
600 * be required by calling {@link #endTrimMemory()}.
601 *
602 * @param level Hint about the amount of memory that should be trimmed,
603 * see {@link android.content.ComponentCallbacks}
604 */
605 static void startTrimMemory(int level) {
606 Gl20Renderer.startTrimMemory(level);
607 }
608
609 /**
610 * Finishes the process of trimming memory. This method will usually
611 * cleanup special resources used by the memory trimming process.
612 */
613 static void endTrimMemory() {
614 Gl20Renderer.endTrimMemory();
Romain Guybdf76092011-07-18 15:00:43 -0700615 }
616
617 /**
Romain Guy2d614592010-06-09 18:21:37 -0700618 * Indicates whether hardware acceleration is currently enabled.
619 *
620 * @return True if hardware acceleration is in use, false otherwise.
621 */
622 boolean isEnabled() {
623 return mEnabled;
624 }
625
626 /**
627 * Indicates whether hardware acceleration is currently enabled.
628 *
629 * @param enabled True if the hardware renderer is in use, false otherwise.
630 */
631 void setEnabled(boolean enabled) {
632 mEnabled = enabled;
633 }
634
635 /**
636 * Indicates whether hardware acceleration is currently request but not
637 * necessarily enabled yet.
638 *
639 * @return True if requested, false otherwise.
640 */
641 boolean isRequested() {
642 return mRequested;
643 }
644
645 /**
Romain Guy9745fae2010-12-08 11:39:15 -0800646 * Indicates whether hardware acceleration is currently requested but not
Romain Guy2d614592010-06-09 18:21:37 -0700647 * necessarily enabled yet.
648 *
649 * @return True to request hardware acceleration, false otherwise.
650 */
651 void setRequested(boolean requested) {
652 mRequested = requested;
653 }
654
Romain Guy48ef4a92013-01-10 18:38:46 -0800655 /**
656 * Describes a series of frames that should be drawn on screen as a graph.
657 * Each frame is composed of 1 or more elements.
658 */
659 abstract class GraphDataProvider {
660 /**
661 * Draws the graph as bars. Frame elements are stacked on top of
662 * each other.
663 */
664 public static final int GRAPH_TYPE_BARS = 0;
665 /**
666 * Draws the graph as lines. The number of series drawn corresponds
667 * to the number of elements.
668 */
669 public static final int GRAPH_TYPE_LINES = 1;
670
671 /**
672 * Returns the type of graph to render.
673 *
674 * @return {@link #GRAPH_TYPE_BARS} or {@link #GRAPH_TYPE_LINES}
675 */
676 abstract int getGraphType();
677
678 /**
679 * This method is invoked before the graph is drawn. This method
680 * can be used to compute sizes, etc.
681 *
682 * @param metrics The display metrics
683 */
684 abstract void prepare(DisplayMetrics metrics);
685
686 /**
687 * @return The size in pixels of a vertical unit.
688 */
689 abstract int getVerticalUnitSize();
690
691 /**
692 * @return The size in pixels of a horizontal unit.
693 */
694 abstract int getHorizontalUnitSize();
695
696 /**
697 * @return The size in pixels of the margin between horizontal units.
698 */
699 abstract int getHorizontaUnitMargin();
700
701 /**
702 * An optional threshold value.
703 *
704 * @return A value >= 0 to draw the threshold, a negative value
705 * to ignore it.
706 */
707 abstract float getThreshold();
708
709 /**
710 * The data to draw in the graph. The number of elements in the
711 * array must be at least {@link #getFrameCount()} * {@link #getElementCount()}.
712 * If a value is negative the following values will be ignored.
713 */
714 abstract float[] getData();
715
716 /**
717 * Returns the number of frames to render in the graph.
718 */
719 abstract int getFrameCount();
720
721 /**
722 * Returns the number of elements in each frame. This directly affects
723 * the number of series drawn in the graph.
724 */
725 abstract int getElementCount();
726
727 /**
728 * Returns the current frame, if any. If the returned value is negative
729 * the current frame is ignored.
730 */
731 abstract int getCurrentFrame();
732
733 /**
734 * Prepares the paint to draw the specified element (or series.)
735 */
736 abstract void setupGraphPaint(Paint paint, int elementIndex);
737
738 /**
739 * Prepares the paint to draw the threshold.
740 */
741 abstract void setupThresholdPaint(Paint paint);
742
743 /**
744 * Prepares the paint to draw the current frame indicator.
745 */
746 abstract void setupCurrentFramePaint(Paint paint);
747 }
748
Romain Guy2d614592010-06-09 18:21:37 -0700749 @SuppressWarnings({"deprecation"})
Romain Guye3924992010-06-10 18:51:21 -0700750 static abstract class GlRenderer extends HardwareRenderer {
Romain Guy16260e72011-09-01 14:26:11 -0700751 static final int SURFACE_STATE_ERROR = 0;
752 static final int SURFACE_STATE_SUCCESS = 1;
753 static final int SURFACE_STATE_UPDATED = 2;
Romain Guy8f3b8e32012-03-27 16:33:45 -0700754
Chris Craik65924a32012-04-05 17:52:11 -0700755 static final int FUNCTOR_PROCESS_DELAY = 4;
Romain Guy8f3b8e32012-03-27 16:33:45 -0700756
Romain Guy48ef4a92013-01-10 18:38:46 -0800757 private static final int PROFILE_DRAW_MARGIN = 0;
758 private static final int PROFILE_DRAW_WIDTH = 3;
759 private static final int[] PROFILE_DRAW_COLORS = { 0xcf3e66cc, 0xcfdc3912, 0xcfe69800 };
760 private static final int PROFILE_DRAW_CURRENT_FRAME_COLOR = 0xcf5faa4d;
761 private static final int PROFILE_DRAW_THRESHOLD_COLOR = 0xff5faa4d;
762 private static final int PROFILE_DRAW_THRESHOLD_STROKE_WIDTH = 2;
763 private static final int PROFILE_DRAW_DP_PER_MS = 7;
764
Romain Guyfb8b7632010-08-23 21:05:08 -0700765 static EGL10 sEgl;
766 static EGLDisplay sEglDisplay;
767 static EGLConfig sEglConfig;
Romain Guy566b3ef2011-07-18 18:38:12 -0700768 static final Object[] sEglLock = new Object[0];
Chet Haase40e03832011-10-06 08:34:13 -0700769 int mWidth = -1, mHeight = -1;
Romain Guy2d614592010-06-09 18:21:37 -0700770
Romain Guy5d6999e2012-03-22 19:15:04 -0700771 static final ThreadLocal<ManagedEGLContext> sEglContextStorage
772 = new ThreadLocal<ManagedEGLContext>();
Romain Guy566b3ef2011-07-18 18:38:12 -0700773
774 EGLContext mEglContext;
775 Thread mEglThread;
Romain Guyfb8b7632010-08-23 21:05:08 -0700776
777 EGLSurface mEglSurface;
778
Romain Guye3924992010-06-10 18:51:21 -0700779 GL mGl;
Romain Guy67f27952010-12-07 20:09:23 -0800780 HardwareCanvas mCanvas;
Romain Guya676ad72012-02-13 17:47:10 -0800781
Romain Guyef359272013-01-31 19:07:29 -0800782 String mName;
783
Michael Jurkaa3fabff2012-03-28 17:22:29 +0200784 long mFrameCount;
Romain Guy7d7b5492011-01-24 16:33:45 -0800785 Paint mDebugPaint;
786
Romain Guy7e1160e2011-07-08 15:49:50 -0700787 static boolean sDirtyRegions;
788 static final boolean sDirtyRegionsRequested;
789 static {
790 String dirtyProperty = SystemProperties.get(RENDER_DIRTY_REGIONS_PROPERTY, "true");
791 //noinspection PointlessBooleanExpression,ConstantConditions
792 sDirtyRegions = RENDER_DIRTY_REGIONS && "true".equalsIgnoreCase(dirtyProperty);
793 sDirtyRegionsRequested = sDirtyRegions;
794 }
795
796 boolean mDirtyRegionsEnabled;
Romain Guy9477c6e2011-12-09 12:27:21 -0800797 boolean mUpdateDirtyRegions;
798
Romain Guy5bb3c732012-11-29 17:52:58 -0800799 boolean mProfileEnabled;
Romain Guy48ef4a92013-01-10 18:38:46 -0800800 int mProfileVisualizerType = -1;
Romain Guy5bb3c732012-11-29 17:52:58 -0800801 float[] mProfileData;
802 ReentrantLock mProfileLock;
Romain Guya676ad72012-02-13 17:47:10 -0800803 int mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT;
Romain Guy672433d2013-01-04 19:05:13 -0800804
Romain Guy48ef4a92013-01-10 18:38:46 -0800805 GraphDataProvider mDebugDataProvider;
806 float[][] mProfileShapes;
Romain Guy672433d2013-01-04 19:05:13 -0800807 Paint mProfilePaint;
808
Romain Guy5bb3c732012-11-29 17:52:58 -0800809 boolean mDebugDirtyRegions;
810 boolean mShowOverdraw;
Romain Guya676ad72012-02-13 17:47:10 -0800811
Romain Guye4d01122010-06-16 18:44:05 -0700812 final int mGlVersion;
813 final boolean mTranslucent;
Romain Guye3924992010-06-10 18:51:21 -0700814
Romain Guyfb8b7632010-08-23 21:05:08 -0700815 private boolean mDestroyed;
Romain Guy566b3ef2011-07-18 18:38:12 -0700816
Romain Guycabfcc12011-03-07 18:06:46 -0800817 private final Rect mRedrawClip = new Rect();
Romain Guy8f3b8e32012-03-27 16:33:45 -0700818
Romain Guy76878822012-03-30 14:54:22 -0700819 private final int[] mSurfaceSize = new int[2];
Romain Guy8f3b8e32012-03-27 16:33:45 -0700820 private final FunctorsRunnable mFunctorsRunnable = new FunctorsRunnable();
Romain Guyfb8b7632010-08-23 21:05:08 -0700821
Romain Guye4d01122010-06-16 18:44:05 -0700822 GlRenderer(int glVersion, boolean translucent) {
Romain Guye3924992010-06-10 18:51:21 -0700823 mGlVersion = glVersion;
Romain Guye4d01122010-06-16 18:44:05 -0700824 mTranslucent = translucent;
Romain Guy9ace8f52011-07-07 20:50:11 -0700825
Romain Guy5bb3c732012-11-29 17:52:58 -0800826 loadSystemProperties(null);
827 }
Romain Guya676ad72012-02-13 17:47:10 -0800828
Romain Guy48ef4a92013-01-10 18:38:46 -0800829 private static final String[] VISUALIZERS = {
830 PROFILE_PROPERTY_VISUALIZE_BARS,
831 PROFILE_PROPERTY_VISUALIZE_LINES
832 };
833
Romain Guy5bb3c732012-11-29 17:52:58 -0800834 @Override
835 boolean loadSystemProperties(Surface surface) {
Romain Guya4fef022013-01-07 11:18:38 -0800836 boolean value;
Romain Guy5bb3c732012-11-29 17:52:58 -0800837 boolean changed = false;
838
Romain Guya4fef022013-01-07 11:18:38 -0800839 String profiling = SystemProperties.get(PROFILE_PROPERTY);
Romain Guy48ef4a92013-01-10 18:38:46 -0800840 int graphType = Arrays.binarySearch(VISUALIZERS, profiling);
841 value = graphType >= 0;
Romain Guya4fef022013-01-07 11:18:38 -0800842
Romain Guy48ef4a92013-01-10 18:38:46 -0800843 if (graphType != mProfileVisualizerType) {
Romain Guya4fef022013-01-07 11:18:38 -0800844 changed = true;
Romain Guy48ef4a92013-01-10 18:38:46 -0800845 mProfileVisualizerType = graphType;
Romain Guya4fef022013-01-07 11:18:38 -0800846
Romain Guy48ef4a92013-01-10 18:38:46 -0800847 mProfileShapes = null;
Romain Guya4fef022013-01-07 11:18:38 -0800848 mProfilePaint = null;
Romain Guy48ef4a92013-01-10 18:38:46 -0800849
850 if (value) {
851 mDebugDataProvider = new DrawPerformanceDataProvider(graphType);
852 } else {
853 mDebugDataProvider = null;
854 }
Romain Guya4fef022013-01-07 11:18:38 -0800855 }
856
857 // If on-screen profiling is not enabled, we need to check whether
858 // console profiling only is enabled
859 if (!value) {
860 value = Boolean.parseBoolean(profiling);
861 }
862
Romain Guy5bb3c732012-11-29 17:52:58 -0800863 if (value != mProfileEnabled) {
864 changed = true;
865 mProfileEnabled = value;
866
867 if (mProfileEnabled) {
868 Log.d(LOG_TAG, "Profiling hardware renderer");
Romain Guy77e67cf2012-06-19 16:38:50 -0700869
Romain Guy5bb3c732012-11-29 17:52:58 -0800870 int maxProfileFrames = SystemProperties.getInt(PROFILE_MAXFRAMES_PROPERTY,
871 PROFILE_MAX_FRAMES);
872 mProfileData = new float[maxProfileFrames * PROFILE_FRAME_DATA_COUNT];
873 for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
874 mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1;
875 }
876
877 mProfileLock = new ReentrantLock();
878 } else {
879 mProfileData = null;
880 mProfileLock = null;
Romain Guy48ef4a92013-01-10 18:38:46 -0800881 mProfileVisualizerType = -1;
Romain Guy5bb3c732012-11-29 17:52:58 -0800882 }
Romain Guy672433d2013-01-04 19:05:13 -0800883
Romain Guy666d5da2013-01-07 11:29:14 -0800884 mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT;
Romain Guy672433d2013-01-04 19:05:13 -0800885 }
886
Romain Guy5bb3c732012-11-29 17:52:58 -0800887 value = SystemProperties.getBoolean(DEBUG_DIRTY_REGIONS_PROPERTY, false);
888 if (value != mDebugDirtyRegions) {
889 changed = true;
890 mDebugDirtyRegions = value;
891
892 if (mDebugDirtyRegions) {
893 Log.d(LOG_TAG, "Debugging dirty regions");
894 }
Romain Guyb04f7e92012-02-15 12:36:54 -0800895 }
Romain Guy7c450aa2012-09-21 19:15:00 -0700896
Romain Guy5bb3c732012-11-29 17:52:58 -0800897 value = SystemProperties.getBoolean(
Romain Guy7c450aa2012-09-21 19:15:00 -0700898 HardwareRenderer.DEBUG_SHOW_OVERDRAW_PROPERTY, false);
Romain Guy5bb3c732012-11-29 17:52:58 -0800899 if (value != mShowOverdraw) {
900 changed = true;
901 mShowOverdraw = value;
Romain Guy5bb3c732012-11-29 17:52:58 -0800902 }
903
904 if (nLoadProperties()) {
905 changed = true;
906 }
907
908 return changed;
Romain Guya676ad72012-02-13 17:47:10 -0800909 }
910
911 @Override
912 void dumpGfxInfo(PrintWriter pw) {
913 if (mProfileEnabled) {
914 pw.printf("\n\tDraw\tProcess\tExecute\n");
Romain Guy77e67cf2012-06-19 16:38:50 -0700915
916 mProfileLock.lock();
917 try {
918 for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
919 if (mProfileData[i] < 0) {
920 break;
921 }
922 pw.printf("\t%3.2f\t%3.2f\t%3.2f\n", mProfileData[i], mProfileData[i + 1],
923 mProfileData[i + 2]);
924 mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1;
Chet Haase09280602012-04-03 16:15:34 -0700925 }
Romain Guy77e67cf2012-06-19 16:38:50 -0700926 mProfileCurrentFrame = mProfileData.length;
927 } finally {
928 mProfileLock.unlock();
Romain Guya676ad72012-02-13 17:47:10 -0800929 }
930 }
Romain Guy2d614592010-06-09 18:21:37 -0700931 }
932
Michael Jurkaa3fabff2012-03-28 17:22:29 +0200933 @Override
934 long getFrameCount() {
935 return mFrameCount;
936 }
937
Romain Guye3924992010-06-10 18:51:21 -0700938 /**
Romain Guy02ccac62011-06-24 13:20:23 -0700939 * Indicates whether this renderer instance can track and update dirty regions.
940 */
941 boolean hasDirtyRegions() {
Romain Guy7e1160e2011-07-08 15:49:50 -0700942 return mDirtyRegionsEnabled;
Romain Guy02ccac62011-06-24 13:20:23 -0700943 }
944
945 /**
Romain Guy4caa4ed2010-08-25 14:46:24 -0700946 * Checks for OpenGL errors. If an error has occured, {@link #destroy(boolean)}
Romain Guye3924992010-06-10 18:51:21 -0700947 * is invoked and the requested flag is turned off. The error code is
948 * also logged as a warning.
949 */
Romain Guyb025b9c2010-09-16 14:16:48 -0700950 void checkEglErrors() {
Romain Guy2d614592010-06-09 18:21:37 -0700951 if (isEnabled()) {
Romain Guy740ee652012-09-17 18:11:40 -0700952 checkEglErrorsForced();
953 }
954 }
955
956 private void checkEglErrorsForced() {
957 int error = sEgl.eglGetError();
958 if (error != EGL_SUCCESS) {
959 // something bad has happened revert to
960 // normal rendering.
961 Log.w(LOG_TAG, "EGL error: " + GLUtils.getEGLErrorString(error));
962 fallback(error != EGL11.EGL_CONTEXT_LOST);
Romain Guy2d614592010-06-09 18:21:37 -0700963 }
964 }
Romain Guy67f27952010-12-07 20:09:23 -0800965
Romain Guy9745fae2010-12-08 11:39:15 -0800966 private void fallback(boolean fallback) {
967 destroy(true);
968 if (fallback) {
969 // we'll try again if it was context lost
970 setRequested(false);
971 Log.w(LOG_TAG, "Mountain View, we've had a problem here. "
972 + "Switching back to software rendering.");
973 }
974 }
975
Romain Guy2d614592010-06-09 18:21:37 -0700976 @Override
Romain Guy786fc932012-07-24 16:24:56 -0700977 boolean initialize(Surface surface) throws Surface.OutOfResourcesException {
Romain Guy2d614592010-06-09 18:21:37 -0700978 if (isRequested() && !isEnabled()) {
Romain Guye3924992010-06-10 18:51:21 -0700979 initializeEgl();
Romain Guy786fc932012-07-24 16:24:56 -0700980 mGl = createEglSurface(surface);
Romain Guyfb8b7632010-08-23 21:05:08 -0700981 mDestroyed = false;
Romain Guye3924992010-06-10 18:51:21 -0700982
983 if (mGl != null) {
Romain Guyfb8b7632010-08-23 21:05:08 -0700984 int err = sEgl.eglGetError();
Romain Guy484c7192011-07-25 11:56:33 -0700985 if (err != EGL_SUCCESS) {
Romain Guy4caa4ed2010-08-25 14:46:24 -0700986 destroy(true);
Romain Guye3924992010-06-10 18:51:21 -0700987 setRequested(false);
988 } else {
Romain Guy4caa4ed2010-08-25 14:46:24 -0700989 if (mCanvas == null) {
990 mCanvas = createCanvas();
Romain Guyef359272013-01-31 19:07:29 -0800991 mCanvas.setName(mName);
Romain Guyfb8b7632010-08-23 21:05:08 -0700992 }
Romain Guye55945e2013-04-04 15:26:04 -0700993 setEnabled(true);
Romain Guye3924992010-06-10 18:51:21 -0700994 }
995
996 return mCanvas != null;
997 }
Romain Guy2d614592010-06-09 18:21:37 -0700998 }
999 return false;
1000 }
Romain Guy2a83f002011-01-18 18:28:21 -08001001
1002 @Override
Romain Guy786fc932012-07-24 16:24:56 -07001003 void updateSurface(Surface surface) throws Surface.OutOfResourcesException {
Romain Guy2a83f002011-01-18 18:28:21 -08001004 if (isRequested() && isEnabled()) {
Romain Guy786fc932012-07-24 16:24:56 -07001005 createEglSurface(surface);
Romain Guy2a83f002011-01-18 18:28:21 -08001006 }
1007 }
Romain Guy2d614592010-06-09 18:21:37 -07001008
Romain Guy5d6999e2012-03-22 19:15:04 -07001009 abstract HardwareCanvas createCanvas();
Romain Guye3924992010-06-10 18:51:21 -07001010
Romain Guy29d23ec2011-07-25 14:42:24 -07001011 abstract int[] getConfig(boolean dirtyRegions);
1012
Romain Guye3924992010-06-10 18:51:21 -07001013 void initializeEgl() {
Romain Guy566b3ef2011-07-18 18:38:12 -07001014 synchronized (sEglLock) {
1015 if (sEgl == null && sEglConfig == null) {
1016 sEgl = (EGL10) EGLContext.getEGL();
1017
1018 // Get to the default display.
Romain Guy484c7192011-07-25 11:56:33 -07001019 sEglDisplay = sEgl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
Romain Guy566b3ef2011-07-18 18:38:12 -07001020
Romain Guy484c7192011-07-25 11:56:33 -07001021 if (sEglDisplay == EGL_NO_DISPLAY) {
Romain Guy566b3ef2011-07-18 18:38:12 -07001022 throw new RuntimeException("eglGetDisplay failed "
Romain Guy407ec782011-08-24 17:06:58 -07001023 + GLUtils.getEGLErrorString(sEgl.eglGetError()));
Romain Guy566b3ef2011-07-18 18:38:12 -07001024 }
1025
1026 // We can now initialize EGL for that display
1027 int[] version = new int[2];
1028 if (!sEgl.eglInitialize(sEglDisplay, version)) {
1029 throw new RuntimeException("eglInitialize failed " +
Romain Guy407ec782011-08-24 17:06:58 -07001030 GLUtils.getEGLErrorString(sEgl.eglGetError()));
Romain Guy566b3ef2011-07-18 18:38:12 -07001031 }
Romain Guy740ee652012-09-17 18:11:40 -07001032
1033 checkEglErrorsForced();
1034
Romain Guy5bb3c732012-11-29 17:52:58 -08001035 sEglConfig = loadEglConfig();
Romain Guy069ea0e2011-02-08 12:24:52 -08001036 }
1037 }
Romain Guy566b3ef2011-07-18 18:38:12 -07001038
Romain Guy5d6999e2012-03-22 19:15:04 -07001039 ManagedEGLContext managedContext = sEglContextStorage.get();
Dianne Hackborn717a25d2011-11-15 18:59:59 -08001040 mEglContext = managedContext != null ? managedContext.getContext() : null;
Romain Guy566b3ef2011-07-18 18:38:12 -07001041 mEglThread = Thread.currentThread();
1042
1043 if (mEglContext == null) {
1044 mEglContext = createContext(sEgl, sEglDisplay, sEglConfig);
Romain Guy5d6999e2012-03-22 19:15:04 -07001045 sEglContextStorage.set(createManagedContext(mEglContext));
Romain Guy566b3ef2011-07-18 18:38:12 -07001046 }
Romain Guy2d614592010-06-09 18:21:37 -07001047 }
1048
Romain Guy5bb3c732012-11-29 17:52:58 -08001049 private EGLConfig loadEglConfig() {
1050 EGLConfig eglConfig = chooseEglConfig();
1051 if (eglConfig == null) {
1052 // We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without
1053 if (sDirtyRegions) {
1054 sDirtyRegions = false;
1055 eglConfig = chooseEglConfig();
1056 if (eglConfig == null) {
1057 throw new RuntimeException("eglConfig not initialized");
1058 }
1059 } else {
1060 throw new RuntimeException("eglConfig not initialized");
1061 }
1062 }
1063 return eglConfig;
1064 }
1065
Romain Guy5d6999e2012-03-22 19:15:04 -07001066 abstract ManagedEGLContext createManagedContext(EGLContext eglContext);
1067
Romain Guye91a9c72011-05-02 14:53:30 -07001068 private EGLConfig chooseEglConfig() {
Romain Guye91a9c72011-05-02 14:53:30 -07001069 EGLConfig[] configs = new EGLConfig[1];
Romain Guy484c7192011-07-25 11:56:33 -07001070 int[] configsCount = new int[1];
Romain Guy7e1160e2011-07-08 15:49:50 -07001071 int[] configSpec = getConfig(sDirtyRegions);
Romain Guy484c7192011-07-25 11:56:33 -07001072
1073 // Debug
Romain Guy29d23ec2011-07-25 14:42:24 -07001074 final String debug = SystemProperties.get(PRINT_CONFIG_PROPERTY, "");
Romain Guy484c7192011-07-25 11:56:33 -07001075 if ("all".equalsIgnoreCase(debug)) {
1076 sEgl.eglChooseConfig(sEglDisplay, configSpec, null, 0, configsCount);
1077
1078 EGLConfig[] debugConfigs = new EGLConfig[configsCount[0]];
1079 sEgl.eglChooseConfig(sEglDisplay, configSpec, debugConfigs,
1080 configsCount[0], configsCount);
1081
1082 for (EGLConfig config : debugConfigs) {
1083 printConfig(config);
1084 }
1085 }
1086
Romain Guye91a9c72011-05-02 14:53:30 -07001087 if (!sEgl.eglChooseConfig(sEglDisplay, configSpec, configs, 1, configsCount)) {
1088 throw new IllegalArgumentException("eglChooseConfig failed " +
Romain Guy407ec782011-08-24 17:06:58 -07001089 GLUtils.getEGLErrorString(sEgl.eglGetError()));
Romain Guye91a9c72011-05-02 14:53:30 -07001090 } else if (configsCount[0] > 0) {
Romain Guy484c7192011-07-25 11:56:33 -07001091 if ("choice".equalsIgnoreCase(debug)) {
1092 printConfig(configs[0]);
1093 }
Romain Guye91a9c72011-05-02 14:53:30 -07001094 return configs[0];
1095 }
Romain Guy484c7192011-07-25 11:56:33 -07001096
Romain Guye91a9c72011-05-02 14:53:30 -07001097 return null;
1098 }
1099
Romain Guye979e622012-03-20 13:50:27 -07001100 private static void printConfig(EGLConfig config) {
Romain Guy484c7192011-07-25 11:56:33 -07001101 int[] value = new int[1];
1102
1103 Log.d(LOG_TAG, "EGL configuration " + config + ":");
1104
1105 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_RED_SIZE, value);
1106 Log.d(LOG_TAG, " RED_SIZE = " + value[0]);
1107
1108 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_GREEN_SIZE, value);
1109 Log.d(LOG_TAG, " GREEN_SIZE = " + value[0]);
1110
1111 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_BLUE_SIZE, value);
1112 Log.d(LOG_TAG, " BLUE_SIZE = " + value[0]);
1113
1114 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_ALPHA_SIZE, value);
1115 Log.d(LOG_TAG, " ALPHA_SIZE = " + value[0]);
1116
1117 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_DEPTH_SIZE, value);
1118 Log.d(LOG_TAG, " DEPTH_SIZE = " + value[0]);
1119
1120 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_STENCIL_SIZE, value);
1121 Log.d(LOG_TAG, " STENCIL_SIZE = " + value[0]);
1122
Romain Guy8efca542012-10-15 18:09:49 -07001123 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SAMPLE_BUFFERS, value);
1124 Log.d(LOG_TAG, " SAMPLE_BUFFERS = " + value[0]);
1125
1126 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SAMPLES, value);
1127 Log.d(LOG_TAG, " SAMPLES = " + value[0]);
1128
Romain Guy484c7192011-07-25 11:56:33 -07001129 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SURFACE_TYPE, value);
Romain Guy29d23ec2011-07-25 14:42:24 -07001130 Log.d(LOG_TAG, " SURFACE_TYPE = 0x" + Integer.toHexString(value[0]));
Romain Guy8efca542012-10-15 18:09:49 -07001131
1132 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_CONFIG_CAVEAT, value);
1133 Log.d(LOG_TAG, " CONFIG_CAVEAT = 0x" + Integer.toHexString(value[0]));
Romain Guy484c7192011-07-25 11:56:33 -07001134 }
1135
Romain Guy786fc932012-07-24 16:24:56 -07001136 GL createEglSurface(Surface surface) throws Surface.OutOfResourcesException {
Romain Guye3924992010-06-10 18:51:21 -07001137 // Check preconditions.
Romain Guyfb8b7632010-08-23 21:05:08 -07001138 if (sEgl == null) {
Romain Guye3924992010-06-10 18:51:21 -07001139 throw new RuntimeException("egl not initialized");
Romain Guy2d614592010-06-09 18:21:37 -07001140 }
Romain Guyfb8b7632010-08-23 21:05:08 -07001141 if (sEglDisplay == null) {
Romain Guye3924992010-06-10 18:51:21 -07001142 throw new RuntimeException("eglDisplay not initialized");
1143 }
Romain Guyfb8b7632010-08-23 21:05:08 -07001144 if (sEglConfig == null) {
Romain Guy069ea0e2011-02-08 12:24:52 -08001145 throw new RuntimeException("eglConfig not initialized");
Romain Guye3924992010-06-10 18:51:21 -07001146 }
Romain Guy566b3ef2011-07-18 18:38:12 -07001147 if (Thread.currentThread() != mEglThread) {
Romain Guyfb8b7632010-08-23 21:05:08 -07001148 throw new IllegalStateException("HardwareRenderer cannot be used "
1149 + "from multiple threads");
1150 }
Romain Guye3924992010-06-10 18:51:21 -07001151
Romain Guy6d7475d2011-07-27 16:28:21 -07001152 // In case we need to destroy an existing surface
1153 destroySurface();
Romain Guye3924992010-06-10 18:51:21 -07001154
1155 // Create an EGL surface we can render into.
Romain Guy786fc932012-07-24 16:24:56 -07001156 if (!createSurface(surface)) {
Romain Guy1d0c7082011-08-03 16:22:24 -07001157 return null;
Romain Guye3924992010-06-10 18:51:21 -07001158 }
1159
Romain Guy8ff6b9e2011-11-09 20:10:18 -08001160 initCaches();
Romain Guy6f7d9392011-06-02 14:17:28 -07001161
Romain Guy9477c6e2011-12-09 12:27:21 -08001162 return mEglContext.getGL();
1163 }
1164
1165 private void enableDirtyRegions() {
Romain Guy6f7d9392011-06-02 14:17:28 -07001166 // If mDirtyRegions is set, this means we have an EGL configuration
1167 // with EGL_SWAP_BEHAVIOR_PRESERVED_BIT set
Romain Guy7e1160e2011-07-08 15:49:50 -07001168 if (sDirtyRegions) {
Romain Guy244ada12012-03-28 16:41:26 -07001169 if (!(mDirtyRegionsEnabled = preserveBackBuffer())) {
Romain Guy7d7b5492011-01-24 16:33:45 -08001170 Log.w(LOG_TAG, "Backbuffer cannot be preserved");
1171 }
Romain Guy7e1160e2011-07-08 15:49:50 -07001172 } else if (sDirtyRegionsRequested) {
Romain Guy6f7d9392011-06-02 14:17:28 -07001173 // If mDirtyRegions is not set, our EGL configuration does not
1174 // have EGL_SWAP_BEHAVIOR_PRESERVED_BIT; however, the default
1175 // swap behavior might be EGL_BUFFER_PRESERVED, which means we
1176 // want to set mDirtyRegions. We try to do this only if dirty
1177 // regions were initially requested as part of the device
1178 // configuration (see RENDER_DIRTY_REGIONS)
Romain Guy244ada12012-03-28 16:41:26 -07001179 mDirtyRegionsEnabled = isBackBufferPreserved();
Romain Guy7d7b5492011-01-24 16:33:45 -08001180 }
Romain Guye3924992010-06-10 18:51:21 -07001181 }
1182
Romain Guy8ff6b9e2011-11-09 20:10:18 -08001183 abstract void initCaches();
1184
Romain Guye3924992010-06-10 18:51:21 -07001185 EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
Romain Guybd431522012-09-26 13:31:25 -07001186 int[] attribs = { EGL14.EGL_CONTEXT_CLIENT_VERSION, mGlVersion, EGL_NONE };
Romain Guye3924992010-06-10 18:51:21 -07001187
Romain Guyc0029362012-09-24 20:18:13 -07001188 EGLContext context = egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT,
1189 mGlVersion != 0 ? attribs : null);
Romain Guyed1b6f42012-09-24 21:42:18 -07001190 if (context == null || context == EGL_NO_CONTEXT) {
Romain Guyc0029362012-09-24 20:18:13 -07001191 //noinspection ConstantConditions
1192 throw new IllegalStateException(
1193 "Could not create an EGL context. eglCreateContext failed with error: " +
1194 GLUtils.getEGLErrorString(sEgl.eglGetError()));
1195 }
1196 return context;
Romain Guy2d614592010-06-09 18:21:37 -07001197 }
1198
1199 @Override
Romain Guy4caa4ed2010-08-25 14:46:24 -07001200 void destroy(boolean full) {
1201 if (full && mCanvas != null) {
Romain Guy4caa4ed2010-08-25 14:46:24 -07001202 mCanvas = null;
1203 }
Romain Guye3924992010-06-10 18:51:21 -07001204
Romain Guy357c9422011-08-02 11:32:49 -07001205 if (!isEnabled() || mDestroyed) {
1206 setEnabled(false);
1207 return;
1208 }
Romain Guyfb8b7632010-08-23 21:05:08 -07001209
Romain Guy6d7475d2011-07-27 16:28:21 -07001210 destroySurface();
Romain Guye3924992010-06-10 18:51:21 -07001211 setEnabled(false);
Romain Guy357c9422011-08-02 11:32:49 -07001212
1213 mDestroyed = true;
1214 mGl = null;
Romain Guyfb8b7632010-08-23 21:05:08 -07001215 }
1216
Romain Guy6d7475d2011-07-27 16:28:21 -07001217 void destroySurface() {
1218 if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) {
1219 sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
1220 sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
1221 mEglSurface = null;
1222 }
1223 }
1224
Romain Guye3924992010-06-10 18:51:21 -07001225 @Override
Romain Guy786fc932012-07-24 16:24:56 -07001226 void invalidate(Surface surface) {
Romain Guy7e1160e2011-07-08 15:49:50 -07001227 // Cancels any existing buffer to ensure we'll get a buffer
1228 // of the right size before we call eglSwapBuffers
Romain Guycf15efb2011-08-02 12:59:32 -07001229 sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
Romain Guy1d0c7082011-08-03 16:22:24 -07001230
Romain Guyd3facf32011-08-03 11:14:38 -07001231 if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) {
1232 sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
Romain Guy1d0c7082011-08-03 16:22:24 -07001233 mEglSurface = null;
1234 setEnabled(false);
Romain Guyd3facf32011-08-03 11:14:38 -07001235 }
Romain Guycf15efb2011-08-02 12:59:32 -07001236
Romain Guy786fc932012-07-24 16:24:56 -07001237 if (surface.isValid()) {
1238 if (!createSurface(surface)) {
Romain Guy1d0c7082011-08-03 16:22:24 -07001239 return;
Romain Guycf15efb2011-08-02 12:59:32 -07001240 }
Romain Guy9477c6e2011-12-09 12:27:21 -08001241
1242 mUpdateDirtyRegions = true;
1243
Romain Guy86e3e222011-08-16 14:30:08 -07001244 if (mCanvas != null) {
1245 setEnabled(true);
1246 }
Romain Guycf15efb2011-08-02 12:59:32 -07001247 }
Romain Guy03985752011-07-11 15:33:51 -07001248 }
Romain Guycf15efb2011-08-02 12:59:32 -07001249
Romain Guy786fc932012-07-24 16:24:56 -07001250 private boolean createSurface(Surface surface) {
1251 mEglSurface = sEgl.eglCreateWindowSurface(sEglDisplay, sEglConfig, surface, null);
Romain Guy1d0c7082011-08-03 16:22:24 -07001252
1253 if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) {
1254 int error = sEgl.eglGetError();
1255 if (error == EGL_BAD_NATIVE_WINDOW) {
1256 Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
1257 return false;
1258 }
1259 throw new RuntimeException("createWindowSurface failed "
Romain Guy407ec782011-08-24 17:06:58 -07001260 + GLUtils.getEGLErrorString(error));
Romain Guy1d0c7082011-08-03 16:22:24 -07001261 }
Romain Guybd431522012-09-26 13:31:25 -07001262
1263 if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
1264 throw new IllegalStateException("eglMakeCurrent failed " +
1265 GLUtils.getEGLErrorString(sEgl.eglGetError()));
1266 }
1267
1268 enableDirtyRegions();
1269
Romain Guy1d0c7082011-08-03 16:22:24 -07001270 return true;
1271 }
1272
Romain Guy03985752011-07-11 15:33:51 -07001273 @Override
1274 boolean validate() {
1275 return checkCurrent() != SURFACE_STATE_ERROR;
Romain Guy7e1160e2011-07-08 15:49:50 -07001276 }
1277
1278 @Override
Romain Guyfb8b7632010-08-23 21:05:08 -07001279 void setup(int width, int height) {
Romain Guyeca9b1f2011-08-26 13:36:37 -07001280 if (validate()) {
1281 mCanvas.setViewport(width, height);
Chet Haase40e03832011-10-06 08:34:13 -07001282 mWidth = width;
1283 mHeight = height;
Romain Guyeca9b1f2011-08-26 13:36:37 -07001284 }
Romain Guye3924992010-06-10 18:51:21 -07001285 }
Romain Guy7d7b5492011-01-24 16:33:45 -08001286
Chet Haase40e03832011-10-06 08:34:13 -07001287 @Override
1288 int getWidth() {
1289 return mWidth;
1290 }
1291
1292 @Override
1293 int getHeight() {
1294 return mHeight;
1295 }
1296
Chet Haase08837c22011-11-28 11:53:21 -08001297 @Override
1298 HardwareCanvas getCanvas() {
1299 return mCanvas;
1300 }
1301
Romain Guyef359272013-01-31 19:07:29 -08001302 @Override
1303 void setName(String name) {
1304 mName = name;
1305 }
1306
Romain Guye3924992010-06-10 18:51:21 -07001307 boolean canDraw() {
1308 return mGl != null && mCanvas != null;
1309 }
1310
Chet Haase44b2fe32012-06-06 19:03:58 -07001311 int onPreDraw(Rect dirty) {
1312 return DisplayList.STATUS_DONE;
Romain Guye3924992010-06-10 18:51:21 -07001313 }
1314
Romain Guyb025b9c2010-09-16 14:16:48 -07001315 void onPostDraw() {
1316 }
Romain Guye3924992010-06-10 18:51:21 -07001317
Romain Guy8f3b8e32012-03-27 16:33:45 -07001318 class FunctorsRunnable implements Runnable {
1319 View.AttachInfo attachInfo;
1320
1321 @Override
1322 public void run() {
1323 final HardwareRenderer renderer = attachInfo.mHardwareRenderer;
1324 if (renderer == null || !renderer.isEnabled() || renderer != GlRenderer.this) {
1325 return;
1326 }
1327
1328 final int surfaceState = checkCurrent();
1329 if (surfaceState != SURFACE_STATE_ERROR) {
1330 int status = mCanvas.invokeFunctors(mRedrawClip);
1331 handleFunctorStatus(attachInfo, status);
1332 }
1333 }
1334 }
1335
Romain Guye3924992010-06-10 18:51:21 -07001336 @Override
Romain Guye55945e2013-04-04 15:26:04 -07001337 void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
Romain Guy7d7b5492011-01-24 16:33:45 -08001338 Rect dirty) {
Romain Guye3924992010-06-10 18:51:21 -07001339 if (canDraw()) {
Romain Guy02ccac62011-06-24 13:20:23 -07001340 if (!hasDirtyRegions()) {
Romain Guy7d7b5492011-01-24 16:33:45 -08001341 dirty = null;
1342 }
Romain Guye3924992010-06-10 18:51:21 -07001343 attachInfo.mIgnoreDirtyState = true;
Romain Guy02ccac62011-06-24 13:20:23 -07001344 attachInfo.mDrawingTime = SystemClock.uptimeMillis();
1345
Dianne Hackborn4702a852012-08-17 15:18:29 -07001346 view.mPrivateFlags |= View.PFLAG_DRAWN;
Romain Guy03985752011-07-11 15:33:51 -07001347
Romain Guyd88f54c2011-01-24 20:22:49 -08001348 final int surfaceState = checkCurrent();
1349 if (surfaceState != SURFACE_STATE_ERROR) {
Romain Guy76878822012-03-30 14:54:22 -07001350 HardwareCanvas canvas = mCanvas;
1351 attachInfo.mHardwareCanvas = canvas;
Romain Guy77e67cf2012-06-19 16:38:50 -07001352
1353 if (mProfileEnabled) {
1354 mProfileLock.lock();
1355 }
1356
Romain Guy672433d2013-01-04 19:05:13 -08001357 dirty = beginFrame(canvas, dirty, surfaceState);
Romain Guyd88f54c2011-01-24 20:22:49 -08001358
Romain Guyd17043d2013-02-19 14:12:55 -08001359 DisplayList displayList = buildDisplayList(view, canvas);
1360
Romain Guy2b7028e2012-09-19 17:25:38 -07001361 int saveCount = 0;
1362 int status = DisplayList.STATUS_DONE;
Chet Haasedaf98e92011-01-10 14:10:36 -08001363
Romain Guyb8c0de22010-12-13 14:42:34 -08001364 try {
Romain Guy672433d2013-01-04 19:05:13 -08001365 status = prepareFrame(dirty);
Jeff Brown95db2b22011-11-30 19:54:41 -08001366
Romain Guy2b7028e2012-09-19 17:25:38 -07001367 saveCount = canvas.save();
1368 callbacks.onHardwarePreDraw(canvas);
1369
Chet Haasedaf98e92011-01-10 14:10:36 -08001370 if (displayList != null) {
Chet Haase58d110a2013-04-10 07:43:29 -07001371 status |= drawDisplayList(attachInfo, canvas, displayList, status);
Chet Haasedaf98e92011-01-10 14:10:36 -08001372 } else {
1373 // Shouldn't reach here
1374 view.draw(canvas);
1375 }
Romain Guyd17043d2013-02-19 14:12:55 -08001376 } catch (Exception e) {
1377 Log.e(LOG_TAG, "An error has occurred while drawing:", e);
Romain Guyb04f7e92012-02-15 12:36:54 -08001378 } finally {
1379 callbacks.onHardwarePostDraw(canvas);
1380 canvas.restoreToCount(saveCount);
1381 view.mRecreateDisplayList = false;
Romain Guy19f86e82012-04-23 15:19:07 -07001382
Michael Jurkaa3fabff2012-03-28 17:22:29 +02001383 mFrameCount++;
Romain Guy19f86e82012-04-23 15:19:07 -07001384
Romain Guy672433d2013-01-04 19:05:13 -08001385 debugDirtyRegions(dirty, canvas);
Romain Guy98e4a522013-01-07 10:58:34 -08001386 drawProfileData(attachInfo);
Romain Guyb8c0de22010-12-13 14:42:34 -08001387 }
Chet Haasedaf98e92011-01-10 14:10:36 -08001388
Romain Guyb8c0de22010-12-13 14:42:34 -08001389 onPostDraw();
Chet Haasedaf98e92011-01-10 14:10:36 -08001390
Romain Guy672433d2013-01-04 19:05:13 -08001391 swapBuffers(status);
Jeff Brown95db2b22011-11-30 19:54:41 -08001392
Romain Guy77e67cf2012-06-19 16:38:50 -07001393 if (mProfileEnabled) {
1394 mProfileLock.unlock();
1395 }
1396
Romain Guy672433d2013-01-04 19:05:13 -08001397 attachInfo.mIgnoreDirtyState = false;
Romain Guye3924992010-06-10 18:51:21 -07001398 }
Romain Guye3924992010-06-10 18:51:21 -07001399 }
1400 }
Romain Guy03985752011-07-11 15:33:51 -07001401
Romain Guyd17043d2013-02-19 14:12:55 -08001402 private DisplayList buildDisplayList(View view, HardwareCanvas canvas) {
1403 view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
1404 == View.PFLAG_INVALIDATED;
1405 view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
1406
1407 long buildDisplayListStartTime = startBuildDisplayListProfiling();
1408 canvas.clearLayerUpdates();
1409
1410 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList");
1411 DisplayList displayList = view.getDisplayList();
1412 Trace.traceEnd(Trace.TRACE_TAG_VIEW);
1413
1414 endBuildDisplayListProfiling(buildDisplayListStartTime);
1415
1416 return displayList;
1417 }
1418
Romain Guy98e4a522013-01-07 10:58:34 -08001419 abstract void drawProfileData(View.AttachInfo attachInfo);
Romain Guy672433d2013-01-04 19:05:13 -08001420
1421 private Rect beginFrame(HardwareCanvas canvas, Rect dirty, int surfaceState) {
1422 // We had to change the current surface and/or context, redraw everything
1423 if (surfaceState == SURFACE_STATE_UPDATED) {
1424 dirty = null;
1425 beginFrame(null);
1426 } else {
1427 int[] size = mSurfaceSize;
1428 beginFrame(size);
1429
1430 if (size[1] != mHeight || size[0] != mWidth) {
1431 mWidth = size[0];
1432 mHeight = size[1];
1433
1434 canvas.setViewport(mWidth, mHeight);
1435
1436 dirty = null;
1437 }
1438 }
1439
Romain Guy48ef4a92013-01-10 18:38:46 -08001440 if (mDebugDataProvider != null) dirty = null;
Romain Guy672433d2013-01-04 19:05:13 -08001441
1442 return dirty;
1443 }
1444
1445 private long startBuildDisplayListProfiling() {
1446 if (mProfileEnabled) {
1447 mProfileCurrentFrame += PROFILE_FRAME_DATA_COUNT;
1448 if (mProfileCurrentFrame >= mProfileData.length) {
1449 mProfileCurrentFrame = 0;
1450 }
1451
1452 return System.nanoTime();
1453 }
1454 return 0;
1455 }
1456
1457 private void endBuildDisplayListProfiling(long getDisplayListStartTime) {
1458 if (mProfileEnabled) {
1459 long now = System.nanoTime();
1460 float total = (now - getDisplayListStartTime) * 0.000001f;
1461 //noinspection PointlessArithmeticExpression
1462 mProfileData[mProfileCurrentFrame] = total;
1463 }
1464 }
1465
Romain Guy672433d2013-01-04 19:05:13 -08001466 private int prepareFrame(Rect dirty) {
1467 int status;
1468 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "prepareFrame");
1469 try {
1470 status = onPreDraw(dirty);
1471 } finally {
1472 Trace.traceEnd(Trace.TRACE_TAG_VIEW);
1473 }
1474 return status;
1475 }
1476
1477 private int drawDisplayList(View.AttachInfo attachInfo, HardwareCanvas canvas,
1478 DisplayList displayList, int status) {
1479
1480 long drawDisplayListStartTime = 0;
1481 if (mProfileEnabled) {
1482 drawDisplayListStartTime = System.nanoTime();
1483 }
1484
1485 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "drawDisplayList");
1486 try {
1487 status |= canvas.drawDisplayList(displayList, mRedrawClip,
1488 DisplayList.FLAG_CLIP_CHILDREN);
1489 } finally {
1490 Trace.traceEnd(Trace.TRACE_TAG_VIEW);
1491 }
1492
1493 if (mProfileEnabled) {
1494 long now = System.nanoTime();
1495 float total = (now - drawDisplayListStartTime) * 0.000001f;
1496 mProfileData[mProfileCurrentFrame + 1] = total;
1497 }
1498
1499 handleFunctorStatus(attachInfo, status);
1500 return status;
1501 }
1502
1503 private void swapBuffers(int status) {
1504 if ((status & DisplayList.STATUS_DREW) == DisplayList.STATUS_DREW) {
1505 long eglSwapBuffersStartTime = 0;
1506 if (mProfileEnabled) {
1507 eglSwapBuffersStartTime = System.nanoTime();
1508 }
1509
1510 sEgl.eglSwapBuffers(sEglDisplay, mEglSurface);
1511
1512 if (mProfileEnabled) {
1513 long now = System.nanoTime();
1514 float total = (now - eglSwapBuffersStartTime) * 0.000001f;
1515 mProfileData[mProfileCurrentFrame + 2] = total;
1516 }
1517
1518 checkEglErrors();
1519 }
1520 }
1521
1522 private void debugDirtyRegions(Rect dirty, HardwareCanvas canvas) {
1523 if (mDebugDirtyRegions) {
1524 if (mDebugPaint == null) {
1525 mDebugPaint = new Paint();
1526 mDebugPaint.setColor(0x7fff0000);
1527 }
1528
1529 if (dirty != null && (mFrameCount & 1) == 0) {
1530 canvas.drawRect(dirty, mDebugPaint);
1531 }
1532 }
1533 }
1534
Romain Guy8f3b8e32012-03-27 16:33:45 -07001535 private void handleFunctorStatus(View.AttachInfo attachInfo, int status) {
1536 // If the draw flag is set, functors will be invoked while executing
1537 // the tree of display lists
1538 if ((status & DisplayList.STATUS_DRAW) != 0) {
1539 if (mRedrawClip.isEmpty()) {
1540 attachInfo.mViewRootImpl.invalidate();
1541 } else {
1542 attachInfo.mViewRootImpl.invalidateChildInParent(null, mRedrawClip);
1543 mRedrawClip.setEmpty();
1544 }
1545 }
1546
Chris Craik9efa2222012-12-04 15:15:31 -08001547 if ((status & DisplayList.STATUS_INVOKE) != 0 ||
1548 attachInfo.mHandler.hasCallbacks(mFunctorsRunnable)) {
1549 attachInfo.mHandler.removeCallbacks(mFunctorsRunnable);
1550 mFunctorsRunnable.attachInfo = attachInfo;
1551 attachInfo.mHandler.postDelayed(mFunctorsRunnable, FUNCTOR_PROCESS_DELAY);
Romain Guy8f3b8e32012-03-27 16:33:45 -07001552 }
1553 }
1554
Romain Guyba6be8a2012-04-23 18:22:09 -07001555 @Override
1556 void detachFunctor(int functor) {
1557 if (mCanvas != null) {
1558 mCanvas.detachFunctor(functor);
Chris Craik932b7f62012-06-06 13:59:33 -07001559 }
Romain Guyba6be8a2012-04-23 18:22:09 -07001560 }
1561
1562 @Override
Chris Craik41ee4652012-05-31 15:05:57 -07001563 boolean attachFunctor(View.AttachInfo attachInfo, int functor) {
Romain Guyba6be8a2012-04-23 18:22:09 -07001564 if (mCanvas != null) {
1565 mCanvas.attachFunctor(functor);
Chris Craik9efa2222012-12-04 15:15:31 -08001566 mFunctorsRunnable.attachInfo = attachInfo;
1567 attachInfo.mHandler.removeCallbacks(mFunctorsRunnable);
1568 attachInfo.mHandler.postDelayed(mFunctorsRunnable, 0);
Chris Craik41ee4652012-05-31 15:05:57 -07001569 return true;
Romain Guyba6be8a2012-04-23 18:22:09 -07001570 }
Chris Craik41ee4652012-05-31 15:05:57 -07001571 return false;
Romain Guyba6be8a2012-04-23 18:22:09 -07001572 }
1573
Romain Guy03985752011-07-11 15:33:51 -07001574 /**
Romain Guy566b3ef2011-07-18 18:38:12 -07001575 * Ensures the current EGL context is the one we expect.
Romain Guy03985752011-07-11 15:33:51 -07001576 *
1577 * @return {@link #SURFACE_STATE_ERROR} if the correct EGL context cannot be made current,
1578 * {@link #SURFACE_STATE_UPDATED} if the EGL context was changed or
1579 * {@link #SURFACE_STATE_SUCCESS} if the EGL context was the correct one
1580 */
Romain Guy912a7b32011-07-26 18:57:28 -07001581 int checkCurrent() {
Romain Guy566b3ef2011-07-18 18:38:12 -07001582 if (mEglThread != Thread.currentThread()) {
Romain Guy03985752011-07-11 15:33:51 -07001583 throw new IllegalStateException("Hardware acceleration can only be used with a " +
Romain Guy566b3ef2011-07-18 18:38:12 -07001584 "single UI thread.\nOriginal thread: " + mEglThread + "\n" +
Romain Guy03985752011-07-11 15:33:51 -07001585 "Current thread: " + Thread.currentThread());
1586 }
1587
Romain Guy566b3ef2011-07-18 18:38:12 -07001588 if (!mEglContext.equals(sEgl.eglGetCurrentContext()) ||
Romain Guy484c7192011-07-25 11:56:33 -07001589 !mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL_DRAW))) {
Romain Guy566b3ef2011-07-18 18:38:12 -07001590 if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
Romain Guy9745fae2010-12-08 11:39:15 -08001591 Log.e(LOG_TAG, "eglMakeCurrent failed " +
Romain Guy407ec782011-08-24 17:06:58 -07001592 GLUtils.getEGLErrorString(sEgl.eglGetError()));
Romain Guyeca9b1f2011-08-26 13:36:37 -07001593 fallback(true);
Romain Guyd88f54c2011-01-24 20:22:49 -08001594 return SURFACE_STATE_ERROR;
1595 } else {
Romain Guy9477c6e2011-12-09 12:27:21 -08001596 if (mUpdateDirtyRegions) {
1597 enableDirtyRegions();
1598 mUpdateDirtyRegions = false;
1599 }
Romain Guyd88f54c2011-01-24 20:22:49 -08001600 return SURFACE_STATE_UPDATED;
Romain Guyfb8b7632010-08-23 21:05:08 -07001601 }
1602 }
Romain Guyd88f54c2011-01-24 20:22:49 -08001603 return SURFACE_STATE_SUCCESS;
Romain Guyfb8b7632010-08-23 21:05:08 -07001604 }
Romain Guy48ef4a92013-01-10 18:38:46 -08001605
1606 private static int dpToPx(int dp, float density) {
1607 return (int) (dp * density + 0.5f);
1608 }
1609
1610 class DrawPerformanceDataProvider extends GraphDataProvider {
1611 private final int mGraphType;
1612
1613 private int mVerticalUnit;
1614 private int mHorizontalUnit;
1615 private int mHorizontalMargin;
1616 private int mThresholdStroke;
1617
1618 DrawPerformanceDataProvider(int graphType) {
1619 mGraphType = graphType;
1620 }
1621
1622 @Override
1623 void prepare(DisplayMetrics metrics) {
1624 final float density = metrics.density;
1625
1626 mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density);
1627 mHorizontalUnit = dpToPx(PROFILE_DRAW_WIDTH, density);
1628 mHorizontalMargin = dpToPx(PROFILE_DRAW_MARGIN, density);
1629 mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density);
1630 }
1631
1632 @Override
1633 int getGraphType() {
1634 return mGraphType;
1635 }
1636
1637 @Override
1638 int getVerticalUnitSize() {
1639 return mVerticalUnit;
1640 }
1641
1642 @Override
1643 int getHorizontalUnitSize() {
1644 return mHorizontalUnit;
1645 }
1646
1647 @Override
1648 int getHorizontaUnitMargin() {
1649 return mHorizontalMargin;
1650 }
1651
1652 @Override
1653 float[] getData() {
1654 return mProfileData;
1655 }
1656
1657 @Override
1658 float getThreshold() {
1659 return 16;
1660 }
1661
1662 @Override
1663 int getFrameCount() {
1664 return mProfileData.length / PROFILE_FRAME_DATA_COUNT;
1665 }
1666
1667 @Override
1668 int getElementCount() {
1669 return PROFILE_FRAME_DATA_COUNT;
1670 }
1671
1672 @Override
1673 int getCurrentFrame() {
1674 return mProfileCurrentFrame / PROFILE_FRAME_DATA_COUNT;
1675 }
1676
1677 @Override
1678 void setupGraphPaint(Paint paint, int elementIndex) {
1679 paint.setColor(PROFILE_DRAW_COLORS[elementIndex]);
1680 if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke);
1681 }
1682
1683 @Override
1684 void setupThresholdPaint(Paint paint) {
1685 paint.setColor(PROFILE_DRAW_THRESHOLD_COLOR);
1686 paint.setStrokeWidth(mThresholdStroke);
1687 }
1688
1689 @Override
1690 void setupCurrentFramePaint(Paint paint) {
1691 paint.setColor(PROFILE_DRAW_CURRENT_FRAME_COLOR);
1692 if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke);
1693 }
1694 }
Romain Guye3924992010-06-10 18:51:21 -07001695 }
Romain Guyfb8b7632010-08-23 21:05:08 -07001696
Romain Guye3924992010-06-10 18:51:21 -07001697 /**
1698 * Hardware renderer using OpenGL ES 2.0.
1699 */
1700 static class Gl20Renderer extends GlRenderer {
Romain Guye4d01122010-06-16 18:44:05 -07001701 private GLES20Canvas mGlCanvas;
1702
Romain Guy98e4a522013-01-07 10:58:34 -08001703 private DisplayMetrics mDisplayMetrics;
1704
Romain Guy912a7b32011-07-26 18:57:28 -07001705 private static EGLSurface sPbuffer;
1706 private static final Object[] sPbufferLock = new Object[0];
1707
Romain Guy31f2c2e2011-11-21 10:55:41 -08001708 static class Gl20RendererEglContext extends ManagedEGLContext {
Dianne Hackborn717a25d2011-11-15 18:59:59 -08001709 final Handler mHandler = new Handler();
1710
Romain Guy31f2c2e2011-11-21 10:55:41 -08001711 public Gl20RendererEglContext(EGLContext context) {
Dianne Hackborn717a25d2011-11-15 18:59:59 -08001712 super(context);
1713 }
1714
1715 @Override
1716 public void onTerminate(final EGLContext eglContext) {
1717 // Make sure we do this on the correct thread.
1718 if (mHandler.getLooper() != Looper.myLooper()) {
1719 mHandler.post(new Runnable() {
Romain Guy5d6999e2012-03-22 19:15:04 -07001720 @Override
1721 public void run() {
Dianne Hackborn717a25d2011-11-15 18:59:59 -08001722 onTerminate(eglContext);
1723 }
1724 });
1725 return;
1726 }
1727
1728 synchronized (sEglLock) {
1729 if (sEgl == null) return;
1730
1731 if (EGLImpl.getInitCount(sEglDisplay) == 1) {
1732 usePbufferSurface(eglContext);
1733 GLES20Canvas.terminateCaches();
1734
1735 sEgl.eglDestroyContext(sEglDisplay, eglContext);
Romain Guya998dff2012-03-23 18:58:36 -07001736 sEglContextStorage.set(null);
Dianne Hackborn717a25d2011-11-15 18:59:59 -08001737 sEglContextStorage.remove();
1738
1739 sEgl.eglDestroySurface(sEglDisplay, sPbuffer);
Romain Guy31f2c2e2011-11-21 10:55:41 -08001740 sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE,
1741 EGL_NO_SURFACE, EGL_NO_CONTEXT);
Dianne Hackborn717a25d2011-11-15 18:59:59 -08001742
1743 sEgl.eglReleaseThread();
1744 sEgl.eglTerminate(sEglDisplay);
1745
1746 sEgl = null;
1747 sEglDisplay = null;
1748 sEglConfig = null;
1749 sPbuffer = null;
Dianne Hackborn717a25d2011-11-15 18:59:59 -08001750 }
1751 }
1752 }
1753 }
1754
Romain Guye4d01122010-06-16 18:44:05 -07001755 Gl20Renderer(boolean translucent) {
1756 super(2, translucent);
Romain Guye3924992010-06-10 18:51:21 -07001757 }
1758
1759 @Override
Romain Guy5d6999e2012-03-22 19:15:04 -07001760 HardwareCanvas createCanvas() {
Romain Guy6b7bd242010-10-06 19:49:23 -07001761 return mGlCanvas = new GLES20Canvas(mTranslucent);
Romain Guye4d01122010-06-16 18:44:05 -07001762 }
Romain Guye91a9c72011-05-02 14:53:30 -07001763
1764 @Override
Romain Guy5d6999e2012-03-22 19:15:04 -07001765 ManagedEGLContext createManagedContext(EGLContext eglContext) {
1766 return new Gl20Renderer.Gl20RendererEglContext(mEglContext);
1767 }
1768
1769 @Override
Romain Guye91a9c72011-05-02 14:53:30 -07001770 int[] getConfig(boolean dirtyRegions) {
Romain Guy8ce00302013-01-15 18:51:42 -08001771 //noinspection PointlessBooleanExpression,ConstantConditions
1772 final int stencilSize = GLES20Canvas.getStencilSize();
Romain Guy735738c2012-12-03 12:34:51 -08001773 final int swapBehavior = dirtyRegions ? EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
1774
Romain Guye91a9c72011-05-02 14:53:30 -07001775 return new int[] {
Romain Guybd431522012-09-26 13:31:25 -07001776 EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
Romain Guy484c7192011-07-25 11:56:33 -07001777 EGL_RED_SIZE, 8,
1778 EGL_GREEN_SIZE, 8,
1779 EGL_BLUE_SIZE, 8,
1780 EGL_ALPHA_SIZE, 8,
1781 EGL_DEPTH_SIZE, 0,
Romain Guy8efca542012-10-15 18:09:49 -07001782 EGL_CONFIG_CAVEAT, EGL_NONE,
Romain Guy735738c2012-12-03 12:34:51 -08001783 EGL_STENCIL_SIZE, stencilSize,
1784 EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior,
Romain Guy484c7192011-07-25 11:56:33 -07001785 EGL_NONE
Romain Guye91a9c72011-05-02 14:53:30 -07001786 };
1787 }
Romain Guy735738c2012-12-03 12:34:51 -08001788
Romain Guy8ff6b9e2011-11-09 20:10:18 -08001789 @Override
1790 void initCaches() {
1791 GLES20Canvas.initCaches();
1792 }
Romain Guye91a9c72011-05-02 14:53:30 -07001793
Romain Guyb8c0de22010-12-13 14:42:34 -08001794 @Override
1795 boolean canDraw() {
1796 return super.canDraw() && mGlCanvas != null;
1797 }
Romain Guye4d01122010-06-16 18:44:05 -07001798
1799 @Override
Chet Haase44b2fe32012-06-06 19:03:58 -07001800 int onPreDraw(Rect dirty) {
1801 return mGlCanvas.onPreDraw(dirty);
Romain Guye3924992010-06-10 18:51:21 -07001802 }
1803
Romain Guyb025b9c2010-09-16 14:16:48 -07001804 @Override
1805 void onPostDraw() {
1806 mGlCanvas.onPostDraw();
1807 }
1808
Romain Guyb051e892010-09-28 19:09:36 -07001809 @Override
Romain Guy98e4a522013-01-07 10:58:34 -08001810 void drawProfileData(View.AttachInfo attachInfo) {
Romain Guy48ef4a92013-01-10 18:38:46 -08001811 if (mDebugDataProvider != null) {
1812 final GraphDataProvider provider = mDebugDataProvider;
1813 initProfileDrawData(attachInfo, provider);
Romain Guy98e4a522013-01-07 10:58:34 -08001814
Romain Guy48ef4a92013-01-10 18:38:46 -08001815 final int height = provider.getVerticalUnitSize();
1816 final int margin = provider.getHorizontaUnitMargin();
1817 final int width = provider.getHorizontalUnitSize();
Romain Guy672433d2013-01-04 19:05:13 -08001818
1819 int x = 0;
1820 int count = 0;
1821 int current = 0;
1822
Romain Guy48ef4a92013-01-10 18:38:46 -08001823 final float[] data = provider.getData();
1824 final int elementCount = provider.getElementCount();
1825 final int graphType = provider.getGraphType();
1826
1827 int totalCount = provider.getFrameCount() * elementCount;
1828 if (graphType == GraphDataProvider.GRAPH_TYPE_LINES) {
1829 totalCount -= elementCount;
1830 }
1831
1832 for (int i = 0; i < totalCount; i += elementCount) {
1833 if (data[i] < 0.0f) break;
Romain Guy672433d2013-01-04 19:05:13 -08001834
1835 int index = count * 4;
Romain Guy48ef4a92013-01-10 18:38:46 -08001836 if (i == provider.getCurrentFrame() * elementCount) current = index;
Romain Guy672433d2013-01-04 19:05:13 -08001837
Romain Guy98e4a522013-01-07 10:58:34 -08001838 x += margin;
1839 int x2 = x + width;
Romain Guy672433d2013-01-04 19:05:13 -08001840
1841 int y2 = mHeight;
Romain Guy48ef4a92013-01-10 18:38:46 -08001842 int y1 = (int) (y2 - data[i] * height);
Romain Guy672433d2013-01-04 19:05:13 -08001843
Romain Guy48ef4a92013-01-10 18:38:46 -08001844 switch (graphType) {
1845 case GraphDataProvider.GRAPH_TYPE_BARS: {
1846 for (int j = 0; j < elementCount; j++) {
1847 //noinspection MismatchedReadAndWriteOfArray
1848 final float[] r = mProfileShapes[j];
1849 r[index] = x;
1850 r[index + 1] = y1;
1851 r[index + 2] = x2;
1852 r[index + 3] = y2;
Romain Guy672433d2013-01-04 19:05:13 -08001853
Romain Guy48ef4a92013-01-10 18:38:46 -08001854 y2 = y1;
1855 if (j < elementCount - 1) {
1856 y1 = (int) (y2 - data[i + j + 1] * height);
1857 }
1858 }
1859 } break;
1860 case GraphDataProvider.GRAPH_TYPE_LINES: {
1861 for (int j = 0; j < elementCount; j++) {
1862 //noinspection MismatchedReadAndWriteOfArray
1863 final float[] r = mProfileShapes[j];
1864 r[index] = (x + x2) * 0.5f;
1865 r[index + 1] = index == 0 ? y1 : r[index - 1];
1866 r[index + 2] = r[index] + width;
1867 r[index + 3] = y1;
Romain Guy672433d2013-01-04 19:05:13 -08001868
Romain Guy48ef4a92013-01-10 18:38:46 -08001869 y2 = y1;
1870 if (j < elementCount - 1) {
1871 y1 = (int) (y2 - data[i + j + 1] * height);
1872 }
1873 }
1874 } break;
1875 }
Romain Guy672433d2013-01-04 19:05:13 -08001876
Romain Guy672433d2013-01-04 19:05:13 -08001877
Romain Guy98e4a522013-01-07 10:58:34 -08001878 x += width;
Romain Guy672433d2013-01-04 19:05:13 -08001879 count++;
1880 }
Romain Guy48ef4a92013-01-10 18:38:46 -08001881
Romain Guy98e4a522013-01-07 10:58:34 -08001882 x += margin;
Romain Guy672433d2013-01-04 19:05:13 -08001883
Romain Guy48ef4a92013-01-10 18:38:46 -08001884 drawGraph(graphType, count);
1885 drawCurrentFrame(graphType, current);
1886 drawThreshold(x, height);
Romain Guy672433d2013-01-04 19:05:13 -08001887 }
1888 }
1889
Romain Guy48ef4a92013-01-10 18:38:46 -08001890 private void drawGraph(int graphType, int count) {
1891 for (int i = 0; i < mProfileShapes.length; i++) {
1892 mDebugDataProvider.setupGraphPaint(mProfilePaint, i);
1893 switch (graphType) {
1894 case GraphDataProvider.GRAPH_TYPE_BARS:
Chris Craik2af46352012-11-26 18:30:17 -08001895 mGlCanvas.drawRects(mProfileShapes[i], count * 4, mProfilePaint);
Romain Guy48ef4a92013-01-10 18:38:46 -08001896 break;
1897 case GraphDataProvider.GRAPH_TYPE_LINES:
1898 mGlCanvas.drawLines(mProfileShapes[i], 0, count * 4, mProfilePaint);
1899 break;
Romain Guy672433d2013-01-04 19:05:13 -08001900 }
Romain Guy48ef4a92013-01-10 18:38:46 -08001901 }
1902 }
1903
1904 private void drawCurrentFrame(int graphType, int index) {
1905 if (index >= 0) {
1906 mDebugDataProvider.setupCurrentFramePaint(mProfilePaint);
1907 switch (graphType) {
1908 case GraphDataProvider.GRAPH_TYPE_BARS:
1909 mGlCanvas.drawRect(mProfileShapes[2][index], mProfileShapes[2][index + 1],
1910 mProfileShapes[2][index + 2], mProfileShapes[0][index + 3],
1911 mProfilePaint);
1912 break;
1913 case GraphDataProvider.GRAPH_TYPE_LINES:
1914 mGlCanvas.drawLine(mProfileShapes[2][index], mProfileShapes[2][index + 1],
1915 mProfileShapes[2][index], mHeight, mProfilePaint);
1916 break;
1917 }
1918 }
1919 }
1920
1921 private void drawThreshold(int x, int height) {
1922 float threshold = mDebugDataProvider.getThreshold();
1923 if (threshold > 0.0f) {
1924 mDebugDataProvider.setupThresholdPaint(mProfilePaint);
1925 int y = (int) (mHeight - threshold * height);
1926 mGlCanvas.drawLine(0.0f, y, x, y, mProfilePaint);
1927 }
1928 }
1929
1930 private void initProfileDrawData(View.AttachInfo attachInfo, GraphDataProvider provider) {
1931 if (mProfileShapes == null) {
1932 final int elementCount = provider.getElementCount();
1933 final int frameCount = provider.getFrameCount();
1934
1935 mProfileShapes = new float[elementCount][];
1936 for (int i = 0; i < elementCount; i++) {
1937 mProfileShapes[i] = new float[frameCount * 4];
1938 }
1939
Romain Guy672433d2013-01-04 19:05:13 -08001940 mProfilePaint = new Paint();
1941 }
Romain Guy98e4a522013-01-07 10:58:34 -08001942
Romain Guy48ef4a92013-01-10 18:38:46 -08001943 mProfilePaint.reset();
1944 if (provider.getGraphType() == GraphDataProvider.GRAPH_TYPE_LINES) {
1945 mProfilePaint.setAntiAlias(true);
1946 }
1947
Romain Guy98e4a522013-01-07 10:58:34 -08001948 if (mDisplayMetrics == null) {
1949 mDisplayMetrics = new DisplayMetrics();
1950 }
Romain Guy48ef4a92013-01-10 18:38:46 -08001951
Romain Guy98e4a522013-01-07 10:58:34 -08001952 attachInfo.mDisplay.getMetrics(mDisplayMetrics);
Romain Guy48ef4a92013-01-10 18:38:46 -08001953 provider.prepare(mDisplayMetrics);
Romain Guy672433d2013-01-04 19:05:13 -08001954 }
1955
1956 @Override
Romain Guy67f27952010-12-07 20:09:23 -08001957 void destroy(boolean full) {
Romain Guyb8c0de22010-12-13 14:42:34 -08001958 try {
1959 super.destroy(full);
1960 } finally {
1961 if (full && mGlCanvas != null) {
1962 mGlCanvas = null;
1963 }
Romain Guy67f27952010-12-07 20:09:23 -08001964 }
1965 }
1966
1967 @Override
Romain Guy11cb6422012-09-21 00:39:43 -07001968 void pushLayerUpdate(HardwareLayer layer) {
1969 mGlCanvas.pushLayerUpdate(layer);
1970 }
1971
1972 @Override
Romain Guy13631f32012-01-30 17:41:55 -08001973 public DisplayList createDisplayList(String name) {
1974 return new GLES20DisplayList(name);
Romain Guyb051e892010-09-28 19:09:36 -07001975 }
Romain Guyaa6c24c2011-04-28 18:40:04 -07001976
1977 @Override
Romain Guya9489272011-06-22 20:58:11 -07001978 HardwareLayer createHardwareLayer(boolean isOpaque) {
1979 return new GLES20TextureLayer(isOpaque);
Romain Guyaa6c24c2011-04-28 18:40:04 -07001980 }
1981
Romain Guy6c319ca2011-01-11 14:29:25 -08001982 @Override
Romain Guy52036b12013-02-14 18:03:37 -08001983 public HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque) {
Romain Guyaa6c24c2011-04-28 18:40:04 -07001984 return new GLES20RenderLayer(width, height, isOpaque);
1985 }
1986
1987 @Override
Romain Guy52036b12013-02-14 18:03:37 -08001988 public SurfaceTexture createSurfaceTexture(HardwareLayer layer) {
Romain Guyaa6c24c2011-04-28 18:40:04 -07001989 return ((GLES20TextureLayer) layer).getSurfaceTexture();
1990 }
1991
Romain Guy6d7475d2011-07-27 16:28:21 -07001992 @Override
Jamie Gennis2af35242012-04-05 11:44:30 -07001993 void setSurfaceTexture(HardwareLayer layer, SurfaceTexture surfaceTexture) {
1994 ((GLES20TextureLayer) layer).setSurfaceTexture(surfaceTexture);
1995 }
1996
1997 @Override
Romain Guy1ac47652012-04-11 18:15:20 -07001998 boolean safelyRun(Runnable action) {
1999 boolean needsContext = true;
2000 if (isEnabled() && checkCurrent() != SURFACE_STATE_ERROR) needsContext = false;
Romain Guy31f2c2e2011-11-21 10:55:41 -08002001
Romain Guy1ac47652012-04-11 18:15:20 -07002002 if (needsContext) {
2003 Gl20RendererEglContext managedContext =
2004 (Gl20RendererEglContext) sEglContextStorage.get();
2005 if (managedContext == null) return false;
2006 usePbufferSurface(managedContext.getContext());
2007 }
Romain Guy31f2c2e2011-11-21 10:55:41 -08002008
Romain Guy1ac47652012-04-11 18:15:20 -07002009 try {
2010 action.run();
2011 } finally {
Jesse Hall0872b372012-04-02 12:22:51 -07002012 if (needsContext) {
2013 sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE,
2014 EGL_NO_SURFACE, EGL_NO_CONTEXT);
2015 }
Romain Guy31f2c2e2011-11-21 10:55:41 -08002016 }
Romain Guy1ac47652012-04-11 18:15:20 -07002017
2018 return true;
2019 }
2020
2021 @Override
Romain Guybd17bd32012-10-23 19:17:15 -07002022 void destroyLayers(final View view) {
2023 if (view != null) {
2024 safelyRun(new Runnable() {
2025 @Override
2026 public void run() {
2027 if (mCanvas != null) {
2028 mCanvas.clearLayerUpdates();
2029 }
2030 destroyHardwareLayer(view);
2031 GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS);
2032 }
2033 });
2034 }
2035 }
2036
2037 private static void destroyHardwareLayer(View view) {
2038 view.destroyLayer(true);
2039
2040 if (view instanceof ViewGroup) {
2041 ViewGroup group = (ViewGroup) view;
2042
2043 int count = group.getChildCount();
2044 for (int i = 0; i < count; i++) {
2045 destroyHardwareLayer(group.getChildAt(i));
2046 }
2047 }
2048 }
2049
2050 @Override
Romain Guy1ac47652012-04-11 18:15:20 -07002051 void destroyHardwareResources(final View view) {
2052 if (view != null) {
2053 safelyRun(new Runnable() {
2054 @Override
2055 public void run() {
Chet Haase6a2d17f2012-09-30 12:14:13 -07002056 if (mCanvas != null) {
2057 mCanvas.clearLayerUpdates();
2058 }
Romain Guy1ac47652012-04-11 18:15:20 -07002059 destroyResources(view);
2060 GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS);
2061 }
2062 });
2063 }
Romain Guy31f2c2e2011-11-21 10:55:41 -08002064 }
Romain Guy244ada12012-03-28 16:41:26 -07002065
Romain Guy31f2c2e2011-11-21 10:55:41 -08002066 private static void destroyResources(View view) {
2067 view.destroyHardwareResources();
2068
2069 if (view instanceof ViewGroup) {
2070 ViewGroup group = (ViewGroup) view;
2071
2072 int count = group.getChildCount();
2073 for (int i = 0; i < count; i++) {
2074 destroyResources(group.getChildAt(i));
2075 }
2076 }
2077 }
Romain Guy6d7475d2011-07-27 16:28:21 -07002078
2079 static HardwareRenderer create(boolean translucent) {
2080 if (GLES20Canvas.isAvailable()) {
2081 return new Gl20Renderer(translucent);
2082 }
2083 return null;
2084 }
2085
Romain Guy19f86e82012-04-23 15:19:07 -07002086 static void startTrimMemory(int level) {
Romain Guy912a7b32011-07-26 18:57:28 -07002087 if (sEgl == null || sEglConfig == null) return;
2088
Romain Guy5d6999e2012-03-22 19:15:04 -07002089 Gl20RendererEglContext managedContext =
2090 (Gl20RendererEglContext) sEglContextStorage.get();
Romain Guy912a7b32011-07-26 18:57:28 -07002091 // We do not have OpenGL objects
Dianne Hackborn717a25d2011-11-15 18:59:59 -08002092 if (managedContext == null) {
Romain Guy912a7b32011-07-26 18:57:28 -07002093 return;
2094 } else {
Dianne Hackborn717a25d2011-11-15 18:59:59 -08002095 usePbufferSurface(managedContext.getContext());
Romain Guye3924992010-06-10 18:51:21 -07002096 }
Romain Guy912a7b32011-07-26 18:57:28 -07002097
Dianne Hackborn27ff9132012-03-06 14:57:58 -08002098 if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
2099 GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_FULL);
2100 } else if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
2101 GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_MODERATE);
Romain Guybdf76092011-07-18 15:00:43 -07002102 }
Romain Guy19f86e82012-04-23 15:19:07 -07002103 }
Jesse Hall0872b372012-04-02 12:22:51 -07002104
Romain Guy19f86e82012-04-23 15:19:07 -07002105 static void endTrimMemory() {
2106 if (sEgl != null && sEglDisplay != null) {
2107 sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
2108 }
Romain Guybdf76092011-07-18 15:00:43 -07002109 }
Romain Guy8ff6b9e2011-11-09 20:10:18 -08002110
2111 private static void usePbufferSurface(EGLContext eglContext) {
2112 synchronized (sPbufferLock) {
2113 // Create a temporary 1x1 pbuffer so we have a context
2114 // to clear our OpenGL objects
2115 if (sPbuffer == null) {
2116 sPbuffer = sEgl.eglCreatePbufferSurface(sEglDisplay, sEglConfig, new int[] {
2117 EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE
2118 });
2119 }
2120 }
2121 sEgl.eglMakeCurrent(sEglDisplay, sPbuffer, sPbuffer, eglContext);
2122 }
Romain Guye3924992010-06-10 18:51:21 -07002123 }
Romain Guy2d614592010-06-09 18:21:37 -07002124}