diff options
Diffstat (limited to 'libs')
-rw-r--r-- | libs/hwui/Android.mk | 1 | ||||
-rw-r--r-- | libs/hwui/DeferredLayerUpdater.cpp | 17 | ||||
-rw-r--r-- | libs/hwui/DeferredLayerUpdater.h | 14 | ||||
-rw-r--r-- | libs/hwui/DrawProfiler.cpp | 261 | ||||
-rw-r--r-- | libs/hwui/DrawProfiler.h | 96 | ||||
-rw-r--r-- | libs/hwui/Properties.h | 30 | ||||
-rw-r--r-- | libs/hwui/RenderNode.cpp | 11 | ||||
-rw-r--r-- | libs/hwui/RenderNode.h | 1 | ||||
-rw-r--r-- | libs/hwui/renderthread/CanvasContext.cpp | 42 | ||||
-rw-r--r-- | libs/hwui/renderthread/CanvasContext.h | 11 | ||||
-rw-r--r-- | libs/hwui/renderthread/DrawFrameTask.cpp | 40 | ||||
-rw-r--r-- | libs/hwui/renderthread/DrawFrameTask.h | 18 | ||||
-rw-r--r-- | libs/hwui/renderthread/RenderProxy.cpp | 58 | ||||
-rw-r--r-- | libs/hwui/renderthread/RenderProxy.h | 9 |
14 files changed, 528 insertions, 81 deletions
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 2cadf09..442f327 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -23,6 +23,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) DisplayListLogBuffer.cpp \ DisplayListRenderer.cpp \ Dither.cpp \ + DrawProfiler.cpp \ Extensions.cpp \ FboCache.cpp \ GradientCache.cpp \ diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp index 285c8c3..97e9bf6 100644 --- a/libs/hwui/DeferredLayerUpdater.cpp +++ b/libs/hwui/DeferredLayerUpdater.cpp @@ -22,14 +22,19 @@ namespace android { namespace uirenderer { -DeferredLayerUpdater::DeferredLayerUpdater(Layer* layer, OpenGLRenderer* renderer) +static void defaultLayerDestroyer(Layer* layer) { + Caches::getInstance().resourceCache.decrementRefcount(layer); +} + +DeferredLayerUpdater::DeferredLayerUpdater(Layer* layer, LayerDestroyer destroyer) : mDisplayList(0) , mSurfaceTexture(0) , mTransform(0) , mNeedsGLContextAttach(false) , mUpdateTexImage(false) , mLayer(layer) - , mCaches(Caches::getInstance()) { + , mCaches(Caches::getInstance()) + , mDestroyer(destroyer) { mWidth = mLayer->layer.getWidth(); mHeight = mLayer->layer.getHeight(); mBlend = mLayer->isBlend(); @@ -37,14 +42,16 @@ DeferredLayerUpdater::DeferredLayerUpdater(Layer* layer, OpenGLRenderer* rendere mAlpha = mLayer->getAlpha(); mMode = mLayer->getMode(); mDirtyRect.setEmpty(); + + if (!mDestroyer) { + mDestroyer = defaultLayerDestroyer; + } } DeferredLayerUpdater::~DeferredLayerUpdater() { SkSafeUnref(mColorFilter); setTransform(0); - if (mLayer) { - mCaches.resourceCache.decrementRefcount(mLayer); - } + mDestroyer(mLayer); } void DeferredLayerUpdater::setPaint(const SkPaint* paint) { diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h index cc62caa..b7cfe80 100644 --- a/libs/hwui/DeferredLayerUpdater.h +++ b/libs/hwui/DeferredLayerUpdater.h @@ -30,13 +30,15 @@ namespace android { namespace uirenderer { +typedef void (*LayerDestroyer)(Layer* layer); + // Container to hold the properties a layer should be set to at the start // of a render pass -class DeferredLayerUpdater { +class DeferredLayerUpdater : public VirtualLightRefBase { public: // Note that DeferredLayerUpdater assumes it is taking ownership of the layer // and will not call incrementRef on it as a result. - ANDROID_API DeferredLayerUpdater(Layer* layer, OpenGLRenderer* renderer = 0); + ANDROID_API DeferredLayerUpdater(Layer* layer, LayerDestroyer = 0); ANDROID_API ~DeferredLayerUpdater(); ANDROID_API bool setSize(uint32_t width, uint32_t height) { @@ -83,12 +85,6 @@ public: return mLayer; } - ANDROID_API Layer* detachBackingLayer() { - Layer* layer = mLayer; - mLayer = 0; - return layer; - } - private: // Generic properties uint32_t mWidth; @@ -111,6 +107,8 @@ private: Layer* mLayer; Caches& mCaches; + LayerDestroyer mDestroyer; + void doUpdateTexImage(); }; diff --git a/libs/hwui/DrawProfiler.cpp b/libs/hwui/DrawProfiler.cpp new file mode 100644 index 0000000..971a66e --- /dev/null +++ b/libs/hwui/DrawProfiler.cpp @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "DrawProfiler.h" + +#include <cutils/compiler.h> + +#include "OpenGLRenderer.h" +#include "Properties.h" + +#define DEFAULT_MAX_FRAMES 128 + +#define RETURN_IF_DISABLED() if (CC_LIKELY(mType == kNone)) return + +#define NANOS_TO_MILLIS_FLOAT(nanos) ((nanos) * 0.000001f) + +#define PROFILE_DRAW_WIDTH 3 +#define PROFILE_DRAW_THRESHOLD_STROKE_WIDTH 2 +#define PROFILE_DRAW_DP_PER_MS 7 + +// Number of floats we want to display from FrameTimingData +// If this is changed make sure to update the indexes below +#define NUM_ELEMENTS 4 + +#define RECORD_INDEX 0 +#define PREPARE_INDEX 1 +#define PLAYBACK_INDEX 2 +#define SWAPBUFFERS_INDEX 3 + +// Must be NUM_ELEMENTS in size +static const SkColor ELEMENT_COLORS[] = { 0xcf3e66cc, 0xcf8f00ff, 0xcfdc3912, 0xcfe69800 }; +static const SkColor CURRENT_FRAME_COLOR = 0xcf5faa4d; +static const SkColor THRESHOLD_COLOR = 0xff5faa4d; + +// We could get this from TimeLord and use the actual frame interval, but +// this is good enough +#define FRAME_THRESHOLD 16 + +namespace android { +namespace uirenderer { + +static int dpToPx(int dp, float density) { + return (int) (dp * density + 0.5f); +} + +DrawProfiler::DrawProfiler() + : mType(kNone) + , mDensity(0) + , mData(NULL) + , mDataSize(0) + , mCurrentFrame(-1) + , mPreviousTime(0) + , mVerticalUnit(0) + , mHorizontalUnit(0) + , mThresholdStroke(0) { + setDensity(1); +} + +DrawProfiler::~DrawProfiler() { + destroyData(); +} + +void DrawProfiler::setDensity(float density) { + if (CC_UNLIKELY(mDensity != density)) { + mDensity = density; + mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density); + mHorizontalUnit = dpToPx(PROFILE_DRAW_WIDTH, density); + mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density); + } +} + +void DrawProfiler::startFrame(nsecs_t recordDurationNanos) { + RETURN_IF_DISABLED(); + mData[mCurrentFrame].record = NANOS_TO_MILLIS_FLOAT(recordDurationNanos); + mPreviousTime = systemTime(CLOCK_MONOTONIC); +} + +void DrawProfiler::markPlaybackStart() { + RETURN_IF_DISABLED(); + nsecs_t now = systemTime(CLOCK_MONOTONIC); + mData[mCurrentFrame].prepare = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime); + mPreviousTime = now; +} + +void DrawProfiler::markPlaybackEnd() { + RETURN_IF_DISABLED(); + nsecs_t now = systemTime(CLOCK_MONOTONIC); + mData[mCurrentFrame].playback = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime); + mPreviousTime = now; +} + +void DrawProfiler::finishFrame() { + RETURN_IF_DISABLED(); + nsecs_t now = systemTime(CLOCK_MONOTONIC); + mData[mCurrentFrame].swapBuffers = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime); + mPreviousTime = now; + mCurrentFrame = (mCurrentFrame + 1) % mDataSize; +} + +void DrawProfiler::unionDirty(Rect* dirty) { + RETURN_IF_DISABLED(); + // Not worth worrying about minimizing the dirty region for debugging, so just + // dirty the entire viewport. + if (dirty) { + dirty->setEmpty(); + } +} + +void DrawProfiler::draw(OpenGLRenderer* canvas) { + if (CC_LIKELY(mType != kBars)) { + return; + } + + prepareShapes(canvas->getViewportHeight()); + drawGraph(canvas); + drawCurrentFrame(canvas); + drawThreshold(canvas); +} + +void DrawProfiler::createData() { + if (mData) return; + + mDataSize = property_get_int32(PROPERTY_PROFILE_MAXFRAMES, DEFAULT_MAX_FRAMES); + if (mDataSize <= 0) mDataSize = 1; + if (mDataSize > 4096) mDataSize = 4096; // Reasonable maximum + mData = (FrameTimingData*) calloc(mDataSize, sizeof(FrameTimingData)); + mRects = new float*[NUM_ELEMENTS]; + for (int i = 0; i < NUM_ELEMENTS; i++) { + // 4 floats per rect + mRects[i] = (float*) calloc(mDataSize, 4 * sizeof(float)); + } + mCurrentFrame = 0; +} + +void DrawProfiler::destroyData() { + delete mData; + mData = NULL; +} + +void DrawProfiler::addRect(Rect& r, float data, float* shapeOutput) { + r.top = r.bottom - (data * mVerticalUnit); + shapeOutput[0] = r.left; + shapeOutput[1] = r.top; + shapeOutput[2] = r.right; + shapeOutput[3] = r.bottom; + r.bottom = r.top; +} + +void DrawProfiler::prepareShapes(const int baseline) { + Rect r; + r.right = mHorizontalUnit; + for (int i = 0; i < mDataSize; i++) { + const int shapeIndex = i * 4; + r.bottom = baseline; + addRect(r, mData[i].record, mRects[RECORD_INDEX] + shapeIndex); + addRect(r, mData[i].prepare, mRects[PREPARE_INDEX] + shapeIndex); + addRect(r, mData[i].playback, mRects[PLAYBACK_INDEX] + shapeIndex); + addRect(r, mData[i].swapBuffers, mRects[SWAPBUFFERS_INDEX] + shapeIndex); + r.translate(mHorizontalUnit, 0); + } +} + +void DrawProfiler::drawGraph(OpenGLRenderer* canvas) { + SkPaint paint; + for (int i = 0; i < NUM_ELEMENTS; i++) { + paint.setColor(ELEMENT_COLORS[i]); + canvas->drawRects(mRects[i], mDataSize * 4, &paint); + } +} + +void DrawProfiler::drawCurrentFrame(OpenGLRenderer* canvas) { + // This draws a solid rect over the entirety of the current frame's shape + // To do so we use the bottom of mRects[0] and the top of mRects[NUM_ELEMENTS-1] + // which will therefore fully overlap the previously drawn rects + SkPaint paint; + paint.setColor(CURRENT_FRAME_COLOR); + const int i = mCurrentFrame * 4; + canvas->drawRect(mRects[0][i], mRects[NUM_ELEMENTS-1][i+1], mRects[0][i+2], + mRects[0][i+3], &paint); +} + +void DrawProfiler::drawThreshold(OpenGLRenderer* canvas) { + SkPaint paint; + paint.setColor(THRESHOLD_COLOR); + paint.setStrokeWidth(mThresholdStroke); + + float pts[4]; + pts[0] = 0.0f; + pts[1] = pts[3] = canvas->getViewportHeight() - (FRAME_THRESHOLD * mVerticalUnit); + pts[2] = canvas->getViewportWidth(); + canvas->drawLines(pts, 4, &paint); +} + +DrawProfiler::ProfileType DrawProfiler::loadRequestedProfileType() { + ProfileType type = kNone; + char buf[PROPERTY_VALUE_MAX] = {'\0',}; + if (property_get(PROPERTY_PROFILE, buf, "") > 0) { + if (!strcmp(buf, PROPERTY_PROFILE_VISUALIZE_BARS)) { + type = kBars; + } else if (!strcmp(buf, "true")) { + type = kConsole; + } + } + return type; +} + +bool DrawProfiler::loadSystemProperties() { + ProfileType newType = loadRequestedProfileType(); + if (newType != mType) { + mType = newType; + if (mType == kNone) { + destroyData(); + } else { + createData(); + } + return true; + } + return false; +} + +void DrawProfiler::dumpData(int fd) { + RETURN_IF_DISABLED(); + + // This method logs the last N frames (where N is <= mDataSize) since the + // last call to dumpData(). In other words if there's a dumpData(), draw frame, + // dumpData(), the last dumpData() should only log 1 frame. + + const FrameTimingData emptyData = {0, 0, 0, 0}; + + FILE *file = fdopen(fd, "a"); + fprintf(file, "\n\tDraw\tPrepare\tProcess\tExecute\n"); + + for (int frameOffset = 1; frameOffset <= mDataSize; frameOffset++) { + int i = (mCurrentFrame + frameOffset) % mDataSize; + if (!memcmp(mData + i, &emptyData, sizeof(FrameTimingData))) { + continue; + } + fprintf(file, "\t%3.2f\t%3.2f\t%3.2f\t%3.2f\n", + mData[i].record, mData[i].prepare, mData[i].playback, mData[i].swapBuffers); + } + // reset the buffer + memset(mData, 0, sizeof(FrameTimingData) * mDataSize); + mCurrentFrame = 0; + + fflush(file); +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/DrawProfiler.h b/libs/hwui/DrawProfiler.h new file mode 100644 index 0000000..c1aa1c6 --- /dev/null +++ b/libs/hwui/DrawProfiler.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef DRAWPROFILER_H +#define DRAWPROFILER_H + +#include <utils/Timers.h> +#include "Rect.h" + +namespace android { +namespace uirenderer { + +class OpenGLRenderer; + +class DrawProfiler { +public: + DrawProfiler(); + ~DrawProfiler(); + + bool loadSystemProperties(); + void setDensity(float density); + + void startFrame(nsecs_t recordDurationNanos = 0); + void markPlaybackStart(); + void markPlaybackEnd(); + void finishFrame(); + + void unionDirty(Rect* dirty); + void draw(OpenGLRenderer* canvas); + + void dumpData(int fd); + +private: + enum ProfileType { + kNone, + kConsole, + kBars, + }; + + typedef struct { + float record; + float prepare; + float playback; + float swapBuffers; + } FrameTimingData; + + void createData(); + void destroyData(); + + void addRect(Rect& r, float data, float* shapeOutput); + void prepareShapes(const int baseline); + void drawGraph(OpenGLRenderer* canvas); + void drawCurrentFrame(OpenGLRenderer* canvas); + void drawThreshold(OpenGLRenderer* canvas); + + ProfileType loadRequestedProfileType(); + + ProfileType mType; + float mDensity; + + FrameTimingData* mData; + int mDataSize; + + int mCurrentFrame; + nsecs_t mPreviousTime; + + int mVerticalUnit; + int mHorizontalUnit; + int mThresholdStroke; + + /* + * mRects represents an array of rect shapes, divided into NUM_ELEMENTS + * groups such that each group is drawn with the same paint. + * For example mRects[0] is the array of rect floats suitable for + * OpenGLRenderer:drawRects() that makes up all the FrameTimingData:record + * information. + */ + float** mRects; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* DRAWPROFILER_H */ diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 20b8f2f..12241b8 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -89,6 +89,36 @@ enum DebugLevel { #define PROPERTY_DEBUG_NV_PROFILING "debug.hwui.nv_profiling" /** + * System property used to enable or disable hardware rendering profiling. + * The default value of this property is assumed to be false. + * + * When profiling is enabled, the adb shell dumpsys gfxinfo command will + * output extra information about the time taken to execute by the last + * frames. + * + * Possible values: + * "true", to enable profiling + * "visual_bars", to enable profiling and visualize the results on screen + * "false", to disable profiling + */ +#define PROPERTY_PROFILE "debug.hwui.profile" +#define PROPERTY_PROFILE_VISUALIZE_BARS "visual_bars" + +/** + * System property used to specify the number of frames to be used + * when doing hardware rendering profiling. + * The default value of this property is #PROFILE_MAX_FRAMES. + * + * When profiling is enabled, the adb shell dumpsys gfxinfo command will + * output extra information about the time taken to execute by the last + * frames. + * + * Possible values: + * "60", to set the limit of frames to 60 + */ +#define PROPERTY_PROFILE_MAXFRAMES "debug.hwui.profile.maxframes" + +/** * Used to enable/disable non-rectangular clipping debugging. * * The accepted values are: diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index df74f31..d964efc 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -93,6 +93,17 @@ void RenderNode::output(uint32_t level) { ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, getName()); } +int RenderNode::getDebugSize() { + int size = sizeof(RenderNode); + if (mStagingDisplayListData) { + size += mStagingDisplayListData->allocator.usedSize(); + } + if (mDisplayListData && mDisplayListData != mStagingDisplayListData) { + size += mDisplayListData->allocator.usedSize(); + } + return size; +} + void RenderNode::prepareTree(TreeInfo& info) { ATRACE_CALL(); diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index 1811a7b..1a5377b 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -119,6 +119,7 @@ public: void replayNodeInParent(ReplayStateStruct& replayStruct, const int level); ANDROID_API void output(uint32_t level = 1); + ANDROID_API int getDebugSize(); bool isRenderable() const { return mDisplayListData && mDisplayListData->hasDrawOps; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 160fbea..9ebee1d 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -31,7 +31,6 @@ #define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions" #define GLES_VERSION 2 -#define USE_TEXTURE_ATLAS false // Android-specific addition that is used to show when frames began in systrace EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface); @@ -229,7 +228,7 @@ void GlobalContext::setTextureAtlas(const sp<GraphicBuffer>& buffer, } void GlobalContext::initAtlas() { - if (USE_TEXTURE_ATLAS) { + if (mAtlasBuffer.get()) { Caches::getInstance().assetAtlas.init(mAtlasBuffer, mAtlasMap, mAtlasMapSize); } } @@ -428,27 +427,11 @@ void CanvasContext::makeCurrent() { mHaveNewSurface |= mGlobalContext->makeCurrent(mEglSurface); } -void CanvasContext::prepareDraw(const Vector<DeferredLayerUpdater*>* layerUpdaters, - TreeInfo& info) { - LOG_ALWAYS_FATAL_IF(!mCanvas, "Cannot prepareDraw without a canvas!"); - makeCurrent(); - - processLayerUpdates(layerUpdaters, info); - if (info.out.hasAnimations) { - // TODO: Uh... crap? - } - prepareTree(info); -} - -void CanvasContext::processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters, - TreeInfo& info) { - for (size_t i = 0; i < layerUpdaters->size(); i++) { - DeferredLayerUpdater* update = layerUpdaters->itemAt(i); - bool success = update->apply(info); - LOG_ALWAYS_FATAL_IF(!success, "Failed to update layer!"); - if (update->backingLayer()->deferredUpdateScheduled) { - mCanvas->pushLayerUpdate(update->backingLayer()); - } +void CanvasContext::processLayerUpdate(DeferredLayerUpdater* layerUpdater, TreeInfo& info) { + bool success = layerUpdater->apply(info); + LOG_ALWAYS_FATAL_IF(!success, "Failed to update layer!"); + if (layerUpdater->backingLayer()->deferredUpdateScheduled) { + mCanvas->pushLayerUpdate(layerUpdater->backingLayer()); } } @@ -486,6 +469,8 @@ void CanvasContext::draw(Rect* dirty) { LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE, "drawDisplayList called on a context with no canvas or surface!"); + profiler().markPlaybackStart(); + EGLint width, height; mGlobalContext->beginFrame(mEglSurface, &width, &height); if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) { @@ -493,6 +478,8 @@ void CanvasContext::draw(Rect* dirty) { dirty = NULL; } else if (!mDirtyRegionsEnabled || mHaveNewSurface) { dirty = NULL; + } else { + profiler().unionDirty(dirty); } status_t status; @@ -506,14 +493,17 @@ void CanvasContext::draw(Rect* dirty) { Rect outBounds; status |= mCanvas->drawDisplayList(mRootRenderNode.get(), outBounds); - // TODO: Draw debug info - // TODO: Performance tracking + profiler().draw(mCanvas); mCanvas->finish(); + profiler().markPlaybackEnd(); + if (status & DrawGlInfo::kStatusDrew) { swapBuffers(); } + + profiler().finishFrame(); } // Called by choreographer to do an RT-driven animation @@ -524,6 +514,8 @@ void CanvasContext::doFrame() { ATRACE_CALL(); + profiler().startFrame(); + TreeInfo info; info.evaluateAnimations = true; info.performStagingPush = false; diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index da85d44..00c5bf0 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -23,6 +23,7 @@ #include <utils/Functor.h> #include <utils/Vector.h> +#include "../DrawProfiler.h" #include "../RenderNode.h" #include "RenderTask.h" #include "RenderThread.h" @@ -54,7 +55,8 @@ public: void setup(int width, int height, const Vector3& lightCenter, float lightRadius); void setOpaque(bool opaque); void makeCurrent(); - void prepareDraw(const Vector<DeferredLayerUpdater*>* layerUpdaters, TreeInfo& info); + void processLayerUpdate(DeferredLayerUpdater* layerUpdater, TreeInfo& info); + void prepareTree(TreeInfo& info); void draw(Rect* dirty); void destroyCanvasAndSurface(); @@ -77,12 +79,11 @@ public: void notifyFramePending(); + DrawProfiler& profiler() { return mProfiler; } + private: friend class RegisterFrameCallbackTask; - void processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters, TreeInfo& info); - void prepareTree(TreeInfo& info); - void setSurface(ANativeWindow* window); void swapBuffers(); void requireSurface(); @@ -100,6 +101,8 @@ private: bool mHaveNewSurface; const sp<RenderNode> mRootRenderNode; + + DrawProfiler mProfiler; }; } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp index ee3e059..61d67ca 100644 --- a/libs/hwui/renderthread/DrawFrameTask.cpp +++ b/libs/hwui/renderthread/DrawFrameTask.cpp @@ -21,6 +21,7 @@ #include <utils/Log.h> #include <utils/Trace.h> +#include "../DeferredLayerUpdater.h" #include "../DisplayList.h" #include "../RenderNode.h" #include "CanvasContext.h" @@ -34,6 +35,8 @@ DrawFrameTask::DrawFrameTask() : mRenderThread(NULL) , mContext(NULL) , mFrameTimeNanos(0) + , mRecordDurationNanos(0) + , mDensity(1.0f) // safe enough default , mSyncResult(kSync_OK) { } @@ -45,17 +48,22 @@ void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context) { mContext = context; } -void DrawFrameTask::addLayer(DeferredLayerUpdater* layer) { - LOG_ALWAYS_FATAL_IF(!mContext, "Lifecycle violation, there's no context to addLayer with!"); +void DrawFrameTask::pushLayerUpdate(DeferredLayerUpdater* layer) { + LOG_ALWAYS_FATAL_IF(!mContext, "Lifecycle violation, there's no context to pushLayerUpdate with!"); - mLayers.push(layer); + for (size_t i = 0; i < mLayers.size(); i++) { + if (mLayers[i].get() == layer) { + return; + } + } + mLayers.push_back(layer); } -void DrawFrameTask::removeLayer(DeferredLayerUpdater* layer) { +void DrawFrameTask::removeLayerUpdate(DeferredLayerUpdater* layer) { for (size_t i = 0; i < mLayers.size(); i++) { - if (mLayers[i] == layer) { - mLayers.removeAt(i); - break; + if (mLayers[i].get() == layer) { + mLayers.erase(mLayers.begin() + i); + return; } } } @@ -64,15 +72,17 @@ void DrawFrameTask::setDirty(int left, int top, int right, int bottom) { mDirty.set(left, top, right, bottom); } -int DrawFrameTask::drawFrame(nsecs_t frameTimeNanos) { +int DrawFrameTask::drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos) { LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!"); mSyncResult = kSync_OK; mFrameTimeNanos = frameTimeNanos; + mRecordDurationNanos = recordDurationNanos; postAndWait(); // Reset the single-frame data mFrameTimeNanos = 0; + mRecordDurationNanos = 0; mDirty.setEmpty(); return mSyncResult; @@ -87,6 +97,9 @@ void DrawFrameTask::postAndWait() { void DrawFrameTask::run() { ATRACE_NAME("DrawFrame"); + mContext->profiler().setDensity(mDensity); + mContext->profiler().startFrame(mRecordDurationNanos); + bool canUnblockUiThread; bool canDrawThisFrame; { @@ -125,7 +138,16 @@ bool DrawFrameTask::syncFrameState(TreeInfo& info) { mContext->makeCurrent(); Caches::getInstance().textureCache.resetMarkInUse(); initTreeInfo(info); - mContext->prepareDraw(&mLayers, info); + + for (size_t i = 0; i < mLayers.size(); i++) { + mContext->processLayerUpdate(mLayers[i].get(), info); + } + mLayers.clear(); + if (info.out.hasAnimations) { + // TODO: Uh... crap? + } + mContext->prepareTree(info); + if (info.out.hasAnimations) { // TODO: dirty calculations, for now just do a full-screen inval mDirty.setEmpty(); diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h index acbc02a..d4129b6 100644 --- a/libs/hwui/renderthread/DrawFrameTask.h +++ b/libs/hwui/renderthread/DrawFrameTask.h @@ -16,10 +16,11 @@ #ifndef DRAWFRAMETASK_H #define DRAWFRAMETASK_H +#include <vector> + #include <utils/Condition.h> #include <utils/Mutex.h> #include <utils/StrongPointer.h> -#include <utils/Vector.h> #include "RenderTask.h" @@ -56,11 +57,12 @@ public: void setContext(RenderThread* thread, CanvasContext* context); - void addLayer(DeferredLayerUpdater* layer); - void removeLayer(DeferredLayerUpdater* layer); + void pushLayerUpdate(DeferredLayerUpdater* layer); + void removeLayerUpdate(DeferredLayerUpdater* layer); void setDirty(int left, int top, int right, int bottom); - int drawFrame(nsecs_t frameTimeNanos); + void setDensity(float density) { mDensity = density; } + int drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos); virtual void run(); @@ -80,13 +82,11 @@ private: *********************************************/ Rect mDirty; nsecs_t mFrameTimeNanos; + nsecs_t mRecordDurationNanos; + float mDensity; + std::vector< sp<DeferredLayerUpdater> > mLayers; int mSyncResult; - - /********************************************* - * Multi frame data - *********************************************/ - Vector<DeferredLayerUpdater*> mLayers; }; } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 8e772f2..0901963 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -99,16 +99,20 @@ void RenderProxy::setFrameInterval(nsecs_t frameIntervalNanos) { post(task); } -CREATE_BRIDGE0(loadSystemProperties) { +CREATE_BRIDGE1(loadSystemProperties, CanvasContext* context) { bool needsRedraw = false; if (Caches::hasInstance()) { needsRedraw = Caches::getInstance().initProperties(); } + if (args->context->profiler().loadSystemProperties()) { + needsRedraw = true; + } return (void*) needsRedraw; } bool RenderProxy::loadSystemProperties() { SETUP_TASK(loadSystemProperties); + args->context = mContext; return (bool) postAndWait(task); } @@ -175,10 +179,11 @@ void RenderProxy::setOpaque(bool opaque) { post(task); } -int RenderProxy::syncAndDrawFrame(nsecs_t frameTimeNanos, - int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom) { +int RenderProxy::syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos, + float density, int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom) { mDrawFrameTask.setDirty(dirtyLeft, dirtyTop, dirtyRight, dirtyBottom); - return mDrawFrameTask.drawFrame(frameTimeNanos); + mDrawFrameTask.setDensity(density); + return mDrawFrameTask.drawFrame(frameTimeNanos, recordDurationNanos); } CREATE_BRIDGE1(destroyCanvasAndSurface, CanvasContext* context) { @@ -224,10 +229,21 @@ void RenderProxy::runWithGlContext(RenderTask* gltask) { postAndWait(task); } +CREATE_BRIDGE1(destroyLayer, Layer* layer) { + LayerRenderer::destroyLayer(args->layer); + return NULL; +} + +static void enqueueDestroyLayer(Layer* layer) { + SETUP_TASK(destroyLayer); + args->layer = layer; + RenderThread::getInstance().queue(task); +} + CREATE_BRIDGE3(createDisplayListLayer, CanvasContext* context, int width, int height) { Layer* layer = args->context->createRenderLayer(args->width, args->height); if (!layer) return 0; - return new DeferredLayerUpdater(layer); + return new DeferredLayerUpdater(layer, enqueueDestroyLayer); } DeferredLayerUpdater* RenderProxy::createDisplayListLayer(int width, int height) { @@ -237,14 +253,13 @@ DeferredLayerUpdater* RenderProxy::createDisplayListLayer(int width, int height) args->context = mContext; void* retval = postAndWait(task); DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(retval); - mDrawFrameTask.addLayer(layer); return layer; } CREATE_BRIDGE1(createTextureLayer, CanvasContext* context) { Layer* layer = args->context->createTextureLayer(); if (!layer) return 0; - return new DeferredLayerUpdater(layer); + return new DeferredLayerUpdater(layer, enqueueDestroyLayer); } DeferredLayerUpdater* RenderProxy::createTextureLayer() { @@ -252,15 +267,9 @@ DeferredLayerUpdater* RenderProxy::createTextureLayer() { args->context = mContext; void* retval = postAndWait(task); DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(retval); - mDrawFrameTask.addLayer(layer); return layer; } -CREATE_BRIDGE1(destroyLayer, Layer* layer) { - LayerRenderer::destroyLayer(args->layer); - return NULL; -} - CREATE_BRIDGE3(copyLayerInto, CanvasContext* context, DeferredLayerUpdater* layer, SkBitmap* bitmap) { bool success = args->context->copyLayerInto(args->layer, args->bitmap); @@ -275,11 +284,12 @@ bool RenderProxy::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) { return (bool) postAndWait(task); } -void RenderProxy::destroyLayer(DeferredLayerUpdater* layer) { - mDrawFrameTask.removeLayer(layer); - SETUP_TASK(destroyLayer); - args->layer = layer->detachBackingLayer(); - post(task); +void RenderProxy::pushLayerUpdate(DeferredLayerUpdater* layer) { + mDrawFrameTask.pushLayerUpdate(layer); +} + +void RenderProxy::cancelLayerUpdate(DeferredLayerUpdater* layer) { + mDrawFrameTask.removeLayerUpdate(layer); } CREATE_BRIDGE2(flushCaches, CanvasContext* context, Caches::FlushMode flushMode) { @@ -315,6 +325,18 @@ void RenderProxy::notifyFramePending() { mRenderThread.queueAtFront(task); } +CREATE_BRIDGE2(dumpProfileInfo, CanvasContext* context, int fd) { + args->context->profiler().dumpData(args->fd); + return NULL; +} + +void RenderProxy::dumpProfileInfo(int fd) { + SETUP_TASK(dumpProfileInfo); + args->context = mContext; + args->fd = fd; + postAndWait(task); +} + void RenderProxy::post(RenderTask* task) { mRenderThread.queue(task); } diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index 22d4e22..944ff9c 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -69,8 +69,8 @@ public: ANDROID_API void pauseSurface(const sp<ANativeWindow>& window); ANDROID_API void setup(int width, int height, const Vector3& lightCenter, float lightRadius); ANDROID_API void setOpaque(bool opaque); - ANDROID_API int syncAndDrawFrame(nsecs_t frameTimeNanos, - int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom); + ANDROID_API int syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos, + float density, int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom); ANDROID_API void destroyCanvasAndSurface(); ANDROID_API void invokeFunctor(Functor* functor, bool waitForCompletion); @@ -80,13 +80,16 @@ public: ANDROID_API DeferredLayerUpdater* createDisplayListLayer(int width, int height); ANDROID_API DeferredLayerUpdater* createTextureLayer(); ANDROID_API bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap); - ANDROID_API void destroyLayer(DeferredLayerUpdater* layer); + ANDROID_API void pushLayerUpdate(DeferredLayerUpdater* layer); + ANDROID_API void cancelLayerUpdate(DeferredLayerUpdater* layer); ANDROID_API void flushCaches(Caches::FlushMode flushMode); ANDROID_API void fence(); ANDROID_API void notifyFramePending(); + ANDROID_API void dumpProfileInfo(int fd); + private: RenderThread& mRenderThread; CanvasContext* mContext; |