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