diff options
Diffstat (limited to 'libs')
42 files changed, 761 insertions, 171 deletions
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 0782269d7de1..7a0ef2b770ce 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -59,7 +59,6 @@ namespace android { #endif #define IDMAP_MAGIC 0x504D4449 -#define IDMAP_CURRENT_VERSION 0x00000001 #define APP_PACKAGE_ID 0x7f #define SYS_PACKAGE_ID 0x01 @@ -246,11 +245,11 @@ static bool assertIdmapHeader(const void* idmap, size_t size) { } const uint32_t version = htodl(*(reinterpret_cast<const uint32_t*>(idmap) + 1)); - if (version != IDMAP_CURRENT_VERSION) { + if (version != ResTable::IDMAP_CURRENT_VERSION) { // We are strict about versions because files with this format are // auto-generated and don't need backwards compatibility. ALOGW("idmap: version mismatch in header (is 0x%08x, expected 0x%08x)", - version, IDMAP_CURRENT_VERSION); + version, ResTable::IDMAP_CURRENT_VERSION); return false; } return true; @@ -6855,7 +6854,7 @@ status_t ResTable::createIdmap(const ResTable& overlay, uint32_t* data = (uint32_t*)*outData; *data++ = htodl(IDMAP_MAGIC); - *data++ = htodl(IDMAP_CURRENT_VERSION); + *data++ = htodl(ResTable::IDMAP_CURRENT_VERSION); *data++ = htodl(targetCrc); *data++ = htodl(overlayCrc); const char* paths[] = { targetPath, overlayPath }; diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index 7a6e37d41b7c..66c66c251d9b 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -1933,6 +1933,7 @@ public: void** outData, size_t* outSize) const; static const size_t IDMAP_HEADER_SIZE_BYTES = 4 * sizeof(uint32_t) + 2 * 256; + static const uint32_t IDMAP_CURRENT_VERSION = 0x00000001; // Retrieve idmap meta-data. // diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp index 4e59baa48983..3c3b3177159b 100644 --- a/libs/hwui/BakedOpRenderer.cpp +++ b/libs/hwui/BakedOpRenderer.cpp @@ -216,10 +216,7 @@ void BakedOpRenderer::drawRects(const float* rects, int count, const SkPaint* pa .setTransform(Matrix4::identity(), TransformFlags::None) .setModelViewIdentityEmptyBounds() .build(); - // Disable blending if this is the first draw to the main framebuffer, in case app has defined - // transparency where it doesn't make sense - as first draw in opaque window. - bool overrideDisableBlending = !mHasDrawn && mOpaque && !mRenderTarget.frameBufferId; - mRenderState.render(glop, mRenderTarget.orthoMatrix, overrideDisableBlending); + mRenderState.render(glop, mRenderTarget.orthoMatrix, false); mHasDrawn = true; } @@ -350,8 +347,14 @@ void BakedOpRenderer::renderGlopImpl(const Rect* dirtyBounds, const ClipBase* cl const Glop& glop) { prepareRender(dirtyBounds, clip); // Disable blending if this is the first draw to the main framebuffer, in case app has defined - // transparency where it doesn't make sense - as first draw in opaque window. - bool overrideDisableBlending = !mHasDrawn && mOpaque && !mRenderTarget.frameBufferId; + // transparency where it doesn't make sense - as first draw in opaque window. Note that we only + // apply this improvement when the blend mode is SRC_OVER - other modes (e.g. CLEAR) can be + // valid draws that affect other content (e.g. draw CLEAR, then draw DST_OVER) + bool overrideDisableBlending = !mHasDrawn + && mOpaque + && !mRenderTarget.frameBufferId + && glop.blend.src == GL_ONE + && glop.blend.dst == GL_ONE_MINUS_SRC_ALPHA; mRenderState.render(glop, mRenderTarget.orthoMatrix, overrideDisableBlending); if (!mRenderTarget.frameBufferId) mHasDrawn = true; } diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index fe291d2a4bfe..3b59bb1a8f02 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -177,7 +177,7 @@ RenderPipelineType Properties::getRenderPipelineType() { return sRenderPipelineType; } char prop[PROPERTY_VALUE_MAX]; - property_get(PROPERTY_RENDERER, prop, "opengl"); + property_get(PROPERTY_RENDERER, prop, "skiagl"); if (!strcmp(prop, "skiagl") ) { ALOGD("Skia GL Pipeline"); sRenderPipelineType = RenderPipelineType::SkiaGL; diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 61b3876cd095..36a747519c37 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -32,6 +32,7 @@ #include "protos/hwui.pb.h" #include "protos/ProtoHelpers.h" +#include <SkPathOps.h> #include <algorithm> #include <sstream> #include <string> @@ -555,5 +556,23 @@ void RenderNode::computeOrderingImpl( } } +const SkPath* RenderNode::getClippedOutline(const SkRect& clipRect) const { + const SkPath* outlinePath = properties().getOutline().getPath(); + const uint32_t outlineID = outlinePath->getGenerationID(); + + if (outlineID != mClippedOutlineCache.outlineID || clipRect != mClippedOutlineCache.clipRect) { + // update the cache keys + mClippedOutlineCache.outlineID = outlineID; + mClippedOutlineCache.clipRect = clipRect; + + // update the cache value by recomputing a new path + SkPath clipPath; + clipPath.addRect(clipRect); + Op(*outlinePath, clipPath, kIntersect_SkPathOp, &mClippedOutlineCache.clippedOutline); + + } + return &mClippedOutlineCache.clippedOutline; +} + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index c4ae82af430c..89e022f5e68d 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -365,6 +365,17 @@ public: return mSkiaLayer.get(); } + /** + * Returns the path that represents the outline of RenderNode intersected with + * the provided rect. This call will internally cache the resulting path in + * order to potentially return that path for subsequent calls to this method. + * By reusing the same path we get better performance on the GPU backends since + * those resources are cached in the hardware based on the path's genID. + * + * The returned path is only guaranteed to be valid until this function is called + * again or the RenderNode's outline is mutated. + */ + const SkPath* getClippedOutline(const SkRect& clipRect) const; private: /** * If this RenderNode has been used in a previous frame then the SkiaDisplayList @@ -380,6 +391,16 @@ private: * when it has been set to draw as a LayerType::RenderLayer. */ std::unique_ptr<skiapipeline::SkiaLayer> mSkiaLayer; + + struct ClippedOutlineCache { + // keys + uint32_t outlineID = 0; + SkRect clipRect; + + // value + SkPath clippedOutline; + }; + mutable ClippedOutlineCache mClippedOutlineCache; }; // class RenderNode class MarkAndSweepRemoved : public TreeObserver { diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp index f4ce864e83e1..376371de80dc 100644 --- a/libs/hwui/VectorDrawable.cpp +++ b/libs/hwui/VectorDrawable.cpp @@ -22,6 +22,7 @@ #include "SkShader.h" #include <utils/Log.h> #include "utils/Macros.h" +#include "utils/TraceUtils.h" #include "utils/VectorDrawableUtils.h" #include <math.h> @@ -511,25 +512,19 @@ void Tree::updateCache(sp<skiapipeline::VectorDrawableAtlas>& atlas, GrContext* } } if (!canReuseSurface || mCache.dirty) { - draw(surface.get(), dst); + if (surface) { + Bitmap& bitmap = getBitmapUpdateIfDirty(); + SkBitmap skiaBitmap; + bitmap.getSkBitmap(&skiaBitmap); + if (!surface->getCanvas()->writePixels(skiaBitmap, dst.fLeft, dst.fTop)) { + ALOGD("VectorDrawable caching failed to efficiently upload"); + surface->getCanvas()->drawBitmap(skiaBitmap, dst.fLeft, dst.fTop); + } + } mCache.dirty = false; } } -void Tree::draw(SkSurface* surface, const SkRect& dst) { - if (surface) { - SkCanvas* canvas = surface->getCanvas(); - float scaleX = dst.width() / mProperties.getViewportWidth(); - float scaleY = dst.height() / mProperties.getViewportHeight(); - SkAutoCanvasRestore acr(canvas, true); - canvas->translate(dst.fLeft, dst.fTop); - canvas->clipRect(SkRect::MakeWH(dst.width(), dst.height())); - canvas->clear(SK_ColorTRANSPARENT); - canvas->scale(scaleX, scaleY); - mRootNode->draw(canvas, false); - } -} - void Tree::Cache::setAtlas(sp<skiapipeline::VectorDrawableAtlas> newAtlas, skiapipeline::AtlasKey newAtlasKey) { LOG_ALWAYS_FATAL_IF(newAtlasKey == INVALID_ATLAS_KEY); @@ -570,22 +565,15 @@ void Tree::draw(SkCanvas* canvas) { // Handle the case when VectorDrawableAtlas has been destroyed, because of memory pressure. // We render the VD into a temporary standalone buffer and mark the frame as dirty. Next // frame will be cached into the atlas. + Bitmap& bitmap = getBitmapUpdateIfDirty(); + SkBitmap skiaBitmap; + bitmap.getSkBitmap(&skiaBitmap); + int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth()); int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight()); - SkRect src = SkRect::MakeWH(scaledWidth, scaledHeight); -#ifndef ANDROID_ENABLE_LINEAR_BLENDING - sk_sp<SkColorSpace> colorSpace = nullptr; -#else - sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeSRGB(); -#endif - SkImageInfo info = SkImageInfo::MakeN32(scaledWidth, scaledHeight, kPremul_SkAlphaType, - colorSpace); - sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(canvas->getGrContext(), - SkBudgeted::kYes, info); - draw(surface.get(), src); + canvas->drawBitmapRect(skiaBitmap, SkRect::MakeWH(scaledWidth, scaledHeight), + mutateProperties()->getBounds(), getPaint(), SkCanvas::kFast_SrcRectConstraint); mCache.clear(); - canvas->drawImageRect(surface->makeImageSnapshot().get(), mutateProperties()->getBounds(), - getPaint(), SkCanvas::kFast_SrcRectConstraint); markDirty(); } } @@ -593,14 +581,17 @@ void Tree::draw(SkCanvas* canvas) { void Tree::updateBitmapCache(Bitmap& bitmap, bool useStagingData) { SkBitmap outCache; bitmap.getSkBitmap(&outCache); + int cacheWidth = outCache.width(); + int cacheHeight = outCache.height(); + ATRACE_FORMAT("VectorDrawable repaint %dx%d", cacheWidth, cacheHeight); outCache.eraseColor(SK_ColorTRANSPARENT); SkCanvas outCanvas(outCache); float viewportWidth = useStagingData ? mStagingProperties.getViewportWidth() : mProperties.getViewportWidth(); float viewportHeight = useStagingData ? mStagingProperties.getViewportHeight() : mProperties.getViewportHeight(); - float scaleX = outCache.width() / viewportWidth; - float scaleY = outCache.height() / viewportHeight; + float scaleX = cacheWidth / viewportWidth; + float scaleY = cacheHeight / viewportHeight; outCanvas.scale(scaleX, scaleY); mRootNode->draw(&outCanvas, useStagingData); } diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h index efbb695a14dd..4b80542ad0d0 100644 --- a/libs/hwui/VectorDrawable.h +++ b/libs/hwui/VectorDrawable.h @@ -558,6 +558,16 @@ public: SkPaint* getPaint(); void syncProperties() { if (mStagingProperties.mNonAnimatablePropertiesDirty) { + mCache.dirty |= (mProperties.mNonAnimatableProperties.viewportWidth + != mStagingProperties.mNonAnimatableProperties.viewportWidth) + || (mProperties.mNonAnimatableProperties.viewportHeight + != mStagingProperties.mNonAnimatableProperties.viewportHeight) + || (mProperties.mNonAnimatableProperties.scaledWidth + != mStagingProperties.mNonAnimatableProperties.scaledWidth) + || (mProperties.mNonAnimatableProperties.scaledHeight + != mStagingProperties.mNonAnimatableProperties.scaledHeight) + || (mProperties.mNonAnimatableProperties.bounds + != mStagingProperties.mNonAnimatableProperties.bounds); mProperties.syncNonAnimatableProperties(mStagingProperties); mStagingProperties.mNonAnimatablePropertiesDirty = false; } @@ -738,11 +748,6 @@ private: bool canReuseBitmap(Bitmap*, int width, int height); void updateBitmapCache(Bitmap& outCache, bool useStagingData); - /** - * Draws the root node into "surface" at a given "dst" position. - */ - void draw(SkSurface* surface, const SkRect& dst); - // Cap the bitmap size, such that it won't hurt the performance too much // and it won't crash due to a very large scale. // The drawable will look blurry above this size. diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp index 5577bbf2bfc9..7da7f3876a3d 100644 --- a/libs/hwui/hwui/MinikinUtils.cpp +++ b/libs/hwui/hwui/MinikinUtils.cpp @@ -68,7 +68,8 @@ float MinikinUtils::measureText(const Paint* paint, int bidiFlags, const Typefac minikin::FontStyle minikinStyle = prepareMinikinPaint(&minikinPaint, paint, typeface); const Typeface* resolvedTypeface = Typeface::resolveDefault(typeface); return minikin::Layout::measureText(buf, start, count, bufSize, bidiFlags, minikinStyle, - minikinPaint, resolvedTypeface->fFontCollection, advances, nullptr /* extent */); + minikinPaint, resolvedTypeface->fFontCollection, advances, nullptr /* extent */, + nullptr /* overhangs */); } bool MinikinUtils::hasVariationSelector(const Typeface* typeface, uint32_t codepoint, uint32_t vs) { diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp index 4ee47afe87fd..47dee9d4e9d8 100644 --- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp +++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp @@ -19,6 +19,7 @@ #include "SkiaDisplayList.h" #include "SkiaPipeline.h" #include "utils/TraceUtils.h" +#include <SkPaintFilterCanvas.h> namespace android { namespace uirenderer { @@ -151,6 +152,27 @@ static bool layerNeedsPaint(const LayerProperties& properties, return false; } +class AlphaFilterCanvas : public SkPaintFilterCanvas { +public: + AlphaFilterCanvas(SkCanvas* canvas, float alpha) : SkPaintFilterCanvas(canvas), mAlpha(alpha) {} +protected: + bool onFilter(SkTCopyOnFirstWrite<SkPaint>* paint, Type t) const override { + SkTLazy<SkPaint> defaultPaint; + if (!*paint) { + paint->init(*defaultPaint.init()); + } + paint->writable()->setAlpha((uint8_t)(*paint)->getAlpha()*mAlpha); + return true; + } + void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override { + // We unroll the drawable using "this" canvas, so that draw calls contained inside will + // get their alpha applied. THe default SkPaintFilterCanvas::onDrawDrawable does not unroll. + drawable->draw(this, matrix); + } +private: + float mAlpha; +}; + void RenderNodeDrawable::drawContent(SkCanvas* canvas) const { RenderNode* renderNode = mRenderNode.get(); float alphaMultiplier = 1.0f; @@ -211,7 +233,14 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const { canvas->restore(); } } else { - displayList->draw(canvas); + if (alphaMultiplier < 1.0f) { + // Non-layer draw for a view with getHasOverlappingRendering=false, will apply + // the alpha to the paint of each nested draw. + AlphaFilterCanvas alphaCanvas(canvas, alphaMultiplier); + displayList->draw(&alphaCanvas); + } else { + displayList->draw(canvas); + } } } } diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp index 374d364787de..c8207bc70dd4 100644 --- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp +++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp @@ -163,28 +163,22 @@ void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable* hwuiMatrix.copyTo(shadowMatrix); canvas->concat(shadowMatrix); - const SkPath* casterOutlinePath = casterProperties.getOutline().getPath(); - // holds temporary SkPath to store the result of intersections - SkPath tmpPath; - const SkPath* casterPath = casterOutlinePath; + // default the shadow-casting path to the outline of the caster + const SkPath* casterPath = casterProperties.getOutline().getPath(); + + // intersect the shadow-casting path with the clipBounds, if present + if (clippedToBounds && !casterClipRect.contains(casterPath->getBounds())) { + casterPath = caster->getRenderNode()->getClippedOutline(casterClipRect); + } - // TODO: In to following course of code that calculates the final shape, is there an optimal - // of doing the Op calculations? // intersect the shadow-casting path with the reveal, if present + SkPath tmpPath; // holds temporary SkPath to store the result of intersections if (revealClipPath) { Op(*casterPath, *revealClipPath, kIntersect_SkPathOp, &tmpPath); tmpPath.setIsVolatile(true); casterPath = &tmpPath; } - // intersect the shadow-casting path with the clipBounds, if present - if (clippedToBounds) { - SkPath clipBoundsPath; - clipBoundsPath.addRect(casterClipRect); - Op(*casterPath, clipBoundsPath, kIntersect_SkPathOp, &tmpPath); - tmpPath.setIsVolatile(true); - casterPath = &tmpPath; - } const Vector3 lightPos = SkiaPipeline::getLightCenter(); SkPoint3 skiaLightPos = SkPoint3::Make(lightPos.x, lightPos.y, lightPos.z); SkPoint3 zParams; diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index a8463ecc44d8..6f117de23864 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -46,7 +46,7 @@ SkiaPipeline::SkiaPipeline(RenderThread& thread) : mRenderThread(thread) { } TaskManager* SkiaPipeline::getTaskManager() { - return &mTaskManager; + return mRenderThread.cacheManager().getTaskManager(); } void SkiaPipeline::onDestroyHardwareResources() { @@ -83,7 +83,8 @@ void SkiaPipeline::renderLayers(const FrameBuilder::LightGeometry& lightGeometry void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque, bool wideColorGamut) { - // TODO: Handle wide color gamut + sk_sp<GrContext> cachedContext; + // Render all layers that need to be updated, in order. for (size_t i = 0; i < layers.entries().size(); i++) { RenderNode* layerNode = layers.entries()[i].renderNode.get(); @@ -118,16 +119,31 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, return; } + ATRACE_FORMAT("drawLayer [%s] %.1f x %.1f", layerNode->getName(), bounds.width(), bounds.height()); + layerNode->getSkiaLayer()->hasRenderedSinceRepaint = false; layerCanvas->clear(SK_ColorTRANSPARENT); RenderNodeDrawable root(layerNode, layerCanvas, false); root.forceDraw(layerCanvas); layerCanvas->restoreToCount(saveCount); - layerCanvas->flush(); mLightCenter = savedLightCenter; + + // cache the current context so that we can defer flushing it until + // either all the layers have been rendered or the context changes + GrContext* currentContext = layerCanvas->getGrContext(); + if (cachedContext.get() != currentContext) { + if (cachedContext.get()) { + cachedContext->flush(); + } + cachedContext.reset(SkSafeRef(currentContext)); + } } } + + if (cachedContext.get()) { + cachedContext->flush(); + } } bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, @@ -143,7 +159,6 @@ bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, } SkSurfaceProps props(0, kUnknown_SkPixelGeometry); SkASSERT(mRenderThread.getGrContext() != nullptr); - // TODO: Handle wide color gamut requests node->setLayerSurface( SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes, info, 0, &props)); @@ -194,10 +209,10 @@ void SkiaPipeline::renderVectorDrawableCache() { sp<VectorDrawableAtlas> atlas = mRenderThread.cacheManager().acquireVectorDrawableAtlas(); auto grContext = mRenderThread.getGrContext(); atlas->prepareForDraw(grContext); + ATRACE_NAME("Update VectorDrawables"); for (auto vd : mVectorDrawables) { vd->updateCache(atlas, grContext); } - grContext->flush(); mVectorDrawables.clear(); } } diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h index 19ffc463c121..2b0c419aa1a1 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaPipeline.h @@ -126,7 +126,6 @@ private: */ void renderVectorDrawableCache(); - TaskManager mTaskManager; std::vector<sk_sp<SkImage>> mPinnedImages; /** diff --git a/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp b/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp index 437653a8dfa8..9c9e17d600bf 100644 --- a/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp +++ b/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp @@ -21,6 +21,7 @@ #include <cmath> #include "utils/TraceUtils.h" #include "renderthread/RenderProxy.h" +#include "renderthread/RenderThread.h" namespace android { namespace uirenderer { @@ -228,6 +229,15 @@ AtlasEntry VectorDrawableAtlas::getEntry(AtlasKey atlasKey) { void VectorDrawableAtlas::releaseEntry(AtlasKey atlasKey) { if (INVALID_ATLAS_KEY != atlasKey) { + if (!renderthread::RenderThread::isCurrent()) { + { + AutoMutex _lock(mReleaseKeyLock); + mKeysForRelease.push_back(atlasKey); + } + // invoke releaseEntry on the renderthread + renderthread::RenderProxy::releaseVDAtlasEntries(); + return; + } CacheEntry* entry = reinterpret_cast<CacheEntry*>(atlasKey); if (!entry->surface) { // Store freed atlas rectangles in "mFreeRects" and try to reuse them later, when atlas @@ -245,6 +255,14 @@ void VectorDrawableAtlas::releaseEntry(AtlasKey atlasKey) { } } +void VectorDrawableAtlas::delayedReleaseEntries() { + AutoMutex _lock(mReleaseKeyLock); + for (auto key : mKeysForRelease) { + releaseEntry(key); + } + mKeysForRelease.clear(); +} + sk_sp<SkSurface> VectorDrawableAtlas::createSurface(int width, int height, GrContext* context) { #ifndef ANDROID_ENABLE_LINEAR_BLENDING sk_sp<SkColorSpace> colorSpace = nullptr; @@ -252,7 +270,10 @@ sk_sp<SkSurface> VectorDrawableAtlas::createSurface(int width, int height, GrCon sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeSRGB(); #endif SkImageInfo info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType, colorSpace); - return SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, info); + // This must have a top-left origin so that calls to surface->canvas->writePixels + // performs a basic texture upload instead of a more complex drawing operation + return SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, info, 0, + kTopLeft_GrSurfaceOrigin, nullptr); } void VectorDrawableAtlas::setStorageMode(StorageMode mode) { diff --git a/libs/hwui/pipeline/skia/VectorDrawableAtlas.h b/libs/hwui/pipeline/skia/VectorDrawableAtlas.h index 496c55742748..0683b3915850 100644 --- a/libs/hwui/pipeline/skia/VectorDrawableAtlas.h +++ b/libs/hwui/pipeline/skia/VectorDrawableAtlas.h @@ -20,6 +20,7 @@ #include <SkSurface.h> #include <utils/FatVector.h> #include <utils/RefBase.h> +#include <utils/Thread.h> #include <list> class GrRectanizer; @@ -103,12 +104,19 @@ public: /** * "releaseEntry" is invoked when a VectorDrawable is deleted. Passing a non-existing "atlasKey" - * is causing an undefined behaviour. + * is causing an undefined behaviour. This is the only function in the class that can be + * invoked from any thread. It will marshal internally to render thread if needed. */ void releaseEntry(AtlasKey atlasKey); void setStorageMode(StorageMode mode); + /** + * "delayedReleaseEntries" is indirectly invoked by "releaseEntry", when "releaseEntry" is + * invoked from a non render thread. + */ + void delayedReleaseEntries(); + private: struct CacheEntry { CacheEntry(const SkRect& newVDrect, const SkRect& newRect, @@ -182,6 +190,17 @@ private: */ StorageMode mStorageMode; + /** + * mKeysForRelease is used by releaseEntry implementation to pass atlas keys from an arbitrary + * calling thread to the render thread. + */ + std::vector<AtlasKey> mKeysForRelease; + + /** + * A lock used to protect access to mKeysForRelease. + */ + Mutex mReleaseKeyLock; + sk_sp<SkSurface> createSurface(int width, int height, GrContext* context); inline bool fitInAtlas(int width, int height) { diff --git a/libs/hwui/renderstate/Blend.h b/libs/hwui/renderstate/Blend.h index ec0e114c998f..a9de24631340 100644 --- a/libs/hwui/renderstate/Blend.h +++ b/libs/hwui/renderstate/Blend.h @@ -40,6 +40,14 @@ public: GLenum* outSrc, GLenum* outDst); void setFactors(GLenum src, GLenum dst); + bool getEnabled() { + return mEnabled; + } + void getFactors(GLenum* src, GLenum* dst) { + *src = mSrcMode; + *dst = mDstMode; + } + void dump(); private: Blend(); diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index f6b23e1a0723..0572a8d0afac 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -22,6 +22,7 @@ #include <gui/Surface.h> #include <GrContextOptions.h> +#include <SkExecutor.h> #include <math.h> #include <set> @@ -73,6 +74,29 @@ void CacheManager::updateContextCacheSizes() { mGrContext->setResourceCacheLimits(mMaxResources, mMaxResourceBytes); } +class CacheManager::SkiaTaskProcessor : public TaskProcessor<bool>, public SkExecutor { +public: + explicit SkiaTaskProcessor(TaskManager* taskManager) : TaskProcessor<bool>(taskManager) {} + + // This is really a Task<void> but that doesn't really work when Future<> + // expects to be able to get/set a value + struct SkiaTask : public Task<bool> { + std::function<void()> func; + }; + + virtual void add(std::function<void(void)> func) override { + sp<SkiaTask> task(new SkiaTask()); + task->func = func; + TaskProcessor<bool>::add(task); + } + + virtual void onProcess(const sp<Task<bool> >& task) override { + SkiaTask* t = static_cast<SkiaTask*>(task.get()); + t->func(); + task->setResult(true); + } +}; + void CacheManager::configureContext(GrContextOptions* contextOptions) { contextOptions->fAllowPathMaskCaching = true; @@ -95,6 +119,13 @@ void CacheManager::configureContext(GrContextOptions* contextOptions) { // Skia's implementation doesn't provide a mechanism to resize the font cache due to // the potential cost of recreating the glyphs. contextOptions->fGlyphCacheTextureMaximumBytes = fontCacheMB * 1024 * 1024; + + if (mTaskManager.canRunTasks()) { + if (!mTaskProcessor.get()) { + mTaskProcessor = new SkiaTaskProcessor(&mTaskManager); + } + contextOptions->fExecutor = mTaskProcessor.get(); + } } void CacheManager::trimMemory(TrimMemoryMode mode) { diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h index 90362f33358d..3ba2690caf31 100644 --- a/libs/hwui/renderthread/CacheManager.h +++ b/libs/hwui/renderthread/CacheManager.h @@ -22,7 +22,10 @@ #include <ui/DisplayInfo.h> #include <utils/String8.h> #include <vector> + #include "pipeline/skia/VectorDrawableAtlas.h" +#include "thread/TaskManager.h" +#include "thread/TaskProcessor.h" namespace android { @@ -54,6 +57,7 @@ public: size_t getCacheSize() const { return mMaxResourceBytes; } size_t getBackgroundCacheSize() const { return mBackgroundResourceBytes; } + TaskManager* getTaskManager() { return &mTaskManager; } private: friend class RenderThread; @@ -77,6 +81,10 @@ private: }; sp<skiapipeline::VectorDrawableAtlas> mVectorDrawableAtlas; + + class SkiaTaskProcessor; + sp<SkiaTaskProcessor> mTaskProcessor; + TaskManager mTaskManager; }; } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index aa6d2f3513d7..4a5b2c72b02a 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -162,8 +162,8 @@ public: void addRenderNode(RenderNode* node, bool placeFront); void removeRenderNode(RenderNode* node); - void setContentDrawBounds(int left, int top, int right, int bottom) { - mContentDrawBounds.set(left, top, right, bottom); + void setContentDrawBounds(const Rect& bounds) { + mContentDrawBounds = bounds; } RenderState& getRenderState() { diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp index 7d641d3ac7c7..a097272df359 100644 --- a/libs/hwui/renderthread/DrawFrameTask.cpp +++ b/libs/hwui/renderthread/DrawFrameTask.cpp @@ -32,6 +32,7 @@ namespace renderthread { DrawFrameTask::DrawFrameTask() : mRenderThread(nullptr) , mContext(nullptr) + , mContentDrawBounds(0, 0, 0, 0) , mSyncResult(SyncResult::OK) { } @@ -123,6 +124,7 @@ bool DrawFrameTask::syncFrameState(TreeInfo& info) { mLayers[i]->apply(); } mLayers.clear(); + mContext->setContentDrawBounds(mContentDrawBounds); mContext->prepareTree(info, mFrameInfo, mSyncQueued, mTargetNode); // This is after the prepareTree so that any pending operations diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h index fb480626d421..83ecb98f548f 100644 --- a/libs/hwui/renderthread/DrawFrameTask.h +++ b/libs/hwui/renderthread/DrawFrameTask.h @@ -61,6 +61,9 @@ public: virtual ~DrawFrameTask(); void setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode); + void setContentDrawBounds(int left, int top, int right, int bottom) { + mContentDrawBounds.set(left, top, right, bottom); + } void pushLayerUpdate(DeferredLayerUpdater* layer); void removeLayerUpdate(DeferredLayerUpdater* layer); @@ -82,6 +85,7 @@ private: RenderThread* mRenderThread; CanvasContext* mContext; RenderNode* mTargetNode = nullptr; + Rect mContentDrawBounds; /********************************************* * Single frame data diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index 16d77364942e..87e5bfdc8ca5 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -138,7 +138,7 @@ void EglManager::initialize() { LOG_ALWAYS_FATAL_IF(!glInterface.get()); GrContextOptions options; - options.fGpuPathRenderers &= ~GrContextOptions::GpuPathRenderers::kDistanceField; + options.fDisableDistanceFieldPaths = true; mRenderThread.cacheManager().configureContext(&options); mRenderThread.setGrContext(GrContext::Create(GrBackend::kOpenGL_GrBackend, (GrBackendContext)glInterface.get(), options)); diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 7fe966dde316..a6aa301021e2 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -551,20 +551,8 @@ void RenderProxy::drawRenderNode(RenderNode* node) { staticPostAndWait(task); } -CREATE_BRIDGE5(setContentDrawBounds, CanvasContext* context, int left, int top, - int right, int bottom) { - args->context->setContentDrawBounds(args->left, args->top, args->right, args->bottom); - return nullptr; -} - void RenderProxy::setContentDrawBounds(int left, int top, int right, int bottom) { - SETUP_TASK(setContentDrawBounds); - args->context = mContext; - args->left = left; - args->top = top; - args->right = right; - args->bottom = bottom; - staticPostAndWait(task); + mDrawFrameTask.setContentDrawBounds(left, top, right, bottom); } CREATE_BRIDGE1(serializeDisplayListTree, CanvasContext* context) { @@ -732,6 +720,18 @@ void RenderProxy::repackVectorDrawableAtlas() { thread.queue(task); } +CREATE_BRIDGE1(releaseVDAtlasEntries, RenderThread* thread) { + args->thread->cacheManager().acquireVectorDrawableAtlas()->delayedReleaseEntries(); + return nullptr; +} + +void RenderProxy::releaseVDAtlasEntries() { + RenderThread& thread = RenderThread::getInstance(); + SETUP_TASK(releaseVDAtlasEntries); + args->thread = &thread; + thread.queue(task); +} + void* RenderProxy::postAndWait(MethodInvokeRenderTask* task) { void* retval; task->setReturnPtr(&retval); diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index 06eaebd066ee..9440b15f6062 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -143,6 +143,8 @@ public: static void repackVectorDrawableAtlas(); + static void releaseVDAtlasEntries(); + private: RenderThread& mRenderThread; CanvasContext* mContext; diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 72a428f1c70c..51e937485fab 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -499,6 +499,10 @@ sk_sp<Bitmap> RenderThread::allocateHardwareBitmap(SkBitmap& skBitmap) { return nullptr; } +bool RenderThread::isCurrent() { + return gettid() == getInstance().getTid(); +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index bef47b3e27c5..30884b571b94 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -111,6 +111,14 @@ public: sk_sp<Bitmap> allocateHardwareBitmap(SkBitmap& skBitmap); void dumpGraphicsMemory(int fd); + /** + * isCurrent provides a way to query, if the caller is running on + * the render thread. + * + * @return true only if isCurrent is invoked from the render thread. + */ + static bool isCurrent(); + protected: virtual bool threadLoop() override; diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp index 3a77195824ad..afb1193657e1 100644 --- a/libs/hwui/service/GraphicsStatsService.cpp +++ b/libs/hwui/service/GraphicsStatsService.cpp @@ -40,7 +40,7 @@ static_assert(sizeof(sCurrentFileVersion) == sHeaderSize, "Header size is wrong" constexpr int sHistogramSize = ProfileData::HistogramSize(); -static void mergeProfileDataIntoProto(service::GraphicsStatsProto* proto, +static bool mergeProfileDataIntoProto(service::GraphicsStatsProto* proto, const std::string& package, int versionCode, int64_t startTime, int64_t endTime, const ProfileData* data); static void dumpAsTextToFd(service::GraphicsStatsProto* proto, int outFd); @@ -159,7 +159,7 @@ bool GraphicsStatsService::parseFromFile(const std::string& path, service::Graph return success; } -void mergeProfileDataIntoProto(service::GraphicsStatsProto* proto, const std::string& package, +bool mergeProfileDataIntoProto(service::GraphicsStatsProto* proto, const std::string& package, int versionCode, int64_t startTime, int64_t endTime, const ProfileData* data) { if (proto->stats_start() == 0 || proto->stats_start() > startTime) { proto->set_stats_start(startTime); @@ -188,23 +188,31 @@ void mergeProfileDataIntoProto(service::GraphicsStatsProto* proto, const std::st proto->mutable_histogram()->Reserve(sHistogramSize); creatingHistogram = true; } else if (proto->histogram_size() != sHistogramSize) { - LOG_ALWAYS_FATAL("Histogram size mismatch, proto is %d expected %d", + ALOGE("Histogram size mismatch, proto is %d expected %d", proto->histogram_size(), sHistogramSize); + return false; } int index = 0; + bool hitMergeError = false; data->histogramForEach([&](ProfileData::HistogramEntry entry) { + if (hitMergeError) return; + service::GraphicsStatsHistogramBucketProto* bucket; if (creatingHistogram) { bucket = proto->add_histogram(); bucket->set_render_millis(entry.renderTimeMs); } else { bucket = proto->mutable_histogram(index); - LOG_ALWAYS_FATAL_IF(bucket->render_millis() != static_cast<int32_t>(entry.renderTimeMs), - "Frame time mistmatch %d vs. %u", bucket->render_millis(), entry.renderTimeMs); + if (bucket->render_millis() != static_cast<int32_t>(entry.renderTimeMs)) { + ALOGW("Frame time mistmatch %d vs. %u", bucket->render_millis(), entry.renderTimeMs); + hitMergeError = true; + return; + } } bucket->set_frame_count(bucket->frame_count() + entry.frameCount); index++; }); + return !hitMergeError; } static int32_t findPercentile(service::GraphicsStatsProto* proto, int percentile) { @@ -221,9 +229,11 @@ static int32_t findPercentile(service::GraphicsStatsProto* proto, int percentile void dumpAsTextToFd(service::GraphicsStatsProto* proto, int fd) { // This isn't a full validation, just enough that we can deref at will - LOG_ALWAYS_FATAL_IF(proto->package_name().empty() - || !proto->has_summary(), "package_name() '%s' summary %d", - proto->package_name().c_str(), proto->has_summary()); + if (proto->package_name().empty() || !proto->has_summary()) { + ALOGW("Skipping dump, invalid package_name() '%s' or summary %d", + proto->package_name().c_str(), proto->has_summary()); + return; + } dprintf(fd, "\nPackage: %s", proto->package_name().c_str()); dprintf(fd, "\nVersion: %d", proto->version_code()); dprintf(fd, "\nStats since: %lldns", proto->stats_start()); @@ -254,14 +264,20 @@ void GraphicsStatsService::saveBuffer(const std::string& path, const std::string if (!parseFromFile(path, &statsProto)) { statsProto.Clear(); } - mergeProfileDataIntoProto(&statsProto, package, versionCode, startTime, endTime, data); + if (!mergeProfileDataIntoProto(&statsProto, package, versionCode, startTime, endTime, data)) { + return; + } // Although we might not have read any data from the file, merging the existing data // should always fully-initialize the proto - LOG_ALWAYS_FATAL_IF(!statsProto.IsInitialized(), "%s", - statsProto.InitializationErrorString().c_str()); - LOG_ALWAYS_FATAL_IF(statsProto.package_name().empty() - || !statsProto.has_summary(), "package_name() '%s' summary %d", - statsProto.package_name().c_str(), statsProto.has_summary()); + if (!statsProto.IsInitialized()) { + ALOGE("proto initialization error %s", statsProto.InitializationErrorString().c_str()); + return; + } + if (statsProto.package_name().empty() || !statsProto.has_summary()) { + ALOGE("missing package_name() '%s' summary %d", + statsProto.package_name().c_str(), statsProto.has_summary()); + return; + } int outFd = open(path.c_str(), O_CREAT | O_RDWR | O_TRUNC, 0660); if (outFd <= 0) { int err = errno; @@ -312,8 +328,9 @@ void GraphicsStatsService::addToDump(Dump* dump, const std::string& path, const if (!path.empty() && !parseFromFile(path, &statsProto)) { statsProto.Clear(); } - if (data) { - mergeProfileDataIntoProto(&statsProto, package, versionCode, startTime, endTime, data); + if (data && !mergeProfileDataIntoProto( + &statsProto, package, versionCode, startTime, endTime, data)) { + return; } if (!statsProto.IsInitialized()) { ALOGW("Failed to load profile data from path '%s' and data %p", diff --git a/libs/hwui/tests/common/scenes/TvApp.cpp b/libs/hwui/tests/common/scenes/TvApp.cpp new file mode 100644 index 000000000000..04fc2d46f946 --- /dev/null +++ b/libs/hwui/tests/common/scenes/TvApp.cpp @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2017 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 "TestSceneBase.h" +#include "tests/common/BitmapAllocationTestUtils.h" +#include "SkBlendMode.h" + +class TvApp; +class TvAppNoRoundedCorner; +class TvAppColorFilter; +class TvAppNoRoundedCornerColorFilter; + +static bool _TvApp( + BitmapAllocationTestUtils::registerBitmapAllocationScene<TvApp>( + "tvapp", "A dense grid of cards:" + "with rounded corner, using overlay RenderNode for dimming.")); + +static bool _TvAppNoRoundedCorner( + BitmapAllocationTestUtils::registerBitmapAllocationScene<TvAppNoRoundedCorner>( + "tvapp_norc", "A dense grid of cards:" + "no rounded corner, using overlay RenderNode for dimming")); + +static bool _TvAppColorFilter( + BitmapAllocationTestUtils::registerBitmapAllocationScene<TvAppColorFilter>( + "tvapp_cf", "A dense grid of cards:" + "with rounded corner, using ColorFilter for dimming")); + +static bool _TvAppNoRoundedCornerColorFilter( + BitmapAllocationTestUtils::registerBitmapAllocationScene<TvAppNoRoundedCornerColorFilter>( + "tvapp_norc_cf", "A dense grid of cards:" + "no rounded corner, using ColorFilter for dimming")); + +class TvApp : public TestScene { +public: + TvApp(BitmapAllocationTestUtils::BitmapAllocator allocator) + : TestScene() + , mAllocator(allocator) { } + + sp<RenderNode> mBg; + std::vector<sp<RenderNode>> mCards; + std::vector<sp<RenderNode>> mInfoAreas; + std::vector<sp<RenderNode>> mImages; + std::vector<sp<RenderNode>> mOverlays; + std::vector<sk_sp<Bitmap>> mCachedBitmaps; + BitmapAllocationTestUtils::BitmapAllocator mAllocator; + sk_sp<Bitmap> mSingleBitmap; + int mSeed = 0; + int mSeed2 = 0; + + void createContent(int width, int height, Canvas& canvas) override { + mBg = createBitmapNode(canvas, 0xFF9C27B0, 0, 0, width, height); + canvas.drawRenderNode(mBg.get()); + + canvas.insertReorderBarrier(true); + mSingleBitmap = mAllocator(dp(160), dp(120), kRGBA_8888_SkColorType, + [](SkBitmap& skBitmap) { + skBitmap.eraseColor(0xFF0000FF); + }); + + for (int y = dp(18) - dp(178); y < height - dp(18); y += dp(178)) { + bool isFirstCard = true; + for (int x = dp(18); x < width - dp(18); x += dp(178)) { + sp<RenderNode> card = createCard(x, y, dp(160), dp(160), isFirstCard); + isFirstCard = false; + canvas.drawRenderNode(card.get()); + mCards.push_back(card); + } + } + canvas.insertReorderBarrier(false); + } + + void doFrame(int frameNr) override { + size_t numCards = mCards.size(); + for (size_t ci = 0; ci < numCards; ci++) { + updateCard(ci, frameNr); + } + } + +private: + sp<RenderNode> createBitmapNode(Canvas& canvas, SkColor color, int left, int top, + int width, int height) { + return TestUtils::createNode(left, top, left + width , top + height, + [this, width, height, color](RenderProperties& props, Canvas& canvas) { + sk_sp<Bitmap> bitmap = mAllocator(width, height, kRGBA_8888_SkColorType, + [color](SkBitmap& skBitmap) { + skBitmap.eraseColor(color); + }); + canvas.drawBitmap(*bitmap, 0, 0, nullptr); + }); + } + + sp<RenderNode> createSharedBitmapNode(Canvas& canvas, int left, int top, + int width, int height, sk_sp<Bitmap>bitmap) { + return TestUtils::createNode(left, top, left + width , top + height, + [bitmap](RenderProperties& props, Canvas& canvas) { + canvas.drawBitmap(*bitmap, 0, 0, nullptr); + }); + } + + sp<RenderNode> createInfoNode(Canvas& canvas, int left, int top, + int width, int height, const char* text, const char* text2) { + return TestUtils::createNode(left, top, left + width , top + height, + [text, text2](RenderProperties& props, Canvas& canvas) { + canvas.drawColor(0xFFFFEEEE, SkBlendMode::kSrcOver); + + SkPaint paint; + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + paint.setAntiAlias(true); + paint.setTextSize(24); + + paint.setColor(Color::Black); + TestUtils::drawUtf8ToCanvas(&canvas, text, paint, 10, 30); + paint.setTextSize(20); + TestUtils::drawUtf8ToCanvas(&canvas, text2, paint, 10, 54); + + }); + } + + sp<RenderNode> createColorNode(Canvas& canvas, int left, int top, + int width, int height, SkColor color) { + return TestUtils::createNode(left, top, left + width , top + height, + [color](RenderProperties& props, Canvas& canvas) { + canvas.drawColor(color, SkBlendMode::kSrcOver); + }); + } + + virtual bool useSingleBitmap() { + return false; + } + + virtual float roundedCornerRadius() { + return dp(2); + } + + // when true, use overlay RenderNode for dimming, otherwise apply a ColorFilter to dim image + virtual bool useOverlay() { + return true; + } + + sp<RenderNode> createCard(int x, int y, int width, int height, bool selected) { + return TestUtils::createNode(x, y, x + width, y + height, + [width, height, selected, this](RenderProperties& props, Canvas& canvas) { + if (selected) { + props.setElevation(dp(16)); + props.setScaleX(1.2); + props.setScaleY(1.2); + } + props.mutableOutline().setRoundRect(0, 0, width, height, roundedCornerRadius(), 1); + props.mutableOutline().setShouldClip(true); + + sk_sp<Bitmap> bitmap = useSingleBitmap() ? mSingleBitmap + : mAllocator(width, dp(120), kRGBA_8888_SkColorType, [this](SkBitmap& skBitmap) { + skBitmap.eraseColor(0xFF000000 | ((mSeed << 3) & 0xFF)); + }); + sp<RenderNode> cardImage = createSharedBitmapNode(canvas, 0, 0, width, dp(120), + bitmap); + canvas.drawRenderNode(cardImage.get()); + mCachedBitmaps.push_back(bitmap); + mImages.push_back(cardImage); + + char buffer[128]; + sprintf(buffer, "Video %d-%d", mSeed, mSeed + 1); + mSeed++; + char buffer2[128]; + sprintf(buffer2, "Studio %d", mSeed2++); + sp<RenderNode> infoArea = createInfoNode(canvas, 0, dp(120), width, height, buffer, buffer2); + canvas.drawRenderNode(infoArea.get()); + mInfoAreas.push_back(infoArea); + + if (useOverlay()) { + sp<RenderNode> overlayColor = createColorNode(canvas, 0, 0, width, height, 0x00000000); + canvas.drawRenderNode(overlayColor.get()); + mOverlays.push_back(overlayColor); + } + }); + } + + void updateCard(int ci, int curFrame) { + // updating card's translation Y + sp<RenderNode> card = mCards[ci]; + card->setPropertyFieldsDirty(RenderNode::Y); + card->mutateStagingProperties().setTranslationY(curFrame % 150); + + // re-recording card's canvas, not necessary but to add some burden to CPU + std::unique_ptr<Canvas> cardcanvas(Canvas::create_recording_canvas( + card->stagingProperties().getWidth(), + card->stagingProperties().getHeight())); + sp<RenderNode> image = mImages[ci]; + sp<RenderNode> infoArea = mInfoAreas[ci]; + cardcanvas->drawRenderNode(infoArea.get()); + + if (useOverlay()) { + cardcanvas->drawRenderNode(image.get()); + // re-recording card overlay's canvas, animating overlay color alpha + sp<RenderNode> overlay = mOverlays[ci]; + std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas( + overlay->stagingProperties().getWidth(), + overlay->stagingProperties().getHeight())); + canvas->drawColor((curFrame % 150) << 24, SkBlendMode::kSrcOver); + overlay->setStagingDisplayList(canvas->finishRecording()); + cardcanvas->drawRenderNode(overlay.get()); + } else { + // re-recording image node's canvas, animating ColorFilter + std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas( + image->stagingProperties().getWidth(), + image->stagingProperties().getHeight())); + SkPaint paint; + sk_sp<SkColorFilter> filter(SkColorFilter::MakeModeFilter((curFrame % 150) << 24, + SkBlendMode::kSrcATop)); + paint.setColorFilter(filter); + sk_sp<Bitmap> bitmap = mCachedBitmaps[ci]; + canvas->drawBitmap(*bitmap, 0, 0, &paint); + image->setStagingDisplayList(canvas->finishRecording()); + cardcanvas->drawRenderNode(image.get()); + } + + card->setStagingDisplayList(cardcanvas->finishRecording()); + } +}; + +class TvAppNoRoundedCorner : public TvApp { +public: + TvAppNoRoundedCorner(BitmapAllocationTestUtils::BitmapAllocator allocator) + : TvApp(allocator) { } + +private: + + virtual float roundedCornerRadius() override { + return dp(0); + } +}; + +class TvAppColorFilter : public TvApp { +public: + TvAppColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator) + : TvApp(allocator) { } + +private: + + virtual bool useOverlay() override { + return false; + } +}; + +class TvAppNoRoundedCornerColorFilter : public TvApp { +public: + TvAppNoRoundedCornerColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator) + : TvApp(allocator) { } + +private: + + virtual float roundedCornerRadius() override { + return dp(0); + } + + virtual bool useOverlay() override { + return false; + } +}; + + diff --git a/libs/hwui/tests/unit/BakedOpRendererTests.cpp b/libs/hwui/tests/unit/BakedOpRendererTests.cpp index 603599ceb88a..38e106a8ab77 100644 --- a/libs/hwui/tests/unit/BakedOpRendererTests.cpp +++ b/libs/hwui/tests/unit/BakedOpRendererTests.cpp @@ -17,6 +17,7 @@ #include <gtest/gtest.h> #include <BakedOpRenderer.h> +#include <GlopBuilder.h> #include <tests/common/TestUtils.h> using namespace android::uirenderer; @@ -53,3 +54,53 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpRenderer, startRepaintLayer_clear) { renderer.endLayer(); } } + +static void drawFirstOp(RenderState& renderState, int color, SkBlendMode mode) { + BakedOpRenderer renderer(Caches::getInstance(), renderState, true, false, sLightInfo); + + renderer.startFrame(100, 100, Rect(100, 100)); + SkPaint paint; + paint.setColor(color); + paint.setBlendMode(mode); + + Rect dest(0, 0, 100, 100); + Glop glop; + GlopBuilder(renderState, Caches::getInstance(), &glop) + .setRoundRectClipState(nullptr) + .setMeshUnitQuad() + .setFillPaint(paint, 1.0f) + .setTransform(Matrix4::identity(), TransformFlags::None) + .setModelViewMapUnitToRectSnap(dest) + .build(); + renderer.renderGlop(nullptr, nullptr, glop); + renderer.endFrame(Rect(100, 100)); +} + +static void verifyBlend(RenderState& renderState, GLenum expectedSrc, GLenum expectedDst) { + EXPECT_TRUE(renderState.blend().getEnabled()); + GLenum src; + GLenum dst; + renderState.blend().getFactors(&src, &dst); + EXPECT_EQ(expectedSrc, src); + EXPECT_EQ(expectedDst, dst); +} + +static void verifyBlendDisabled(RenderState& renderState) { + EXPECT_FALSE(renderState.blend().getEnabled()); +} + +RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpRenderer, firstDrawBlend_clear) { + // initialize blend state to nonsense value + renderThread.renderState().blend().setFactors(GL_ONE, GL_ONE); + + drawFirstOp(renderThread.renderState(), 0xfeff0000, SkBlendMode::kClear); + verifyBlend(renderThread.renderState(), GL_ZERO, GL_ONE_MINUS_SRC_ALPHA); +} + +RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpRenderer, firstDrawBlend_srcover) { + // initialize blend state to nonsense value + renderThread.renderState().blend().setFactors(GL_ONE, GL_ONE); + + drawFirstOp(renderThread.renderState(), 0xfeff0000, SkBlendMode::kSrcOver); + verifyBlendDisabled(renderThread.renderState()); +} diff --git a/libs/incident/include/android/os/IncidentReportArgs.h b/libs/incident/include/android/os/IncidentReportArgs.h index 956ef6c39b99..da8098970962 100644 --- a/libs/incident/include/android/os/IncidentReportArgs.h +++ b/libs/incident/include/android/os/IncidentReportArgs.h @@ -39,12 +39,13 @@ public: virtual status_t readFromParcel(const Parcel* in); void setAll(bool all); + void setDest(int dest); void addSection(int section); void addHeader(const vector<int8_t>& header); - inline bool all() const { return mAll; }; + inline bool all() const { return mAll; } bool containsSection(int section) const; - + inline int dest() const { return mDest; } inline const set<int>& sections() const { return mSections; } inline const vector<vector<int8_t>>& headers() const { return mHeaders; } @@ -54,6 +55,7 @@ private: set<int> mSections; vector<vector<int8_t>> mHeaders; bool mAll; + int mDest; }; } diff --git a/libs/incident/src/IncidentReportArgs.cpp b/libs/incident/src/IncidentReportArgs.cpp index f60490911aed..e62872238387 100644 --- a/libs/incident/src/IncidentReportArgs.cpp +++ b/libs/incident/src/IncidentReportArgs.cpp @@ -25,14 +25,16 @@ namespace os { IncidentReportArgs::IncidentReportArgs() :mSections(), - mAll(false) + mAll(false), + mDest(-1) { } IncidentReportArgs::IncidentReportArgs(const IncidentReportArgs& that) :mSections(that.mSections), mHeaders(that.mHeaders), - mAll(that.mAll) + mAll(that.mAll), + mDest(that.mDest) { } @@ -74,6 +76,11 @@ IncidentReportArgs::writeToParcel(Parcel* out) const } } + err = out->writeInt32(mDest); + if (err != NO_ERROR) { + return err; + } + return NO_ERROR; } @@ -120,6 +127,13 @@ IncidentReportArgs::readFromParcel(const Parcel* in) } } + int32_t dest; + err = in->readInt32(&dest); + if (err != NO_ERROR) { + return err; + } + mDest = dest; + return OK; } @@ -133,6 +147,12 @@ IncidentReportArgs::setAll(bool all) } void +IncidentReportArgs::setDest(int dest) +{ + mDest = dest; +} + +void IncidentReportArgs::addSection(int section) { if (!mAll) { diff --git a/libs/services/include/android/os/DropBoxManager.h b/libs/services/include/android/os/DropBoxManager.h index 8717178bb7d6..2ed203d9f865 100644 --- a/libs/services/include/android/os/DropBoxManager.h +++ b/libs/services/include/android/os/DropBoxManager.h @@ -57,7 +57,7 @@ public: // and a handle will be passed to the system process, so no additional permissions // are required from the system process. Returns NULL if the file can't be opened. Status addFile(const String16& tag, const string& filename, int flags); - + class Entry : public virtual RefBase, public Parcelable { public: Entry(); @@ -65,7 +65,12 @@ public: virtual status_t writeToParcel(Parcel* out) const; virtual status_t readFromParcel(const Parcel* in); - + + const vector<uint8_t>& getData() const; + const unique_fd& getFd() const; + int32_t getFlags() const; + int64_t getTimestamp() const; + private: Entry(const String16& tag, int32_t flags); Entry(const String16& tag, int32_t flags, int fd); @@ -80,6 +85,9 @@ public: friend class DropBoxManager; }; + // Get the next entry from the drop box after the specified time. + Status getNextEntry(const String16& tag, long msec, Entry* entry); + private: enum { HAS_BYTE_ARRAY = 8 diff --git a/libs/services/src/os/DropBoxManager.cpp b/libs/services/src/os/DropBoxManager.cpp index bbb45f022a87..1c760e850e4f 100644 --- a/libs/services/src/os/DropBoxManager.cpp +++ b/libs/services/src/os/DropBoxManager.cpp @@ -143,6 +143,29 @@ DropBoxManager::Entry::readFromParcel(const Parcel* in) return NO_ERROR; } +const vector<uint8_t>& +DropBoxManager::Entry::getData() const +{ + return mData; +} + +const unique_fd& +DropBoxManager::Entry::getFd() const +{ + return mFd; +} + +int32_t +DropBoxManager::Entry::getFlags() const +{ + return mFlags; +} + +int64_t +DropBoxManager::Entry::getTimestamp() const +{ + return mTimeMillis; +} DropBoxManager::DropBoxManager() { @@ -195,5 +218,16 @@ DropBoxManager::add(const Entry& entry) return service->add(entry); } +Status +DropBoxManager::getNextEntry(const String16& tag, long msec, Entry* entry) +{ + sp<IDropBoxManagerService> service = interface_cast<IDropBoxManagerService>( + defaultServiceManager()->getService(android::String16("dropbox"))); + if (service == NULL) { + return Status::fromExceptionCode(Status::EX_NULL_POINTER, "can't find dropbox service"); + } + return service->getNextEntry(tag, msec, entry); +} + }} // namespace android::os diff --git a/libs/usb/Android.bp b/libs/usb/Android.bp new file mode 100644 index 000000000000..b8f29043e597 --- /dev/null +++ b/libs/usb/Android.bp @@ -0,0 +1 @@ +subdirs = ["tests/*"] diff --git a/libs/usb/tests/AccessoryChat/Android.bp b/libs/usb/tests/AccessoryChat/Android.bp new file mode 100644 index 000000000000..4af6274b7ece --- /dev/null +++ b/libs/usb/tests/AccessoryChat/Android.bp @@ -0,0 +1 @@ +subdirs = ["accessorychat"] diff --git a/libs/usb/tests/AccessoryChat/accessorychat/Android.bp b/libs/usb/tests/AccessoryChat/accessorychat/Android.bp new file mode 100644 index 000000000000..5613745e966b --- /dev/null +++ b/libs/usb/tests/AccessoryChat/accessorychat/Android.bp @@ -0,0 +1,30 @@ +cc_binary { + name: "accessorychat", + host_supported: true, + + srcs: ["accessorychat.c"], + cflags: [ + "-Werror", + "-Wno-unused-parameter", + ], + + target: { + android: { + shared_libs: [ + "libusbhost", + "libcutils", + ], + }, + host: { + static_libs: [ + "libusbhost", + "libcutils", + ], + + cflags: ["-O0"], + }, + darwin: { + enabled: false, + }, + }, +} diff --git a/libs/usb/tests/AccessoryChat/accessorychat/Android.mk b/libs/usb/tests/AccessoryChat/accessorychat/Android.mk deleted file mode 100644 index 51f2111f1e0b..000000000000 --- a/libs/usb/tests/AccessoryChat/accessorychat/Android.mk +++ /dev/null @@ -1,35 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -# Build for Linux (desktop) host -ifeq ($(HOST_OS),linux) - -include $(CLEAR_VARS) -LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk - -LOCAL_MODULE_TAGS := optional - -LOCAL_SRC_FILES := accessorychat.c - -LOCAL_MODULE := accessorychat - -LOCAL_STATIC_LIBRARIES := libusbhost libcutils -LOCAL_LDLIBS += -lpthread -LOCAL_CFLAGS := -g -O0 - -include $(BUILD_HOST_EXECUTABLE) - -endif - -# Build for device -include $(CLEAR_VARS) -LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk - -LOCAL_MODULE_TAGS := optional - -LOCAL_SRC_FILES := accessorychat.c - -LOCAL_MODULE := accessorychat - -LOCAL_SHARED_LIBRARIES := libusbhost libcutils - -include $(BUILD_EXECUTABLE) diff --git a/libs/usb/tests/accessorytest/Android.bp b/libs/usb/tests/accessorytest/Android.bp new file mode 100644 index 000000000000..c6340e32e60c --- /dev/null +++ b/libs/usb/tests/accessorytest/Android.bp @@ -0,0 +1,28 @@ +cc_binary_host { + name: "accessorytest", + + srcs: [ + "accessory.c", + "audio.c", + "hid.c", + "usb.c", + ], + + static_libs: [ + "libusbhost", + "libcutils", + "libtinyalsa", + ], + cflags: [ + "-O0", + "-Wno-unused-parameter", + "-Werror", + ], + + target: { + darwin: { + // Build for Linux host only + enabled: false, + }, + }, +} diff --git a/libs/usb/tests/accessorytest/Android.mk b/libs/usb/tests/accessorytest/Android.mk deleted file mode 100644 index 6d9a9460c675..000000000000 --- a/libs/usb/tests/accessorytest/Android.mk +++ /dev/null @@ -1,25 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -# Build for Linux host only -ifeq ($(HOST_OS),linux) - -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := optional - -LOCAL_SRC_FILES := accessory.c \ - audio.c \ - hid.c \ - usb.c - -LOCAL_C_INCLUDES += external/tinyalsa/include - -LOCAL_MODULE := accessorytest - -LOCAL_STATIC_LIBRARIES := libusbhost libcutils libtinyalsa -LOCAL_LDLIBS += -lpthread -LOCAL_CFLAGS := -g -O0 - -include $(BUILD_HOST_EXECUTABLE) - -endif diff --git a/libs/usb/tests/accessorytest/accessory.h b/libs/usb/tests/accessorytest/accessory.h index 55c4550f79e0..e86986eb403f 100644 --- a/libs/usb/tests/accessorytest/accessory.h +++ b/libs/usb/tests/accessorytest/accessory.h @@ -19,7 +19,7 @@ int init_audio(unsigned int ic, unsigned int id, unsigned int oc, unsigned int od); void init_hid(); -void usb_run(int enable_accessory); +void usb_run(uintptr_t enable_accessory); struct usb_device* usb_wait_for_device(); diff --git a/libs/usb/tests/accessorytest/hid.c b/libs/usb/tests/accessorytest/hid.c index b70d678543ca..283755dbf215 100644 --- a/libs/usb/tests/accessorytest/hid.c +++ b/libs/usb/tests/accessorytest/hid.c @@ -139,7 +139,7 @@ static void open_hid(const char* name) fprintf(stderr, "opened /dev/%s\n", name); pthread_t th; - pthread_create(&th, NULL, hid_thread, (void *)fd); + pthread_create(&th, NULL, hid_thread, (void *)(uintptr_t)fd); } static void* inotify_thread(void* arg) diff --git a/libs/usb/tests/accessorytest/usb.c b/libs/usb/tests/accessorytest/usb.c index ac72b35b908e..1a161adf8ac2 100644 --- a/libs/usb/tests/accessorytest/usb.c +++ b/libs/usb/tests/accessorytest/usb.c @@ -219,7 +219,7 @@ struct usb_device* usb_wait_for_device() { return device; } -void usb_run(int enable_accessory) { +void usb_run(uintptr_t enable_accessory) { struct usb_host_context* context = usb_host_init(); usb_host_run(context, usb_device_added, usb_device_removed, NULL, (void *)enable_accessory); |