Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright 2013 Google Inc. | 2 * Copyright 2013 Google Inc. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
| 6 */ | 6 */ |
| 7 | 7 |
| 8 #ifndef GrGeometryProcessor_DEFINED | 8 #ifndef GrGeometryProcessor_DEFINED |
| 9 #define GrGeometryProcessor_DEFINED | 9 #define GrGeometryProcessor_DEFINED |
| 10 | 10 |
| 11 #include "GrColor.h" | 11 #include "GrColor.h" |
| 12 #include "GrGeometryData.h" | 12 #include "GrGeometryData.h" |
| 13 #include "GrProcessor.h" | 13 #include "GrProcessor.h" |
| 14 #include "GrShaderVar.h" | 14 #include "GrShaderVar.h" |
| 15 | 15 |
| 16 /* | 16 /* |
| 17 * The GrPrimitiveProcessor represents some kind of geometric primitive. This i ncludes the shape | |
|
bsalomon
2014/12/15 15:31:13
This comment is great!
Two minor suggestions:
ma
| |
| 18 * of the primitive and the inherent color of the primitive. The GrPrimitivePro cessor is | |
| 19 * responsible for providing a color and coverage input into the Ganesh renderin g pipeline. Through | |
| 20 * optimization, Ganesh may decide a different color, no color, and / or no cove rage are required | |
| 21 * from the GrPrimitiveProcessor, so the GrPrimitiveProcessor must be able to su pport this | |
| 22 * functionality. We also use the GrPrimitiveProcessor to make batching decisio ns. | |
| 23 * | |
| 24 * There are two feedback loops between the GrFragmentProcessors, the GrXferProc essor, and the | |
| 25 * GrPrimitiveProcessor. The GrPrimitiveProcessor seeds these loops, one with i nitial color and one | |
| 26 * with initial coverage, in its onComputeInvariantColor / Coverage calls. Thes e seed values are | |
| 27 * processed by the subsequent stages of the rendering pipeline and the output i s then fed back | |
| 28 * into the GrPrimitiveProcessor in the initBatchTracker call, where the GrPrimi tiveProcessor can | |
| 29 * then initialize the GrBatchTracker struct with the appropriate values. | |
| 30 * | |
| 31 * In a deferred geometry world, the GrPrimitiveProcessor can always 'batch' To do this, each | |
| 32 * primitive type is associated with one GrPrimitiveProcessor, who has complete control of how | |
| 33 * it draws. Each primitive draw will bundle all required data to perform the d raw, and these | |
| 34 * bundles of data will be owned by an instance of the associated GrPrimitivePro cessor. Bundles | |
| 35 * can be updated alongside the GrBatchTracker struct itself, ultimately allowin g the | |
| 36 * GrPrimitiveProcessor complete control of how it gets data into the fragment s hader as long as | |
| 37 * it emits the appropriate color, or none at all, as directed. | |
| 38 */ | |
| 39 | |
| 40 /* | |
| 17 * A struct for tracking batching decisions. While this lives on GrOptState, it is managed | 41 * A struct for tracking batching decisions. While this lives on GrOptState, it is managed |
| 18 * entirely by the derived classes of the GP. | 42 * entirely by the derived classes of the GP. |
| 19 */ | 43 */ |
| 20 class GrBatchTracker { | 44 class GrBatchTracker { |
| 21 public: | 45 public: |
| 22 template <typename T> const T& cast() const { | 46 template <typename T> const T& cast() const { |
| 23 SkASSERT(sizeof(T) <= kMaxSize); | 47 SkASSERT(sizeof(T) <= kMaxSize); |
| 24 return *reinterpret_cast<const T*>(fData); | 48 return *reinterpret_cast<const T*>(fData.get()); |
| 25 } | 49 } |
| 26 | 50 |
| 27 template <typename T> T* cast() { | 51 template <typename T> T* cast() { |
| 28 SkASSERT(sizeof(T) <= kMaxSize); | 52 SkASSERT(sizeof(T) <= kMaxSize); |
| 29 return reinterpret_cast<T*>(fData); | 53 return reinterpret_cast<T*>(fData.get()); |
| 30 } | 54 } |
| 31 | 55 |
| 32 static const size_t kMaxSize = 32; | 56 static const size_t kMaxSize = 32; |
| 33 | 57 |
| 34 private: | 58 private: |
| 35 uint8_t fData[kMaxSize]; | 59 mutable SkAlignedSStorage<kMaxSize> fData; |
|
bsalomon
2014/12/15 15:31:13
still unclear to me why this is mutable and the th
| |
| 36 }; | 60 }; |
| 37 | 61 |
| 38 class GrGLCaps; | 62 class GrGLCaps; |
| 39 class GrGLGeometryProcessor; | 63 class GrGLGeometryProcessor; |
| 40 class GrOptDrawState; | 64 class GrOptDrawState; |
| 41 | 65 |
| 42 struct GrInitInvariantOutput; | 66 struct GrInitInvariantOutput; |
| 43 | 67 |
| 44 /* | 68 /* |
| 45 * GrGeometryProcessors and GrPathProcessors may effect invariantColor | 69 * GrPrimitiveProcessor defines an interface which all subclasses must implement . All |
| 70 * GrPrimitiveProcessors must proivide seed color and coverage for the Ganesh co lor / coverage | |
| 71 * pipelines, and they must provide some notion of equality | |
| 46 */ | 72 */ |
| 47 class GrPrimitiveProcessor : public GrProcessor { | 73 class GrPrimitiveProcessor : public GrProcessor { |
| 48 public: | 74 public: |
| 49 // TODO GPs and PPs have to provide an initial coverage because the coverage invariant code is | 75 virtual bool canMakeEqual(const GrBatchTracker& mine, |
| 50 // broken right now | 76 const GrPrimitiveProcessor& that, |
| 51 virtual uint8_t coverage() const = 0; | 77 const GrBatchTracker& theirs) const = 0; |
| 78 | |
| 79 /* | |
| 80 * We always call canMakeEqual before makeEqual so there is no need to do an y kind of equality | |
| 81 * testing here | |
| 82 * TODO make this pure virtual when primProcs can actually use it | |
| 83 */ | |
| 84 virtual void makeEqual(GrBatchTracker*, const GrBatchTracker&) const {} | |
| 85 | |
| 52 virtual void getInvariantOutputColor(GrInitInvariantOutput* out) const = 0; | 86 virtual void getInvariantOutputColor(GrInitInvariantOutput* out) const = 0; |
| 53 virtual void getInvariantOutputCoverage(GrInitInvariantOutput* out) const = 0; | 87 virtual void getInvariantOutputCoverage(GrInitInvariantOutput* out) const = 0; |
| 54 | 88 |
| 55 private: | 89 private: |
| 56 typedef GrProcessor INHERITED; | 90 typedef GrProcessor INHERITED; |
| 57 }; | 91 }; |
| 58 | 92 |
| 93 /* | |
| 94 * This enum is shared by GrPrimitiveProcessors and GrGLPrimitiveProcessors to c oordinate shaders | |
| 95 * with vertex attributes / uniforms. | |
| 96 */ | |
| 97 enum GrGPInput { | |
| 98 kAllOnes_GrGPInput, | |
| 99 kAttribute_GrGPInput, | |
| 100 kUniform_GrGPInput, | |
| 101 kIgnored_GrGPInput, | |
| 102 }; | |
| 103 | |
| 59 /** | 104 /** |
| 60 * A GrGeometryProcessor is used to perform computation in the vertex shader and | 105 * A GrGeometryProcessor is a flexible method for rendering a primitive. The Gr GeometryProcessor |
| 61 * add support for custom vertex attributes. A GrGemeotryProcessor is typically | 106 * has complete control over vertex attributes and uniforms(aside from the rende r target) but it |
| 62 * tied to the code that does a specific type of high-level primitive rendering | 107 * must obey the same contract as any GrPrimitiveProcessor, specifically it must emit a color and |
| 63 * (e.g. anti-aliased circle rendering). The GrGeometryProcessor used for a draw is | 108 * coverage into the fragment shader. Where this color and coverage come from i s completely the |
| 64 * specified using GrDrawState. There can only be one geometry processor active for | 109 * responsibility of the GrGeometryProcessor. |
|
bsalomon
2014/12/15 15:31:13
nice
| |
| 65 * a draw. The custom vertex attributes required by the geometry processor must be | |
| 66 * added to the vertex attribute array specified on the GrDrawState. | |
| 67 * GrGeometryProcessor subclasses should be immutable after construction. | |
| 68 */ | 110 */ |
| 69 class GrGeometryProcessor : public GrPrimitiveProcessor { | 111 class GrGeometryProcessor : public GrPrimitiveProcessor { |
| 70 public: | 112 public: |
| 71 // TODO the Hint can be handled in a much more clean way when we have deferr ed geometry or | 113 // TODO the Hint can be handled in a much more clean way when we have deferr ed geometry or |
| 72 // atleast bundles | 114 // atleast bundles |
| 73 GrGeometryProcessor(GrColor color, bool opaqueVertexColors = false, uint8_t coverage = 0xff) | 115 GrGeometryProcessor(GrColor color, bool opaqueVertexColors = false) |
| 74 : fVertexStride(0) | 116 : fVertexStride(0) |
| 75 , fColor(color) | 117 , fColor(color) |
| 76 , fCoverage(coverage) | |
| 77 , fOpaqueVertexColors(opaqueVertexColors) | 118 , fOpaqueVertexColors(opaqueVertexColors) |
| 78 , fWillUseGeoShader(false) | 119 , fWillUseGeoShader(false) |
| 79 , fHasVertexColor(false) | 120 , fHasVertexColor(false) |
| 80 , fHasVertexCoverage(false) | |
| 81 , fHasLocalCoords(false) {} | 121 , fHasLocalCoords(false) {} |
| 82 | 122 |
| 83 virtual const char* name() const = 0; | 123 virtual const char* name() const = 0; |
| 84 | 124 |
| 85 /** | 125 /** |
| 86 * Sets a unique key on the GrProcessorKeyBuilder that is directly associate d with this geometry | 126 * Sets a unique key on the GrProcessorKeyBuilder that is directly associate d with this geometry |
| 87 * processor's GL backend implementation. | 127 * processor's GL backend implementation. |
| 88 */ | 128 */ |
| 89 virtual void getGLProcessorKey(const GrBatchTracker& bt, | 129 virtual void getGLProcessorKey(const GrBatchTracker& bt, |
| 90 const GrGLCaps& caps, | 130 const GrGLCaps& caps, |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 116 | 156 |
| 117 const VertexAttribArray& getAttribs() const { return fAttribs; } | 157 const VertexAttribArray& getAttribs() const { return fAttribs; } |
| 118 | 158 |
| 119 // Returns the vertex stride of the GP. A common use case is to request geo metry from a | 159 // Returns the vertex stride of the GP. A common use case is to request geo metry from a |
| 120 // drawtarget based off of the stride, and to populate this memory using an implicit array of | 160 // drawtarget based off of the stride, and to populate this memory using an implicit array of |
| 121 // structs. In this case, it is best to assert the vertexstride == sizeof(V ertexStruct). | 161 // structs. In this case, it is best to assert the vertexstride == sizeof(V ertexStruct). |
| 122 size_t getVertexStride() const { return fVertexStride; } | 162 size_t getVertexStride() const { return fVertexStride; } |
| 123 | 163 |
| 124 bool willUseGeoShader() const { return fWillUseGeoShader; } | 164 bool willUseGeoShader() const { return fWillUseGeoShader; } |
| 125 | 165 |
| 126 /** Returns true if this and other processor conservatively draw identically . It can only return | 166 /* |
| 127 true when the two prcoessors are of the same subclass (i.e. they return the same object from | 167 * In an ideal world, two GrGeometryProcessors with the same class id and te xture accesses |
| 128 from getFactory()). | 168 * would ALWAYS be able to batch together. If two GrGeometryProcesosrs are the same then we |
| 129 A return value of true from isEqual() should not be used to test whether the processors | 169 * will only keep one of them. The remaining GrGeometryProcessor then updat es its |
| 130 would generate the same shader code. To test for identical code generati on use the | 170 * GrBatchTracker to incorporate the draw information from the GrGeometryPro cessor we discard. |
| 131 processors' keys computed by the GrBackendEffectFactory. */ | 171 * Any bundles associated with the discarded GrGeometryProcessor will be att ached to the |
| 132 bool isEqual(const GrGeometryProcessor& that) const { | 172 * remaining GrGeometryProcessor. |
| 173 */ | |
| 174 bool canMakeEqual(const GrBatchTracker& mine, | |
| 175 const GrPrimitiveProcessor& that, | |
| 176 const GrBatchTracker& theirs) const SK_OVERRIDE { | |
| 133 if (this->classID() != that.classID() || !this->hasSameTextureAccesses(t hat)) { | 177 if (this->classID() != that.classID() || !this->hasSameTextureAccesses(t hat)) { |
| 134 return false; | 178 return false; |
| 135 } | 179 } |
| 136 | 180 |
| 137 // TODO remove the hint | 181 // TODO remove the hint |
| 138 if (fHasVertexColor && fOpaqueVertexColors != that.fOpaqueVertexColors) { | 182 const GrGeometryProcessor& other = that.cast<GrGeometryProcessor>(); |
| 183 if (fHasVertexColor && fOpaqueVertexColors != other.fOpaqueVertexColors) { | |
| 139 return false; | 184 return false; |
| 140 } | 185 } |
| 141 | 186 |
| 142 if (!fHasVertexColor && this->color() != that.color()) { | 187 if (!fHasVertexColor && this->color() != other.color()) { |
| 143 return false; | 188 return false; |
| 144 } | 189 } |
| 145 | 190 return this->onCanMakeEqual(mine, theirs); |
| 146 // TODO this is fragile, most gps set their coverage to 0xff so this is okay. In the long | |
| 147 // term this should move to subclasses which set explicit coverage | |
| 148 if (!fHasVertexCoverage && this->coverage() != that.coverage()) { | |
| 149 return false; | |
| 150 } | |
| 151 return this->onIsEqual(that); | |
| 152 } | 191 } |
| 153 | 192 |
| 193 virtual bool onCanMakeEqual(const GrBatchTracker& mine, const GrBatchTracker & theirs) const = 0; | |
|
bsalomon
2014/12/15 15:31:13
move to private?
| |
| 194 | |
| 195 /* | |
| 196 * This struct allows the optstate to communicate requirements to the GP. | |
| 197 */ | |
| 154 struct InitBT { | 198 struct InitBT { |
| 155 bool fOutputColor; | 199 bool fColorIgnored; |
| 156 bool fOutputCoverage; | 200 bool fCoverageIgnored; |
| 157 GrColor fColor; | 201 GrColor fOverrideColor; |
| 158 GrColor fCoverage; | |
| 159 }; | 202 }; |
| 160 | 203 |
| 161 virtual void initBatchTracker(GrBatchTracker*, const InitBT&) const {} | 204 virtual void initBatchTracker(GrBatchTracker*, const InitBT&) const = 0; |
| 205 | |
| 206 // TODO we can remove color from the GrGeometryProcessor base class once we have bundles of | |
| 207 // primitive data | |
| 208 GrColor color() const { return fColor; } | |
| 162 | 209 |
| 163 GrColor color() const { return fColor; } | 210 // TODO this is a total hack until the gp can do deferred geometry |
| 164 uint8_t coverage() const { return fCoverage; } | 211 bool hasVertexColor() const { return fHasVertexColor; } |
| 165 | 212 |
| 166 // TODO this is a total hack until the gp can own whether or not it uses uni form | 213 // TODO this is a total hack until gp can setup and manage local coords |
| 167 // color / coverage | |
| 168 bool hasVertexColor() const { return fHasVertexColor; } | |
| 169 bool hasVertexCoverage() const { return fHasVertexCoverage; } | |
| 170 bool hasLocalCoords() const { return fHasLocalCoords; } | 214 bool hasLocalCoords() const { return fHasLocalCoords; } |
| 171 | 215 |
| 172 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE; | 216 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE; |
| 173 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRID E; | 217 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRID E; |
| 174 | 218 |
| 175 protected: | 219 protected: |
| 220 /* | |
| 221 * A simple helper function to determine by what means the GrGeometryProcess or should use to | |
|
bsalomon
2014/12/15 15:31:13
Can you throw in the word "optional" ? Just want t
| |
| 222 * provide color. If we are given an override color(ie the given overrideco lor is NOT illegal) | |
|
bsalomon
2014/12/15 15:31:13
can you say GrColor_ILLEGAL? I think it might be c
| |
| 223 * then we must always emit that color via a uniform. Otherwise, if our col or is ignored | |
|
bsalomon
2014/12/15 15:31:13
Does it have to be via a uniform? A GP could rewr
| |
| 224 * then we should not emit a color. Lastly, if we don't have vertex colors then we must emit | |
| 225 * a color via uniform | |
| 226 * TODO this function changes quite a bit with deferred geometry. There the GrGeometryProcessor | |
| 227 * can upload a new color via attribute if needed. | |
| 228 */ | |
| 229 static GrGPInput GetColorInputType(GrColor* color, GrColor primitiveColor, c onst InitBT& init, | |
| 230 bool hasVertexColor) { | |
| 231 if (GrColor_ILLEGAL != init.fOverrideColor) { | |
| 232 *color = init.fOverrideColor; | |
| 233 return kUniform_GrGPInput; | |
| 234 } else if (init.fColorIgnored) { | |
| 235 *color = GrColor_ILLEGAL; | |
| 236 return kIgnored_GrGPInput; | |
| 237 } | |
| 238 | |
| 239 *color = primitiveColor; | |
| 240 if (hasVertexColor) { | |
| 241 return kAttribute_GrGPInput; | |
| 242 } else { | |
| 243 return kUniform_GrGPInput; | |
| 244 } | |
| 245 } | |
| 246 | |
| 247 /* | |
| 248 * CanCombineOutput will return true if two draws are 'batchable' from a col or perspective. | |
| 249 * TODO remove this when GPs can upgrade to attribute color | |
| 250 */ | |
| 251 static bool CanCombineOutput(GrGPInput left, GrColor lColor, GrGPInput right , GrColor rColor) { | |
| 252 if (left != right) { | |
| 253 return false; | |
| 254 } | |
| 255 | |
| 256 if (kUniform_GrGPInput == left && lColor != rColor) { | |
| 257 return false; | |
| 258 } | |
| 259 | |
| 260 return true; | |
| 261 } | |
| 262 | |
| 176 /** | 263 /** |
| 177 * Subclasses call this from their constructor to register vertex attributes . Attributes | 264 * Subclasses call this from their constructor to register vertex attributes . Attributes |
| 178 * will be padded to the nearest 4 bytes for performance reasons. | 265 * will be padded to the nearest 4 bytes for performance reasons. |
| 179 * TODO After deferred geometry, we should do all of this inline in Generate Geometry alongside | 266 * TODO After deferred geometry, we should do all of this inline in Generate Geometry alongside |
| 180 * the struct used to actually populate the attributes | 267 * the struct used to actually populate the attributes |
| 181 */ | 268 */ |
| 182 const GrAttribute& addVertexAttrib(const GrAttribute& attribute) { | 269 const GrAttribute& addVertexAttrib(const GrAttribute& attribute) { |
| 183 fVertexStride += attribute.fOffset; | 270 fVertexStride += attribute.fOffset; |
| 184 return fAttribs.push_back(attribute); | 271 return fAttribs.push_back(attribute); |
| 185 } | 272 } |
| 186 | 273 |
| 187 void setWillUseGeoShader() { fWillUseGeoShader = true; } | 274 void setWillUseGeoShader() { fWillUseGeoShader = true; } |
| 188 | 275 |
| 189 // TODO hack see above | 276 // TODO hack see above |
| 190 void setHasVertexColor() { fHasVertexColor = true; } | 277 void setHasVertexColor() { fHasVertexColor = true; } |
| 191 void setHasVertexCoverage() { fHasVertexCoverage = true; } | |
| 192 void setHasLocalCoords() { fHasLocalCoords = true; } | 278 void setHasLocalCoords() { fHasLocalCoords = true; } |
| 193 | 279 |
| 194 virtual void onGetInvariantOutputColor(GrInitInvariantOutput*) const {} | 280 virtual void onGetInvariantOutputColor(GrInitInvariantOutput*) const {} |
| 195 virtual void onGetInvariantOutputCoverage(GrInitInvariantOutput*) const = 0; | 281 virtual void onGetInvariantOutputCoverage(GrInitInvariantOutput*) const = 0; |
| 196 | 282 |
| 197 private: | 283 private: |
| 198 virtual bool onIsEqual(const GrGeometryProcessor&) const = 0; | 284 virtual bool onIsEqual(const GrGeometryProcessor&) const = 0; |
| 199 | 285 |
| 200 SkSTArray<kMaxVertexAttribs, GrAttribute, true> fAttribs; | 286 SkSTArray<kMaxVertexAttribs, GrAttribute, true> fAttribs; |
| 201 size_t fVertexStride; | 287 size_t fVertexStride; |
| 202 GrColor fColor; | 288 GrColor fColor; |
| 203 uint8_t fCoverage; | |
| 204 bool fOpaqueVertexColors; | 289 bool fOpaqueVertexColors; |
| 205 bool fWillUseGeoShader; | 290 bool fWillUseGeoShader; |
| 206 bool fHasVertexColor; | 291 bool fHasVertexColor; |
| 207 bool fHasVertexCoverage; | |
| 208 bool fHasLocalCoords; | 292 bool fHasLocalCoords; |
| 209 | 293 |
| 210 typedef GrProcessor INHERITED; | 294 typedef GrProcessor INHERITED; |
| 211 }; | 295 }; |
| 212 | 296 |
| 213 /* | 297 /* |
| 214 * The path equivalent of the GP. For now this just manages color. In the long term we plan on | 298 * The path equivalent of the GP. For now this just manages color. In the long term we plan on |
| 215 * extending this class to handle all nvpr uniform / varying / program work. | 299 * extending this class to handle all nvpr uniform / varying / program work. |
| 216 */ | 300 */ |
| 217 class GrPathProcessor : public GrPrimitiveProcessor { | 301 class GrPathProcessor : public GrPrimitiveProcessor { |
| 218 public: | 302 public: |
| 219 static GrPathProcessor* Create(GrColor color) { | 303 static GrPathProcessor* Create(GrColor color) { |
| 220 return SkNEW_ARGS(GrPathProcessor, (color)); | 304 return SkNEW_ARGS(GrPathProcessor, (color)); |
| 221 } | 305 } |
| 222 | 306 |
| 307 bool canMakeEqual(const GrBatchTracker& mine, | |
| 308 const GrPrimitiveProcessor& that, | |
| 309 const GrBatchTracker& theirs) const SK_OVERRIDE { | |
| 310 if (this->classID() != that.classID() || !this->hasSameTextureAccesses(t hat)) { | |
|
bsalomon
2014/12/15 15:31:13
Seems a little weird that path procs can even have
| |
| 311 return false; | |
| 312 } | |
| 313 const GrPathProcessor& other = that.cast<GrPathProcessor>(); | |
| 314 return other.color() == this->color(); | |
|
bsalomon
2014/12/15 15:31:13
So if there is an override these two colors would
| |
| 315 } | |
| 316 | |
| 223 const char* name() const SK_OVERRIDE { return "PathProcessor"; } | 317 const char* name() const SK_OVERRIDE { return "PathProcessor"; } |
| 224 uint8_t coverage() const SK_OVERRIDE { return 0xff; } | 318 GrColor color() const { return fColor; } |
| 225 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE; | 319 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE; |
| 226 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRID E; | 320 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRID E; |
| 227 | 321 |
| 228 private: | 322 private: |
| 229 GrPathProcessor(GrColor color) : fColor(color) {} | 323 GrPathProcessor(GrColor color); |
| 230 GrColor fColor; | 324 GrColor fColor; |
| 231 | 325 |
| 232 typedef GrProcessor INHERITED; | 326 typedef GrProcessor INHERITED; |
| 233 }; | 327 }; |
| 234 #endif | 328 #endif |
| OLD | NEW |