Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
GrQuadBuffer.h
Go to the documentation of this file.
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
10#include "include/core/SkRect.h"
15
16#include <cstdint>
17#include <cstring>
18
19template<typename T>
21public:
23 : fCount(0)
24 , fDeviceType(GrQuad::Type::kAxisAligned)
25 , fLocalType(GrQuad::Type::kAxisAligned) {
26 // Pre-allocate space for 1 2D device-space quad, metadata, and header
27 fData.reserve(this->entrySize(fDeviceType, nullptr));
28 }
29
30 // Reserves space for the given number of entries; if 'needsLocals' is true, space will be
31 // reserved for each entry to also have a 2D local quad. The reserved space assumes 2D device
32 // quad for simplicity. Since this buffer has a variable bitrate encoding for quads, this may
33 // over or under reserve, but pre-allocating still helps when possible.
34 GrQuadBuffer(int count, bool needsLocals = false)
35 : fCount(0)
36 , fDeviceType(GrQuad::Type::kAxisAligned)
37 , fLocalType(GrQuad::Type::kAxisAligned) {
38 int entrySize = this->entrySize(fDeviceType, needsLocals ? &fLocalType : nullptr);
39 fData.reserve(count * entrySize);
40 }
41
42 // The number of device-space quads (and metadata, and optional local quads) that are in the
43 // the buffer.
44 int count() const { return fCount; }
45
46 // The most general type for the device-space quads in this buffer
47 GrQuad::Type deviceQuadType() const { return fDeviceType; }
48
49 // The most general type for the local quads; if no local quads are ever added, this will
50 // return kAxisAligned.
51 GrQuad::Type localQuadType() const { return fLocalType; }
52
53 // Append the given 'deviceQuad' to this buffer, with its associated 'metadata'. If 'localQuad'
54 // is not null, the local coordinates will also be attached to the entry. When an entry
55 // has local coordinates, during iteration, the Iter::hasLocals() will return true and its
56 // Iter::localQuad() will be equivalent to the provided local coordinates. If 'localQuad' is
57 // null then Iter::hasLocals() will report false for the added entry.
58 void append(const GrQuad& deviceQuad, T&& metadata, const GrQuad* localQuad = nullptr);
59
60 // Copies all entries from 'that' to this buffer
61 void concat(const GrQuadBuffer<T>& that);
62
63 // Provides a read-only iterator over a quad buffer, giving access to the device quad, metadata
64 // and optional local quad.
65 class Iter {
66 public:
68 : fDeviceQuad(SkRect::MakeEmpty())
69 , fLocalQuad(SkRect::MakeEmpty())
70 , fBuffer(buffer)
71 , fCurrentEntry(nullptr)
72 , fNextEntry(buffer->fData.begin()) {
73 SkDEBUGCODE(fExpectedCount = buffer->count();)
74 }
75
76 bool next();
77
78 const T& metadata() const { this->validate(); return *(fBuffer->metadata(fCurrentEntry)); }
79
80 // The returned pointer is mutable so that the object can be used for scratch calculations
81 // during op preparation. However, any changes are not persisted in the GrQuadBuffer and
82 // subsequent calls to next() will overwrite the state of the GrQuad.
83 GrQuad* deviceQuad() { this->validate(); return &fDeviceQuad; }
84
85 // If isLocalValid() returns false, this returns nullptr. Otherwise, the returned pointer
86 // is mutable in the same manner as deviceQuad().
88 this->validate();
89 return this->isLocalValid() ? &fLocalQuad : nullptr;
90 }
91
92 bool isLocalValid() const {
93 this->validate();
94 return fBuffer->header(fCurrentEntry)->fHasLocals;
95 }
96
97 private:
98 // Quads are stored locally so that calling code doesn't need to re-declare their own quads
99 GrQuad fDeviceQuad;
100 GrQuad fLocalQuad;
101
102 const GrQuadBuffer<T>* fBuffer;
103 // The pointer to the current entry to read metadata/header details from
104 const char* fCurrentEntry;
105 // The pointer to replace fCurrentEntry when next() is called, cached since it is calculated
106 // automatically while unpacking the quad data.
107 const char* fNextEntry;
108
109 SkDEBUGCODE(int fExpectedCount;)
110
111 void validate() const {
112 SkDEBUGCODE(fBuffer->validate(fCurrentEntry, fExpectedCount);)
113 }
114 };
115
116 Iter iterator() const { return Iter(this); }
117
118 // Provides a *mutable* iterator over just the metadata stored in the quad buffer. This skips
119 // unpacking the device and local quads into GrQuads and is intended for use during op
120 // finalization, which may require rewriting state such as color.
122 public:
124 : fBuffer(list)
125 , fCurrentEntry(nullptr) {
126 SkDEBUGCODE(fExpectedCount = list->count();)
127 }
128
129 bool next();
130
131 T& operator*() { this->validate(); return *(fBuffer->metadata(fCurrentEntry)); }
132
133 T* operator->() { this->validate(); return fBuffer->metadata(fCurrentEntry); }
134
135 private:
136 GrQuadBuffer<T>* fBuffer;
137 char* fCurrentEntry;
138
139 SkDEBUGCODE(int fExpectedCount;)
140
141 void validate() const {
142 SkDEBUGCODE(fBuffer->validate(fCurrentEntry, fExpectedCount);)
143 }
144 };
145
147
148private:
149 struct alignas(int32_t) Header {
150 unsigned fDeviceType : 2;
151 unsigned fLocalType : 2; // Ignore if fHasLocals is false
152 unsigned fHasLocals : 1;
153 // Known value to detect if iteration doesn't properly advance through the buffer
154 SkDEBUGCODE(unsigned fSentinel : 27;)
155 };
156 static_assert(sizeof(Header) == sizeof(int32_t), "Header should be 4 bytes");
157
158 inline static constexpr unsigned kSentinel = 0xbaffe;
159 inline static constexpr int kMetaSize = sizeof(Header) + sizeof(T);
160 inline static constexpr int k2DQuadFloats = 8;
161 inline static constexpr int k3DQuadFloats = 12;
162
163 // Each logical entry in the buffer is a variable length tuple storing device coordinates,
164 // optional local coordinates, and metadata. An entry always has a header that defines the
165 // quad types of device and local coordinates, and always has metadata of type T. The device
166 // and local quads' data follows as a variable length array of floats:
167 // [ header ] = 4 bytes
168 // [ metadata ] = sizeof(T), assert alignof(T) == 4 so that pointer casts are valid
169 // [ device xs ] = 4 floats = 16 bytes
170 // [ device ys ] = 4 floats
171 // [ device ws ] = 4 floats or 0 floats depending on fDeviceType in header
172 // [ local xs ] = 4 floats or 0 floats depending on fHasLocals in header
173 // [ local ys ] = 4 floats or 0 floats depending on fHasLocals in header
174 // [ local ws ] = 4 floats or 0 floats depending on fHasLocals and fLocalType in header
175 // FIXME (michaelludwig) - Since this is intended only for ops, can we use the arena to
176 // allocate storage for the quad buffer? Since this is forward-iteration only, could also
177 // explore a linked-list structure for concatenating quads when batching ops
178 SkTDArray<char> fData;
179
180 int fCount; // Number of (device, local, metadata) entries
181 GrQuad::Type fDeviceType; // Most general type of all entries
182 GrQuad::Type fLocalType;
183
184 inline int entrySize(GrQuad::Type deviceType, const GrQuad::Type* localType) const {
185 int size = kMetaSize;
186 size += (deviceType == GrQuad::Type::kPerspective ? k3DQuadFloats
187 : k2DQuadFloats) * sizeof(float);
188 if (localType) {
189 size += (*localType == GrQuad::Type::kPerspective ? k3DQuadFloats
190 : k2DQuadFloats) * sizeof(float);
191 }
192 return size;
193 }
194 inline int entrySize(const Header* header) const {
195 if (header->fHasLocals) {
196 GrQuad::Type localType = static_cast<GrQuad::Type>(header->fLocalType);
197 return this->entrySize(static_cast<GrQuad::Type>(header->fDeviceType), &localType);
198 } else {
199 return this->entrySize(static_cast<GrQuad::Type>(header->fDeviceType), nullptr);
200 }
201 }
202
203 // Helpers to access typed sections of the buffer, given the start of an entry
204 inline Header* header(char* entry) {
205 return static_cast<Header*>(static_cast<void*>(entry));
206 }
207 inline const Header* header(const char* entry) const {
208 return static_cast<const Header*>(static_cast<const void*>(entry));
209 }
210
211 inline T* metadata(char* entry) {
212 return static_cast<T*>(static_cast<void*>(entry + sizeof(Header)));
213 }
214 inline const T* metadata(const char* entry) const {
215 return static_cast<const T*>(static_cast<const void*>(entry + sizeof(Header)));
216 }
217
218 inline float* coords(char* entry) {
219 return static_cast<float*>(static_cast<void*>(entry + kMetaSize));
220 }
221 inline const float* coords(const char* entry) const {
222 return static_cast<const float*>(static_cast<const void*>(entry + kMetaSize));
223 }
224
225 // Helpers to convert from coordinates to GrQuad and vice versa, returning pointer to the
226 // next packed quad coordinates.
227 float* packQuad(const GrQuad& quad, float* coords);
228 const float* unpackQuad(GrQuad::Type type, const float* coords, GrQuad* quad) const;
229
230#ifdef SK_DEBUG
231 void validate(const char* entry, int expectedCount) const;
232#endif
233};
234
235///////////////////////////////////////////////////////////////////////////////////////////////////
236// Buffer implementation
237///////////////////////////////////////////////////////////////////////////////////////////////////
238
239template<typename T>
240float* GrQuadBuffer<T>::packQuad(const GrQuad& quad, float* coords) {
241 // Copies all 12 (or 8) floats at once, so requires the 3 arrays to be contiguous
242 // FIXME(michaelludwig) - If this turns out not to be the case, just do 4 copies
243 SkASSERT(quad.xs() + 4 == quad.ys() && quad.xs() + 8 == quad.ws());
244 if (quad.hasPerspective()) {
245 memcpy(coords, quad.xs(), k3DQuadFloats * sizeof(float));
246 return coords + k3DQuadFloats;
247 } else {
248 memcpy(coords, quad.xs(), k2DQuadFloats * sizeof(float));
249 return coords + k2DQuadFloats;
250 }
251}
252
253template<typename T>
254const float* GrQuadBuffer<T>::unpackQuad(GrQuad::Type type, const float* coords, GrQuad* quad) const {
255 SkASSERT(quad->xs() + 4 == quad->ys() && quad->xs() + 8 == quad->ws());
257 // Fill in X, Y, and W in one go
258 memcpy(quad->xs(), coords, k3DQuadFloats * sizeof(float));
259 coords = coords + k3DQuadFloats;
260 } else {
261 // Fill in X and Y of the quad, the setQuadType() below will set Ws to 1 if needed
262 memcpy(quad->xs(), coords, k2DQuadFloats * sizeof(float));
263 coords = coords + k2DQuadFloats;
264 }
265
266 quad->setQuadType(type);
267 return coords;
268}
269
270template<typename T>
271void GrQuadBuffer<T>::append(const GrQuad& deviceQuad, T&& metadata, const GrQuad* localQuad) {
272 GrQuad::Type localType = localQuad ? localQuad->quadType() : GrQuad::Type::kAxisAligned;
273 int entrySize = this->entrySize(deviceQuad.quadType(), localQuad ? &localType : nullptr);
274
275 // Fill in the entry, as described in fData's declaration
276 char* entry = fData.append(entrySize);
277 // First the header
278 Header* h = this->header(entry);
279 h->fDeviceType = static_cast<unsigned>(deviceQuad.quadType());
280 h->fHasLocals = static_cast<unsigned>(localQuad != nullptr);
281 h->fLocalType = static_cast<unsigned>(localQuad ? localQuad->quadType()
283 SkDEBUGCODE(h->fSentinel = static_cast<unsigned>(kSentinel);)
284
285 // Second, the fixed-size metadata
286 static_assert(alignof(T) == 4, "Metadata must be 4 byte aligned");
287 *(this->metadata(entry)) = std::move(metadata);
288
289 // Then the variable blocks of x, y, and w float coordinates
290 float* coords = this->coords(entry);
291 coords = this->packQuad(deviceQuad, coords);
292 if (localQuad) {
293 coords = this->packQuad(*localQuad, coords);
294 }
295 SkASSERT((char*)coords - entry == entrySize);
296
297 // Entry complete, update buffer-level state
298 fCount++;
299 if (deviceQuad.quadType() > fDeviceType) {
300 fDeviceType = deviceQuad.quadType();
301 }
302 if (localQuad && localQuad->quadType() > fLocalType) {
303 fLocalType = localQuad->quadType();
304 }
305}
306
307template<typename T>
309 fData.append(that.fData.size(), that.fData.begin());
310 fCount += that.fCount;
311 if (that.fDeviceType > fDeviceType) {
312 fDeviceType = that.fDeviceType;
313 }
314 if (that.fLocalType > fLocalType) {
315 fLocalType = that.fLocalType;
316 }
317}
318
319#ifdef SK_DEBUG
320template<typename T>
321void GrQuadBuffer<T>::validate(const char* entry, int expectedCount) const {
322 // Triggers if accessing before next() is called on an iterator
323 SkASSERT(entry);
324 // Triggers if accessing after next() returns false
325 SkASSERT(entry < fData.end());
326 // Triggers if elements have been added to the buffer while iterating entries
327 SkASSERT(expectedCount == fCount);
328 // Make sure the start of the entry looks like a header
329 SkASSERT(this->header(entry)->fSentinel == kSentinel);
330}
331#endif
332
333///////////////////////////////////////////////////////////////////////////////////////////////////
334// Iterator implementations
335///////////////////////////////////////////////////////////////////////////////////////////////////
336
337template<typename T>
339 SkASSERT(fNextEntry);
340 if (fNextEntry >= fBuffer->fData.end()) {
341 return false;
342 }
343 // There is at least one more entry, so store the current start for metadata access
344 fCurrentEntry = fNextEntry;
345
346 // And then unpack the device and optional local coordinates into fDeviceQuad and fLocalQuad
347 const Header* h = fBuffer->header(fCurrentEntry);
348 const float* coords = fBuffer->coords(fCurrentEntry);
349 coords = fBuffer->unpackQuad(static_cast<GrQuad::Type>(h->fDeviceType), coords, &fDeviceQuad);
350 if (h->fHasLocals) {
351 coords = fBuffer->unpackQuad(static_cast<GrQuad::Type>(h->fLocalType), coords, &fLocalQuad);
352 } // else localQuad() will return a nullptr so no need to reset fLocalQuad
353
354 // At this point, coords points to the start of the next entry
355 fNextEntry = static_cast<const char*>(static_cast<const void*>(coords));
356 SkASSERT((fNextEntry - fCurrentEntry) == fBuffer->entrySize(h));
357 return true;
358}
359
360template<typename T>
362 if (fCurrentEntry) {
363 // Advance pointer by entry size
364 if (fCurrentEntry < fBuffer->fData.end()) {
365 const Header* h = fBuffer->header(fCurrentEntry);
366 fCurrentEntry += fBuffer->entrySize(h);
367 }
368 } else {
369 // First call to next
370 fCurrentEntry = fBuffer->fData.begin();
371 }
372 // Nothing else is needed to do but report whether or not the updated pointer is valid
373 return fCurrentEntry < fBuffer->fData.end();
374}
375#endif // GrQuadBuffer_DEFINED
#define SkASSERT(cond)
Definition SkAssert.h:116
#define SkDEBUGCODE(...)
Definition SkDebug.h:23
@ kSentinel
GrQuad * localQuad()
GrQuad * deviceQuad()
Iter(const GrQuadBuffer< T > *buffer)
bool isLocalValid() const
const T & metadata() const
MetadataIter(GrQuadBuffer< T > *list)
MetadataIter metadata()
void append(const GrQuad &deviceQuad, T &&metadata, const GrQuad *localQuad=nullptr)
GrQuadBuffer(int count, bool needsLocals=false)
Iter iterator() const
int count() const
GrQuad::Type deviceQuadType() const
void concat(const GrQuadBuffer< T > &that)
GrQuad::Type localQuadType() const
const float * xs() const
Definition GrQuad.h:132
const float * ys() const
Definition GrQuad.h:134
Type quadType() const
Definition GrQuad.h:118
bool hasPerspective() const
Definition GrQuad.h:120
void setQuadType(Type newType)
Definition GrQuad.h:140
const float * ws() const
Definition GrQuad.h:136
T * end()
Definition SkTDArray.h:152
int size() const
Definition SkTDArray.h:138
void reserve(int n)
Definition SkTDArray.h:187
T * begin()
Definition SkTDArray.h:150
T * append()
Definition SkTDArray.h:191
static const char * begin(const StringSlice &s)
Definition editor.cpp:252
static const uint8_t buffer[]
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition switches.h:259
SkScalar h
#define T
static const char header[]
Definition skpbench.cpp:88