diff --git a/gyp/gpu.gyp b/gyp/gpu.gyp index 410a5b7ace..7080ebbea2 100644 --- a/gyp/gpu.gyp +++ b/gyp/gpu.gyp @@ -189,6 +189,8 @@ '../src/gpu/GrAllocPool.cpp', '../src/gpu/GrAtlas.cpp', '../src/gpu/GrAtlas.h', + '../src/gpu/GrBatchedTextContext.cpp', + '../src/gpu/GrBatchedTextContext.h', '../src/gpu/GrBinHashKey.h', '../src/gpu/GrBufferAllocPool.cpp', '../src/gpu/GrBufferAllocPool.h', @@ -196,6 +198,8 @@ '../src/gpu/GrContext.cpp', '../src/gpu/GrDefaultPathRenderer.cpp', '../src/gpu/GrDefaultPathRenderer.h', + '../src/gpu/GrDefaultTextContext.cpp', + '../src/gpu/GrDefaultTextContext.h', '../src/gpu/GrDrawState.h', '../src/gpu/GrDrawTarget.cpp', '../src/gpu/GrDrawTarget.h', @@ -233,7 +237,6 @@ '../src/gpu/GrTDArray.h', '../src/gpu/GrTesselatedPathRenderer.cpp', '../src/gpu/GrTesselatedPathRenderer.h', - '../src/gpu/GrTextContext.cpp', '../src/gpu/GrTextStrike.cpp', '../src/gpu/GrTextStrike.h', '../src/gpu/GrTextStrike_impl.h', diff --git a/include/gpu/GrTextContext.h b/include/gpu/GrTextContext.h index 5983e358c8..588ae6ee34 100644 --- a/include/gpu/GrTextContext.h +++ b/include/gpu/GrTextContext.h @@ -12,57 +12,99 @@ #define GrTextContext_DEFINED #include "GrGlyph.h" -#include "GrPaint.h" #include "GrMatrix.h" +#include "GrRefCnt.h" -struct GrGpuTextVertex; class GrContext; -class GrTextStrike; class GrFontScaler; -class GrDrawTarget; +class GrPaint; -class GrTextContext { -public: - GrTextContext(GrContext*, - const GrPaint& paint, - const GrMatrix* extMatrix = NULL); - ~GrTextContext(); +class SkGpuDevice; +class SkPaint; - void drawPackedGlyph(GrGlyph::PackedID, GrFixed left, GrFixed top, - GrFontScaler*); - - void flush(); // optional; automatically called by destructor - -private: - GrPaint fPaint; - GrVertexLayout fVertexLayout; +class GrTextContext: public GrRefCnt { +protected: GrContext* fContext; - GrDrawTarget* fDrawTarget; - GrMatrix fExtMatrix; - GrFontScaler* fScaler; - GrTextStrike* fStrike; +public: + /** + * To use a text context it must be wrapped in an AutoFinish. AutoFinish's + * destructor ensures all drawing is flushed to the GrContext. + */ + class AutoFinish { + public: + AutoFinish(GrTextContext* textContext, GrContext* context, + const GrPaint&, const GrMatrix* extMatrix); + ~AutoFinish(); + GrTextContext* getTextContext() const; - inline void flushGlyphs(); - void setupDrawTarget(); - - enum { - kMinRequestedGlyphs = 1, - kDefaultRequestedGlyphs = 64, - kMinRequestedVerts = kMinRequestedGlyphs * 4, - kDefaultRequestedVerts = kDefaultRequestedGlyphs * 4, + private: + GrTextContext* fTextContext; }; - GrGpuTextVertex* fVertices; + virtual void drawPackedGlyph(GrGlyph::PackedID, GrFixed left, GrFixed top, + GrFontScaler*) = 0; - int32_t fMaxVertices; - GrTexture* fCurrTexture; - int fCurrVertex; + virtual ~GrTextContext() {} - GrIRect fClipRect; - GrMatrix fOrigViewMatrix; // restore previous viewmatrix +protected: + GrTextContext() { + fContext = NULL; + } + + bool isValid() const { + return (NULL != fContext); + } + + /** + * Initialize the object. + * + * Before call to this method, the instance is considered to be in + * invalid state. I.e. call to any method other than isValid will result in + * undefined behaviour. + * + * @see finish + */ + virtual void init(GrContext* context, const GrPaint&, + const GrMatrix* extMatrix) { + fContext = context; + } + + /** + * Reset the object to invalid state. + * + * After call to this method, the instance is considered to be in + * invalid state. + * + * It might be brought back to a valid state by calling init. + * + * @see init + */ + virtual void finish() { + fContext = NULL; + } + +private: + typedef GrRefCnt INHERITED; }; +inline GrTextContext::AutoFinish::AutoFinish(GrTextContext* textContext, + GrContext* context, + const GrPaint& grPaint, + const GrMatrix* extMatrix) { + GrAssert(NULL != textContext); + fTextContext = textContext; + fTextContext->ref(); + fTextContext->init(context, grPaint, extMatrix); +} + +inline GrTextContext::AutoFinish::~AutoFinish() { + fTextContext->finish(); + fTextContext->unref(); +} + +inline GrTextContext* GrTextContext::AutoFinish::getTextContext() const { + return fTextContext; +} + #endif - - diff --git a/include/gpu/SkGpuDevice.h b/include/gpu/SkGpuDevice.h index 46c15d6ce7..24e6e5adb1 100644 --- a/include/gpu/SkGpuDevice.h +++ b/include/gpu/SkGpuDevice.h @@ -147,6 +147,8 @@ private: bool fNeedClear; bool fNeedPrepareRenderTarget; + GrTextContext* fTextContext; + // called from rt and tex cons void initFromRenderTarget(GrContext*, GrRenderTarget*); @@ -167,6 +169,11 @@ private: void internalDrawBitmap(const SkDraw&, const SkBitmap&, const SkIRect&, const SkMatrix&, GrPaint* grPaint); + /** + * Returns non-initialized instance. + */ + GrTextContext* getTextContext(); + typedef SkDevice INHERITED; }; diff --git a/src/gpu/GrBatchedTextContext.cpp b/src/gpu/GrBatchedTextContext.cpp new file mode 100644 index 0000000000..3b3a4ba0b9 --- /dev/null +++ b/src/gpu/GrBatchedTextContext.cpp @@ -0,0 +1,97 @@ + +/* + * Copyright 2010 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + + +#include "GrBatchedTextContext.h" +#include "GrContext.h" +#include "GrDrawTarget.h" +#include "GrIndexBuffer.h" +#include "GrTextContext.h" + + +GrBatchedTextContext::GrBatchedTextContext() { +} + +GrBatchedTextContext::~GrBatchedTextContext() { +} + +void GrBatchedTextContext::init(GrContext* context, + const GrPaint& grPaint, + const GrMatrix* extMatrix) { + this->INHERITED::init(context, grPaint, extMatrix); + fGrPaint = grPaint; + fDrawTarget = fContext->getTextTarget(fGrPaint); + + fMaxVertices = 0; + fCurrTexture = NULL; + fCurrVertex = 0; +} + +void GrBatchedTextContext::finish() { + fDrawTarget = NULL; + + this->INHERITED::finish(); +} + +void GrBatchedTextContext::reset() { + GrAssert(this->isValid()); + fDrawTarget->resetVertexSource(); + fMaxVertices = 0; + fCurrVertex = 0; + fCurrTexture->unref(); + fCurrTexture = NULL; +} + +void GrBatchedTextContext::prepareForGlyph(GrTexture* texture) { + GrAssert(this->isValid()); + GrAssert(texture); + if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) { + this->flush(); + fCurrTexture = texture; + fCurrTexture->ref(); + } +} + +void GrBatchedTextContext::setupVertexBuff(void** vertexBuff, + GrVertexLayout vertexLayout) { + GrAssert(this->isValid()); + if (NULL == *vertexBuff) { + // If we need to reserve vertices allow the draw target to suggest + // a number of verts to reserve and whether to perform a flush. + fMaxVertices = kMinRequestedVerts; + bool flush = fDrawTarget->geometryHints(vertexLayout, + &fMaxVertices, + NULL); + if (flush) { + this->flush(); + fContext->flushText(); + fDrawTarget = fContext->getTextTarget(fGrPaint); + fMaxVertices = kDefaultRequestedVerts; + // ignore return, no point in flushing again. + fDrawTarget->geometryHints(vertexLayout, + &fMaxVertices, + NULL); + } + + int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads(); + if (fMaxVertices < kMinRequestedVerts) { + fMaxVertices = kDefaultRequestedVerts; + } else if (fMaxVertices > maxQuadVertices) { + // don't exceed the limit of the index buffer + fMaxVertices = maxQuadVertices; + } + bool success = fDrawTarget->reserveVertexAndIndexSpace( + vertexLayout, + fMaxVertices, + 0, + vertexBuff, + NULL); + GrAlwaysAssert(success); + } +} diff --git a/src/gpu/GrBatchedTextContext.h b/src/gpu/GrBatchedTextContext.h new file mode 100644 index 0000000000..c0a136ba26 --- /dev/null +++ b/src/gpu/GrBatchedTextContext.h @@ -0,0 +1,82 @@ + +/* + * Copyright 2010 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + + +#ifndef GrBatchedTextContext_DEFINED +#define GrBatchedTextContext_DEFINED + +#include "GrPaint.h" +#include "GrTextContext.h" + +class GrDrawTarget; +class GrTexture; + +/** + * Base class for TextContexts that can batch multiple glyphs into single draw. + * + * Every glyph is encoded on a single texture. + * Every glyph is enclosed within a quad (formed by triangle fan) represented + * by 4 vertices. + */ +class GrBatchedTextContext: public GrTextContext { +public: + virtual ~GrBatchedTextContext(); + +protected: + enum { + kMinRequestedGlyphs = 1, + kDefaultRequestedGlyphs = 64, + kMinRequestedVerts = kMinRequestedGlyphs * 4, + kDefaultRequestedVerts = kDefaultRequestedGlyphs * 4, + kGlyphMaskStage = GrPaint::kTotalStages, + }; + + GrPaint fGrPaint; + GrDrawTarget* fDrawTarget; + + int32_t fMaxVertices; + GrTexture* fCurrTexture; + int fCurrVertex; + + GrBatchedTextContext(); + virtual void init(GrContext* context, const GrPaint&, + const GrMatrix* extMatrix) SK_OVERRIDE; + virtual void finish() SK_OVERRIDE; + + /** + * Prepare to add another glyph to buffer. The glyph is encoded on the + * texture provided. Make sure we are using the right texture (or switch + * to a new texture) and that our buffer is big enough. + */ + void prepareForGlyph(GrTexture*); + + /** + * Flush the buffer. Called when switching textures. + * Must be called in finish() method of all derived classes. + */ + virtual void flush() = 0; + + /** + * Set up a buffer to hold vertices of given layout. + * If NULL != *vertexBuff, don't do anything. + * Might cause flushing, if draw target suggests it. + */ + void setupVertexBuff(void** vertexBuff, GrVertexLayout vertexLayout); + + /** + * Reset after flushing. + */ + void reset(); + +private: + + typedef GrTextContext INHERITED; +}; + +#endif diff --git a/src/gpu/GrTextContext.cpp b/src/gpu/GrDefaultTextContext.cpp similarity index 70% rename from src/gpu/GrTextContext.cpp rename to src/gpu/GrDefaultTextContext.cpp index 62c894e275..bde06c6905 100644 --- a/src/gpu/GrTextContext.cpp +++ b/src/gpu/GrDefaultTextContext.cpp @@ -8,21 +8,18 @@ -#include "GrTextContext.h" #include "GrAtlas.h" +#include "GrDefaultTextContext.h" #include "GrContext.h" +#include "GrDrawTarget.h" +#include "GrFontScaler.h" +#include "GrGpuVertex.h" +#include "GrTemplates.h" #include "GrTextStrike.h" #include "GrTextStrike_impl.h" -#include "GrFontScaler.h" -#include "GrIndexBuffer.h" -#include "GrGpuVertex.h" -#include "GrDrawTarget.h" -enum { - kGlyphMaskStage = GrPaint::kTotalStages, -}; - -void GrTextContext::flushGlyphs() { +void GrDefaultTextContext::flushGlyphs() { + GrAssert(this->isValid()); if (fCurrVertex > 0) { GrDrawTarget::AutoStateRestore asr(fDrawTarget); GrDrawState* drawState = fDrawTarget->drawState(); @@ -42,44 +39,44 @@ void GrTextContext::flushGlyphs() { drawState->setTexture(kGlyphMaskStage, fCurrTexture); if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) { - if (kOne_BlendCoeff != fPaint.fSrcBlendCoeff || - kISA_BlendCoeff != fPaint.fDstBlendCoeff || - fPaint.hasTexture()) { + if (kOne_BlendCoeff != fGrPaint.fSrcBlendCoeff || + kISA_BlendCoeff != fGrPaint.fDstBlendCoeff || + fGrPaint.hasTexture()) { GrPrintf("LCD Text will not draw correctly.\n"); } // setup blend so that we get mask * paintColor + (1-mask)*dstColor - drawState->setBlendConstant(fPaint.fColor); + drawState->setBlendConstant(fGrPaint.fColor); drawState->setBlendFunc(kConstC_BlendCoeff, kISC_BlendCoeff); // don't modulate by the paint's color in the frag since we're // already doing it via the blend const. drawState->setColor(0xffffffff); } else { // set back to normal in case we took LCD path previously. - drawState->setBlendFunc(fPaint.fSrcBlendCoeff, fPaint.fDstBlendCoeff); - drawState->setColor(fPaint.fColor); + drawState->setBlendFunc(fGrPaint.fSrcBlendCoeff, fGrPaint.fDstBlendCoeff); + drawState->setColor(fGrPaint.fColor); } fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer()); fDrawTarget->drawIndexed(kTriangles_PrimitiveType, 0, 0, fCurrVertex, nIndices); - fDrawTarget->resetVertexSource(); fVertices = NULL; - fMaxVertices = 0; - fCurrVertex = 0; - fCurrTexture->unref(); - fCurrTexture = NULL; + this->INHERITED::reset(); } } -GrTextContext::GrTextContext(GrContext* context, - const GrPaint& paint, - const GrMatrix* extMatrix) : fPaint(paint) { - fContext = context; - fStrike = NULL; +GrDefaultTextContext::GrDefaultTextContext() { +} - fCurrTexture = NULL; - fCurrVertex = 0; +GrDefaultTextContext::~GrDefaultTextContext() { +} + +void GrDefaultTextContext::init(GrContext* context, + const GrPaint& paint, + const GrMatrix* extMatrix) { + this->INHERITED::init(context, paint, extMatrix); + + fStrike = NULL; if (NULL != extMatrix) { fExtMatrix = *extMatrix; @@ -102,7 +99,7 @@ GrTextContext::GrTextContext(GrContext* context, } // save the context's original matrix off and restore in destructor - // this must be done before getTextTarget. + // getTextTarget should be called after that fOrigViewMatrix = fContext->getMatrix(); fContext->setMatrix(fExtMatrix); @@ -118,26 +115,27 @@ GrTextContext::GrTextContext(GrContext* context, bool invVMComputed = false; GrMatrix invVM; for (int t = 0; t < GrPaint::kMaxTextures; ++t) { - if (NULL != fPaint.getTexture(t)) { + if (NULL != fGrPaint.getTexture(t)) { if (invVMComputed || fOrigViewMatrix.invert(&invVM)) { invVMComputed = true; - fPaint.textureSampler(t)->preConcatMatrix(invVM); + fGrPaint.textureSampler(t)->preConcatMatrix(invVM); } } } for (int m = 0; m < GrPaint::kMaxMasks; ++m) { - if (NULL != fPaint.getMask(m)) { + if (NULL != fGrPaint.getMask(m)) { if (invVMComputed || fOrigViewMatrix.invert(&invVM)) { invVMComputed = true; - fPaint.maskSampler(m)->preConcatMatrix(invVM); + fGrPaint.maskSampler(m)->preConcatMatrix(invVM); } } } - fDrawTarget = fContext->getTextTarget(fPaint); + // this has been already done in the baseclass, but we need to repeat + // due to new matrix + fDrawTarget = fContext->getTextTarget(fGrPaint); fVertices = NULL; - fMaxVertices = 0; fVertexLayout = GrDrawTarget::kTextFormat_VertexLayoutBit | @@ -155,12 +153,17 @@ GrTextContext::GrTextContext(GrContext* context, } } -GrTextContext::~GrTextContext() { - this->flushGlyphs(); +void GrDefaultTextContext::finish() { + this->flush(); + + fStrike = NULL; fContext->setMatrix(fOrigViewMatrix); + + this->INHERITED::finish(); } -void GrTextContext::flush() { +void GrDefaultTextContext::flush() { + GrAssert(this->isValid()); this->flushGlyphs(); } @@ -172,9 +175,10 @@ static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b, v[3 * stride].setI(r, t); } -void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed, +void GrDefaultTextContext::drawPackedGlyph(GrGlyph::PackedID packed, GrFixed vx, GrFixed vy, GrFontScaler* scaler) { + GrAssert(this->isValid()); if (NULL == fStrike) { fStrike = fContext->getFontCache()->getStrike(scaler); } @@ -229,7 +233,7 @@ void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed, GrPoint translate; translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)), GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop))); - fContext->drawPath(fPaint, *glyph->fPath, kWinding_PathFill, + fContext->drawPath(fGrPaint, *glyph->fPath, kWinding_PathFill, &translate); return; } @@ -242,47 +246,10 @@ HAS_ATLAS: height = GrIntToFixed(height); GrTexture* texture = glyph->fAtlas->texture(); - GrAssert(texture); + this->prepareForGlyph(texture); - if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) { - this->flushGlyphs(); - fCurrTexture = texture; - fCurrTexture->ref(); - } - - if (NULL == fVertices) { - // If we need to reserve vertices allow the draw target to suggest - // a number of verts to reserve and whether to perform a flush. - fMaxVertices = kMinRequestedVerts; - bool flush = fDrawTarget->geometryHints(fVertexLayout, - &fMaxVertices, - NULL); - if (flush) { - this->flushGlyphs(); - fContext->flushText(); - fDrawTarget = fContext->getTextTarget(fPaint); - fMaxVertices = kDefaultRequestedVerts; - // ignore return, no point in flushing again. - fDrawTarget->geometryHints(fVertexLayout, - &fMaxVertices, - NULL); - } - - int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads(); - if (fMaxVertices < kMinRequestedVerts) { - fMaxVertices = kDefaultRequestedVerts; - } else if (fMaxVertices > maxQuadVertices) { - // don't exceed the limit of the index buffer - fMaxVertices = maxQuadVertices; - } - bool success = fDrawTarget->reserveVertexAndIndexSpace( - fVertexLayout, - fMaxVertices, - 0, - GrTCast(&fVertices), - NULL); - GrAlwaysAssert(success); - } + this->setupVertexBuff(GrTCast(&fVertices), + fVertexLayout); GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX); GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY); @@ -311,5 +278,3 @@ HAS_ATLAS: #endif fCurrVertex += 4; } - - diff --git a/src/gpu/GrDefaultTextContext.h b/src/gpu/GrDefaultTextContext.h new file mode 100644 index 0000000000..71a7f51651 --- /dev/null +++ b/src/gpu/GrDefaultTextContext.h @@ -0,0 +1,49 @@ + +/* + * Copyright 2010 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + + +#ifndef GrDefaultTextContext_DEFINED +#define GrDefaultTextContext_DEFINED + +#include "GrBatchedTextContext.h" + +struct GrGpuTextVertex; +class GrTextStrike; + +class GrDefaultTextContext: public GrBatchedTextContext { +public: + GrDefaultTextContext(); + ~GrDefaultTextContext(); + + virtual void drawPackedGlyph(GrGlyph::PackedID, GrFixed left, GrFixed top, + GrFontScaler*) SK_OVERRIDE; + +protected: + virtual void flush() SK_OVERRIDE; + virtual void init(GrContext* context, const GrPaint&, + const GrMatrix* extMatrix) SK_OVERRIDE; + virtual void finish() SK_OVERRIDE; + +private: + GrVertexLayout fVertexLayout; + GrGpuTextVertex* fVertices; + GrIRect fClipRect; + + GrFontScaler* fScaler; + GrTextStrike* fStrike; + + GrMatrix fExtMatrix; + GrMatrix fOrigViewMatrix; // restore previous viewmatrix + + inline void flushGlyphs(); + + typedef GrBatchedTextContext INHERITED; +}; + +#endif diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index 9769cf462e..2e82ad9fcd 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -9,6 +9,7 @@ #include "GrContext.h" +#include "GrDefaultTextContext.h" #include "GrTextContext.h" #include "SkGpuDevice.h" @@ -184,6 +185,8 @@ void SkGpuDevice::initFromRenderTarget(GrContext* context, pr = new SkGrRenderTargetPixelRef(fRenderTarget); } this->setPixelRef(pr, 0)->unref(); + + fTextContext = NULL; } SkGpuDevice::SkGpuDevice(GrContext* context, SkBitmap::Config config, int width, @@ -245,6 +248,8 @@ SkGpuDevice::SkGpuDevice(GrContext* context, SkBitmap::Config config, int width, width, height); GrAssert(false); } + + fTextContext = NULL; } SkGpuDevice::~SkGpuDevice() { @@ -260,6 +265,10 @@ SkGpuDevice::~SkGpuDevice() { fContext->unlockTexture(fCache); } fContext->unref(); + + if (NULL != fTextContext) { + fTextContext->unref(); + } } /////////////////////////////////////////////////////////////////////////////// @@ -1595,8 +1604,9 @@ void SkGpuDevice::drawText(const SkDraw& draw, const void* text, &grPaint)) { return; } - GrTextContext context(fContext, grPaint, draw.fExtMatrix); - myDraw.fProcs = this->initDrawForText(&context); + GrTextContext::AutoFinish txtCtxAF(this->getTextContext(), fContext, + grPaint, draw.fExtMatrix); + myDraw.fProcs = this->initDrawForText(txtCtxAF.getTextContext()); this->INHERITED::drawText(myDraw, text, byteLength, x, y, paint); } } @@ -1624,9 +1634,9 @@ void SkGpuDevice::drawPosText(const SkDraw& draw, const void* text, &grPaint)) { return; } - - GrTextContext context(fContext, grPaint, draw.fExtMatrix); - myDraw.fProcs = this->initDrawForText(&context); + GrTextContext::AutoFinish txtCtxAF(this->getTextContext(), fContext, + grPaint, draw.fExtMatrix); + myDraw.fProcs = this->initDrawForText(txtCtxAF.getTextContext()); this->INHERITED::drawPosText(myDraw, text, byteLength, pos, constY, scalarsPerPos, paint); } @@ -1742,3 +1752,9 @@ SkDevice* SkGpuDevice::onCreateCompatibleDevice(SkBitmap::Config config, width, height, usage)); } +GrTextContext* SkGpuDevice::getTextContext() { + if (NULL == fTextContext) { + fTextContext = new GrDefaultTextContext(); + } + return fTextContext; +}