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