Allow Tessellator to operate on provided GrQuads

To facilitate this, the GrQuadBuffer::Iter's local GrQuads that are
modified on each next() are now allowed to be operated on for the AA
inset/outsetting. Previously this required additional GrQuads on the
stack to hold the results, and additional guards for accessing localQuad()
when the entry didn't have actual coords.

With this change, a 2D op should have its device and src GrQuads' Ws
set to 1 once, and then they are completely ignored for all iteration
and tessellation, without any more redundant initialization. In all
likelihood we won't see the needle move on powerful platforms, but may
help lower end devices.

Change-Id: I457205786766403a760918e779d36ba056d69cde
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/256097
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
This commit is contained in:
Michael Ludwig 2019-11-25 09:43:37 -05:00 committed by Skia Commit-Bot
parent 849f4d6571
commit 704d5408db
7 changed files with 88 additions and 69 deletions

View File

@ -422,7 +422,7 @@ private:
float vertices[56]; // 2 quads, with x, y, coverage, and geometry domain (7 floats x 8 vert)
GrQuadPerEdgeAA::Tessellator tessellator(kSpec, (char*) vertices);
tessellator.append(quad, GrQuad(SkRect::MakeEmpty()), {1.f, 1.f, 1.f, 1.f},
tessellator.append(&quad, nullptr, {1.f, 1.f, 1.f, 1.f},
SkRect::MakeEmpty(), flags);
// The first quad in vertices is the inset, then the outset, but they

View File

@ -71,14 +71,16 @@ public:
const T& metadata() const { this->validate(); return *(fBuffer->metadata(fCurrentEntry)); }
const GrQuad& deviceQuad() const { this->validate(); return fDeviceQuad; }
// The returned pointer is mutable so that the object can be used for scratch calculations
// during op preparation. However, any changes are not persisted in the GrQuadBuffer and
// subsequent calls to next() will overwrite the state of the GrQuad.
GrQuad* deviceQuad() { 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 {
// If isLocalValid() returns false, this returns nullptr. Otherwise, the returned pointer
// is mutable in the same manner as deviceQuad().
GrQuad* localQuad() {
this->validate();
return fLocalQuad;
return this->isLocalValid() ? &fLocalQuad : nullptr;
}
bool isLocalValid() const {
@ -341,10 +343,8 @@ bool GrQuadBuffer<T>::Iter::next() {
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;
}
} // else localQuad() will return a nullptr so no need to reset fLocalQuad
// 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));

View File

@ -29,9 +29,10 @@ using VertexSpec = GrQuadPerEdgeAA::VertexSpec;
using ColorType = GrQuadPerEdgeAA::ColorType;
#ifdef SK_DEBUG
static SkString dump_quad_info(int index, const GrQuad& deviceQuad,
const GrQuad& localQuad, const SkPMColor4f& color,
static SkString dump_quad_info(int index, const GrQuad* deviceQuad,
const GrQuad* localQuad, const SkPMColor4f& color,
GrQuadAAFlags aaFlags) {
GrQuad safeLocal = localQuad ? *localQuad : GrQuad();
SkString str;
str.appendf("%d: Color: [%.2f, %.2f, %.2f, %.2f], Edge AA: l%u_t%u_r%u_b%u, \n"
" device quad: [(%.2f, %2.f, %.2f), (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f), "
@ -43,14 +44,14 @@ static SkString dump_quad_info(int index, const GrQuad& deviceQuad,
(uint32_t) (aaFlags & GrQuadAAFlags::kTop),
(uint32_t) (aaFlags & GrQuadAAFlags::kRight),
(uint32_t) (aaFlags & GrQuadAAFlags::kBottom),
deviceQuad.x(0), deviceQuad.y(0), deviceQuad.w(0),
deviceQuad.x(1), deviceQuad.y(1), deviceQuad.w(1),
deviceQuad.x(2), deviceQuad.y(2), deviceQuad.w(2),
deviceQuad.x(3), deviceQuad.y(3), deviceQuad.w(3),
localQuad.x(0), localQuad.y(0), localQuad.w(0),
localQuad.x(1), localQuad.y(1), localQuad.w(1),
localQuad.x(2), localQuad.y(2), localQuad.w(2),
localQuad.x(3), localQuad.y(3), localQuad.w(3));
deviceQuad->x(0), deviceQuad->y(0), deviceQuad->w(0),
deviceQuad->x(1), deviceQuad->y(1), deviceQuad->w(1),
deviceQuad->x(2), deviceQuad->y(2), deviceQuad->w(2),
deviceQuad->x(3), deviceQuad->y(3), deviceQuad->w(3),
safeLocal.x(0), safeLocal.y(0), safeLocal.w(0),
safeLocal.x(1), safeLocal.y(1), safeLocal.w(1),
safeLocal.x(2), safeLocal.y(2), safeLocal.w(2),
safeLocal.x(3), safeLocal.y(3), safeLocal.w(3));
return str;
}
#endif

View File

@ -23,16 +23,19 @@ namespace {
// Generic WriteQuadProc that can handle any VertexSpec. It writes the 4 vertices in triangle strip
// order, although the data per-vertex is dependent on the VertexSpec.
static void write_quad_generic(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
const GrQuad& deviceQuad, const GrQuad& localQuad,
const GrQuad* deviceQuad, const GrQuad* localQuad,
const float coverage[4], const SkPMColor4f& color,
const SkRect& geomDomain, const SkRect& texDomain) {
static constexpr auto If = GrVertexWriter::If<float>;
SkASSERT(!spec.hasLocalCoords() || localQuad);
GrQuadPerEdgeAA::CoverageMode mode = spec.coverageMode();
for (int i = 0; i < 4; ++i) {
// save position, this is a float2 or float3 or float4 depending on the combination of
// perspective and coverage mode.
vb->write(deviceQuad.x(i), deviceQuad.y(i),
If(spec.deviceQuadType() == GrQuad::Type::kPerspective, deviceQuad.w(i)),
vb->write(deviceQuad->x(i), deviceQuad->y(i),
If(spec.deviceQuadType() == GrQuad::Type::kPerspective, deviceQuad->w(i)),
If(mode == GrQuadPerEdgeAA::CoverageMode::kWithPosition, coverage[i]));
// save color
@ -45,8 +48,8 @@ static void write_quad_generic(GrVertexWriter* vb, const GrQuadPerEdgeAA::Vertex
// save local position
if (spec.hasLocalCoords()) {
vb->write(localQuad.x(i), localQuad.y(i),
If(spec.localQuadType() == GrQuad::Type::kPerspective, localQuad.w(i)));
vb->write(localQuad->x(i), localQuad->y(i),
If(spec.localQuadType() == GrQuad::Type::kPerspective, localQuad->w(i)));
}
// save the geometry domain
@ -67,7 +70,7 @@ static void write_quad_generic(GrVertexWriter* vb, const GrQuadPerEdgeAA::Vertex
// 2D (XY), no explicit coverage, vertex color, no locals, no geometry domain, no texture domain
// This represents simple, solid color or shader, non-AA (or AA with cov. as alpha) rects.
static void write_2d_color(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
const GrQuad& deviceQuad, const GrQuad& localQuad,
const GrQuad* deviceQuad, const GrQuad* localQuad,
const float coverage[4], const SkPMColor4f& color,
const SkRect& geomDomain, const SkRect& texDomain) {
// Assert assumptions about VertexSpec
@ -78,20 +81,23 @@ static void write_2d_color(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec
SkASSERT(spec.hasVertexColors());
SkASSERT(!spec.requiresGeometryDomain());
SkASSERT(!spec.hasDomain());
// We don't assert that localQuad == nullptr, since it is possible for GrFillRectOp to
// accumulate local coords conservatively (paint not trivial), and then after analysis realize
// the processors don't need local coordinates.
bool wide = spec.colorType() == GrQuadPerEdgeAA::ColorType::kHalf;
for (int i = 0; i < 4; ++i) {
// If this is not coverage-with-alpha, make sure coverage == 1 so it doesn't do anything
SkASSERT(spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kWithColor ||
coverage[i] == 1.f);
vb->write(deviceQuad.x(i), deviceQuad.y(i), GrVertexColor(color * coverage[i], wide));
vb->write(deviceQuad->x(i), deviceQuad->y(i), GrVertexColor(color * coverage[i], wide));
}
}
// 2D (XY), no explicit coverage, UV locals, no color, no geometry domain, no texture domain
// This represents opaque, non AA, textured rects
static void write_2d_uv(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
const GrQuad& deviceQuad, const GrQuad& localQuad,
const GrQuad* deviceQuad, const GrQuad* localQuad,
const float coverage[4], const SkPMColor4f& color,
const SkRect& geomDomain, const SkRect& texDomain) {
// Assert assumptions about VertexSpec
@ -101,16 +107,17 @@ static void write_2d_uv(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& s
SkASSERT(!spec.hasVertexColors());
SkASSERT(!spec.requiresGeometryDomain());
SkASSERT(!spec.hasDomain());
SkASSERT(localQuad);
for (int i = 0; i < 4; ++i) {
vb->write(deviceQuad.x(i), deviceQuad.y(i), localQuad.x(i), localQuad.y(i));
vb->write(deviceQuad->x(i), deviceQuad->y(i), localQuad->x(i), localQuad->y(i));
}
}
// 2D (XY), no explicit coverage, UV locals, vertex color, no geometry or texture domains
// This represents transparent, non AA (or AA with cov. as alpha), textured rects
static void write_2d_color_uv(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
const GrQuad& deviceQuad, const GrQuad& localQuad,
const GrQuad* deviceQuad, const GrQuad* localQuad,
const float coverage[4], const SkPMColor4f& color,
const SkRect& geomDomain, const SkRect& texDomain) {
// Assert assumptions about VertexSpec
@ -121,21 +128,22 @@ static void write_2d_color_uv(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexS
SkASSERT(spec.hasVertexColors());
SkASSERT(!spec.requiresGeometryDomain());
SkASSERT(!spec.hasDomain());
SkASSERT(localQuad);
bool wide = spec.colorType() == GrQuadPerEdgeAA::ColorType::kHalf;
for (int i = 0; i < 4; ++i) {
// If this is not coverage-with-alpha, make sure coverage == 1 so it doesn't do anything
SkASSERT(spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kWithColor ||
coverage[i] == 1.f);
vb->write(deviceQuad.x(i), deviceQuad.y(i), GrVertexColor(color * coverage[i], wide),
localQuad.x(i), localQuad.y(i));
vb->write(deviceQuad->x(i), deviceQuad->y(i), GrVertexColor(color * coverage[i], wide),
localQuad->x(i), localQuad->y(i));
}
}
// 2D (XY), explicit coverage, UV locals, no color, no geometry domain, no texture domain
// This represents opaque, AA, textured rects
static void write_2d_cov_uv(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
const GrQuad& deviceQuad, const GrQuad& localQuad,
const GrQuad* deviceQuad, const GrQuad* localQuad,
const float coverage[4], const SkPMColor4f& color,
const SkRect& geomDomain, const SkRect& texDomain) {
// Assert assumptions about VertexSpec
@ -145,9 +153,11 @@ static void write_2d_cov_uv(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpe
SkASSERT(!spec.hasVertexColors());
SkASSERT(!spec.requiresGeometryDomain());
SkASSERT(!spec.hasDomain());
SkASSERT(localQuad);
for (int i = 0; i < 4; ++i) {
vb->write(deviceQuad.x(i), deviceQuad.y(i), coverage[i], localQuad.x(i), localQuad.y(i));
vb->write(deviceQuad->x(i), deviceQuad->y(i), coverage[i],
localQuad->x(i), localQuad->y(i));
}
}
@ -160,7 +170,7 @@ static void write_2d_cov_uv(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpe
// 2D (XY), no explicit coverage, UV locals, no color, tex domain but no geometry domain
// This represents opaque, non AA, textured rects with strict uv sampling
static void write_2d_uv_strict(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
const GrQuad& deviceQuad, const GrQuad& localQuad,
const GrQuad* deviceQuad, const GrQuad* localQuad,
const float coverage[4], const SkPMColor4f& color,
const SkRect& geomDomain, const SkRect& texDomain) {
// Assert assumptions about VertexSpec
@ -170,16 +180,17 @@ static void write_2d_uv_strict(GrVertexWriter* vb, const GrQuadPerEdgeAA::Vertex
SkASSERT(!spec.hasVertexColors());
SkASSERT(!spec.requiresGeometryDomain());
SkASSERT(spec.hasDomain());
SkASSERT(localQuad);
for (int i = 0; i < 4; ++i) {
vb->write(deviceQuad.x(i), deviceQuad.y(i), localQuad.x(i), localQuad.y(i), texDomain);
vb->write(deviceQuad->x(i), deviceQuad->y(i), localQuad->x(i), localQuad->y(i), texDomain);
}
}
// 2D (XY), no explicit coverage, UV locals, vertex color, tex domain but no geometry domain
// This represents transparent, non AA (or AA with cov. as alpha), textured rects with strict sample
static void write_2d_color_uv_strict(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
const GrQuad& deviceQuad, const GrQuad& localQuad,
const GrQuad* deviceQuad, const GrQuad* localQuad,
const float coverage[4], const SkPMColor4f& color,
const SkRect& geomDomain, const SkRect& texDomain) {
// Assert assumptions about VertexSpec
@ -190,21 +201,22 @@ static void write_2d_color_uv_strict(GrVertexWriter* vb, const GrQuadPerEdgeAA::
SkASSERT(spec.hasVertexColors());
SkASSERT(!spec.requiresGeometryDomain());
SkASSERT(spec.hasDomain());
SkASSERT(localQuad);
bool wide = spec.colorType() == GrQuadPerEdgeAA::ColorType::kHalf;
for (int i = 0; i < 4; ++i) {
// If this is not coverage-with-alpha, make sure coverage == 1 so it doesn't do anything
SkASSERT(spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kWithColor ||
coverage[i] == 1.f);
vb->write(deviceQuad.x(i), deviceQuad.y(i), GrVertexColor(color * coverage[i], wide),
localQuad.x(i), localQuad.y(i), texDomain);
vb->write(deviceQuad->x(i), deviceQuad->y(i), GrVertexColor(color * coverage[i], wide),
localQuad->x(i), localQuad->y(i), texDomain);
}
}
// 2D (XY), explicit coverage, UV locals, no color, tex domain but no geometry domain
// This represents opaque, AA, textured rects with strict uv sampling
static void write_2d_cov_uv_strict(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
const GrQuad& deviceQuad, const GrQuad& localQuad,
const GrQuad* deviceQuad, const GrQuad* localQuad,
const float coverage[4], const SkPMColor4f& color,
const SkRect& geomDomain, const SkRect& texDomain) {
// Assert assumptions about VertexSpec
@ -214,10 +226,11 @@ static void write_2d_cov_uv_strict(GrVertexWriter* vb, const GrQuadPerEdgeAA::Ve
SkASSERT(!spec.hasVertexColors());
SkASSERT(!spec.requiresGeometryDomain());
SkASSERT(spec.hasDomain());
SkASSERT(localQuad);
for (int i = 0; i < 4; ++i) {
vb->write(deviceQuad.x(i), deviceQuad.y(i), coverage[i],
localQuad.x(i), localQuad.y(i), texDomain);
vb->write(deviceQuad->x(i), deviceQuad->y(i), coverage[i],
localQuad->x(i), localQuad->y(i), texDomain);
}
}
@ -289,13 +302,14 @@ Tessellator::Tessellator(const VertexSpec& spec, char* vertices)
, fVertexWriter{vertices}
, fWriteProc(Tessellator::GetWriteQuadProc(spec)) {}
void Tessellator::append(const GrQuad& deviceQuad, const GrQuad& localQuad,
void Tessellator::append(GrQuad* deviceQuad, GrQuad* localQuad,
const SkPMColor4f& color, const SkRect& uvDomain, GrQuadAAFlags aaFlags) {
// We allow Tessellator to be created with a null vertices pointer for convenience, but it is
// assumed it will never actually be used in those cases.
SkASSERT(fVertexWriter.fPtr);
SkASSERT(deviceQuad.quadType() <= fVertexSpec.deviceQuadType());
SkASSERT(!fVertexSpec.hasLocalCoords() || localQuad.quadType() <= fVertexSpec.localQuadType());
SkASSERT(deviceQuad->quadType() <= fVertexSpec.deviceQuadType());
SkASSERT(localQuad || !fVertexSpec.hasLocalCoords());
SkASSERT(!fVertexSpec.hasLocalCoords() || localQuad->quadType() <= fVertexSpec.localQuadType());
static const float kFullCoverage[4] = {1.f, 1.f, 1.f, 1.f};
static const float kZeroCoverage[4] = {0.f, 0.f, 0.f, 0.f};
@ -308,7 +322,7 @@ void Tessellator::append(const GrQuad& deviceQuad, const GrQuad& localQuad,
// a geometry domain if corners are not right angles
SkRect geomDomain;
if (fVertexSpec.requiresGeometryDomain()) {
geomDomain = deviceQuad.bounds();
geomDomain = deviceQuad->bounds();
geomDomain.outset(0.5f, 0.5f); // account for AA expansion
}
@ -323,7 +337,7 @@ void Tessellator::append(const GrQuad& deviceQuad, const GrQuad& localQuad,
geomDomain, uvDomain);
} else {
// Reset the tessellation helper to match the current geometry
fAAHelper.reset(deviceQuad, fVertexSpec.hasLocalCoords() ? &localQuad : nullptr);
fAAHelper.reset(*deviceQuad, localQuad);
// Edge inset/outset distance ordered LBTR, set to 0.5 for a half pixel if the AA flag
// is turned on, or 0.0 if the edge is not anti-aliased.
@ -337,17 +351,15 @@ void Tessellator::append(const GrQuad& deviceQuad, const GrQuad& localQuad,
(aaFlags & GrQuadAAFlags::kRight) ? 0.5f : 0.f };
}
GrQuad aaDeviceQuad, aaLocalQuad;
// Write inner vertices first
float coverage[4];
fAAHelper.inset(edgeDistances, &aaDeviceQuad, &aaLocalQuad).store(coverage);
fWriteProc(&fVertexWriter, fVertexSpec, aaDeviceQuad, aaLocalQuad, coverage, color,
fAAHelper.inset(edgeDistances, deviceQuad, localQuad).store(coverage);
fWriteProc(&fVertexWriter, fVertexSpec, deviceQuad, localQuad, coverage, color,
geomDomain, uvDomain);
// Then outer vertices, which use 0.f for their coverage
fAAHelper.outset(edgeDistances, &aaDeviceQuad, &aaLocalQuad);
fWriteProc(&fVertexWriter, fVertexSpec, aaDeviceQuad, aaLocalQuad, kZeroCoverage, color,
fAAHelper.outset(edgeDistances, deviceQuad, localQuad);
fWriteProc(&fVertexWriter, fVertexSpec, deviceQuad, localQuad, kZeroCoverage, color,
geomDomain, uvDomain);
}
} else {

View File

@ -139,8 +139,10 @@ namespace GrQuadPerEdgeAA {
// Calculates (as needed) inset and outset geometry for anti-aliasing, and appends all
// necessary position and vertex attributes required by this Tessellator's VertexSpec into
// the 'vertices' the Tessellator was called with.
void append(const GrQuad& deviceQuad, const GrQuad& localQuad,
// the 'vertices' the Tessellator was called with. The insetting and outsetting may
// damage the provided GrQuads (as this is intended to work with GrQuadBuffer::Iter).
// 'localQuad' can be null if the VertexSpec does not use local coords.
void append(GrQuad* deviceQuad, GrQuad* localQuad,
const SkPMColor4f& color, const SkRect& uvDomain, GrQuadAAFlags aaFlags);
SkDEBUGCODE(char* vertices() const { return (char*) fVertexWriter.fPtr; })
@ -151,7 +153,7 @@ namespace GrQuadPerEdgeAA {
// specs that appear in the wild far more frequently, so they use explicit WriteQuadProcs
// that have no branches.
typedef void (*WriteQuadProc)(GrVertexWriter* vertices, const VertexSpec& spec,
const GrQuad& deviceQuad, const GrQuad& localQuad,
const GrQuad* deviceQuad, const GrQuad* localQuad,
const float coverage[4], const SkPMColor4f& color,
const SkRect& geomDomain, const SkRect& texDomain);
static WriteQuadProc GetWriteQuadProc(const VertexSpec& spec);

View File

@ -238,8 +238,8 @@ public:
static_cast<int>(fFilter));
int i = 0;
while(i < fViewCountPairs[p].fQuadCnt && iter.next()) {
const GrQuad& quad = iter.deviceQuad();
const GrQuad& uv = iter.localQuad();
const GrQuad* quad = iter.deviceQuad();
GrQuad uv = iter.isLocalValid() ? *(iter.localQuad()) : GrQuad();
const ColorDomainAndAA& info = iter.metadata();
str.appendf(
"%d: Color: 0x%08x, Domain(%d): [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n"
@ -247,8 +247,8 @@ public:
" Quad [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n",
i, info.fColor.toBytes_RGBA(), fDomain, info.fDomainRect.fLeft,
info.fDomainRect.fTop, info.fDomainRect.fRight, info.fDomainRect.fBottom,
quad.point(0).fX, 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,
quad->point(0).fX, 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,
uv.point(0).fX, uv.point(0).fY, uv.point(1).fX, uv.point(1).fY,
uv.point(2).fX, uv.point(2).fY, uv.point(3).fX, uv.point(3).fY);

View File

@ -112,16 +112,17 @@ TEST(Append) {
auto iter = buffer.iterator();
while(iter.next()) {
// Each entry always has the device quad
assert_quad_eq(r, expectedDeviceQuads[i], iter.deviceQuad());
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());
assert_quad_eq(r, expectedLocalQuads[i], *iter.localQuad());
} else {
// Should not have locals
ASSERT(!iter.isLocalValid());
ASSERT(!iter.localQuad());
}
i++;
@ -161,25 +162,27 @@ TEST(Concat) {
while(iter.next()) {
if (i < kQuadCount) {
// First half should match original buffer1
assert_quad_eq(r, quadsA[i], iter.deviceQuad());
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());
assert_quad_eq(r, quadsB[i], *iter.localQuad());
} else {
ASSERT(!iter.isLocalValid());
ASSERT(!iter.localQuad());
}
} else {
// Second half should match buffer2
int j = i - kQuadCount;
assert_quad_eq(r, quadsB[j], iter.deviceQuad());
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());
ASSERT(!iter.localQuad());
} else {
ASSERT(iter.isLocalValid());
assert_quad_eq(r, quadsA[j], iter.localQuad());
assert_quad_eq(r, quadsA[j], *iter.localQuad());
}
}
@ -221,12 +224,13 @@ TEST(Metadata) {
assert_metadata_eq(r, {2 * i, 0.5f * i}, iter.metadata());
// Quad coordinates are unchanged
assert_quad_eq(r, quad, iter.deviceQuad());
assert_quad_eq(r, quad, *iter.deviceQuad());
if (i % 2 == 0) {
ASSERT(iter.isLocalValid());
assert_quad_eq(r, quad, iter.localQuad());
assert_quad_eq(r, quad, *iter.localQuad());
} else {
ASSERT(!iter.isLocalValid());
ASSERT(!iter.localQuad());
}
i++;
}