1/*
2 * Copyright 2019 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "tests/Test.h"
9
10#include "src/gpu/geometry/GrQuadBuffer.h"
11
12#include <vector>
13
14#define ASSERT(cond) REPORTER_ASSERT(r, cond)
15#define ASSERTF(cond, ...) REPORTER_ASSERT(r, cond, __VA_ARGS__)
16#define TEST(name) DEF_TEST(GrQuadBuffer##name, r)
17
18struct TestData {
19    int fItem1;
20    float fItem2;
21};
22
23static void assert_quad_eq(skiatest::Reporter* r, const GrQuad& expected, const GrQuad& actual) {
24    ASSERTF(expected.quadType() == actual.quadType(), "Expected type %d, got %d",
25            (int) expected.quadType(), (int) actual.quadType());
26    for (int i = 0; i < 4; ++i) {
27        ASSERTF(expected.x(i) == actual.x(i), "Expected x(%d) = %f, got %f",
28                i, expected.x(i), actual.x(i));
29        ASSERTF(expected.y(i) == actual.y(i), "Expected y(%d) = %f, got %f",
30                i, expected.y(i), actual.y(i));
31        ASSERTF(expected.w(i) == actual.w(i), "Expected w(%d) = %f, got %f",
32                i, expected.w(i), actual.w(i));
33    }
34}
35
36static void assert_metadata_eq(skiatest::Reporter* r, const TestData& expected,
37                               const TestData& actual) {
38    ASSERTF(expected.fItem1 == actual.fItem1 && expected.fItem2 == actual.fItem2,
39            "Expected { %d, %f } for metadata, got: { %d %f }",
40            expected.fItem1, expected.fItem2, actual.fItem1, actual.fItem2);
41}
42
43static std::vector<GrQuad> generate_quads(float seed, int cnt, const GrQuad::Type types[]) {
44    // For convenience use matrix to derive each quad type, rely on different seed values to
45    // differentiate between quads of the same type
46    SkMatrix rotate;
47    rotate.setRotate(45.f);
48    SkMatrix skew;
49    skew.setSkew(0.5f, 0.5f);
50    SkMatrix perspective;
51    perspective.setPerspX(0.01f);
52    perspective.setPerspY(0.001f);
53
54    std::vector<GrQuad> quads;
55    SkRect rect = SkRect::MakeXYWH(seed, 2.f * seed, 2.f * seed, seed);
56    for (int i = 0; i < cnt; ++i) {
57        GrQuad quad;
58        switch(types[i]) {
59            case GrQuad::Type::kAxisAligned:
60                quad = GrQuad(rect);
61                break;
62            case GrQuad::Type::kRectilinear:
63                quad = GrQuad::MakeFromRect(rect, rotate);
64                break;
65            case GrQuad::Type::kGeneral:
66                quad = GrQuad::MakeFromRect(rect, skew);
67                break;
68            default:
69                SkASSERT(types[i] == GrQuad::Type::kPerspective);
70                quad = GrQuad::MakeFromRect(rect, perspective);
71                break;
72        }
73
74        SkASSERT(quad.quadType() == types[i]);
75        quads.push_back(quad);
76    }
77    return quads;
78}
79
80TEST(Append) {
81    // Generate test data, which includes all quad types out of enum-order and duplicates
82    static const int kQuadCount = 6;
83    static const GrQuad::Type kDeviceTypes[] = {
84        GrQuad::Type::kAxisAligned, GrQuad::Type::kRectilinear, GrQuad::Type::kGeneral,
85        GrQuad::Type::kPerspective, GrQuad::Type::kRectilinear, GrQuad::Type::kAxisAligned
86    };
87    // Odd indexed quads will be ignored and not stored in the buffer
88    static const GrQuad::Type kLocalTypes[] = {
89        GrQuad::Type::kGeneral, GrQuad::Type::kGeneral, GrQuad::Type::kRectilinear,
90        GrQuad::Type::kRectilinear, GrQuad::Type::kAxisAligned, GrQuad::Type::kAxisAligned
91    };
92    static_assert(SK_ARRAY_COUNT(kDeviceTypes) == kQuadCount, "device quad count");
93    static_assert(SK_ARRAY_COUNT(kLocalTypes) == kQuadCount, "local quad count");
94
95    std::vector<GrQuad> expectedDeviceQuads = generate_quads(1.f, kQuadCount, kDeviceTypes);
96    std::vector<GrQuad> expectedLocalQuads = generate_quads(2.f, kQuadCount, kLocalTypes);
97
98    // Fill in the buffer with the device quads, and a local quad if the index is even
99    GrQuadBuffer<TestData> buffer;
100    for (int i = 0; i < kQuadCount; ++i) {
101        buffer.append(expectedDeviceQuads[i],                          // device quad
102                      { 2 * i, 3.f * i },                              // metadata
103                      i % 2 == 0 ? &expectedLocalQuads[i] : nullptr);  // optional local quad
104    }
105
106    // Confirm the state of the buffer
107    ASSERT(kQuadCount == buffer.count());
108    ASSERT(GrQuad::Type::kPerspective == buffer.deviceQuadType());
109    ASSERT(GrQuad::Type::kGeneral == buffer.localQuadType());
110
111    int i = 0;
112    auto iter = buffer.iterator();
113    while(iter.next()) {
114        // Each entry always has the device quad
115        assert_quad_eq(r, expectedDeviceQuads[i], *iter.deviceQuad());
116        assert_metadata_eq(r, {2 * i, 3.f * i}, iter.metadata());
117
118        if (i % 2 == 0) {
119            // Confirm local quads included on even entries
120            ASSERT(iter.isLocalValid());
121            assert_quad_eq(r, expectedLocalQuads[i], *iter.localQuad());
122        } else {
123            // Should not have locals
124            ASSERT(!iter.isLocalValid());
125            ASSERT(!iter.localQuad());
126        }
127
128        i++;
129    }
130    ASSERTF(i == kQuadCount, "Expected %d iterations, got: %d", kQuadCount, i);
131}
132
133TEST(Concat) {
134    static const int kQuadCount = 2;
135    static const GrQuad::Type kTypesA[] = { GrQuad::Type::kAxisAligned, GrQuad::Type::kRectilinear };
136    static const GrQuad::Type kTypesB[] = { GrQuad::Type::kGeneral, GrQuad::Type::kPerspective };
137    static_assert(SK_ARRAY_COUNT(kTypesA) == kQuadCount, "quadsA count");
138    static_assert(SK_ARRAY_COUNT(kTypesB) == kQuadCount, "quadsB count");
139
140    std::vector<GrQuad> quadsA = generate_quads(1.f, kQuadCount, kTypesA);
141    std::vector<GrQuad> quadsB = generate_quads(2.f, kQuadCount, kTypesB);
142    // Make two buffers, the first uses 'quadsA' for device quads and 'quadsB' for local quads
143    // on even indices. The second uses 'quadsB' for device quads and 'quadsA' for local quads
144    // on odd indices.
145    GrQuadBuffer<TestData> buffer1;
146    GrQuadBuffer<TestData> buffer2;
147    for (int i = 0; i < kQuadCount; ++i) {
148        buffer1.append(quadsA[i], {i, 2.f * i}, i % 2 == 0 ? &quadsB[i] : nullptr);
149        buffer2.append(quadsB[i], {2 * i, 0.5f * i}, i % 2 == 0 ? nullptr : &quadsA[i]);
150    }
151
152    ASSERT(kQuadCount == buffer1.count());
153    ASSERT(kQuadCount == buffer2.count());
154
155    // Perform the concatenation and then confirm the new state of buffer1
156    buffer1.concat(buffer2);
157
158    ASSERT(2 * kQuadCount == buffer1.count());
159    int i = 0;
160    auto iter = buffer1.iterator();
161    while(iter.next()) {
162        if (i < kQuadCount) {
163            // First half should match original buffer1
164            assert_quad_eq(r, quadsA[i], *iter.deviceQuad());
165            assert_metadata_eq(r, {i, 2.f * i}, iter.metadata());
166            if (i % 2 == 0) {
167                ASSERT(iter.isLocalValid());
168                assert_quad_eq(r, quadsB[i], *iter.localQuad());
169            } else {
170                ASSERT(!iter.isLocalValid());
171                ASSERT(!iter.localQuad());
172            }
173
174        } else {
175            // Second half should match buffer2
176            int j = i - kQuadCount;
177            assert_quad_eq(r, quadsB[j], *iter.deviceQuad());
178            assert_metadata_eq(r, {2 * j, 0.5f * j}, iter.metadata());
179            if (j % 2 == 0) {
180                ASSERT(!iter.isLocalValid());
181                ASSERT(!iter.localQuad());
182            } else {
183                ASSERT(iter.isLocalValid());
184                assert_quad_eq(r, quadsA[j], *iter.localQuad());
185            }
186        }
187
188        i++;
189    }
190    ASSERTF(i == 2 * kQuadCount, "Expected %d iterations, got: %d",2 * kQuadCount, i);
191}
192
193TEST(Metadata) {
194    static const int kQuadCount = 3;
195
196    // This test doesn't really care about the quad coordinates (except that they aren't modified
197    // when mutating the metadata)
198    GrQuad quad(SkRect::MakeLTRB(1.f, 2.f, 3.f, 4.f));
199
200    GrQuadBuffer<TestData> buffer;
201    for (int i = 0; i < kQuadCount; ++i) {
202        buffer.append(quad, {i, 2.f * i}, i % 2 == 0 ? &quad : nullptr);
203    }
204
205    // Iterate once using the metadata iterator, confirm the test data and rewrite
206    int i = 0;
207    auto meta = buffer.metadata();
208    while(meta.next()) {
209        // Confirm initial state
210        assert_metadata_eq(r, {i, 2.f * i}, *meta);
211        // Rewrite
212        *meta = {2 * i, 0.5f * i};
213        i++;
214    }
215    ASSERTF(i == kQuadCount, "Expected %d iterations, got: %d", kQuadCount, i);
216
217    // Now that all metadata has been touched, read with regular iterator and confirm updated state
218    // and that no quad coordinates have been changed.
219    i = 0;
220    auto iter = buffer.iterator();
221    while(iter.next()) {
222        // New metadata
223        assert_metadata_eq(r, {2 * i, 0.5f * i}, iter.metadata());
224
225        // Quad coordinates are unchanged
226        assert_quad_eq(r, quad, *iter.deviceQuad());
227        if (i % 2 == 0) {
228            ASSERT(iter.isLocalValid());
229            assert_quad_eq(r, quad, *iter.localQuad());
230        } else {
231            ASSERT(!iter.isLocalValid());
232            ASSERT(!iter.localQuad());
233        }
234        i++;
235    }
236    ASSERTF(i == kQuadCount, "Expected %d iterations, got: %d", kQuadCount, i);
237}
238