Flutter Engine
The Flutter Engine
BufferWriter.h
Go to the documentation of this file.
1/*
2 * Copyright 2010 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 skgpu_BufferWriter_DEFINED
9#define skgpu_BufferWriter_DEFINED
10
12#include "include/core/SkRect.h"
19#include "src/base/SkVx.h"
21
22#include <array>
23#include <cstddef>
24#include <cstdint>
25#include <cstring>
26#include <type_traits>
27#include <utility>
28
29namespace skgpu {
30
32public:
33 // Marks a read-only position in the underlying buffer
34 struct Mark {
35 public:
36 Mark() : Mark(nullptr) {}
37 Mark(void* ptr, size_t offset = 0)
38 : fMark(reinterpret_cast<uintptr_t>(ptr) + offset) {
39 SkASSERT(ptr || offset == 0);
40 }
41
42 bool operator< (const Mark& o) const { return fMark < o.fMark; }
43 bool operator<=(const Mark& o) const { return fMark <= o.fMark; }
44 bool operator==(const Mark& o) const { return fMark == o.fMark; }
45 bool operator!=(const Mark& o) const { return fMark != o.fMark; }
46 bool operator>=(const Mark& o) const { return fMark >= o.fMark; }
47 bool operator> (const Mark& o) const { return fMark > o.fMark; }
48
49 ptrdiff_t operator-(const Mark& o) const { return fMark - o.fMark; }
50
51 explicit operator bool() const { return *this != Mark(); }
52 private:
53 uintptr_t fMark;
54 };
55
56 explicit operator bool() const { return fPtr != nullptr; }
57
58 Mark mark(size_t offset=0) const {
59 this->validate(offset);
60 return Mark(fPtr, offset);
61 }
62
63protected:
64 BufferWriter() = default;
65 BufferWriter(void* ptr, size_t size) : fPtr(ptr) {
66 SkDEBUGCODE(fEnd = Mark(ptr, ptr ? size : 0);)
67 }
68 BufferWriter(void* ptr, Mark end = {}) : fPtr(ptr) {
69 SkDEBUGCODE(fEnd = end;)
70 }
71
74 fPtr = that.fPtr;
75 that.fPtr = nullptr;
76 SkDEBUGCODE(fEnd = that.fEnd;)
77 SkDEBUGCODE(that.fEnd = Mark();)
78 return *this;
79 }
80
81 // makeOffset effectively splits the current writer from {fPtr, fEnd} into {fPtr, p} and
82 // a new writer {p, fEnd}. The same data range is accessible, but each byte can only be
83 // set by a single writer. Automatically validates that there is enough bytes remaining in this
84 // writer to do such a split.
85 //
86 // This splitting and validation means that providers of BufferWriters to callers can easily
87 // and correctly track everything in a single BufferWriter field and use
88 // return std::exchange(fCurrWriter, fCurrWriter.makeOffset(requestedBytes));
89 // This exposes the current writer position to the caller and sets the provider's new current
90 // position to be just after the requested bytes.
91 //
92 // Templated so that it can create subclasses directly.
93 template<typename W>
94 W makeOffset(size_t offsetInBytes) const {
95 this->validate(offsetInBytes);
96 void* p = SkTAddOffset<void>(fPtr, offsetInBytes);
97 Mark end{SkDEBUGCODE(fEnd)};
98 SkDEBUGCODE(fEnd = Mark(p);)
99 return W{p, end};
100 }
101
102 void validate(size_t bytesToWrite) const {
103 // If the buffer writer had an end marked, make sure we're not crossing it.
104 // Ideally, all creators of BufferWriters mark the end, but a lot of legacy code is not set
105 // up to easily do this.
106 SkASSERT(fPtr || bytesToWrite == 0);
107 SkASSERT(!fEnd || Mark(fPtr, bytesToWrite) <= fEnd);
108 }
109
110protected:
111 void* fPtr = nullptr;
112 SkDEBUGCODE(mutable Mark fEnd = {};)
113};
114
115/**
116 * Helper for writing vertex data to a buffer. Usage:
117 * VertexWriter vertices{target->makeVertexSpace(...)};
118 * vertices << A0 << B0 << C0 << ...;
119 * vertices << A1 << B1 << C1 << ...;
120 *
121 * Each value must be POD (plain old data), or have a specialization of the "<<" operator.
122 */
123struct VertexWriter : public BufferWriter {
124 inline constexpr static uint32_t kIEEE_32_infinity = 0x7f800000;
125
126 VertexWriter() = default;
127 // DEPRECATED: Prefer specifying the size of the buffer being written to as well
128 explicit VertexWriter(void* ptr) : BufferWriter(ptr, Mark()) {}
129
130 VertexWriter(void* ptr, size_t size) : BufferWriter(ptr, size) {}
131 VertexWriter(void* ptr, Mark end) : BufferWriter(ptr, end) {}
132
133 VertexWriter(const VertexWriter&) = delete;
134 VertexWriter(VertexWriter&& that) { *this = std::move(that); }
135
138 BufferWriter::operator=(std::move(that));
139 return *this;
140 }
141
142 VertexWriter makeOffset(size_t offsetInBytes) const {
143 return this->BufferWriter::makeOffset<VertexWriter>(offsetInBytes);
144 }
145
146 template <typename T>
147 struct Conditional {
150 };
151
152 template <typename T>
153 static Conditional<T> If(bool condition, const T& value) {
154 return {condition, value};
155 }
156
157 template <typename T>
158 struct Skip {};
159
160 template<typename T>
161 struct ArrayDesc {
162 const T* fArray;
164 };
165
166 template <typename T>
167 static ArrayDesc<T> Array(const T* array, int count) {
168 return {array, count};
169 }
170
171 template<int kCount, typename T>
172 struct RepeatDesc {
173 const T& fVal;
174 };
175
176 template <int kCount, typename T>
177 static RepeatDesc<kCount, T> Repeat(const T& val) {
178 return {val};
179 }
180
181 /**
182 * Specialized utilities for writing a four-vertices, with some data being replicated at each
183 * vertex, and other data being the appropriate 2-components from an SkRect to construct a
184 * triangle strip.
185 *
186 * - Four sets of data will be written
187 *
188 * - For any arguments where is_quad<Type>::value is true, a unique point will be written at
189 * each vertex. To make a custom type be emitted as a quad, declare:
190 *
191 * template<> struct VertexWriter::is_quad<MyQuadClass> : std::true_type {};
192 *
193 * and define:
194 *
195 * MyQuadClass::writeVertex(int cornerIdx, VertexWriter&) const { ... }
196 *
197 * - For any arguments where is_quad<Type>::value is false, its value will be replicated at each
198 * vertex.
199 */
200 template <typename T>
201 struct is_quad : std::false_type {};
202
203 template <typename T>
204 struct TriStrip {
205 void writeVertex(int cornerIdx, VertexWriter& w) const {
206 switch (cornerIdx) {
207 case 0: w << l << t; return;
208 case 1: w << l << b; return;
209 case 2: w << r << t; return;
210 case 3: w << r << b; return;
211 }
213 }
214 T l, t, r, b;
215 };
216
218 return { r.fLeft, r.fTop, r.fRight, r.fBottom };
219 }
220
221 static TriStrip<uint16_t> TriStripFromUVs(const std::array<uint16_t, 4>& rect) {
222 return { rect[0], rect[1], rect[2], rect[3] };
223 }
224
225 template <typename T>
226 struct TriFan {
227 void writeVertex(int cornerIdx, VertexWriter& w) const {
228 switch (cornerIdx) {
229 case 0: w << l << t; return;
230 case 1: w << l << b; return;
231 case 2: w << r << b; return;
232 case 3: w << r << t; return;
233 }
235 }
236 T l, t, r, b;
237 };
238
240 return { r.fLeft, r.fTop, r.fRight, r.fBottom };
241 }
242
243 template <typename... Args>
244 void writeQuad(const Args&... remainder) {
245 this->writeQuadVertex<0>(remainder...);
246 this->writeQuadVertex<1>(remainder...);
247 this->writeQuadVertex<2>(remainder...);
248 this->writeQuadVertex<3>(remainder...);
249 }
250
251private:
252 template <int kCornerIdx, typename T, typename... Args>
253 std::enable_if_t<!is_quad<T>::value, void> writeQuadVertex(const T& val,
254 const Args&... remainder) {
255 *this << val; // Non-quads duplicate their value.
256 this->writeQuadVertex<kCornerIdx>(remainder...);
257 }
258
259 template <int kCornerIdx, typename Q, typename... Args>
260 std::enable_if_t<is_quad<Q>::value, void> writeQuadVertex(const Q& quad,
261 const Args&... remainder) {
262 quad.writeVertex(kCornerIdx, *this); // Quads emit a different corner each time.
263 this->writeQuadVertex<kCornerIdx>(remainder...);
264 }
265
266 template <int kCornerIdx>
267 void writeQuadVertex() {}
268
269 template <typename T>
270 friend VertexWriter& operator<<(VertexWriter&, const T&);
271
272 template <typename T>
273 friend VertexWriter& operator<<(VertexWriter&, const ArrayDesc<T>&);
274};
275
276template <typename T>
277inline VertexWriter& operator<<(VertexWriter& w, const T& val) {
278 static_assert(std::is_trivially_copyable<T>::value, "");
279 w.validate(sizeof(T));
280 memcpy(w.fPtr, &val, sizeof(T));
281 w = w.makeOffset(sizeof(T));
282 return w;
283}
284
285template <typename T>
287 static_assert(std::is_trivially_copyable<T>::value, "");
288 if (val.fCondition) {
289 w << val.fValue;
290 }
291 return w;
292}
293
294template <typename T>
296 w = w.makeOffset(sizeof(T));
297 return w;
298}
299
300template <typename T>
302 static_assert(std::is_trivially_copyable<T>::value, "");
303 w.validate(array.fCount * sizeof(T));
304 memcpy(w.fPtr, array.fArray, array.fCount * sizeof(T));
305 w = w.makeOffset(sizeof(T) * array.fCount);
306 return w;
307}
308
309template <int kCount, typename T>
311 for (int i = 0; i < kCount; ++i) {
312 w << repeat.fVal;
313 }
314 return w;
315}
316
317template <>
318[[maybe_unused]] inline VertexWriter& operator<<(VertexWriter& w, const skvx::float4& vector) {
319 w.validate(sizeof(vector));
320 vector.store(w.fPtr);
321 w = w.makeOffset(sizeof(vector));
322 return w;
323}
324
325// Allow r-value/temporary writers to be appended to
326template <typename T>
327inline VertexWriter& operator<<(VertexWriter&& w, const T& val) { return w << val; }
328
329template <typename T>
330struct VertexWriter::is_quad<VertexWriter::TriStrip<T>> : std::true_type {};
331
332template <typename T>
333struct VertexWriter::is_quad<VertexWriter::TriFan<T>> : std::true_type {};
334
335/**
336 * VertexColor is a helper for writing colors to a vertex buffer. It outputs either four bytes or
337 * or four float32 channels, depending on the wideColor parameter. Note that the GP needs to have
338 * been constructed with the correct attribute type for colors, to match the usage here.
339 */
341public:
342 VertexColor() = default;
343
344 explicit VertexColor(const SkPMColor4f& color, bool wideColor) {
345 this->set(color, wideColor);
346 }
347
348 void set(const SkPMColor4f& color, bool wideColor) {
349 if (wideColor) {
350 memcpy(fColor, color.vec(), sizeof(fColor));
351 } else {
352 fColor[0] = color.toBytes_RGBA();
353 }
354 fWideColor = wideColor;
355 }
356
357 size_t size() const { return fWideColor ? 16 : 4; }
358
359private:
360 template <typename T>
361 friend VertexWriter& operator<<(VertexWriter&, const T&);
362
363 uint32_t fColor[4];
364 bool fWideColor;
365};
366
367template <>
368[[maybe_unused]] inline VertexWriter& operator<<(VertexWriter& w, const VertexColor& color) {
369 w << color.fColor[0];
370 if (color.fWideColor) {
371 w << color.fColor[1]
372 << color.fColor[2]
373 << color.fColor[3];
374 }
375 return w;
376}
377
378///////////////////////////////////////////////////////////////////////////////////////////////////
379
380struct IndexWriter : public BufferWriter {
381 IndexWriter() = default;
382
383 IndexWriter(void* ptr, size_t size) : BufferWriter(ptr, size) {}
384 IndexWriter(void* ptr, Mark end) : BufferWriter(ptr, end) {}
385
386 IndexWriter(const IndexWriter&) = delete;
387 IndexWriter(IndexWriter&& that) { *this = std::move(that); }
388
391 BufferWriter::operator=(std::move(that));
392 return *this;
393 }
394
395 IndexWriter makeOffset(int numIndices) const {
396 return this->BufferWriter::makeOffset<IndexWriter>(numIndices * sizeof(uint16_t));
397 }
398
399 void writeArray(const uint16_t* array, int count) {
400 size_t arraySize = count * sizeof(uint16_t);
401 this->validate(arraySize);
402 memcpy(fPtr, array, arraySize);
403 fPtr = SkTAddOffset<void>(fPtr, arraySize);
404
405 }
406
407 friend IndexWriter& operator<<(IndexWriter& w, uint16_t val);
408};
409
410inline IndexWriter& operator<<(IndexWriter& w, uint16_t val) {
411 w.validate(sizeof(uint16_t));
412 memcpy(w.fPtr, &val, sizeof(uint16_t));
413 w = w.makeOffset(1);
414 return w;
415}
416
417inline IndexWriter& operator<<(IndexWriter& w, int val) { return (w << SkTo<uint16_t>(val)); }
418
419template<typename T>
420inline IndexWriter& operator<<(IndexWriter&& w, const T& val) { return w << val; }
421
422///////////////////////////////////////////////////////////////////////////////////////////////////
423
425 UniformWriter() = default;
426
427 UniformWriter(void* ptr, size_t size) : BufferWriter(ptr, size) {}
428 UniformWriter(void* ptr, Mark end) : BufferWriter(ptr, end) {}
429
430 UniformWriter(const UniformWriter&) = delete;
431 UniformWriter(UniformWriter&& that) { *this = std::move(that); }
432
435 BufferWriter::operator=(std::move(that));
436 return *this;
437 }
438
439 void write(const void* src, size_t bytes) {
440 this->validate(bytes);
441 memcpy(fPtr, src, bytes);
442 fPtr = SkTAddOffset<void>(fPtr, bytes);
443 }
444 void skipBytes(size_t bytes) {
445 this->validate(bytes);
446 fPtr = SkTAddOffset<void>(fPtr, bytes);
447 }
448};
449
450///////////////////////////////////////////////////////////////////////////////////////////////////
451
454
455 TextureUploadWriter(void* ptr, size_t size) : BufferWriter(ptr, size) {}
456
458 TextureUploadWriter(TextureUploadWriter&& that) { *this = std::move(that); }
459
462 BufferWriter::operator=(std::move(that));
463 return *this;
464 }
465
466 // Writes a block of image data to the upload buffer, starting at `offset`. The source image is
467 // `srcRowBytes` wide, and the written block is `dstRowBytes` wide and `rowCount` bytes tall.
468 void write(size_t offset, const void* src, size_t srcRowBytes, size_t dstRowBytes,
469 size_t trimRowBytes, int rowCount) {
470 this->validate(offset + dstRowBytes * rowCount);
471 void* dst = SkTAddOffset<void>(fPtr, offset);
472 SkRectMemcpy(dst, dstRowBytes, src, srcRowBytes, trimRowBytes, rowCount);
473 }
474
476 const SkImageInfo& srcInfo, const void* src, size_t srcRowBytes,
477 const SkImageInfo& dstInfo, size_t dstRowBytes) {
478 SkASSERT(srcInfo.width() == dstInfo.width() && srcInfo.height() == dstInfo.height());
479 this->validate(offset + dstRowBytes * dstInfo.height());
480 void* dst = SkTAddOffset<void>(fPtr, offset);
481 SkAssertResult(SkConvertPixels(dstInfo, dst, dstRowBytes, srcInfo, src, srcRowBytes));
482 }
483
484 // Writes a block of image data to the upload buffer. It converts src data of RGB_888x
485 // colorType into a 3 channel RGB_888 format.
486 void writeRGBFromRGBx(size_t offset, const void* src, size_t srcRowBytes, size_t dstRowBytes,
487 int rowPixels, int rowCount) {
488 this->validate(offset + dstRowBytes * rowCount);
489 void* dst = SkTAddOffset<void>(fPtr, offset);
490 auto* sRow = reinterpret_cast<const char*>(src);
491 auto* dRow = reinterpret_cast<char*>(dst);
492
493 for (int y = 0; y < rowCount; ++y) {
494 for (int x = 0; x < rowPixels; ++x) {
495 memcpy(dRow + 3*x, sRow+4*x, 3);
496 }
497 sRow += srcRowBytes;
498 dRow += dstRowBytes;
499 }
500 }
501};
502
503} // namespace skgpu
504
505#endif // skgpu_BufferWriter_DEFINED
SkAssertResult(font.textToGlyphs("Hello", 5, SkTextEncoding::kUTF8, glyphs, std::size(glyphs))==count)
int count
Definition: FontMgrTest.cpp:50
#define SkUNREACHABLE
Definition: SkAssert.h:135
#define SkASSERT(cond)
Definition: SkAssert.h:116
static unsigned repeat(SkFixed fx, int max)
bool SkConvertPixels(const SkImageInfo &dstInfo, void *dstPixels, size_t dstRB, const SkImageInfo &srcInfo, const void *srcPixels, size_t srcRB)
static void SkRectMemcpy(void *dst, size_t dstRB, const void *src, size_t srcRB, size_t trimRowBytes, int rowCount)
Definition: SkRectMemcpy.h:16
SkDEBUGCODE(SK_SPI) SkThreadID SkGetThreadID()
#define W
Definition: aaa.cpp:17
VertexColor(const SkPMColor4f &color, bool wideColor)
Definition: BufferWriter.h:344
friend VertexWriter & operator<<(VertexWriter &, const T &)
Definition: BufferWriter.h:277
void set(const SkPMColor4f &color, bool wideColor)
Definition: BufferWriter.h:348
size_t size() const
Definition: BufferWriter.h:357
VertexColor()=default
DlColor color
glong glong end
uint8_t value
double y
double x
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
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
dst
Definition: cp.py:12
Definition: GpuTools.h:21
VertexWriter & operator<<(VertexWriter &w, const T &val)
Definition: BufferWriter.h:277
SkScalar w
#define T
Definition: precompiler.cc:65
SeparatedVector2 offset
int width() const
Definition: SkImageInfo.h:365
int height() const
Definition: SkImageInfo.h:371
SkScalar fBottom
larger y-axis bounds
Definition: extension.cpp:17
SkScalar fLeft
smaller x-axis bounds
Definition: extension.cpp:14
SkScalar fRight
larger x-axis bounds
Definition: extension.cpp:16
SkScalar fTop
smaller y-axis bounds
Definition: extension.cpp:15
bool operator==(const Mark &o) const
Definition: BufferWriter.h:44
ptrdiff_t operator-(const Mark &o) const
Definition: BufferWriter.h:49
Mark(void *ptr, size_t offset=0)
Definition: BufferWriter.h:37
bool operator>=(const Mark &o) const
Definition: BufferWriter.h:46
bool operator<=(const Mark &o) const
Definition: BufferWriter.h:43
bool operator!=(const Mark &o) const
Definition: BufferWriter.h:45
bool operator<(const Mark &o) const
Definition: BufferWriter.h:42
bool operator>(const Mark &o) const
Definition: BufferWriter.h:47
BufferWriter & operator=(const BufferWriter &)=delete
BufferWriter(void *ptr, Mark end={})
Definition: BufferWriter.h:68
BufferWriter & operator=(BufferWriter &&that)
Definition: BufferWriter.h:73
BufferWriter()=default
W makeOffset(size_t offsetInBytes) const
Definition: BufferWriter.h:94
Mark mark(size_t offset=0) const
Definition: BufferWriter.h:58
BufferWriter(void *ptr, size_t size)
Definition: BufferWriter.h:65
void validate(size_t bytesToWrite) const
Definition: BufferWriter.h:102
IndexWriter()=default
IndexWriter(IndexWriter &&that)
Definition: BufferWriter.h:387
IndexWriter(const IndexWriter &)=delete
IndexWriter(void *ptr, Mark end)
Definition: BufferWriter.h:384
friend IndexWriter & operator<<(IndexWriter &w, uint16_t val)
Definition: BufferWriter.h:410
IndexWriter(void *ptr, size_t size)
Definition: BufferWriter.h:383
IndexWriter makeOffset(int numIndices) const
Definition: BufferWriter.h:395
void writeArray(const uint16_t *array, int count)
Definition: BufferWriter.h:399
IndexWriter & operator=(const IndexWriter &)=delete
IndexWriter & operator=(IndexWriter &&that)
Definition: BufferWriter.h:390
TextureUploadWriter & operator=(TextureUploadWriter &&that)
Definition: BufferWriter.h:461
TextureUploadWriter & operator=(const TextureUploadWriter &)=delete
void convertAndWrite(size_t offset, const SkImageInfo &srcInfo, const void *src, size_t srcRowBytes, const SkImageInfo &dstInfo, size_t dstRowBytes)
Definition: BufferWriter.h:475
TextureUploadWriter(const TextureUploadWriter &)=delete
void writeRGBFromRGBx(size_t offset, const void *src, size_t srcRowBytes, size_t dstRowBytes, int rowPixels, int rowCount)
Definition: BufferWriter.h:486
void write(size_t offset, const void *src, size_t srcRowBytes, size_t dstRowBytes, size_t trimRowBytes, int rowCount)
Definition: BufferWriter.h:468
TextureUploadWriter(void *ptr, size_t size)
Definition: BufferWriter.h:455
TextureUploadWriter(TextureUploadWriter &&that)
Definition: BufferWriter.h:458
void skipBytes(size_t bytes)
Definition: BufferWriter.h:444
UniformWriter(void *ptr, size_t size)
Definition: BufferWriter.h:427
UniformWriter(void *ptr, Mark end)
Definition: BufferWriter.h:428
UniformWriter(UniformWriter &&that)
Definition: BufferWriter.h:431
void write(const void *src, size_t bytes)
Definition: BufferWriter.h:439
UniformWriter & operator=(const UniformWriter &)=delete
UniformWriter & operator=(UniformWriter &&that)
Definition: BufferWriter.h:434
UniformWriter(const UniformWriter &)=delete
void writeVertex(int cornerIdx, VertexWriter &w) const
Definition: BufferWriter.h:227
void writeVertex(int cornerIdx, VertexWriter &w) const
Definition: BufferWriter.h:205
VertexWriter(const VertexWriter &)=delete
static RepeatDesc< kCount, T > Repeat(const T &val)
Definition: BufferWriter.h:177
static ArrayDesc< T > Array(const T *array, int count)
Definition: BufferWriter.h:167
static TriStrip< uint16_t > TriStripFromUVs(const std::array< uint16_t, 4 > &rect)
Definition: BufferWriter.h:221
friend VertexWriter & operator<<(VertexWriter &, const T &)
Definition: BufferWriter.h:277
VertexWriter & operator=(VertexWriter &&that)
Definition: BufferWriter.h:137
static TriFan< float > TriFanFromRect(const SkRect &r)
Definition: BufferWriter.h:239
VertexWriter()=default
VertexWriter(void *ptr, Mark end)
Definition: BufferWriter.h:131
void writeQuad(const Args &... remainder)
Definition: BufferWriter.h:244
static Conditional< T > If(bool condition, const T &value)
Definition: BufferWriter.h:153
static TriStrip< float > TriStripFromRect(const SkRect &r)
Definition: BufferWriter.h:217
static constexpr uint32_t kIEEE_32_infinity
Definition: BufferWriter.h:124
VertexWriter & operator=(const VertexWriter &)=delete
VertexWriter(void *ptr)
Definition: BufferWriter.h:128
VertexWriter(void *ptr, size_t size)
Definition: BufferWriter.h:130
VertexWriter makeOffset(size_t offsetInBytes) const
Definition: BufferWriter.h:142
VertexWriter(VertexWriter &&that)
Definition: BufferWriter.h:134
Definition: SkVx.h:83
SKVX_ALWAYS_INLINE void store(void *ptr) const
Definition: SkVx.h:112