diff options
-rw-r--r-- | include/core/SkXfermode.h | 4 | ||||
-rw-r--r-- | include/gpu/effects/GrCustomXfermode.h | 2 | ||||
-rw-r--r-- | include/gpu/effects/GrPorterDuffXferProcessor.h | 51 | ||||
-rw-r--r-- | src/core/SkXfermode.cpp | 8 | ||||
-rw-r--r-- | src/core/SkXfermode_proccoeff.h | 2 | ||||
-rw-r--r-- | src/effects/SkArithmeticMode.cpp | 4 | ||||
-rw-r--r-- | src/gpu/GrProcessor.cpp | 2 | ||||
-rw-r--r-- | src/gpu/SkGr.cpp | 2 | ||||
-rw-r--r-- | src/gpu/effects/GrCustomXfermode.cpp | 2 | ||||
-rw-r--r-- | src/gpu/effects/GrPorterDuffXferProcessor.cpp | 139 | ||||
-rw-r--r-- | tests/GrPorterDuffTest.cpp | 8 |
11 files changed, 174 insertions, 50 deletions
diff --git a/include/core/SkXfermode.h b/include/core/SkXfermode.h index cb9557f92c..4337d20b61 100644 --- a/include/core/SkXfermode.h +++ b/include/core/SkXfermode.h @@ -209,13 +209,13 @@ public: will install it and own a ref to it. Since the xfermode may or may not assign *xpf, the caller should set *xpf to NULL beforehand. XferProcessors cannot use a background texture. */ - virtual bool asXPFactory(GrXPFactory** xpf) const; + virtual bool asXPFactory(const GrXPFactory** xpf) const; /** Returns true if the xfermode can be expressed as an xfer processor factory (xpFactory). This helper calls the asXPFactory() virtual. If the xfermode is NULL, it is treated as kSrcOver_Mode. It is legal to call this with xpf param NULL to simply test the return value. */ - static bool AsXPFactory(SkXfermode*, GrXPFactory**); + static bool AsXPFactory(SkXfermode*, const GrXPFactory**); SK_TO_STRING_PUREVIRT() SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP() diff --git a/include/gpu/effects/GrCustomXfermode.h b/include/gpu/effects/GrCustomXfermode.h index bcbd5833ca..7fe930822a 100644 --- a/include/gpu/effects/GrCustomXfermode.h +++ b/include/gpu/effects/GrCustomXfermode.h @@ -18,7 +18,7 @@ class GrTexture; */ namespace GrCustomXfermode { bool IsSupportedMode(SkXfermode::Mode mode); - GrXPFactory* CreateXPFactory(SkXfermode::Mode mode); + const GrXPFactory* CreateXPFactory(SkXfermode::Mode mode); }; #endif diff --git a/include/gpu/effects/GrPorterDuffXferProcessor.h b/include/gpu/effects/GrPorterDuffXferProcessor.h index d297e32908..72faeb572c 100644 --- a/include/gpu/effects/GrPorterDuffXferProcessor.h +++ b/include/gpu/effects/GrPorterDuffXferProcessor.h @@ -14,9 +14,9 @@ class GrProcOptInfo; -class GrPorterDuffXPFactory : public GrXPFactory { +class GrPDXPFactory : public GrXPFactory { public: - static GrXPFactory* Create(SkXfermode::Mode mode); + static const GrXPFactory* Create(SkXfermode::Mode mode); bool supportsRGBCoverage(GrColor /*knownColor*/, uint32_t /*knownColorFlags*/) const override { return true; @@ -26,7 +26,7 @@ public: GrXPFactory::InvariantBlendedColor*) const override; private: - GrPorterDuffXPFactory(SkXfermode::Mode); + GrPDXPFactory(SkXfermode::Mode); GrXferProcessor* onCreateXferProcessor(const GrCaps& caps, const GrProcOptInfo& colorPOI, @@ -40,7 +40,7 @@ private: bool hasMixedSamples) const override; bool onIsEqual(const GrXPFactory& xpfBase) const override { - const GrPorterDuffXPFactory& xpf = xpfBase.cast<GrPorterDuffXPFactory>(); + const GrPDXPFactory& xpf = xpfBase.cast<GrPDXPFactory>(); return fXfermode == xpf.fXfermode; } @@ -53,4 +53,47 @@ private: typedef GrXPFactory INHERITED; }; +class GrSrcOverPDXPFactory : public GrXPFactory { +public: + GrSrcOverPDXPFactory(); + + bool supportsRGBCoverage(GrColor /*knownColor*/, uint32_t /*knownColorFlags*/) const override { + return true; + } + + void getInvariantBlendedColor(const GrProcOptInfo& colorPOI, + GrXPFactory::InvariantBlendedColor*) const override; + +private: + GrXferProcessor* onCreateXferProcessor(const GrCaps& caps, + const GrProcOptInfo& colorPOI, + const GrProcOptInfo& coveragePOI, + bool hasMixedSamples, + const DstTexture*) const override; + + bool willReadDstColor(const GrCaps& caps, + const GrProcOptInfo& colorPOI, + const GrProcOptInfo& coveragePOI, + bool hasMixedSamples) const override; + + bool onIsEqual(const GrXPFactory& /*xpfBase*/) const override { + return true; + } + + GR_DECLARE_XP_FACTORY_TEST; + + typedef GrXPFactory INHERITED; +}; + +namespace GrPorterDuffXPFactory { + const GrSrcOverPDXPFactory gSrcOverPDXPFactory; + + inline const GrXPFactory* Create(SkXfermode::Mode mode) { + if (SkXfermode::kSrcOver_Mode == mode) { + return SkRef(&gSrcOverPDXPFactory); + } + return GrPDXPFactory::Create(mode); + } +}; + #endif diff --git a/src/core/SkXfermode.cpp b/src/core/SkXfermode.cpp index 9083814d1a..ddfa80e197 100644 --- a/src/core/SkXfermode.cpp +++ b/src/core/SkXfermode.cpp @@ -656,7 +656,7 @@ bool SkXfermode::asFragmentProcessor(const GrFragmentProcessor**, return false; } -bool SkXfermode::asXPFactory(GrXPFactory**) const { +bool SkXfermode::asXPFactory(const GrXPFactory**) const { return false; } @@ -664,7 +664,7 @@ bool SkXfermode::asXPFactory(GrXPFactory**) const { #if SK_SUPPORT_GPU #include "effects/GrPorterDuffXferProcessor.h" -bool SkXfermode::AsXPFactory(SkXfermode* xfermode, GrXPFactory** xpf) { +bool SkXfermode::AsXPFactory(SkXfermode* xfermode, const GrXPFactory** xpf) { if (nullptr == xfermode) { if (xpf) { *xpf = GrPorterDuffXPFactory::Create(kSrcOver_Mode); @@ -675,7 +675,7 @@ bool SkXfermode::AsXPFactory(SkXfermode* xfermode, GrXPFactory** xpf) { } } #else -bool SkXfermode::AsXPFactory(SkXfermode* xfermode, GrXPFactory** xpf) { +bool SkXfermode::AsXPFactory(SkXfermode* xfermode, const GrXPFactory** xpf) { return false; } #endif @@ -932,7 +932,7 @@ bool SkProcCoeffXfermode::asFragmentProcessor(const GrFragmentProcessor** fp, return true; } -bool SkProcCoeffXfermode::asXPFactory(GrXPFactory** xp) const { +bool SkProcCoeffXfermode::asXPFactory(const GrXPFactory** xp) const { if (CANNOT_USE_COEFF != fSrcCoeff) { if (xp) { *xp = GrPorterDuffXPFactory::Create(fMode); diff --git a/src/core/SkXfermode_proccoeff.h b/src/core/SkXfermode_proccoeff.h index f86af2df01..e0d2743ac9 100644 --- a/src/core/SkXfermode_proccoeff.h +++ b/src/core/SkXfermode_proccoeff.h @@ -47,7 +47,7 @@ public: bool asFragmentProcessor(const GrFragmentProcessor**, const GrFragmentProcessor*) const override; - bool asXPFactory(GrXPFactory**) const override; + bool asXPFactory(const GrXPFactory**) const override; #endif SK_TO_STRING_OVERRIDE() diff --git a/src/effects/SkArithmeticMode.cpp b/src/effects/SkArithmeticMode.cpp index 87af82efdb..c8749b91bb 100644 --- a/src/effects/SkArithmeticMode.cpp +++ b/src/effects/SkArithmeticMode.cpp @@ -34,7 +34,7 @@ public: bool asFragmentProcessor(const GrFragmentProcessor**, const GrFragmentProcessor* dst) const override; - bool asXPFactory(GrXPFactory**) const override; + bool asXPFactory(const GrXPFactory**) const override; #endif private: @@ -248,7 +248,7 @@ bool SkArithmeticMode_scalar::asFragmentProcessor(const GrFragmentProcessor** fp return true; } -bool SkArithmeticMode_scalar::asXPFactory(GrXPFactory** xpf) const { +bool SkArithmeticMode_scalar::asXPFactory(const GrXPFactory** xpf) const { if (xpf) { *xpf = GrArithmeticXPFactory::Create(SkScalarToFloat(fK[0]), SkScalarToFloat(fK[1]), diff --git a/src/gpu/GrProcessor.cpp b/src/gpu/GrProcessor.cpp index 1db9d76fda..f4893ee66b 100644 --- a/src/gpu/GrProcessor.cpp +++ b/src/gpu/GrProcessor.cpp @@ -50,7 +50,7 @@ GrProcessorTestFactory<GrGeometryProcessor>::GetFactories() { */ static const int kFPFactoryCount = 38; static const int kGPFactoryCount = 14; -static const int kXPFactoryCount = 5; +static const int kXPFactoryCount = 6; template<> void GrProcessorTestFactory<GrFragmentProcessor>::VerifyFactoryCount() { diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp index 411b5b2b60..4f22472c82 100644 --- a/src/gpu/SkGr.cpp +++ b/src/gpu/SkGr.cpp @@ -488,7 +488,7 @@ static inline bool skpaint_to_grpaint_impl(GrContext* context, } SkXfermode* mode = skPaint.getXfermode(); - GrXPFactory* xpFactory = nullptr; + const GrXPFactory* xpFactory = nullptr; if (!SkXfermode::AsXPFactory(mode, &xpFactory)) { // Fall back to src-over // return false here? diff --git a/src/gpu/effects/GrCustomXfermode.cpp b/src/gpu/effects/GrCustomXfermode.cpp index 6b3a20ec2d..bb4b28ebbf 100644 --- a/src/gpu/effects/GrCustomXfermode.cpp +++ b/src/gpu/effects/GrCustomXfermode.cpp @@ -395,7 +395,7 @@ const GrXPFactory* CustomXPFactory::TestCreate(GrProcessorTestData* d) { /////////////////////////////////////////////////////////////////////////////// -GrXPFactory* GrCustomXfermode::CreateXPFactory(SkXfermode::Mode mode) { +const GrXPFactory* GrCustomXfermode::CreateXPFactory(SkXfermode::Mode mode) { if (!GrCustomXfermode::IsSupportedMode(mode)) { return nullptr; } else { diff --git a/src/gpu/effects/GrPorterDuffXferProcessor.cpp b/src/gpu/effects/GrPorterDuffXferProcessor.cpp index 4245caafed..f0682f480c 100644 --- a/src/gpu/effects/GrPorterDuffXferProcessor.cpp +++ b/src/gpu/effects/GrPorterDuffXferProcessor.cpp @@ -691,30 +691,30 @@ PDLCDXferProcessor::onGetOptimizations(const GrProcOptInfo& colorPOI, /////////////////////////////////////////////////////////////////////////////// -GrPorterDuffXPFactory::GrPorterDuffXPFactory(SkXfermode::Mode xfermode) +GrPDXPFactory::GrPDXPFactory(SkXfermode::Mode xfermode) : fXfermode(xfermode) { SkASSERT(fXfermode <= SkXfermode::kLastCoeffMode); - this->initClassID<GrPorterDuffXPFactory>(); + this->initClassID<GrPDXPFactory>(); } -GrXPFactory* GrPorterDuffXPFactory::Create(SkXfermode::Mode xfermode) { - static GrPorterDuffXPFactory gClearPDXPF(SkXfermode::kClear_Mode); - static GrPorterDuffXPFactory gSrcPDXPF(SkXfermode::kSrc_Mode); - static GrPorterDuffXPFactory gDstPDXPF(SkXfermode::kDst_Mode); - static GrPorterDuffXPFactory gSrcOverPDXPF(SkXfermode::kSrcOver_Mode); - static GrPorterDuffXPFactory gDstOverPDXPF(SkXfermode::kDstOver_Mode); - static GrPorterDuffXPFactory gSrcInPDXPF(SkXfermode::kSrcIn_Mode); - static GrPorterDuffXPFactory gDstInPDXPF(SkXfermode::kDstIn_Mode); - static GrPorterDuffXPFactory gSrcOutPDXPF(SkXfermode::kSrcOut_Mode); - static GrPorterDuffXPFactory gDstOutPDXPF(SkXfermode::kDstOut_Mode); - static GrPorterDuffXPFactory gSrcATopPDXPF(SkXfermode::kSrcATop_Mode); - static GrPorterDuffXPFactory gDstATopPDXPF(SkXfermode::kDstATop_Mode); - static GrPorterDuffXPFactory gXorPDXPF(SkXfermode::kXor_Mode); - static GrPorterDuffXPFactory gPlusPDXPF(SkXfermode::kPlus_Mode); - static GrPorterDuffXPFactory gModulatePDXPF(SkXfermode::kModulate_Mode); - static GrPorterDuffXPFactory gScreenPDXPF(SkXfermode::kScreen_Mode); - - static GrPorterDuffXPFactory* gFactories[] = { +const GrXPFactory* GrPDXPFactory::Create(SkXfermode::Mode xfermode) { + static const GrPDXPFactory gClearPDXPF(SkXfermode::kClear_Mode); + static const GrPDXPFactory gSrcPDXPF(SkXfermode::kSrc_Mode); + static const GrPDXPFactory gDstPDXPF(SkXfermode::kDst_Mode); + static const GrPDXPFactory gSrcOverPDXPF(SkXfermode::kSrcOver_Mode); + static const GrPDXPFactory gDstOverPDXPF(SkXfermode::kDstOver_Mode); + static const GrPDXPFactory gSrcInPDXPF(SkXfermode::kSrcIn_Mode); + static const GrPDXPFactory gDstInPDXPF(SkXfermode::kDstIn_Mode); + static const GrPDXPFactory gSrcOutPDXPF(SkXfermode::kSrcOut_Mode); + static const GrPDXPFactory gDstOutPDXPF(SkXfermode::kDstOut_Mode); + static const GrPDXPFactory gSrcATopPDXPF(SkXfermode::kSrcATop_Mode); + static const GrPDXPFactory gDstATopPDXPF(SkXfermode::kDstATop_Mode); + static const GrPDXPFactory gXorPDXPF(SkXfermode::kXor_Mode); + static const GrPDXPFactory gPlusPDXPF(SkXfermode::kPlus_Mode); + static const GrPDXPFactory gModulatePDXPF(SkXfermode::kModulate_Mode); + static const GrPDXPFactory gScreenPDXPF(SkXfermode::kScreen_Mode); + + static const GrPDXPFactory* gFactories[] = { &gClearPDXPF, &gSrcPDXPF, &gDstPDXPF, &gSrcOverPDXPF, &gDstOverPDXPF, &gSrcInPDXPF, &gDstInPDXPF, &gSrcOutPDXPF, &gDstOutPDXPF, &gSrcATopPDXPF, &gDstATopPDXPF, &gXorPDXPF, &gPlusPDXPF, &gModulatePDXPF, &gScreenPDXPF @@ -728,7 +728,7 @@ GrXPFactory* GrPorterDuffXPFactory::Create(SkXfermode::Mode xfermode) { } GrXferProcessor* -GrPorterDuffXPFactory::onCreateXferProcessor(const GrCaps& caps, +GrPDXPFactory::onCreateXferProcessor(const GrCaps& caps, const GrProcOptInfo& colorPOI, const GrProcOptInfo& covPOI, bool hasMixedSamples, @@ -757,7 +757,7 @@ GrPorterDuffXPFactory::onCreateXferProcessor(const GrCaps& caps, return new PorterDuffXferProcessor(blendFormula); } -void GrPorterDuffXPFactory::getInvariantBlendedColor(const GrProcOptInfo& colorPOI, +void GrPDXPFactory::getInvariantBlendedColor(const GrProcOptInfo& colorPOI, InvariantBlendedColor* blendedColor) const { // Find the blended color info based on the formula that does not have coverage. BlendFormula colorFormula = gBlendTable[colorPOI.isOpaque()][0][fXfermode]; @@ -788,7 +788,7 @@ void GrPorterDuffXPFactory::getInvariantBlendedColor(const GrProcOptInfo& colorP } } -bool GrPorterDuffXPFactory::willReadDstColor(const GrCaps& caps, +bool GrPDXPFactory::willReadDstColor(const GrCaps& caps, const GrProcOptInfo& colorPOI, const GrProcOptInfo& covPOI, bool hasMixedSamples) const { @@ -812,16 +812,16 @@ bool GrPorterDuffXPFactory::willReadDstColor(const GrCaps& caps, return get_blend_formula(colorPOI, covPOI, hasMixedSamples, fXfermode).hasSecondaryOutput(); } -GR_DEFINE_XP_FACTORY_TEST(GrPorterDuffXPFactory); +GR_DEFINE_XP_FACTORY_TEST(GrPDXPFactory); -const GrXPFactory* GrPorterDuffXPFactory::TestCreate(GrProcessorTestData* d) { +const GrXPFactory* GrPDXPFactory::TestCreate(GrProcessorTestData* d) { SkXfermode::Mode mode = SkXfermode::Mode(d->fRandom->nextULessThan(SkXfermode::kLastCoeffMode)); - return GrPorterDuffXPFactory::Create(mode); + return GrPDXPFactory::Create(mode); } -void GrPorterDuffXPFactory::TestGetXPOutputTypes(const GrXferProcessor* xp, - int* outPrimary, - int* outSecondary) { +void GrPDXPFactory::TestGetXPOutputTypes(const GrXferProcessor* xp, + int* outPrimary, + int* outSecondary) { if (!!strcmp(xp->name(), "Porter Duff")) { *outPrimary = *outSecondary = -1; return; @@ -830,3 +830,84 @@ void GrPorterDuffXPFactory::TestGetXPOutputTypes(const GrXferProcessor* xp, *outPrimary = blendFormula.fPrimaryOutputType; *outSecondary = blendFormula.fSecondaryOutputType; } + +//////////////////////////////////////////////////////////////////////////////// + +GrSrcOverPDXPFactory::GrSrcOverPDXPFactory() { + this->initClassID<GrSrcOverPDXPFactory>(); +} + +GrXferProcessor* +GrSrcOverPDXPFactory::onCreateXferProcessor(const GrCaps& caps, + const GrProcOptInfo& colorPOI, + const GrProcOptInfo& covPOI, + bool hasMixedSamples, + const DstTexture* dstTexture) const { + BlendFormula blendFormula; + if (covPOI.isFourChannelOutput()) { + if (kRGBA_GrColorComponentFlags == colorPOI.validFlags() && + !caps.shaderCaps()->dualSourceBlendingSupport() && + !caps.shaderCaps()->dstReadInShaderSupport()) { + // If we don't have dual source blending or in shader dst reads, we fall back to this + // trick for rendering SrcOver LCD text instead of doing a dst copy. + SkASSERT(!dstTexture || !dstTexture->texture()); + return PDLCDXferProcessor::Create(SkXfermode::kSrcOver_Mode, colorPOI); + } + blendFormula = get_lcd_blend_formula(covPOI, SkXfermode::kSrcOver_Mode); + } else { + blendFormula = get_blend_formula(colorPOI, covPOI, hasMixedSamples, + SkXfermode::kSrcOver_Mode); + } + + if (blendFormula.hasSecondaryOutput() && !caps.shaderCaps()->dualSourceBlendingSupport()) { + return new ShaderPDXferProcessor(dstTexture, hasMixedSamples, SkXfermode::kSrcOver_Mode); + } + + SkASSERT(!dstTexture || !dstTexture->texture()); + return new PorterDuffXferProcessor(blendFormula); +} + +void GrSrcOverPDXPFactory::getInvariantBlendedColor(const GrProcOptInfo& colorPOI, + InvariantBlendedColor* blendedColor) const { + if (!colorPOI.isOpaque()) { + blendedColor->fWillBlendWithDst = true; + blendedColor->fKnownColorFlags = kNone_GrColorComponentFlags; + return; + } + + blendedColor->fWillBlendWithDst = false; + + blendedColor->fKnownColor = colorPOI.color(); + blendedColor->fKnownColorFlags = colorPOI.validFlags(); +} + +bool GrSrcOverPDXPFactory::willReadDstColor(const GrCaps& caps, + const GrProcOptInfo& colorPOI, + const GrProcOptInfo& covPOI, + bool hasMixedSamples) const { + if (caps.shaderCaps()->dualSourceBlendingSupport()) { + return false; + } + + // When we have four channel coverage we always need to read the dst in order to correctly + // blend. The one exception is when we are using srcover mode and we know the input color into + // the XP. + if (covPOI.isFourChannelOutput()) { + if (kRGBA_GrColorComponentFlags == colorPOI.validFlags() && + !caps.shaderCaps()->dstReadInShaderSupport()) { + return false; + } + return get_lcd_blend_formula(covPOI, SkXfermode::kSrcOver_Mode).hasSecondaryOutput(); + } + // We fallback on the shader XP when the blend formula would use dual source blending but we + // don't have support for it. + return get_blend_formula(colorPOI, covPOI, + hasMixedSamples, SkXfermode::kSrcOver_Mode).hasSecondaryOutput(); +} + +GR_DEFINE_XP_FACTORY_TEST(GrSrcOverPDXPFactory); + +const GrXPFactory* GrSrcOverPDXPFactory::TestCreate(GrProcessorTestData* d) { + return SkRef(&GrPorterDuffXPFactory::gSrcOverPDXPFactory); +} + diff --git a/tests/GrPorterDuffTest.cpp b/tests/GrPorterDuffTest.cpp index d984f92142..d0c91cb265 100644 --- a/tests/GrPorterDuffTest.cpp +++ b/tests/GrPorterDuffTest.cpp @@ -77,7 +77,7 @@ public: struct XPInfo { XPInfo(skiatest::Reporter* reporter, SkXfermode::Mode xfermode, const GrCaps& caps, const GrProcOptInfo& colorPOI, const GrProcOptInfo& covPOI) { - SkAutoTUnref<GrXPFactory> xpf(GrPorterDuffXPFactory::Create(xfermode)); + SkAutoTUnref<const GrXPFactory> xpf(GrPorterDuffXPFactory::Create(xfermode)); SkAutoTUnref<GrXferProcessor> xp( xpf->createXferProcessor(colorPOI, covPOI, false, nullptr, caps)); TEST_ASSERT(!xpf->willNeedDstTexture(caps, colorPOI, covPOI, false)); @@ -97,7 +97,7 @@ public: }; static void GetXPOutputTypes(const GrXferProcessor* xp, int* outPrimary, int* outSecondary) { - GrPorterDuffXPFactory::TestGetXPOutputTypes(xp, outPrimary, outSecondary); + GrPDXPFactory::TestGetXPOutputTypes(xp, outPrimary, outSecondary); } }; @@ -1124,7 +1124,7 @@ static void test_lcd_coverage_fallback_case(skiatest::Reporter* reporter, const SkASSERT(kRGBA_GrColorComponentFlags == colorPOI.validFlags()); SkASSERT(covPOI.isFourChannelOutput()); - SkAutoTUnref<GrXPFactory> xpf(GrPorterDuffXPFactory::Create(SkXfermode::kSrcOver_Mode)); + SkAutoTUnref<const GrXPFactory> xpf(GrPorterDuffXPFactory::Create(SkXfermode::kSrcOver_Mode)); TEST_ASSERT(!xpf->willNeedDstTexture(caps, colorPOI, covPOI, false)); SkAutoTUnref<GrXferProcessor> xp( @@ -1200,7 +1200,7 @@ static void test_no_dual_source_blending(skiatest::Reporter* reporter) { } for (int m = 0; m <= SkXfermode::kLastCoeffMode; m++) { SkXfermode::Mode xfermode = static_cast<SkXfermode::Mode>(m); - SkAutoTUnref<GrXPFactory> xpf(GrPorterDuffXPFactory::Create(xfermode)); + SkAutoTUnref<const GrXPFactory> xpf(GrPorterDuffXPFactory::Create(xfermode)); GrXferProcessor::DstTexture* dstTexture = xpf->willNeedDstTexture(caps, colorPOI, covPOI, false) ? &fakeDstTexture : 0; SkAutoTUnref<GrXferProcessor> xp( |