1 /* 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 17 package android.view; 18 19 import com.android.internal.R; 20 21 import android.content.Context; 22 import android.content.res.Resources; 23 import android.content.res.TypedArray; 24 import android.graphics.Bitmap; 25 import android.graphics.Rect; 26 import android.graphics.drawable.Drawable; 27 import android.os.IBinder; 28 import android.os.RemoteException; 29 import android.os.ServiceManager; 30 import android.os.SystemProperties; 31 import android.os.Trace; 32 import android.util.Log; 33 import android.util.LongSparseArray; 34 import android.util.TimeUtils; 35 import android.view.Surface.OutOfResourcesException; 36 import android.view.View.AttachInfo; 37 38 import java.io.FileDescriptor; 39 import java.io.PrintWriter; 40 import java.util.ArrayList; 41 import java.util.HashSet; 42 43 /** 44 * Hardware renderer that proxies the rendering to a render thread. Most calls 45 * are currently synchronous. 46 * 47 * The UI thread can block on the RenderThread, but RenderThread must never 48 * block on the UI thread. 49 * 50 * ThreadedRenderer creates an instance of RenderProxy. RenderProxy in turn creates 51 * and manages a CanvasContext on the RenderThread. The CanvasContext is fully managed 52 * by the lifecycle of the RenderProxy. 53 * 54 * Note that although currently the EGL context & surfaces are created & managed 55 * by the render thread, the goal is to move that into a shared structure that can 56 * be managed by both threads. EGLSurface creation & deletion should ideally be 57 * done on the UI thread and not the RenderThread to avoid stalling the 58 * RenderThread with surface buffer allocation. 59 * 60 * @hide 61 */ 62 public class ThreadedRenderer extends HardwareRenderer { 63 private static final String LOGTAG = "ThreadedRenderer"; 64 65 // Keep in sync with DrawFrameTask.h SYNC_* flags 66 // Nothing interesting to report 67 private static final int SYNC_OK = 0; 68 // Needs a ViewRoot invalidate 69 private static final int SYNC_INVALIDATE_REQUIRED = 1 << 0; 70 // Spoiler: the reward is GPU-accelerated drawing, better find that Surface! 71 private static final int SYNC_LOST_SURFACE_REWARD_IF_FOUND = 1 << 1; 72 73 private static final String[] VISUALIZERS = { 74 PROFILE_PROPERTY_VISUALIZE_BARS, 75 }; 76 77 // Size of the rendered content. 78 private int mWidth, mHeight; 79 80 // Actual size of the drawing surface. 81 private int mSurfaceWidth, mSurfaceHeight; 82 83 // Insets between the drawing surface and rendered content. These are 84 // applied as translation when updating the root render node. 85 private int mInsetTop, mInsetLeft; 86 87 // Whether the surface has insets. Used to protect opacity. 88 private boolean mHasInsets; 89 90 // Light and shadow properties specified by the theme. 91 private final float mLightY; 92 private final float mLightZ; 93 private final float mLightRadius; 94 private final int mAmbientShadowAlpha; 95 private final int mSpotShadowAlpha; 96 97 private long mNativeProxy; 98 private boolean mInitialized = false; 99 private RenderNode mRootNode; 100 private Choreographer mChoreographer; 101 private boolean mProfilingEnabled; 102 private boolean mRootNodeNeedsUpdate; 103 104 ThreadedRenderer(Context context, boolean translucent) { 105 final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0); 106 mLightY = a.getDimension(R.styleable.Lighting_lightY, 0); 107 mLightZ = a.getDimension(R.styleable.Lighting_lightZ, 0); 108 mLightRadius = a.getDimension(R.styleable.Lighting_lightRadius, 0); 109 mAmbientShadowAlpha = 110 (int) (255 * a.getFloat(R.styleable.Lighting_ambientShadowAlpha, 0) + 0.5f); 111 mSpotShadowAlpha = (int) (255 * a.getFloat(R.styleable.Lighting_spotShadowAlpha, 0) + 0.5f); 112 a.recycle(); 113 114 long rootNodePtr = nCreateRootRenderNode(); 115 mRootNode = RenderNode.adopt(rootNodePtr); 116 mRootNode.setClipToBounds(false); 117 mNativeProxy = nCreateProxy(translucent, rootNodePtr); 118 119 AtlasInitializer.sInstance.init(context, mNativeProxy); 120 121 // Setup timing 122 mChoreographer = Choreographer.getInstance(); 123 nSetFrameInterval(mNativeProxy, mChoreographer.getFrameIntervalNanos()); 124 125 loadSystemProperties(); 126 } 127 128 @Override 129 void destroy() { 130 mInitialized = false; 131 updateEnabledState(null); 132 nDestroy(mNativeProxy); 133 } 134 135 private void updateEnabledState(Surface surface) { 136 if (surface == null || !surface.isValid()) { 137 setEnabled(false); 138 } else { 139 setEnabled(mInitialized); 140 } 141 } 142 143 @Override 144 boolean initialize(Surface surface) throws OutOfResourcesException { 145 mInitialized = true; 146 updateEnabledState(surface); 147 boolean status = nInitialize(mNativeProxy, surface); 148 surface.allocateBuffers(); 149 return status; 150 } 151 152 @Override 153 void updateSurface(Surface surface) throws OutOfResourcesException { 154 updateEnabledState(surface); 155 nUpdateSurface(mNativeProxy, surface); 156 } 157 158 @Override 159 boolean pauseSurface(Surface surface) { 160 return nPauseSurface(mNativeProxy, surface); 161 } 162 163 @Override 164 void destroyHardwareResources(View view) { 165 destroyResources(view); 166 nDestroyHardwareResources(mNativeProxy); 167 } 168 169 private static void destroyResources(View view) { 170 view.destroyHardwareResources(); 171 172 if (view instanceof ViewGroup) { 173 ViewGroup group = (ViewGroup) view; 174 175 int count = group.getChildCount(); 176 for (int i = 0; i < count; i++) { 177 destroyResources(group.getChildAt(i)); 178 } 179 } 180 } 181 182 @Override 183 void invalidate(Surface surface) { 184 updateSurface(surface); 185 } 186 187 @Override 188 void detachSurfaceTexture(long hardwareLayer) { 189 nDetachSurfaceTexture(mNativeProxy, hardwareLayer); 190 } 191 192 @Override 193 void setup(int width, int height, Rect surfaceInsets) { 194 final float lightX = width / 2.0f; 195 mWidth = width; 196 mHeight = height; 197 if (surfaceInsets != null && (surfaceInsets.left != 0 || surfaceInsets.right != 0 198 || surfaceInsets.top != 0 || surfaceInsets.bottom != 0)) { 199 mHasInsets = true; 200 mInsetLeft = surfaceInsets.left; 201 mInsetTop = surfaceInsets.top; 202 mSurfaceWidth = width + mInsetLeft + surfaceInsets.right; 203 mSurfaceHeight = height + mInsetTop + surfaceInsets.bottom; 204 205 // If the surface has insets, it can't be opaque. 206 setOpaque(false); 207 } else { 208 mHasInsets = false; 209 mInsetLeft = 0; 210 mInsetTop = 0; 211 mSurfaceWidth = width; 212 mSurfaceHeight = height; 213 } 214 mRootNode.setLeftTopRightBottom(-mInsetLeft, -mInsetTop, mSurfaceWidth, mSurfaceHeight); 215 nSetup(mNativeProxy, mSurfaceWidth, mSurfaceHeight, 216 lightX, mLightY, mLightZ, mLightRadius, 217 mAmbientShadowAlpha, mSpotShadowAlpha); 218 } 219 220 @Override 221 void setOpaque(boolean opaque) { 222 nSetOpaque(mNativeProxy, opaque && !mHasInsets); 223 } 224 225 @Override 226 int getWidth() { 227 return mWidth; 228 } 229 230 @Override 231 int getHeight() { 232 return mHeight; 233 } 234 235 @Override 236 void dumpGfxInfo(PrintWriter pw, FileDescriptor fd) { 237 pw.flush(); 238 nDumpProfileInfo(mNativeProxy, fd); 239 } 240 241 private static int search(String[] values, String value) { 242 for (int i = 0; i < values.length; i++) { 243 if (values[i].equals(value)) return i; 244 } 245 return -1; 246 } 247 248 private static boolean checkIfProfilingRequested() { 249 String profiling = SystemProperties.get(HardwareRenderer.PROFILE_PROPERTY); 250 int graphType = search(VISUALIZERS, profiling); 251 return (graphType >= 0) || Boolean.parseBoolean(profiling); 252 } 253 254 @Override 255 boolean loadSystemProperties() { 256 boolean changed = nLoadSystemProperties(mNativeProxy); 257 boolean wantProfiling = checkIfProfilingRequested(); 258 if (wantProfiling != mProfilingEnabled) { 259 mProfilingEnabled = wantProfiling; 260 changed = true; 261 } 262 if (changed) { 263 invalidateRoot(); 264 } 265 return changed; 266 } 267 268 private void updateViewTreeDisplayList(View view) { 269 view.mPrivateFlags |= View.PFLAG_DRAWN; 270 view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED) 271 == View.PFLAG_INVALIDATED; 272 view.mPrivateFlags &= ~View.PFLAG_INVALIDATED; 273 view.getDisplayList(); 274 view.mRecreateDisplayList = false; 275 } 276 277 private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) { 278 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Record View#draw()"); 279 updateViewTreeDisplayList(view); 280 281 if (mRootNodeNeedsUpdate || !mRootNode.isValid()) { 282 HardwareCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight); 283 try { 284 final int saveCount = canvas.save(); 285 canvas.translate(mInsetLeft, mInsetTop); 286 callbacks.onHardwarePreDraw(canvas); 287 288 canvas.insertReorderBarrier(); 289 canvas.drawRenderNode(view.getDisplayList()); 290 canvas.insertInorderBarrier(); 291 292 callbacks.onHardwarePostDraw(canvas); 293 canvas.restoreToCount(saveCount); 294 mRootNodeNeedsUpdate = false; 295 } finally { 296 mRootNode.end(canvas); 297 } 298 } 299 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 300 } 301 302 @Override 303 void invalidateRoot() { 304 mRootNodeNeedsUpdate = true; 305 } 306 307 @Override 308 void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) { 309 attachInfo.mIgnoreDirtyState = true; 310 long frameTimeNanos = mChoreographer.getFrameTimeNanos(); 311 attachInfo.mDrawingTime = frameTimeNanos / TimeUtils.NANOS_PER_MS; 312 313 long recordDuration = 0; 314 if (mProfilingEnabled) { 315 recordDuration = System.nanoTime(); 316 } 317 318 updateRootDisplayList(view, callbacks); 319 320 if (mProfilingEnabled) { 321 recordDuration = System.nanoTime() - recordDuration; 322 } 323 324 attachInfo.mIgnoreDirtyState = false; 325 326 // register animating rendernodes which started animating prior to renderer 327 // creation, which is typical for animators started prior to first draw 328 if (attachInfo.mPendingAnimatingRenderNodes != null) { 329 final int count = attachInfo.mPendingAnimatingRenderNodes.size(); 330 for (int i = 0; i < count; i++) { 331 registerAnimatingRenderNode( 332 attachInfo.mPendingAnimatingRenderNodes.get(i)); 333 } 334 attachInfo.mPendingAnimatingRenderNodes.clear(); 335 // We don't need this anymore as subsequent calls to 336 // ViewRootImpl#attachRenderNodeAnimator will go directly to us. 337 attachInfo.mPendingAnimatingRenderNodes = null; 338 } 339 340 int syncResult = nSyncAndDrawFrame(mNativeProxy, frameTimeNanos, 341 recordDuration, view.getResources().getDisplayMetrics().density); 342 if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) { 343 setEnabled(false); 344 attachInfo.mViewRootImpl.mSurface.release(); 345 // Invalidate since we failed to draw. This should fetch a Surface 346 // if it is still needed or do nothing if we are no longer drawing 347 attachInfo.mViewRootImpl.invalidate(); 348 } 349 if ((syncResult & SYNC_INVALIDATE_REQUIRED) != 0) { 350 attachInfo.mViewRootImpl.invalidate(); 351 } 352 } 353 354 static void invokeFunctor(long functor, boolean waitForCompletion) { 355 nInvokeFunctor(functor, waitForCompletion); 356 } 357 358 @Override 359 HardwareLayer createTextureLayer() { 360 long layer = nCreateTextureLayer(mNativeProxy); 361 return HardwareLayer.adoptTextureLayer(this, layer); 362 } 363 364 @Override 365 void buildLayer(RenderNode node) { 366 nBuildLayer(mNativeProxy, node.getNativeDisplayList()); 367 } 368 369 @Override 370 boolean copyLayerInto(final HardwareLayer layer, final Bitmap bitmap) { 371 return nCopyLayerInto(mNativeProxy, 372 layer.getDeferredLayerUpdater(), bitmap.mNativeBitmap); 373 } 374 375 @Override 376 void pushLayerUpdate(HardwareLayer layer) { 377 nPushLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater()); 378 } 379 380 @Override 381 void onLayerDestroyed(HardwareLayer layer) { 382 nCancelLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater()); 383 } 384 385 @Override 386 void setName(String name) { 387 } 388 389 @Override 390 void fence() { 391 nFence(mNativeProxy); 392 } 393 394 @Override 395 void stopDrawing() { 396 nStopDrawing(mNativeProxy); 397 } 398 399 @Override 400 public void notifyFramePending() { 401 nNotifyFramePending(mNativeProxy); 402 } 403 404 @Override 405 void registerAnimatingRenderNode(RenderNode animator) { 406 nRegisterAnimatingRenderNode(mRootNode.mNativeRenderNode, animator.mNativeRenderNode); 407 } 408 409 @Override 410 protected void finalize() throws Throwable { 411 try { 412 nDeleteProxy(mNativeProxy); 413 mNativeProxy = 0; 414 } finally { 415 super.finalize(); 416 } 417 } 418 419 static void trimMemory(int level) { 420 nTrimMemory(level); 421 } 422 423 private static class AtlasInitializer { 424 static AtlasInitializer sInstance = new AtlasInitializer(); 425 426 private boolean mInitialized = false; 427 428 private AtlasInitializer() {} 429 430 synchronized void init(Context context, long renderProxy) { 431 if (mInitialized) return; 432 IBinder binder = ServiceManager.getService("assetatlas"); 433 if (binder == null) return; 434 435 IAssetAtlas atlas = IAssetAtlas.Stub.asInterface(binder); 436 try { 437 if (atlas.isCompatible(android.os.Process.myPpid())) { 438 GraphicBuffer buffer = atlas.getBuffer(); 439 if (buffer != null) { 440 long[] map = atlas.getMap(); 441 if (map != null) { 442 // TODO Remove after fixing b/15425820 443 validateMap(context, map); 444 nSetAtlas(renderProxy, buffer, map); 445 mInitialized = true; 446 } 447 // If IAssetAtlas is not the same class as the IBinder 448 // we are using a remote service and we can safely 449 // destroy the graphic buffer 450 if (atlas.getClass() != binder.getClass()) { 451 buffer.destroy(); 452 } 453 } 454 } 455 } catch (RemoteException e) { 456 Log.w(LOG_TAG, "Could not acquire atlas", e); 457 } 458 } 459 460 private static void validateMap(Context context, long[] map) { 461 Log.d("Atlas", "Validating map..."); 462 HashSet<Long> preloadedPointers = new HashSet<Long>(); 463 464 // We only care about drawables that hold bitmaps 465 final Resources resources = context.getResources(); 466 final LongSparseArray<Drawable.ConstantState> drawables = resources.getPreloadedDrawables(); 467 468 final int count = drawables.size(); 469 ArrayList<Bitmap> tmpList = new ArrayList<Bitmap>(); 470 for (int i = 0; i < count; i++) { 471 drawables.valueAt(i).addAtlasableBitmaps(tmpList); 472 for (int j = 0; j < tmpList.size(); j++) { 473 preloadedPointers.add(tmpList.get(j).mNativeBitmap); 474 } 475 tmpList.clear(); 476 } 477 478 for (int i = 0; i < map.length; i += 4) { 479 if (!preloadedPointers.contains(map[i])) { 480 Log.w("Atlas", String.format("Pointer 0x%X, not in getPreloadedDrawables?", map[i])); 481 map[i] = 0; 482 } 483 } 484 } 485 } 486 487 static native void setupShadersDiskCache(String cacheFile); 488 489 private static native void nSetAtlas(long nativeProxy, GraphicBuffer buffer, long[] map); 490 491 private static native long nCreateRootRenderNode(); 492 private static native long nCreateProxy(boolean translucent, long rootRenderNode); 493 private static native void nDeleteProxy(long nativeProxy); 494 495 private static native void nSetFrameInterval(long nativeProxy, long frameIntervalNanos); 496 private static native boolean nLoadSystemProperties(long nativeProxy); 497 498 private static native boolean nInitialize(long nativeProxy, Surface window); 499 private static native void nUpdateSurface(long nativeProxy, Surface window); 500 private static native boolean nPauseSurface(long nativeProxy, Surface window); 501 private static native void nSetup(long nativeProxy, int width, int height, 502 float lightX, float lightY, float lightZ, float lightRadius, 503 int ambientShadowAlpha, int spotShadowAlpha); 504 private static native void nSetOpaque(long nativeProxy, boolean opaque); 505 private static native int nSyncAndDrawFrame(long nativeProxy, 506 long frameTimeNanos, long recordDuration, float density); 507 private static native void nDestroy(long nativeProxy); 508 private static native void nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode); 509 510 private static native void nInvokeFunctor(long functor, boolean waitForCompletion); 511 512 private static native long nCreateTextureLayer(long nativeProxy); 513 private static native void nBuildLayer(long nativeProxy, long node); 514 private static native boolean nCopyLayerInto(long nativeProxy, long layer, long bitmap); 515 private static native void nPushLayerUpdate(long nativeProxy, long layer); 516 private static native void nCancelLayerUpdate(long nativeProxy, long layer); 517 private static native void nDetachSurfaceTexture(long nativeProxy, long layer); 518 519 private static native void nDestroyHardwareResources(long nativeProxy); 520 private static native void nTrimMemory(int level); 521 522 private static native void nFence(long nativeProxy); 523 private static native void nStopDrawing(long nativeProxy); 524 private static native void nNotifyFramePending(long nativeProxy); 525 526 private static native void nDumpProfileInfo(long nativeProxy, FileDescriptor fd); 527 } 528