1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 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 */
|