blob: b948566e2797c3c0943e7812b73d433af193b25a [file] [log] [blame]
rileya@google.com2e2aedc2012-08-13 20:28:48 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#ifndef GrTextureStripAtlas_DEFINED
9#define GrTextureStripAtlas_DEFINED
10
commit-bot@chromium.orga0b40282013-09-18 13:00:55 +000011#include "SkBitmap.h"
rileya@google.com2e2aedc2012-08-13 20:28:48 +000012#include "SkGr.h"
Robert Phillips96b6d532018-03-19 10:57:42 -040013#include "SkOpts.h"
14#include "SkRefCnt.h"
rileya@google.com2e2aedc2012-08-13 20:28:48 +000015#include "SkTDArray.h"
robertphillips3d533ac2014-07-20 09:40:00 -070016#include "SkTDynamicHash.h"
commit-bot@chromium.orga0b40282013-09-18 13:00:55 +000017#include "SkTypes.h"
rileya@google.com2e2aedc2012-08-13 20:28:48 +000018
Robert Phillips30f9bc62017-02-22 15:28:38 -050019class GrSurfaceContext;
20class GrTextureProxy;
21
rileya@google.com2e2aedc2012-08-13 20:28:48 +000022/**
rmistry@google.comfbfcd562012-08-23 18:09:54 +000023 * Maintains a single large texture whose rows store many textures of a small fixed height,
rileya@google.com2e2aedc2012-08-13 20:28:48 +000024 * stored in rows across the x-axis such that we can safely wrap/repeat them horizontally.
25 */
Robert Phillips96b6d532018-03-19 10:57:42 -040026class GrTextureStripAtlas : public SkRefCnt {
rileya@google.com2e2aedc2012-08-13 20:28:48 +000027public:
rileya@google.com2e2aedc2012-08-13 20:28:48 +000028 /**
29 * Descriptor struct which we'll use as a hash table key
Robert Phillips41a3b872018-03-09 12:00:34 -050030 */
rileya@google.com2e2aedc2012-08-13 20:28:48 +000031 struct Desc {
joshualitt690fc752015-07-13 12:49:13 -070032 Desc() { sk_bzero(this, sizeof(*this)); }
joshualitt690fc752015-07-13 12:49:13 -070033 GrPixelConfig fConfig;
34 uint16_t fWidth, fHeight, fRowHeight;
35 uint16_t fUnusedPadding;
36 bool operator==(const Desc& other) const {
37 return 0 == memcmp(this, &other, sizeof(Desc));
38 }
rileya@google.com2e2aedc2012-08-13 20:28:48 +000039 };
40
rileya@google.com2e2aedc2012-08-13 20:28:48 +000041 ~GrTextureStripAtlas();
42
43 /**
44 * Add a texture to the atlas
45 * @param data Bitmap data to copy into the row
46 * @return The row index we inserted into, or -1 if we failed to find an open row. The caller
47 * is responsible for calling unlockRow() with this row index when it's done with it.
48 */
Robert Phillips41a3b872018-03-09 12:00:34 -050049 int lockRow(GrContext*, const SkBitmap&);
50
Brian Salomon0e05a822017-07-25 09:43:22 -040051 /**
52 * This is intended to be used when cloning a processor that already holds a lock. It is
53 * assumed that the row already has at least one lock.
54 */
55 void lockRow(int row);
rileya@google.com2e2aedc2012-08-13 20:28:48 +000056 void unlockRow(int row);
57
rmistry@google.comfbfcd562012-08-23 18:09:54 +000058 /**
59 * These functions help turn an integer row index in [0, 1, 2, ... numRows] into a scalar y
rileya@google.com2e2aedc2012-08-13 20:28:48 +000060 * texture coordinate in [0, 1] that we can use in a shader.
61 *
rmistry@google.comfbfcd562012-08-23 18:09:54 +000062 * If a regular texture access without using the atlas looks like:
rileya@google.com2e2aedc2012-08-13 20:28:48 +000063 *
Ethan Nicholas5af9ea32017-07-28 15:19:46 -040064 * texture2D(sampler, float2(x, y))
rileya@google.com2e2aedc2012-08-13 20:28:48 +000065 *
rmistry@google.comfbfcd562012-08-23 18:09:54 +000066 * Then when using the atlas we'd replace it with:
rileya@google.com2e2aedc2012-08-13 20:28:48 +000067 *
Ethan Nicholas5af9ea32017-07-28 15:19:46 -040068 * texture2D(sampler, float2(x, yOffset + y * scaleFactor))
rileya@google.com2e2aedc2012-08-13 20:28:48 +000069 *
70 * Where yOffset, returned by getYOffset(), is the offset to the start of the row within the
bsalomonc6327a82014-10-27 12:53:08 -070071 * atlas and scaleFactor, returned by getNormalizedTexelHeight, is the normalized height of
72 * one texel row.
rileya@google.com2e2aedc2012-08-13 20:28:48 +000073 */
bsalomon@google.com81712882012-11-01 17:12:34 +000074 SkScalar getYOffset(int row) const { return SkIntToScalar(row) / fNumRows; }
bsalomonc6327a82014-10-27 12:53:08 -070075 SkScalar getNormalizedTexelHeight() const { return fNormalizedYHeight; }
rileya@google.com2e2aedc2012-08-13 20:28:48 +000076
Robert Phillips30f9bc62017-02-22 15:28:38 -050077 sk_sp<GrTextureProxy> asTextureProxyRef() const;
rileya@google.com2e2aedc2012-08-13 20:28:48 +000078
79private:
Robert Phillips41a3b872018-03-09 12:00:34 -050080 friend class GrTextureStripAtlasManager; // for ctor
81
82 static uint32_t CreateUniqueID();
rileya@google.com2e2aedc2012-08-13 20:28:48 +000083
84 // Key to indicate an atlas row without any meaningful data stored in it
85 const static uint32_t kEmptyAtlasRowKey = 0xffffffff;
86
rmistry@google.comfbfcd562012-08-23 18:09:54 +000087 /**
rileya@google.com2e2aedc2012-08-13 20:28:48 +000088 * The state of a single row in our cache, next/prev pointers allow these to be chained
89 * together to represent LRU status
90 */
Robert Phillips96b6d532018-03-19 10:57:42 -040091 struct AtlasRow : ::SkNoncopyable {
halcanary96fcdcc2015-08-27 07:41:13 -070092 AtlasRow() : fKey(kEmptyAtlasRowKey), fLocks(0), fNext(nullptr), fPrev(nullptr) { }
rileya@google.com2e2aedc2012-08-13 20:28:48 +000093 // GenerationID of the bitmap that is represented by this row, 0xffffffff means "empty"
rmistry@google.comfbfcd562012-08-23 18:09:54 +000094 uint32_t fKey;
rileya@google.com2e2aedc2012-08-13 20:28:48 +000095 // How many times this has been locked (0 == unlocked)
rmistry@google.comfbfcd562012-08-23 18:09:54 +000096 int32_t fLocks;
rileya@google.com2e2aedc2012-08-13 20:28:48 +000097 // We maintain an LRU linked list between unlocked nodes with these pointers
98 AtlasRow* fNext;
99 AtlasRow* fPrev;
100 };
101
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000102 /**
Robert Phillips41a3b872018-03-09 12:00:34 -0500103 * Only the GrTextureStripAtlasManager is allowed to create GrTextureStripAtlases
rileya@google.com2e2aedc2012-08-13 20:28:48 +0000104 */
Robert Phillips41a3b872018-03-09 12:00:34 -0500105 GrTextureStripAtlas(const Desc& desc);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000106
Robert Phillips41a3b872018-03-09 12:00:34 -0500107 void lockTexture(GrContext*);
rileya@google.com2e2aedc2012-08-13 20:28:48 +0000108 void unlockTexture();
109
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000110 /**
rileya@google.com2e2aedc2012-08-13 20:28:48 +0000111 * Initialize our LRU list (if one already exists, clear it and start anew)
112 */
113 void initLRU();
114
115 /**
Robert Phillips41a3b872018-03-09 12:00:34 -0500116 * Grabs the least recently used free row out of the LRU list, returns nullptr if no rows
117 * are free.
rileya@google.com2e2aedc2012-08-13 20:28:48 +0000118 */
119 AtlasRow* getLRU();
120
121 void appendLRU(AtlasRow* row);
122 void removeFromLRU(AtlasRow* row);
123
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000124 /**
125 * Searches the key table for a key and returns the index if found; if not found, it returns
rileya@google.com2e2aedc2012-08-13 20:28:48 +0000126 * the bitwise not of the index at which we could insert the key to maintain a sorted list.
127 **/
128 int searchByKey(uint32_t key);
129
130 /**
131 * Compare two atlas rows by key, so we can sort/search by key
132 */
bsalomon@google.com20f7f172013-05-17 19:05:03 +0000133 static bool KeyLess(const AtlasRow& lhs, const AtlasRow& rhs) {
134 return lhs.fKey < rhs.fKey;
rileya@google.com2e2aedc2012-08-13 20:28:48 +0000135 }
136
137#ifdef SK_DEBUG
138 void validate();
139#endif
140
Robert Phillips41a3b872018-03-09 12:00:34 -0500141 // A unique ID for this atlas, so we can be sure that if we
rileya@google.comf61c7462012-08-13 21:03:39 +0000142 // get a texture back from the texture cache, that it's the same one we last used.
Robert Phillips41a3b872018-03-09 12:00:34 -0500143 const uint32_t fCacheKey;
rileya@google.com2e2aedc2012-08-13 20:28:48 +0000144
145 // Total locks on all rows (when this reaches zero, we can unlock our texture)
146 int32_t fLockedRows;
147
148 const Desc fDesc;
149 const uint16_t fNumRows;
Robert Phillips30f9bc62017-02-22 15:28:38 -0500150 sk_sp<GrSurfaceContext> fTexContext;
rileya@google.com2e2aedc2012-08-13 20:28:48 +0000151
bsalomonc6327a82014-10-27 12:53:08 -0700152 SkScalar fNormalizedYHeight;
153
rileya@google.com2e2aedc2012-08-13 20:28:48 +0000154 // Array of AtlasRows which store the state of all our rows. Stored in a contiguous array, in
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000155 // order that they appear in our texture, this means we can subtract this pointer from a row
rileya@google.com2e2aedc2012-08-13 20:28:48 +0000156 // pointer to get its index in the texture, and can save storing a row number in AtlasRow.
157 AtlasRow* fRows;
158
159 // Head and tail for linked list of least-recently-used rows (front = least recently used).
160 // Note that when a texture is locked, it gets removed from this list until it is unlocked.
161 AtlasRow* fLRUFront;
162 AtlasRow* fLRUBack;
163
164 // A list of pointers to AtlasRows that currently contain cached images, sorted by key
165 SkTDArray<AtlasRow*> fKeyTable;
166};
167
Robert Phillips41a3b872018-03-09 12:00:34 -0500168class GrTextureStripAtlasManager {
169public:
170 GrTextureStripAtlasManager() {}
171 ~GrTextureStripAtlasManager();
172
173 void abandon();
174
175 /**
176 * Try to find an atlas with the required parameters, creates a new one if necessary
177 */
Robert Phillips96b6d532018-03-19 10:57:42 -0400178 sk_sp<GrTextureStripAtlas> refAtlas(const GrTextureStripAtlas::Desc&);
Robert Phillips41a3b872018-03-09 12:00:34 -0500179
180private:
181 void deleteAllAtlases();
182
183 // Hash table entry for atlases
184 class AtlasEntry : public ::SkNoncopyable {
185 public:
Robert Phillips96b6d532018-03-19 10:57:42 -0400186 AtlasEntry(const GrTextureStripAtlas::Desc& desc, sk_sp<GrTextureStripAtlas> atlas)
Robert Phillips41a3b872018-03-09 12:00:34 -0500187 : fDesc(desc)
Robert Phillips96b6d532018-03-19 10:57:42 -0400188 , fAtlas(std::move(atlas)) {
Robert Phillips41a3b872018-03-09 12:00:34 -0500189 }
Robert Phillips96b6d532018-03-19 10:57:42 -0400190 ~AtlasEntry() { }
Robert Phillips41a3b872018-03-09 12:00:34 -0500191
192 // for SkTDynamicHash
193 static const GrTextureStripAtlas::Desc& GetKey(const AtlasEntry& entry) {
194 return entry.fDesc;
195 }
196 static uint32_t Hash(const GrTextureStripAtlas::Desc& desc) {
197 return SkOpts::hash(&desc, sizeof(GrTextureStripAtlas::Desc));
198 }
199
200 const GrTextureStripAtlas::Desc fDesc;
Robert Phillips96b6d532018-03-19 10:57:42 -0400201 sk_sp<GrTextureStripAtlas> fAtlas;
Robert Phillips41a3b872018-03-09 12:00:34 -0500202 };
203
204 typedef SkTDynamicHash<AtlasEntry, GrTextureStripAtlas::Desc> AtlasHash;
205
206 AtlasHash fAtlasCache;
207};
208
rileya@google.com2e2aedc2012-08-13 20:28:48 +0000209#endif