blob: 8584bf22126f94457a4ba3f44d7d336e916b9f73 [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 Guy2d614592010-06-09 18:21:37 -070023import android.os.SystemClock;
Romain Guy9a40bab2010-09-23 16:32:47 -070024import android.util.EventLog;
Romain Guye3924992010-06-10 18:51:21 -070025import android.util.Log;
Romain Guy2d614592010-06-09 18:21:37 -070026
27import javax.microedition.khronos.egl.EGL10;
28import javax.microedition.khronos.egl.EGL11;
29import javax.microedition.khronos.egl.EGLConfig;
30import javax.microedition.khronos.egl.EGLContext;
31import javax.microedition.khronos.egl.EGLDisplay;
32import javax.microedition.khronos.egl.EGLSurface;
Romain Guye3924992010-06-10 18:51:21 -070033import javax.microedition.khronos.opengles.GL;
Romain Guy2d614592010-06-09 18:21:37 -070034
35/**
36 * Interface for rendering a ViewRoot using hardware acceleration.
37 *
38 * @hide
39 */
Romain Guy61c8c9c2010-08-09 20:48:09 -070040public abstract class HardwareRenderer {
Romain Guy4f6aff32011-01-12 16:21:41 -080041 static final String LOG_TAG = "HardwareRenderer";
Romain Guyfb8b7632010-08-23 21:05:08 -070042
Romain Guy52339202010-09-03 16:04:46 -070043 /**
Romain Guy7d7b5492011-01-24 16:33:45 -080044 * Turn on to only refresh the parts of the screen that need updating.
Romain Guy069ea0e2011-02-08 12:24:52 -080045 * When turned on the property defined by {@link #RENDER_DIRTY_REGIONS_PROPERTY}
46 * must also have the value "true".
Romain Guy7d7b5492011-01-24 16:33:45 -080047 */
48 public static final boolean RENDER_DIRTY_REGIONS = true;
49
50 /**
Romain Guy069ea0e2011-02-08 12:24:52 -080051 * System property used to enable or disable dirty regions invalidation.
52 * This property is only queried if {@link #RENDER_DIRTY_REGIONS} is true.
53 * The default value of this property is assumed to be true.
54 *
55 * Possible values:
56 * "true", to enable partial invalidates
57 * "false", to disable partial invalidates
58 */
59 static final String RENDER_DIRTY_REGIONS_PROPERTY = "hwui.render_dirty_regions";
60
61 /**
Romain Guy7d7b5492011-01-24 16:33:45 -080062 * Turn on to draw dirty regions every other frame.
63 */
64 private static final boolean DEBUG_DIRTY_REGION = false;
65
66 /**
Romain Guy52339202010-09-03 16:04:46 -070067 * A process can set this flag to false to prevent the use of hardware
68 * rendering.
69 *
70 * @hide
71 */
72 public static boolean sRendererDisabled = false;
73
Romain Guy2d614592010-06-09 18:21:37 -070074 private boolean mEnabled;
75 private boolean mRequested = true;
76
77 /**
Romain Guy67f27952010-12-07 20:09:23 -080078 * Invoke this method to disable hardware rendering in the current process.
Romain Guy52339202010-09-03 16:04:46 -070079 *
80 * @hide
81 */
82 public static void disable() {
83 sRendererDisabled = true;
84 }
85
86 /**
Romain Guy61c8c9c2010-08-09 20:48:09 -070087 * Indicates whether hardware acceleration is available under any form for
88 * the view hierarchy.
89 *
90 * @return True if the view hierarchy can potentially be hardware accelerated,
91 * false otherwise
92 */
93 public static boolean isAvailable() {
94 return GLES20Canvas.isAvailable();
95 }
96
97 /**
Romain Guy2d614592010-06-09 18:21:37 -070098 * Destroys the hardware rendering context.
Romain Guy4caa4ed2010-08-25 14:46:24 -070099 *
100 * @param full If true, destroys all associated resources.
Romain Guy2d614592010-06-09 18:21:37 -0700101 */
Romain Guy4caa4ed2010-08-25 14:46:24 -0700102 abstract void destroy(boolean full);
Romain Guy2d614592010-06-09 18:21:37 -0700103
104 /**
105 * Initializes the hardware renderer for the specified surface.
106 *
107 * @param holder The holder for the surface to hardware accelerate.
108 *
109 * @return True if the initialization was successful, false otherwise.
110 */
Dianne Hackborn64825172011-03-02 21:32:58 -0800111 abstract boolean initialize(SurfaceHolder holder) throws Surface.OutOfResourcesException;
Romain Guy2a83f002011-01-18 18:28:21 -0800112
113 /**
114 * Updates the hardware renderer for the specified surface.
115 *
116 * @param holder The holder for the surface to hardware accelerate.
117 */
Dianne Hackborn64825172011-03-02 21:32:58 -0800118 abstract void updateSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException;
Romain Guy2d614592010-06-09 18:21:37 -0700119
120 /**
121 * Setup the hardware renderer for drawing. This is called for every
122 * frame to draw.
123 *
124 * @param width Width of the drawing surface.
125 * @param height Height of the drawing surface.
Romain Guy2d614592010-06-09 18:21:37 -0700126 */
Romain Guyfb8b7632010-08-23 21:05:08 -0700127 abstract void setup(int width, int height);
Romain Guy2d614592010-06-09 18:21:37 -0700128
Romain Guy069ea0e2011-02-08 12:24:52 -0800129 /**
130 * Interface used to receive callbacks whenever a view is drawn by
131 * a hardware renderer instance.
132 */
Dianne Hackborn0f761d62010-11-30 22:06:10 -0800133 interface HardwareDrawCallbacks {
Romain Guy069ea0e2011-02-08 12:24:52 -0800134 /**
135 * Invoked before a view is drawn by a hardware renderer.
136 *
137 * @param canvas The Canvas used to render the view.
138 */
Dianne Hackborn0f761d62010-11-30 22:06:10 -0800139 void onHardwarePreDraw(Canvas canvas);
Romain Guy069ea0e2011-02-08 12:24:52 -0800140
141 /**
142 * Invoked after a view is drawn by a hardware renderer.
143 *
144 * @param canvas The Canvas used to render the view.
145 */
Dianne Hackborn0f761d62010-11-30 22:06:10 -0800146 void onHardwarePostDraw(Canvas canvas);
147 }
148
Romain Guy2d614592010-06-09 18:21:37 -0700149 /**
150 * Draws the specified view.
Romain Guy7d7b5492011-01-24 16:33:45 -0800151 *
Romain Guy2d614592010-06-09 18:21:37 -0700152 * @param view The view to draw.
153 * @param attachInfo AttachInfo tied to the specified view.
Romain Guy7d7b5492011-01-24 16:33:45 -0800154 * @param callbacks Callbacks invoked when drawing happens.
155 * @param dirty The dirty rectangle to update, can be null.
Romain Guy2d614592010-06-09 18:21:37 -0700156 */
Romain Guy7d7b5492011-01-24 16:33:45 -0800157 abstract void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
158 Rect dirty);
Romain Guy2d614592010-06-09 18:21:37 -0700159
160 /**
Romain Guy53ca03d2010-10-08 18:55:27 -0700161 * Creates a new display list that can be used to record batches of
162 * drawing operations.
Romain Guyb051e892010-09-28 19:09:36 -0700163 *
Romain Guy53ca03d2010-10-08 18:55:27 -0700164 * @return A new display list.
Romain Guyb051e892010-09-28 19:09:36 -0700165 */
Chet Haasedaf98e92011-01-10 14:10:36 -0800166 abstract DisplayList createDisplayList(View v);
Romain Guyb051e892010-09-28 19:09:36 -0700167
168 /**
Romain Guy6c319ca2011-01-11 14:29:25 -0800169 * Creates a new hardware layer.
170 *
171 * @param width The minimum width of the layer
172 * @param height The minimum height of the layer
173 * @param isOpaque Whether the layer should be opaque or not
174 *
175 * @return A hardware layer
176 */
177 abstract HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque);
178
179 /**
Romain Guy2d614592010-06-09 18:21:37 -0700180 * Initializes the hardware renderer for the specified surface and setup the
181 * renderer for drawing, if needed. This is invoked when the ViewRoot has
182 * potentially lost the hardware renderer. The hardware renderer should be
183 * reinitialized and setup when the render {@link #isRequested()} and
184 * {@link #isEnabled()}.
185 *
186 * @param width The width of the drawing surface.
187 * @param height The height of the drawing surface.
188 * @param attachInfo The
189 * @param holder
190 */
191 void initializeIfNeeded(int width, int height, View.AttachInfo attachInfo,
Dianne Hackborn64825172011-03-02 21:32:58 -0800192 SurfaceHolder holder) throws Surface.OutOfResourcesException {
Romain Guy2d614592010-06-09 18:21:37 -0700193 if (isRequested()) {
194 // We lost the gl context, so recreate it.
195 if (!isEnabled()) {
196 if (initialize(holder)) {
Romain Guyfb8b7632010-08-23 21:05:08 -0700197 setup(width, height);
Romain Guy2d614592010-06-09 18:21:37 -0700198 }
199 }
200 }
201 }
202
203 /**
204 * Creates a hardware renderer using OpenGL.
205 *
206 * @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 -0700207 * @param translucent True if the surface is translucent, false otherwise
Romain Guy2d614592010-06-09 18:21:37 -0700208 *
209 * @return A hardware renderer backed by OpenGL.
210 */
Romain Guye4d01122010-06-16 18:44:05 -0700211 static HardwareRenderer createGlRenderer(int glVersion, boolean translucent) {
Romain Guy2d614592010-06-09 18:21:37 -0700212 switch (glVersion) {
Romain Guye3924992010-06-10 18:51:21 -0700213 case 2:
Romain Guy16393512010-08-08 00:14:31 -0700214 return Gl20Renderer.create(translucent);
Romain Guy2d614592010-06-09 18:21:37 -0700215 }
216 throw new IllegalArgumentException("Unknown GL version: " + glVersion);
217 }
218
219 /**
220 * Indicates whether hardware acceleration is currently enabled.
221 *
222 * @return True if hardware acceleration is in use, false otherwise.
223 */
224 boolean isEnabled() {
225 return mEnabled;
226 }
227
228 /**
229 * Indicates whether hardware acceleration is currently enabled.
230 *
231 * @param enabled True if the hardware renderer is in use, false otherwise.
232 */
233 void setEnabled(boolean enabled) {
234 mEnabled = enabled;
235 }
236
237 /**
238 * Indicates whether hardware acceleration is currently request but not
239 * necessarily enabled yet.
240 *
241 * @return True if requested, false otherwise.
242 */
243 boolean isRequested() {
244 return mRequested;
245 }
246
247 /**
Romain Guy9745fae2010-12-08 11:39:15 -0800248 * Indicates whether hardware acceleration is currently requested but not
Romain Guy2d614592010-06-09 18:21:37 -0700249 * necessarily enabled yet.
250 *
251 * @return True to request hardware acceleration, false otherwise.
252 */
253 void setRequested(boolean requested) {
254 mRequested = requested;
255 }
256
Romain Guy2d614592010-06-09 18:21:37 -0700257 @SuppressWarnings({"deprecation"})
Romain Guye3924992010-06-10 18:51:21 -0700258 static abstract class GlRenderer extends HardwareRenderer {
259 private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
Romain Guy7d7b5492011-01-24 16:33:45 -0800260 private static final int EGL_SURFACE_TYPE = 0x3033;
261 private static final int EGL_SWAP_BEHAVIOR_PRESERVED_BIT = 0x0400;
Romain Guy2d614592010-06-09 18:21:37 -0700262
Romain Guyd88f54c2011-01-24 20:22:49 -0800263 private static final int SURFACE_STATE_ERROR = 0;
264 private static final int SURFACE_STATE_SUCCESS = 1;
265 private static final int SURFACE_STATE_UPDATED = 2;
266
Romain Guyfb8b7632010-08-23 21:05:08 -0700267 static EGLContext sEglContext;
268 static EGL10 sEgl;
269 static EGLDisplay sEglDisplay;
270 static EGLConfig sEglConfig;
Romain Guy2d614592010-06-09 18:21:37 -0700271
Romain Guycabfcc12011-03-07 18:06:46 -0800272 private static Thread sEglThread;
Romain Guyfb8b7632010-08-23 21:05:08 -0700273
274 EGLSurface mEglSurface;
275
Romain Guye3924992010-06-10 18:51:21 -0700276 GL mGl;
Romain Guy67f27952010-12-07 20:09:23 -0800277 HardwareCanvas mCanvas;
Romain Guy7d7b5492011-01-24 16:33:45 -0800278 int mFrameCount;
279 Paint mDebugPaint;
280
Romain Guy069ea0e2011-02-08 12:24:52 -0800281 boolean mDirtyRegions;
Romain Guye3924992010-06-10 18:51:21 -0700282
Romain Guye4d01122010-06-16 18:44:05 -0700283 final int mGlVersion;
284 final boolean mTranslucent;
Romain Guye3924992010-06-10 18:51:21 -0700285
Romain Guyfb8b7632010-08-23 21:05:08 -0700286 private boolean mDestroyed;
Romain Guycabfcc12011-03-07 18:06:46 -0800287
288 private final Rect mRedrawClip = new Rect();
Romain Guyfb8b7632010-08-23 21:05:08 -0700289
Romain Guye4d01122010-06-16 18:44:05 -0700290 GlRenderer(int glVersion, boolean translucent) {
Romain Guye3924992010-06-10 18:51:21 -0700291 mGlVersion = glVersion;
Romain Guye4d01122010-06-16 18:44:05 -0700292 mTranslucent = translucent;
Romain Guy069ea0e2011-02-08 12:24:52 -0800293 final String dirtyProperty = System.getProperty(RENDER_DIRTY_REGIONS_PROPERTY, "true");
294 //noinspection PointlessBooleanExpression,ConstantConditions
295 mDirtyRegions = RENDER_DIRTY_REGIONS && "true".equalsIgnoreCase(dirtyProperty);
Romain Guy2d614592010-06-09 18:21:37 -0700296 }
297
Romain Guye3924992010-06-10 18:51:21 -0700298 /**
Mike Dodd5d3e2ea2010-10-07 18:02:47 -0700299 * Return a string for the EGL error code, or the hex representation
Romain Guyd10cd572010-10-10 13:33:22 -0700300 * if the error is unknown.
301 *
302 * @param error The EGL error to convert into a String.
303 *
304 * @return An error string correponding to the EGL error code.
Mike Dodd5d3e2ea2010-10-07 18:02:47 -0700305 */
306 static String getEGLErrorString(int error) {
307 switch (error) {
308 case EGL10.EGL_SUCCESS:
309 return "EGL_SUCCESS";
310 case EGL10.EGL_NOT_INITIALIZED:
311 return "EGL_NOT_INITIALIZED";
312 case EGL10.EGL_BAD_ACCESS:
313 return "EGL_BAD_ACCESS";
314 case EGL10.EGL_BAD_ALLOC:
315 return "EGL_BAD_ALLOC";
316 case EGL10.EGL_BAD_ATTRIBUTE:
317 return "EGL_BAD_ATTRIBUTE";
318 case EGL10.EGL_BAD_CONFIG:
319 return "EGL_BAD_CONFIG";
320 case EGL10.EGL_BAD_CONTEXT:
321 return "EGL_BAD_CONTEXT";
322 case EGL10.EGL_BAD_CURRENT_SURFACE:
323 return "EGL_BAD_CURRENT_SURFACE";
324 case EGL10.EGL_BAD_DISPLAY:
325 return "EGL_BAD_DISPLAY";
326 case EGL10.EGL_BAD_MATCH:
327 return "EGL_BAD_MATCH";
328 case EGL10.EGL_BAD_NATIVE_PIXMAP:
329 return "EGL_BAD_NATIVE_PIXMAP";
330 case EGL10.EGL_BAD_NATIVE_WINDOW:
331 return "EGL_BAD_NATIVE_WINDOW";
332 case EGL10.EGL_BAD_PARAMETER:
333 return "EGL_BAD_PARAMETER";
334 case EGL10.EGL_BAD_SURFACE:
335 return "EGL_BAD_SURFACE";
336 case EGL11.EGL_CONTEXT_LOST:
337 return "EGL_CONTEXT_LOST";
338 default:
339 return "0x" + Integer.toHexString(error);
340 }
341 }
342
343 /**
Romain Guy4caa4ed2010-08-25 14:46:24 -0700344 * Checks for OpenGL errors. If an error has occured, {@link #destroy(boolean)}
Romain Guye3924992010-06-10 18:51:21 -0700345 * is invoked and the requested flag is turned off. The error code is
346 * also logged as a warning.
347 */
Romain Guyb025b9c2010-09-16 14:16:48 -0700348 void checkEglErrors() {
Romain Guy2d614592010-06-09 18:21:37 -0700349 if (isEnabled()) {
Romain Guyfb8b7632010-08-23 21:05:08 -0700350 int error = sEgl.eglGetError();
Romain Guye3924992010-06-10 18:51:21 -0700351 if (error != EGL10.EGL_SUCCESS) {
Romain Guy2d614592010-06-09 18:21:37 -0700352 // something bad has happened revert to
353 // normal rendering.
Romain Guy9745fae2010-12-08 11:39:15 -0800354 fallback(error != EGL11.EGL_CONTEXT_LOST);
Mike Dodd5d3e2ea2010-10-07 18:02:47 -0700355 Log.w(LOG_TAG, "EGL error: " + getEGLErrorString(error));
Romain Guy2d614592010-06-09 18:21:37 -0700356 }
357 }
358 }
Romain Guy67f27952010-12-07 20:09:23 -0800359
Romain Guy9745fae2010-12-08 11:39:15 -0800360 private void fallback(boolean fallback) {
361 destroy(true);
362 if (fallback) {
363 // we'll try again if it was context lost
364 setRequested(false);
365 Log.w(LOG_TAG, "Mountain View, we've had a problem here. "
366 + "Switching back to software rendering.");
367 }
368 }
369
Romain Guy2d614592010-06-09 18:21:37 -0700370 @Override
Dianne Hackborn64825172011-03-02 21:32:58 -0800371 boolean initialize(SurfaceHolder holder) throws Surface.OutOfResourcesException {
Romain Guy2d614592010-06-09 18:21:37 -0700372 if (isRequested() && !isEnabled()) {
Romain Guye3924992010-06-10 18:51:21 -0700373 initializeEgl();
374 mGl = createEglSurface(holder);
Romain Guyfb8b7632010-08-23 21:05:08 -0700375 mDestroyed = false;
Romain Guye3924992010-06-10 18:51:21 -0700376
377 if (mGl != null) {
Romain Guyfb8b7632010-08-23 21:05:08 -0700378 int err = sEgl.eglGetError();
Romain Guye3924992010-06-10 18:51:21 -0700379 if (err != EGL10.EGL_SUCCESS) {
Romain Guy4caa4ed2010-08-25 14:46:24 -0700380 destroy(true);
Romain Guye3924992010-06-10 18:51:21 -0700381 setRequested(false);
382 } else {
Romain Guy4caa4ed2010-08-25 14:46:24 -0700383 if (mCanvas == null) {
384 mCanvas = createCanvas();
Romain Guyfb8b7632010-08-23 21:05:08 -0700385 }
Romain Guye3924992010-06-10 18:51:21 -0700386 if (mCanvas != null) {
387 setEnabled(true);
388 } else {
389 Log.w(LOG_TAG, "Hardware accelerated Canvas could not be created");
390 }
391 }
392
393 return mCanvas != null;
394 }
Romain Guy2d614592010-06-09 18:21:37 -0700395 }
396 return false;
397 }
Romain Guy2a83f002011-01-18 18:28:21 -0800398
399 @Override
Dianne Hackborn64825172011-03-02 21:32:58 -0800400 void updateSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException {
Romain Guy2a83f002011-01-18 18:28:21 -0800401 if (isRequested() && isEnabled()) {
402 createEglSurface(holder);
403 }
404 }
Romain Guy2d614592010-06-09 18:21:37 -0700405
Romain Guyfb8b7632010-08-23 21:05:08 -0700406 abstract GLES20Canvas createCanvas();
Romain Guye3924992010-06-10 18:51:21 -0700407
408 void initializeEgl() {
Romain Guyfb8b7632010-08-23 21:05:08 -0700409 if (sEglContext != null) return;
410
411 sEglThread = Thread.currentThread();
412 sEgl = (EGL10) EGLContext.getEGL();
Romain Guye3924992010-06-10 18:51:21 -0700413
414 // Get to the default display.
Romain Guyfb8b7632010-08-23 21:05:08 -0700415 sEglDisplay = sEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
Romain Guye3924992010-06-10 18:51:21 -0700416
Romain Guyfb8b7632010-08-23 21:05:08 -0700417 if (sEglDisplay == EGL10.EGL_NO_DISPLAY) {
Mike Dodd5d3e2ea2010-10-07 18:02:47 -0700418 throw new RuntimeException("eglGetDisplay failed "
419 + getEGLErrorString(sEgl.eglGetError()));
Romain Guye3924992010-06-10 18:51:21 -0700420 }
421
422 // We can now initialize EGL for that display
423 int[] version = new int[2];
Romain Guyfb8b7632010-08-23 21:05:08 -0700424 if (!sEgl.eglInitialize(sEglDisplay, version)) {
Romain Guy069ea0e2011-02-08 12:24:52 -0800425 throw new RuntimeException("eglInitialize failed " +
426 getEGLErrorString(sEgl.eglGetError()));
Romain Guye3924992010-06-10 18:51:21 -0700427 }
Romain Guy069ea0e2011-02-08 12:24:52 -0800428
Romain Guyfb8b7632010-08-23 21:05:08 -0700429 sEglConfig = getConfigChooser(mGlVersion).chooseConfig(sEgl, sEglDisplay);
Romain Guy069ea0e2011-02-08 12:24:52 -0800430 if (sEglConfig == null) {
431 // We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without
432 if (mDirtyRegions) {
433 mDirtyRegions = false;
434
435 sEglConfig = getConfigChooser(mGlVersion).chooseConfig(sEgl, sEglDisplay);
436 if (sEglConfig == null) {
437 throw new RuntimeException("eglConfig not initialized");
438 }
439 } else {
440 throw new RuntimeException("eglConfig not initialized");
441 }
442 }
Romain Guye3924992010-06-10 18:51:21 -0700443
444 /*
445 * Create an EGL context. We want to do this as rarely as we can, because an
446 * EGL context is a somewhat heavy object.
447 */
Romain Guyfb8b7632010-08-23 21:05:08 -0700448 sEglContext = createContext(sEgl, sEglDisplay, sEglConfig);
Romain Guy2d614592010-06-09 18:21:37 -0700449 }
450
Dianne Hackborn64825172011-03-02 21:32:58 -0800451 GL createEglSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException {
Romain Guye3924992010-06-10 18:51:21 -0700452 // Check preconditions.
Romain Guyfb8b7632010-08-23 21:05:08 -0700453 if (sEgl == null) {
Romain Guye3924992010-06-10 18:51:21 -0700454 throw new RuntimeException("egl not initialized");
Romain Guy2d614592010-06-09 18:21:37 -0700455 }
Romain Guyfb8b7632010-08-23 21:05:08 -0700456 if (sEglDisplay == null) {
Romain Guye3924992010-06-10 18:51:21 -0700457 throw new RuntimeException("eglDisplay not initialized");
458 }
Romain Guyfb8b7632010-08-23 21:05:08 -0700459 if (sEglConfig == null) {
Romain Guy069ea0e2011-02-08 12:24:52 -0800460 throw new RuntimeException("eglConfig not initialized");
Romain Guye3924992010-06-10 18:51:21 -0700461 }
Romain Guyfb8b7632010-08-23 21:05:08 -0700462 if (Thread.currentThread() != sEglThread) {
463 throw new IllegalStateException("HardwareRenderer cannot be used "
464 + "from multiple threads");
465 }
Romain Guye3924992010-06-10 18:51:21 -0700466
467 /*
468 * The window size has changed, so we need to create a new
469 * surface.
470 */
471 if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
Romain Guye3924992010-06-10 18:51:21 -0700472 /*
473 * Unbind and destroy the old EGL surface, if
474 * there is one.
475 */
Romain Guyfb8b7632010-08-23 21:05:08 -0700476 sEgl.eglMakeCurrent(sEglDisplay, EGL10.EGL_NO_SURFACE,
Romain Guye3924992010-06-10 18:51:21 -0700477 EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
Romain Guyfb8b7632010-08-23 21:05:08 -0700478 sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
Romain Guye3924992010-06-10 18:51:21 -0700479 }
480
481 // Create an EGL surface we can render into.
Romain Guyfb8b7632010-08-23 21:05:08 -0700482 mEglSurface = sEgl.eglCreateWindowSurface(sEglDisplay, sEglConfig, holder, null);
Romain Guye3924992010-06-10 18:51:21 -0700483
484 if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
Romain Guyfb8b7632010-08-23 21:05:08 -0700485 int error = sEgl.eglGetError();
Romain Guye3924992010-06-10 18:51:21 -0700486 if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
Romain Guy7d7b5492011-01-24 16:33:45 -0800487 Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
Romain Guye3924992010-06-10 18:51:21 -0700488 return null;
489 }
Mike Dodd5d3e2ea2010-10-07 18:02:47 -0700490 throw new RuntimeException("createWindowSurface failed "
491 + getEGLErrorString(error));
Romain Guye3924992010-06-10 18:51:21 -0700492 }
493
494 /*
495 * Before we can issue GL commands, we need to make sure
496 * the context is current and bound to a surface.
497 */
Romain Guyfb8b7632010-08-23 21:05:08 -0700498 if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, sEglContext)) {
Dianne Hackborn64825172011-03-02 21:32:58 -0800499 throw new Surface.OutOfResourcesException("eglMakeCurrent failed "
Mike Dodd5d3e2ea2010-10-07 18:02:47 -0700500 + getEGLErrorString(sEgl.eglGetError()));
Romain Guye3924992010-06-10 18:51:21 -0700501 }
Romain Guy7d7b5492011-01-24 16:33:45 -0800502
Romain Guy069ea0e2011-02-08 12:24:52 -0800503 if (mDirtyRegions) {
Romain Guy7d7b5492011-01-24 16:33:45 -0800504 if (!GLES20Canvas.preserveBackBuffer()) {
505 Log.w(LOG_TAG, "Backbuffer cannot be preserved");
506 }
507 }
Romain Guye3924992010-06-10 18:51:21 -0700508
Romain Guyfb8b7632010-08-23 21:05:08 -0700509 return sEglContext.getGL();
Romain Guye3924992010-06-10 18:51:21 -0700510 }
511
512 EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
513 int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, mGlVersion, EGL10.EGL_NONE };
514
515 return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT,
516 mGlVersion != 0 ? attrib_list : null);
Romain Guy2d614592010-06-09 18:21:37 -0700517 }
518
519 @Override
520 void initializeIfNeeded(int width, int height, View.AttachInfo attachInfo,
Dianne Hackborn64825172011-03-02 21:32:58 -0800521 SurfaceHolder holder) throws Surface.OutOfResourcesException {
Romain Guy2d614592010-06-09 18:21:37 -0700522 if (isRequested()) {
Romain Guyb025b9c2010-09-16 14:16:48 -0700523 checkEglErrors();
Romain Guy2d614592010-06-09 18:21:37 -0700524 super.initializeIfNeeded(width, height, attachInfo, holder);
525 }
526 }
Romain Guye3924992010-06-10 18:51:21 -0700527
528 @Override
Romain Guy4caa4ed2010-08-25 14:46:24 -0700529 void destroy(boolean full) {
530 if (full && mCanvas != null) {
Romain Guy4caa4ed2010-08-25 14:46:24 -0700531 mCanvas = null;
532 }
533
Romain Guyfb8b7632010-08-23 21:05:08 -0700534 if (!isEnabled() || mDestroyed) return;
Romain Guye3924992010-06-10 18:51:21 -0700535
Romain Guyfb8b7632010-08-23 21:05:08 -0700536 mDestroyed = true;
537
Romain Guyfb8b7632010-08-23 21:05:08 -0700538 sEgl.eglMakeCurrent(sEglDisplay, EGL10.EGL_NO_SURFACE,
Romain Guye3924992010-06-10 18:51:21 -0700539 EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
Romain Guyfb8b7632010-08-23 21:05:08 -0700540 sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
Romain Guye3924992010-06-10 18:51:21 -0700541
Romain Guye3924992010-06-10 18:51:21 -0700542 mEglSurface = null;
Romain Guye3924992010-06-10 18:51:21 -0700543 mGl = null;
Romain Guyfb8b7632010-08-23 21:05:08 -0700544
Romain Guye3924992010-06-10 18:51:21 -0700545 setEnabled(false);
Romain Guyfb8b7632010-08-23 21:05:08 -0700546 }
547
Romain Guye3924992010-06-10 18:51:21 -0700548 @Override
Romain Guyfb8b7632010-08-23 21:05:08 -0700549 void setup(int width, int height) {
550 mCanvas.setViewport(width, height);
Romain Guye3924992010-06-10 18:51:21 -0700551 }
Romain Guy7d7b5492011-01-24 16:33:45 -0800552
Romain Guye3924992010-06-10 18:51:21 -0700553 boolean canDraw() {
554 return mGl != null && mCanvas != null;
555 }
556
Romain Guy7d7b5492011-01-24 16:33:45 -0800557 void onPreDraw(Rect dirty) {
Romain Guye3924992010-06-10 18:51:21 -0700558 }
559
Romain Guyb025b9c2010-09-16 14:16:48 -0700560 void onPostDraw() {
561 }
562
Romain Guye3924992010-06-10 18:51:21 -0700563 /**
Romain Guyd10cd572010-10-10 13:33:22 -0700564 * Defines the EGL configuration for this renderer.
Romain Guye3924992010-06-10 18:51:21 -0700565 *
566 * @return An {@link android.view.HardwareRenderer.GlRenderer.EglConfigChooser}.
Romain Guye3924992010-06-10 18:51:21 -0700567 */
568 EglConfigChooser getConfigChooser(int glVersion) {
Romain Guy069ea0e2011-02-08 12:24:52 -0800569 return new ComponentSizeChooser(glVersion, 8, 8, 8, 8, 0, 0, mDirtyRegions);
Romain Guye3924992010-06-10 18:51:21 -0700570 }
571
572 @Override
Romain Guy7d7b5492011-01-24 16:33:45 -0800573 void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
574 Rect dirty) {
Romain Guye3924992010-06-10 18:51:21 -0700575 if (canDraw()) {
Romain Guy069ea0e2011-02-08 12:24:52 -0800576 if (!mDirtyRegions) {
Romain Guy7d7b5492011-01-24 16:33:45 -0800577 dirty = null;
578 }
579
Romain Guye3924992010-06-10 18:51:21 -0700580 attachInfo.mDrawingTime = SystemClock.uptimeMillis();
581 attachInfo.mIgnoreDirtyState = true;
582 view.mPrivateFlags |= View.DRAWN;
Romain Guy9a40bab2010-09-23 16:32:47 -0700583
584 long startTime;
585 if (ViewDebug.DEBUG_PROFILE_DRAWING) {
586 startTime = SystemClock.elapsedRealtime();
587 }
Romain Guye3924992010-06-10 18:51:21 -0700588
Romain Guyd88f54c2011-01-24 20:22:49 -0800589 final int surfaceState = checkCurrent();
590 if (surfaceState != SURFACE_STATE_ERROR) {
591 // We had to change the current surface and/or context, redraw everything
592 if (surfaceState == SURFACE_STATE_UPDATED) {
593 dirty = null;
594 }
595
Romain Guy7d7b5492011-01-24 16:33:45 -0800596 onPreDraw(dirty);
Romain Guy62687ec2011-02-02 15:44:19 -0800597
Chet Haasedaf98e92011-01-10 14:10:36 -0800598 HardwareCanvas canvas = mCanvas;
599 attachInfo.mHardwareCanvas = canvas;
Romain Guy7d7b5492011-01-24 16:33:45 -0800600
Romain Guyb8c0de22010-12-13 14:42:34 -0800601 int saveCount = canvas.save();
602 callbacks.onHardwarePreDraw(canvas);
Chet Haasedaf98e92011-01-10 14:10:36 -0800603
Romain Guyb8c0de22010-12-13 14:42:34 -0800604 try {
Chet Haasedaf98e92011-01-10 14:10:36 -0800605 view.mRecreateDisplayList =
606 (view.mPrivateFlags & View.INVALIDATED) == View.INVALIDATED;
607 view.mPrivateFlags &= ~View.INVALIDATED;
Romain Guy7d7b5492011-01-24 16:33:45 -0800608
Chet Haasedaf98e92011-01-10 14:10:36 -0800609 DisplayList displayList = view.getDisplayList();
610 if (displayList != null) {
Romain Guy7b5b6ab2011-03-14 18:05:08 -0700611 if (canvas.drawDisplayList(displayList, view.getWidth(),
612 view.getHeight(), mRedrawClip)) {
Romain Guye294d412011-03-08 17:35:10 -0800613 if (mRedrawClip.isEmpty() || view.getParent() == null) {
Romain Guycabfcc12011-03-07 18:06:46 -0800614 view.invalidate();
615 } else {
616 view.getParent().invalidateChild(view, mRedrawClip);
617 }
618 mRedrawClip.setEmpty();
Chet Haasedaf98e92011-01-10 14:10:36 -0800619 }
620 } else {
621 // Shouldn't reach here
622 view.draw(canvas);
623 }
Romain Guy7d7b5492011-01-24 16:33:45 -0800624
625 if (DEBUG_DIRTY_REGION) {
626 if (mDebugPaint == null) {
627 mDebugPaint = new Paint();
628 mDebugPaint.setColor(0x7fff0000);
629 }
630 if (dirty != null && (mFrameCount++ & 1) == 0) {
631 canvas.drawRect(dirty, mDebugPaint);
632 }
633 }
Romain Guyb8c0de22010-12-13 14:42:34 -0800634 } finally {
635 callbacks.onHardwarePostDraw(canvas);
636 canvas.restoreToCount(saveCount);
Chet Haasedaf98e92011-01-10 14:10:36 -0800637 view.mRecreateDisplayList = false;
Romain Guyb8c0de22010-12-13 14:42:34 -0800638 }
Chet Haasedaf98e92011-01-10 14:10:36 -0800639
Romain Guyb8c0de22010-12-13 14:42:34 -0800640 onPostDraw();
Chet Haasedaf98e92011-01-10 14:10:36 -0800641
Romain Guyb8c0de22010-12-13 14:42:34 -0800642 if (ViewDebug.DEBUG_PROFILE_DRAWING) {
643 EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime);
644 }
645
646 attachInfo.mIgnoreDirtyState = false;
647
648 sEgl.eglSwapBuffers(sEglDisplay, mEglSurface);
649 checkEglErrors();
Romain Guye3924992010-06-10 18:51:21 -0700650 }
Romain Guye3924992010-06-10 18:51:21 -0700651 }
652 }
Romain Guyd88f54c2011-01-24 20:22:49 -0800653
654 private int checkCurrent() {
Romain Guyfb8b7632010-08-23 21:05:08 -0700655 // TODO: Don't check the current context when we have one per UI thread
656 // TODO: Use a threadlocal flag to know whether the surface has changed
Romain Guycabfcc12011-03-07 18:06:46 -0800657 if (!sEglContext.equals(sEgl.eglGetCurrentContext()) ||
658 !mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL10.EGL_DRAW))) {
Romain Guyfb8b7632010-08-23 21:05:08 -0700659 if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, sEglContext)) {
Romain Guy9745fae2010-12-08 11:39:15 -0800660 fallback(true);
661 Log.e(LOG_TAG, "eglMakeCurrent failed " +
662 getEGLErrorString(sEgl.eglGetError()));
Romain Guyd88f54c2011-01-24 20:22:49 -0800663 return SURFACE_STATE_ERROR;
664 } else {
665 return SURFACE_STATE_UPDATED;
Romain Guyfb8b7632010-08-23 21:05:08 -0700666 }
667 }
Romain Guyd88f54c2011-01-24 20:22:49 -0800668 return SURFACE_STATE_SUCCESS;
Romain Guyfb8b7632010-08-23 21:05:08 -0700669 }
670
Romain Guye3924992010-06-10 18:51:21 -0700671 static abstract class EglConfigChooser {
672 final int[] mConfigSpec;
673 private final int mGlVersion;
674
675 EglConfigChooser(int glVersion, int[] configSpec) {
676 mGlVersion = glVersion;
677 mConfigSpec = filterConfigSpec(configSpec);
678 }
679
680 EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
Romain Guyf86ef572010-07-01 11:05:42 -0700681 int[] index = new int[1];
682 if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, index)) {
Mike Dodd5d3e2ea2010-10-07 18:02:47 -0700683 throw new IllegalArgumentException("eglChooseConfig failed "
684 + getEGLErrorString(egl.eglGetError()));
Romain Guye3924992010-06-10 18:51:21 -0700685 }
686
Romain Guyf86ef572010-07-01 11:05:42 -0700687 int numConfigs = index[0];
Romain Guye3924992010-06-10 18:51:21 -0700688 if (numConfigs <= 0) {
Romain Guyf86ef572010-07-01 11:05:42 -0700689 throw new IllegalArgumentException("No configs match configSpec");
Romain Guye3924992010-06-10 18:51:21 -0700690 }
691
692 EGLConfig[] configs = new EGLConfig[numConfigs];
Romain Guyf86ef572010-07-01 11:05:42 -0700693 if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, index)) {
Mike Dodd5d3e2ea2010-10-07 18:02:47 -0700694 throw new IllegalArgumentException("eglChooseConfig failed "
695 + getEGLErrorString(egl.eglGetError()));
Romain Guye3924992010-06-10 18:51:21 -0700696 }
Romain Guyf86ef572010-07-01 11:05:42 -0700697
Romain Guye3924992010-06-10 18:51:21 -0700698 EGLConfig config = chooseConfig(egl, display, configs);
699 if (config == null) {
700 throw new IllegalArgumentException("No config chosen");
701 }
Romain Guyf86ef572010-07-01 11:05:42 -0700702
Romain Guye3924992010-06-10 18:51:21 -0700703 return config;
704 }
705
Romain Guyf86ef572010-07-01 11:05:42 -0700706 abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs);
Romain Guye3924992010-06-10 18:51:21 -0700707
708 private int[] filterConfigSpec(int[] configSpec) {
709 if (mGlVersion != 2) {
710 return configSpec;
711 }
712 /* We know none of the subclasses define EGL_RENDERABLE_TYPE.
713 * And we know the configSpec is well formed.
714 */
715 int len = configSpec.length;
716 int[] newConfigSpec = new int[len + 2];
Romain Guyf86ef572010-07-01 11:05:42 -0700717 System.arraycopy(configSpec, 0, newConfigSpec, 0, len - 1);
718 newConfigSpec[len - 1] = EGL10.EGL_RENDERABLE_TYPE;
Romain Guye3924992010-06-10 18:51:21 -0700719 newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */
Romain Guyf86ef572010-07-01 11:05:42 -0700720 newConfigSpec[len + 1] = EGL10.EGL_NONE;
Romain Guye3924992010-06-10 18:51:21 -0700721 return newConfigSpec;
722 }
723 }
724
725 /**
726 * Choose a configuration with exactly the specified r,g,b,a sizes,
727 * and at least the specified depth and stencil sizes.
728 */
729 static class ComponentSizeChooser extends EglConfigChooser {
730 private int[] mValue;
731
Romain Guy069ea0e2011-02-08 12:24:52 -0800732 private final int mRedSize;
733 private final int mGreenSize;
734 private final int mBlueSize;
735 private final int mAlphaSize;
736 private final int mDepthSize;
737 private final int mStencilSize;
738 private final boolean mDirtyRegions;
Romain Guye3924992010-06-10 18:51:21 -0700739
740 ComponentSizeChooser(int glVersion, int redSize, int greenSize, int blueSize,
Romain Guy069ea0e2011-02-08 12:24:52 -0800741 int alphaSize, int depthSize, int stencilSize, boolean dirtyRegions) {
Romain Guye3924992010-06-10 18:51:21 -0700742 super(glVersion, new int[] {
743 EGL10.EGL_RED_SIZE, redSize,
744 EGL10.EGL_GREEN_SIZE, greenSize,
745 EGL10.EGL_BLUE_SIZE, blueSize,
746 EGL10.EGL_ALPHA_SIZE, alphaSize,
747 EGL10.EGL_DEPTH_SIZE, depthSize,
748 EGL10.EGL_STENCIL_SIZE, stencilSize,
Romain Guy7d7b5492011-01-24 16:33:45 -0800749 EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT |
Romain Guy069ea0e2011-02-08 12:24:52 -0800750 (dirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0),
Romain Guye3924992010-06-10 18:51:21 -0700751 EGL10.EGL_NONE });
752 mValue = new int[1];
753 mRedSize = redSize;
754 mGreenSize = greenSize;
755 mBlueSize = blueSize;
756 mAlphaSize = alphaSize;
757 mDepthSize = depthSize;
758 mStencilSize = stencilSize;
Romain Guy069ea0e2011-02-08 12:24:52 -0800759 mDirtyRegions = dirtyRegions;
760 }
Romain Guye3924992010-06-10 18:51:21 -0700761
762 @Override
763 EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) {
764 for (EGLConfig config : configs) {
765 int d = findConfigAttrib(egl, display, config, EGL10.EGL_DEPTH_SIZE, 0);
766 int s = findConfigAttrib(egl, display, config, EGL10.EGL_STENCIL_SIZE, 0);
Romain Guyf86ef572010-07-01 11:05:42 -0700767 if (d >= mDepthSize && s >= mStencilSize) {
Romain Guye3924992010-06-10 18:51:21 -0700768 int r = findConfigAttrib(egl, display, config, EGL10.EGL_RED_SIZE, 0);
769 int g = findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0);
770 int b = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0);
771 int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0);
Romain Guy7d7b5492011-01-24 16:33:45 -0800772 boolean backBuffer;
Romain Guy069ea0e2011-02-08 12:24:52 -0800773 if (mDirtyRegions) {
Romain Guy7d7b5492011-01-24 16:33:45 -0800774 int surfaceType = findConfigAttrib(egl, display, config,
775 EGL_SURFACE_TYPE, 0);
776 backBuffer = (surfaceType & EGL_SWAP_BEHAVIOR_PRESERVED_BIT) != 0;
777 } else {
778 backBuffer = true;
779 }
780 if (r >= mRedSize && g >= mGreenSize && b >= mBlueSize && a >= mAlphaSize
781 && backBuffer) {
Romain Guye3924992010-06-10 18:51:21 -0700782 return config;
783 }
784 }
785 }
786 return null;
787 }
788
Romain Guyf86ef572010-07-01 11:05:42 -0700789 private int findConfigAttrib(EGL10 egl, EGLDisplay display, EGLConfig config,
790 int attribute, int defaultValue) {
Romain Guye3924992010-06-10 18:51:21 -0700791 if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
792 return mValue[0];
793 }
794
795 return defaultValue;
796 }
Romain Guyf86ef572010-07-01 11:05:42 -0700797 }
Romain Guye3924992010-06-10 18:51:21 -0700798 }
Romain Guyfb8b7632010-08-23 21:05:08 -0700799
Romain Guye3924992010-06-10 18:51:21 -0700800 /**
801 * Hardware renderer using OpenGL ES 2.0.
802 */
803 static class Gl20Renderer extends GlRenderer {
Romain Guye4d01122010-06-16 18:44:05 -0700804 private GLES20Canvas mGlCanvas;
805
806 Gl20Renderer(boolean translucent) {
807 super(2, translucent);
Romain Guye3924992010-06-10 18:51:21 -0700808 }
809
810 @Override
Romain Guyfb8b7632010-08-23 21:05:08 -0700811 GLES20Canvas createCanvas() {
Romain Guy6b7bd242010-10-06 19:49:23 -0700812 return mGlCanvas = new GLES20Canvas(mTranslucent);
Romain Guye4d01122010-06-16 18:44:05 -0700813 }
Romain Guyb8c0de22010-12-13 14:42:34 -0800814
815 @Override
816 boolean canDraw() {
817 return super.canDraw() && mGlCanvas != null;
818 }
Romain Guye4d01122010-06-16 18:44:05 -0700819
820 @Override
Romain Guy7d7b5492011-01-24 16:33:45 -0800821 void onPreDraw(Rect dirty) {
822 mGlCanvas.onPreDraw(dirty);
Romain Guye3924992010-06-10 18:51:21 -0700823 }
824
Romain Guyb025b9c2010-09-16 14:16:48 -0700825 @Override
826 void onPostDraw() {
827 mGlCanvas.onPostDraw();
828 }
829
Romain Guyb051e892010-09-28 19:09:36 -0700830 @Override
Romain Guy67f27952010-12-07 20:09:23 -0800831 void destroy(boolean full) {
Romain Guyb8c0de22010-12-13 14:42:34 -0800832 try {
833 super.destroy(full);
834 } finally {
835 if (full && mGlCanvas != null) {
836 mGlCanvas = null;
837 }
Romain Guy67f27952010-12-07 20:09:23 -0800838 }
839 }
840
841 @Override
Chet Haasedaf98e92011-01-10 14:10:36 -0800842 DisplayList createDisplayList(View v) {
843 return new GLES20DisplayList(v);
Romain Guyb051e892010-09-28 19:09:36 -0700844 }
Romain Guy6c319ca2011-01-11 14:29:25 -0800845
846 @Override
847 HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque) {
848 return new GLES20Layer(width, height, isOpaque);
849 }
Romain Guyb051e892010-09-28 19:09:36 -0700850
Romain Guy16393512010-08-08 00:14:31 -0700851 static HardwareRenderer create(boolean translucent) {
852 if (GLES20Canvas.isAvailable()) {
853 return new Gl20Renderer(translucent);
Romain Guye3924992010-06-10 18:51:21 -0700854 }
Romain Guy16393512010-08-08 00:14:31 -0700855 return null;
Romain Guye3924992010-06-10 18:51:21 -0700856 }
857 }
Romain Guy2d614592010-06-09 18:21:37 -0700858}