#include "GrColor.h"
#include "GrProcessorUnitTest.h"
#include "GrProgramElement.h"
-#include "GrShaderVar.h"
#include "GrTextureAccess.h"
-#include "GrTypesPriv.h"
-#include "SkString.h"
+#include "SkMath.h"
-class GrBackendProcessorFactory;
class GrContext;
class GrCoordTransform;
-/** Provides custom vertex shader, fragment shader, uniform data for a particular stage of the
- Ganesh shading pipeline.
- Subclasses must have a function that produces a human-readable name:
- static const char* Name();
- GrProcessor objects *must* be immutable: after being constructed, their fields may not change.
+/** Provides custom shader code to the Ganesh shading pipeline. GrProcessor objects *must* be
+ immutable: after being constructed, their fields may not change.
Dynamically allocated GrProcessors are managed by a per-thread memory pool. The ref count of an
- effect must reach 0 before the thread terminates and the pool is destroyed. To create a static
- effect use the macro GR_CREATE_STATIC_EFFECT declared below.
- */
+ processor must reach 0 before the thread terminates and the pool is destroyed. To create a
+ static processor use the helper macro GR_CREATE_STATIC_PROCESSOR declared below.
+ */
class GrProcessor : public GrProgramElement {
public:
SK_DECLARE_INST_COUNT(GrProcessor)
virtual ~GrProcessor();
+ struct InvariantOutput{
+ InvariantOutput() : fColor(0), fValidFlags(0), fIsSingleComponent(false),
+ fNonMulStageFound(false), fWillUseInputColor(true) {}
+
+ enum ReadInput {
+ kWill_ReadInput,
+ kWillNot_ReadInput,
+ };
+
+ void mulByUnknownOpaqueColor() {
+ if (this->isOpaque()) {
+ fValidFlags = kA_GrColorComponentFlag;
+ fIsSingleComponent = false;
+ } else {
+ // Since the current state is not opaque we no longer care if the color being
+ // multiplied is opaque.
+ this->mulByUnknownColor();
+ }
+ }
+
+ void mulByUnknownColor() {
+ if (this->hasZeroAlpha()) {
+ this->internalSetToTransparentBlack();
+ } else {
+ this->internalSetToUnknown();
+ }
+ }
+
+ void mulByUnknownAlpha() {
+ if (this->hasZeroAlpha()) {
+ this->internalSetToTransparentBlack();
+ } else {
+ // We don't need to change fIsSingleComponent in this case
+ fValidFlags = 0;
+ }
+ }
+
+ void mulByKnownAlpha(uint8_t alpha) {
+ if (this->hasZeroAlpha() || 0 == alpha) {
+ this->internalSetToTransparentBlack();
+ } else {
+ if (alpha != 255) {
+ // Multiply color by alpha
+ fColor = GrColorPackRGBA(SkMulDiv255Round(GrColorUnpackR(fColor), alpha),
+ SkMulDiv255Round(GrColorUnpackG(fColor), alpha),
+ SkMulDiv255Round(GrColorUnpackB(fColor), alpha),
+ SkMulDiv255Round(GrColorUnpackA(fColor), alpha));
+ }
+ }
+ }
+
+ void invalidateComponents(uint8_t invalidateFlags, ReadInput readsInput) {
+ fValidFlags &= ~invalidateFlags;
+ fIsSingleComponent = false;
+ if (kWillNot_ReadInput == readsInput) {
+ fWillUseInputColor = false;
+ }
+ }
+
+ void setToOther(uint8_t validFlags, GrColor color, ReadInput readsInput) {
+ fValidFlags = validFlags;
+ fColor = color;
+ fIsSingleComponent = false;
+ fNonMulStageFound = true;
+ if (kWillNot_ReadInput == readsInput) {
+ fWillUseInputColor = false;
+ }
+ }
+
+ void setToUnknown(ReadInput readsInput) {
+ this->internalSetToUnknown();
+ fNonMulStageFound= true;
+ if (kWillNot_ReadInput == readsInput) {
+ fWillUseInputColor = false;
+ }
+ }
+
+ bool isOpaque() const {
+ return ((fValidFlags & kA_GrColorComponentFlag) && 0xFF == GrColorUnpackA(fColor));
+ }
+
+ bool isSolidWhite() const {
+ return (fValidFlags == kRGBA_GrColorComponentFlags && 0xFFFFFFFF == fColor);
+ }
+
+ GrColor color() const { return fColor; }
+ uint8_t validFlags() const { return fValidFlags; }
+
+ /**
+ * If isSingleComponent is true, then the flag values for r, g, b, and a must all be the
+ * same. If the flags are all set then all color components must be equal.
+ */
+ SkDEBUGCODE(void validate() const;)
+
+ private:
+ void internalSetToTransparentBlack() {
+ fValidFlags = kRGBA_GrColorComponentFlags;
+ fColor = 0;
+ fIsSingleComponent = true;
+ }
+
+ void internalSetToUnknown() {
+ fValidFlags = 0;
+ fIsSingleComponent = false;
+ }
+
+ bool hasZeroAlpha() const {
+ return ((fValidFlags & kA_GrColorComponentFlag) && 0 == GrColorUnpackA(fColor));
+ }
+
+ SkDEBUGCODE(bool colorComponentsAllEqual() const;)
+ /**
+ * If alpha is valid, check that any valid R,G,B values are <= A
+ */
+ SkDEBUGCODE(bool validPreMulColor() const;)
+
+ // Friended class that have "controller" code which loop over stages calling
+ // computeInvarianteOutput(). These controllers may need to manually adjust the internal
+ // members of InvariantOutput
+ friend class GrDrawState;
+ friend class GrOptDrawState;
+ friend class GrPaint;
+ friend class GrProcessor;
+
+ GrColor fColor;
+ uint32_t fValidFlags;
+ bool fIsSingleComponent;
+ bool fNonMulStageFound;
+ bool fWillUseInputColor;
+ };
+
/**
- * This function is used to perform optimizations. When called the color and validFlags params
- * indicate whether the input components to this effect in the FS will have known values.
- * validFlags is a bitfield of GrColorComponentFlags. The function updates both params to
- * indicate known values of its output. A component of the color param only has meaning if the
- * corresponding bit in validFlags is set.
+ * This function is used to perform optimizations. When called the invarientOuput param
+ * indicate whether the input components to this processor in the FS will have known values.
+ * In inout the validFlags member is a bitfield of GrColorComponentFlags. The isSingleComponent
+ * member indicates whether the input will be 1 or 4 bytes. The function updates the members of
+ * inout to indicate known values of its output. A component of the color member only has
+ * meaning if the corresponding bit in validFlags is set.
*/
- virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const = 0;
+ void computeInvariantOutput(InvariantOutput* inout) const {
+ inout->fWillUseInputColor = true;
+ this->onComputeInvariantOutput(inout);
+#ifdef SK_DEBUG
+ inout->validate();
+#endif
+ }
/** This object, besides creating back-end-specific helper objects, is used for run-time-type-
identification. The factory should be an instance of templated class,
- GrTBackendEffectFactory. It is templated on the subclass of GrProcessor. The subclass must
- have a nested type (or typedef) named GLProcessor which will be the subclass of
+ GrTBackendProcessorFactory. It is templated on the subclass of GrProcessor. The subclass
+ must have a nested type (or typedef) named GLProcessor which will be the subclass of
GrGLProcessor created by the factory.
Example:
- class MyCustomEffect : public GrProcessor {
+ class MyCustomProcessor : public GrProcessor {
...
- virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
- return GrTBackendEffectFactory<MyCustomEffect>::getInstance();
+ virtual const GrBackendProcessorFactory& getFactory() const SK_OVERRIDE {
+ return GrTBackendProcessorFactory<MyCustomProcessor>::getInstance();
}
...
};
*/
virtual const GrBackendProcessorFactory& getFactory() const = 0;
- /** Returns true if this and other effect conservatively draw identically. It can only return
- true when the two effects are of the same subclass (i.e. they return the same object from
- from getFactory()).
-
- A return value of true from isEqual() should not be used to test whether the effects would
- generate the same shader code. To test for identical code generation use the effects' keys
- computed by the GrBackendEffectFactory.
- */
- bool isEqual(const GrProcessor& other) const {
- if (&this->getFactory() != &other.getFactory()) {
- return false;
- }
- bool result = this->onIsEqual(other);
-#ifdef SK_DEBUG
- if (result) {
- this->assertEquality(other);
- }
-#endif
- return result;
- }
-
- /** Human-meaningful string to identify this effect; may be embedded
+ /** Human-meaningful string to identify this prcoessor; may be embedded
in generated shader code. */
const char* name() const;
- int numTransforms() const { return fCoordTransforms.count(); }
-
- /** Returns the coordinate transformation at index. index must be valid according to
- numTransforms(). */
- const GrCoordTransform& coordTransform(int index) const { return *fCoordTransforms[index]; }
-
int numTextures() const { return fTextureAccesses.count(); }
/** Returns the access pattern for the texture at index. index must be valid according to
/** Shortcut for textureAccess(index).texture(); */
GrTexture* texture(int index) const { return this->textureAccess(index).getTexture(); }
- /** Will this effect read the fragment position? */
+ /** Will this processor read the fragment position? */
bool willReadFragmentPosition() const { return fWillReadFragmentPosition; }
void* operator new(size_t size);
template <typename T> const T& cast() const { return *static_cast<const T*>(this); }
protected:
- /**
- * Subclasses call this from their constructor to register coordinate transformations. The
- * effect subclass manages the lifetime of the transformations (this function only stores a
- * pointer). The GrCoordTransform is typically a member field of the GrProcessor subclass. When
- * the matrix has perspective, the transformed coordinates will have 3 components. Otherwise
- * they'll have 2. This must only be called from the constructor because GrProcessors are
- * immutable.
- */
- void addCoordTransform(const GrCoordTransform* coordTransform);
+ GrProcessor() : fWillReadFragmentPosition(false) {}
/**
- * Subclasses call this from their constructor to register GrTextureAccesses. The effect
+ * Subclasses call this from their constructor to register GrTextureAccesses. The processor
* subclass manages the lifetime of the accesses (this function only stores a pointer). The
* GrTextureAccess is typically a member field of the GrProcessor subclass. This must only be
* called from the constructor because GrProcessors are immutable.
*/
void addTextureAccess(const GrTextureAccess* textureAccess);
- GrProcessor()
- : fWillReadFragmentPosition(false) {}
+ bool hasSameTextureAccesses(const GrProcessor&) const;
/**
- * If the effect will generate a backend-specific effect that will read the fragment position
- * in the FS then it must call this method from its constructor. Otherwise, the request to
- * access the fragment position will be denied.
+ * If the prcoessor will generate a backend-specific processor that will read the fragment
+ * position in the FS then it must call this method from its constructor. Otherwise, the
+ * request to access the fragment position will be denied.
*/
void setWillReadFragmentPosition() { fWillReadFragmentPosition = true; }
private:
- SkDEBUGCODE(void assertEquality(const GrProcessor& other) const;)
- /** Subclass implements this to support isEqual(). It will only be called if it is known that
- the two effects are of the same subclass (i.e. they return the same object from
- getFactory()).*/
- virtual bool onIsEqual(const GrProcessor& other) const = 0;
-
- friend class GrGeometryProcessor; // to set fRequiresVertexShader and build fVertexAttribTypes.
+ /**
+ * Subclass implements this to support getConstantColorComponents(...).
+ */
+ virtual void onComputeInvariantOutput(InvariantOutput* inout) const = 0;
- SkSTArray<4, const GrCoordTransform*, true> fCoordTransforms;
SkSTArray<4, const GrTextureAccess*, true> fTextureAccesses;
bool fWillReadFragmentPosition;
typedef GrProgramElement INHERITED;
};
-class GrFragmentProcessor : public GrProcessor {
-public:
- GrFragmentProcessor()
- : INHERITED()
- , fWillReadDstColor(false)
- , fWillUseInputColor(true) {}
-
- virtual const GrBackendFragmentProcessorFactory& getFactory() const = 0;
-
- /** Will this effect read the destination pixel value? */
- bool willReadDstColor() const { return fWillReadDstColor; }
-
- /** Will this effect read the source color value? */
- bool willUseInputColor() const { return fWillUseInputColor; }
-
-protected:
- /**
- * If the effect subclass will read the destination pixel value then it must call this function
- * from its constructor. Otherwise, when its generated backend-specific effect class attempts
- * to generate code that reads the destination pixel it will fail.
- */
- void setWillReadDstColor() { fWillReadDstColor = true; }
-
- /**
- * If the effect will generate a result that does not depend on the input color value then it
- * must call this function from its constructor. Otherwise, when its generated backend-specific
- * code might fail during variable binding due to unused variables.
- */
- void setWillNotUseInputColor() { fWillUseInputColor = false; }
-
-private:
- bool fWillReadDstColor;
- bool fWillUseInputColor;
-
- typedef GrProcessor INHERITED;
-};
/**
- * This creates an effect outside of the effect memory pool. The effect's destructor will be called
- * at global destruction time. NAME will be the name of the created GrProcessor.
+ * This creates a processor outside of the memory pool. The processor's destructor will be called
+ * at global destruction time. NAME will be the name of the created instance.
*/
-#define GR_CREATE_STATIC_FRAGMENT_PROCESSOR(NAME, EFFECT_CLASS, ARGS) \
-static SkAlignedSStorage<sizeof(EFFECT_CLASS)> g_##NAME##_Storage; \
-static GrFragmentProcessor* \
-NAME SkNEW_PLACEMENT_ARGS(g_##NAME##_Storage.get(), EFFECT_CLASS, ARGS); \
-static SkAutoTDestroy<GrFragmentProcessor> NAME##_ad(NAME);
+#define GR_CREATE_STATIC_PROCESSOR(NAME, PROC_CLASS, ARGS) \
+static SkAlignedSStorage<sizeof(PROC_CLASS)> g_##NAME##_Storage; \
+static PROC_CLASS* NAME SkNEW_PLACEMENT_ARGS(g_##NAME##_Storage.get(), PROC_CLASS, ARGS); \
+static SkAutoTDestroy<GrProcessor> NAME##_ad(NAME);
#endif