Revert "Replace GrQuadList with variable-length quad buffer"
This reverts commit f281604429
.
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>
This commit is contained in:
parent
70aae53d57
commit
19628ec144
@ -346,7 +346,7 @@ skia_gpu_sources = [
|
|||||||
"$_src/gpu/geometry/GrPathUtils.h",
|
"$_src/gpu/geometry/GrPathUtils.h",
|
||||||
"$_src/gpu/geometry/GrQuad.cpp",
|
"$_src/gpu/geometry/GrQuad.cpp",
|
||||||
"$_src/gpu/geometry/GrQuad.h",
|
"$_src/gpu/geometry/GrQuad.h",
|
||||||
"$_src/gpu/geometry/GrQuadBuffer.h",
|
"$_src/gpu/geometry/GrQuadList.h",
|
||||||
"$_src/gpu/geometry/GrQuadUtils.cpp",
|
"$_src/gpu/geometry/GrQuadUtils.cpp",
|
||||||
"$_src/gpu/geometry/GrQuadUtils.h",
|
"$_src/gpu/geometry/GrQuadUtils.h",
|
||||||
"$_src/gpu/geometry/GrRect.h",
|
"$_src/gpu/geometry/GrRect.h",
|
||||||
|
@ -106,8 +106,8 @@ tests_sources = [
|
|||||||
"$_tests/GrOpListFlushTest.cpp",
|
"$_tests/GrOpListFlushTest.cpp",
|
||||||
"$_tests/GrPipelineDynamicStateTest.cpp",
|
"$_tests/GrPipelineDynamicStateTest.cpp",
|
||||||
"$_tests/GrPorterDuffTest.cpp",
|
"$_tests/GrPorterDuffTest.cpp",
|
||||||
"$_tests/GrQuadBufferTest.cpp",
|
|
||||||
"$_tests/GrQuadCropTest.cpp",
|
"$_tests/GrQuadCropTest.cpp",
|
||||||
|
"$_tests/GrQuadListTest.cpp",
|
||||||
"$_tests/GrShapeTest.cpp",
|
"$_tests/GrShapeTest.cpp",
|
||||||
"$_tests/GrSurfaceTest.cpp",
|
"$_tests/GrSurfaceTest.cpp",
|
||||||
"$_tests/GrTRecorderTest.cpp",
|
"$_tests/GrTRecorderTest.cpp",
|
||||||
|
@ -132,11 +132,9 @@ public:
|
|||||||
// The non-const pointers are provided to support modifying a GrQuad in-place, but care must be
|
// 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
|
// 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.
|
// no different than using the constructors that accept a quad type.
|
||||||
const float* xs() const { return fX; }
|
|
||||||
float* xs() { return fX; }
|
float* xs() { return fX; }
|
||||||
const float* ys() const { return fY; }
|
|
||||||
float* ys() { return fY; }
|
float* ys() { return fY; }
|
||||||
const float* ws() const { return fW; }
|
|
||||||
float* ws() { return fW; }
|
float* ws() { return fW; }
|
||||||
|
|
||||||
void setQuadType(Type newType) { fType = newType; }
|
void setQuadType(Type newType) { fType = newType; }
|
||||||
|
@ -1,375 +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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#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 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();
|
|
||||||
}
|
|
172
src/gpu/geometry/GrQuadList.h
Normal file
172
src/gpu/geometry/GrQuadList.h
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
@ -14,7 +14,7 @@
|
|||||||
#include "src/gpu/GrPaint.h"
|
#include "src/gpu/GrPaint.h"
|
||||||
#include "src/gpu/SkGr.h"
|
#include "src/gpu/SkGr.h"
|
||||||
#include "src/gpu/geometry/GrQuad.h"
|
#include "src/gpu/geometry/GrQuad.h"
|
||||||
#include "src/gpu/geometry/GrQuadBuffer.h"
|
#include "src/gpu/geometry/GrQuadList.h"
|
||||||
#include "src/gpu/geometry/GrQuadUtils.h"
|
#include "src/gpu/geometry/GrQuadUtils.h"
|
||||||
#include "src/gpu/glsl/GrGLSLColorSpaceXformHelper.h"
|
#include "src/gpu/glsl/GrGLSLColorSpaceXformHelper.h"
|
||||||
#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
|
#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
|
||||||
@ -79,13 +79,17 @@ public:
|
|||||||
GrQuadAAFlags edgeFlags, const GrUserStencilSettings* stencil,
|
GrQuadAAFlags edgeFlags, const GrUserStencilSettings* stencil,
|
||||||
const GrQuad& deviceQuad, const GrQuad& localQuad)
|
const GrQuad& deviceQuad, const GrQuad& localQuad)
|
||||||
: INHERITED(ClassID())
|
: INHERITED(ClassID())
|
||||||
, fHelper(args, aaType, stencil)
|
, fHelper(args, aaType, stencil) {
|
||||||
, fQuads(1, !fHelper.isTrivial()) {
|
// The color stored with the quad is the clear color if a scissor-clear is decided upon
|
||||||
// Conservatively keep track of the local coordinates; it may be that the paint doesn't
|
// when executing the op.
|
||||||
// need them after analysis is finished. If the paint is known to be solid up front they
|
fDeviceQuads.push_back(deviceQuad, { paintColor, edgeFlags });
|
||||||
// can be skipped entirely.
|
|
||||||
fQuads.append(deviceQuad, { paintColor, edgeFlags },
|
if (!fHelper.isTrivial()) {
|
||||||
fHelper.isTrivial() ? nullptr : &localQuad);
|
// 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);
|
||||||
|
}
|
||||||
this->setBounds(deviceQuad.bounds(), HasAABloat(aaType == GrAAType::kCoverage),
|
this->setBounds(deviceQuad.bounds(), HasAABloat(aaType == GrAAType::kCoverage),
|
||||||
IsZeroArea::kNo);
|
IsZeroArea::kNo);
|
||||||
}
|
}
|
||||||
@ -99,17 +103,18 @@ public:
|
|||||||
#ifdef SK_DEBUG
|
#ifdef SK_DEBUG
|
||||||
SkString dumpInfo() const override {
|
SkString dumpInfo() const override {
|
||||||
SkString str;
|
SkString str;
|
||||||
str.appendf("# draws: %u\n", fQuads.count());
|
str.appendf("# draws: %u\n", this->quadCount());
|
||||||
str.appendf("Device quad type: %u, local quad type: %u\n",
|
str.appendf("Device quad type: %u, local quad type: %u\n",
|
||||||
(uint32_t) fQuads.deviceQuadType(), (uint32_t) fQuads.localQuadType());
|
(uint32_t) fDeviceQuads.quadType(), (uint32_t) fLocalQuads.quadType());
|
||||||
str += fHelper.dumpInfo();
|
str += fHelper.dumpInfo();
|
||||||
int i = 0;
|
GrQuad device, local;
|
||||||
auto iter = fQuads.iterator();
|
for (int i = 0; i < this->quadCount(); i++) {
|
||||||
while(iter.next()) {
|
device = fDeviceQuads[i];
|
||||||
const ColorAndAA& info = iter.metadata();
|
const ColorAndAA& info = fDeviceQuads.metadata(i);
|
||||||
str += dump_quad_info(i, iter.deviceQuad(), iter.localQuad(),
|
if (!fHelper.isTrivial()) {
|
||||||
info.fColor, info.fAAFlags);
|
local = fLocalQuads[i];
|
||||||
i++;
|
}
|
||||||
|
str += dump_quad_info(i, device, local, info.fColor, info.fAAFlags);
|
||||||
}
|
}
|
||||||
str += INHERITED::dumpInfo();
|
str += INHERITED::dumpInfo();
|
||||||
return str;
|
return str;
|
||||||
@ -120,12 +125,12 @@ public:
|
|||||||
const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
|
const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
|
||||||
GrClampType clampType) override {
|
GrClampType clampType) override {
|
||||||
// Initialize aggregate color analysis with the first quad's color (which always exists)
|
// Initialize aggregate color analysis with the first quad's color (which always exists)
|
||||||
auto iter = fQuads.metadata();
|
SkASSERT(this->quadCount() > 0);
|
||||||
SkAssertResult(iter.next());
|
GrProcessorAnalysisColor quadColors(fDeviceQuads.metadata(0).fColor);
|
||||||
GrProcessorAnalysisColor quadColors(iter->fColor);
|
|
||||||
// Then combine the colors of any additional quads (e.g. from MakeSet)
|
// Then combine the colors of any additional quads (e.g. from MakeSet)
|
||||||
while(iter.next()) {
|
for (int i = 1; i < this->quadCount(); ++i) {
|
||||||
quadColors = GrProcessorAnalysisColor::Combine(quadColors, iter->fColor);
|
quadColors = GrProcessorAnalysisColor::Combine(quadColors,
|
||||||
|
fDeviceQuads.metadata(i).fColor);
|
||||||
if (quadColors.isUnknown()) {
|
if (quadColors.isUnknown()) {
|
||||||
// No point in accumulating additional starting colors, combining cannot make it
|
// No point in accumulating additional starting colors, combining cannot make it
|
||||||
// less unknown.
|
// less unknown.
|
||||||
@ -142,19 +147,19 @@ public:
|
|||||||
caps, clip, hasMixedSampledCoverage, clampType, coverage, &quadColors);
|
caps, clip, hasMixedSampledCoverage, clampType, coverage, &quadColors);
|
||||||
// If there is a constant color after analysis, that means all of the quads should be set
|
// 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).
|
// to the same color (even if they started out with different colors).
|
||||||
iter = fQuads.metadata();
|
|
||||||
SkPMColor4f colorOverride;
|
SkPMColor4f colorOverride;
|
||||||
if (quadColors.isConstant(&colorOverride)) {
|
if (quadColors.isConstant(&colorOverride)) {
|
||||||
fColorType = GrQuadPerEdgeAA::MinColorType(colorOverride, clampType, caps);
|
fColorType = GrQuadPerEdgeAA::MinColorType(colorOverride, clampType, caps);
|
||||||
while(iter.next()) {
|
for (int i = 0; i < this->quadCount(); ++i) {
|
||||||
iter->fColor = colorOverride;
|
fDeviceQuads.metadata(i).fColor = colorOverride;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Otherwise compute the color type needed as the max over all quads.
|
// Otherwise compute the color type needed as the max over all quads.
|
||||||
fColorType = ColorType::kNone;
|
fColorType = ColorType::kNone;
|
||||||
while(iter.next()) {
|
for (int i = 0; i < this->quadCount(); ++i) {
|
||||||
|
SkPMColor4f* color = &fDeviceQuads.metadata(i).fColor;
|
||||||
fColorType = SkTMax(fColorType,
|
fColorType = SkTMax(fColorType,
|
||||||
GrQuadPerEdgeAA::MinColorType(iter->fColor, clampType, caps));
|
GrQuadPerEdgeAA::MinColorType(*color, clampType, caps));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Most SkShaders' FPs multiply their calculated color by the paint color or alpha. We want
|
// Most SkShaders' FPs multiply their calculated color by the paint color or alpha. We want
|
||||||
@ -192,7 +197,7 @@ private:
|
|||||||
using Domain = GrQuadPerEdgeAA::Domain;
|
using Domain = GrQuadPerEdgeAA::Domain;
|
||||||
static constexpr SkRect kEmptyDomain = SkRect::MakeEmpty();
|
static constexpr SkRect kEmptyDomain = SkRect::MakeEmpty();
|
||||||
|
|
||||||
VertexSpec vertexSpec(fQuads.deviceQuadType(), fColorType, fQuads.localQuadType(),
|
VertexSpec vertexSpec(fDeviceQuads.quadType(), fColorType, fLocalQuads.quadType(),
|
||||||
fHelper.usesLocalCoords(), Domain::kNo, fHelper.aaType(),
|
fHelper.usesLocalCoords(), Domain::kNo, fHelper.aaType(),
|
||||||
fHelper.compatibleWithCoverageAsAlpha());
|
fHelper.compatibleWithCoverageAsAlpha());
|
||||||
// Make sure that if the op thought it was a solid color, the vertex spec does not use
|
// Make sure that if the op thought it was a solid color, the vertex spec does not use
|
||||||
@ -207,7 +212,7 @@ private:
|
|||||||
|
|
||||||
// Fill the allocated vertex data
|
// Fill the allocated vertex data
|
||||||
void* vdata = target->makeVertexSpace(
|
void* vdata = target->makeVertexSpace(
|
||||||
vertexSize, fQuads.count() * vertexSpec.verticesPerQuad(),
|
vertexSize, this->quadCount() * vertexSpec.verticesPerQuad(),
|
||||||
&vbuffer, &vertexOffsetInBuffer);
|
&vbuffer, &vertexOffsetInBuffer);
|
||||||
if (!vdata) {
|
if (!vdata) {
|
||||||
SkDebugf("Could not allocate vertices\n");
|
SkDebugf("Could not allocate vertices\n");
|
||||||
@ -216,19 +221,27 @@ private:
|
|||||||
|
|
||||||
// vertices pointer advances through vdata based on Tessellate's return value
|
// vertices pointer advances through vdata based on Tessellate's return value
|
||||||
void* vertices = vdata;
|
void* vertices = vdata;
|
||||||
auto iter = fQuads.iterator();
|
if (fHelper.isTrivial()) {
|
||||||
while(iter.next()) {
|
SkASSERT(fLocalQuads.count() == 0); // No local coords, so send an ignored dummy quad
|
||||||
// All entries should have local coords, or no entries should have local coords,
|
static const GrQuad kIgnoredLocal(SkRect::MakeEmpty());
|
||||||
// matching !helper.isTrivial() (which is more conservative than helper.usesLocalCoords)
|
|
||||||
SkASSERT(iter.isLocalValid() != fHelper.isTrivial());
|
for (int i = 0; i < this->quadCount(); ++i) {
|
||||||
auto info = iter.metadata();
|
const ColorAndAA& info = fDeviceQuads.metadata(i);
|
||||||
vertices = GrQuadPerEdgeAA::Tessellate(vertices, vertexSpec, iter.deviceQuad(),
|
vertices = GrQuadPerEdgeAA::Tessellate(vertices, vertexSpec, fDeviceQuads[i],
|
||||||
info.fColor, iter.localQuad(), kEmptyDomain, info.fAAFlags);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure the mesh for the vertex data
|
// Configure the mesh for the vertex data
|
||||||
GrMesh* mesh = target->allocMeshes(1);
|
GrMesh* mesh = target->allocMeshes(1);
|
||||||
if (!GrQuadPerEdgeAA::ConfigureMeshIndices(target, mesh, vertexSpec, fQuads.count())) {
|
if (!GrQuadPerEdgeAA::ConfigureMeshIndices(target, mesh, vertexSpec, this->quadCount())) {
|
||||||
SkDebugf("Could not allocate indices\n");
|
SkDebugf("Could not allocate indices\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -246,7 +259,7 @@ private:
|
|||||||
|
|
||||||
if ((fHelper.aaType() == GrAAType::kCoverage ||
|
if ((fHelper.aaType() == GrAAType::kCoverage ||
|
||||||
that->fHelper.aaType() == GrAAType::kCoverage) &&
|
that->fHelper.aaType() == GrAAType::kCoverage) &&
|
||||||
fQuads.count() + that->fQuads.count() > GrQuadPerEdgeAA::kNumAAQuadsInIndexBuffer) {
|
this->quadCount() + that->quadCount() > GrQuadPerEdgeAA::kNumAAQuadsInIndexBuffer) {
|
||||||
// This limit on batch size seems to help on Adreno devices
|
// This limit on batch size seems to help on Adreno devices
|
||||||
return CombineResult::kCannotCombine;
|
return CombineResult::kCannotCombine;
|
||||||
}
|
}
|
||||||
@ -272,7 +285,10 @@ private:
|
|||||||
fHelper.setAAType(GrAAType::kCoverage);
|
fHelper.setAAType(GrAAType::kCoverage);
|
||||||
}
|
}
|
||||||
|
|
||||||
fQuads.concat(that->fQuads);
|
fDeviceQuads.concat(that->fDeviceQuads);
|
||||||
|
if (!fHelper.isTrivial()) {
|
||||||
|
fLocalQuads.concat(that->fLocalQuads);
|
||||||
|
}
|
||||||
return CombineResult::kMerged;
|
return CombineResult::kMerged;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,7 +316,17 @@ private:
|
|||||||
newBounds.joinPossiblyEmptyRect(deviceQuad.bounds());
|
newBounds.joinPossiblyEmptyRect(deviceQuad.bounds());
|
||||||
this->setBounds(newBounds, HasAABloat(fHelper.aaType() == GrAAType::kCoverage),
|
this->setBounds(newBounds, HasAABloat(fHelper.aaType() == GrAAType::kCoverage),
|
||||||
IsZeroArea::kNo);
|
IsZeroArea::kNo);
|
||||||
fQuads.append(deviceQuad, { color, edgeAA }, fHelper.isTrivial() ? nullptr : &localQuad);
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ColorAndAA {
|
struct ColorAndAA {
|
||||||
@ -309,7 +335,9 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
Helper fHelper;
|
Helper fHelper;
|
||||||
GrQuadBuffer<ColorAndAA> fQuads;
|
GrTQuadList<ColorAndAA> fDeviceQuads;
|
||||||
|
// No metadata attached to the local quads; this list is empty when local coords are not needed.
|
||||||
|
GrQuadList fLocalQuads;
|
||||||
|
|
||||||
ColorType fColorType;
|
ColorType fColorType;
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
#include "src/gpu/GrTextureProxy.h"
|
#include "src/gpu/GrTextureProxy.h"
|
||||||
#include "src/gpu/SkGr.h"
|
#include "src/gpu/SkGr.h"
|
||||||
#include "src/gpu/geometry/GrQuad.h"
|
#include "src/gpu/geometry/GrQuad.h"
|
||||||
#include "src/gpu/geometry/GrQuadBuffer.h"
|
#include "src/gpu/geometry/GrQuadList.h"
|
||||||
#include "src/gpu/geometry/GrQuadUtils.h"
|
#include "src/gpu/geometry/GrQuadUtils.h"
|
||||||
#include "src/gpu/glsl/GrGLSLVarying.h"
|
#include "src/gpu/glsl/GrGLSLVarying.h"
|
||||||
#include "src/gpu/ops/GrMeshDrawOp.h"
|
#include "src/gpu/ops/GrMeshDrawOp.h"
|
||||||
@ -200,14 +200,13 @@ public:
|
|||||||
SkString dumpInfo() const override {
|
SkString dumpInfo() const override {
|
||||||
SkString str;
|
SkString str;
|
||||||
str.appendf("# draws: %d\n", fQuads.count());
|
str.appendf("# draws: %d\n", fQuads.count());
|
||||||
auto iter = fQuads.iterator();
|
int q = 0;
|
||||||
for (unsigned p = 0; p < fProxyCnt; ++p) {
|
for (unsigned p = 0; p < fProxyCnt; ++p) {
|
||||||
str.appendf("Proxy ID: %d, Filter: %d\n", fProxies[p].fProxy->uniqueID().asUInt(),
|
str.appendf("Proxy ID: %d, Filter: %d\n", fProxies[p].fProxy->uniqueID().asUInt(),
|
||||||
static_cast<int>(fFilter));
|
static_cast<int>(fFilter));
|
||||||
int i = 0;
|
for (int i = 0; i < fProxies[p].fQuadCnt; ++i, ++q) {
|
||||||
while(i < fProxies[p].fQuadCnt && iter.next()) {
|
GrQuad quad = fQuads[q];
|
||||||
const GrQuad& quad = iter.deviceQuad();
|
const ColorDomainAndAA& info = fQuads.metadata(i);
|
||||||
const ColorDomainAndAA& info = iter.metadata();
|
|
||||||
str.appendf(
|
str.appendf(
|
||||||
"%d: Color: 0x%08x, TexRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f] "
|
"%d: Color: 0x%08x, TexRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f] "
|
||||||
"Quad [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n",
|
"Quad [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n",
|
||||||
@ -216,7 +215,6 @@ public:
|
|||||||
quad.point(0).fY, quad.point(1).fX, quad.point(1).fY,
|
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(2).fX, quad.point(2).fY, quad.point(3).fX,
|
||||||
quad.point(3).fY);
|
quad.point(3).fY);
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
str += INHERITED::dumpInfo();
|
str += INHERITED::dumpInfo();
|
||||||
@ -228,9 +226,9 @@ public:
|
|||||||
const GrCaps& caps, const GrAppliedClip*, bool hasMixedSampledCoverage,
|
const GrCaps& caps, const GrAppliedClip*, bool hasMixedSampledCoverage,
|
||||||
GrClampType clampType) override {
|
GrClampType clampType) override {
|
||||||
fColorType = static_cast<unsigned>(ColorType::kNone);
|
fColorType = static_cast<unsigned>(ColorType::kNone);
|
||||||
auto iter = fQuads.metadata();
|
for (int q = 0; q < fQuads.count(); ++q) {
|
||||||
while(iter.next()) {
|
const ColorDomainAndAA& info = fQuads.metadata(q);
|
||||||
auto colorType = GrQuadPerEdgeAA::MinColorType(iter->fColor, clampType, caps);
|
auto colorType = GrQuadPerEdgeAA::MinColorType(info.fColor, clampType, caps);
|
||||||
fColorType = SkTMax(fColorType, static_cast<unsigned>(colorType));
|
fColorType = SkTMax(fColorType, static_cast<unsigned>(colorType));
|
||||||
}
|
}
|
||||||
return GrProcessorSet::EmptySetAnalysis();
|
return GrProcessorSet::EmptySetAnalysis();
|
||||||
@ -246,35 +244,6 @@ public:
|
|||||||
private:
|
private:
|
||||||
friend class ::GrOpMemoryPool;
|
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.
|
// 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
|
// 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
|
// If srcQuad is provided, it will be used for the local coords instead of srcRect, although
|
||||||
@ -284,7 +253,6 @@ private:
|
|||||||
SkCanvas::SrcRectConstraint constraint, const GrQuad* srcQuad, GrAAType aaType,
|
SkCanvas::SrcRectConstraint constraint, const GrQuad* srcQuad, GrAAType aaType,
|
||||||
GrQuadAAFlags aaFlags, sk_sp<GrColorSpaceXform> textureColorSpaceXform)
|
GrQuadAAFlags aaFlags, sk_sp<GrColorSpaceXform> textureColorSpaceXform)
|
||||||
: INHERITED(ClassID())
|
: INHERITED(ClassID())
|
||||||
, fQuads(1, srcQuad != nullptr)
|
|
||||||
, fTextureColorSpaceXform(std::move(textureColorSpaceXform))
|
, fTextureColorSpaceXform(std::move(textureColorSpaceXform))
|
||||||
, fFilter(static_cast<unsigned>(filter)) {
|
, fFilter(static_cast<unsigned>(filter)) {
|
||||||
// Clean up disparities between the overall aa type and edge configuration and apply
|
// Clean up disparities between the overall aa type and edge configuration and apply
|
||||||
@ -307,8 +275,11 @@ private:
|
|||||||
|
|
||||||
Domain domain = constraint == SkCanvas::kStrict_SrcRectConstraint ? Domain::kYes
|
Domain domain = constraint == SkCanvas::kStrict_SrcRectConstraint ? Domain::kYes
|
||||||
: Domain::kNo;
|
: Domain::kNo;
|
||||||
fQuads.append(dstQuad, {color, srcRect, domain, aaFlags}, srcQuad);
|
// 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);
|
||||||
|
}
|
||||||
fProxyCnt = 1;
|
fProxyCnt = 1;
|
||||||
fProxies[0] = {proxy.release(), 1};
|
fProxies[0] = {proxy.release(), 1};
|
||||||
this->setBounds(dstQuad.bounds(), HasAABloat(aaType == GrAAType::kCoverage),
|
this->setBounds(dstQuad.bounds(), HasAABloat(aaType == GrAAType::kCoverage),
|
||||||
@ -320,13 +291,16 @@ private:
|
|||||||
SkCanvas::SrcRectConstraint constraint, const SkMatrix& viewMatrix,
|
SkCanvas::SrcRectConstraint constraint, const SkMatrix& viewMatrix,
|
||||||
sk_sp<GrColorSpaceXform> textureColorSpaceXform)
|
sk_sp<GrColorSpaceXform> textureColorSpaceXform)
|
||||||
: INHERITED(ClassID())
|
: INHERITED(ClassID())
|
||||||
, fQuads(cnt, false)
|
|
||||||
, fTextureColorSpaceXform(std::move(textureColorSpaceXform))
|
, fTextureColorSpaceXform(std::move(textureColorSpaceXform))
|
||||||
, fFilter(static_cast<unsigned>(filter)) {
|
, fFilter(static_cast<unsigned>(filter)) {
|
||||||
fProxyCnt = SkToUInt(cnt);
|
fProxyCnt = SkToUInt(cnt);
|
||||||
SkRect bounds = SkRectPriv::MakeLargestInverted();
|
SkRect bounds = SkRectPriv::MakeLargestInverted();
|
||||||
GrAAType overallAAType = GrAAType::kNone; // aa type maximally compatible with all dst rects
|
GrAAType overallAAType = GrAAType::kNone; // aa type maximally compatible with all dst rects
|
||||||
bool mustFilter = false;
|
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;
|
bool allOpaque = true;
|
||||||
Domain netDomain = Domain::kNo;
|
Domain netDomain = Domain::kNo;
|
||||||
for (unsigned p = 0; p < fProxyCnt; ++p) {
|
for (unsigned p = 0; p < fProxyCnt; ++p) {
|
||||||
@ -374,17 +348,16 @@ private:
|
|||||||
float alpha = SkTPin(set[p].fAlpha, 0.f, 1.f);
|
float alpha = SkTPin(set[p].fAlpha, 0.f, 1.f);
|
||||||
allOpaque &= (1.f == alpha);
|
allOpaque &= (1.f == alpha);
|
||||||
SkPMColor4f color{alpha, alpha, alpha, alpha};
|
SkPMColor4f color{alpha, alpha, alpha, alpha};
|
||||||
GrQuad srcQuad;
|
int srcQuadIndex = -1;
|
||||||
const GrQuad* srcQuadPtr = nullptr;
|
|
||||||
if (set[p].fDstClipQuad) {
|
if (set[p].fDstClipQuad) {
|
||||||
// Derive new source coordinates that match dstClip's relative locations in dstRect,
|
// Derive new source coordinates that match dstClip's relative locations in dstRect,
|
||||||
// but with respect to srcRect
|
// but with respect to srcRect
|
||||||
SkPoint srcPts[4];
|
SkPoint srcQuad[4];
|
||||||
GrMapRectPoints(set[p].fDstRect, set[p].fSrcRect, set[p].fDstClipQuad, srcPts, 4);
|
GrMapRectPoints(set[p].fDstRect, set[p].fSrcRect, set[p].fDstClipQuad, srcQuad, 4);
|
||||||
srcQuad = GrQuad::MakeFromSkQuad(srcPts, SkMatrix::I());
|
fSrcQuads.push_back(GrQuad::MakeFromSkQuad(srcQuad, SkMatrix::I()));
|
||||||
srcQuadPtr = &srcQuad;
|
srcQuadIndex = fSrcQuads.count() - 1;
|
||||||
}
|
}
|
||||||
fQuads.append(quad, {color, set[p].fSrcRect, domainForQuad, aaFlags}, srcQuadPtr);
|
fQuads.push_back(quad, {color, set[p].fSrcRect, srcQuadIndex, domainForQuad, aaFlags});
|
||||||
}
|
}
|
||||||
fAAType = static_cast<unsigned>(overallAAType);
|
fAAType = static_cast<unsigned>(overallAAType);
|
||||||
if (!mustFilter) {
|
if (!mustFilter) {
|
||||||
@ -394,8 +367,8 @@ private:
|
|||||||
fDomain = static_cast<unsigned>(netDomain);
|
fDomain = static_cast<unsigned>(netDomain);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tess(void* v, const VertexSpec& spec, const GrTextureProxy* proxy,
|
void tess(void* v, const VertexSpec& spec, const GrTextureProxy* proxy, int start,
|
||||||
GrQuadBuffer<ColorDomainAndAA>::Iter* iter, int cnt) const {
|
int cnt) const {
|
||||||
TRACE_EVENT0("skia", TRACE_FUNC);
|
TRACE_EVENT0("skia", TRACE_FUNC);
|
||||||
auto origin = proxy->origin();
|
auto origin = proxy->origin();
|
||||||
const auto* texture = proxy->peekTexture();
|
const auto* texture = proxy->peekTexture();
|
||||||
@ -409,18 +382,17 @@ private:
|
|||||||
h = 1.f;
|
h = 1.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
int i = 0;
|
for (int i = start; i < start + cnt; ++i) {
|
||||||
while(i < cnt && iter->next()) {
|
const GrQuad& device = fQuads[i];
|
||||||
const ColorDomainAndAA& info = iter->metadata();
|
const ColorDomainAndAA& info = fQuads.metadata(i);
|
||||||
|
|
||||||
GrQuad srcQuad = iter->isLocalValid() ?
|
GrQuad srcQuad = info.fSrcQuadIndex >= 0 ?
|
||||||
compute_src_quad(origin, iter->localQuad(), iw, ih, h) :
|
compute_src_quad(origin, fSrcQuads[info.fSrcQuadIndex], iw, ih, h) :
|
||||||
compute_src_quad_from_rect(origin, info.fSrcRect, iw, ih, h);
|
compute_src_quad_from_rect(origin, info.fSrcRect, iw, ih, h);
|
||||||
SkRect domain =
|
SkRect domain =
|
||||||
compute_domain(info.domain(), this->filter(), origin, info.fSrcRect, iw, ih, h);
|
compute_domain(info.domain(), this->filter(), origin, info.fSrcRect, iw, ih, h);
|
||||||
v = GrQuadPerEdgeAA::Tessellate(v, spec, iter->deviceQuad(), info.fColor, srcQuad,
|
v = GrQuadPerEdgeAA::Tessellate(v, spec, device, info.fColor, srcQuad, domain,
|
||||||
domain, info.aaFlags());
|
info.aaFlags());
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -437,11 +409,13 @@ private:
|
|||||||
const GrSwizzle& swizzle = fProxies[0].fProxy->textureSwizzle();
|
const GrSwizzle& swizzle = fProxies[0].fProxy->textureSwizzle();
|
||||||
GrAAType aaType = this->aaType();
|
GrAAType aaType = this->aaType();
|
||||||
for (const auto& op : ChainRange<TextureOp>(this)) {
|
for (const auto& op : ChainRange<TextureOp>(this)) {
|
||||||
if (op.fQuads.deviceQuadType() > quadType) {
|
if (op.fQuads.quadType() > quadType) {
|
||||||
quadType = op.fQuads.deviceQuadType();
|
quadType = op.fQuads.quadType();
|
||||||
}
|
}
|
||||||
if (op.fQuads.localQuadType() > srcQuadType) {
|
if (op.fSrcQuads.quadType() > srcQuadType) {
|
||||||
srcQuadType = op.fQuads.localQuadType();
|
// 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.fDomain) {
|
if (op.fDomain) {
|
||||||
domain = Domain::kYes;
|
domain = Domain::kYes;
|
||||||
@ -501,7 +475,7 @@ private:
|
|||||||
|
|
||||||
int m = 0;
|
int m = 0;
|
||||||
for (const auto& op : ChainRange<TextureOp>(this)) {
|
for (const auto& op : ChainRange<TextureOp>(this)) {
|
||||||
auto iter = op.fQuads.iterator();
|
int q = 0;
|
||||||
for (unsigned p = 0; p < op.fProxyCnt; ++p) {
|
for (unsigned p = 0; p < op.fProxyCnt; ++p) {
|
||||||
int quadCnt = op.fProxies[p].fQuadCnt;
|
int quadCnt = op.fProxies[p].fQuadCnt;
|
||||||
auto* proxy = op.fProxies[p].fProxy;
|
auto* proxy = op.fProxies[p].fProxy;
|
||||||
@ -518,7 +492,7 @@ private:
|
|||||||
}
|
}
|
||||||
SkASSERT(numAllocatedVertices >= meshVertexCnt);
|
SkASSERT(numAllocatedVertices >= meshVertexCnt);
|
||||||
|
|
||||||
op.tess(vdata, vertexSpec, proxy, &iter, quadCnt);
|
op.tess(vdata, vertexSpec, proxy, q, quadCnt);
|
||||||
|
|
||||||
if (!GrQuadPerEdgeAA::ConfigureMeshIndices(target, &(meshes[m]), vertexSpec,
|
if (!GrQuadPerEdgeAA::ConfigureMeshIndices(target, &(meshes[m]), vertexSpec,
|
||||||
quadCnt)) {
|
quadCnt)) {
|
||||||
@ -534,10 +508,8 @@ private:
|
|||||||
numQuadVerticesLeft -= meshVertexCnt;
|
numQuadVerticesLeft -= meshVertexCnt;
|
||||||
vertexOffsetInBuffer += meshVertexCnt;
|
vertexOffsetInBuffer += meshVertexCnt;
|
||||||
vdata = reinterpret_cast<char*>(vdata) + vertexSize * 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(!numQuadVerticesLeft);
|
||||||
SkASSERT(!numAllocatedVertices);
|
SkASSERT(!numAllocatedVertices);
|
||||||
@ -595,17 +567,79 @@ private:
|
|||||||
fAAType = static_cast<unsigned>(GrAAType::kCoverage);
|
fAAType = static_cast<unsigned>(GrAAType::kCoverage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Concatenate quad lists together
|
// 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);
|
||||||
fQuads.concat(that->fQuads);
|
fQuads.concat(that->fQuads);
|
||||||
fProxies[0].fQuadCnt += that->fQuads.count();
|
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;
|
return CombineResult::kMerged;
|
||||||
}
|
}
|
||||||
|
|
||||||
GrAAType aaType() const { return static_cast<GrAAType>(fAAType); }
|
GrAAType aaType() const { return static_cast<GrAAType>(fAAType); }
|
||||||
GrSamplerState::Filter filter() const { return static_cast<GrSamplerState::Filter>(fFilter); }
|
GrSamplerState::Filter filter() const { return static_cast<GrSamplerState::Filter>(fFilter); }
|
||||||
|
|
||||||
GrQuadBuffer<ColorDomainAndAA> fQuads;
|
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;
|
||||||
sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
|
sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
|
||||||
unsigned fFilter : 2;
|
unsigned fFilter : 2;
|
||||||
unsigned fAAType : 2;
|
unsigned fAAType : 2;
|
||||||
|
@ -1,234 +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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "tests/Test.h"
|
|
||||||
|
|
||||||
#include "src/gpu/geometry/GrQuadBuffer.h"
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#define ASSERT(cond) REPORTER_ASSERT(r, cond)
|
|
||||||
#define ASSERTF(cond, ...) REPORTER_ASSERT(r, cond, __VA_ARGS__)
|
|
||||||
#define TEST(name) DEF_TEST(GrQuadBuffer##name, r)
|
|
||||||
|
|
||||||
struct TestData {
|
|
||||||
int fItem1;
|
|
||||||
float fItem2;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void assert_quad_eq(skiatest::Reporter* r, const GrQuad& expected, const GrQuad& actual) {
|
|
||||||
ASSERTF(expected.quadType() == actual.quadType(), "Expected type %d, got %d",
|
|
||||||
(int) expected.quadType(), (int) actual.quadType());
|
|
||||||
for (int i = 0; i < 4; ++i) {
|
|
||||||
ASSERTF(expected.x(i) == actual.x(i), "Expected x(%d) = %f, got %d",
|
|
||||||
i, expected.x(i), actual.x(i));
|
|
||||||
ASSERTF(expected.y(i) == actual.y(i), "Expected y(%d) = %f, got %d",
|
|
||||||
i, expected.y(i), actual.y(i));
|
|
||||||
ASSERTF(expected.w(i) == actual.w(i), "Expected w(%d) = %f, got %d",
|
|
||||||
i, expected.w(i), actual.w(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void assert_metadata_eq(skiatest::Reporter* r, const TestData& expected,
|
|
||||||
const TestData& actual) {
|
|
||||||
ASSERTF(expected.fItem1 == actual.fItem1 && expected.fItem2 == actual.fItem2,
|
|
||||||
"Expected { %d, %f } for metadata, got: { %d %f }",
|
|
||||||
expected.fItem1, expected.fItem2, actual.fItem1, actual.fItem2);
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::vector<GrQuad> generate_quads(float seed, int cnt, const GrQuad::Type types[]) {
|
|
||||||
// For convenience use matrix to derive each quad type, rely on different seed values to
|
|
||||||
// differentiate between quads of the same type
|
|
||||||
SkMatrix rotate;
|
|
||||||
rotate.setRotate(45.f);
|
|
||||||
SkMatrix skew;
|
|
||||||
skew.setSkew(0.5f, 0.5f);
|
|
||||||
SkMatrix perspective;
|
|
||||||
perspective.setPerspX(0.01f);
|
|
||||||
perspective.setPerspY(0.001f);
|
|
||||||
|
|
||||||
std::vector<GrQuad> quads;
|
|
||||||
SkRect rect = SkRect::MakeXYWH(seed, 2.f * seed, 2.f * seed, seed);
|
|
||||||
for (int i = 0; i < cnt; ++i) {
|
|
||||||
GrQuad quad;
|
|
||||||
switch(types[i]) {
|
|
||||||
case GrQuad::Type::kAxisAligned:
|
|
||||||
quad = GrQuad(rect);
|
|
||||||
break;
|
|
||||||
case GrQuad::Type::kRectilinear:
|
|
||||||
quad = GrQuad::MakeFromRect(rect, rotate);
|
|
||||||
break;
|
|
||||||
case GrQuad::Type::kGeneral:
|
|
||||||
quad = GrQuad::MakeFromRect(rect, skew);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
SkASSERT(types[i] == GrQuad::Type::kPerspective);
|
|
||||||
quad = GrQuad::MakeFromRect(rect, perspective);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
SkASSERT(quad.quadType() == types[i]); // sanity check
|
|
||||||
quads.push_back(quad);
|
|
||||||
}
|
|
||||||
return quads;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Append) {
|
|
||||||
// Generate test data, which includes all quad types out of enum-order and duplicates
|
|
||||||
static const int kQuadCount = 6;
|
|
||||||
static const GrQuad::Type kDeviceTypes[] = {
|
|
||||||
GrQuad::Type::kAxisAligned, GrQuad::Type::kRectilinear, GrQuad::Type::kGeneral,
|
|
||||||
GrQuad::Type::kPerspective, GrQuad::Type::kRectilinear, GrQuad::Type::kAxisAligned
|
|
||||||
};
|
|
||||||
// Odd indexed quads will be ignored and not stored in the buffer
|
|
||||||
static const GrQuad::Type kLocalTypes[] = {
|
|
||||||
GrQuad::Type::kGeneral, GrQuad::Type::kGeneral, GrQuad::Type::kRectilinear,
|
|
||||||
GrQuad::Type::kRectilinear, GrQuad::Type::kAxisAligned, GrQuad::Type::kAxisAligned
|
|
||||||
};
|
|
||||||
static_assert(SK_ARRAY_COUNT(kDeviceTypes) == kQuadCount, "device quad count");
|
|
||||||
static_assert(SK_ARRAY_COUNT(kLocalTypes) == kQuadCount, "local quad count");
|
|
||||||
|
|
||||||
std::vector<GrQuad> expectedDeviceQuads = generate_quads(1.f, kQuadCount, kDeviceTypes);
|
|
||||||
std::vector<GrQuad> expectedLocalQuads = generate_quads(2.f, kQuadCount, kLocalTypes);
|
|
||||||
|
|
||||||
// Fill in the buffer with the device quads, and a local quad if the index is even
|
|
||||||
GrQuadBuffer<TestData> buffer;
|
|
||||||
for (int i = 0; i < kQuadCount; ++i) {
|
|
||||||
buffer.append(expectedDeviceQuads[i], // device quad
|
|
||||||
{ 2 * i, 3.f * i }, // metadata
|
|
||||||
i % 2 == 0 ? &expectedLocalQuads[i] : nullptr); // optional local quad
|
|
||||||
}
|
|
||||||
|
|
||||||
// Confirm the state of the buffer
|
|
||||||
ASSERT(kQuadCount == buffer.count());
|
|
||||||
ASSERT(GrQuad::Type::kPerspective == buffer.deviceQuadType());
|
|
||||||
ASSERT(GrQuad::Type::kGeneral == buffer.localQuadType());
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
auto iter = buffer.iterator();
|
|
||||||
while(iter.next()) {
|
|
||||||
// Each entry always has the device quad
|
|
||||||
assert_quad_eq(r, expectedDeviceQuads[i], iter.deviceQuad());
|
|
||||||
assert_metadata_eq(r, {2 * i, 3.f * i}, iter.metadata());
|
|
||||||
|
|
||||||
if (i % 2 == 0) {
|
|
||||||
// Confirm local quads included on even entries
|
|
||||||
ASSERT(iter.isLocalValid());
|
|
||||||
assert_quad_eq(r, expectedLocalQuads[i], iter.localQuad());
|
|
||||||
} else {
|
|
||||||
// Should not have locals
|
|
||||||
ASSERT(!iter.isLocalValid());
|
|
||||||
}
|
|
||||||
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
ASSERTF(i == kQuadCount, "Expected %d iterations, got: %d", kQuadCount, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Concat) {
|
|
||||||
static const int kQuadCount = 2;
|
|
||||||
static const GrQuad::Type kTypesA[] = { GrQuad::Type::kAxisAligned, GrQuad::Type::kRectilinear };
|
|
||||||
static const GrQuad::Type kTypesB[] = { GrQuad::Type::kGeneral, GrQuad::Type::kPerspective };
|
|
||||||
static_assert(SK_ARRAY_COUNT(kTypesA) == kQuadCount, "quadsA count");
|
|
||||||
static_assert(SK_ARRAY_COUNT(kTypesB) == kQuadCount, "quadsB count");
|
|
||||||
|
|
||||||
std::vector<GrQuad> quadsA = generate_quads(1.f, kQuadCount, kTypesA);
|
|
||||||
std::vector<GrQuad> quadsB = generate_quads(2.f, kQuadCount, kTypesB);
|
|
||||||
// Make two buffers, the first uses 'quadsA' for device quads and 'quadsB' for local quads
|
|
||||||
// on even indices. The second uses 'quadsB' for device quads and 'quadsA' for local quads
|
|
||||||
// on odd indices.
|
|
||||||
GrQuadBuffer<TestData> buffer1;
|
|
||||||
GrQuadBuffer<TestData> buffer2;
|
|
||||||
for (int i = 0; i < kQuadCount; ++i) {
|
|
||||||
buffer1.append(quadsA[i], {i, 2.f * i}, i % 2 == 0 ? &quadsB[i] : nullptr);
|
|
||||||
buffer2.append(quadsB[i], {2 * i, 0.5f * i}, i % 2 == 0 ? nullptr : &quadsA[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sanity check
|
|
||||||
ASSERT(kQuadCount == buffer1.count());
|
|
||||||
ASSERT(kQuadCount == buffer2.count());
|
|
||||||
|
|
||||||
// Perform the concatenation and then confirm the new state of buffer1
|
|
||||||
buffer1.concat(buffer2);
|
|
||||||
|
|
||||||
ASSERT(2 * kQuadCount == buffer1.count());
|
|
||||||
int i = 0;
|
|
||||||
auto iter = buffer1.iterator();
|
|
||||||
while(iter.next()) {
|
|
||||||
if (i < kQuadCount) {
|
|
||||||
// First half should match original buffer1
|
|
||||||
assert_quad_eq(r, quadsA[i], iter.deviceQuad());
|
|
||||||
assert_metadata_eq(r, {i, 2.f * i}, iter.metadata());
|
|
||||||
if (i % 2 == 0) {
|
|
||||||
ASSERT(iter.isLocalValid());
|
|
||||||
assert_quad_eq(r, quadsB[i], iter.localQuad());
|
|
||||||
} else {
|
|
||||||
ASSERT(!iter.isLocalValid());
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Second half should match buffer2
|
|
||||||
int j = i - kQuadCount;
|
|
||||||
assert_quad_eq(r, quadsB[j], iter.deviceQuad());
|
|
||||||
assert_metadata_eq(r, {2 * j, 0.5f * j}, iter.metadata());
|
|
||||||
if (j % 2 == 0) {
|
|
||||||
ASSERT(!iter.isLocalValid());
|
|
||||||
} else {
|
|
||||||
ASSERT(iter.isLocalValid());
|
|
||||||
assert_quad_eq(r, quadsA[j], iter.localQuad());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
ASSERTF(i == 2 * kQuadCount, "Expected %d iterations, got: %d",2 * kQuadCount, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Metadata) {
|
|
||||||
static const int kQuadCount = 3;
|
|
||||||
|
|
||||||
// This test doesn't really care about the quad coordinates (except that they aren't modified
|
|
||||||
// when mutating the metadata)
|
|
||||||
GrQuad quad(SkRect::MakeLTRB(1.f, 2.f, 3.f, 4.f));
|
|
||||||
|
|
||||||
GrQuadBuffer<TestData> buffer;
|
|
||||||
for (int i = 0; i < kQuadCount; ++i) {
|
|
||||||
buffer.append(quad, {i, 2.f * i}, i % 2 == 0 ? &quad : nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate once using the metadata iterator, confirm the test data and rewrite
|
|
||||||
int i = 0;
|
|
||||||
auto meta = buffer.metadata();
|
|
||||||
while(meta.next()) {
|
|
||||||
// Confirm initial state
|
|
||||||
assert_metadata_eq(r, {i, 2.f * i}, *meta);
|
|
||||||
// Rewrite
|
|
||||||
*meta = {2 * i, 0.5f * i};
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
ASSERTF(i == kQuadCount, "Expected %d iterations, got: %d", kQuadCount, i);
|
|
||||||
|
|
||||||
// Now that all metadata has been touched, read with regular iterator and confirm updated state
|
|
||||||
// and that no quad coordinates have been changed.
|
|
||||||
i = 0;
|
|
||||||
auto iter = buffer.iterator();
|
|
||||||
while(iter.next()) {
|
|
||||||
// New metadata
|
|
||||||
assert_metadata_eq(r, {2 * i, 0.5f * i}, iter.metadata());
|
|
||||||
|
|
||||||
// Quad coordinates are unchanged
|
|
||||||
assert_quad_eq(r, quad, iter.deviceQuad());
|
|
||||||
if (i % 2 == 0) {
|
|
||||||
ASSERT(iter.isLocalValid());
|
|
||||||
assert_quad_eq(r, quad, iter.localQuad());
|
|
||||||
} else {
|
|
||||||
ASSERT(!iter.isLocalValid());
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
ASSERTF(i == kQuadCount, "Expected %d iterations, got: %d", kQuadCount, i);
|
|
||||||
}
|
|
268
tests/GrQuadListTest.cpp
Normal file
268
tests/GrQuadListTest.cpp
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 Google Inc.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license that can be
|
||||||
|
* found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "tests/Test.h"
|
||||||
|
|
||||||
|
#include "src/gpu/geometry/GrQuadList.h"
|
||||||
|
|
||||||
|
#define ASSERT(cond) REPORTER_ASSERT(r, cond)
|
||||||
|
#define ASSERTF(cond, ...) REPORTER_ASSERT(r, cond, __VA_ARGS__)
|
||||||
|
#define TEST(name) DEF_TEST(GrQuadList##name, r)
|
||||||
|
|
||||||
|
struct TestData {
|
||||||
|
int fItem1;
|
||||||
|
float fItem2;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Simple factories to make placeholder quads used in the tests. The 2D quads
|
||||||
|
// will have the kRect quad type.
|
||||||
|
static GrQuad make_2d_quad() {
|
||||||
|
return GrQuad(SkRect::MakeLTRB(1.f, 2.f, 3.f, 4.f));
|
||||||
|
}
|
||||||
|
static bool is_2d_quad(const GrQuad& quad) {
|
||||||
|
return quad.x(0) == 1.f && quad.x(1) == 1.f && quad.x(2) == 3.f && quad.x(3) == 3.f &&
|
||||||
|
quad.y(0) == 2.f && quad.y(1) == 4.f && quad.y(2) == 2.f && quad.y(3) == 4.f &&
|
||||||
|
quad.w(0) == 1.f && quad.w(1) == 1.f && quad.w(2) == 1.f && quad.w(3) == 1.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GrQuad make_3d_quad() {
|
||||||
|
// This perspective matrix leaves x and y unmodified, and sets w to the persp2 value
|
||||||
|
SkMatrix p = SkMatrix::I();
|
||||||
|
p[SkMatrix::kMPersp2] = 13.f;
|
||||||
|
SkASSERT(p.hasPerspective()); // Sanity check
|
||||||
|
return GrQuad::MakeFromRect(SkRect::MakeLTRB(9.f, 10.f, 11.f, 12.f), p);
|
||||||
|
}
|
||||||
|
static bool is_3d_quad(const GrQuad& quad) {
|
||||||
|
return quad.x(0) == 9.f && quad.x(1) == 9.f && quad.x(2) == 11.f && quad.x(3) == 11.f &&
|
||||||
|
quad.y(0) == 10.f && quad.y(1) == 12.f && quad.y(2) == 10.f && quad.y(3) == 12.f &&
|
||||||
|
quad.w(0) == 13.f && quad.w(1) == 13.f && quad.w(2) == 13.f && quad.w(3) == 13.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Add2D) {
|
||||||
|
GrQuadList list2D;
|
||||||
|
// Add a plain quad and then a 3D persp quad, then read back and make sure
|
||||||
|
// the coordinates make sense (including that the type was lifted to perspective).
|
||||||
|
list2D.push_back(make_2d_quad());
|
||||||
|
|
||||||
|
// Check 2D state of the list
|
||||||
|
ASSERTF(list2D.count() == 1, "Unexpected count: %d", list2D.count());
|
||||||
|
ASSERTF(list2D.quadType() == GrQuad::Type::kAxisAligned, "Unexpected quad type: %d",
|
||||||
|
(uint32_t) list2D.quadType());
|
||||||
|
ASSERTF(is_2d_quad(list2D[0]), "Incorrect quad at i=0");
|
||||||
|
|
||||||
|
// Force the 2D quads to be updated to store ws by adding a perspective quad
|
||||||
|
list2D.push_back(make_3d_quad());
|
||||||
|
ASSERTF(list2D.quadType() == GrQuad::Type::kPerspective,
|
||||||
|
"Expected 2D list to be upgraded to perspective");
|
||||||
|
|
||||||
|
// Re-check full state of list after type upgrade
|
||||||
|
ASSERTF(list2D.count() == 2, "Unexpected count: %d", list2D.count());
|
||||||
|
ASSERTF(is_2d_quad(list2D[0]), "Incorrect quad at i=0 after upgrade");
|
||||||
|
ASSERTF(is_3d_quad(list2D[1]), "Incorrect quad at i=1");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Add3D) {
|
||||||
|
// Now make a list that starts with a 3D persp quad, then has conventional quads added to it
|
||||||
|
// and make sure its state is correct
|
||||||
|
GrQuadList list3D;
|
||||||
|
list3D.push_back(make_3d_quad());
|
||||||
|
list3D.push_back(make_2d_quad());
|
||||||
|
|
||||||
|
ASSERTF(list3D.count() == 2, "Unexpected count: %d", list3D.count());
|
||||||
|
ASSERTF(is_3d_quad(list3D[0]), "Incorrect quad at i=0");
|
||||||
|
ASSERTF(is_2d_quad(list3D[1]), "Incorrect quad at i=2");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(AddWithMetadata2D) {
|
||||||
|
// As above, but also make sure that the metadata is saved and read properly
|
||||||
|
GrTQuadList<TestData> list2D;
|
||||||
|
// Add two plain quads, and then a 3D persp quad, then read back and make sure
|
||||||
|
// the coordinates make sense (including that the type was lifted to perspective).
|
||||||
|
list2D.push_back(make_2d_quad(), {1, 1.f});
|
||||||
|
list2D.push_back(make_2d_quad(), {2, 2.f});
|
||||||
|
|
||||||
|
// Check 2D state of the list
|
||||||
|
ASSERTF(list2D.count() == 2, "Unexpected count: %d", list2D.count());
|
||||||
|
ASSERTF(list2D.quadType() == GrQuad::Type::kAxisAligned, "Unexpected quad type: %d",
|
||||||
|
(uint32_t) list2D.quadType());
|
||||||
|
ASSERTF(is_2d_quad(list2D[0]), "Incorrect quad at i=0");
|
||||||
|
ASSERTF(list2D.metadata(0).fItem1 == 1 && list2D.metadata(0).fItem2 == 1.f,
|
||||||
|
"Incorrect metadata at i=0");
|
||||||
|
ASSERTF(is_2d_quad(list2D[1]), "Incorrect quad at i=1");
|
||||||
|
ASSERTF(list2D.metadata(1).fItem1 == 2 && list2D.metadata(1).fItem2 == 2.f,
|
||||||
|
"Incorrect metadata at i=1");
|
||||||
|
|
||||||
|
// Force the 2D quads to be updated to store ws by adding a perspective quad
|
||||||
|
list2D.push_back(make_3d_quad(), {3, 3.f});
|
||||||
|
ASSERTF(list2D.quadType() == GrQuad::Type::kPerspective,
|
||||||
|
"Expected 2D list to be upgraded to perspective");
|
||||||
|
|
||||||
|
// Re-check full state of list after type upgrade
|
||||||
|
ASSERTF(list2D.count() == 3, "Unexpected count: %d", list2D.count());
|
||||||
|
ASSERTF(is_2d_quad(list2D[0]), "Incorrect quad at i=0 after upgrade");
|
||||||
|
ASSERTF(list2D.metadata(0).fItem1 == 1 && list2D.metadata(0).fItem2 == 1.f,
|
||||||
|
"Incorrect metadata at i=0");
|
||||||
|
ASSERTF(is_2d_quad(list2D[1]), "Incorrect quad at i=1 after upgrade");
|
||||||
|
ASSERTF(list2D.metadata(1).fItem1 == 2 && list2D.metadata(1).fItem2 == 2.f,
|
||||||
|
"Incorrect metadata at i=1");
|
||||||
|
ASSERTF(is_3d_quad(list2D[2]), "Incorrect quad at i=2");
|
||||||
|
ASSERTF(list2D.metadata(2).fItem1 == 3 && list2D.metadata(2).fItem2 == 3.f,
|
||||||
|
"Incorrect metadata at i=2");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(AddWithMetadata3D) {
|
||||||
|
// Now make a list that starts with a 3D persp quad, then has conventional quads added to it
|
||||||
|
// and make sure its state is correct
|
||||||
|
GrTQuadList<TestData> list3D;
|
||||||
|
list3D.push_back(make_3d_quad(), {3, 3.f});
|
||||||
|
list3D.push_back(make_2d_quad(), {2, 2.f});
|
||||||
|
list3D.push_back(make_2d_quad(), {1, 1.f});
|
||||||
|
|
||||||
|
ASSERTF(list3D.count() == 3, "Unexpected count: %d", list3D.count());
|
||||||
|
ASSERTF(is_3d_quad(list3D[0]), "Incorrect quad at i=0");
|
||||||
|
ASSERTF(list3D.metadata(0).fItem1 == 3 && list3D.metadata(0).fItem2 == 3.f,
|
||||||
|
"Incorrect metadata at i=0");
|
||||||
|
ASSERTF(is_2d_quad(list3D[1]), "Incorrect quad at i=1");
|
||||||
|
ASSERTF(list3D.metadata(1).fItem1 == 2 && list3D.metadata(1).fItem2 == 2.f,
|
||||||
|
"Incorrect metadata at i=1");
|
||||||
|
ASSERTF(is_2d_quad(list3D[2]), "Incorrect quad at i=2");
|
||||||
|
ASSERTF(list3D.metadata(2).fItem1 == 1 && list3D.metadata(2).fItem2 == 1.f,
|
||||||
|
"Incorrect metadata at i=2");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Concat2DWith2D) {
|
||||||
|
GrQuadList a2D;
|
||||||
|
a2D.push_back(make_2d_quad());
|
||||||
|
GrQuadList b2D;
|
||||||
|
b2D.push_back(make_2d_quad());
|
||||||
|
|
||||||
|
a2D.concat(b2D);
|
||||||
|
|
||||||
|
ASSERTF(a2D.count() == 2, "Unexpected count: %d", a2D.count());
|
||||||
|
ASSERTF(is_2d_quad(a2D[0]), "Incorrect quad at i=0");
|
||||||
|
ASSERTF(is_2d_quad(a2D[1]), "Incorrect quad at i=1");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Concat2DWith3D) {
|
||||||
|
GrQuadList a2D;
|
||||||
|
a2D.push_back(make_2d_quad());
|
||||||
|
GrQuadList b3D;
|
||||||
|
b3D.push_back(make_3d_quad());
|
||||||
|
|
||||||
|
a2D.concat(b3D);
|
||||||
|
|
||||||
|
ASSERTF(a2D.count() == 2, "Unexpected count: %d", a2D.count());
|
||||||
|
ASSERTF(is_2d_quad(a2D[0]), "Incorrect quad at i=0");
|
||||||
|
ASSERTF(is_3d_quad(a2D[1]), "Incorrect quad at i=1");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Concat3DWith2D) {
|
||||||
|
GrQuadList a3D;
|
||||||
|
a3D.push_back(make_3d_quad());
|
||||||
|
GrQuadList b2D;
|
||||||
|
b2D.push_back(make_2d_quad());
|
||||||
|
|
||||||
|
a3D.concat(b2D);
|
||||||
|
|
||||||
|
ASSERTF(a3D.count() == 2, "Unexpected count: %d", a3D.count());
|
||||||
|
ASSERTF(is_3d_quad(a3D[0]), "Incorrect quad at i=0");
|
||||||
|
ASSERTF(is_2d_quad(a3D[1]), "Incorrect quad at i=1");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Concat3DWith3D) {
|
||||||
|
GrQuadList a3D;
|
||||||
|
a3D.push_back(make_3d_quad());
|
||||||
|
GrQuadList b3D;
|
||||||
|
b3D.push_back(make_3d_quad());
|
||||||
|
|
||||||
|
a3D.concat(b3D);
|
||||||
|
|
||||||
|
ASSERTF(a3D.count() == 2, "Unexpected count: %d", a3D.count());
|
||||||
|
ASSERTF(is_3d_quad(a3D[0]), "Incorrect quad at i=0");
|
||||||
|
ASSERTF(is_3d_quad(a3D[1]), "Incorrect quad at i=1");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Concat2DWith2DMetadata) {
|
||||||
|
GrTQuadList<TestData> a2D;
|
||||||
|
a2D.push_back(make_2d_quad(), {1, 1.f});
|
||||||
|
GrTQuadList<TestData> b2D;
|
||||||
|
b2D.push_back(make_2d_quad(), {2, 2.f});
|
||||||
|
|
||||||
|
a2D.concat(b2D);
|
||||||
|
|
||||||
|
ASSERTF(a2D.count() == 2, "Unexpected count: %d", a2D.count());
|
||||||
|
ASSERTF(is_2d_quad(a2D[0]), "Incorrect quad at i=0");
|
||||||
|
ASSERTF(a2D.metadata(0).fItem1 == 1 && a2D.metadata(0).fItem2 == 1.f,
|
||||||
|
"Incorrect metadata at i=0");
|
||||||
|
ASSERTF(is_2d_quad(a2D[1]), "Incorrect quad at i=1");
|
||||||
|
ASSERTF(a2D.metadata(1).fItem1 == 2 && a2D.metadata(1).fItem2 == 2.f,
|
||||||
|
"Incorrect metadata at i=1");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Concat2DWith3DMetadata) {
|
||||||
|
GrTQuadList<TestData> a2D;
|
||||||
|
a2D.push_back(make_2d_quad(), {1, 1.f});
|
||||||
|
GrTQuadList<TestData> b3D;
|
||||||
|
b3D.push_back(make_3d_quad(), {2, 2.f});
|
||||||
|
|
||||||
|
a2D.concat(b3D);
|
||||||
|
|
||||||
|
ASSERTF(a2D.count() == 2, "Unexpected count: %d", a2D.count());
|
||||||
|
ASSERTF(is_2d_quad(a2D[0]), "Incorrect quad at i=0");
|
||||||
|
ASSERTF(a2D.metadata(0).fItem1 == 1 && a2D.metadata(0).fItem2 == 1.f,
|
||||||
|
"Incorrect metadata at i=0");
|
||||||
|
ASSERTF(is_3d_quad(a2D[1]), "Incorrect quad at i=1");
|
||||||
|
ASSERTF(a2D.metadata(1).fItem1 == 2 && a2D.metadata(1).fItem2 == 2.f,
|
||||||
|
"Incorrect metadata at i=1");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Concat3DWith2DMetadata) {
|
||||||
|
GrTQuadList<TestData> a3D;
|
||||||
|
a3D.push_back(make_3d_quad(), {1, 1.f});
|
||||||
|
GrTQuadList<TestData> b2D;
|
||||||
|
b2D.push_back(make_2d_quad(), {2, 2.f});
|
||||||
|
|
||||||
|
a3D.concat(b2D);
|
||||||
|
|
||||||
|
ASSERTF(a3D.count() == 2, "Unexpected count: %d", a3D.count());
|
||||||
|
ASSERTF(is_3d_quad(a3D[0]), "Incorrect quad at i=0");
|
||||||
|
ASSERTF(a3D.metadata(0).fItem1 == 1 && a3D.metadata(0).fItem2 == 1.f,
|
||||||
|
"Incorrect metadata at i=0");
|
||||||
|
ASSERTF(is_2d_quad(a3D[1]), "Incorrect quad at i=1");
|
||||||
|
ASSERTF(a3D.metadata(1).fItem1 == 2 && a3D.metadata(1).fItem2 == 2.f,
|
||||||
|
"Incorrect metadata at i=1");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Concat3DWith3DMetadata) {
|
||||||
|
GrTQuadList<TestData> a3D;
|
||||||
|
a3D.push_back(make_3d_quad(), {1, 1.f});
|
||||||
|
GrTQuadList<TestData> b3D;
|
||||||
|
b3D.push_back(make_3d_quad(), {2, 2.f});
|
||||||
|
|
||||||
|
a3D.concat(b3D);
|
||||||
|
|
||||||
|
ASSERTF(a3D.count() == 2, "Unexpected count: %d", a3D.count());
|
||||||
|
ASSERTF(is_3d_quad(a3D[0]), "Incorrect quad at i=0");
|
||||||
|
ASSERTF(a3D.metadata(0).fItem1 == 1 && a3D.metadata(0).fItem2 == 1.f,
|
||||||
|
"Incorrect metadata at i=0");
|
||||||
|
ASSERTF(is_3d_quad(a3D[1]), "Incorrect quad at i=1");
|
||||||
|
ASSERTF(a3D.metadata(1).fItem1 == 2 && a3D.metadata(1).fItem2 == 2.f,
|
||||||
|
"Incorrect metadata at i=1");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(WriteMetadata) {
|
||||||
|
GrTQuadList<TestData> list;
|
||||||
|
list.push_back(make_2d_quad(), {1, 1.f});
|
||||||
|
ASSERTF(list.metadata(0).fItem1 == 1 && list.metadata(0).fItem2 == 1.f,
|
||||||
|
"Incorrect metadata at i=0"); // Sanity check
|
||||||
|
|
||||||
|
// Rewrite metadata within the list and read back
|
||||||
|
list.metadata(0).fItem1 = 2;
|
||||||
|
list.metadata(0).fItem2 = 2.f;
|
||||||
|
ASSERTF(list.metadata(0).fItem1 == 2 && list.metadata(0).fItem2 == 2.f,
|
||||||
|
"Incorrect metadata at i=0 after edit");
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user