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