skia2/tests/GrQuadBufferTest.cpp
Michael Ludwig 704d5408db 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>
2019-11-26 14:57:44 +00:00

239 lines
9.0 KiB
C++

/*
* 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());
ASSERT(!iter.localQuad());
}
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());
ASSERT(!iter.localQuad());
}
} 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());
ASSERT(!iter.localQuad());
} 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());
ASSERT(!iter.localQuad());
}
i++;
}
ASSERTF(i == kQuadCount, "Expected %d iterations, got: %d", kQuadCount, i);
}