aboutsummaryrefslogtreecommitdiffhomepage
path: root/gm/recordopts.cpp
blob: 341b7ce9242218988a9291a074413ddb8834d964 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
/*
 * Copyright 2014 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "gm.h"
#include "SkCanvas.h"
#include "SkPath.h"
#include "SkPictureRecorder.h"
#include "SkTableColorFilter.h"
#include "SkColorFilterImageFilter.h"
#include "SkPictureImageFilter.h"

constexpr int kTestRectSize = 50;
constexpr int kDetectorGreenValue = 50;

// Below are few functions to install "detector" color filters. The filter is there to assert that
// the color value it sees is the expected. It will trigger only with kDetectorGreenValue, and
// turn that value into full green. The idea is that if an optimization incorrectly changes
// kDetectorGreenValue and then the incorrect value is observable by some part of the drawing
// pipeline, that pixel will remain empty.

static sk_sp<SkColorFilter> make_detector_color_filter() {
    uint8_t tableA[256] = { 0, };
    uint8_t tableR[256] = { 0, };
    uint8_t tableG[256] = { 0, };
    uint8_t tableB[256] = { 0, };
    tableA[255] = 255;
    tableG[kDetectorGreenValue] = 255;
    return SkTableColorFilter::MakeARGB(tableA, tableR, tableG, tableB);
}

// This detector detects that color filter phase of the pixel pipeline receives the correct value.
static void install_detector_color_filter(SkPaint* drawPaint) {
    drawPaint->setColorFilter(make_detector_color_filter());
}

// This detector detects that image filter phase of the pixel pipeline receives the correct value.
static void install_detector_image_filter(SkPaint* drawPaint) {
    drawPaint->setImageFilter(SkColorFilterImageFilter::Make(make_detector_color_filter(),
                                                             drawPaint->refImageFilter()));
}

static void no_detector_install(SkPaint*) {
}

typedef void(*InstallDetectorFunc)(SkPaint*);


// Draws an pattern that can be optimized by alpha folding outer savelayer alpha value to
// inner draw. Since we know that folding will happen to the inner draw, install a detector
// to make sure that optimization does not change anything observable.
static void draw_save_layer_draw_rect_restore_sequence(SkCanvas* canvas, SkColor shapeColor,
                                                       InstallDetectorFunc installDetector) {
    SkRect targetRect(SkRect::MakeWH(SkIntToScalar(kTestRectSize), SkIntToScalar(kTestRectSize)));
    SkPaint layerPaint;
    layerPaint.setColor(SkColorSetARGB(128, 0, 0, 0));
    canvas->saveLayer(&targetRect, &layerPaint);
        SkPaint drawPaint;
        drawPaint.setColor(shapeColor);
        installDetector(&drawPaint);
        canvas->drawRect(targetRect, drawPaint);
    canvas->restore();
}

// Draws an pattern that can be optimized by alpha folding outer savelayer alpha value to
// inner draw. A variant where the draw is not uniform color.
static void draw_save_layer_draw_bitmap_restore_sequence(SkCanvas* canvas, SkColor shapeColor,
                                                         InstallDetectorFunc installDetector) {
    SkBitmap bitmap;
    bitmap.allocN32Pixels(kTestRectSize, kTestRectSize);
    bitmap.eraseColor(shapeColor);
    {
        // Make the bitmap non-uniform color, so that it can not be optimized as uniform drawRect.
        SkCanvas canvas(bitmap);
        SkPaint p;
        p.setColor(SK_ColorWHITE);
        SkASSERT(shapeColor != SK_ColorWHITE);
        canvas.drawRect(SkRect::MakeWH(SkIntToScalar(7), SkIntToScalar(7)), p);
        canvas.flush();
    }

    SkRect targetRect(SkRect::MakeWH(SkIntToScalar(kTestRectSize), SkIntToScalar(kTestRectSize)));
    SkPaint layerPaint;
    layerPaint.setColor(SkColorSetARGB(129, 0, 0, 0));
    canvas->saveLayer(&targetRect, &layerPaint);
        SkPaint drawPaint;
        installDetector(&drawPaint);
        canvas->drawBitmap(bitmap, SkIntToScalar(0), SkIntToScalar(0), &drawPaint);
    canvas->restore();
}

// Draws an pattern that can be optimized by alpha folding outer savelayer alpha value to
// inner savelayer. We know that alpha folding happens to inner savelayer, so add detector there.
static void draw_svg_opacity_and_filter_layer_sequence(SkCanvas* canvas, SkColor shapeColor,
                                                       InstallDetectorFunc installDetector) {

    SkRect targetRect(SkRect::MakeWH(SkIntToScalar(kTestRectSize), SkIntToScalar(kTestRectSize)));
    sk_sp<SkPicture> shape;
    {
        SkPictureRecorder recorder;
        SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(kTestRectSize + 2),
                                                   SkIntToScalar(kTestRectSize + 2));
        SkPaint shapePaint;
        shapePaint.setColor(shapeColor);
        canvas->drawRect(targetRect, shapePaint);
        shape = recorder.finishRecordingAsPicture();
    }

    SkPaint layerPaint;
    layerPaint.setColor(SkColorSetARGB(130, 0, 0, 0));
    canvas->saveLayer(&targetRect, &layerPaint);
        canvas->save();
            canvas->clipRect(targetRect);
            SkPaint drawPaint;
            drawPaint.setImageFilter(SkPictureImageFilter::Make(shape));
            installDetector(&drawPaint);
            canvas->saveLayer(&targetRect, &drawPaint);
            canvas->restore();
        canvas->restore();
    canvas->restore();
}

// Draws two columns of rectangles. The test is correct when:
//  - Left and right columns always identical
//  - First 3 rows are green, with a white dent in the middle row
//  - Next 6 rows are green, with a grey dent in the middle row
//    (the grey dent is from the color filter removing everything but the "good" green, see below)
//  - Last 6 rows are grey
DEF_SIMPLE_GM(recordopts, canvas, (kTestRectSize+1)*2, (kTestRectSize+1)*15) {
    canvas->clear(SK_ColorTRANSPARENT);

    typedef void (*TestVariantSequence)(SkCanvas*, SkColor, InstallDetectorFunc);
    TestVariantSequence funcs[] = {
        draw_save_layer_draw_rect_restore_sequence,
        draw_save_layer_draw_bitmap_restore_sequence,
        draw_svg_opacity_and_filter_layer_sequence,
    };

    // Draw layer-related sequences that can be optimized by folding the opacity layer alpha to
    // the inner draw operation. This tries to trigger the optimization, and relies on gm diffs
    // to keep the color value correct over time.

    // Draws two green rects side by side: one is without the optimization, the other is with
    // the optimization applied.

    SkColor shapeColor = SkColorSetARGB(255, 0, 255, 0);
    for (size_t k = 0; k < SK_ARRAY_COUNT(funcs); ++k) {
        canvas->save();

        TestVariantSequence drawTestSequence = funcs[k];
        drawTestSequence(canvas, shapeColor, no_detector_install);
        canvas->flush();
        canvas->translate(SkIntToScalar(kTestRectSize) + SkIntToScalar(1), SkIntToScalar(0));
        {
            SkPictureRecorder recorder;
            drawTestSequence(recorder.beginRecording(SkIntToScalar(kTestRectSize),
                                                     SkIntToScalar(kTestRectSize)),
                             shapeColor, no_detector_install);
            recorder.finishRecordingAsPicture()->playback(canvas);
            canvas->flush();
        }
        canvas->restore();
        canvas->translate(SkIntToScalar(0), SkIntToScalar(kTestRectSize) + SkIntToScalar(1));
    }

    // Draw the same layer related sequences, but manipulate the sequences so that the result is
    // incorrect if the alpha is folded or folded incorrectly. These test the observable state
    // throughout the pixel pipeline, and thus may turn off the optimizations (this is why we
    // trigger the optimizations above).

    // Draws two green rects side by side: one is without the optimization, the other is with
    // the possibility that optimization is applied.
    // At the end, draws the same patterns in translucent black. This tests that the detectors
    // work, eg. that if the value the detector sees is wrong, the resulting image shows this.
    SkColor shapeColors[] = {
        SkColorSetARGB(255, 0, kDetectorGreenValue, 0),
        SkColorSetARGB(255, 0, (kDetectorGreenValue + 1), 0) // This tests that detectors work.
    };

    InstallDetectorFunc detectorInstallFuncs[] = {
        install_detector_image_filter,
        install_detector_color_filter
    };

    for (size_t i = 0; i < SK_ARRAY_COUNT(shapeColors); ++i) {
        shapeColor = shapeColors[i];
        for (size_t j = 0; j < SK_ARRAY_COUNT(detectorInstallFuncs); ++j) {
            InstallDetectorFunc detectorInstallFunc = detectorInstallFuncs[j];
            for (size_t k = 0; k < SK_ARRAY_COUNT(funcs); ++k) {
                TestVariantSequence drawTestSequence = funcs[k];
                canvas->save();
                drawTestSequence(canvas, shapeColor, detectorInstallFunc);
                canvas->flush();
                canvas->translate(SkIntToScalar(kTestRectSize) + SkIntToScalar(1), SkIntToScalar(0));
                {
                    SkPictureRecorder recorder;
                    drawTestSequence(recorder.beginRecording(SkIntToScalar(kTestRectSize),
                                                             SkIntToScalar(kTestRectSize)),
                                     shapeColor, detectorInstallFunc);
                    recorder.finishRecordingAsPicture()->playback(canvas);
                    canvas->flush();
                }

                canvas->restore();
                canvas->translate(SkIntToScalar(0), SkIntToScalar(kTestRectSize) + SkIntToScalar(1));
            }

        }
    }
}