 Chromium Code Reviews
 Chromium Code Reviews Issue 840483005:
  Fold alpha to the draw in savelayer-draw-restore patterns with non-opaque draw  (Closed) 
  Base URL: https://skia.googlesource.com/skia.git@unique-id-unflatten
    
  
    Issue 840483005:
  Fold alpha to the draw in savelayer-draw-restore patterns with non-opaque draw  (Closed) 
  Base URL: https://skia.googlesource.com/skia.git@unique-id-unflatten| 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 |