OLD | NEW |
---|---|
(Empty) | |
1 /* | |
2 * Copyright 2014 Google Inc. | |
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 "gm.h" | |
9 #include "SkCanvas.h" | |
10 #include "SkPath.h" | |
11 #include "SkPictureRecorder.h" | |
12 | |
13 /** A call pattern that should be optimized to a single draw call. */ | |
14 static void draw_save_layer_draw_restore(SkCanvas* canvas, int layerAlpha, int d rawAlpha, | |
15 bool isForOptimizedRecord) { | |
16 SkRect targetRect(SkRect::MakeWH(SK_Scalar1, SK_Scalar1)); | |
17 SkPaint layerPaint; | |
18 layerPaint.setColor(SkColorSetARGB(layerAlpha, 0x0, 0x00, 0x0)); | |
19 if (!isForOptimizedRecord) { | |
20 // When drawing without the picture optimization, eg. for example direct ly to a canvas, we | |
21 // can not save layer for every call. This takes too much time for a gm test. The drawing | |
mtklein
2015/01/14 13:28:30
... can not save the full layer ... ?
| |
22 // needs to constrain the saveLayer. | |
23 canvas->saveLayer(&targetRect, &layerPaint); | |
24 } else { | |
25 // The optimization can not handle rect constraints yet. | |
26 canvas->saveLayer(NULL, &layerPaint); | |
27 } | |
28 SkPaint drawPaint; | |
29 // Using just one color should not lose any generality. This way it is just easier to reason | |
30 // about the magnitude of the error. 255 should have the biggest error. | |
31 drawPaint.setColor(SkColorSetARGB(drawAlpha, 0, 255, 0)); | |
32 | |
33 canvas->drawRect(targetRect, drawPaint); | |
34 canvas->restore(); | |
35 } | |
36 | |
37 /** Draws all (layer alpha, draw alpha) combinations as pixel-wide rects. This should identify the | |
38 * combinations that show difference between "savelayer-draw-restore" sequence a nd optimized "draw" | |
39 * sequence. */ | |
40 static void draw_all_save_layer_draw_restore_combinations(SkCanvas* canvas, | |
41 bool isForOptimizedRec ord) { | |
42 canvas->save(); | |
43 for (int layerAlpha = 0; layerAlpha < 256; ++layerAlpha) { | |
44 canvas->save(); | |
45 for (int drawAlpha = 0; drawAlpha < 256; ++drawAlpha) { | |
46 draw_save_layer_draw_restore(canvas, layerAlpha, drawAlpha, isForOpt imizedRecord); | |
47 canvas->translate(SK_Scalar1, 0); | |
48 } | |
49 canvas->restore(); | |
50 canvas->translate(0, SK_Scalar1); | |
51 } | |
52 canvas->restore(); | |
53 } | |
54 | |
55 | |
56 /** Xfermode that highlights small differences between the pixel values that are being drawn. | |
57 * HighlightSmallDifferencesXfermode shows difference between existing image and new image pixel | |
58 * values. The image is the component-wise manhattan distance between the what i s there in the | |
59 * backbuffer and the new shape. The distance is visualized as step colors: dist ance of 0 is black, | |
60 * 1 is dark gray (100), 2 is gray (200) and distance > 2 is white (255). In or der to debug this: | |
61 * This should produce black image (with a deterministic backend): | |
62 * | |
63 * SkCanvas canvas(...); | |
64 * SkPicture picture(...); | |
65 * picture.draw(canvas); | |
66 * SkPaint paint; | |
67 * paint.setXfermode(HighlightSmallDifferencesXfermode::Create()); | |
68 * canvas.saveLayer(NULL, &paint); | |
69 * picture.draw(canvas); | |
70 * canvas.restore(); | |
71 */ | |
72 class HighlightSmallDifferencesXfermode : public SkXfermode { | |
73 public: | |
74 static HighlightSmallDifferencesXfermode* Create() { | |
75 return SkNEW(HighlightSmallDifferencesXfermode); | |
76 } | |
77 | |
78 SK_TO_STRING_OVERRIDE() | |
79 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkPixelXorXfermode) | |
80 | |
81 protected: | |
82 HighlightSmallDifferencesXfermode() { } | |
83 void flatten(SkWriteBuffer&) const SK_OVERRIDE { } | |
84 | |
85 SkPMColor xferColor(SkPMColor src, SkPMColor dst) const SK_OVERRIDE { | |
86 int d = SkAbs32(SkGetPackedR32(dst) - SkGetPackedR32(src)) | |
87 + SkAbs32(SkGetPackedG32(dst) - SkGetPackedG32(src)) | |
88 + SkAbs32(SkGetPackedB32(dst) - SkGetPackedB32(src)); | |
89 d = SkClampMax(d * 100, 255); | |
90 return SkPackARGB32(0xFF, d, d, d); | |
91 } | |
92 #if SK_SUPPORT_GPU | |
93 bool asFragmentProcessor(GrFragmentProcessor** fp, GrTexture* background) co nst; | |
94 #endif | |
95 typedef SkXfermode INHERITED; | |
96 }; | |
97 | |
98 SkFlattenable* HighlightSmallDifferencesXfermode::CreateProc(SkReadBuffer& buffe r) { | |
99 return Create(); | |
100 } | |
101 | |
102 #ifndef SK_IGNORE_TO_STRING | |
103 void HighlightSmallDifferencesXfermode::toString(SkString* str) const { | |
104 str->append("HighlightSmallDifferencesXfermode: ()"); | |
105 } | |
106 #endif | |
107 | |
108 #if SK_SUPPORT_GPU | |
109 | |
110 #include "GrFragmentProcessor.h" | |
111 #include "GrCoordTransform.h" | |
112 #include "GrInvariantOutput.h" | |
113 #include "GrProcessorUnitTest.h" | |
114 #include "gl/GrGLProcessor.h" | |
115 #include "gl/builders/GrGLProgramBuilder.h" | |
116 | |
117 class HighlightSmallDifferencesXferEffect : public GrFragmentProcessor { | |
118 public: | |
119 static GrFragmentProcessor* Create(GrTexture* background) { | |
120 return SkNEW_ARGS(HighlightSmallDifferencesXferEffect, (background)); | |
121 } | |
122 | |
123 void getGLProcessorKey(const GrGLCaps& caps, | |
124 GrProcessorKeyBuilder* b) const SK_OVERRIDE { | |
125 GLProcessor::GenKey(*this, caps, b); | |
126 } | |
127 | |
128 GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE { | |
129 return SkNEW_ARGS(GLProcessor, (*this)); | |
130 } | |
131 | |
132 const char* name() const SK_OVERRIDE { return "HighlightSmallDifferencesXfer Effect"; } | |
133 | |
134 const GrTextureAccess& backgroundAccess() const { return fBackgroundAccess; } | |
135 | |
136 class GLProcessor : public GrGLFragmentProcessor { | |
137 public: | |
138 GLProcessor(const GrFragmentProcessor&) {} | |
139 | |
140 void emitCode(GrGLFPBuilder* builder, | |
141 const GrFragmentProcessor& fp, | |
142 const char* outputColor, | |
143 const char* inputColor, | |
144 const TransformedCoordsArray& coords, | |
145 const TextureSamplerArray& samplers) SK_OVERRIDE { | |
146 const GrTexture* backgroundTex = | |
147 fp.cast<HighlightSmallDifferencesXferEffect>().backgroundAcc ess().getTexture(); | |
148 GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder (); | |
149 const char* dstColor; | |
150 if (backgroundTex) { | |
151 dstColor = "bgColor"; | |
152 fsBuilder->codeAppendf("\t\tvec4 %s = ", dstColor); | |
153 fsBuilder->appendTextureLookup(samplers[0], coords[0].c_str(), c oords[0].getType()); | |
154 fsBuilder->codeAppendf(";\n"); | |
155 } else { | |
156 dstColor = fsBuilder->dstColor(); | |
157 } | |
158 SkASSERT(dstColor); | |
159 | |
160 // We don't try to optimize for this case at all | |
161 if (NULL == inputColor) { | |
162 fsBuilder->codeAppendf("\t\tconst vec4 ones = vec4(1);\n"); | |
163 inputColor = "ones"; | |
164 } | |
165 fsBuilder->codeAppendf("\t\t%s = vec4(vec3(dot(abs(%s.rgb - %s.rgb), vec3(1)) * 100.0)," | |
166 " 1.0);\n", outputColor, dstColor, inputColor ); | |
167 } | |
168 | |
169 static inline void GenKey(const GrProcessor& proc, const GrGLCaps&, | |
170 GrProcessorKeyBuilder* b) { | |
171 // The background may come from the dst or from a texture. | |
172 uint32_t key = proc.numTextures(); | |
173 SkASSERT(key <= 1); | |
174 b->add32(key); | |
175 } | |
176 | |
177 typedef GrGLFragmentProcessor INHERITED; | |
178 }; | |
179 | |
180 private: | |
181 HighlightSmallDifferencesXferEffect(GrTexture* background) { | |
182 this->initClassID<HighlightSmallDifferencesXferEffect>(); | |
183 if (background) { | |
184 fBackgroundTransform.reset(kLocal_GrCoordSet, background, | |
185 GrTextureParams::kNone_FilterMode); | |
186 this->addCoordTransform(&fBackgroundTransform); | |
187 fBackgroundAccess.reset(background); | |
188 this->addTextureAccess(&fBackgroundAccess); | |
189 } else { | |
190 this->setWillReadDstColor(); | |
191 } | |
192 } | |
193 | |
194 bool onIsEqual(const GrFragmentProcessor& other) const SK_OVERRIDE { | |
195 return true; | |
196 } | |
197 | |
198 void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE { | |
199 inout->setToUnknown(GrInvariantOutput::kWill_ReadInput); | |
200 } | |
201 | |
202 GrCoordTransform fBackgroundTransform; | |
203 GrTextureAccess fBackgroundAccess; | |
204 | |
205 typedef GrFragmentProcessor INHERITED; | |
206 }; | |
207 | |
208 bool HighlightSmallDifferencesXfermode::asFragmentProcessor(GrFragmentProcessor* * fp, | |
209 GrTexture* background) const { | |
210 *fp = HighlightSmallDifferencesXferEffect::Create(background); | |
211 SkASSERT(*fp); | |
212 return true; | |
213 }; | |
214 | |
215 #endif | |
216 | |
217 /** | |
218 Tests record optimizations. Draws shapes using a call patterns that get | |
219 optimized in SkRecord. Used to ensure that the optimization does not change t he | |
220 pixels (too drasticly). | |
221 */ | |
222 | |
223 class RecordOptsGM : public skiagm::GM { | |
224 public: | |
225 RecordOptsGM() { | |
226 } | |
227 | |
228 protected: | |
229 SkString onShortName() SK_OVERRIDE { | |
230 return SkString("recordopts"); | |
231 } | |
232 | |
233 SkISize onISize() SK_OVERRIDE { | |
234 return SkISize::Make(256, 256); | |
235 } | |
236 | |
237 void onOnceBeforeDraw() SK_OVERRIDE { | |
238 // Create a more optimal picture of unoptimal call sequence. | |
239 SkPictureRecorder recorder; | |
240 draw_all_save_layer_draw_restore_combinations(recorder.beginRecording(25 6, 256), | |
241 true /*isForOptimizedRecor d*/); | |
242 fSaveLayerDrawRestorePicture.reset(recorder.endRecordingAsPicture()); | |
243 | |
244 // Create a reference bitmap image from the same calls, without the opti mization. | |
245 // The reference must be a bitmap image instead, see below. | |
246 fSaveLayerDrawRestoreReferenceBitmap.allocN32Pixels(256, 256); | |
247 fSaveLayerDrawRestoreReferenceBitmap.eraseColor(SK_ColorBLACK); | |
248 SkCanvas referenceCanvas(fSaveLayerDrawRestoreReferenceBitmap); | |
249 draw_all_save_layer_draw_restore_combinations(&referenceCanvas, | |
250 false /*isForOptimizedReco rd*/); | |
251 referenceCanvas.flush(); | |
252 } | |
253 | |
254 void onDraw(SkCanvas* canvas) SK_OVERRIDE { | |
255 canvas->clear(SK_ColorTRANSPARENT); | |
256 | |
257 // Test savelayer-draw-restore picture optimization. | |
258 // Draw optimized picture. | |
259 fSaveLayerDrawRestorePicture->playback(canvas); | |
260 // Visualize the difference between optimized picture and unoptimized co mmands by drawing | |
261 // the unoptimized commands on top of the existing picture using the | |
262 // HighlightSmallDifferences xfermode. If there would not be any differe nces the resulting | |
263 // bitmap would be empty. | |
264 SkPaint diffPaint; | |
265 diffPaint.setXfermode(HighlightSmallDifferencesXfermode::Create()); | |
266 // Unfortunately we can not draw the unoptimized commands, because GPU b ackend can not | |
267 // execute so many save layers in time. Thus our difference image is cor rect only for the | |
mtklein
2015/01/14 13:28:30
If we're only going to get a useful diff with the
Kimmo Kinnunen
2015/01/14 14:07:20
Right. Well, with my gpu and the current code it g
mtklein
2015/01/14 14:53:10
Hmmm. I think I misunderstood the downside of the
| |
268 // 8888 backend. For GPU backends, the difference image is the differenc e between optimized | |
269 // GPU picture and unoptimized 8888 commands. | |
270 canvas->drawBitmap(fSaveLayerDrawRestoreReferenceBitmap, 0, 0, &diffPain t); | |
271 } | |
272 | |
273 private: | |
274 SkAutoTUnref<SkPicture> fSaveLayerDrawRestorePicture; | |
275 SkBitmap fSaveLayerDrawRestoreReferenceBitmap; | |
276 | |
277 typedef skiagm::GM INHERITED; | |
278 }; | |
279 | |
280 DEF_GM( return SkNEW(RecordOptsGM); ) | |
281 | |
OLD | NEW |