blob: 5002fe5ba6088e7a162f729d90ececde5a191e7a [file] [log] [blame]
John Reck30835792013-11-07 14:21:24 -08001/*
2 * Copyright (C) 2013 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
17package android.view;
18
19import static javax.microedition.khronos.egl.EGL10.EGL_ALPHA_SIZE;
20import static javax.microedition.khronos.egl.EGL10.EGL_BAD_NATIVE_WINDOW;
21import static javax.microedition.khronos.egl.EGL10.EGL_BLUE_SIZE;
22import static javax.microedition.khronos.egl.EGL10.EGL_CONFIG_CAVEAT;
23import static javax.microedition.khronos.egl.EGL10.EGL_DEFAULT_DISPLAY;
24import static javax.microedition.khronos.egl.EGL10.EGL_DEPTH_SIZE;
25import static javax.microedition.khronos.egl.EGL10.EGL_DRAW;
26import static javax.microedition.khronos.egl.EGL10.EGL_GREEN_SIZE;
27import static javax.microedition.khronos.egl.EGL10.EGL_HEIGHT;
28import static javax.microedition.khronos.egl.EGL10.EGL_NONE;
29import static javax.microedition.khronos.egl.EGL10.EGL_NO_CONTEXT;
30import static javax.microedition.khronos.egl.EGL10.EGL_NO_DISPLAY;
31import static javax.microedition.khronos.egl.EGL10.EGL_NO_SURFACE;
32import static javax.microedition.khronos.egl.EGL10.EGL_RED_SIZE;
33import static javax.microedition.khronos.egl.EGL10.EGL_RENDERABLE_TYPE;
34import static javax.microedition.khronos.egl.EGL10.EGL_SAMPLES;
35import static javax.microedition.khronos.egl.EGL10.EGL_SAMPLE_BUFFERS;
36import static javax.microedition.khronos.egl.EGL10.EGL_STENCIL_SIZE;
37import static javax.microedition.khronos.egl.EGL10.EGL_SUCCESS;
38import static javax.microedition.khronos.egl.EGL10.EGL_SURFACE_TYPE;
39import static javax.microedition.khronos.egl.EGL10.EGL_WIDTH;
40import static javax.microedition.khronos.egl.EGL10.EGL_WINDOW_BIT;
41
42import android.content.ComponentCallbacks2;
43import android.graphics.Color;
44import android.graphics.Paint;
45import android.graphics.Rect;
46import android.graphics.SurfaceTexture;
47import android.opengl.EGL14;
48import android.opengl.GLUtils;
49import android.opengl.ManagedEGLContext;
50import android.os.Handler;
51import android.os.IBinder;
52import android.os.Looper;
53import android.os.RemoteException;
54import android.os.ServiceManager;
55import android.os.SystemClock;
56import android.os.SystemProperties;
57import android.os.Trace;
58import android.util.DisplayMetrics;
59import android.util.Log;
60import android.view.Surface.OutOfResourcesException;
61
62import com.google.android.gles_jni.EGLImpl;
63
64import java.io.PrintWriter;
65import java.util.concurrent.locks.ReentrantLock;
66
67import javax.microedition.khronos.egl.EGL10;
68import javax.microedition.khronos.egl.EGL11;
69import javax.microedition.khronos.egl.EGLConfig;
70import javax.microedition.khronos.egl.EGLContext;
71import javax.microedition.khronos.egl.EGLDisplay;
72import javax.microedition.khronos.egl.EGLSurface;
73import javax.microedition.khronos.opengles.GL;
74
75/**
76 * Hardware renderer using OpenGL
77 *
78 * @hide
79 */
80public class GLRenderer extends HardwareRenderer {
81 static final int SURFACE_STATE_ERROR = 0;
82 static final int SURFACE_STATE_SUCCESS = 1;
83 static final int SURFACE_STATE_UPDATED = 2;
84
85 static final int FUNCTOR_PROCESS_DELAY = 4;
86
87 /**
88 * Number of frames to profile.
89 */
90 private static final int PROFILE_MAX_FRAMES = 128;
91
92 /**
93 * Number of floats per profiled frame.
94 */
95 private static final int PROFILE_FRAME_DATA_COUNT = 3;
96
97 private static final int PROFILE_DRAW_MARGIN = 0;
98 private static final int PROFILE_DRAW_WIDTH = 3;
99 private static final int[] PROFILE_DRAW_COLORS = { 0xcf3e66cc, 0xcfdc3912, 0xcfe69800 };
100 private static final int PROFILE_DRAW_CURRENT_FRAME_COLOR = 0xcf5faa4d;
101 private static final int PROFILE_DRAW_THRESHOLD_COLOR = 0xff5faa4d;
102 private static final int PROFILE_DRAW_THRESHOLD_STROKE_WIDTH = 2;
103 private static final int PROFILE_DRAW_DP_PER_MS = 7;
104
105 private static final String[] VISUALIZERS = {
106 PROFILE_PROPERTY_VISUALIZE_BARS,
107 PROFILE_PROPERTY_VISUALIZE_LINES
108 };
109
110 private static final String[] OVERDRAW = {
111 OVERDRAW_PROPERTY_SHOW,
John Reck30835792013-11-07 14:21:24 -0800112 };
John Reck30835792013-11-07 14:21:24 -0800113 private static final int GL_VERSION = 2;
114
115 static EGL10 sEgl;
116 static EGLDisplay sEglDisplay;
117 static EGLConfig sEglConfig;
118 static final Object[] sEglLock = new Object[0];
119 int mWidth = -1, mHeight = -1;
120
121 static final ThreadLocal<ManagedEGLContext> sEglContextStorage
122 = new ThreadLocal<ManagedEGLContext>();
123
124 EGLContext mEglContext;
125 Thread mEglThread;
126
127 EGLSurface mEglSurface;
128
129 GL mGl;
130 HardwareCanvas mCanvas;
131
132 String mName;
133
134 long mFrameCount;
135 Paint mDebugPaint;
136
137 static boolean sDirtyRegions;
138 static final boolean sDirtyRegionsRequested;
139 static {
140 String dirtyProperty = SystemProperties.get(RENDER_DIRTY_REGIONS_PROPERTY, "true");
141 //noinspection PointlessBooleanExpression,ConstantConditions
142 sDirtyRegions = "true".equalsIgnoreCase(dirtyProperty);
143 sDirtyRegionsRequested = sDirtyRegions;
144 }
145
146 boolean mDirtyRegionsEnabled;
147 boolean mUpdateDirtyRegions;
148
149 boolean mProfileEnabled;
150 int mProfileVisualizerType = -1;
151 float[] mProfileData;
152 ReentrantLock mProfileLock;
153 int mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT;
154
155 GraphDataProvider mDebugDataProvider;
156 float[][] mProfileShapes;
157 Paint mProfilePaint;
158
159 boolean mDebugDirtyRegions;
160 int mDebugOverdraw = -1;
John Reck30835792013-11-07 14:21:24 -0800161
162 final boolean mTranslucent;
163
164 private boolean mDestroyed;
165
166 private final Rect mRedrawClip = new Rect();
167
168 private final int[] mSurfaceSize = new int[2];
169 private final FunctorsRunnable mFunctorsRunnable = new FunctorsRunnable();
170
171 private long mDrawDelta = Long.MAX_VALUE;
172
173 private GLES20Canvas mGlCanvas;
174
175 private DisplayMetrics mDisplayMetrics;
176
177 private static EGLSurface sPbuffer;
178 private static final Object[] sPbufferLock = new Object[0];
179
180 private static class GLRendererEglContext extends ManagedEGLContext {
181 final Handler mHandler = new Handler();
182
183 public GLRendererEglContext(EGLContext context) {
184 super(context);
185 }
186
187 @Override
188 public void onTerminate(final EGLContext eglContext) {
189 // Make sure we do this on the correct thread.
190 if (mHandler.getLooper() != Looper.myLooper()) {
191 mHandler.post(new Runnable() {
192 @Override
193 public void run() {
194 onTerminate(eglContext);
195 }
196 });
197 return;
198 }
199
200 synchronized (sEglLock) {
201 if (sEgl == null) return;
202
203 if (EGLImpl.getInitCount(sEglDisplay) == 1) {
204 usePbufferSurface(eglContext);
205 GLES20Canvas.terminateCaches();
206
207 sEgl.eglDestroyContext(sEglDisplay, eglContext);
208 sEglContextStorage.set(null);
209 sEglContextStorage.remove();
210
211 sEgl.eglDestroySurface(sEglDisplay, sPbuffer);
212 sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE,
213 EGL_NO_SURFACE, EGL_NO_CONTEXT);
214
215 sEgl.eglReleaseThread();
216 sEgl.eglTerminate(sEglDisplay);
217
218 sEgl = null;
219 sEglDisplay = null;
220 sEglConfig = null;
221 sPbuffer = null;
222 }
223 }
224 }
225 }
226
227 HardwareCanvas createCanvas() {
228 return mGlCanvas = new GLES20Canvas(mTranslucent);
229 }
230
231 ManagedEGLContext createManagedContext(EGLContext eglContext) {
232 return new GLRendererEglContext(mEglContext);
233 }
234
235 int[] getConfig(boolean dirtyRegions) {
236 //noinspection PointlessBooleanExpression,ConstantConditions
237 final int stencilSize = GLES20Canvas.getStencilSize();
238 final int swapBehavior = dirtyRegions ? EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
239
240 return new int[] {
241 EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
242 EGL_RED_SIZE, 8,
243 EGL_GREEN_SIZE, 8,
244 EGL_BLUE_SIZE, 8,
245 EGL_ALPHA_SIZE, 8,
246 EGL_DEPTH_SIZE, 0,
247 EGL_CONFIG_CAVEAT, EGL_NONE,
248 EGL_STENCIL_SIZE, stencilSize,
249 EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior,
250 EGL_NONE
251 };
252 }
253
254 void initCaches() {
255 if (GLES20Canvas.initCaches()) {
256 // Caches were (re)initialized, rebind atlas
257 initAtlas();
258 }
259 }
260
261 void initAtlas() {
262 IBinder binder = ServiceManager.getService("assetatlas");
263 if (binder == null) return;
264
265 IAssetAtlas atlas = IAssetAtlas.Stub.asInterface(binder);
266 try {
267 if (atlas.isCompatible(android.os.Process.myPpid())) {
268 GraphicBuffer buffer = atlas.getBuffer();
269 if (buffer != null) {
270 int[] map = atlas.getMap();
271 if (map != null) {
272 GLES20Canvas.initAtlas(buffer, map);
273 }
274 // If IAssetAtlas is not the same class as the IBinder
275 // we are using a remote service and we can safely
276 // destroy the graphic buffer
277 if (atlas.getClass() != binder.getClass()) {
278 buffer.destroy();
279 }
280 }
281 }
282 } catch (RemoteException e) {
283 Log.w(LOG_TAG, "Could not acquire atlas", e);
284 }
285 }
286
287 boolean canDraw() {
288 return mGl != null && mCanvas != null && mGlCanvas != null;
289 }
290
291 int onPreDraw(Rect dirty) {
292 return mGlCanvas.onPreDraw(dirty);
293 }
294
295 void onPostDraw() {
296 mGlCanvas.onPostDraw();
297 }
298
299 void drawProfileData(View.AttachInfo attachInfo) {
300 if (mDebugDataProvider != null) {
301 final GraphDataProvider provider = mDebugDataProvider;
302 initProfileDrawData(attachInfo, provider);
303
304 final int height = provider.getVerticalUnitSize();
305 final int margin = provider.getHorizontaUnitMargin();
306 final int width = provider.getHorizontalUnitSize();
307
308 int x = 0;
309 int count = 0;
310 int current = 0;
311
312 final float[] data = provider.getData();
313 final int elementCount = provider.getElementCount();
314 final int graphType = provider.getGraphType();
315
316 int totalCount = provider.getFrameCount() * elementCount;
317 if (graphType == GraphDataProvider.GRAPH_TYPE_LINES) {
318 totalCount -= elementCount;
319 }
320
321 for (int i = 0; i < totalCount; i += elementCount) {
322 if (data[i] < 0.0f) break;
323
324 int index = count * 4;
325 if (i == provider.getCurrentFrame() * elementCount) current = index;
326
327 x += margin;
328 int x2 = x + width;
329
330 int y2 = mHeight;
331 int y1 = (int) (y2 - data[i] * height);
332
333 switch (graphType) {
334 case GraphDataProvider.GRAPH_TYPE_BARS: {
335 for (int j = 0; j < elementCount; j++) {
336 //noinspection MismatchedReadAndWriteOfArray
337 final float[] r = mProfileShapes[j];
338 r[index] = x;
339 r[index + 1] = y1;
340 r[index + 2] = x2;
341 r[index + 3] = y2;
342
343 y2 = y1;
344 if (j < elementCount - 1) {
345 y1 = (int) (y2 - data[i + j + 1] * height);
346 }
347 }
348 } break;
349 case GraphDataProvider.GRAPH_TYPE_LINES: {
350 for (int j = 0; j < elementCount; j++) {
351 //noinspection MismatchedReadAndWriteOfArray
352 final float[] r = mProfileShapes[j];
353 r[index] = (x + x2) * 0.5f;
354 r[index + 1] = index == 0 ? y1 : r[index - 1];
355 r[index + 2] = r[index] + width;
356 r[index + 3] = y1;
357
358 y2 = y1;
359 if (j < elementCount - 1) {
360 y1 = (int) (y2 - data[i + j + 1] * height);
361 }
362 }
363 } break;
364 }
365
366
367 x += width;
368 count++;
369 }
370
371 x += margin;
372
373 drawGraph(graphType, count);
374 drawCurrentFrame(graphType, current);
375 drawThreshold(x, height);
376 }
377 }
378
379 private void drawGraph(int graphType, int count) {
380 for (int i = 0; i < mProfileShapes.length; i++) {
381 mDebugDataProvider.setupGraphPaint(mProfilePaint, i);
382 switch (graphType) {
383 case GraphDataProvider.GRAPH_TYPE_BARS:
384 mGlCanvas.drawRects(mProfileShapes[i], count * 4, mProfilePaint);
385 break;
386 case GraphDataProvider.GRAPH_TYPE_LINES:
387 mGlCanvas.drawLines(mProfileShapes[i], 0, count * 4, mProfilePaint);
388 break;
389 }
390 }
391 }
392
393 private void drawCurrentFrame(int graphType, int index) {
394 if (index >= 0) {
395 mDebugDataProvider.setupCurrentFramePaint(mProfilePaint);
396 switch (graphType) {
397 case GraphDataProvider.GRAPH_TYPE_BARS:
398 mGlCanvas.drawRect(mProfileShapes[2][index], mProfileShapes[2][index + 1],
399 mProfileShapes[2][index + 2], mProfileShapes[0][index + 3],
400 mProfilePaint);
401 break;
402 case GraphDataProvider.GRAPH_TYPE_LINES:
403 mGlCanvas.drawLine(mProfileShapes[2][index], mProfileShapes[2][index + 1],
404 mProfileShapes[2][index], mHeight, mProfilePaint);
405 break;
406 }
407 }
408 }
409
410 private void drawThreshold(int x, int height) {
411 float threshold = mDebugDataProvider.getThreshold();
412 if (threshold > 0.0f) {
413 mDebugDataProvider.setupThresholdPaint(mProfilePaint);
414 int y = (int) (mHeight - threshold * height);
415 mGlCanvas.drawLine(0.0f, y, x, y, mProfilePaint);
416 }
417 }
418
419 private void initProfileDrawData(View.AttachInfo attachInfo, GraphDataProvider provider) {
420 if (mProfileShapes == null) {
421 final int elementCount = provider.getElementCount();
422 final int frameCount = provider.getFrameCount();
423
424 mProfileShapes = new float[elementCount][];
425 for (int i = 0; i < elementCount; i++) {
426 mProfileShapes[i] = new float[frameCount * 4];
427 }
428
429 mProfilePaint = new Paint();
430 }
431
432 mProfilePaint.reset();
433 if (provider.getGraphType() == GraphDataProvider.GRAPH_TYPE_LINES) {
434 mProfilePaint.setAntiAlias(true);
435 }
436
437 if (mDisplayMetrics == null) {
438 mDisplayMetrics = new DisplayMetrics();
439 }
440
441 attachInfo.mDisplay.getMetrics(mDisplayMetrics);
442 provider.prepare(mDisplayMetrics);
443 }
444
445 @Override
446 void destroy(boolean full) {
447 try {
448 if (full && mCanvas != null) {
449 mCanvas = null;
450 }
451
452 if (!isEnabled() || mDestroyed) {
453 setEnabled(false);
454 return;
455 }
456
457 destroySurface();
458 setEnabled(false);
459
460 mDestroyed = true;
461 mGl = null;
462 } finally {
463 if (full && mGlCanvas != null) {
464 mGlCanvas = null;
465 }
466 }
467 }
468
469 @Override
470 void pushLayerUpdate(HardwareLayer layer) {
471 mGlCanvas.pushLayerUpdate(layer);
472 }
473
474 @Override
475 void cancelLayerUpdate(HardwareLayer layer) {
476 mGlCanvas.cancelLayerUpdate(layer);
477 }
478
479 @Override
480 void flushLayerUpdates() {
481 mGlCanvas.flushLayerUpdates();
482 }
483
484 @Override
485 HardwareLayer createHardwareLayer(boolean isOpaque) {
486 return new GLES20TextureLayer(isOpaque);
487 }
488
489 @Override
490 public HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque) {
491 return new GLES20RenderLayer(width, height, isOpaque);
492 }
493
John Reck30835792013-11-07 14:21:24 -0800494 @Override
495 public SurfaceTexture createSurfaceTexture(HardwareLayer layer) {
496 return ((GLES20TextureLayer) layer).getSurfaceTexture();
497 }
498
499 @Override
500 void setSurfaceTexture(HardwareLayer layer, SurfaceTexture surfaceTexture) {
501 ((GLES20TextureLayer) layer).setSurfaceTexture(surfaceTexture);
502 }
503
504 @Override
505 boolean safelyRun(Runnable action) {
506 boolean needsContext = !isEnabled() || checkRenderContext() == SURFACE_STATE_ERROR;
507
508 if (needsContext) {
509 GLRendererEglContext managedContext =
510 (GLRendererEglContext) sEglContextStorage.get();
511 if (managedContext == null) return false;
512 usePbufferSurface(managedContext.getContext());
513 }
514
515 try {
516 action.run();
517 } finally {
518 if (needsContext) {
519 sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE,
520 EGL_NO_SURFACE, EGL_NO_CONTEXT);
521 }
522 }
523
524 return true;
525 }
526
527 @Override
528 void destroyLayers(final View view) {
529 if (view != null) {
530 safelyRun(new Runnable() {
531 @Override
532 public void run() {
533 if (mCanvas != null) {
534 mCanvas.clearLayerUpdates();
535 }
536 destroyHardwareLayer(view);
537 GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS);
538 }
539 });
540 }
541 }
542
543 private static void destroyHardwareLayer(View view) {
544 view.destroyLayer(true);
545
546 if (view instanceof ViewGroup) {
547 ViewGroup group = (ViewGroup) view;
548
549 int count = group.getChildCount();
550 for (int i = 0; i < count; i++) {
551 destroyHardwareLayer(group.getChildAt(i));
552 }
553 }
554 }
555
556 @Override
557 void destroyHardwareResources(final View view) {
558 if (view != null) {
559 safelyRun(new Runnable() {
560 @Override
561 public void run() {
562 if (mCanvas != null) {
563 mCanvas.clearLayerUpdates();
564 }
565 destroyResources(view);
566 GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS);
567 }
568 });
569 }
570 }
571
572 private static void destroyResources(View view) {
573 view.destroyHardwareResources();
574
575 if (view instanceof ViewGroup) {
576 ViewGroup group = (ViewGroup) view;
577
578 int count = group.getChildCount();
579 for (int i = 0; i < count; i++) {
580 destroyResources(group.getChildAt(i));
581 }
582 }
583 }
584
585 static void startTrimMemory(int level) {
586 if (sEgl == null || sEglConfig == null) return;
587
588 GLRendererEglContext managedContext =
589 (GLRendererEglContext) sEglContextStorage.get();
590 // We do not have OpenGL objects
591 if (managedContext == null) {
592 return;
593 } else {
594 usePbufferSurface(managedContext.getContext());
595 }
596
597 if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
598 GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_FULL);
599 } else if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
600 GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_MODERATE);
601 }
602 }
603
604 static void endTrimMemory() {
605 if (sEgl != null && sEglDisplay != null) {
606 sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
607 }
608 }
609
610 private static void usePbufferSurface(EGLContext eglContext) {
611 synchronized (sPbufferLock) {
612 // Create a temporary 1x1 pbuffer so we have a context
613 // to clear our OpenGL objects
614 if (sPbuffer == null) {
615 sPbuffer = sEgl.eglCreatePbufferSurface(sEglDisplay, sEglConfig, new int[] {
616 EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE
617 });
618 }
619 }
620 sEgl.eglMakeCurrent(sEglDisplay, sPbuffer, sPbuffer, eglContext);
621 }
622
623 GLRenderer(boolean translucent) {
624 mTranslucent = translucent;
625
John Reckcec24ae2013-11-05 13:27:50 -0800626 loadSystemProperties();
John Reck30835792013-11-07 14:21:24 -0800627 }
628
629 @Override
John Reckcec24ae2013-11-05 13:27:50 -0800630 boolean loadSystemProperties() {
John Reck30835792013-11-07 14:21:24 -0800631 boolean value;
632 boolean changed = false;
633
634 String profiling = SystemProperties.get(PROFILE_PROPERTY);
635 int graphType = search(VISUALIZERS, profiling);
636 value = graphType >= 0;
637
638 if (graphType != mProfileVisualizerType) {
639 changed = true;
640 mProfileVisualizerType = graphType;
641
642 mProfileShapes = null;
643 mProfilePaint = null;
644
645 if (value) {
646 mDebugDataProvider = new DrawPerformanceDataProvider(graphType);
647 } else {
648 mDebugDataProvider = null;
649 }
650 }
651
652 // If on-screen profiling is not enabled, we need to check whether
653 // console profiling only is enabled
654 if (!value) {
655 value = Boolean.parseBoolean(profiling);
656 }
657
658 if (value != mProfileEnabled) {
659 changed = true;
660 mProfileEnabled = value;
661
662 if (mProfileEnabled) {
663 Log.d(LOG_TAG, "Profiling hardware renderer");
664
665 int maxProfileFrames = SystemProperties.getInt(PROFILE_MAXFRAMES_PROPERTY,
666 PROFILE_MAX_FRAMES);
667 mProfileData = new float[maxProfileFrames * PROFILE_FRAME_DATA_COUNT];
668 for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
669 mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1;
670 }
671
672 mProfileLock = new ReentrantLock();
673 } else {
674 mProfileData = null;
675 mProfileLock = null;
676 mProfileVisualizerType = -1;
677 }
678
679 mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT;
680 }
681
682 value = SystemProperties.getBoolean(DEBUG_DIRTY_REGIONS_PROPERTY, false);
683 if (value != mDebugDirtyRegions) {
684 changed = true;
685 mDebugDirtyRegions = value;
686
687 if (mDebugDirtyRegions) {
688 Log.d(LOG_TAG, "Debugging dirty regions");
689 }
690 }
691
692 String overdraw = SystemProperties.get(HardwareRenderer.DEBUG_OVERDRAW_PROPERTY);
693 int debugOverdraw = search(OVERDRAW, overdraw);
694 if (debugOverdraw != mDebugOverdraw) {
695 changed = true;
696 mDebugOverdraw = debugOverdraw;
John Reck30835792013-11-07 14:21:24 -0800697 }
698
699 if (loadProperties()) {
700 changed = true;
701 }
702
703 return changed;
704 }
705
706 private static int search(String[] values, String value) {
707 for (int i = 0; i < values.length; i++) {
708 if (values[i].equals(value)) return i;
709 }
710 return -1;
711 }
712
713 @Override
714 void dumpGfxInfo(PrintWriter pw) {
715 if (mProfileEnabled) {
716 pw.printf("\n\tDraw\tProcess\tExecute\n");
717
718 mProfileLock.lock();
719 try {
720 for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
721 if (mProfileData[i] < 0) {
722 break;
723 }
724 pw.printf("\t%3.2f\t%3.2f\t%3.2f\n", mProfileData[i], mProfileData[i + 1],
725 mProfileData[i + 2]);
726 mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1;
727 }
728 mProfileCurrentFrame = mProfileData.length;
729 } finally {
730 mProfileLock.unlock();
731 }
732 }
733 }
734
735 @Override
736 long getFrameCount() {
737 return mFrameCount;
738 }
739
740 /**
741 * Indicates whether this renderer instance can track and update dirty regions.
742 */
743 boolean hasDirtyRegions() {
744 return mDirtyRegionsEnabled;
745 }
746
747 /**
748 * Checks for OpenGL errors. If an error has occured, {@link #destroy(boolean)}
749 * is invoked and the requested flag is turned off. The error code is
750 * also logged as a warning.
751 */
752 void checkEglErrors() {
753 if (isEnabled()) {
754 checkEglErrorsForced();
755 }
756 }
757
758 private void checkEglErrorsForced() {
759 int error = sEgl.eglGetError();
760 if (error != EGL_SUCCESS) {
761 // something bad has happened revert to
762 // normal rendering.
763 Log.w(LOG_TAG, "EGL error: " + GLUtils.getEGLErrorString(error));
764 fallback(error != EGL11.EGL_CONTEXT_LOST);
765 }
766 }
767
768 private void fallback(boolean fallback) {
769 destroy(true);
770 if (fallback) {
771 // we'll try again if it was context lost
772 setRequested(false);
773 Log.w(LOG_TAG, "Mountain View, we've had a problem here. "
774 + "Switching back to software rendering.");
775 }
776 }
777
778 @Override
779 boolean initialize(Surface surface) throws OutOfResourcesException {
780 if (isRequested() && !isEnabled()) {
781 boolean contextCreated = initializeEgl();
782 mGl = createEglSurface(surface);
783 mDestroyed = false;
784
785 if (mGl != null) {
786 int err = sEgl.eglGetError();
787 if (err != EGL_SUCCESS) {
788 destroy(true);
789 setRequested(false);
790 } else {
791 if (mCanvas == null) {
792 mCanvas = createCanvas();
John Reck30835792013-11-07 14:21:24 -0800793 }
794 setEnabled(true);
795
796 if (contextCreated) {
797 initAtlas();
798 }
799 }
800
801 return mCanvas != null;
802 }
803 }
804 return false;
805 }
806
807 @Override
808 void updateSurface(Surface surface) throws OutOfResourcesException {
809 if (isRequested() && isEnabled()) {
810 createEglSurface(surface);
811 }
812 }
813
814 boolean initializeEgl() {
815 synchronized (sEglLock) {
816 if (sEgl == null && sEglConfig == null) {
817 sEgl = (EGL10) EGLContext.getEGL();
818
819 // Get to the default display.
820 sEglDisplay = sEgl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
821
822 if (sEglDisplay == EGL_NO_DISPLAY) {
823 throw new RuntimeException("eglGetDisplay failed "
824 + GLUtils.getEGLErrorString(sEgl.eglGetError()));
825 }
826
827 // We can now initialize EGL for that display
828 int[] version = new int[2];
829 if (!sEgl.eglInitialize(sEglDisplay, version)) {
830 throw new RuntimeException("eglInitialize failed " +
831 GLUtils.getEGLErrorString(sEgl.eglGetError()));
832 }
833
834 checkEglErrorsForced();
835
836 sEglConfig = loadEglConfig();
837 }
838 }
839
840 ManagedEGLContext managedContext = sEglContextStorage.get();
841 mEglContext = managedContext != null ? managedContext.getContext() : null;
842 mEglThread = Thread.currentThread();
843
844 if (mEglContext == null) {
845 mEglContext = createContext(sEgl, sEglDisplay, sEglConfig);
846 sEglContextStorage.set(createManagedContext(mEglContext));
847 return true;
848 }
849
850 return false;
851 }
852
853 private EGLConfig loadEglConfig() {
854 EGLConfig eglConfig = chooseEglConfig();
855 if (eglConfig == null) {
856 // We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without
857 if (sDirtyRegions) {
858 sDirtyRegions = false;
859 eglConfig = chooseEglConfig();
860 if (eglConfig == null) {
861 throw new RuntimeException("eglConfig not initialized");
862 }
863 } else {
864 throw new RuntimeException("eglConfig not initialized");
865 }
866 }
867 return eglConfig;
868 }
869
870 private EGLConfig chooseEglConfig() {
871 EGLConfig[] configs = new EGLConfig[1];
872 int[] configsCount = new int[1];
873 int[] configSpec = getConfig(sDirtyRegions);
874
875 // Debug
876 final String debug = SystemProperties.get(PRINT_CONFIG_PROPERTY, "");
877 if ("all".equalsIgnoreCase(debug)) {
878 sEgl.eglChooseConfig(sEglDisplay, configSpec, null, 0, configsCount);
879
880 EGLConfig[] debugConfigs = new EGLConfig[configsCount[0]];
881 sEgl.eglChooseConfig(sEglDisplay, configSpec, debugConfigs,
882 configsCount[0], configsCount);
883
884 for (EGLConfig config : debugConfigs) {
885 printConfig(config);
886 }
887 }
888
889 if (!sEgl.eglChooseConfig(sEglDisplay, configSpec, configs, 1, configsCount)) {
890 throw new IllegalArgumentException("eglChooseConfig failed " +
891 GLUtils.getEGLErrorString(sEgl.eglGetError()));
892 } else if (configsCount[0] > 0) {
893 if ("choice".equalsIgnoreCase(debug)) {
894 printConfig(configs[0]);
895 }
896 return configs[0];
897 }
898
899 return null;
900 }
901
902 private static void printConfig(EGLConfig config) {
903 int[] value = new int[1];
904
905 Log.d(LOG_TAG, "EGL configuration " + config + ":");
906
907 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_RED_SIZE, value);
908 Log.d(LOG_TAG, " RED_SIZE = " + value[0]);
909
910 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_GREEN_SIZE, value);
911 Log.d(LOG_TAG, " GREEN_SIZE = " + value[0]);
912
913 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_BLUE_SIZE, value);
914 Log.d(LOG_TAG, " BLUE_SIZE = " + value[0]);
915
916 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_ALPHA_SIZE, value);
917 Log.d(LOG_TAG, " ALPHA_SIZE = " + value[0]);
918
919 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_DEPTH_SIZE, value);
920 Log.d(LOG_TAG, " DEPTH_SIZE = " + value[0]);
921
922 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_STENCIL_SIZE, value);
923 Log.d(LOG_TAG, " STENCIL_SIZE = " + value[0]);
924
925 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SAMPLE_BUFFERS, value);
926 Log.d(LOG_TAG, " SAMPLE_BUFFERS = " + value[0]);
927
928 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SAMPLES, value);
929 Log.d(LOG_TAG, " SAMPLES = " + value[0]);
930
931 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SURFACE_TYPE, value);
932 Log.d(LOG_TAG, " SURFACE_TYPE = 0x" + Integer.toHexString(value[0]));
933
934 sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_CONFIG_CAVEAT, value);
935 Log.d(LOG_TAG, " CONFIG_CAVEAT = 0x" + Integer.toHexString(value[0]));
936 }
937
938 GL createEglSurface(Surface surface) throws OutOfResourcesException {
939 // Check preconditions.
940 if (sEgl == null) {
941 throw new RuntimeException("egl not initialized");
942 }
943 if (sEglDisplay == null) {
944 throw new RuntimeException("eglDisplay not initialized");
945 }
946 if (sEglConfig == null) {
947 throw new RuntimeException("eglConfig not initialized");
948 }
949 if (Thread.currentThread() != mEglThread) {
950 throw new IllegalStateException("HardwareRenderer cannot be used "
951 + "from multiple threads");
952 }
953
954 // In case we need to destroy an existing surface
955 destroySurface();
956
957 // Create an EGL surface we can render into.
958 if (!createSurface(surface)) {
959 return null;
960 }
961
962 initCaches();
963
964 return mEglContext.getGL();
965 }
966
967 private void enableDirtyRegions() {
968 // If mDirtyRegions is set, this means we have an EGL configuration
969 // with EGL_SWAP_BEHAVIOR_PRESERVED_BIT set
970 if (sDirtyRegions) {
971 if (!(mDirtyRegionsEnabled = preserveBackBuffer())) {
972 Log.w(LOG_TAG, "Backbuffer cannot be preserved");
973 }
974 } else if (sDirtyRegionsRequested) {
975 // If mDirtyRegions is not set, our EGL configuration does not
976 // have EGL_SWAP_BEHAVIOR_PRESERVED_BIT; however, the default
977 // swap behavior might be EGL_BUFFER_PRESERVED, which means we
978 // want to set mDirtyRegions. We try to do this only if dirty
979 // regions were initially requested as part of the device
980 // configuration (see RENDER_DIRTY_REGIONS)
981 mDirtyRegionsEnabled = isBackBufferPreserved();
982 }
983 }
984
985 EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
986 final int[] attribs = { EGL14.EGL_CONTEXT_CLIENT_VERSION, GL_VERSION, EGL_NONE };
987
988 EGLContext context = egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT,
989 attribs);
990 if (context == null || context == EGL_NO_CONTEXT) {
991 //noinspection ConstantConditions
992 throw new IllegalStateException(
993 "Could not create an EGL context. eglCreateContext failed with error: " +
994 GLUtils.getEGLErrorString(sEgl.eglGetError()));
995 }
996
997 return context;
998 }
999
1000 void destroySurface() {
1001 if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) {
1002 if (mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL_DRAW))) {
1003 sEgl.eglMakeCurrent(sEglDisplay,
1004 EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
1005 }
1006 sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
1007 mEglSurface = null;
1008 }
1009 }
1010
1011 @Override
1012 void invalidate(Surface surface) {
1013 // Cancels any existing buffer to ensure we'll get a buffer
1014 // of the right size before we call eglSwapBuffers
1015 sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
1016
1017 if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) {
1018 sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
1019 mEglSurface = null;
1020 setEnabled(false);
1021 }
1022
1023 if (surface.isValid()) {
1024 if (!createSurface(surface)) {
1025 return;
1026 }
1027
1028 mUpdateDirtyRegions = true;
1029
1030 if (mCanvas != null) {
1031 setEnabled(true);
1032 }
1033 }
1034 }
1035
1036 private boolean createSurface(Surface surface) {
1037 mEglSurface = sEgl.eglCreateWindowSurface(sEglDisplay, sEglConfig, surface, null);
1038
1039 if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) {
1040 int error = sEgl.eglGetError();
1041 if (error == EGL_BAD_NATIVE_WINDOW) {
1042 Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
1043 return false;
1044 }
1045 throw new RuntimeException("createWindowSurface failed "
1046 + GLUtils.getEGLErrorString(error));
1047 }
1048
1049 if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
1050 throw new IllegalStateException("eglMakeCurrent failed " +
1051 GLUtils.getEGLErrorString(sEgl.eglGetError()));
1052 }
1053
1054 enableDirtyRegions();
1055
1056 return true;
1057 }
1058
1059 @Override
1060 boolean validate() {
1061 return checkRenderContext() != SURFACE_STATE_ERROR;
1062 }
1063
1064 @Override
1065 void setup(int width, int height) {
1066 if (validate()) {
1067 mCanvas.setViewport(width, height);
1068 mWidth = width;
1069 mHeight = height;
1070 }
1071 }
1072
1073 @Override
1074 int getWidth() {
1075 return mWidth;
1076 }
1077
1078 @Override
1079 int getHeight() {
1080 return mHeight;
1081 }
1082
1083 @Override
John Reck30835792013-11-07 14:21:24 -08001084 void setName(String name) {
1085 mName = name;
1086 }
1087
1088 class FunctorsRunnable implements Runnable {
1089 View.AttachInfo attachInfo;
1090
1091 @Override
1092 public void run() {
1093 final HardwareRenderer renderer = attachInfo.mHardwareRenderer;
1094 if (renderer == null || !renderer.isEnabled() || renderer != GLRenderer.this) {
1095 return;
1096 }
1097
1098 if (checkRenderContext() != SURFACE_STATE_ERROR) {
1099 int status = mCanvas.invokeFunctors(mRedrawClip);
1100 handleFunctorStatus(attachInfo, status);
1101 }
1102 }
1103 }
1104
1105 @Override
1106 void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
1107 Rect dirty) {
1108 if (canDraw()) {
1109 if (!hasDirtyRegions()) {
1110 dirty = null;
1111 }
1112 attachInfo.mIgnoreDirtyState = true;
1113 attachInfo.mDrawingTime = SystemClock.uptimeMillis();
1114
1115 view.mPrivateFlags |= View.PFLAG_DRAWN;
1116
1117 // We are already on the correct thread
1118 final int surfaceState = checkRenderContextUnsafe();
1119 if (surfaceState != SURFACE_STATE_ERROR) {
1120 HardwareCanvas canvas = mCanvas;
John Reck30835792013-11-07 14:21:24 -08001121
1122 if (mProfileEnabled) {
1123 mProfileLock.lock();
1124 }
1125
1126 dirty = beginFrame(canvas, dirty, surfaceState);
1127
1128 DisplayList displayList = buildDisplayList(view, canvas);
1129
1130 // buildDisplayList() calls into user code which can cause
1131 // an eglMakeCurrent to happen with a different surface/context.
1132 // We must therefore check again here.
1133 if (checkRenderContextUnsafe() == SURFACE_STATE_ERROR) {
1134 return;
1135 }
1136
1137 int saveCount = 0;
1138 int status = DisplayList.STATUS_DONE;
1139
1140 long start = getSystemTime();
1141 try {
1142 status = prepareFrame(dirty);
1143
1144 saveCount = canvas.save();
1145 callbacks.onHardwarePreDraw(canvas);
1146
1147 if (displayList != null) {
1148 status |= drawDisplayList(attachInfo, canvas, displayList, status);
1149 } else {
1150 // Shouldn't reach here
1151 view.draw(canvas);
1152 }
1153 } catch (Exception e) {
1154 Log.e(LOG_TAG, "An error has occurred while drawing:", e);
1155 } finally {
1156 callbacks.onHardwarePostDraw(canvas);
1157 canvas.restoreToCount(saveCount);
1158 view.mRecreateDisplayList = false;
1159
1160 mDrawDelta = getSystemTime() - start;
1161
1162 if (mDrawDelta > 0) {
1163 mFrameCount++;
1164
John Reck30835792013-11-07 14:21:24 -08001165 debugDirtyRegions(dirty, canvas);
1166 drawProfileData(attachInfo);
1167 }
1168 }
1169
1170 onPostDraw();
1171
1172 swapBuffers(status);
1173
1174 if (mProfileEnabled) {
1175 mProfileLock.unlock();
1176 }
1177
1178 attachInfo.mIgnoreDirtyState = false;
1179 }
1180 }
1181 }
1182
John Reck30835792013-11-07 14:21:24 -08001183 private DisplayList buildDisplayList(View view, HardwareCanvas canvas) {
1184 if (mDrawDelta <= 0) {
1185 return view.mDisplayList;
1186 }
1187
1188 view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
1189 == View.PFLAG_INVALIDATED;
1190 view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
1191
1192 long buildDisplayListStartTime = startBuildDisplayListProfiling();
1193 canvas.clearLayerUpdates();
1194
1195 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList");
1196 DisplayList displayList = view.getDisplayList();
1197 Trace.traceEnd(Trace.TRACE_TAG_VIEW);
1198
1199 endBuildDisplayListProfiling(buildDisplayListStartTime);
1200
1201 return displayList;
1202 }
1203
1204 private Rect beginFrame(HardwareCanvas canvas, Rect dirty, int surfaceState) {
1205 // We had to change the current surface and/or context, redraw everything
1206 if (surfaceState == SURFACE_STATE_UPDATED) {
1207 dirty = null;
1208 beginFrame(null);
1209 } else {
1210 int[] size = mSurfaceSize;
1211 beginFrame(size);
1212
1213 if (size[1] != mHeight || size[0] != mWidth) {
1214 mWidth = size[0];
1215 mHeight = size[1];
1216
1217 canvas.setViewport(mWidth, mHeight);
1218
1219 dirty = null;
1220 }
1221 }
1222
1223 if (mDebugDataProvider != null) dirty = null;
1224
1225 return dirty;
1226 }
1227
1228 private long startBuildDisplayListProfiling() {
1229 if (mProfileEnabled) {
1230 mProfileCurrentFrame += PROFILE_FRAME_DATA_COUNT;
1231 if (mProfileCurrentFrame >= mProfileData.length) {
1232 mProfileCurrentFrame = 0;
1233 }
1234
1235 return System.nanoTime();
1236 }
1237 return 0;
1238 }
1239
1240 private void endBuildDisplayListProfiling(long getDisplayListStartTime) {
1241 if (mProfileEnabled) {
1242 long now = System.nanoTime();
1243 float total = (now - getDisplayListStartTime) * 0.000001f;
1244 //noinspection PointlessArithmeticExpression
1245 mProfileData[mProfileCurrentFrame] = total;
1246 }
1247 }
1248
1249 private int prepareFrame(Rect dirty) {
1250 int status;
1251 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "prepareFrame");
1252 try {
1253 status = onPreDraw(dirty);
1254 } finally {
1255 Trace.traceEnd(Trace.TRACE_TAG_VIEW);
1256 }
1257 return status;
1258 }
1259
1260 private int drawDisplayList(View.AttachInfo attachInfo, HardwareCanvas canvas,
1261 DisplayList displayList, int status) {
1262
1263 long drawDisplayListStartTime = 0;
1264 if (mProfileEnabled) {
1265 drawDisplayListStartTime = System.nanoTime();
1266 }
1267
1268 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "drawDisplayList");
1269 try {
1270 status |= canvas.drawDisplayList(displayList, mRedrawClip,
1271 DisplayList.FLAG_CLIP_CHILDREN);
1272 } finally {
1273 Trace.traceEnd(Trace.TRACE_TAG_VIEW);
1274 }
1275
1276 if (mProfileEnabled) {
1277 long now = System.nanoTime();
1278 float total = (now - drawDisplayListStartTime) * 0.000001f;
1279 mProfileData[mProfileCurrentFrame + 1] = total;
1280 }
1281
1282 handleFunctorStatus(attachInfo, status);
1283 return status;
1284 }
1285
1286 private void swapBuffers(int status) {
1287 if ((status & DisplayList.STATUS_DREW) == DisplayList.STATUS_DREW) {
1288 long eglSwapBuffersStartTime = 0;
1289 if (mProfileEnabled) {
1290 eglSwapBuffersStartTime = System.nanoTime();
1291 }
1292
1293 sEgl.eglSwapBuffers(sEglDisplay, mEglSurface);
1294
1295 if (mProfileEnabled) {
1296 long now = System.nanoTime();
1297 float total = (now - eglSwapBuffersStartTime) * 0.000001f;
1298 mProfileData[mProfileCurrentFrame + 2] = total;
1299 }
1300
1301 checkEglErrors();
1302 }
1303 }
1304
1305 private void debugDirtyRegions(Rect dirty, HardwareCanvas canvas) {
1306 if (mDebugDirtyRegions) {
1307 if (mDebugPaint == null) {
1308 mDebugPaint = new Paint();
1309 mDebugPaint.setColor(0x7fff0000);
1310 }
1311
1312 if (dirty != null && (mFrameCount & 1) == 0) {
1313 canvas.drawRect(dirty, mDebugPaint);
1314 }
1315 }
1316 }
1317
1318 private void handleFunctorStatus(View.AttachInfo attachInfo, int status) {
1319 // If the draw flag is set, functors will be invoked while executing
1320 // the tree of display lists
1321 if ((status & DisplayList.STATUS_DRAW) != 0) {
1322 if (mRedrawClip.isEmpty()) {
1323 attachInfo.mViewRootImpl.invalidate();
1324 } else {
1325 attachInfo.mViewRootImpl.invalidateChildInParent(null, mRedrawClip);
1326 mRedrawClip.setEmpty();
1327 }
1328 }
1329
1330 if ((status & DisplayList.STATUS_INVOKE) != 0 ||
1331 attachInfo.mHandler.hasCallbacks(mFunctorsRunnable)) {
1332 attachInfo.mHandler.removeCallbacks(mFunctorsRunnable);
1333 mFunctorsRunnable.attachInfo = attachInfo;
1334 attachInfo.mHandler.postDelayed(mFunctorsRunnable, FUNCTOR_PROCESS_DELAY);
1335 }
1336 }
1337
1338 @Override
Ashok Bhat36bef0b2014-01-20 20:08:01 +00001339 void detachFunctor(long functor) {
John Reck30835792013-11-07 14:21:24 -08001340 if (mCanvas != null) {
1341 mCanvas.detachFunctor(functor);
1342 }
1343 }
1344
1345 @Override
Ashok Bhat36bef0b2014-01-20 20:08:01 +00001346 void attachFunctor(View.AttachInfo attachInfo, long functor) {
John Reck30835792013-11-07 14:21:24 -08001347 if (mCanvas != null) {
1348 mCanvas.attachFunctor(functor);
1349 mFunctorsRunnable.attachInfo = attachInfo;
1350 attachInfo.mHandler.removeCallbacks(mFunctorsRunnable);
1351 attachInfo.mHandler.postDelayed(mFunctorsRunnable, 0);
John Reck30835792013-11-07 14:21:24 -08001352 }
John Reck30835792013-11-07 14:21:24 -08001353 }
1354
1355 /**
1356 * Ensures the current EGL context and surface are the ones we expect.
1357 * This method throws an IllegalStateException if invoked from a thread
1358 * that did not initialize EGL.
1359 *
1360 * @return {@link #SURFACE_STATE_ERROR} if the correct EGL context cannot be made current,
1361 * {@link #SURFACE_STATE_UPDATED} if the EGL context was changed or
1362 * {@link #SURFACE_STATE_SUCCESS} if the EGL context was the correct one
1363 *
1364 * @see #checkRenderContextUnsafe()
1365 */
1366 int checkRenderContext() {
1367 if (mEglThread != Thread.currentThread()) {
1368 throw new IllegalStateException("Hardware acceleration can only be used with a " +
1369 "single UI thread.\nOriginal thread: " + mEglThread + "\n" +
1370 "Current thread: " + Thread.currentThread());
1371 }
1372
1373 return checkRenderContextUnsafe();
1374 }
1375
1376 /**
1377 * Ensures the current EGL context and surface are the ones we expect.
1378 * This method does not check the current thread.
1379 *
1380 * @return {@link #SURFACE_STATE_ERROR} if the correct EGL context cannot be made current,
1381 * {@link #SURFACE_STATE_UPDATED} if the EGL context was changed or
1382 * {@link #SURFACE_STATE_SUCCESS} if the EGL context was the correct one
1383 *
1384 * @see #checkRenderContext()
1385 */
1386 private int checkRenderContextUnsafe() {
1387 if (!mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL_DRAW)) ||
1388 !mEglContext.equals(sEgl.eglGetCurrentContext())) {
1389 if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
1390 Log.e(LOG_TAG, "eglMakeCurrent failed " +
1391 GLUtils.getEGLErrorString(sEgl.eglGetError()));
1392 fallback(true);
1393 return SURFACE_STATE_ERROR;
1394 } else {
1395 if (mUpdateDirtyRegions) {
1396 enableDirtyRegions();
1397 mUpdateDirtyRegions = false;
1398 }
1399 return SURFACE_STATE_UPDATED;
1400 }
1401 }
1402 return SURFACE_STATE_SUCCESS;
1403 }
1404
1405 private static int dpToPx(int dp, float density) {
1406 return (int) (dp * density + 0.5f);
1407 }
1408
John Reck3dfe19f2013-12-13 14:25:19 -08001409 static native boolean loadProperties();
John Reck30835792013-11-07 14:21:24 -08001410
1411 static native void setupShadersDiskCache(String cacheFile);
1412
1413 /**
1414 * Notifies EGL that the frame is about to be rendered.
1415 * @param size
1416 */
John Reck3dfe19f2013-12-13 14:25:19 -08001417 static native void beginFrame(int[] size);
John Reck30835792013-11-07 14:21:24 -08001418
1419 /**
1420 * Returns the current system time according to the renderer.
1421 * This method is used for debugging only and should not be used
1422 * as a clock.
1423 */
John Reck3dfe19f2013-12-13 14:25:19 -08001424 static native long getSystemTime();
John Reck30835792013-11-07 14:21:24 -08001425
1426 /**
1427 * Preserves the back buffer of the current surface after a buffer swap.
1428 * Calling this method sets the EGL_SWAP_BEHAVIOR attribute of the current
1429 * surface to EGL_BUFFER_PRESERVED. Calling this method requires an EGL
1430 * config that supports EGL_SWAP_BEHAVIOR_PRESERVED_BIT.
1431 *
1432 * @return True if the swap behavior was successfully changed,
1433 * false otherwise.
1434 */
John Reck3dfe19f2013-12-13 14:25:19 -08001435 static native boolean preserveBackBuffer();
John Reck30835792013-11-07 14:21:24 -08001436
1437 /**
1438 * Indicates whether the current surface preserves its back buffer
1439 * after a buffer swap.
1440 *
1441 * @return True, if the surface's EGL_SWAP_BEHAVIOR is EGL_BUFFER_PRESERVED,
1442 * false otherwise
1443 */
John Reck3dfe19f2013-12-13 14:25:19 -08001444 static native boolean isBackBufferPreserved();
John Reck30835792013-11-07 14:21:24 -08001445
1446 class DrawPerformanceDataProvider extends GraphDataProvider {
1447 private final int mGraphType;
1448
1449 private int mVerticalUnit;
1450 private int mHorizontalUnit;
1451 private int mHorizontalMargin;
1452 private int mThresholdStroke;
1453
1454 DrawPerformanceDataProvider(int graphType) {
1455 mGraphType = graphType;
1456 }
1457
1458 @Override
1459 void prepare(DisplayMetrics metrics) {
1460 final float density = metrics.density;
1461
1462 mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density);
1463 mHorizontalUnit = dpToPx(PROFILE_DRAW_WIDTH, density);
1464 mHorizontalMargin = dpToPx(PROFILE_DRAW_MARGIN, density);
1465 mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density);
1466 }
1467
1468 @Override
1469 int getGraphType() {
1470 return mGraphType;
1471 }
1472
1473 @Override
1474 int getVerticalUnitSize() {
1475 return mVerticalUnit;
1476 }
1477
1478 @Override
1479 int getHorizontalUnitSize() {
1480 return mHorizontalUnit;
1481 }
1482
1483 @Override
1484 int getHorizontaUnitMargin() {
1485 return mHorizontalMargin;
1486 }
1487
1488 @Override
1489 float[] getData() {
1490 return mProfileData;
1491 }
1492
1493 @Override
1494 float getThreshold() {
1495 return 16;
1496 }
1497
1498 @Override
1499 int getFrameCount() {
1500 return mProfileData.length / PROFILE_FRAME_DATA_COUNT;
1501 }
1502
1503 @Override
1504 int getElementCount() {
1505 return PROFILE_FRAME_DATA_COUNT;
1506 }
1507
1508 @Override
1509 int getCurrentFrame() {
1510 return mProfileCurrentFrame / PROFILE_FRAME_DATA_COUNT;
1511 }
1512
1513 @Override
1514 void setupGraphPaint(Paint paint, int elementIndex) {
1515 paint.setColor(PROFILE_DRAW_COLORS[elementIndex]);
1516 if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke);
1517 }
1518
1519 @Override
1520 void setupThresholdPaint(Paint paint) {
1521 paint.setColor(PROFILE_DRAW_THRESHOLD_COLOR);
1522 paint.setStrokeWidth(mThresholdStroke);
1523 }
1524
1525 @Override
1526 void setupCurrentFramePaint(Paint paint) {
1527 paint.setColor(PROFILE_DRAW_CURRENT_FRAME_COLOR);
1528 if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke);
1529 }
1530 }
1531}