blob: 845fbc3727bcf634c8c86dbd4b085facf28aa8ca [file] [log] [blame]
Romain Guy2d614592010-06-09 18:21:37 -07001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17
18package android.view;
19
Romain Guy2d614592010-06-09 18:21:37 -070020import android.graphics.Canvas;
Romain Guy7d7b5492011-01-24 16:33:45 -080021import android.graphics.Paint;
22import android.graphics.Rect;
Romain Guyaa6c24c2011-04-28 18:40:04 -070023import android.graphics.SurfaceTexture;
Romain Guyaaceeb02011-03-23 19:56:13 -070024import android.os.*;
Romain Guy9a40bab2010-09-23 16:32:47 -070025import android.util.EventLog;
Romain Guye3924992010-06-10 18:51:21 -070026import android.util.Log;
Romain Guy2d614592010-06-09 18:21:37 -070027
28import javax.microedition.khronos.egl.EGL10;
29import javax.microedition.khronos.egl.EGL11;
30import javax.microedition.khronos.egl.EGLConfig;
31import javax.microedition.khronos.egl.EGLContext;
32import javax.microedition.khronos.egl.EGLDisplay;
33import javax.microedition.khronos.egl.EGLSurface;
Romain Guye3924992010-06-10 18:51:21 -070034import javax.microedition.khronos.opengles.GL;
Romain Guy2d614592010-06-09 18:21:37 -070035
36/**
Joe Onoratoc6cc0f82011-04-12 11:53:13 -070037 * Interface for rendering a ViewAncestor using hardware acceleration.
Romain Guy2d614592010-06-09 18:21:37 -070038 *
39 * @hide
40 */
Romain Guy61c8c9c2010-08-09 20:48:09 -070041public abstract class HardwareRenderer {
Romain Guy4f6aff32011-01-12 16:21:41 -080042 static final String LOG_TAG = "HardwareRenderer";
Romain Guyfb8b7632010-08-23 21:05:08 -070043
Romain Guy52339202010-09-03 16:04:46 -070044 /**
Romain Guy7d7b5492011-01-24 16:33:45 -080045 * Turn on to only refresh the parts of the screen that need updating.
Romain Guy069ea0e2011-02-08 12:24:52 -080046 * When turned on the property defined by {@link #RENDER_DIRTY_REGIONS_PROPERTY}
47 * must also have the value "true".
Romain Guy7d7b5492011-01-24 16:33:45 -080048 */
49 public static final boolean RENDER_DIRTY_REGIONS = true;
50
51 /**
Romain Guy069ea0e2011-02-08 12:24:52 -080052 * System property used to enable or disable dirty regions invalidation.
53 * This property is only queried if {@link #RENDER_DIRTY_REGIONS} is true.
54 * The default value of this property is assumed to be true.
55 *
56 * Possible values:
57 * "true", to enable partial invalidates
58 * "false", to disable partial invalidates
59 */
60 static final String RENDER_DIRTY_REGIONS_PROPERTY = "hwui.render_dirty_regions";
61
62 /**
Romain Guy7d7b5492011-01-24 16:33:45 -080063 * Turn on to draw dirty regions every other frame.
64 */
65 private static final boolean DEBUG_DIRTY_REGION = false;
66
67 /**
Romain Guy52339202010-09-03 16:04:46 -070068 * A process can set this flag to false to prevent the use of hardware
69 * rendering.
70 *
71 * @hide
72 */
73 public static boolean sRendererDisabled = false;
74
Romain Guy2d614592010-06-09 18:21:37 -070075 private boolean mEnabled;
76 private boolean mRequested = true;
77
78 /**
Romain Guy67f27952010-12-07 20:09:23 -080079 * Invoke this method to disable hardware rendering in the current process.
Romain Guy52339202010-09-03 16:04:46 -070080 *
81 * @hide
82 */
83 public static void disable() {
84 sRendererDisabled = true;
85 }
86
87 /**
Romain Guy61c8c9c2010-08-09 20:48:09 -070088 * Indicates whether hardware acceleration is available under any form for
89 * the view hierarchy.
90 *
91 * @return True if the view hierarchy can potentially be hardware accelerated,
92 * false otherwise
93 */
94 public static boolean isAvailable() {
95 return GLES20Canvas.isAvailable();
96 }
97
98 /**
Romain Guy2d614592010-06-09 18:21:37 -070099 * Destroys the hardware rendering context.
Romain Guy4caa4ed2010-08-25 14:46:24 -0700100 *
101 * @param full If true, destroys all associated resources.
Romain Guy2d614592010-06-09 18:21:37 -0700102 */
Romain Guy4caa4ed2010-08-25 14:46:24 -0700103 abstract void destroy(boolean full);
Romain Guy2d614592010-06-09 18:21:37 -0700104
105 /**
106 * Initializes the hardware renderer for the specified surface.
107 *
108 * @param holder The holder for the surface to hardware accelerate.
109 *
110 * @return True if the initialization was successful, false otherwise.
111 */
Dianne Hackborn64825172011-03-02 21:32:58 -0800112 abstract boolean initialize(SurfaceHolder holder) throws Surface.OutOfResourcesException;
Romain Guy2a83f002011-01-18 18:28:21 -0800113
114 /**
115 * Updates the hardware renderer for the specified surface.
116 *
117 * @param holder The holder for the surface to hardware accelerate.
118 */
Dianne Hackborn64825172011-03-02 21:32:58 -0800119 abstract void updateSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException;
Romain Guy2d614592010-06-09 18:21:37 -0700120
121 /**
122 * Setup the hardware renderer for drawing. This is called for every
123 * frame to draw.
124 *
125 * @param width Width of the drawing surface.
126 * @param height Height of the drawing surface.
Romain Guy2d614592010-06-09 18:21:37 -0700127 */
Romain Guyfb8b7632010-08-23 21:05:08 -0700128 abstract void setup(int width, int height);
Romain Guy2d614592010-06-09 18:21:37 -0700129
Romain Guy069ea0e2011-02-08 12:24:52 -0800130 /**
131 * Interface used to receive callbacks whenever a view is drawn by
132 * a hardware renderer instance.
133 */
Dianne Hackborn0f761d62010-11-30 22:06:10 -0800134 interface HardwareDrawCallbacks {
Romain Guy069ea0e2011-02-08 12:24:52 -0800135 /**
136 * Invoked before a view is drawn by a hardware renderer.
137 *
138 * @param canvas The Canvas used to render the view.
139 */
Dianne Hackborn0f761d62010-11-30 22:06:10 -0800140 void onHardwarePreDraw(Canvas canvas);
Romain Guy069ea0e2011-02-08 12:24:52 -0800141
142 /**
143 * Invoked after a view is drawn by a hardware renderer.
144 *
145 * @param canvas The Canvas used to render the view.
146 */
Dianne Hackborn0f761d62010-11-30 22:06:10 -0800147 void onHardwarePostDraw(Canvas canvas);
148 }
149
Romain Guy2d614592010-06-09 18:21:37 -0700150 /**
151 * Draws the specified view.
Romain Guy7d7b5492011-01-24 16:33:45 -0800152 *
Romain Guy2d614592010-06-09 18:21:37 -0700153 * @param view The view to draw.
154 * @param attachInfo AttachInfo tied to the specified view.
Romain Guy7d7b5492011-01-24 16:33:45 -0800155 * @param callbacks Callbacks invoked when drawing happens.
156 * @param dirty The dirty rectangle to update, can be null.
Romain Guy2d614592010-06-09 18:21:37 -0700157 */
Romain Guy7d7b5492011-01-24 16:33:45 -0800158 abstract void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
159 Rect dirty);
Romain Guy2d614592010-06-09 18:21:37 -0700160
161 /**
Romain Guy53ca03d2010-10-08 18:55:27 -0700162 * Creates a new display list that can be used to record batches of
163 * drawing operations.
Romain Guyb051e892010-09-28 19:09:36 -0700164 *
Romain Guy53ca03d2010-10-08 18:55:27 -0700165 * @return A new display list.
Romain Guyb051e892010-09-28 19:09:36 -0700166 */
Chet Haasedaf98e92011-01-10 14:10:36 -0800167 abstract DisplayList createDisplayList(View v);
Romain Guyb051e892010-09-28 19:09:36 -0700168
169 /**
Romain Guyaa6c24c2011-04-28 18:40:04 -0700170 * Creates a new hardware layer. A hardware layer built by calling this
171 * method will be treated as a texture layer, instead of as a render target.
172 *
173 * @return A hardware layer
174 */
175 abstract HardwareLayer createHardwareLayer();
176
177 /**
Romain Guy6c319ca2011-01-11 14:29:25 -0800178 * Creates a new hardware layer.
179 *
180 * @param width The minimum width of the layer
181 * @param height The minimum height of the layer
182 * @param isOpaque Whether the layer should be opaque or not
183 *
184 * @return A hardware layer
185 */
186 abstract HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque);
Romain Guyaa6c24c2011-04-28 18:40:04 -0700187
188 /**
189 * Creates a new {@link SurfaceTexture} that can be used to render into the
190 * specified hardware layer.
191 *
192 *
193 * @param layer The layer to render into using a {@link android.graphics.SurfaceTexture}
194 *
195 * @return A {@link SurfaceTexture}
196 */
197 abstract SurfaceTexture createSuraceTexture(HardwareLayer layer);
198
199 /**
200 * Updates the specified layer.
201 *
202 * @param layer The hardware layer to update
203 * @param width The layer's width
204 * @param height The layer's height
Romain Guy8f0095c2011-05-02 17:24:22 -0700205 * @param surface The surface to update
Romain Guyaa6c24c2011-04-28 18:40:04 -0700206 */
207 abstract void updateTextureLayer(HardwareLayer layer, int width, int height,
Romain Guy8f0095c2011-05-02 17:24:22 -0700208 SurfaceTexture surface);
Romain Guy6c319ca2011-01-11 14:29:25 -0800209
210 /**
Romain Guy2d614592010-06-09 18:21:37 -0700211 * Initializes the hardware renderer for the specified surface and setup the
Joe Onoratoc6cc0f82011-04-12 11:53:13 -0700212 * renderer for drawing, if needed. This is invoked when the ViewAncestor has
Romain Guy2d614592010-06-09 18:21:37 -0700213 * potentially lost the hardware renderer. The hardware renderer should be
214 * reinitialized and setup when the render {@link #isRequested()} and
215 * {@link #isEnabled()}.
216 *
217 * @param width The width of the drawing surface.
218 * @param height The height of the drawing surface.
219 * @param attachInfo The
220 * @param holder
221 */
222 void initializeIfNeeded(int width, int height, View.AttachInfo attachInfo,
Dianne Hackborn64825172011-03-02 21:32:58 -0800223 SurfaceHolder holder) throws Surface.OutOfResourcesException {
Romain Guy2d614592010-06-09 18:21:37 -0700224 if (isRequested()) {
225 // We lost the gl context, so recreate it.
226 if (!isEnabled()) {
227 if (initialize(holder)) {
Romain Guyfb8b7632010-08-23 21:05:08 -0700228 setup(width, height);
Romain Guy2d614592010-06-09 18:21:37 -0700229 }
230 }
231 }
232 }
233
234 /**
235 * Creates a hardware renderer using OpenGL.
236 *
237 * @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 -0700238 * @param translucent True if the surface is translucent, false otherwise
Romain Guy2d614592010-06-09 18:21:37 -0700239 *
240 * @return A hardware renderer backed by OpenGL.
241 */
Romain Guye4d01122010-06-16 18:44:05 -0700242 static HardwareRenderer createGlRenderer(int glVersion, boolean translucent) {
Romain Guy2d614592010-06-09 18:21:37 -0700243 switch (glVersion) {
Romain Guye3924992010-06-10 18:51:21 -0700244 case 2:
Romain Guy16393512010-08-08 00:14:31 -0700245 return Gl20Renderer.create(translucent);
Romain Guy2d614592010-06-09 18:21:37 -0700246 }
247 throw new IllegalArgumentException("Unknown GL version: " + glVersion);
248 }
249
250 /**
251 * Indicates whether hardware acceleration is currently enabled.
252 *
253 * @return True if hardware acceleration is in use, false otherwise.
254 */
255 boolean isEnabled() {
256 return mEnabled;
257 }
258
259 /**
260 * Indicates whether hardware acceleration is currently enabled.
261 *
262 * @param enabled True if the hardware renderer is in use, false otherwise.
263 */
264 void setEnabled(boolean enabled) {
265 mEnabled = enabled;
266 }
267
268 /**
269 * Indicates whether hardware acceleration is currently request but not
270 * necessarily enabled yet.
271 *
272 * @return True if requested, false otherwise.
273 */
274 boolean isRequested() {
275 return mRequested;
276 }
277
278 /**
Romain Guy9745fae2010-12-08 11:39:15 -0800279 * Indicates whether hardware acceleration is currently requested but not
Romain Guy2d614592010-06-09 18:21:37 -0700280 * necessarily enabled yet.
281 *
282 * @return True to request hardware acceleration, false otherwise.
283 */
284 void setRequested(boolean requested) {
285 mRequested = requested;
286 }
287
Romain Guy2d614592010-06-09 18:21:37 -0700288 @SuppressWarnings({"deprecation"})
Romain Guye3924992010-06-10 18:51:21 -0700289 static abstract class GlRenderer extends HardwareRenderer {
Romain Guyaaceeb02011-03-23 19:56:13 -0700290 // These values are not exposed in our EGL APIs
Romain Guye91a9c72011-05-02 14:53:30 -0700291 static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
292 static final int EGL_SURFACE_TYPE = 0x3033;
293 static final int EGL_SWAP_BEHAVIOR_PRESERVED_BIT = 0x0400;
294 static final int EGL_OPENGL_ES2_BIT = 4;
Romain Guy2d614592010-06-09 18:21:37 -0700295
Romain Guyd88f54c2011-01-24 20:22:49 -0800296 private static final int SURFACE_STATE_ERROR = 0;
297 private static final int SURFACE_STATE_SUCCESS = 1;
298 private static final int SURFACE_STATE_UPDATED = 2;
299
Romain Guyfb8b7632010-08-23 21:05:08 -0700300 static EGLContext sEglContext;
301 static EGL10 sEgl;
302 static EGLDisplay sEglDisplay;
303 static EGLConfig sEglConfig;
Romain Guy2d614592010-06-09 18:21:37 -0700304
Romain Guycabfcc12011-03-07 18:06:46 -0800305 private static Thread sEglThread;
Romain Guyfb8b7632010-08-23 21:05:08 -0700306
307 EGLSurface mEglSurface;
308
Romain Guye3924992010-06-10 18:51:21 -0700309 GL mGl;
Romain Guy67f27952010-12-07 20:09:23 -0800310 HardwareCanvas mCanvas;
Romain Guy7d7b5492011-01-24 16:33:45 -0800311 int mFrameCount;
312 Paint mDebugPaint;
313
Romain Guy069ea0e2011-02-08 12:24:52 -0800314 boolean mDirtyRegions;
Romain Guye3924992010-06-10 18:51:21 -0700315
Romain Guye4d01122010-06-16 18:44:05 -0700316 final int mGlVersion;
317 final boolean mTranslucent;
Romain Guye3924992010-06-10 18:51:21 -0700318
Romain Guyfb8b7632010-08-23 21:05:08 -0700319 private boolean mDestroyed;
Romain Guycabfcc12011-03-07 18:06:46 -0800320
321 private final Rect mRedrawClip = new Rect();
Romain Guyfb8b7632010-08-23 21:05:08 -0700322
Romain Guye4d01122010-06-16 18:44:05 -0700323 GlRenderer(int glVersion, boolean translucent) {
Romain Guye3924992010-06-10 18:51:21 -0700324 mGlVersion = glVersion;
Romain Guye4d01122010-06-16 18:44:05 -0700325 mTranslucent = translucent;
Romain Guyaaceeb02011-03-23 19:56:13 -0700326 final String dirtyProperty = SystemProperties.get(RENDER_DIRTY_REGIONS_PROPERTY, "true");
Romain Guy069ea0e2011-02-08 12:24:52 -0800327 //noinspection PointlessBooleanExpression,ConstantConditions
328 mDirtyRegions = RENDER_DIRTY_REGIONS && "true".equalsIgnoreCase(dirtyProperty);
Romain Guy2d614592010-06-09 18:21:37 -0700329 }
330
Romain Guye3924992010-06-10 18:51:21 -0700331 /**
Mike Dodd5d3e2ea2010-10-07 18:02:47 -0700332 * Return a string for the EGL error code, or the hex representation
Romain Guyd10cd572010-10-10 13:33:22 -0700333 * if the error is unknown.
334 *
335 * @param error The EGL error to convert into a String.
336 *
337 * @return An error string correponding to the EGL error code.
Mike Dodd5d3e2ea2010-10-07 18:02:47 -0700338 */
339 static String getEGLErrorString(int error) {
340 switch (error) {
341 case EGL10.EGL_SUCCESS:
342 return "EGL_SUCCESS";
343 case EGL10.EGL_NOT_INITIALIZED:
344 return "EGL_NOT_INITIALIZED";
345 case EGL10.EGL_BAD_ACCESS:
346 return "EGL_BAD_ACCESS";
347 case EGL10.EGL_BAD_ALLOC:
348 return "EGL_BAD_ALLOC";
349 case EGL10.EGL_BAD_ATTRIBUTE:
350 return "EGL_BAD_ATTRIBUTE";
351 case EGL10.EGL_BAD_CONFIG:
352 return "EGL_BAD_CONFIG";
353 case EGL10.EGL_BAD_CONTEXT:
354 return "EGL_BAD_CONTEXT";
355 case EGL10.EGL_BAD_CURRENT_SURFACE:
356 return "EGL_BAD_CURRENT_SURFACE";
357 case EGL10.EGL_BAD_DISPLAY:
358 return "EGL_BAD_DISPLAY";
359 case EGL10.EGL_BAD_MATCH:
360 return "EGL_BAD_MATCH";
361 case EGL10.EGL_BAD_NATIVE_PIXMAP:
362 return "EGL_BAD_NATIVE_PIXMAP";
363 case EGL10.EGL_BAD_NATIVE_WINDOW:
364 return "EGL_BAD_NATIVE_WINDOW";
365 case EGL10.EGL_BAD_PARAMETER:
366 return "EGL_BAD_PARAMETER";
367 case EGL10.EGL_BAD_SURFACE:
368 return "EGL_BAD_SURFACE";
369 case EGL11.EGL_CONTEXT_LOST:
370 return "EGL_CONTEXT_LOST";
371 default:
372 return "0x" + Integer.toHexString(error);
373 }
374 }
375
376 /**
Romain Guy4caa4ed2010-08-25 14:46:24 -0700377 * Checks for OpenGL errors. If an error has occured, {@link #destroy(boolean)}
Romain Guye3924992010-06-10 18:51:21 -0700378 * is invoked and the requested flag is turned off. The error code is
379 * also logged as a warning.
380 */
Romain Guyb025b9c2010-09-16 14:16:48 -0700381 void checkEglErrors() {
Romain Guy2d614592010-06-09 18:21:37 -0700382 if (isEnabled()) {
Romain Guyfb8b7632010-08-23 21:05:08 -0700383 int error = sEgl.eglGetError();
Romain Guye3924992010-06-10 18:51:21 -0700384 if (error != EGL10.EGL_SUCCESS) {
Romain Guy2d614592010-06-09 18:21:37 -0700385 // something bad has happened revert to
386 // normal rendering.
Romain Guy9745fae2010-12-08 11:39:15 -0800387 fallback(error != EGL11.EGL_CONTEXT_LOST);
Mike Dodd5d3e2ea2010-10-07 18:02:47 -0700388 Log.w(LOG_TAG, "EGL error: " + getEGLErrorString(error));
Romain Guy2d614592010-06-09 18:21:37 -0700389 }
390 }
391 }
Romain Guy67f27952010-12-07 20:09:23 -0800392
Romain Guy9745fae2010-12-08 11:39:15 -0800393 private void fallback(boolean fallback) {
394 destroy(true);
395 if (fallback) {
396 // we'll try again if it was context lost
397 setRequested(false);
398 Log.w(LOG_TAG, "Mountain View, we've had a problem here. "
399 + "Switching back to software rendering.");
400 }
401 }
402
Romain Guy2d614592010-06-09 18:21:37 -0700403 @Override
Dianne Hackborn64825172011-03-02 21:32:58 -0800404 boolean initialize(SurfaceHolder holder) throws Surface.OutOfResourcesException {
Romain Guy2d614592010-06-09 18:21:37 -0700405 if (isRequested() && !isEnabled()) {
Romain Guye3924992010-06-10 18:51:21 -0700406 initializeEgl();
407 mGl = createEglSurface(holder);
Romain Guyfb8b7632010-08-23 21:05:08 -0700408 mDestroyed = false;
Romain Guye3924992010-06-10 18:51:21 -0700409
410 if (mGl != null) {
Romain Guyfb8b7632010-08-23 21:05:08 -0700411 int err = sEgl.eglGetError();
Romain Guye3924992010-06-10 18:51:21 -0700412 if (err != EGL10.EGL_SUCCESS) {
Romain Guy4caa4ed2010-08-25 14:46:24 -0700413 destroy(true);
Romain Guye3924992010-06-10 18:51:21 -0700414 setRequested(false);
415 } else {
Romain Guy4caa4ed2010-08-25 14:46:24 -0700416 if (mCanvas == null) {
417 mCanvas = createCanvas();
Romain Guyfb8b7632010-08-23 21:05:08 -0700418 }
Romain Guye3924992010-06-10 18:51:21 -0700419 if (mCanvas != null) {
420 setEnabled(true);
421 } else {
422 Log.w(LOG_TAG, "Hardware accelerated Canvas could not be created");
423 }
424 }
425
426 return mCanvas != null;
427 }
Romain Guy2d614592010-06-09 18:21:37 -0700428 }
429 return false;
430 }
Romain Guy2a83f002011-01-18 18:28:21 -0800431
432 @Override
Dianne Hackborn64825172011-03-02 21:32:58 -0800433 void updateSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException {
Romain Guy2a83f002011-01-18 18:28:21 -0800434 if (isRequested() && isEnabled()) {
435 createEglSurface(holder);
436 }
437 }
Romain Guy2d614592010-06-09 18:21:37 -0700438
Romain Guyfb8b7632010-08-23 21:05:08 -0700439 abstract GLES20Canvas createCanvas();
Romain Guye3924992010-06-10 18:51:21 -0700440
441 void initializeEgl() {
Romain Guyfb8b7632010-08-23 21:05:08 -0700442 if (sEglContext != null) return;
443
444 sEglThread = Thread.currentThread();
445 sEgl = (EGL10) EGLContext.getEGL();
Romain Guye3924992010-06-10 18:51:21 -0700446
447 // Get to the default display.
Romain Guyfb8b7632010-08-23 21:05:08 -0700448 sEglDisplay = sEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
Romain Guye3924992010-06-10 18:51:21 -0700449
Romain Guyfb8b7632010-08-23 21:05:08 -0700450 if (sEglDisplay == EGL10.EGL_NO_DISPLAY) {
Mike Dodd5d3e2ea2010-10-07 18:02:47 -0700451 throw new RuntimeException("eglGetDisplay failed "
452 + getEGLErrorString(sEgl.eglGetError()));
Romain Guye3924992010-06-10 18:51:21 -0700453 }
454
455 // We can now initialize EGL for that display
456 int[] version = new int[2];
Romain Guyfb8b7632010-08-23 21:05:08 -0700457 if (!sEgl.eglInitialize(sEglDisplay, version)) {
Romain Guy069ea0e2011-02-08 12:24:52 -0800458 throw new RuntimeException("eglInitialize failed " +
459 getEGLErrorString(sEgl.eglGetError()));
Romain Guye3924992010-06-10 18:51:21 -0700460 }
Romain Guy069ea0e2011-02-08 12:24:52 -0800461
Romain Guye91a9c72011-05-02 14:53:30 -0700462 sEglConfig = chooseEglConfig();
Romain Guy069ea0e2011-02-08 12:24:52 -0800463 if (sEglConfig == null) {
464 // We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without
465 if (mDirtyRegions) {
466 mDirtyRegions = false;
Romain Guye91a9c72011-05-02 14:53:30 -0700467 sEglConfig = chooseEglConfig();
Romain Guy069ea0e2011-02-08 12:24:52 -0800468 if (sEglConfig == null) {
469 throw new RuntimeException("eglConfig not initialized");
470 }
471 } else {
472 throw new RuntimeException("eglConfig not initialized");
473 }
474 }
Romain Guye3924992010-06-10 18:51:21 -0700475
476 /*
477 * Create an EGL context. We want to do this as rarely as we can, because an
478 * EGL context is a somewhat heavy object.
479 */
Romain Guyfb8b7632010-08-23 21:05:08 -0700480 sEglContext = createContext(sEgl, sEglDisplay, sEglConfig);
Romain Guy2d614592010-06-09 18:21:37 -0700481 }
482
Romain Guye91a9c72011-05-02 14:53:30 -0700483 private EGLConfig chooseEglConfig() {
484 int[] configsCount = new int[1];
485 EGLConfig[] configs = new EGLConfig[1];
486 int[] configSpec = getConfig(mDirtyRegions);
487 if (!sEgl.eglChooseConfig(sEglDisplay, configSpec, configs, 1, configsCount)) {
488 throw new IllegalArgumentException("eglChooseConfig failed " +
489 getEGLErrorString(sEgl.eglGetError()));
490 } else if (configsCount[0] > 0) {
491 return configs[0];
492 }
493 return null;
494 }
495
496 abstract int[] getConfig(boolean dirtyRegions);
497
Dianne Hackborn64825172011-03-02 21:32:58 -0800498 GL createEglSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException {
Romain Guye3924992010-06-10 18:51:21 -0700499 // Check preconditions.
Romain Guyfb8b7632010-08-23 21:05:08 -0700500 if (sEgl == null) {
Romain Guye3924992010-06-10 18:51:21 -0700501 throw new RuntimeException("egl not initialized");
Romain Guy2d614592010-06-09 18:21:37 -0700502 }
Romain Guyfb8b7632010-08-23 21:05:08 -0700503 if (sEglDisplay == null) {
Romain Guye3924992010-06-10 18:51:21 -0700504 throw new RuntimeException("eglDisplay not initialized");
505 }
Romain Guyfb8b7632010-08-23 21:05:08 -0700506 if (sEglConfig == null) {
Romain Guy069ea0e2011-02-08 12:24:52 -0800507 throw new RuntimeException("eglConfig not initialized");
Romain Guye3924992010-06-10 18:51:21 -0700508 }
Romain Guyfb8b7632010-08-23 21:05:08 -0700509 if (Thread.currentThread() != sEglThread) {
510 throw new IllegalStateException("HardwareRenderer cannot be used "
511 + "from multiple threads");
512 }
Romain Guye3924992010-06-10 18:51:21 -0700513
514 /*
515 * The window size has changed, so we need to create a new
516 * surface.
517 */
518 if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
Romain Guye3924992010-06-10 18:51:21 -0700519 /*
520 * Unbind and destroy the old EGL surface, if
521 * there is one.
522 */
Romain Guyfb8b7632010-08-23 21:05:08 -0700523 sEgl.eglMakeCurrent(sEglDisplay, EGL10.EGL_NO_SURFACE,
Romain Guye3924992010-06-10 18:51:21 -0700524 EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
Romain Guyfb8b7632010-08-23 21:05:08 -0700525 sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
Romain Guye3924992010-06-10 18:51:21 -0700526 }
527
528 // Create an EGL surface we can render into.
Romain Guyfb8b7632010-08-23 21:05:08 -0700529 mEglSurface = sEgl.eglCreateWindowSurface(sEglDisplay, sEglConfig, holder, null);
Romain Guye3924992010-06-10 18:51:21 -0700530
531 if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
Romain Guyfb8b7632010-08-23 21:05:08 -0700532 int error = sEgl.eglGetError();
Romain Guye3924992010-06-10 18:51:21 -0700533 if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
Romain Guy7d7b5492011-01-24 16:33:45 -0800534 Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
Romain Guye3924992010-06-10 18:51:21 -0700535 return null;
536 }
Mike Dodd5d3e2ea2010-10-07 18:02:47 -0700537 throw new RuntimeException("createWindowSurface failed "
538 + getEGLErrorString(error));
Romain Guye3924992010-06-10 18:51:21 -0700539 }
540
541 /*
542 * Before we can issue GL commands, we need to make sure
543 * the context is current and bound to a surface.
544 */
Romain Guyfb8b7632010-08-23 21:05:08 -0700545 if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, sEglContext)) {
Dianne Hackborn64825172011-03-02 21:32:58 -0800546 throw new Surface.OutOfResourcesException("eglMakeCurrent failed "
Mike Dodd5d3e2ea2010-10-07 18:02:47 -0700547 + getEGLErrorString(sEgl.eglGetError()));
Romain Guye3924992010-06-10 18:51:21 -0700548 }
Romain Guy7d7b5492011-01-24 16:33:45 -0800549
Romain Guy069ea0e2011-02-08 12:24:52 -0800550 if (mDirtyRegions) {
Romain Guy7d7b5492011-01-24 16:33:45 -0800551 if (!GLES20Canvas.preserveBackBuffer()) {
552 Log.w(LOG_TAG, "Backbuffer cannot be preserved");
553 }
554 }
Romain Guye3924992010-06-10 18:51:21 -0700555
Romain Guyfb8b7632010-08-23 21:05:08 -0700556 return sEglContext.getGL();
Romain Guye3924992010-06-10 18:51:21 -0700557 }
558
559 EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
560 int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, mGlVersion, EGL10.EGL_NONE };
561
562 return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT,
563 mGlVersion != 0 ? attrib_list : null);
Romain Guy2d614592010-06-09 18:21:37 -0700564 }
565
566 @Override
567 void initializeIfNeeded(int width, int height, View.AttachInfo attachInfo,
Dianne Hackborn64825172011-03-02 21:32:58 -0800568 SurfaceHolder holder) throws Surface.OutOfResourcesException {
Romain Guy2d614592010-06-09 18:21:37 -0700569 if (isRequested()) {
Romain Guyb025b9c2010-09-16 14:16:48 -0700570 checkEglErrors();
Romain Guy2d614592010-06-09 18:21:37 -0700571 super.initializeIfNeeded(width, height, attachInfo, holder);
572 }
573 }
Romain Guye3924992010-06-10 18:51:21 -0700574
575 @Override
Romain Guy4caa4ed2010-08-25 14:46:24 -0700576 void destroy(boolean full) {
577 if (full && mCanvas != null) {
Romain Guy4caa4ed2010-08-25 14:46:24 -0700578 mCanvas = null;
579 }
580
Romain Guyfb8b7632010-08-23 21:05:08 -0700581 if (!isEnabled() || mDestroyed) return;
Romain Guye3924992010-06-10 18:51:21 -0700582
Romain Guyfb8b7632010-08-23 21:05:08 -0700583 mDestroyed = true;
584
Romain Guyfb8b7632010-08-23 21:05:08 -0700585 sEgl.eglMakeCurrent(sEglDisplay, EGL10.EGL_NO_SURFACE,
Romain Guye3924992010-06-10 18:51:21 -0700586 EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
Romain Guyfb8b7632010-08-23 21:05:08 -0700587 sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
Romain Guye3924992010-06-10 18:51:21 -0700588
Romain Guye3924992010-06-10 18:51:21 -0700589 mEglSurface = null;
Romain Guye3924992010-06-10 18:51:21 -0700590 mGl = null;
Romain Guyfb8b7632010-08-23 21:05:08 -0700591
Romain Guye3924992010-06-10 18:51:21 -0700592 setEnabled(false);
Romain Guyfb8b7632010-08-23 21:05:08 -0700593 }
594
Romain Guye3924992010-06-10 18:51:21 -0700595 @Override
Romain Guyfb8b7632010-08-23 21:05:08 -0700596 void setup(int width, int height) {
597 mCanvas.setViewport(width, height);
Romain Guye3924992010-06-10 18:51:21 -0700598 }
Romain Guy7d7b5492011-01-24 16:33:45 -0800599
Romain Guye3924992010-06-10 18:51:21 -0700600 boolean canDraw() {
601 return mGl != null && mCanvas != null;
602 }
603
Romain Guy7d7b5492011-01-24 16:33:45 -0800604 void onPreDraw(Rect dirty) {
Romain Guye3924992010-06-10 18:51:21 -0700605 }
606
Romain Guyb025b9c2010-09-16 14:16:48 -0700607 void onPostDraw() {
608 }
Romain Guye3924992010-06-10 18:51:21 -0700609
610 @Override
Romain Guy7d7b5492011-01-24 16:33:45 -0800611 void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
612 Rect dirty) {
Romain Guye3924992010-06-10 18:51:21 -0700613 if (canDraw()) {
Romain Guy069ea0e2011-02-08 12:24:52 -0800614 if (!mDirtyRegions) {
Romain Guy7d7b5492011-01-24 16:33:45 -0800615 dirty = null;
616 }
617
Romain Guye3924992010-06-10 18:51:21 -0700618 attachInfo.mDrawingTime = SystemClock.uptimeMillis();
619 attachInfo.mIgnoreDirtyState = true;
620 view.mPrivateFlags |= View.DRAWN;
Romain Guy9a40bab2010-09-23 16:32:47 -0700621
622 long startTime;
623 if (ViewDebug.DEBUG_PROFILE_DRAWING) {
624 startTime = SystemClock.elapsedRealtime();
625 }
Romain Guye3924992010-06-10 18:51:21 -0700626
Romain Guyd88f54c2011-01-24 20:22:49 -0800627 final int surfaceState = checkCurrent();
628 if (surfaceState != SURFACE_STATE_ERROR) {
629 // We had to change the current surface and/or context, redraw everything
630 if (surfaceState == SURFACE_STATE_UPDATED) {
631 dirty = null;
632 }
633
Romain Guy7d7b5492011-01-24 16:33:45 -0800634 onPreDraw(dirty);
Romain Guy62687ec2011-02-02 15:44:19 -0800635
Chet Haasedaf98e92011-01-10 14:10:36 -0800636 HardwareCanvas canvas = mCanvas;
637 attachInfo.mHardwareCanvas = canvas;
Romain Guy7d7b5492011-01-24 16:33:45 -0800638
Romain Guyb8c0de22010-12-13 14:42:34 -0800639 int saveCount = canvas.save();
640 callbacks.onHardwarePreDraw(canvas);
Chet Haasedaf98e92011-01-10 14:10:36 -0800641
Romain Guyb8c0de22010-12-13 14:42:34 -0800642 try {
Chet Haasedaf98e92011-01-10 14:10:36 -0800643 view.mRecreateDisplayList =
644 (view.mPrivateFlags & View.INVALIDATED) == View.INVALIDATED;
645 view.mPrivateFlags &= ~View.INVALIDATED;
Romain Guy7d7b5492011-01-24 16:33:45 -0800646
Chet Haasedaf98e92011-01-10 14:10:36 -0800647 DisplayList displayList = view.getDisplayList();
648 if (displayList != null) {
Romain Guy7b5b6ab2011-03-14 18:05:08 -0700649 if (canvas.drawDisplayList(displayList, view.getWidth(),
650 view.getHeight(), mRedrawClip)) {
Romain Guye294d412011-03-08 17:35:10 -0800651 if (mRedrawClip.isEmpty() || view.getParent() == null) {
Romain Guycabfcc12011-03-07 18:06:46 -0800652 view.invalidate();
653 } else {
654 view.getParent().invalidateChild(view, mRedrawClip);
655 }
656 mRedrawClip.setEmpty();
Chet Haasedaf98e92011-01-10 14:10:36 -0800657 }
658 } else {
659 // Shouldn't reach here
660 view.draw(canvas);
661 }
Romain Guy7d7b5492011-01-24 16:33:45 -0800662
663 if (DEBUG_DIRTY_REGION) {
664 if (mDebugPaint == null) {
665 mDebugPaint = new Paint();
666 mDebugPaint.setColor(0x7fff0000);
667 }
668 if (dirty != null && (mFrameCount++ & 1) == 0) {
669 canvas.drawRect(dirty, mDebugPaint);
670 }
671 }
Romain Guyb8c0de22010-12-13 14:42:34 -0800672 } finally {
673 callbacks.onHardwarePostDraw(canvas);
674 canvas.restoreToCount(saveCount);
Chet Haasedaf98e92011-01-10 14:10:36 -0800675 view.mRecreateDisplayList = false;
Romain Guyb8c0de22010-12-13 14:42:34 -0800676 }
Chet Haasedaf98e92011-01-10 14:10:36 -0800677
Romain Guyb8c0de22010-12-13 14:42:34 -0800678 onPostDraw();
Chet Haasedaf98e92011-01-10 14:10:36 -0800679
Romain Guyb8c0de22010-12-13 14:42:34 -0800680 if (ViewDebug.DEBUG_PROFILE_DRAWING) {
681 EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime);
682 }
683
684 attachInfo.mIgnoreDirtyState = false;
Jeff Brown4e91a182011-04-07 11:38:09 -0700685
686 final long swapBuffersStartTime;
687 if (ViewDebug.DEBUG_LATENCY) {
688 swapBuffersStartTime = System.nanoTime();
689 }
690
Romain Guyb8c0de22010-12-13 14:42:34 -0800691 sEgl.eglSwapBuffers(sEglDisplay, mEglSurface);
Jeff Brown4e91a182011-04-07 11:38:09 -0700692
693 if (ViewDebug.DEBUG_LATENCY) {
694 long now = System.nanoTime();
695 Log.d(LOG_TAG, "Latency: Spent "
696 + ((now - swapBuffersStartTime) * 0.000001f)
697 + "ms waiting for eglSwapBuffers()");
698 }
699
Romain Guyb8c0de22010-12-13 14:42:34 -0800700 checkEglErrors();
Romain Guye3924992010-06-10 18:51:21 -0700701 }
Romain Guye3924992010-06-10 18:51:21 -0700702 }
703 }
Romain Guyd88f54c2011-01-24 20:22:49 -0800704
705 private int checkCurrent() {
Romain Guyfb8b7632010-08-23 21:05:08 -0700706 // TODO: Don't check the current context when we have one per UI thread
707 // TODO: Use a threadlocal flag to know whether the surface has changed
Romain Guycabfcc12011-03-07 18:06:46 -0800708 if (!sEglContext.equals(sEgl.eglGetCurrentContext()) ||
709 !mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL10.EGL_DRAW))) {
Romain Guyfb8b7632010-08-23 21:05:08 -0700710 if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, sEglContext)) {
Romain Guy9745fae2010-12-08 11:39:15 -0800711 fallback(true);
712 Log.e(LOG_TAG, "eglMakeCurrent failed " +
713 getEGLErrorString(sEgl.eglGetError()));
Romain Guyd88f54c2011-01-24 20:22:49 -0800714 return SURFACE_STATE_ERROR;
715 } else {
716 return SURFACE_STATE_UPDATED;
Romain Guyfb8b7632010-08-23 21:05:08 -0700717 }
718 }
Romain Guyd88f54c2011-01-24 20:22:49 -0800719 return SURFACE_STATE_SUCCESS;
Romain Guyfb8b7632010-08-23 21:05:08 -0700720 }
Romain Guye3924992010-06-10 18:51:21 -0700721 }
Romain Guyfb8b7632010-08-23 21:05:08 -0700722
Romain Guye3924992010-06-10 18:51:21 -0700723 /**
724 * Hardware renderer using OpenGL ES 2.0.
725 */
726 static class Gl20Renderer extends GlRenderer {
Romain Guye4d01122010-06-16 18:44:05 -0700727 private GLES20Canvas mGlCanvas;
728
729 Gl20Renderer(boolean translucent) {
730 super(2, translucent);
Romain Guye3924992010-06-10 18:51:21 -0700731 }
732
733 @Override
Romain Guyfb8b7632010-08-23 21:05:08 -0700734 GLES20Canvas createCanvas() {
Romain Guy6b7bd242010-10-06 19:49:23 -0700735 return mGlCanvas = new GLES20Canvas(mTranslucent);
Romain Guye4d01122010-06-16 18:44:05 -0700736 }
Romain Guye91a9c72011-05-02 14:53:30 -0700737
738 @Override
739 int[] getConfig(boolean dirtyRegions) {
740 return new int[] {
741 EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
742 EGL10.EGL_RED_SIZE, 8,
743 EGL10.EGL_GREEN_SIZE, 8,
744 EGL10.EGL_BLUE_SIZE, 8,
745 EGL10.EGL_ALPHA_SIZE, 8,
746 EGL10.EGL_DEPTH_SIZE, 0,
747 EGL10.EGL_STENCIL_SIZE, 0,
748 EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT |
749 (dirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0),
750 EGL10.EGL_NONE
751 };
752 }
753
Romain Guyb8c0de22010-12-13 14:42:34 -0800754 @Override
755 boolean canDraw() {
756 return super.canDraw() && mGlCanvas != null;
757 }
Romain Guye4d01122010-06-16 18:44:05 -0700758
759 @Override
Romain Guy7d7b5492011-01-24 16:33:45 -0800760 void onPreDraw(Rect dirty) {
761 mGlCanvas.onPreDraw(dirty);
Romain Guye3924992010-06-10 18:51:21 -0700762 }
763
Romain Guyb025b9c2010-09-16 14:16:48 -0700764 @Override
765 void onPostDraw() {
766 mGlCanvas.onPostDraw();
767 }
768
Romain Guyb051e892010-09-28 19:09:36 -0700769 @Override
Romain Guy67f27952010-12-07 20:09:23 -0800770 void destroy(boolean full) {
Romain Guyb8c0de22010-12-13 14:42:34 -0800771 try {
772 super.destroy(full);
773 } finally {
774 if (full && mGlCanvas != null) {
775 mGlCanvas = null;
776 }
Romain Guy67f27952010-12-07 20:09:23 -0800777 }
778 }
779
780 @Override
Chet Haasedaf98e92011-01-10 14:10:36 -0800781 DisplayList createDisplayList(View v) {
782 return new GLES20DisplayList(v);
Romain Guyb051e892010-09-28 19:09:36 -0700783 }
Romain Guyaa6c24c2011-04-28 18:40:04 -0700784
785 @Override
786 HardwareLayer createHardwareLayer() {
787 return new GLES20TextureLayer();
788 }
789
Romain Guy6c319ca2011-01-11 14:29:25 -0800790 @Override
791 HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque) {
Romain Guyaa6c24c2011-04-28 18:40:04 -0700792 return new GLES20RenderLayer(width, height, isOpaque);
793 }
794
795 @Override
796 SurfaceTexture createSuraceTexture(HardwareLayer layer) {
797 return ((GLES20TextureLayer) layer).getSurfaceTexture();
798 }
799
800 @Override
801 void updateTextureLayer(HardwareLayer layer, int width, int height,
Romain Guy8f0095c2011-05-02 17:24:22 -0700802 SurfaceTexture surface) {
803 ((GLES20TextureLayer) layer).update(width, height, surface.mSurfaceTexture);
Romain Guy6c319ca2011-01-11 14:29:25 -0800804 }
Romain Guyb051e892010-09-28 19:09:36 -0700805
Romain Guy16393512010-08-08 00:14:31 -0700806 static HardwareRenderer create(boolean translucent) {
807 if (GLES20Canvas.isAvailable()) {
808 return new Gl20Renderer(translucent);
Romain Guye3924992010-06-10 18:51:21 -0700809 }
Romain Guy16393512010-08-08 00:14:31 -0700810 return null;
Romain Guye3924992010-06-10 18:51:21 -0700811 }
812 }
Romain Guy2d614592010-06-09 18:21:37 -0700813}