blob: a7fea765ab6cdc9d104f2f3fa093651f98a00e03 [file] [log] [blame]
joshualittb0a8a372014-09-23 09:50:21 -07001/*
2 * Copyright 2013 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#ifndef GrGeometryProcessor_DEFINED
9#define GrGeometryProcessor_DEFINED
10
joshualitt2e3b3e32014-12-09 13:31:14 -080011#include "GrColor.h"
joshualittb0a8a372014-09-23 09:50:21 -070012#include "GrProcessor.h"
bsalomon6251d172014-10-15 10:50:36 -070013#include "GrShaderVar.h"
joshualittb0a8a372014-09-23 09:50:21 -070014
joshualitt87f48d92014-12-04 10:41:40 -080015/*
joshualitt9b989322014-12-15 14:16:27 -080016 * The GrPrimitiveProcessor represents some kind of geometric primitive. This includes the shape
17 * of the primitive and the inherent color of the primitive. The GrPrimitiveProcessor is
18 * responsible for providing a color and coverage input into the Ganesh rendering pipeline. Through
19 * optimization, Ganesh may decide a different color, no color, and / or no coverage are required
20 * from the GrPrimitiveProcessor, so the GrPrimitiveProcessor must be able to support this
21 * functionality. We also use the GrPrimitiveProcessor to make batching decisions.
22 *
23 * There are two feedback loops between the GrFragmentProcessors, the GrXferProcessor, and the
24 * GrPrimitiveProcessor. These loops run on the CPU and compute any invariant components which
25 * might be useful for correctness / optimization decisions. The GrPrimitiveProcessor seeds these
26 * loops, one with initial color and one with initial coverage, in its
27 * onComputeInvariantColor / Coverage calls. These seed values are processed by the subsequent
28 * stages of the rendering pipeline and the output is then fed back into the GrPrimitiveProcessor in
29 * the initBatchTracker call, where the GrPrimitiveProcessor can then initialize the GrBatchTracker
30 * struct with the appropriate values.
31 *
32 * We are evolving this system to move towards generating geometric meshes and their associated
33 * vertex data after we have batched and reordered draws. This system, known as 'deferred geometry'
34 * will allow the GrPrimitiveProcessor much greater control over how data is transmitted to shaders.
35 *
36 * In a deferred geometry world, the GrPrimitiveProcessor can always 'batch' To do this, each
37 * primitive type is associated with one GrPrimitiveProcessor, who has complete control of how
38 * it draws. Each primitive draw will bundle all required data to perform the draw, and these
39 * bundles of data will be owned by an instance of the associated GrPrimitiveProcessor. Bundles
40 * can be updated alongside the GrBatchTracker struct itself, ultimately allowing the
41 * GrPrimitiveProcessor complete control of how it gets data into the fragment shader as long as
42 * it emits the appropriate color, or none at all, as directed.
43 */
44
45/*
joshualitt87f48d92014-12-04 10:41:40 -080046 * A struct for tracking batching decisions. While this lives on GrOptState, it is managed
47 * entirely by the derived classes of the GP.
joshualittd5a7db42015-01-27 15:39:06 -080048 * // TODO this was an early attempt at handling out of order batching. It should be
49 * used carefully as it is being replaced by GrBatch
joshualitt87f48d92014-12-04 10:41:40 -080050 */
51class GrBatchTracker {
52public:
53 template <typename T> const T& cast() const {
54 SkASSERT(sizeof(T) <= kMaxSize);
joshualitt9b989322014-12-15 14:16:27 -080055 return *reinterpret_cast<const T*>(fData.get());
joshualitt87f48d92014-12-04 10:41:40 -080056 }
57
58 template <typename T> T* cast() {
59 SkASSERT(sizeof(T) <= kMaxSize);
joshualitt9b989322014-12-15 14:16:27 -080060 return reinterpret_cast<T*>(fData.get());
joshualitt87f48d92014-12-04 10:41:40 -080061 }
62
63 static const size_t kMaxSize = 32;
64
65private:
joshualitt9b989322014-12-15 14:16:27 -080066 SkAlignedSStorage<kMaxSize> fData;
joshualitt87f48d92014-12-04 10:41:40 -080067};
68
joshualittd5a7db42015-01-27 15:39:06 -080069class GrIndexBufferAllocPool;
joshualitteb2a6762014-12-04 11:35:33 -080070class GrGLCaps;
joshualittabb52a12015-01-13 15:02:10 -080071class GrGLPrimitiveProcessor;
joshualittd5a7db42015-01-27 15:39:06 -080072class GrVertexBufferAllocPool;
joshualitt87f48d92014-12-04 10:41:40 -080073
joshualitt56995b52014-12-11 15:44:02 -080074struct GrInitInvariantOutput;
75
joshualittd5a7db42015-01-27 15:39:06 -080076/*
77 * This struct allows the GrPipeline to communicate information about the pipeline. Most of this
78 * is overrides, but some of it is general information. Logically it should live in GrPipeline.h,
79 * but this is problematic due to circular dependencies.
80 */
81struct GrPipelineInfo {
82 bool fColorIgnored;
83 bool fCoverageIgnored;
84 GrColor fOverrideColor;
85 bool fUsesLocalCoords;
86};
joshualitt9b989322014-12-15 14:16:27 -080087
joshualitt56995b52014-12-11 15:44:02 -080088/*
joshualitt9b989322014-12-15 14:16:27 -080089 * This enum is shared by GrPrimitiveProcessors and GrGLPrimitiveProcessors to coordinate shaders
90 * with vertex attributes / uniforms.
91 */
92enum GrGPInput {
93 kAllOnes_GrGPInput,
94 kAttribute_GrGPInput,
95 kUniform_GrGPInput,
96 kIgnored_GrGPInput,
97};
98
99/*
100 * GrPrimitiveProcessor defines an interface which all subclasses must implement. All
101 * GrPrimitiveProcessors must proivide seed color and coverage for the Ganesh color / coverage
102 * pipelines, and they must provide some notion of equality
joshualitt56995b52014-12-11 15:44:02 -0800103 */
104class GrPrimitiveProcessor : public GrProcessor {
105public:
joshualitt290c09b2014-12-19 13:45:20 -0800106 // TODO let the PrimProc itself set this in its setData call, this should really live on the
107 // bundle of primitive data
joshualitt8059eb92014-12-29 15:10:07 -0800108 const SkMatrix& viewMatrix() const { return fViewMatrix; }
joshualitt290c09b2014-12-19 13:45:20 -0800109 const SkMatrix& localMatrix() const { return fLocalMatrix; }
110
joshualittd5a7db42015-01-27 15:39:06 -0800111 virtual void initBatchTracker(GrBatchTracker*, const GrPipelineInfo&) const = 0;
joshualitt9b989322014-12-15 14:16:27 -0800112
113 virtual bool canMakeEqual(const GrBatchTracker& mine,
114 const GrPrimitiveProcessor& that,
115 const GrBatchTracker& theirs) const = 0;
116
joshualitt56995b52014-12-11 15:44:02 -0800117 virtual void getInvariantOutputColor(GrInitInvariantOutput* out) const = 0;
118 virtual void getInvariantOutputCoverage(GrInitInvariantOutput* out) const = 0;
119
joshualitt71c92602015-01-14 08:12:47 -0800120 // Only the GrGeometryProcessor subclass actually has a geo shader or vertex attributes, but
121 // we put these calls on the base class to prevent having to cast
122 virtual bool willUseGeoShader() const = 0;
123
124 /*
125 * This is a safeguard to prevent GrPrimitiveProcessor's from going beyond platform specific
126 * attribute limits. This number can almost certainly be raised if required.
127 */
128 static const int kMaxVertexAttribs = 6;
129
130 struct Attribute {
131 Attribute()
132 : fName(NULL)
133 , fType(kFloat_GrVertexAttribType)
134 , fOffset(0) {}
135 Attribute(const char* name, GrVertexAttribType type)
136 : fName(name)
137 , fType(type)
138 , fOffset(SkAlign4(GrVertexAttribTypeSize(type))) {}
139 const char* fName;
140 GrVertexAttribType fType;
141 size_t fOffset;
142 };
143
144 int numAttribs() const { return fNumAttribs; }
145 const Attribute& getAttrib(int index) const {
146 SkASSERT(index < fNumAttribs);
147 return fAttribs[index];
148 }
149
150 // Returns the vertex stride of the GP. A common use case is to request geometry from a
151 // drawtarget based off of the stride, and to populate this memory using an implicit array of
152 // structs. In this case, it is best to assert the vertexstride == sizeof(VertexStruct).
153 size_t getVertexStride() const { return fVertexStride; }
154
egdanielc2304142014-12-11 13:15:13 -0800155 /**
joshualittabb52a12015-01-13 15:02:10 -0800156 * Gets a transformKey from an array of coord transforms
157 */
158 uint32_t getTransformKey(const SkTArray<const GrCoordTransform*, true>&) const;
159
160 /**
egdanielc2304142014-12-11 13:15:13 -0800161 * Sets a unique key on the GrProcessorKeyBuilder that is directly associated with this geometry
162 * processor's GL backend implementation.
163 */
joshualitteb2a6762014-12-04 11:35:33 -0800164 virtual void getGLProcessorKey(const GrBatchTracker& bt,
165 const GrGLCaps& caps,
166 GrProcessorKeyBuilder* b) const = 0;
167
168
169 /** Returns a new instance of the appropriate *GL* implementation class
170 for the given GrProcessor; caller is responsible for deleting
171 the object. */
joshualittabb52a12015-01-13 15:02:10 -0800172 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
173 const GrGLCaps& caps) const = 0;
joshualittb0a8a372014-09-23 09:50:21 -0700174
joshualitt17e73142015-01-21 11:52:36 -0800175 bool isPathRendering() const { return fIsPathRendering; }
176
joshualitt9b989322014-12-15 14:16:27 -0800177protected:
joshualitt17e73142015-01-21 11:52:36 -0800178 GrPrimitiveProcessor(const SkMatrix& viewMatrix, const SkMatrix& localMatrix,
179 bool isPathRendering)
joshualitt71c92602015-01-14 08:12:47 -0800180 : fNumAttribs(0)
181 , fVertexStride(0)
182 , fViewMatrix(viewMatrix)
joshualitt17e73142015-01-21 11:52:36 -0800183 , fLocalMatrix(localMatrix)
184 , fIsPathRendering(isPathRendering) {}
joshualitt290c09b2014-12-19 13:45:20 -0800185
joshualitt9b989322014-12-15 14:16:27 -0800186 /*
187 * CanCombineOutput will return true if two draws are 'batchable' from a color perspective.
188 * TODO remove this when GPs can upgrade to attribute color
189 */
190 static bool CanCombineOutput(GrGPInput left, GrColor lColor, GrGPInput right, GrColor rColor) {
191 if (left != right) {
192 return false;
193 }
194
195 if (kUniform_GrGPInput == left && lColor != rColor) {
196 return false;
197 }
198
199 return true;
200 }
201
joshualitt290c09b2014-12-19 13:45:20 -0800202 static bool CanCombineLocalMatrices(const GrPrimitiveProcessor& left,
203 bool leftUsesLocalCoords,
204 const GrPrimitiveProcessor& right,
205 bool rightUsesLocalCoords) {
206 if (leftUsesLocalCoords != rightUsesLocalCoords) {
207 return false;
208 }
209
210 if (leftUsesLocalCoords && !left.localMatrix().cheapEqualTo(right.localMatrix())) {
211 return false;
212 }
213 return true;
214 }
215
joshualitt71c92602015-01-14 08:12:47 -0800216 Attribute fAttribs[kMaxVertexAttribs];
217 int fNumAttribs;
218 size_t fVertexStride;
219
joshualitt9b989322014-12-15 14:16:27 -0800220private:
joshualittabb52a12015-01-13 15:02:10 -0800221 virtual bool hasExplicitLocalCoords() const = 0;
222
robertphillips46d36f02015-01-18 08:14:14 -0800223 const SkMatrix fViewMatrix;
joshualitt290c09b2014-12-19 13:45:20 -0800224 SkMatrix fLocalMatrix;
joshualitt17e73142015-01-21 11:52:36 -0800225 bool fIsPathRendering;
joshualitt290c09b2014-12-19 13:45:20 -0800226
joshualitt9b989322014-12-15 14:16:27 -0800227 typedef GrProcessor INHERITED;
228};
229
230/**
231 * A GrGeometryProcessor is a flexible method for rendering a primitive. The GrGeometryProcessor
232 * has complete control over vertex attributes and uniforms(aside from the render target) but it
233 * must obey the same contract as any GrPrimitiveProcessor, specifically it must emit a color and
234 * coverage into the fragment shader. Where this color and coverage come from is completely the
235 * responsibility of the GrGeometryProcessor.
236 */
237class GrGeometryProcessor : public GrPrimitiveProcessor {
238public:
239 // TODO the Hint can be handled in a much more clean way when we have deferred geometry or
240 // atleast bundles
joshualitt290c09b2014-12-19 13:45:20 -0800241 GrGeometryProcessor(GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800242 const SkMatrix& viewMatrix = SkMatrix::I(),
243 const SkMatrix& localMatrix = SkMatrix::I(),
244 bool opaqueVertexColors = false)
joshualitt17e73142015-01-21 11:52:36 -0800245 : INHERITED(viewMatrix, localMatrix, false)
joshualitt9b989322014-12-15 14:16:27 -0800246 , fColor(color)
247 , fOpaqueVertexColors(opaqueVertexColors)
248 , fWillUseGeoShader(false)
249 , fHasVertexColor(false)
250 , fHasLocalCoords(false) {}
251
joshualitt74077b92014-10-24 11:26:03 -0700252 bool willUseGeoShader() const { return fWillUseGeoShader; }
253
joshualitt9b989322014-12-15 14:16:27 -0800254 /*
255 * In an ideal world, two GrGeometryProcessors with the same class id and texture accesses
256 * would ALWAYS be able to batch together. If two GrGeometryProcesosrs are the same then we
257 * will only keep one of them. The remaining GrGeometryProcessor then updates its
258 * GrBatchTracker to incorporate the draw information from the GrGeometryProcessor we discard.
259 * Any bundles associated with the discarded GrGeometryProcessor will be attached to the
260 * remaining GrGeometryProcessor.
261 */
262 bool canMakeEqual(const GrBatchTracker& mine,
263 const GrPrimitiveProcessor& that,
264 const GrBatchTracker& theirs) const SK_OVERRIDE {
joshualitteb2a6762014-12-04 11:35:33 -0800265 if (this->classID() != that.classID() || !this->hasSameTextureAccesses(that)) {
bsalomon0e08fc12014-10-15 08:19:04 -0700266 return false;
267 }
joshualitt2e3b3e32014-12-09 13:31:14 -0800268
joshualitt8059eb92014-12-29 15:10:07 -0800269 // TODO let the GPs decide this
270 if (!this->viewMatrix().cheapEqualTo(that.viewMatrix())) {
271 return false;
272 }
273
joshualitt56995b52014-12-11 15:44:02 -0800274 // TODO remove the hint
joshualitt9b989322014-12-15 14:16:27 -0800275 const GrGeometryProcessor& other = that.cast<GrGeometryProcessor>();
276 if (fHasVertexColor && fOpaqueVertexColors != other.fOpaqueVertexColors) {
joshualitt2e3b3e32014-12-09 13:31:14 -0800277 return false;
278 }
279
joshualitt9b989322014-12-15 14:16:27 -0800280 // TODO this equality test should really be broken up, some of this can live on the batch
281 // tracker test and some of this should be in bundles
282 if (!this->onIsEqual(other)) {
joshualitt56995b52014-12-11 15:44:02 -0800283 return false;
284 }
285
joshualitt290c09b2014-12-19 13:45:20 -0800286 return this->onCanMakeEqual(mine, other, theirs);
bsalomon0e08fc12014-10-15 08:19:04 -0700287 }
joshualitt9b989322014-12-15 14:16:27 -0800288
289 // TODO we can remove color from the GrGeometryProcessor base class once we have bundles of
290 // primitive data
joshualitt56995b52014-12-11 15:44:02 -0800291 GrColor color() const { return fColor; }
joshualitt2e3b3e32014-12-09 13:31:14 -0800292
joshualitt9b989322014-12-15 14:16:27 -0800293 // TODO this is a total hack until the gp can do deferred geometry
joshualitt2dd1ae02014-12-03 06:24:10 -0800294 bool hasVertexColor() const { return fHasVertexColor; }
joshualitt9b989322014-12-15 14:16:27 -0800295
joshualitt56995b52014-12-11 15:44:02 -0800296 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE;
297 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE;
joshualitt2e3b3e32014-12-09 13:31:14 -0800298
joshualittb0a8a372014-09-23 09:50:21 -0700299protected:
joshualitt9b989322014-12-15 14:16:27 -0800300 /*
301 * An optional simple helper function to determine by what means the GrGeometryProcessor should
302 * use to provide color. If we are given an override color(ie the given overridecolor is NOT
303 * GrColor_ILLEGAL) then we must always emit that color(currently overrides are only supported
304 * via uniform, but with deferred Geometry we could use attributes). Otherwise, if our color is
305 * ignored then we should not emit a color. Lastly, if we don't have vertex colors then we must
306 * emit a color via uniform
307 * TODO this function changes quite a bit with deferred geometry. There the GrGeometryProcessor
308 * can upload a new color via attribute if needed.
309 */
joshualittd5a7db42015-01-27 15:39:06 -0800310 static GrGPInput GetColorInputType(GrColor* color, GrColor primitiveColor,
311 const GrPipelineInfo& init,
joshualitt9b989322014-12-15 14:16:27 -0800312 bool hasVertexColor) {
313 if (init.fColorIgnored) {
314 *color = GrColor_ILLEGAL;
315 return kIgnored_GrGPInput;
316 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
317 *color = init.fOverrideColor;
318 return kUniform_GrGPInput;
319 }
320
321 *color = primitiveColor;
322 if (hasVertexColor) {
323 return kAttribute_GrGPInput;
324 } else {
325 return kUniform_GrGPInput;
326 }
327 }
328
joshualittb0a8a372014-09-23 09:50:21 -0700329 /**
joshualitt2dd1ae02014-12-03 06:24:10 -0800330 * Subclasses call this from their constructor to register vertex attributes. Attributes
331 * will be padded to the nearest 4 bytes for performance reasons.
332 * TODO After deferred geometry, we should do all of this inline in GenerateGeometry alongside
joshualitt9b989322014-12-15 14:16:27 -0800333 * the struct used to actually populate the attributes. This is all extremely fragile, vertex
334 * attributes have to be added in the order they will appear in the struct which maps memory.
335 * The processor key should reflect the vertex attributes, or there lack thereof in the
336 * GrGeometryProcessor.
joshualittb0a8a372014-09-23 09:50:21 -0700337 */
joshualitt71c92602015-01-14 08:12:47 -0800338 const Attribute& addVertexAttrib(const Attribute& attribute) {
339 SkASSERT(fNumAttribs < kMaxVertexAttribs);
joshualitt2dd1ae02014-12-03 06:24:10 -0800340 fVertexStride += attribute.fOffset;
joshualitt71c92602015-01-14 08:12:47 -0800341 fAttribs[fNumAttribs] = attribute;
342 return fAttribs[fNumAttribs++];
joshualittb0a8a372014-09-23 09:50:21 -0700343 }
344
joshualitt74077b92014-10-24 11:26:03 -0700345 void setWillUseGeoShader() { fWillUseGeoShader = true; }
346
joshualitt2dd1ae02014-12-03 06:24:10 -0800347 // TODO hack see above
348 void setHasVertexColor() { fHasVertexColor = true; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800349 void setHasLocalCoords() { fHasLocalCoords = true; }
350
joshualitt56995b52014-12-11 15:44:02 -0800351 virtual void onGetInvariantOutputColor(GrInitInvariantOutput*) const {}
352 virtual void onGetInvariantOutputCoverage(GrInitInvariantOutput*) const = 0;
353
joshualittb0a8a372014-09-23 09:50:21 -0700354private:
joshualitt290c09b2014-12-19 13:45:20 -0800355 virtual bool onCanMakeEqual(const GrBatchTracker& mine,
356 const GrGeometryProcessor& that,
357 const GrBatchTracker& theirs) const = 0;
joshualittabb52a12015-01-13 15:02:10 -0800358
joshualitt9b989322014-12-15 14:16:27 -0800359 // TODO delete this when we have more advanced equality testing via bundles and the BT
bsalomon0e08fc12014-10-15 08:19:04 -0700360 virtual bool onIsEqual(const GrGeometryProcessor&) const = 0;
361
joshualittabb52a12015-01-13 15:02:10 -0800362 bool hasExplicitLocalCoords() const SK_OVERRIDE { return fHasLocalCoords; }
363
joshualitt2e3b3e32014-12-09 13:31:14 -0800364 GrColor fColor;
joshualitt56995b52014-12-11 15:44:02 -0800365 bool fOpaqueVertexColors;
joshualitt74077b92014-10-24 11:26:03 -0700366 bool fWillUseGeoShader;
joshualitt2dd1ae02014-12-03 06:24:10 -0800367 bool fHasVertexColor;
joshualitt2dd1ae02014-12-03 06:24:10 -0800368 bool fHasLocalCoords;
joshualittb0a8a372014-09-23 09:50:21 -0700369
joshualitt290c09b2014-12-19 13:45:20 -0800370 typedef GrPrimitiveProcessor INHERITED;
joshualittb0a8a372014-09-23 09:50:21 -0700371};
joshualitt56995b52014-12-11 15:44:02 -0800372
373/*
374 * The path equivalent of the GP. For now this just manages color. In the long term we plan on
375 * extending this class to handle all nvpr uniform / varying / program work.
376 */
377class GrPathProcessor : public GrPrimitiveProcessor {
378public:
joshualitt8059eb92014-12-29 15:10:07 -0800379 static GrPathProcessor* Create(GrColor color,
380 const SkMatrix& viewMatrix = SkMatrix::I(),
381 const SkMatrix& localMatrix = SkMatrix::I()) {
382 return SkNEW_ARGS(GrPathProcessor, (color, viewMatrix, localMatrix));
joshualitt56995b52014-12-11 15:44:02 -0800383 }
joshualitt9b989322014-12-15 14:16:27 -0800384
joshualittd5a7db42015-01-27 15:39:06 -0800385 void initBatchTracker(GrBatchTracker*, const GrPipelineInfo&) const SK_OVERRIDE;
joshualitt9b989322014-12-15 14:16:27 -0800386
387 bool canMakeEqual(const GrBatchTracker& mine,
388 const GrPrimitiveProcessor& that,
389 const GrBatchTracker& theirs) const SK_OVERRIDE;
joshualitt56995b52014-12-11 15:44:02 -0800390
391 const char* name() const SK_OVERRIDE { return "PathProcessor"; }
joshualitt9b989322014-12-15 14:16:27 -0800392
393 GrColor color() const { return fColor; }
394
joshualitt56995b52014-12-11 15:44:02 -0800395 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE;
396 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE;
397
joshualitt71c92602015-01-14 08:12:47 -0800398 bool willUseGeoShader() const SK_OVERRIDE { return false; }
399
joshualitt9b989322014-12-15 14:16:27 -0800400 virtual void getGLProcessorKey(const GrBatchTracker& bt,
401 const GrGLCaps& caps,
402 GrProcessorKeyBuilder* b) const SK_OVERRIDE;
403
joshualittabb52a12015-01-13 15:02:10 -0800404 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
405 const GrGLCaps& caps) const SK_OVERRIDE;
406
407protected:
408 GrPathProcessor(GrColor color, const SkMatrix& viewMatrix, const SkMatrix& localMatrix);
joshualitt9b989322014-12-15 14:16:27 -0800409
joshualitt56995b52014-12-11 15:44:02 -0800410private:
joshualittabb52a12015-01-13 15:02:10 -0800411 bool hasExplicitLocalCoords() const SK_OVERRIDE { return false; }
412
joshualitt56995b52014-12-11 15:44:02 -0800413 GrColor fColor;
414
joshualitt290c09b2014-12-19 13:45:20 -0800415 typedef GrPrimitiveProcessor INHERITED;
joshualitt56995b52014-12-11 15:44:02 -0800416};
joshualittabb52a12015-01-13 15:02:10 -0800417
joshualittb0a8a372014-09-23 09:50:21 -0700418#endif