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