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 android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.content.Context; 22 import android.content.res.TypedArray; 23 import android.graphics.Bitmap; 24 import android.graphics.Point; 25 import android.graphics.Rect; 26 import android.os.Binder; 27 import android.os.IBinder; 28 import android.os.ParcelFileDescriptor; 29 import android.os.RemoteException; 30 import android.os.ServiceManager; 31 import android.os.Trace; 32 import android.util.Log; 33 import android.view.Surface.OutOfResourcesException; 34 import android.view.View.AttachInfo; 35 36 import com.android.internal.R; 37 38 import java.io.FileDescriptor; 39 import java.io.PrintWriter; 40 import java.lang.annotation.Retention; 41 import java.lang.annotation.RetentionPolicy; 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 private static final int FLAG_DUMP_FRAMESTATS = 1 << 0; 78 private static final int FLAG_DUMP_RESET = 1 << 1; 79 80 @IntDef(flag = true, value = { 81 FLAG_DUMP_FRAMESTATS, FLAG_DUMP_RESET }) 82 @Retention(RetentionPolicy.SOURCE) 83 public @interface DumpFlags {} 84 85 // Size of the rendered content. 86 private int mWidth, mHeight; 87 88 // Actual size of the drawing surface. 89 private int mSurfaceWidth, mSurfaceHeight; 90 91 // Insets between the drawing surface and rendered content. These are 92 // applied as translation when updating the root render node. 93 private int mInsetTop, mInsetLeft; 94 95 // Whether the surface has insets. Used to protect opacity. 96 private boolean mHasInsets; 97 98 // Light and shadow properties specified by the theme. 99 private final float mLightY; 100 private final float mLightZ; 101 private final float mLightRadius; 102 private final int mAmbientShadowAlpha; 103 private final int mSpotShadowAlpha; 104 105 private long mNativeProxy; 106 private boolean mInitialized = false; 107 private RenderNode mRootNode; 108 private Choreographer mChoreographer; 109 private boolean mRootNodeNeedsUpdate; 110 ThreadedRenderer(Context context, boolean translucent)111 ThreadedRenderer(Context context, boolean translucent) { 112 final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0); 113 mLightY = a.getDimension(R.styleable.Lighting_lightY, 0); 114 mLightZ = a.getDimension(R.styleable.Lighting_lightZ, 0); 115 mLightRadius = a.getDimension(R.styleable.Lighting_lightRadius, 0); 116 mAmbientShadowAlpha = 117 (int) (255 * a.getFloat(R.styleable.Lighting_ambientShadowAlpha, 0) + 0.5f); 118 mSpotShadowAlpha = (int) (255 * a.getFloat(R.styleable.Lighting_spotShadowAlpha, 0) + 0.5f); 119 a.recycle(); 120 121 long rootNodePtr = nCreateRootRenderNode(); 122 mRootNode = RenderNode.adopt(rootNodePtr); 123 mRootNode.setClipToBounds(false); 124 mNativeProxy = nCreateProxy(translucent, rootNodePtr); 125 126 ProcessInitializer.sInstance.init(context, mNativeProxy); 127 128 loadSystemProperties(); 129 } 130 131 @Override destroy()132 void destroy() { 133 mInitialized = false; 134 updateEnabledState(null); 135 nDestroy(mNativeProxy); 136 } 137 updateEnabledState(Surface surface)138 private void updateEnabledState(Surface surface) { 139 if (surface == null || !surface.isValid()) { 140 setEnabled(false); 141 } else { 142 setEnabled(mInitialized); 143 } 144 } 145 146 @Override initialize(Surface surface)147 boolean initialize(Surface surface) throws OutOfResourcesException { 148 mInitialized = true; 149 updateEnabledState(surface); 150 boolean status = nInitialize(mNativeProxy, surface); 151 return status; 152 } 153 154 @Override updateSurface(Surface surface)155 void updateSurface(Surface surface) throws OutOfResourcesException { 156 updateEnabledState(surface); 157 nUpdateSurface(mNativeProxy, surface); 158 } 159 160 @Override pauseSurface(Surface surface)161 boolean pauseSurface(Surface surface) { 162 return nPauseSurface(mNativeProxy, surface); 163 } 164 165 @Override destroyHardwareResources(View view)166 void destroyHardwareResources(View view) { 167 destroyResources(view); 168 nDestroyHardwareResources(mNativeProxy); 169 } 170 destroyResources(View view)171 private static void destroyResources(View view) { 172 view.destroyHardwareResources(); 173 174 if (view instanceof ViewGroup) { 175 ViewGroup group = (ViewGroup) view; 176 177 int count = group.getChildCount(); 178 for (int i = 0; i < count; i++) { 179 destroyResources(group.getChildAt(i)); 180 } 181 } 182 } 183 184 @Override invalidate(Surface surface)185 void invalidate(Surface surface) { 186 updateSurface(surface); 187 } 188 189 @Override detachSurfaceTexture(long hardwareLayer)190 void detachSurfaceTexture(long hardwareLayer) { 191 nDetachSurfaceTexture(mNativeProxy, hardwareLayer); 192 } 193 194 @Override setup(int width, int height, AttachInfo attachInfo, Rect surfaceInsets)195 void setup(int width, int height, AttachInfo attachInfo, Rect surfaceInsets) { 196 mWidth = width; 197 mHeight = height; 198 199 if (surfaceInsets != null && (surfaceInsets.left != 0 || surfaceInsets.right != 0 200 || surfaceInsets.top != 0 || surfaceInsets.bottom != 0)) { 201 mHasInsets = true; 202 mInsetLeft = surfaceInsets.left; 203 mInsetTop = surfaceInsets.top; 204 mSurfaceWidth = width + mInsetLeft + surfaceInsets.right; 205 mSurfaceHeight = height + mInsetTop + surfaceInsets.bottom; 206 207 // If the surface has insets, it can't be opaque. 208 setOpaque(false); 209 } else { 210 mHasInsets = false; 211 mInsetLeft = 0; 212 mInsetTop = 0; 213 mSurfaceWidth = width; 214 mSurfaceHeight = height; 215 } 216 217 mRootNode.setLeftTopRightBottom(-mInsetLeft, -mInsetTop, mSurfaceWidth, mSurfaceHeight); 218 nSetup(mNativeProxy, mSurfaceWidth, mSurfaceHeight, mLightRadius, 219 mAmbientShadowAlpha, mSpotShadowAlpha); 220 221 setLightCenter(attachInfo); 222 } 223 224 @Override setLightCenter(AttachInfo attachInfo)225 void setLightCenter(AttachInfo attachInfo) { 226 // Adjust light position for window offsets. 227 final Point displaySize = attachInfo.mPoint; 228 attachInfo.mDisplay.getRealSize(displaySize); 229 final float lightX = displaySize.x / 2f - attachInfo.mWindowLeft; 230 final float lightY = mLightY - attachInfo.mWindowTop; 231 232 nSetLightCenter(mNativeProxy, lightX, lightY, mLightZ); 233 } 234 235 @Override setOpaque(boolean opaque)236 void setOpaque(boolean opaque) { 237 nSetOpaque(mNativeProxy, opaque && !mHasInsets); 238 } 239 240 @Override getWidth()241 int getWidth() { 242 return mWidth; 243 } 244 245 @Override getHeight()246 int getHeight() { 247 return mHeight; 248 } 249 250 @Override dumpGfxInfo(PrintWriter pw, FileDescriptor fd, String[] args)251 void dumpGfxInfo(PrintWriter pw, FileDescriptor fd, String[] args) { 252 pw.flush(); 253 int flags = 0; 254 for (int i = 0; i < args.length; i++) { 255 switch (args[i]) { 256 case "framestats": 257 flags |= FLAG_DUMP_FRAMESTATS; 258 break; 259 case "reset": 260 flags |= FLAG_DUMP_RESET; 261 break; 262 } 263 } 264 nDumpProfileInfo(mNativeProxy, fd, flags); 265 } 266 267 @Override loadSystemProperties()268 boolean loadSystemProperties() { 269 boolean changed = nLoadSystemProperties(mNativeProxy); 270 if (changed) { 271 invalidateRoot(); 272 } 273 return changed; 274 } 275 updateViewTreeDisplayList(View view)276 private void updateViewTreeDisplayList(View view) { 277 view.mPrivateFlags |= View.PFLAG_DRAWN; 278 view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED) 279 == View.PFLAG_INVALIDATED; 280 view.mPrivateFlags &= ~View.PFLAG_INVALIDATED; 281 view.updateDisplayListIfDirty(); 282 view.mRecreateDisplayList = false; 283 } 284 updateRootDisplayList(View view, HardwareDrawCallbacks callbacks)285 private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) { 286 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Record View#draw()"); 287 updateViewTreeDisplayList(view); 288 289 if (mRootNodeNeedsUpdate || !mRootNode.isValid()) { 290 DisplayListCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight); 291 try { 292 final int saveCount = canvas.save(); 293 canvas.translate(mInsetLeft, mInsetTop); 294 callbacks.onHardwarePreDraw(canvas); 295 296 canvas.insertReorderBarrier(); 297 canvas.drawRenderNode(view.updateDisplayListIfDirty()); 298 canvas.insertInorderBarrier(); 299 300 callbacks.onHardwarePostDraw(canvas); 301 canvas.restoreToCount(saveCount); 302 mRootNodeNeedsUpdate = false; 303 } finally { 304 mRootNode.end(canvas); 305 } 306 } 307 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 308 } 309 310 @Override invalidateRoot()311 void invalidateRoot() { 312 mRootNodeNeedsUpdate = true; 313 } 314 315 @Override draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks)316 void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) { 317 attachInfo.mIgnoreDirtyState = true; 318 319 final Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer; 320 choreographer.mFrameInfo.markDrawStart(); 321 322 updateRootDisplayList(view, callbacks); 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 final long[] frameInfo = choreographer.mFrameInfo.mFrameInfo; 341 int syncResult = nSyncAndDrawFrame(mNativeProxy, frameInfo, frameInfo.length); 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); 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 nSetName(mNativeProxy, name); 388 } 389 390 @Override fence()391 void fence() { 392 nFence(mNativeProxy); 393 } 394 395 @Override stopDrawing()396 void stopDrawing() { 397 nStopDrawing(mNativeProxy); 398 } 399 400 @Override notifyFramePending()401 public void notifyFramePending() { 402 nNotifyFramePending(mNativeProxy); 403 } 404 405 @Override registerAnimatingRenderNode(RenderNode animator)406 void registerAnimatingRenderNode(RenderNode animator) { 407 nRegisterAnimatingRenderNode(mRootNode.mNativeRenderNode, animator.mNativeRenderNode); 408 } 409 410 @Override finalize()411 protected void finalize() throws Throwable { 412 try { 413 nDeleteProxy(mNativeProxy); 414 mNativeProxy = 0; 415 } finally { 416 super.finalize(); 417 } 418 } 419 trimMemory(int level)420 static void trimMemory(int level) { 421 nTrimMemory(level); 422 } 423 overrideProperty(@onNull String name, @NonNull String value)424 public static void overrideProperty(@NonNull String name, @NonNull String value) { 425 if (name == null || value == null) { 426 throw new IllegalArgumentException("name and value must be non-null"); 427 } 428 nOverrideProperty(name, value); 429 } 430 dumpProfileData(byte[] data, FileDescriptor fd)431 public static void dumpProfileData(byte[] data, FileDescriptor fd) { 432 nDumpProfileData(data, fd); 433 } 434 435 private static class ProcessInitializer { 436 static ProcessInitializer sInstance = new ProcessInitializer(); 437 private static IBinder sProcToken; 438 439 private boolean mInitialized = false; 440 ProcessInitializer()441 private ProcessInitializer() {} 442 init(Context context, long renderProxy)443 synchronized void init(Context context, long renderProxy) { 444 if (mInitialized) return; 445 mInitialized = true; 446 initGraphicsStats(context, renderProxy); 447 initAssetAtlas(context, renderProxy); 448 } 449 initGraphicsStats(Context context, long renderProxy)450 private static void initGraphicsStats(Context context, long renderProxy) { 451 try { 452 IBinder binder = ServiceManager.getService("graphicsstats"); 453 if (binder == null) return; 454 IGraphicsStats graphicsStatsService = IGraphicsStats.Stub 455 .asInterface(binder); 456 sProcToken = new Binder(); 457 final String pkg = context.getApplicationInfo().packageName; 458 ParcelFileDescriptor pfd = graphicsStatsService. 459 requestBufferForProcess(pkg, sProcToken); 460 nSetProcessStatsBuffer(renderProxy, pfd.getFd()); 461 pfd.close(); 462 } catch (Throwable t) { 463 Log.w(LOG_TAG, "Could not acquire gfx stats buffer", t); 464 } 465 } 466 initAssetAtlas(Context context, long renderProxy)467 private static void initAssetAtlas(Context context, long renderProxy) { 468 IBinder binder = ServiceManager.getService("assetatlas"); 469 if (binder == null) return; 470 471 IAssetAtlas atlas = IAssetAtlas.Stub.asInterface(binder); 472 try { 473 if (atlas.isCompatible(android.os.Process.myPpid())) { 474 GraphicBuffer buffer = atlas.getBuffer(); 475 if (buffer != null) { 476 long[] map = atlas.getMap(); 477 if (map != null) { 478 nSetAtlas(renderProxy, buffer, map); 479 } 480 // If IAssetAtlas is not the same class as the IBinder 481 // we are using a remote service and we can safely 482 // destroy the graphic buffer 483 if (atlas.getClass() != binder.getClass()) { 484 buffer.destroy(); 485 } 486 } 487 } 488 } catch (RemoteException e) { 489 Log.w(LOG_TAG, "Could not acquire atlas", e); 490 } 491 } 492 } 493 setupShadersDiskCache(String cacheFile)494 static native void setupShadersDiskCache(String cacheFile); 495 nSetAtlas(long nativeProxy, GraphicBuffer buffer, long[] map)496 private static native void nSetAtlas(long nativeProxy, GraphicBuffer buffer, long[] map); nSetProcessStatsBuffer(long nativeProxy, int fd)497 private static native void nSetProcessStatsBuffer(long nativeProxy, int fd); 498 nCreateRootRenderNode()499 private static native long nCreateRootRenderNode(); nCreateProxy(boolean translucent, long rootRenderNode)500 private static native long nCreateProxy(boolean translucent, long rootRenderNode); nDeleteProxy(long nativeProxy)501 private static native void nDeleteProxy(long nativeProxy); 502 nLoadSystemProperties(long nativeProxy)503 private static native boolean nLoadSystemProperties(long nativeProxy); nSetName(long nativeProxy, String name)504 private static native void nSetName(long nativeProxy, String name); 505 nInitialize(long nativeProxy, Surface window)506 private static native boolean nInitialize(long nativeProxy, Surface window); nUpdateSurface(long nativeProxy, Surface window)507 private static native void nUpdateSurface(long nativeProxy, Surface window); nPauseSurface(long nativeProxy, Surface window)508 private static native boolean nPauseSurface(long nativeProxy, Surface window); nSetup(long nativeProxy, int width, int height, float lightRadius, int ambientShadowAlpha, int spotShadowAlpha)509 private static native void nSetup(long nativeProxy, int width, int height, 510 float lightRadius, int ambientShadowAlpha, int spotShadowAlpha); nSetLightCenter(long nativeProxy, float lightX, float lightY, float lightZ)511 private static native void nSetLightCenter(long nativeProxy, 512 float lightX, float lightY, float lightZ); nSetOpaque(long nativeProxy, boolean opaque)513 private static native void nSetOpaque(long nativeProxy, boolean opaque); nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size)514 private static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size); nDestroy(long nativeProxy)515 private static native void nDestroy(long nativeProxy); nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode)516 private static native void nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode); 517 nInvokeFunctor(long functor, boolean waitForCompletion)518 private static native void nInvokeFunctor(long functor, boolean waitForCompletion); 519 nCreateTextureLayer(long nativeProxy)520 private static native long nCreateTextureLayer(long nativeProxy); nBuildLayer(long nativeProxy, long node)521 private static native void nBuildLayer(long nativeProxy, long node); nCopyLayerInto(long nativeProxy, long layer, Bitmap bitmap)522 private static native boolean nCopyLayerInto(long nativeProxy, long layer, Bitmap bitmap); nPushLayerUpdate(long nativeProxy, long layer)523 private static native void nPushLayerUpdate(long nativeProxy, long layer); nCancelLayerUpdate(long nativeProxy, long layer)524 private static native void nCancelLayerUpdate(long nativeProxy, long layer); nDetachSurfaceTexture(long nativeProxy, long layer)525 private static native void nDetachSurfaceTexture(long nativeProxy, long layer); 526 nDestroyHardwareResources(long nativeProxy)527 private static native void nDestroyHardwareResources(long nativeProxy); nTrimMemory(int level)528 private static native void nTrimMemory(int level); nOverrideProperty(String name, String value)529 private static native void nOverrideProperty(String name, String value); 530 nFence(long nativeProxy)531 private static native void nFence(long nativeProxy); nStopDrawing(long nativeProxy)532 private static native void nStopDrawing(long nativeProxy); nNotifyFramePending(long nativeProxy)533 private static native void nNotifyFramePending(long nativeProxy); 534 nDumpProfileInfo(long nativeProxy, FileDescriptor fd, @DumpFlags int dumpFlags)535 private static native void nDumpProfileInfo(long nativeProxy, FileDescriptor fd, 536 @DumpFlags int dumpFlags); nDumpProfileData(byte[] data, FileDescriptor fd)537 private static native void nDumpProfileData(byte[] data, FileDescriptor fd); 538 } 539