Reland "Replace GrQuadList with variable-length quad buffer"
This reverts commit 19628ec144a6e480e56bbfff38ae07a3db1588d1.
Reason for revert: fixed struct size on 32-bit iOS
Original change's description:
> Revert "Replace GrQuadList with variable-length quad buffer"
>
> This reverts commit f2816044292f0d405c3060572d65fb35cef7cc3a.
>
> Reason for revert: Breaking G3 and iOS Build
>
> Original change's description:
> > Replace GrQuadList with variable-length quad buffer
> >
> > Change-Id: I5cc391e8d143032893511695961f5251f40e8291
> > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/223803
> > Reviewed-by: Brian Salomon <bsalomon@google.com>
> > Commit-Queue: Michael Ludwig <michaelludwig@google.com>
>
> TBR=bsalomon@google.com,michaelludwig@google.com
>
> Change-Id: I55947c068c6472c301952e33cbc36d04505f9800
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/223993
> Reviewed-by: Brian Salomon <bsalomon@google.com>
> Commit-Queue: Brian Salomon <bsalomon@google.com>
TBR=bsalomon@google.com,michaelludwig@google.com
Change-Id: I7522270d467faf0f4e777831e9186bad010409ab
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/224184
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
diff --git a/src/gpu/geometry/GrQuad.h b/src/gpu/geometry/GrQuad.h
index ea3cdec..361c9a3 100644
--- a/src/gpu/geometry/GrQuad.h
+++ b/src/gpu/geometry/GrQuad.h
@@ -132,9 +132,11 @@
// The non-const pointers are provided to support modifying a GrQuad in-place, but care must be
// taken to keep its quad type aligned with the geometric nature of the new coordinates. This is
// no different than using the constructors that accept a quad type.
-
+ const float* xs() const { return fX; }
float* xs() { return fX; }
+ const float* ys() const { return fY; }
float* ys() { return fY; }
+ const float* ws() const { return fW; }
float* ws() { return fW; }
void setQuadType(Type newType) { fType = newType; }
diff --git a/src/gpu/geometry/GrQuadBuffer.h b/src/gpu/geometry/GrQuadBuffer.h
new file mode 100644
index 0000000..a2e2f4c
--- /dev/null
+++ b/src/gpu/geometry/GrQuadBuffer.h
@@ -0,0 +1,375 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/private/SkTDArray.h"
+#include "src/gpu/geometry/GrQuad.h"
+
+template<typename T>
+class GrQuadBuffer {
+public:
+ GrQuadBuffer()
+ : fCount(0)
+ , fDeviceType(GrQuad::Type::kAxisAligned)
+ , fLocalType(GrQuad::Type::kAxisAligned) {
+ // Pre-allocate space for 1 2D device-space quad, metadata, and header
+ fData.reserve(this->entrySize(fDeviceType, nullptr));
+ }
+
+ // Reserves space for the given number of entries; if 'needsLocals' is true, space will be
+ // reserved for each entry to also have a 2D local quad. The reserved space assumes 2D device
+ // quad for simplicity. Since this buffer has a variable bitrate encoding for quads, this may
+ // over or under reserve, but pre-allocating still helps when possible.
+ GrQuadBuffer(int count, bool needsLocals = false)
+ : fCount(0)
+ , fDeviceType(GrQuad::Type::kAxisAligned)
+ , fLocalType(GrQuad::Type::kAxisAligned) {
+ int entrySize = this->entrySize(fDeviceType, needsLocals ? &fLocalType : nullptr);
+ fData.reserve(count * entrySize);
+ }
+
+ // The number of device-space quads (and metadata, and optional local quads) that are in the
+ // the buffer.
+ int count() const { return fCount; }
+
+ // The most general type for the device-space quads in this buffer
+ GrQuad::Type deviceQuadType() const { return fDeviceType; }
+
+ // The most general type for the local quads; if no local quads are ever added, this will
+ // return kAxisAligned.
+ GrQuad::Type localQuadType() const { return fLocalType; }
+
+ // Append the given 'deviceQuad' to this buffer, with its associated 'metadata'. If 'localQuad'
+ // is not null, the local coordinates will also be attached to the entry. When an entry
+ // has local coordinates, during iteration, the Iter::hasLocals() will return true and its
+ // Iter::localQuad() will be equivalent to the provided local coordinates. If 'localQuad' is
+ // null then Iter::hasLocals() will report false for the added entry.
+ void append(const GrQuad& deviceQuad, T&& metadata, const GrQuad* localQuad = nullptr);
+
+ // Copies all entries from 'that' to this buffer
+ void concat(const GrQuadBuffer<T>& that);
+
+ // Provides a read-only iterator over a quad buffer, giving access to the device quad, metadata
+ // and optional local quad.
+ class Iter {
+ public:
+ Iter(const GrQuadBuffer<T>* buffer)
+ : fDeviceQuad(SkRect::MakeEmpty())
+ , fLocalQuad(SkRect::MakeEmpty())
+ , fBuffer(buffer)
+ , fCurrentEntry(nullptr)
+ , fNextEntry(buffer->fData.begin()) {
+ SkDEBUGCODE(fExpectedCount = buffer->count();)
+ }
+
+ bool next();
+
+ const T& metadata() const { this->validate(); return *(fBuffer->metadata(fCurrentEntry)); }
+
+ const GrQuad& deviceQuad() const { this->validate(); return fDeviceQuad; }
+
+ // If isLocalValid() returns false, this returns an empty quad (all 0s) so that localQuad()
+ // can be called without triggering any sanitizers, for convenience when some other state
+ // ensures that the quad will eventually not be used.
+ const GrQuad& localQuad() const {
+ this->validate();
+ return fLocalQuad;
+ }
+
+ bool isLocalValid() const {
+ this->validate();
+ return fBuffer->header(fCurrentEntry)->fHasLocals;
+ }
+
+ private:
+ // Quads are stored locally so that calling code doesn't need to re-declare their own quads
+ GrQuad fDeviceQuad;
+ GrQuad fLocalQuad;
+
+ const GrQuadBuffer<T>* fBuffer;
+ // The pointer to the current entry to read metadata/header details from
+ const char* fCurrentEntry;
+ // The pointer to replace fCurrentEntry when next() is called, cached since it is calculated
+ // automatically while unpacking the quad data.
+ const char* fNextEntry;
+
+ SkDEBUGCODE(int fExpectedCount;)
+
+ void validate() const {
+ SkDEBUGCODE(fBuffer->validate(fCurrentEntry, fExpectedCount);)
+ }
+ };
+
+ Iter iterator() const { return Iter(this); }
+
+ // Provides a *mutable* iterator over just the metadata stored in the quad buffer. This skips
+ // unpacking the device and local quads into GrQuads and is intended for use during op
+ // finalization, which may require rewriting state such as color.
+ class MetadataIter {
+ public:
+ MetadataIter(GrQuadBuffer<T>* list)
+ : fBuffer(list)
+ , fCurrentEntry(nullptr) {
+ SkDEBUGCODE(fExpectedCount = list->count();)
+ }
+
+ bool next();
+
+ T& operator*() { this->validate(); return *(fBuffer->metadata(fCurrentEntry)); }
+
+ T* operator->() { this->validate(); return fBuffer->metadata(fCurrentEntry); }
+
+ private:
+ GrQuadBuffer<T>* fBuffer;
+ char* fCurrentEntry;
+
+ SkDEBUGCODE(int fExpectedCount;)
+
+ void validate() const {
+ SkDEBUGCODE(fBuffer->validate(fCurrentEntry, fExpectedCount);)
+ }
+ };
+
+ MetadataIter metadata() { return MetadataIter(this); }
+
+private:
+ struct alignas(int32_t) Header {
+ unsigned fDeviceType : 2;
+ unsigned fLocalType : 2; // Ignore if fHasLocals is false
+ unsigned fHasLocals : 1;
+ // Known value to detect if iteration doesn't properly advance through the buffer
+ SkDEBUGCODE(unsigned fSentinel : 27;)
+ };
+ static_assert(sizeof(Header) == sizeof(int32_t), "Header should be 4 bytes");
+
+ static constexpr unsigned kSentinel = 0xbaffe;
+ static constexpr int kMetaSize = sizeof(Header) + sizeof(T);
+ static constexpr int k2DQuadFloats = 8;
+ static constexpr int k3DQuadFloats = 12;
+
+ // Each logical entry in the buffer is a variable length tuple storing device coordinates,
+ // optional local coordinates, and metadata. An entry always has a header that defines the
+ // quad types of device and local coordinates, and always has metadata of type T. The device
+ // and local quads' data follows as a variable length array of floats:
+ // [ header ] = 4 bytes
+ // [ metadata ] = sizeof(T), assert alignof(T) == 4 so that pointer casts are valid
+ // [ device xs ] = 4 floats = 16 bytes
+ // [ device ys ] = 4 floats
+ // [ device ws ] = 4 floats or 0 floats depending on fDeviceType in header
+ // [ local xs ] = 4 floats or 0 floats depending on fHasLocals in header
+ // [ local ys ] = 4 floats or 0 floats depending on fHasLocals in header
+ // [ local ws ] = 4 floats or 0 floats depending on fHasLocals and fLocalType in header
+ // FIXME (michaelludwig) - Since this is intended only for ops, can we use the arena to
+ // allocate storage for the quad buffer? Since this is forward-iteration only, could also
+ // explore a linked-list structure for concatenating quads when batching ops
+ SkTDArray<char> fData;
+
+ int fCount; // Number of (device, local, metadata) entries
+ GrQuad::Type fDeviceType; // Most general type of all entries
+ GrQuad::Type fLocalType;
+
+ inline int entrySize(GrQuad::Type deviceType, const GrQuad::Type* localType) const {
+ int size = kMetaSize;
+ size += (deviceType == GrQuad::Type::kPerspective ? k3DQuadFloats
+ : k2DQuadFloats) * sizeof(float);
+ if (localType) {
+ size += (*localType == GrQuad::Type::kPerspective ? k3DQuadFloats
+ : k2DQuadFloats) * sizeof(float);
+ }
+ return size;
+ }
+ inline int entrySize(const Header* header) const {
+ if (header->fHasLocals) {
+ GrQuad::Type localType = static_cast<GrQuad::Type>(header->fLocalType);
+ return this->entrySize(static_cast<GrQuad::Type>(header->fDeviceType), &localType);
+ } else {
+ return this->entrySize(static_cast<GrQuad::Type>(header->fDeviceType), nullptr);
+ }
+ }
+
+ // Helpers to access typed sections of the buffer, given the start of an entry
+ inline Header* header(char* entry) {
+ return static_cast<Header*>(static_cast<void*>(entry));
+ }
+ inline const Header* header(const char* entry) const {
+ return static_cast<const Header*>(static_cast<const void*>(entry));
+ }
+
+ inline T* metadata(char* entry) {
+ return static_cast<T*>(static_cast<void*>(entry + sizeof(Header)));
+ }
+ inline const T* metadata(const char* entry) const {
+ return static_cast<const T*>(static_cast<const void*>(entry + sizeof(Header)));
+ }
+
+ inline float* coords(char* entry) {
+ return static_cast<float*>(static_cast<void*>(entry + kMetaSize));
+ }
+ inline const float* coords(const char* entry) const {
+ return static_cast<const float*>(static_cast<const void*>(entry + kMetaSize));
+ }
+
+ // Helpers to convert from coordinates to GrQuad and vice versa, returning pointer to the
+ // next packed quad coordinates.
+ float* packQuad(const GrQuad& quad, float* coords);
+ const float* unpackQuad(GrQuad::Type type, const float* coords, GrQuad* quad) const;
+
+#ifdef SK_DEBUG
+ void validate(const char* entry, int expectedCount) const;
+#endif
+};
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Buffer implementation
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+template<typename T>
+float* GrQuadBuffer<T>::packQuad(const GrQuad& quad, float* coords) {
+ // Copies all 12 (or 8) floats at once, so requires the 3 arrays to be contiguous
+ // FIXME(michaelludwig) - If this turns out not to be the case, just do 4 copies
+ SkASSERT(quad.xs() + 4 == quad.ys() && quad.xs() + 8 == quad.ws());
+ if (quad.hasPerspective()) {
+ memcpy(coords, quad.xs(), k3DQuadFloats * sizeof(float));
+ return coords + k3DQuadFloats;
+ } else {
+ memcpy(coords, quad.xs(), k2DQuadFloats * sizeof(float));
+ return coords + k2DQuadFloats;
+ }
+}
+
+template<typename T>
+const float* GrQuadBuffer<T>::unpackQuad(GrQuad::Type type, const float* coords, GrQuad* quad) const {
+ SkASSERT(quad->xs() + 4 == quad->ys() && quad->xs() + 8 == quad->ws());
+ if (type == GrQuad::Type::kPerspective) {
+ // Fill in X, Y, and W in one go
+ memcpy(quad->xs(), coords, k3DQuadFloats * sizeof(float));
+ coords = coords + k3DQuadFloats;
+ } else {
+ // Fill in X and Y of the quad, and set W to 1s if needed
+ memcpy(quad->xs(), coords, k2DQuadFloats * sizeof(float));
+ coords = coords + k2DQuadFloats;
+
+ if (quad->quadType() == GrQuad::Type::kPerspective) {
+ // The output quad was previously perspective, so its ws are not 1s
+ static constexpr float kNoPerspectiveWs[4] = {1.f, 1.f, 1.f, 1.f};
+ memcpy(quad->ws(), kNoPerspectiveWs, 4 * sizeof(float));
+ }
+ // Else the quad should already have 1s in w
+ SkASSERT(quad->w(0) == 1.f && quad->w(1) == 1.f &&
+ quad->w(2) == 1.f && quad->w(3) == 1.f);
+ }
+
+ quad->setQuadType(type);
+ return coords;
+}
+
+template<typename T>
+void GrQuadBuffer<T>::append(const GrQuad& deviceQuad, T&& metadata, const GrQuad* localQuad) {
+ GrQuad::Type localType = localQuad ? localQuad->quadType() : GrQuad::Type::kAxisAligned;
+ int entrySize = this->entrySize(deviceQuad.quadType(), localQuad ? &localType : nullptr);
+
+ // Fill in the entry, as described in fData's declaration
+ char* entry = fData.append(entrySize);
+ // First the header
+ Header* h = this->header(entry);
+ h->fDeviceType = static_cast<unsigned>(deviceQuad.quadType());
+ h->fHasLocals = static_cast<unsigned>(localQuad != nullptr);
+ h->fLocalType = static_cast<unsigned>(localQuad ? localQuad->quadType()
+ : GrQuad::Type::kAxisAligned);
+ SkDEBUGCODE(h->fSentinel = static_cast<unsigned>(kSentinel);)
+
+ // Second, the fixed-size metadata
+ static_assert(alignof(T) == 4, "Metadata must be 4 byte aligned");
+ *(this->metadata(entry)) = std::move(metadata);
+
+ // Then the variable blocks of x, y, and w float coordinates
+ float* coords = this->coords(entry);
+ coords = this->packQuad(deviceQuad, coords);
+ if (localQuad) {
+ coords = this->packQuad(*localQuad, coords);
+ }
+ SkASSERT((char*)coords - entry == entrySize);
+
+ // Entry complete, update buffer-level state
+ fCount++;
+ if (deviceQuad.quadType() > fDeviceType) {
+ fDeviceType = deviceQuad.quadType();
+ }
+ if (localQuad && localQuad->quadType() > fLocalType) {
+ fLocalType = localQuad->quadType();
+ }
+}
+
+template<typename T>
+void GrQuadBuffer<T>::concat(const GrQuadBuffer<T>& that) {
+ fData.append(that.fData.count(), that.fData.begin());
+ fCount += that.fCount;
+ if (that.fDeviceType > fDeviceType) {
+ fDeviceType = that.fDeviceType;
+ }
+ if (that.fLocalType > fLocalType) {
+ fLocalType = that.fLocalType;
+ }
+}
+
+#ifdef SK_DEBUG
+template<typename T>
+void GrQuadBuffer<T>::validate(const char* entry, int expectedCount) const {
+ // Triggers if accessing before next() is called on an iterator
+ SkASSERT(entry);
+ // Triggers if accessing after next() returns false
+ SkASSERT(entry < fData.end());
+ // Triggers if elements have been added to the buffer while iterating entries
+ SkASSERT(expectedCount == fCount);
+ // Make sure the start of the entry looks like a header
+ SkASSERT(this->header(entry)->fSentinel == kSentinel);
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Iterator implementations
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+template<typename T>
+bool GrQuadBuffer<T>::Iter::next() {
+ SkASSERT(fNextEntry);
+ if (fNextEntry >= fBuffer->fData.end()) {
+ return false;
+ }
+ // There is at least one more entry, so store the current start for metadata access
+ fCurrentEntry = fNextEntry;
+
+ // And then unpack the device and optional local coordinates into fDeviceQuad and fLocalQuad
+ const Header* h = fBuffer->header(fCurrentEntry);
+ const float* coords = fBuffer->coords(fCurrentEntry);
+ coords = fBuffer->unpackQuad(static_cast<GrQuad::Type>(h->fDeviceType), coords, &fDeviceQuad);
+ if (h->fHasLocals) {
+ coords = fBuffer->unpackQuad(static_cast<GrQuad::Type>(h->fLocalType), coords, &fLocalQuad);
+ } else {
+ static const GrQuad kEmptyLocal(SkRect::MakeEmpty());
+ fLocalQuad = kEmptyLocal;
+ }
+ // At this point, coords points to the start of the next entry
+ fNextEntry = static_cast<const char*>(static_cast<const void*>(coords));
+ SkASSERT((fNextEntry - fCurrentEntry) == fBuffer->entrySize(h));
+ return true;
+}
+
+template<typename T>
+bool GrQuadBuffer<T>::MetadataIter::next() {
+ if (fCurrentEntry) {
+ // Advance pointer by entry size
+ if (fCurrentEntry < fBuffer->fData.end()) {
+ const Header* h = fBuffer->header(fCurrentEntry);
+ fCurrentEntry += fBuffer->entrySize(h);
+ }
+ } else {
+ // First call to next
+ fCurrentEntry = fBuffer->fData.begin();
+ }
+ // Nothing else is needed to do but report whether or not the updated pointer is valid
+ return fCurrentEntry < fBuffer->fData.end();
+}
diff --git a/src/gpu/geometry/GrQuadList.h b/src/gpu/geometry/GrQuadList.h
deleted file mode 100644
index b1cbf6a..0000000
--- a/src/gpu/geometry/GrQuadList.h
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Copyright 2019 Google LLC
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrQuadList_DEFINED
-#define GrQuadList_DEFINED
-
-#include "include/private/SkTArray.h"
-#include "src/gpu/geometry/GrQuad.h"
-
-// Underlying data used by GrQuadListBase. It is defined outside of GrQuadListBase due to compiler
-// issues related to specializing member types.
-template<typename T>
-struct QuadData {
- float fX[4];
- float fY[4];
- T fMetadata;
-};
-
-template<>
-struct QuadData<void> {
- float fX[4];
- float fY[4];
-};
-
-// A dynamic list of (possibly) perspective quads that tracks the most general quad type of all
-// added quads. It avoids storing the 3rd component if the quad type never becomes perspective.
-// Use GrQuadList subclass when only storing quads. Use GrTQuadList subclass when storing quads
-// and per-quad templated metadata (such as color or domain).
-template<typename T>
-class GrQuadListBase {
-public:
-
- int count() const { return fXYs.count(); }
-
- GrQuad::Type quadType() const { return fType; }
-
- void reserve(int count, bool needsPerspective) {
- fXYs.reserve(count);
- if (needsPerspective || fType == GrQuad::Type::kPerspective) {
- fWs.reserve(4 * count);
- }
- }
-
- GrQuad operator[] (int i) const {
- SkASSERT(i < this->count());
- SkASSERT(i >= 0);
-
- const QuadData<T>& item = fXYs[i];
- if (fType == GrQuad::Type::kPerspective) {
- // Read the explicit ws
- return GrQuad(item.fX, item.fY, fWs.begin() + 4 * i, fType);
- } else {
- // Ws are implicitly 1s.
- static constexpr float kNoPerspectiveWs[4] = {1.f, 1.f, 1.f, 1.f};
- return GrQuad(item.fX, item.fY, kNoPerspectiveWs, fType);
- }
- }
-
- // Subclasses expose push_back(const GrQuad|GrPerspQuad&, GrQuadType, [const T&]), where
- // the metadata argument is only present in GrTQuadList's push_back definition.
-
-protected:
- GrQuadListBase() : fType(GrQuad::Type::kAxisAligned) {}
-
- void concatImpl(const GrQuadListBase<T>& that) {
- this->upgradeType(that.fType);
- fXYs.push_back_n(that.fXYs.count(), that.fXYs.begin());
- if (fType == GrQuad::Type::kPerspective) {
- if (that.fType == GrQuad::Type::kPerspective) {
- // Copy the other's ws into the end of this list's data
- fWs.push_back_n(that.fWs.count(), that.fWs.begin());
- } else {
- // This list stores ws but the appended list had implicit 1s, so add explicit 1s to
- // fill out the total list
- fWs.push_back_n(4 * that.count(), 1.f);
- }
- }
- }
-
- // Returns the added item data so that its metadata can be initialized if T is not void
- QuadData<T>& pushBackImpl(const GrQuad& quad) {
- this->upgradeType(quad.quadType());
- QuadData<T>& item = fXYs.push_back();
- memcpy(item.fX, quad.fX, 4 * sizeof(float));
- memcpy(item.fY, quad.fY, 4 * sizeof(float));
- if (fType == GrQuad::Type::kPerspective) {
- fWs.push_back_n(4, quad.fW);
- }
- return item;
- }
-
- const QuadData<T>& item(int i) const {
- return fXYs[i];
- }
-
- QuadData<T>& item(int i) {
- return fXYs[i];
- }
-
-private:
- void upgradeType(GrQuad::Type type) {
- // Possibly upgrade the overall type tracked by the list
- if (type > fType) {
- fType = type;
- if (type == GrQuad::Type::kPerspective) {
- // All existing quads were 2D, so the ws array just needs to be filled with 1s
- fWs.push_back_n(4 * this->count(), 1.f);
- }
- }
- }
-
- // Interleaves xs, ys, and per-quad metadata so that all data for a single quad is together
- // (barring ws, which can be dropped entirely if the quad type allows it).
- SkSTArray<1, QuadData<T>, true> fXYs;
- // The w channel is kept separate so that it can remain empty when only dealing with 2D quads.
- SkTArray<float, true> fWs;
-
- GrQuad::Type fType;
-};
-
-// This list only stores the quad data itself.
-class GrQuadList : public GrQuadListBase<void> {
-public:
- GrQuadList() : INHERITED() {}
-
- void concat(const GrQuadList& that) {
- this->concatImpl(that);
- }
-
- void push_back(const GrQuad& quad) {
- this->pushBackImpl(quad);
- }
-
-private:
- typedef GrQuadListBase<void> INHERITED;
-};
-
-// This variant of the list allows simple metadata to be stored per quad as well, such as color
-// or texture domain.
-template<typename T>
-class GrTQuadList : public GrQuadListBase<T> {
-public:
- GrTQuadList() : INHERITED() {}
-
- void concat(const GrTQuadList<T>& that) {
- this->concatImpl(that);
- }
-
- // Adding to the list requires metadata
- void push_back(const GrQuad& quad, T&& metadata) {
- QuadData<T>& item = this->pushBackImpl(quad);
- item.fMetadata = std::move(metadata);
- }
-
- // And provide access to the metadata per quad
- const T& metadata(int i) const {
- return this->item(i).fMetadata;
- }
-
- T& metadata(int i) {
- return this->item(i).fMetadata;
- }
-
-private:
- typedef GrQuadListBase<T> INHERITED;
-};
-
-#endif
diff --git a/src/gpu/ops/GrFillRectOp.cpp b/src/gpu/ops/GrFillRectOp.cpp
index f5f7c7c..fe3cc65 100644
--- a/src/gpu/ops/GrFillRectOp.cpp
+++ b/src/gpu/ops/GrFillRectOp.cpp
@@ -14,7 +14,7 @@
#include "src/gpu/GrPaint.h"
#include "src/gpu/SkGr.h"
#include "src/gpu/geometry/GrQuad.h"
-#include "src/gpu/geometry/GrQuadList.h"
+#include "src/gpu/geometry/GrQuadBuffer.h"
#include "src/gpu/geometry/GrQuadUtils.h"
#include "src/gpu/glsl/GrGLSLColorSpaceXformHelper.h"
#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
@@ -79,17 +79,13 @@
GrQuadAAFlags edgeFlags, const GrUserStencilSettings* stencil,
const GrQuad& deviceQuad, const GrQuad& localQuad)
: INHERITED(ClassID())
- , fHelper(args, aaType, stencil) {
- // The color stored with the quad is the clear color if a scissor-clear is decided upon
- // when executing the op.
- fDeviceQuads.push_back(deviceQuad, { paintColor, edgeFlags });
-
- if (!fHelper.isTrivial()) {
- // Conservatively keep track of the local coordinates; it may be that the paint doesn't
- // need them after analysis is finished. If the paint is known to be solid up front they
- // can be skipped entirely.
- fLocalQuads.push_back(localQuad);
- }
+ , fHelper(args, aaType, stencil)
+ , fQuads(1, !fHelper.isTrivial()) {
+ // Conservatively keep track of the local coordinates; it may be that the paint doesn't
+ // need them after analysis is finished. If the paint is known to be solid up front they
+ // can be skipped entirely.
+ fQuads.append(deviceQuad, { paintColor, edgeFlags },
+ fHelper.isTrivial() ? nullptr : &localQuad);
this->setBounds(deviceQuad.bounds(), HasAABloat(aaType == GrAAType::kCoverage),
IsZeroArea::kNo);
}
@@ -103,18 +99,17 @@
#ifdef SK_DEBUG
SkString dumpInfo() const override {
SkString str;
- str.appendf("# draws: %u\n", this->quadCount());
+ str.appendf("# draws: %u\n", fQuads.count());
str.appendf("Device quad type: %u, local quad type: %u\n",
- (uint32_t) fDeviceQuads.quadType(), (uint32_t) fLocalQuads.quadType());
+ (uint32_t) fQuads.deviceQuadType(), (uint32_t) fQuads.localQuadType());
str += fHelper.dumpInfo();
- GrQuad device, local;
- for (int i = 0; i < this->quadCount(); i++) {
- device = fDeviceQuads[i];
- const ColorAndAA& info = fDeviceQuads.metadata(i);
- if (!fHelper.isTrivial()) {
- local = fLocalQuads[i];
- }
- str += dump_quad_info(i, device, local, info.fColor, info.fAAFlags);
+ int i = 0;
+ auto iter = fQuads.iterator();
+ while(iter.next()) {
+ const ColorAndAA& info = iter.metadata();
+ str += dump_quad_info(i, iter.deviceQuad(), iter.localQuad(),
+ info.fColor, info.fAAFlags);
+ i++;
}
str += INHERITED::dumpInfo();
return str;
@@ -125,12 +120,12 @@
const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
GrClampType clampType) override {
// Initialize aggregate color analysis with the first quad's color (which always exists)
- SkASSERT(this->quadCount() > 0);
- GrProcessorAnalysisColor quadColors(fDeviceQuads.metadata(0).fColor);
+ auto iter = fQuads.metadata();
+ SkAssertResult(iter.next());
+ GrProcessorAnalysisColor quadColors(iter->fColor);
// Then combine the colors of any additional quads (e.g. from MakeSet)
- for (int i = 1; i < this->quadCount(); ++i) {
- quadColors = GrProcessorAnalysisColor::Combine(quadColors,
- fDeviceQuads.metadata(i).fColor);
+ while(iter.next()) {
+ quadColors = GrProcessorAnalysisColor::Combine(quadColors, iter->fColor);
if (quadColors.isUnknown()) {
// No point in accumulating additional starting colors, combining cannot make it
// less unknown.
@@ -147,19 +142,19 @@
caps, clip, hasMixedSampledCoverage, clampType, coverage, &quadColors);
// If there is a constant color after analysis, that means all of the quads should be set
// to the same color (even if they started out with different colors).
+ iter = fQuads.metadata();
SkPMColor4f colorOverride;
if (quadColors.isConstant(&colorOverride)) {
fColorType = GrQuadPerEdgeAA::MinColorType(colorOverride, clampType, caps);
- for (int i = 0; i < this->quadCount(); ++i) {
- fDeviceQuads.metadata(i).fColor = colorOverride;
+ while(iter.next()) {
+ iter->fColor = colorOverride;
}
} else {
// Otherwise compute the color type needed as the max over all quads.
fColorType = ColorType::kNone;
- for (int i = 0; i < this->quadCount(); ++i) {
- SkPMColor4f* color = &fDeviceQuads.metadata(i).fColor;
+ while(iter.next()) {
fColorType = SkTMax(fColorType,
- GrQuadPerEdgeAA::MinColorType(*color, clampType, caps));
+ GrQuadPerEdgeAA::MinColorType(iter->fColor, clampType, caps));
}
}
// Most SkShaders' FPs multiply their calculated color by the paint color or alpha. We want
@@ -197,7 +192,7 @@
using Domain = GrQuadPerEdgeAA::Domain;
static constexpr SkRect kEmptyDomain = SkRect::MakeEmpty();
- VertexSpec vertexSpec(fDeviceQuads.quadType(), fColorType, fLocalQuads.quadType(),
+ VertexSpec vertexSpec(fQuads.deviceQuadType(), fColorType, fQuads.localQuadType(),
fHelper.usesLocalCoords(), Domain::kNo, fHelper.aaType(),
fHelper.compatibleWithCoverageAsAlpha());
// Make sure that if the op thought it was a solid color, the vertex spec does not use
@@ -212,7 +207,7 @@
// Fill the allocated vertex data
void* vdata = target->makeVertexSpace(
- vertexSize, this->quadCount() * vertexSpec.verticesPerQuad(),
+ vertexSize, fQuads.count() * vertexSpec.verticesPerQuad(),
&vbuffer, &vertexOffsetInBuffer);
if (!vdata) {
SkDebugf("Could not allocate vertices\n");
@@ -221,27 +216,19 @@
// vertices pointer advances through vdata based on Tessellate's return value
void* vertices = vdata;
- if (fHelper.isTrivial()) {
- SkASSERT(fLocalQuads.count() == 0); // No local coords, so send an ignored dummy quad
- static const GrQuad kIgnoredLocal(SkRect::MakeEmpty());
-
- for (int i = 0; i < this->quadCount(); ++i) {
- const ColorAndAA& info = fDeviceQuads.metadata(i);
- vertices = GrQuadPerEdgeAA::Tessellate(vertices, vertexSpec, fDeviceQuads[i],
- info.fColor, kIgnoredLocal, kEmptyDomain, info.fAAFlags);
- }
- } else {
- SkASSERT(fLocalQuads.count() == fDeviceQuads.count());
- for (int i = 0; i < this->quadCount(); ++i) {
- const ColorAndAA& info = fDeviceQuads.metadata(i);
- vertices = GrQuadPerEdgeAA::Tessellate(vertices, vertexSpec, fDeviceQuads[i],
- info.fColor, fLocalQuads[i], kEmptyDomain, info.fAAFlags);
- }
+ auto iter = fQuads.iterator();
+ while(iter.next()) {
+ // All entries should have local coords, or no entries should have local coords,
+ // matching !helper.isTrivial() (which is more conservative than helper.usesLocalCoords)
+ SkASSERT(iter.isLocalValid() != fHelper.isTrivial());
+ auto info = iter.metadata();
+ vertices = GrQuadPerEdgeAA::Tessellate(vertices, vertexSpec, iter.deviceQuad(),
+ info.fColor, iter.localQuad(), kEmptyDomain, info.fAAFlags);
}
// Configure the mesh for the vertex data
GrMesh* mesh = target->allocMeshes(1);
- if (!GrQuadPerEdgeAA::ConfigureMeshIndices(target, mesh, vertexSpec, this->quadCount())) {
+ if (!GrQuadPerEdgeAA::ConfigureMeshIndices(target, mesh, vertexSpec, fQuads.count())) {
SkDebugf("Could not allocate indices\n");
return;
}
@@ -259,7 +246,7 @@
if ((fHelper.aaType() == GrAAType::kCoverage ||
that->fHelper.aaType() == GrAAType::kCoverage) &&
- this->quadCount() + that->quadCount() > GrQuadPerEdgeAA::kNumAAQuadsInIndexBuffer) {
+ fQuads.count() + that->fQuads.count() > GrQuadPerEdgeAA::kNumAAQuadsInIndexBuffer) {
// This limit on batch size seems to help on Adreno devices
return CombineResult::kCannotCombine;
}
@@ -285,10 +272,7 @@
fHelper.setAAType(GrAAType::kCoverage);
}
- fDeviceQuads.concat(that->fDeviceQuads);
- if (!fHelper.isTrivial()) {
- fLocalQuads.concat(that->fLocalQuads);
- }
+ fQuads.concat(that->fQuads);
return CombineResult::kMerged;
}
@@ -316,17 +300,7 @@
newBounds.joinPossiblyEmptyRect(deviceQuad.bounds());
this->setBounds(newBounds, HasAABloat(fHelper.aaType() == GrAAType::kCoverage),
IsZeroArea::kNo);
- fDeviceQuads.push_back(deviceQuad, { color, edgeAA });
- if (!fHelper.isTrivial()) {
- fLocalQuads.push_back(localQuad);
- }
- }
-
- int quadCount() const {
- // Sanity check that the parallel arrays for quad properties all have the same size
- SkASSERT(fDeviceQuads.count() == fLocalQuads.count() ||
- (fLocalQuads.count() == 0 && fHelper.isTrivial()));
- return fDeviceQuads.count();
+ fQuads.append(deviceQuad, { color, edgeAA }, fHelper.isTrivial() ? nullptr : &localQuad);
}
struct ColorAndAA {
@@ -335,9 +309,7 @@
};
Helper fHelper;
- GrTQuadList<ColorAndAA> fDeviceQuads;
- // No metadata attached to the local quads; this list is empty when local coords are not needed.
- GrQuadList fLocalQuads;
+ GrQuadBuffer<ColorAndAA> fQuads;
ColorType fColorType;
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index d77e88b..2aa2c06 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -30,7 +30,7 @@
#include "src/gpu/GrTextureProxy.h"
#include "src/gpu/SkGr.h"
#include "src/gpu/geometry/GrQuad.h"
-#include "src/gpu/geometry/GrQuadList.h"
+#include "src/gpu/geometry/GrQuadBuffer.h"
#include "src/gpu/geometry/GrQuadUtils.h"
#include "src/gpu/glsl/GrGLSLVarying.h"
#include "src/gpu/ops/GrMeshDrawOp.h"
@@ -200,13 +200,14 @@
SkString dumpInfo() const override {
SkString str;
str.appendf("# draws: %d\n", fQuads.count());
- int q = 0;
+ auto iter = fQuads.iterator();
for (unsigned p = 0; p < fProxyCnt; ++p) {
str.appendf("Proxy ID: %d, Filter: %d\n", fProxies[p].fProxy->uniqueID().asUInt(),
static_cast<int>(fFilter));
- for (int i = 0; i < fProxies[p].fQuadCnt; ++i, ++q) {
- GrQuad quad = fQuads[q];
- const ColorDomainAndAA& info = fQuads.metadata(i);
+ int i = 0;
+ while(i < fProxies[p].fQuadCnt && iter.next()) {
+ const GrQuad& quad = iter.deviceQuad();
+ const ColorDomainAndAA& info = iter.metadata();
str.appendf(
"%d: Color: 0x%08x, TexRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f] "
"Quad [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n",
@@ -215,6 +216,7 @@
quad.point(0).fY, quad.point(1).fX, quad.point(1).fY,
quad.point(2).fX, quad.point(2).fY, quad.point(3).fX,
quad.point(3).fY);
+ i++;
}
}
str += INHERITED::dumpInfo();
@@ -226,9 +228,9 @@
const GrCaps& caps, const GrAppliedClip*, bool hasMixedSampledCoverage,
GrClampType clampType) override {
fColorType = static_cast<unsigned>(ColorType::kNone);
- for (int q = 0; q < fQuads.count(); ++q) {
- const ColorDomainAndAA& info = fQuads.metadata(q);
- auto colorType = GrQuadPerEdgeAA::MinColorType(info.fColor, clampType, caps);
+ auto iter = fQuads.metadata();
+ while(iter.next()) {
+ auto colorType = GrQuadPerEdgeAA::MinColorType(iter->fColor, clampType, caps);
fColorType = SkTMax(fColorType, static_cast<unsigned>(colorType));
}
return GrProcessorSet::EmptySetAnalysis();
@@ -244,6 +246,35 @@
private:
friend class ::GrOpMemoryPool;
+ struct ColorDomainAndAA {
+ // Special constructor to convert enums into the packed bits, which should not delete
+ // the implicit move constructor (but it does require us to declare an empty ctor for
+ // use with the GrTQuadList).
+ ColorDomainAndAA(const SkPMColor4f& color, const SkRect& srcRect,
+ Domain hasDomain, GrQuadAAFlags aaFlags)
+ : fColor(color)
+ , fSrcRect(srcRect)
+ , fHasDomain(static_cast<unsigned>(hasDomain))
+ , fAAFlags(static_cast<unsigned>(aaFlags)) {
+ SkASSERT(fHasDomain == static_cast<unsigned>(hasDomain));
+ SkASSERT(fAAFlags == static_cast<unsigned>(aaFlags));
+ }
+ ColorDomainAndAA() = default;
+
+ SkPMColor4f fColor;
+ // Even if fSrcQuadIndex provides source coords, use fSrcRect for domain constraint
+ SkRect fSrcRect;
+ unsigned fHasDomain : 1;
+ unsigned fAAFlags : 4;
+
+ Domain domain() const { return Domain(fHasDomain); }
+ GrQuadAAFlags aaFlags() const { return static_cast<GrQuadAAFlags>(fAAFlags); }
+ };
+ struct Proxy {
+ GrTextureProxy* fProxy;
+ int fQuadCnt;
+ };
+
// dstQuad and dstQuadType should be the geometry transformed by the view matrix.
// srcRect represents original src rect and will be used as the domain when constraint is strict
// If srcQuad is provided, it will be used for the local coords instead of srcRect, although
@@ -253,6 +284,7 @@
SkCanvas::SrcRectConstraint constraint, const GrQuad* srcQuad, GrAAType aaType,
GrQuadAAFlags aaFlags, sk_sp<GrColorSpaceXform> textureColorSpaceXform)
: INHERITED(ClassID())
+ , fQuads(1, srcQuad != nullptr)
, fTextureColorSpaceXform(std::move(textureColorSpaceXform))
, fFilter(static_cast<unsigned>(filter)) {
// Clean up disparities between the overall aa type and edge configuration and apply
@@ -275,11 +307,8 @@
Domain domain = constraint == SkCanvas::kStrict_SrcRectConstraint ? Domain::kYes
: Domain::kNo;
- // Initially, if srcQuad is provided it will always be at index 0 of fSrcQuads
- fQuads.push_back(dstQuad, {color, srcRect, srcQuad ? 0 : -1, domain, aaFlags});
- if (srcQuad) {
- fSrcQuads.push_back(*srcQuad);
- }
+ fQuads.append(dstQuad, {color, srcRect, domain, aaFlags}, srcQuad);
+
fProxyCnt = 1;
fProxies[0] = {proxy.release(), 1};
this->setBounds(dstQuad.bounds(), HasAABloat(aaType == GrAAType::kCoverage),
@@ -291,16 +320,13 @@
SkCanvas::SrcRectConstraint constraint, const SkMatrix& viewMatrix,
sk_sp<GrColorSpaceXform> textureColorSpaceXform)
: INHERITED(ClassID())
+ , fQuads(cnt, false)
, fTextureColorSpaceXform(std::move(textureColorSpaceXform))
, fFilter(static_cast<unsigned>(filter)) {
fProxyCnt = SkToUInt(cnt);
SkRect bounds = SkRectPriv::MakeLargestInverted();
GrAAType overallAAType = GrAAType::kNone; // aa type maximally compatible with all dst rects
bool mustFilter = false;
- // Most dst rects are transformed by the same view matrix, so their quad types start
- // identical, unless an entry provides a dstClip or additional transform that changes it.
- // The quad list will automatically adapt to that.
- fQuads.reserve(cnt, viewMatrix.hasPerspective());
bool allOpaque = true;
Domain netDomain = Domain::kNo;
for (unsigned p = 0; p < fProxyCnt; ++p) {
@@ -348,16 +374,17 @@
float alpha = SkTPin(set[p].fAlpha, 0.f, 1.f);
allOpaque &= (1.f == alpha);
SkPMColor4f color{alpha, alpha, alpha, alpha};
- int srcQuadIndex = -1;
+ GrQuad srcQuad;
+ const GrQuad* srcQuadPtr = nullptr;
if (set[p].fDstClipQuad) {
// Derive new source coordinates that match dstClip's relative locations in dstRect,
// but with respect to srcRect
- SkPoint srcQuad[4];
- GrMapRectPoints(set[p].fDstRect, set[p].fSrcRect, set[p].fDstClipQuad, srcQuad, 4);
- fSrcQuads.push_back(GrQuad::MakeFromSkQuad(srcQuad, SkMatrix::I()));
- srcQuadIndex = fSrcQuads.count() - 1;
+ SkPoint srcPts[4];
+ GrMapRectPoints(set[p].fDstRect, set[p].fSrcRect, set[p].fDstClipQuad, srcPts, 4);
+ srcQuad = GrQuad::MakeFromSkQuad(srcPts, SkMatrix::I());
+ srcQuadPtr = &srcQuad;
}
- fQuads.push_back(quad, {color, set[p].fSrcRect, srcQuadIndex, domainForQuad, aaFlags});
+ fQuads.append(quad, {color, set[p].fSrcRect, domainForQuad, aaFlags}, srcQuadPtr);
}
fAAType = static_cast<unsigned>(overallAAType);
if (!mustFilter) {
@@ -367,8 +394,8 @@
fDomain = static_cast<unsigned>(netDomain);
}
- void tess(void* v, const VertexSpec& spec, const GrTextureProxy* proxy, int start,
- int cnt) const {
+ void tess(void* v, const VertexSpec& spec, const GrTextureProxy* proxy,
+ GrQuadBuffer<ColorDomainAndAA>::Iter* iter, int cnt) const {
TRACE_EVENT0("skia", TRACE_FUNC);
auto origin = proxy->origin();
const auto* texture = proxy->peekTexture();
@@ -382,17 +409,18 @@
h = 1.f;
}
- for (int i = start; i < start + cnt; ++i) {
- const GrQuad& device = fQuads[i];
- const ColorDomainAndAA& info = fQuads.metadata(i);
+ int i = 0;
+ while(i < cnt && iter->next()) {
+ const ColorDomainAndAA& info = iter->metadata();
- GrQuad srcQuad = info.fSrcQuadIndex >= 0 ?
- compute_src_quad(origin, fSrcQuads[info.fSrcQuadIndex], iw, ih, h) :
+ GrQuad srcQuad = iter->isLocalValid() ?
+ compute_src_quad(origin, iter->localQuad(), iw, ih, h) :
compute_src_quad_from_rect(origin, info.fSrcRect, iw, ih, h);
SkRect domain =
compute_domain(info.domain(), this->filter(), origin, info.fSrcRect, iw, ih, h);
- v = GrQuadPerEdgeAA::Tessellate(v, spec, device, info.fColor, srcQuad, domain,
- info.aaFlags());
+ v = GrQuadPerEdgeAA::Tessellate(v, spec, iter->deviceQuad(), info.fColor, srcQuad,
+ domain, info.aaFlags());
+ i++;
}
}
@@ -409,13 +437,11 @@
const GrSwizzle& swizzle = fProxies[0].fProxy->textureSwizzle();
GrAAType aaType = this->aaType();
for (const auto& op : ChainRange<TextureOp>(this)) {
- if (op.fQuads.quadType() > quadType) {
- quadType = op.fQuads.quadType();
+ if (op.fQuads.deviceQuadType() > quadType) {
+ quadType = op.fQuads.deviceQuadType();
}
- if (op.fSrcQuads.quadType() > srcQuadType) {
- // Should only become more general if there are quads to use instead of fSrcRect
- SkASSERT(op.fSrcQuads.count() > 0);
- srcQuadType = op.fSrcQuads.quadType();
+ if (op.fQuads.localQuadType() > srcQuadType) {
+ srcQuadType = op.fQuads.localQuadType();
}
if (op.fDomain) {
domain = Domain::kYes;
@@ -475,7 +501,7 @@
int m = 0;
for (const auto& op : ChainRange<TextureOp>(this)) {
- int q = 0;
+ auto iter = op.fQuads.iterator();
for (unsigned p = 0; p < op.fProxyCnt; ++p) {
int quadCnt = op.fProxies[p].fQuadCnt;
auto* proxy = op.fProxies[p].fProxy;
@@ -492,7 +518,7 @@
}
SkASSERT(numAllocatedVertices >= meshVertexCnt);
- op.tess(vdata, vertexSpec, proxy, q, quadCnt);
+ op.tess(vdata, vertexSpec, proxy, &iter, quadCnt);
if (!GrQuadPerEdgeAA::ConfigureMeshIndices(target, &(meshes[m]), vertexSpec,
quadCnt)) {
@@ -508,8 +534,10 @@
numQuadVerticesLeft -= meshVertexCnt;
vertexOffsetInBuffer += meshVertexCnt;
vdata = reinterpret_cast<char*>(vdata) + vertexSize * meshVertexCnt;
- q += quadCnt;
}
+ // If quad counts per proxy were calculated correctly, the entire iterator should have
+ // been consumed.
+ SkASSERT(!iter.next());
}
SkASSERT(!numQuadVerticesLeft);
SkASSERT(!numAllocatedVertices);
@@ -567,79 +595,17 @@
fAAType = static_cast<unsigned>(GrAAType::kCoverage);
}
- // Concatenate quad lists together, updating the fSrcQuadIndex in the appended quads
- // to account for the new starting index in fSrcQuads
- int srcQuadOffset = fSrcQuads.count();
- int oldQuadCount = fQuads.count();
-
- fSrcQuads.concat(that->fSrcQuads);
+ // Concatenate quad lists together
fQuads.concat(that->fQuads);
fProxies[0].fQuadCnt += that->fQuads.count();
- if (that->fSrcQuads.count() > 0) {
- // Some of the concatenated quads pointed to fSrcQuads, so adjust the indices for the
- // newly appended quads
- for (int i = oldQuadCount; i < fQuads.count(); ++i) {
- if (fQuads.metadata(i).fSrcQuadIndex >= 0) {
- fQuads.metadata(i).fSrcQuadIndex += srcQuadOffset;
- }
- }
- }
-
- // Confirm all tracked state makes sense when in debug builds
-#ifdef SK_DEBUG
- SkASSERT(fSrcQuads.count() <= fQuads.count());
- for (int i = 0; i < fQuads.count(); ++i) {
- int srcIndex = fQuads.metadata(i).fSrcQuadIndex;
- if (srcIndex >= 0) {
- // Make sure it points to a valid index, in the right region of the list
- SkASSERT(srcIndex < fSrcQuads.count());
- SkASSERT((i < oldQuadCount && srcIndex < srcQuadOffset) ||
- (i >= oldQuadCount && srcIndex >= srcQuadOffset));
- }
- }
-#endif
return CombineResult::kMerged;
}
GrAAType aaType() const { return static_cast<GrAAType>(fAAType); }
GrSamplerState::Filter filter() const { return static_cast<GrSamplerState::Filter>(fFilter); }
- struct ColorDomainAndAA {
- // Special constructor to convert enums into the packed bits, which should not delete
- // the implicit move constructor (but it does require us to declare an empty ctor for
- // use with the GrTQuadList).
- ColorDomainAndAA(const SkPMColor4f& color, const SkRect& srcRect, int srcQuadIndex,
- Domain hasDomain, GrQuadAAFlags aaFlags)
- : fColor(color)
- , fSrcRect(srcRect)
- , fSrcQuadIndex(srcQuadIndex)
- , fHasDomain(static_cast<unsigned>(hasDomain))
- , fAAFlags(static_cast<unsigned>(aaFlags)) {
- SkASSERT(fHasDomain == static_cast<unsigned>(hasDomain));
- SkASSERT(fAAFlags == static_cast<unsigned>(aaFlags));
- }
- ColorDomainAndAA() = default;
-
- SkPMColor4f fColor;
- // Even if fSrcQuadIndex provides source coords, use fSrcRect for domain constraint
- SkRect fSrcRect;
- // If >= 0, use to access fSrcQuads instead of fSrcRect for the source coordinates
- int fSrcQuadIndex;
- unsigned fHasDomain : 1;
- unsigned fAAFlags : 4;
-
- Domain domain() const { return Domain(fHasDomain); }
- GrQuadAAFlags aaFlags() const { return static_cast<GrQuadAAFlags>(fAAFlags); }
- };
- struct Proxy {
- GrTextureProxy* fProxy;
- int fQuadCnt;
- };
- GrTQuadList<ColorDomainAndAA> fQuads;
- // The majority of texture ops will not track a complete src quad so this is indexed separately
- // and may be of different size to fQuads.
- GrQuadList fSrcQuads;
+ GrQuadBuffer<ColorDomainAndAA> fQuads;
sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
unsigned fFilter : 2;
unsigned fAAType : 2;