GrQuadBuffer.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. /*
  2. * Copyright 2019 Google LLC
  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. #ifndef GrQuadBuffer_DEFINED
  8. #define GrQuadBuffer_DEFINED
  9. #include "include/private/SkTDArray.h"
  10. #include "src/gpu/geometry/GrQuad.h"
  11. template<typename T>
  12. class GrQuadBuffer {
  13. public:
  14. GrQuadBuffer()
  15. : fCount(0)
  16. , fDeviceType(GrQuad::Type::kAxisAligned)
  17. , fLocalType(GrQuad::Type::kAxisAligned) {
  18. // Pre-allocate space for 1 2D device-space quad, metadata, and header
  19. fData.reserve(this->entrySize(fDeviceType, nullptr));
  20. }
  21. // Reserves space for the given number of entries; if 'needsLocals' is true, space will be
  22. // reserved for each entry to also have a 2D local quad. The reserved space assumes 2D device
  23. // quad for simplicity. Since this buffer has a variable bitrate encoding for quads, this may
  24. // over or under reserve, but pre-allocating still helps when possible.
  25. GrQuadBuffer(int count, bool needsLocals = false)
  26. : fCount(0)
  27. , fDeviceType(GrQuad::Type::kAxisAligned)
  28. , fLocalType(GrQuad::Type::kAxisAligned) {
  29. int entrySize = this->entrySize(fDeviceType, needsLocals ? &fLocalType : nullptr);
  30. fData.reserve(count * entrySize);
  31. }
  32. // The number of device-space quads (and metadata, and optional local quads) that are in the
  33. // the buffer.
  34. int count() const { return fCount; }
  35. // The most general type for the device-space quads in this buffer
  36. GrQuad::Type deviceQuadType() const { return fDeviceType; }
  37. // The most general type for the local quads; if no local quads are ever added, this will
  38. // return kAxisAligned.
  39. GrQuad::Type localQuadType() const { return fLocalType; }
  40. // Append the given 'deviceQuad' to this buffer, with its associated 'metadata'. If 'localQuad'
  41. // is not null, the local coordinates will also be attached to the entry. When an entry
  42. // has local coordinates, during iteration, the Iter::hasLocals() will return true and its
  43. // Iter::localQuad() will be equivalent to the provided local coordinates. If 'localQuad' is
  44. // null then Iter::hasLocals() will report false for the added entry.
  45. void append(const GrQuad& deviceQuad, T&& metadata, const GrQuad* localQuad = nullptr);
  46. // Copies all entries from 'that' to this buffer
  47. void concat(const GrQuadBuffer<T>& that);
  48. // Provides a read-only iterator over a quad buffer, giving access to the device quad, metadata
  49. // and optional local quad.
  50. class Iter {
  51. public:
  52. Iter(const GrQuadBuffer<T>* buffer)
  53. : fDeviceQuad(SkRect::MakeEmpty())
  54. , fLocalQuad(SkRect::MakeEmpty())
  55. , fBuffer(buffer)
  56. , fCurrentEntry(nullptr)
  57. , fNextEntry(buffer->fData.begin()) {
  58. SkDEBUGCODE(fExpectedCount = buffer->count();)
  59. }
  60. bool next();
  61. const T& metadata() const { this->validate(); return *(fBuffer->metadata(fCurrentEntry)); }
  62. const GrQuad& deviceQuad() const { this->validate(); return fDeviceQuad; }
  63. // If isLocalValid() returns false, this returns an empty quad (all 0s) so that localQuad()
  64. // can be called without triggering any sanitizers, for convenience when some other state
  65. // ensures that the quad will eventually not be used.
  66. const GrQuad& localQuad() const {
  67. this->validate();
  68. return fLocalQuad;
  69. }
  70. bool isLocalValid() const {
  71. this->validate();
  72. return fBuffer->header(fCurrentEntry)->fHasLocals;
  73. }
  74. private:
  75. // Quads are stored locally so that calling code doesn't need to re-declare their own quads
  76. GrQuad fDeviceQuad;
  77. GrQuad fLocalQuad;
  78. const GrQuadBuffer<T>* fBuffer;
  79. // The pointer to the current entry to read metadata/header details from
  80. const char* fCurrentEntry;
  81. // The pointer to replace fCurrentEntry when next() is called, cached since it is calculated
  82. // automatically while unpacking the quad data.
  83. const char* fNextEntry;
  84. SkDEBUGCODE(int fExpectedCount;)
  85. void validate() const {
  86. SkDEBUGCODE(fBuffer->validate(fCurrentEntry, fExpectedCount);)
  87. }
  88. };
  89. Iter iterator() const { return Iter(this); }
  90. // Provides a *mutable* iterator over just the metadata stored in the quad buffer. This skips
  91. // unpacking the device and local quads into GrQuads and is intended for use during op
  92. // finalization, which may require rewriting state such as color.
  93. class MetadataIter {
  94. public:
  95. MetadataIter(GrQuadBuffer<T>* list)
  96. : fBuffer(list)
  97. , fCurrentEntry(nullptr) {
  98. SkDEBUGCODE(fExpectedCount = list->count();)
  99. }
  100. bool next();
  101. T& operator*() { this->validate(); return *(fBuffer->metadata(fCurrentEntry)); }
  102. T* operator->() { this->validate(); return fBuffer->metadata(fCurrentEntry); }
  103. private:
  104. GrQuadBuffer<T>* fBuffer;
  105. char* fCurrentEntry;
  106. SkDEBUGCODE(int fExpectedCount;)
  107. void validate() const {
  108. SkDEBUGCODE(fBuffer->validate(fCurrentEntry, fExpectedCount);)
  109. }
  110. };
  111. MetadataIter metadata() { return MetadataIter(this); }
  112. private:
  113. struct alignas(int32_t) Header {
  114. unsigned fDeviceType : 2;
  115. unsigned fLocalType : 2; // Ignore if fHasLocals is false
  116. unsigned fHasLocals : 1;
  117. // Known value to detect if iteration doesn't properly advance through the buffer
  118. SkDEBUGCODE(unsigned fSentinel : 27;)
  119. };
  120. static_assert(sizeof(Header) == sizeof(int32_t), "Header should be 4 bytes");
  121. static constexpr unsigned kSentinel = 0xbaffe;
  122. static constexpr int kMetaSize = sizeof(Header) + sizeof(T);
  123. static constexpr int k2DQuadFloats = 8;
  124. static constexpr int k3DQuadFloats = 12;
  125. // Each logical entry in the buffer is a variable length tuple storing device coordinates,
  126. // optional local coordinates, and metadata. An entry always has a header that defines the
  127. // quad types of device and local coordinates, and always has metadata of type T. The device
  128. // and local quads' data follows as a variable length array of floats:
  129. // [ header ] = 4 bytes
  130. // [ metadata ] = sizeof(T), assert alignof(T) == 4 so that pointer casts are valid
  131. // [ device xs ] = 4 floats = 16 bytes
  132. // [ device ys ] = 4 floats
  133. // [ device ws ] = 4 floats or 0 floats depending on fDeviceType in header
  134. // [ local xs ] = 4 floats or 0 floats depending on fHasLocals in header
  135. // [ local ys ] = 4 floats or 0 floats depending on fHasLocals in header
  136. // [ local ws ] = 4 floats or 0 floats depending on fHasLocals and fLocalType in header
  137. // FIXME (michaelludwig) - Since this is intended only for ops, can we use the arena to
  138. // allocate storage for the quad buffer? Since this is forward-iteration only, could also
  139. // explore a linked-list structure for concatenating quads when batching ops
  140. SkTDArray<char> fData;
  141. int fCount; // Number of (device, local, metadata) entries
  142. GrQuad::Type fDeviceType; // Most general type of all entries
  143. GrQuad::Type fLocalType;
  144. inline int entrySize(GrQuad::Type deviceType, const GrQuad::Type* localType) const {
  145. int size = kMetaSize;
  146. size += (deviceType == GrQuad::Type::kPerspective ? k3DQuadFloats
  147. : k2DQuadFloats) * sizeof(float);
  148. if (localType) {
  149. size += (*localType == GrQuad::Type::kPerspective ? k3DQuadFloats
  150. : k2DQuadFloats) * sizeof(float);
  151. }
  152. return size;
  153. }
  154. inline int entrySize(const Header* header) const {
  155. if (header->fHasLocals) {
  156. GrQuad::Type localType = static_cast<GrQuad::Type>(header->fLocalType);
  157. return this->entrySize(static_cast<GrQuad::Type>(header->fDeviceType), &localType);
  158. } else {
  159. return this->entrySize(static_cast<GrQuad::Type>(header->fDeviceType), nullptr);
  160. }
  161. }
  162. // Helpers to access typed sections of the buffer, given the start of an entry
  163. inline Header* header(char* entry) {
  164. return static_cast<Header*>(static_cast<void*>(entry));
  165. }
  166. inline const Header* header(const char* entry) const {
  167. return static_cast<const Header*>(static_cast<const void*>(entry));
  168. }
  169. inline T* metadata(char* entry) {
  170. return static_cast<T*>(static_cast<void*>(entry + sizeof(Header)));
  171. }
  172. inline const T* metadata(const char* entry) const {
  173. return static_cast<const T*>(static_cast<const void*>(entry + sizeof(Header)));
  174. }
  175. inline float* coords(char* entry) {
  176. return static_cast<float*>(static_cast<void*>(entry + kMetaSize));
  177. }
  178. inline const float* coords(const char* entry) const {
  179. return static_cast<const float*>(static_cast<const void*>(entry + kMetaSize));
  180. }
  181. // Helpers to convert from coordinates to GrQuad and vice versa, returning pointer to the
  182. // next packed quad coordinates.
  183. float* packQuad(const GrQuad& quad, float* coords);
  184. const float* unpackQuad(GrQuad::Type type, const float* coords, GrQuad* quad) const;
  185. #ifdef SK_DEBUG
  186. void validate(const char* entry, int expectedCount) const;
  187. #endif
  188. };
  189. ///////////////////////////////////////////////////////////////////////////////////////////////////
  190. // Buffer implementation
  191. ///////////////////////////////////////////////////////////////////////////////////////////////////
  192. template<typename T>
  193. float* GrQuadBuffer<T>::packQuad(const GrQuad& quad, float* coords) {
  194. // Copies all 12 (or 8) floats at once, so requires the 3 arrays to be contiguous
  195. // FIXME(michaelludwig) - If this turns out not to be the case, just do 4 copies
  196. SkASSERT(quad.xs() + 4 == quad.ys() && quad.xs() + 8 == quad.ws());
  197. if (quad.hasPerspective()) {
  198. memcpy(coords, quad.xs(), k3DQuadFloats * sizeof(float));
  199. return coords + k3DQuadFloats;
  200. } else {
  201. memcpy(coords, quad.xs(), k2DQuadFloats * sizeof(float));
  202. return coords + k2DQuadFloats;
  203. }
  204. }
  205. template<typename T>
  206. const float* GrQuadBuffer<T>::unpackQuad(GrQuad::Type type, const float* coords, GrQuad* quad) const {
  207. SkASSERT(quad->xs() + 4 == quad->ys() && quad->xs() + 8 == quad->ws());
  208. if (type == GrQuad::Type::kPerspective) {
  209. // Fill in X, Y, and W in one go
  210. memcpy(quad->xs(), coords, k3DQuadFloats * sizeof(float));
  211. coords = coords + k3DQuadFloats;
  212. } else {
  213. // Fill in X and Y of the quad, and set W to 1s if needed
  214. memcpy(quad->xs(), coords, k2DQuadFloats * sizeof(float));
  215. coords = coords + k2DQuadFloats;
  216. if (quad->quadType() == GrQuad::Type::kPerspective) {
  217. // The output quad was previously perspective, so its ws are not 1s
  218. static constexpr float kNoPerspectiveWs[4] = {1.f, 1.f, 1.f, 1.f};
  219. memcpy(quad->ws(), kNoPerspectiveWs, 4 * sizeof(float));
  220. }
  221. // Else the quad should already have 1s in w
  222. SkASSERT(quad->w(0) == 1.f && quad->w(1) == 1.f &&
  223. quad->w(2) == 1.f && quad->w(3) == 1.f);
  224. }
  225. quad->setQuadType(type);
  226. return coords;
  227. }
  228. template<typename T>
  229. void GrQuadBuffer<T>::append(const GrQuad& deviceQuad, T&& metadata, const GrQuad* localQuad) {
  230. GrQuad::Type localType = localQuad ? localQuad->quadType() : GrQuad::Type::kAxisAligned;
  231. int entrySize = this->entrySize(deviceQuad.quadType(), localQuad ? &localType : nullptr);
  232. // Fill in the entry, as described in fData's declaration
  233. char* entry = fData.append(entrySize);
  234. // First the header
  235. Header* h = this->header(entry);
  236. h->fDeviceType = static_cast<unsigned>(deviceQuad.quadType());
  237. h->fHasLocals = static_cast<unsigned>(localQuad != nullptr);
  238. h->fLocalType = static_cast<unsigned>(localQuad ? localQuad->quadType()
  239. : GrQuad::Type::kAxisAligned);
  240. SkDEBUGCODE(h->fSentinel = static_cast<unsigned>(kSentinel);)
  241. // Second, the fixed-size metadata
  242. static_assert(alignof(T) == 4, "Metadata must be 4 byte aligned");
  243. *(this->metadata(entry)) = std::move(metadata);
  244. // Then the variable blocks of x, y, and w float coordinates
  245. float* coords = this->coords(entry);
  246. coords = this->packQuad(deviceQuad, coords);
  247. if (localQuad) {
  248. coords = this->packQuad(*localQuad, coords);
  249. }
  250. SkASSERT((char*)coords - entry == entrySize);
  251. // Entry complete, update buffer-level state
  252. fCount++;
  253. if (deviceQuad.quadType() > fDeviceType) {
  254. fDeviceType = deviceQuad.quadType();
  255. }
  256. if (localQuad && localQuad->quadType() > fLocalType) {
  257. fLocalType = localQuad->quadType();
  258. }
  259. }
  260. template<typename T>
  261. void GrQuadBuffer<T>::concat(const GrQuadBuffer<T>& that) {
  262. fData.append(that.fData.count(), that.fData.begin());
  263. fCount += that.fCount;
  264. if (that.fDeviceType > fDeviceType) {
  265. fDeviceType = that.fDeviceType;
  266. }
  267. if (that.fLocalType > fLocalType) {
  268. fLocalType = that.fLocalType;
  269. }
  270. }
  271. #ifdef SK_DEBUG
  272. template<typename T>
  273. void GrQuadBuffer<T>::validate(const char* entry, int expectedCount) const {
  274. // Triggers if accessing before next() is called on an iterator
  275. SkASSERT(entry);
  276. // Triggers if accessing after next() returns false
  277. SkASSERT(entry < fData.end());
  278. // Triggers if elements have been added to the buffer while iterating entries
  279. SkASSERT(expectedCount == fCount);
  280. // Make sure the start of the entry looks like a header
  281. SkASSERT(this->header(entry)->fSentinel == kSentinel);
  282. }
  283. #endif
  284. ///////////////////////////////////////////////////////////////////////////////////////////////////
  285. // Iterator implementations
  286. ///////////////////////////////////////////////////////////////////////////////////////////////////
  287. template<typename T>
  288. bool GrQuadBuffer<T>::Iter::next() {
  289. SkASSERT(fNextEntry);
  290. if (fNextEntry >= fBuffer->fData.end()) {
  291. return false;
  292. }
  293. // There is at least one more entry, so store the current start for metadata access
  294. fCurrentEntry = fNextEntry;
  295. // And then unpack the device and optional local coordinates into fDeviceQuad and fLocalQuad
  296. const Header* h = fBuffer->header(fCurrentEntry);
  297. const float* coords = fBuffer->coords(fCurrentEntry);
  298. coords = fBuffer->unpackQuad(static_cast<GrQuad::Type>(h->fDeviceType), coords, &fDeviceQuad);
  299. if (h->fHasLocals) {
  300. coords = fBuffer->unpackQuad(static_cast<GrQuad::Type>(h->fLocalType), coords, &fLocalQuad);
  301. } else {
  302. static const GrQuad kEmptyLocal(SkRect::MakeEmpty());
  303. fLocalQuad = kEmptyLocal;
  304. }
  305. // At this point, coords points to the start of the next entry
  306. fNextEntry = static_cast<const char*>(static_cast<const void*>(coords));
  307. SkASSERT((fNextEntry - fCurrentEntry) == fBuffer->entrySize(h));
  308. return true;
  309. }
  310. template<typename T>
  311. bool GrQuadBuffer<T>::MetadataIter::next() {
  312. if (fCurrentEntry) {
  313. // Advance pointer by entry size
  314. if (fCurrentEntry < fBuffer->fData.end()) {
  315. const Header* h = fBuffer->header(fCurrentEntry);
  316. fCurrentEntry += fBuffer->entrySize(h);
  317. }
  318. } else {
  319. // First call to next
  320. fCurrentEntry = fBuffer->fData.begin();
  321. }
  322. // Nothing else is needed to do but report whether or not the updated pointer is valid
  323. return fCurrentEntry < fBuffer->fData.end();
  324. }
  325. #endif // GrQuadBuffer_DEFINED