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