diff options
Diffstat (limited to 'libs/hwui/pipeline/skia/VectorDrawableAtlas.h')
-rw-r--r-- | libs/hwui/pipeline/skia/VectorDrawableAtlas.h | 198 |
1 files changed, 198 insertions, 0 deletions
diff --git a/libs/hwui/pipeline/skia/VectorDrawableAtlas.h b/libs/hwui/pipeline/skia/VectorDrawableAtlas.h new file mode 100644 index 000000000000..496c55742748 --- /dev/null +++ b/libs/hwui/pipeline/skia/VectorDrawableAtlas.h @@ -0,0 +1,198 @@ +/* + * 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. + */ + +#pragma once + +#include <map> +#include <SkSurface.h> +#include <utils/FatVector.h> +#include <utils/RefBase.h> +#include <list> + +class GrRectanizer; + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +typedef uintptr_t AtlasKey; + +#define INVALID_ATLAS_KEY 0 + +struct AtlasEntry { + sk_sp<SkSurface> surface; + SkRect rect; + AtlasKey key = INVALID_ATLAS_KEY; +}; + +/** + * VectorDrawableAtlas provides offscreen buffers used to draw VD and AnimatedVD. + * VectorDrawableAtlas can allocate a standalone surface or provide a subrect from a shared surface. + * VectorDrawableAtlas is owned by the CacheManager and weak pointers are kept by each + * VectorDrawable that is using it. VectorDrawableAtlas and its surface can be deleted at any time, + * except during a renderFrame call. VectorDrawable does not contain a pointer to atlas SkSurface + * nor any coordinates into the atlas, but instead holds a rectangle "id", which is resolved only + * when drawing. This design makes VectorDrawableAtlas free to move the data internally. + * At draw time a VectorDrawable may find, that its atlas has been deleted, which will make it + * draw in a standalone cache surface not part of an atlas. In this case VD won't use + * VectorDrawableAtlas until the next frame. + * VectorDrawableAtlas tries to fit VDs in the atlas SkSurface. If there is not enough space in + * the atlas, VectorDrawableAtlas creates a standalone surface for each VD. + * When a VectorDrawable is deleted, it invokes VectorDrawableAtlas::releaseEntry, which is keeping + * track of free spaces and allow to reuse the surface for another VD. + */ + //TODO: Check if not using atlas for AnimatedVD is more efficient. + //TODO: For low memory situations, when there are no paint effects in VD, we may render without an + //TODO: offscreen surface. +class VectorDrawableAtlas : public virtual RefBase { +public: + enum class StorageMode { + allowSharedSurface, + disallowSharedSurface + }; + + VectorDrawableAtlas(size_t surfaceArea, + StorageMode storageMode = StorageMode::allowSharedSurface); + + /** + * "prepareForDraw" may allocate a new surface if needed. It may schedule to repack the + * atlas at a later time. + */ + void prepareForDraw(GrContext* context); + + /** + * Repack the atlas if needed, by moving used rectangles into a new atlas surface. + * The goal of repacking is to fix a fragmented atlas. + */ + void repackIfNeeded(GrContext* context); + + /** + * Returns true if atlas is fragmented and repack is needed. + */ + bool isFragmented(); + + /** + * "requestNewEntry" is called by VectorDrawable to allocate a new rectangle area from the atlas + * or create a standalone surface if atlas is full. + * On success it returns a non-negative unique id, which can be used later with "getEntry" and + * "releaseEntry". + */ + AtlasEntry requestNewEntry(int width, int height, GrContext* context); + + /** + * "getEntry" extracts coordinates and surface of a previously created rectangle. + * "atlasKey" is an unique id created by "requestNewEntry". Passing a non-existing "atlasKey" is + * causing an undefined behaviour. + * On success it returns a rectangle Id -> may be same or different from "atlasKey" if + * implementation decides to move the record internally. + */ + AtlasEntry getEntry(AtlasKey atlasKey); + + /** + * "releaseEntry" is invoked when a VectorDrawable is deleted. Passing a non-existing "atlasKey" + * is causing an undefined behaviour. + */ + void releaseEntry(AtlasKey atlasKey); + + void setStorageMode(StorageMode mode); + +private: + struct CacheEntry { + CacheEntry(const SkRect& newVDrect, const SkRect& newRect, + const sk_sp<SkSurface>& newSurface) + : VDrect(newVDrect) + , rect(newRect) + , surface(newSurface) { } + + /** + * size and position of VectorDrawable into the atlas or in "this.surface" + */ + SkRect VDrect; + + /** + * rect allocated in atlas surface or "this.surface". It may be bigger than "VDrect" + */ + SkRect rect; + + /** + * this surface is used if atlas is full or VD is too big + */ + sk_sp<SkSurface> surface; + + /** + * iterator is used to delete self with a constant complexity (without traversing the list) + */ + std::list<CacheEntry>::iterator eraseIt; + }; + + /** + * atlas surface shared by all VDs + */ + sk_sp<SkSurface> mSurface; + + std::unique_ptr<GrRectanizer> mRectanizer; + const int mWidth; + const int mHeight; + + /** + * "mRects" keeps records only for rectangles used by VDs. List has nice properties: constant + * complexity to insert and erase and references are not invalidated by insert/erase. + */ + std::list<CacheEntry> mRects; + + /** + * Rectangles freed by "releaseEntry" are removed from "mRects" and added to "mFreeRects". + * "mFreeRects" is using for an index the rectangle area. There could be more than one free + * rectangle with the same area, which is the reason to use "multimap" instead of "map". + */ + std::multimap<size_t, SkRect> mFreeRects; + + /** + * area in atlas used by VectorDrawables (area in standalone surface not counted) + */ + int mPixelUsedByVDs = 0; + + /** + * area allocated in mRectanizer + */ + int mPixelAllocated = 0; + + /** + * Consecutive times we had to allocate standalone surfaces, because atlas was full. + */ + int mConsecutiveFailures = 0; + + /** + * mStorageMode allows using a shared surface to store small vector drawables. + * Using a shared surface can boost the performance by allowing GL ops to be batched, but may + * consume more memory. + */ + StorageMode mStorageMode; + + sk_sp<SkSurface> createSurface(int width, int height, GrContext* context); + + inline bool fitInAtlas(int width, int height) { + return 2*width < mWidth && 2*height < mHeight; + } + + void repack(GrContext* context); + + static bool compareCacheEntry(const CacheEntry& first, const CacheEntry& second); +}; + +} /* namespace skiapipeline */ +} /* namespace uirenderer */ +} /* namespace android */ |