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 ThreadedRenderer(Context context, boolean translucent)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 destroy()129 void destroy() { 130 mInitialized = false; 131 updateEnabledState(null); 132 nDestroy(mNativeProxy); 133 } 134 updateEnabledState(Surface surface)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 initialize(Surface surface)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 updateSurface(Surface surface)153 void updateSurface(Surface surface) throws OutOfResourcesException { 154 updateEnabledState(surface); 155 nUpdateSurface(mNativeProxy, surface); 156 } 157 158 @Override pauseSurface(Surface surface)159 boolean pauseSurface(Surface surface) { 160 return nPauseSurface(mNativeProxy, surface); 161 } 162 163 @Override destroyHardwareResources(View view)164 void destroyHardwareResources(View view) { 165 destroyResources(view); 166 nDestroyHardwareResources(mNativeProxy); 167 } 168 destroyResources(View view)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 invalidate(Surface surface)183 void invalidate(Surface surface) { 184 updateSurface(surface); 185 } 186 187 @Override detachSurfaceTexture(long hardwareLayer)188 void detachSurfaceTexture(long hardwareLayer) { 189 nDetachSurfaceTexture(mNativeProxy, hardwareLayer); 190 } 191 192 @Override setup(int width, int height, Rect surfaceInsets)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 setOpaque(boolean opaque)221 void setOpaque(boolean opaque) { 222 nSetOpaque(mNativeProxy, opaque && !mHasInsets); 223 } 224 225 @Override getWidth()226 int getWidth() { 227 return mWidth; 228 } 229 230 @Override getHeight()231 int getHeight() { 232 return mHeight; 233 } 234 235 @Override dumpGfxInfo(PrintWriter pw, FileDescriptor fd)236 void dumpGfxInfo(PrintWriter pw, FileDescriptor fd) { 237 pw.flush(); 238 nDumpProfileInfo(mNativeProxy, fd); 239 } 240 search(String[] values, String value)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 checkIfProfilingRequested()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 loadSystemProperties()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 updateViewTreeDisplayList(View view)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 updateRootDisplayList(View view, HardwareDrawCallbacks callbacks)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 invalidateRoot()303 void invalidateRoot() { 304 mRootNodeNeedsUpdate = true; 305 } 306 307 @Override draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks)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 invokeFunctor(long functor, boolean waitForCompletion)354 static void invokeFunctor(long functor, boolean waitForCompletion) { 355 nInvokeFunctor(functor, waitForCompletion); 356 } 357 358 @Override createTextureLayer()359 HardwareLayer createTextureLayer() { 360 long layer = nCreateTextureLayer(mNativeProxy); 361 return HardwareLayer.adoptTextureLayer(this, layer); 362 } 363 364 @Override buildLayer(RenderNode node)365 void buildLayer(RenderNode node) { 366 nBuildLayer(mNativeProxy, node.getNativeDisplayList()); 367 } 368 369 @Override copyLayerInto(final HardwareLayer layer, final Bitmap bitmap)370 boolean copyLayerInto(final HardwareLayer layer, final Bitmap bitmap) { 371 return nCopyLayerInto(mNativeProxy, 372 layer.getDeferredLayerUpdater(), bitmap.mNativeBitmap); 373 } 374 375 @Override pushLayerUpdate(HardwareLayer layer)376 void pushLayerUpdate(HardwareLayer layer) { 377 nPushLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater()); 378 } 379 380 @Override onLayerDestroyed(HardwareLayer layer)381 void onLayerDestroyed(HardwareLayer layer) { 382 nCancelLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater()); 383 } 384 385 @Override setName(String name)386 void setName(String name) { 387 } 388 389 @Override fence()390 void fence() { 391 nFence(mNativeProxy); 392 } 393 394 @Override stopDrawing()395 void stopDrawing() { 396 nStopDrawing(mNativeProxy); 397 } 398 399 @Override notifyFramePending()400 public void notifyFramePending() { 401 nNotifyFramePending(mNativeProxy); 402 } 403 404 @Override registerAnimatingRenderNode(RenderNode animator)405 void registerAnimatingRenderNode(RenderNode animator) { 406 nRegisterAnimatingRenderNode(mRootNode.mNativeRenderNode, animator.mNativeRenderNode); 407 } 408 409 @Override finalize()410 protected void finalize() throws Throwable { 411 try { 412 nDeleteProxy(mNativeProxy); 413 mNativeProxy = 0; 414 } finally { 415 super.finalize(); 416 } 417 } 418 trimMemory(int level)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 AtlasInitializer()428 private AtlasInitializer() {} 429 init(Context context, long renderProxy)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 validateMap(Context context, long[] map)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 setupShadersDiskCache(String cacheFile)487 static native void setupShadersDiskCache(String cacheFile); 488 nSetAtlas(long nativeProxy, GraphicBuffer buffer, long[] map)489 private static native void nSetAtlas(long nativeProxy, GraphicBuffer buffer, long[] map); 490 nCreateRootRenderNode()491 private static native long nCreateRootRenderNode(); nCreateProxy(boolean translucent, long rootRenderNode)492 private static native long nCreateProxy(boolean translucent, long rootRenderNode); nDeleteProxy(long nativeProxy)493 private static native void nDeleteProxy(long nativeProxy); 494 nSetFrameInterval(long nativeProxy, long frameIntervalNanos)495 private static native void nSetFrameInterval(long nativeProxy, long frameIntervalNanos); nLoadSystemProperties(long nativeProxy)496 private static native boolean nLoadSystemProperties(long nativeProxy); 497 nInitialize(long nativeProxy, Surface window)498 private static native boolean nInitialize(long nativeProxy, Surface window); nUpdateSurface(long nativeProxy, Surface window)499 private static native void nUpdateSurface(long nativeProxy, Surface window); nPauseSurface(long nativeProxy, Surface window)500 private static native boolean nPauseSurface(long nativeProxy, Surface window); nSetup(long nativeProxy, int width, int height, float lightX, float lightY, float lightZ, float lightRadius, int ambientShadowAlpha, int spotShadowAlpha)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); nSetOpaque(long nativeProxy, boolean opaque)504 private static native void nSetOpaque(long nativeProxy, boolean opaque); nSyncAndDrawFrame(long nativeProxy, long frameTimeNanos, long recordDuration, float density)505 private static native int nSyncAndDrawFrame(long nativeProxy, 506 long frameTimeNanos, long recordDuration, float density); nDestroy(long nativeProxy)507 private static native void nDestroy(long nativeProxy); nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode)508 private static native void nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode); 509 nInvokeFunctor(long functor, boolean waitForCompletion)510 private static native void nInvokeFunctor(long functor, boolean waitForCompletion); 511 nCreateTextureLayer(long nativeProxy)512 private static native long nCreateTextureLayer(long nativeProxy); nBuildLayer(long nativeProxy, long node)513 private static native void nBuildLayer(long nativeProxy, long node); nCopyLayerInto(long nativeProxy, long layer, long bitmap)514 private static native boolean nCopyLayerInto(long nativeProxy, long layer, long bitmap); nPushLayerUpdate(long nativeProxy, long layer)515 private static native void nPushLayerUpdate(long nativeProxy, long layer); nCancelLayerUpdate(long nativeProxy, long layer)516 private static native void nCancelLayerUpdate(long nativeProxy, long layer); nDetachSurfaceTexture(long nativeProxy, long layer)517 private static native void nDetachSurfaceTexture(long nativeProxy, long layer); 518 nDestroyHardwareResources(long nativeProxy)519 private static native void nDestroyHardwareResources(long nativeProxy); nTrimMemory(int level)520 private static native void nTrimMemory(int level); 521 nFence(long nativeProxy)522 private static native void nFence(long nativeProxy); nStopDrawing(long nativeProxy)523 private static native void nStopDrawing(long nativeProxy); nNotifyFramePending(long nativeProxy)524 private static native void nNotifyFramePending(long nativeProxy); 525 nDumpProfileInfo(long nativeProxy, FileDescriptor fd)526 private static native void nDumpProfileInfo(long nativeProxy, FileDescriptor fd); 527 } 528