CanvasContext.cpp revision 3e8249568cc428296ac76c7ddce3f0382d40fe5b
1/* 2 * Copyright (C) 2014 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#include "CanvasContext.h" 18 19#include <private/hwui/DrawGlInfo.h> 20#include <strings.h> 21 22#include "EglManager.h" 23#include "RenderThread.h" 24#include "../Caches.h" 25#include "../DeferredLayerUpdater.h" 26#include "../RenderState.h" 27#include "../LayerRenderer.h" 28#include "../OpenGLRenderer.h" 29#include "../Stencil.h" 30 31#define TRIM_MEMORY_COMPLETE 80 32#define TRIM_MEMORY_UI_HIDDEN 20 33 34namespace android { 35namespace uirenderer { 36namespace renderthread { 37 38CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode) 39 : mRenderThread(thread) 40 , mEglManager(thread.eglManager()) 41 , mEglSurface(EGL_NO_SURFACE) 42 , mDirtyRegionsEnabled(false) 43 , mOpaque(!translucent) 44 , mCanvas(NULL) 45 , mHaveNewSurface(false) 46 , mRootRenderNode(rootRenderNode) { 47} 48 49CanvasContext::~CanvasContext() { 50 destroyCanvasAndSurface(); 51 mRenderThread.removeFrameCallback(this); 52} 53 54void CanvasContext::destroyCanvasAndSurface() { 55 if (mCanvas) { 56 delete mCanvas; 57 mCanvas = 0; 58 } 59 setSurface(NULL); 60} 61 62void CanvasContext::setSurface(ANativeWindow* window) { 63 mNativeWindow = window; 64 65 if (mEglSurface != EGL_NO_SURFACE) { 66 mEglManager.destroySurface(mEglSurface); 67 mEglSurface = EGL_NO_SURFACE; 68 } 69 70 if (window) { 71 mEglSurface = mEglManager.createSurface(window); 72 } 73 74 if (mEglSurface != EGL_NO_SURFACE) { 75 mDirtyRegionsEnabled = mEglManager.enableDirtyRegions(mEglSurface); 76 mHaveNewSurface = true; 77 makeCurrent(); 78 } else { 79 mRenderThread.removeFrameCallback(this); 80 } 81} 82 83void CanvasContext::swapBuffers() { 84 mEglManager.swapBuffers(mEglSurface); 85 mHaveNewSurface = false; 86} 87 88void CanvasContext::requireSurface() { 89 LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE, 90 "requireSurface() called but no surface set!"); 91 makeCurrent(); 92} 93 94bool CanvasContext::initialize(ANativeWindow* window) { 95 if (mCanvas) return false; 96 setSurface(window); 97 mCanvas = new OpenGLRenderer(mRenderThread.renderState()); 98 mCanvas->initProperties(); 99 return true; 100} 101 102void CanvasContext::updateSurface(ANativeWindow* window) { 103 setSurface(window); 104} 105 106void CanvasContext::pauseSurface(ANativeWindow* window) { 107 // TODO: For now we just need a fence, in the future suspend any animations 108 // and such to prevent from trying to render into this surface 109} 110 111void CanvasContext::setup(int width, int height, const Vector3& lightCenter, float lightRadius, 112 uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) { 113 if (!mCanvas) return; 114 mCanvas->setViewport(width, height); 115 mCanvas->initLight(lightCenter, lightRadius, ambientShadowAlpha, spotShadowAlpha); 116} 117 118void CanvasContext::setOpaque(bool opaque) { 119 mOpaque = opaque; 120} 121 122void CanvasContext::makeCurrent() { 123 // TODO: Figure out why this workaround is needed, see b/13913604 124 // In the meantime this matches the behavior of GLRenderer, so it is not a regression 125 mHaveNewSurface |= mEglManager.makeCurrent(mEglSurface); 126} 127 128void CanvasContext::processLayerUpdate(DeferredLayerUpdater* layerUpdater) { 129 bool success = layerUpdater->apply(); 130 LOG_ALWAYS_FATAL_IF(!success, "Failed to update layer!"); 131 if (layerUpdater->backingLayer()->deferredUpdateScheduled) { 132 mCanvas->pushLayerUpdate(layerUpdater->backingLayer()); 133 } 134} 135 136void CanvasContext::prepareTree(TreeInfo& info) { 137 mRenderThread.removeFrameCallback(this); 138 139 info.frameTimeMs = mRenderThread.timeLord().frameTimeMs(); 140 info.damageAccumulator = &mDamageAccumulator; 141 info.renderer = mCanvas; 142 mRootRenderNode->prepareTree(info); 143 144 int runningBehind = 0; 145 // TODO: This query is moderately expensive, investigate adding some sort 146 // of fast-path based off when we last called eglSwapBuffers() as well as 147 // last vsync time. Or something. 148 mNativeWindow->query(mNativeWindow.get(), 149 NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind); 150 info.out.canDrawThisFrame = !runningBehind; 151 152 if (info.out.hasAnimations || !info.out.canDrawThisFrame) { 153 if (!info.out.requiresUiRedraw) { 154 // If animationsNeedsRedraw is set don't bother posting for an RT anim 155 // as we will just end up fighting the UI thread. 156 mRenderThread.postFrameCallback(this); 157 } 158 } 159} 160 161void CanvasContext::stopDrawing() { 162 mRenderThread.removeFrameCallback(this); 163} 164 165void CanvasContext::notifyFramePending() { 166 ATRACE_CALL(); 167 mRenderThread.pushBackFrameCallback(this); 168} 169 170void CanvasContext::draw() { 171 LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE, 172 "drawRenderNode called on a context with no canvas or surface!"); 173 174 profiler().markPlaybackStart(); 175 176 SkRect dirty; 177 mDamageAccumulator.finish(&dirty); 178 179 EGLint width, height; 180 mEglManager.beginFrame(mEglSurface, &width, &height); 181 if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) { 182 mCanvas->setViewport(width, height); 183 dirty.setEmpty(); 184 } else if (!mDirtyRegionsEnabled || mHaveNewSurface) { 185 dirty.setEmpty(); 186 } else { 187 if (!dirty.isEmpty() && !dirty.intersect(0, 0, width, height)) { 188 ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?", 189 SK_RECT_ARGS(dirty), width, height); 190 dirty.setEmpty(); 191 } 192 profiler().unionDirty(&dirty); 193 } 194 195 status_t status; 196 if (!dirty.isEmpty()) { 197 status = mCanvas->prepareDirty(dirty.fLeft, dirty.fTop, 198 dirty.fRight, dirty.fBottom, mOpaque); 199 } else { 200 status = mCanvas->prepare(mOpaque); 201 } 202 203 Rect outBounds; 204 status |= mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds); 205 206 profiler().draw(mCanvas); 207 208 mCanvas->finish(); 209 210 profiler().markPlaybackEnd(); 211 212 if (status & DrawGlInfo::kStatusDrew) { 213 swapBuffers(); 214 } 215 216 profiler().finishFrame(); 217} 218 219// Called by choreographer to do an RT-driven animation 220void CanvasContext::doFrame() { 221 if (CC_UNLIKELY(!mCanvas || mEglSurface == EGL_NO_SURFACE)) { 222 return; 223 } 224 225 ATRACE_CALL(); 226 227 profiler().startFrame(); 228 229 TreeInfo info(TreeInfo::MODE_RT_ONLY, mRenderThread.renderState()); 230 prepareTree(info); 231 if (info.out.canDrawThisFrame) { 232 draw(); 233 } 234} 235 236void CanvasContext::invokeFunctor(RenderThread& thread, Functor* functor) { 237 ATRACE_CALL(); 238 DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext; 239 if (thread.eglManager().hasEglContext()) { 240 thread.eglManager().requireGlContext(); 241 mode = DrawGlInfo::kModeProcess; 242 } 243 244 thread.renderState().invokeFunctor(functor, mode, NULL); 245} 246 247void CanvasContext::buildLayer(RenderNode* node) { 248 ATRACE_CALL(); 249 if (!mEglManager.hasEglContext() || !mCanvas) { 250 return; 251 } 252 requireGlContext(); 253 // buildLayer() will leave the tree in an unknown state, so we must stop drawing 254 stopDrawing(); 255 256 TreeInfo info(TreeInfo::MODE_FULL, mRenderThread.renderState()); 257 info.frameTimeMs = mRenderThread.timeLord().frameTimeMs(); 258 info.damageAccumulator = &mDamageAccumulator; 259 info.renderer = mCanvas; 260 node->prepareTree(info); 261 SkRect ignore; 262 mDamageAccumulator.finish(&ignore); 263 // Tickle the GENERIC property on node to mark it as dirty for damaging 264 // purposes when the frame is actually drawn 265 node->setPropertyFieldsDirty(RenderNode::GENERIC); 266 267 mCanvas->flushLayerUpdates(); 268} 269 270bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) { 271 requireGlContext(); 272 layer->apply(); 273 return LayerRenderer::copyLayer(mRenderThread.renderState(), layer->backingLayer(), bitmap); 274} 275 276void CanvasContext::destroyHardwareResources() { 277 stopDrawing(); 278 if (mEglManager.hasEglContext()) { 279 requireGlContext(); 280 mRootRenderNode->destroyHardwareResources(); 281 Caches::getInstance().flush(Caches::kFlushMode_Layers); 282 } 283} 284 285void CanvasContext::trimMemory(RenderThread& thread, int level) { 286 // No context means nothing to free 287 if (!thread.eglManager().hasEglContext()) return; 288 289 thread.eglManager().requireGlContext(); 290 if (level >= TRIM_MEMORY_COMPLETE) { 291 Caches::getInstance().flush(Caches::kFlushMode_Full); 292 thread.eglManager().destroy(); 293 } else if (level >= TRIM_MEMORY_UI_HIDDEN) { 294 Caches::getInstance().flush(Caches::kFlushMode_Moderate); 295 } 296} 297 298void CanvasContext::runWithGlContext(RenderTask* task) { 299 requireGlContext(); 300 task->run(); 301} 302 303Layer* CanvasContext::createRenderLayer(int width, int height) { 304 requireSurface(); 305 return LayerRenderer::createRenderLayer(mRenderThread.renderState(), width, height); 306} 307 308Layer* CanvasContext::createTextureLayer() { 309 requireSurface(); 310 return LayerRenderer::createTextureLayer(mRenderThread.renderState()); 311} 312 313void CanvasContext::requireGlContext() { 314 mEglManager.requireGlContext(); 315} 316 317void CanvasContext::setTextureAtlas(RenderThread& thread, 318 const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize) { 319 thread.eglManager().setTextureAtlas(buffer, map, mapSize); 320} 321 322} /* namespace renderthread */ 323} /* namespace uirenderer */ 324} /* namespace android */ 325