| /* | |
| * 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 "GrDrawTarget.h" | |
| #include "GrAARectRenderer.h" | |
| #include "GrBatch.h" | |
| #include "GrCaps.h" | |
| #include "GrContext.h" | |
| #include "GrPath.h" | |
| #include "GrPipeline.h" | |
| #include "GrMemoryPool.h" | |
| #include "GrRectBatch.h" | |
| #include "GrRenderTarget.h" | |
| #include "GrRenderTargetPriv.h" | |
| #include "GrSurfacePriv.h" | |
| #include "GrTemplates.h" | |
| #include "GrTexture.h" | |
| #include "GrVertexBuffer.h" | |
| #include "SkStrokeRec.h" | |
| //////////////////////////////////////////////////////////////////////////////// | |
| #define DEBUG_INVAL_BUFFER 0xdeadcafe | |
| #define DEBUG_INVAL_START_IDX -1 | |
| GrDrawTarget::GrDrawTarget(GrContext* context) | |
| : fContext(context) | |
| , fCaps(SkRef(context->caps())) | |
| , fGpuTraceMarkerCount(0) | |
| , fFlushing(false) { | |
| SkASSERT(context); | |
| } | |
| //////////////////////////////////////////////////////////////////////////////// | |
| bool GrDrawTarget::setupDstReadIfNecessary(const GrPipelineBuilder& pipelineBuilder, | |
| const GrProcOptInfo& colorPOI, | |
| const GrProcOptInfo& coveragePOI, | |
| GrXferProcessor::DstTexture* dstTexture, | |
| const SkRect* drawBounds) { | |
| if (!pipelineBuilder.willXPNeedDstTexture(*this->caps(), colorPOI, coveragePOI)) { | |
| return true; | |
| } | |
| GrRenderTarget* rt = pipelineBuilder.getRenderTarget(); | |
| if (this->caps()->textureBarrierSupport()) { | |
| if (GrTexture* rtTex = rt->asTexture()) { | |
| // The render target is a texture, so we can read from it directly in the shader. The XP | |
| // will be responsible to detect this situation and request a texture barrier. | |
| dstTexture->setTexture(rtTex); | |
| dstTexture->setOffset(0, 0); | |
| return true; | |
| } | |
| } | |
| SkIRect copyRect; | |
| pipelineBuilder.clip().getConservativeBounds(rt, ©Rect); | |
| if (drawBounds) { | |
| SkIRect drawIBounds; | |
| drawBounds->roundOut(&drawIBounds); | |
| if (!copyRect.intersect(drawIBounds)) { | |
| #ifdef SK_DEBUG | |
| GrCapsDebugf(fCaps, "Missed an early reject. " | |
| "Bailing on draw from setupDstReadIfNecessary.\n"); | |
| #endif | |
| return false; | |
| } | |
| } else { | |
| #ifdef SK_DEBUG | |
| //SkDebugf("No dev bounds when dst copy is made.\n"); | |
| #endif | |
| } | |
| // MSAA consideration: When there is support for reading MSAA samples in the shader we could | |
| // have per-sample dst values by making the copy multisampled. | |
| GrSurfaceDesc desc; | |
| if (!this->getGpu()->initCopySurfaceDstDesc(rt, &desc)) { | |
| desc.fOrigin = kDefault_GrSurfaceOrigin; | |
| desc.fFlags = kRenderTarget_GrSurfaceFlag; | |
| desc.fConfig = rt->config(); | |
| } | |
| desc.fWidth = copyRect.width(); | |
| desc.fHeight = copyRect.height(); | |
| SkAutoTUnref<GrTexture> copy(fContext->textureProvider()->refScratchTexture(desc, | |
| GrTextureProvider::kApprox_ScratchTexMatch)); | |
| if (!copy) { | |
| SkDebugf("Failed to create temporary copy of destination texture.\n"); | |
| return false; | |
| } | |
| SkIPoint dstPoint = {0, 0}; | |
| if (this->copySurface(copy, rt, copyRect, dstPoint)) { | |
| dstTexture->setTexture(copy); | |
| dstTexture->setOffset(copyRect.fLeft, copyRect.fTop); | |
| return true; | |
| } else { | |
| return false; | |
| } | |
| } | |
| void GrDrawTarget::flush() { | |
| if (fFlushing) { | |
| return; | |
| } | |
| fFlushing = true; | |
| this->getGpu()->saveActiveTraceMarkers(); | |
| this->onFlush(); | |
| this->getGpu()->restoreActiveTraceMarkers(); | |
| fFlushing = false; | |
| this->reset(); | |
| } | |
| void GrDrawTarget::drawBatch(GrPipelineBuilder* pipelineBuilder, | |
| GrBatch* batch) { | |
| SkASSERT(pipelineBuilder); | |
| // TODO some kind of checkdraw, but not at this level | |
| // Setup clip | |
| GrScissorState scissorState; | |
| GrPipelineBuilder::AutoRestoreFragmentProcessors arfp; | |
| GrPipelineBuilder::AutoRestoreStencil ars; | |
| if (!this->setupClip(pipelineBuilder, &arfp, &ars, &scissorState, &batch->bounds())) { | |
| return; | |
| } | |
| // Batch bounds are tight, so for dev copies | |
| // TODO move this into setupDstReadIfNecessary when paths are in batch | |
| SkRect bounds = batch->bounds(); | |
| bounds.outset(0.5f, 0.5f); | |
| GrDrawTarget::PipelineInfo pipelineInfo(pipelineBuilder, &scissorState, batch, &bounds, | |
| this); | |
| if (pipelineInfo.mustSkipDraw()) { | |
| return; | |
| } | |
| this->onDrawBatch(batch, pipelineInfo); | |
| } | |
| static const GrStencilSettings& winding_path_stencil_settings() { | |
| GR_STATIC_CONST_SAME_STENCIL_STRUCT(gSettings, | |
| kIncClamp_StencilOp, | |
| kIncClamp_StencilOp, | |
| kAlwaysIfInClip_StencilFunc, | |
| 0xFFFF, 0xFFFF, 0xFFFF); | |
| return *GR_CONST_STENCIL_SETTINGS_PTR_FROM_STRUCT_PTR(&gSettings); | |
| } | |
| static const GrStencilSettings& even_odd_path_stencil_settings() { | |
| GR_STATIC_CONST_SAME_STENCIL_STRUCT(gSettings, | |
| kInvert_StencilOp, | |
| kInvert_StencilOp, | |
| kAlwaysIfInClip_StencilFunc, | |
| 0xFFFF, 0xFFFF, 0xFFFF); | |
| return *GR_CONST_STENCIL_SETTINGS_PTR_FROM_STRUCT_PTR(&gSettings); | |
| } | |
| void GrDrawTarget::getPathStencilSettingsForFilltype(GrPathRendering::FillType fill, | |
| const GrStencilAttachment* sb, | |
| GrStencilSettings* outStencilSettings) { | |
| switch (fill) { | |
| default: | |
| SkFAIL("Unexpected path fill."); | |
| case GrPathRendering::kWinding_FillType: | |
| *outStencilSettings = winding_path_stencil_settings(); | |
| break; | |
| case GrPathRendering::kEvenOdd_FillType: | |
| *outStencilSettings = even_odd_path_stencil_settings(); | |
| break; | |
| } | |
| this->clipMaskManager()->adjustPathStencilParams(sb, outStencilSettings); | |
| } | |
| void GrDrawTarget::stencilPath(GrPipelineBuilder* pipelineBuilder, | |
| const GrPathProcessor* pathProc, | |
| const GrPath* path, | |
| GrPathRendering::FillType fill) { | |
| // TODO: extract portions of checkDraw that are relevant to path stenciling. | |
| SkASSERT(path); | |
| SkASSERT(this->caps()->shaderCaps()->pathRenderingSupport()); | |
| SkASSERT(pipelineBuilder); | |
| // Setup clip | |
| GrScissorState scissorState; | |
| GrPipelineBuilder::AutoRestoreFragmentProcessors arfp; | |
| GrPipelineBuilder::AutoRestoreStencil ars; | |
| if (!this->setupClip(pipelineBuilder, &arfp, &ars, &scissorState, NULL)) { | |
| return; | |
| } | |
| // set stencil settings for path | |
| GrStencilSettings stencilSettings; | |
| GrRenderTarget* rt = pipelineBuilder->getRenderTarget(); | |
| GrStencilAttachment* sb = rt->renderTargetPriv().attachStencilAttachment(); | |
| this->getPathStencilSettingsForFilltype(fill, sb, &stencilSettings); | |
| this->onStencilPath(*pipelineBuilder, pathProc, path, scissorState, stencilSettings); | |
| } | |
| void GrDrawTarget::drawPath(GrPipelineBuilder* pipelineBuilder, | |
| const GrPathProcessor* pathProc, | |
| const GrPath* path, | |
| GrPathRendering::FillType fill) { | |
| // TODO: extract portions of checkDraw that are relevant to path rendering. | |
| SkASSERT(path); | |
| SkASSERT(this->caps()->shaderCaps()->pathRenderingSupport()); | |
| SkASSERT(pipelineBuilder); | |
| SkRect devBounds = path->getBounds(); | |
| pathProc->viewMatrix().mapRect(&devBounds); | |
| // Setup clip | |
| GrScissorState scissorState; | |
| GrPipelineBuilder::AutoRestoreFragmentProcessors arfp; | |
| GrPipelineBuilder::AutoRestoreStencil ars; | |
| if (!this->setupClip(pipelineBuilder, &arfp, &ars, &scissorState, &devBounds)) { | |
| return; | |
| } | |
| // set stencil settings for path | |
| GrStencilSettings stencilSettings; | |
| GrRenderTarget* rt = pipelineBuilder->getRenderTarget(); | |
| GrStencilAttachment* sb = rt->renderTargetPriv().attachStencilAttachment(); | |
| this->getPathStencilSettingsForFilltype(fill, sb, &stencilSettings); | |
| GrDrawTarget::PipelineInfo pipelineInfo(pipelineBuilder, &scissorState, pathProc, &devBounds, | |
| this); | |
| if (pipelineInfo.mustSkipDraw()) { | |
| return; | |
| } | |
| this->onDrawPath(pathProc, path, stencilSettings, pipelineInfo); | |
| } | |
| void GrDrawTarget::drawPaths(GrPipelineBuilder* pipelineBuilder, | |
| const GrPathProcessor* pathProc, | |
| const GrPathRange* pathRange, | |
| const void* indices, | |
| PathIndexType indexType, | |
| const float transformValues[], | |
| PathTransformType transformType, | |
| int count, | |
| GrPathRendering::FillType fill) { | |
| SkASSERT(this->caps()->shaderCaps()->pathRenderingSupport()); | |
| SkASSERT(pathRange); | |
| SkASSERT(indices); | |
| SkASSERT(0 == reinterpret_cast<long>(indices) % GrPathRange::PathIndexSizeInBytes(indexType)); | |
| SkASSERT(transformValues); | |
| SkASSERT(pipelineBuilder); | |
| // Setup clip | |
| GrScissorState scissorState; | |
| GrPipelineBuilder::AutoRestoreFragmentProcessors arfp; | |
| GrPipelineBuilder::AutoRestoreStencil ars; | |
| if (!this->setupClip(pipelineBuilder, &arfp, &ars, &scissorState, NULL)) { | |
| return; | |
| } | |
| // set stencil settings for path | |
| GrStencilSettings stencilSettings; | |
| GrRenderTarget* rt = pipelineBuilder->getRenderTarget(); | |
| GrStencilAttachment* sb = rt->renderTargetPriv().attachStencilAttachment(); | |
| this->getPathStencilSettingsForFilltype(fill, sb, &stencilSettings); | |
| // Don't compute a bounding box for dst copy texture, we'll opt | |
| // instead for it to just copy the entire dst. Realistically this is a moot | |
| // point, because any context that supports NV_path_rendering will also | |
| // support NV_blend_equation_advanced. | |
| GrDrawTarget::PipelineInfo pipelineInfo(pipelineBuilder, &scissorState, pathProc, NULL, this); | |
| if (pipelineInfo.mustSkipDraw()) { | |
| return; | |
| } | |
| this->onDrawPaths(pathProc, pathRange, indices, indexType, transformValues, | |
| transformType, count, stencilSettings, pipelineInfo); | |
| } | |
| void GrDrawTarget::drawBWRect(GrPipelineBuilder* pipelineBuilder, | |
| GrColor color, | |
| const SkMatrix& viewMatrix, | |
| const SkRect& rect, | |
| const SkRect* localRect, | |
| const SkMatrix* localMatrix) { | |
| SkAutoTUnref<GrBatch> batch(GrRectBatch::Create(color, viewMatrix, rect, localRect, | |
| localMatrix)); | |
| this->drawBatch(pipelineBuilder, batch); | |
| } | |
| void GrDrawTarget::drawAARect(GrPipelineBuilder* pipelineBuilder, | |
| GrColor color, | |
| const SkMatrix& viewMatrix, | |
| const SkRect& rect, | |
| const SkRect& devRect) { | |
| GrAARectRenderer::FillAARect(this, pipelineBuilder, color, viewMatrix, rect, devRect); | |
| } | |
| void GrDrawTarget::clear(const SkIRect* rect, | |
| GrColor color, | |
| bool canIgnoreRect, | |
| GrRenderTarget* renderTarget) { | |
| if (fCaps->useDrawInsteadOfClear()) { | |
| // This works around a driver bug with clear by drawing a rect instead. | |
| // The driver will ignore a clear if it is the only thing rendered to a | |
| // target before the target is read. | |
| SkIRect rtRect = SkIRect::MakeWH(renderTarget->width(), renderTarget->height()); | |
| if (NULL == rect || canIgnoreRect || rect->contains(rtRect)) { | |
| rect = &rtRect; | |
| // We first issue a discard() since that may help tilers. | |
| this->discard(renderTarget); | |
| } | |
| GrPipelineBuilder pipelineBuilder; | |
| pipelineBuilder.setRenderTarget(renderTarget); | |
| this->drawSimpleRect(&pipelineBuilder, color, SkMatrix::I(), *rect); | |
| } else { | |
| this->onClear(rect, color, canIgnoreRect, renderTarget); | |
| } | |
| } | |
| typedef GrTraceMarkerSet::Iter TMIter; | |
| void GrDrawTarget::saveActiveTraceMarkers() { | |
| if (this->caps()->gpuTracingSupport()) { | |
| SkASSERT(0 == fStoredTraceMarkers.count()); | |
| fStoredTraceMarkers.addSet(fActiveTraceMarkers); | |
| for (TMIter iter = fStoredTraceMarkers.begin(); iter != fStoredTraceMarkers.end(); ++iter) { | |
| this->removeGpuTraceMarker(&(*iter)); | |
| } | |
| } | |
| } | |
| void GrDrawTarget::restoreActiveTraceMarkers() { | |
| if (this->caps()->gpuTracingSupport()) { | |
| SkASSERT(0 == fActiveTraceMarkers.count()); | |
| for (TMIter iter = fStoredTraceMarkers.begin(); iter != fStoredTraceMarkers.end(); ++iter) { | |
| this->addGpuTraceMarker(&(*iter)); | |
| } | |
| for (TMIter iter = fActiveTraceMarkers.begin(); iter != fActiveTraceMarkers.end(); ++iter) { | |
| this->fStoredTraceMarkers.remove(*iter); | |
| } | |
| } | |
| } | |
| void GrDrawTarget::addGpuTraceMarker(const GrGpuTraceMarker* marker) { | |
| if (this->caps()->gpuTracingSupport()) { | |
| SkASSERT(fGpuTraceMarkerCount >= 0); | |
| this->fActiveTraceMarkers.add(*marker); | |
| ++fGpuTraceMarkerCount; | |
| } | |
| } | |
| void GrDrawTarget::removeGpuTraceMarker(const GrGpuTraceMarker* marker) { | |
| if (this->caps()->gpuTracingSupport()) { | |
| SkASSERT(fGpuTraceMarkerCount >= 1); | |
| this->fActiveTraceMarkers.remove(*marker); | |
| --fGpuTraceMarkerCount; | |
| } | |
| } | |
| //////////////////////////////////////////////////////////////////////////////// | |
| namespace { | |
| // returns true if the read/written rect intersects the src/dst and false if not. | |
| bool clip_srcrect_and_dstpoint(const GrSurface* dst, | |
| const GrSurface* src, | |
| const SkIRect& srcRect, | |
| const SkIPoint& dstPoint, | |
| SkIRect* clippedSrcRect, | |
| SkIPoint* clippedDstPoint) { | |
| *clippedSrcRect = srcRect; | |
| *clippedDstPoint = dstPoint; | |
| // clip the left edge to src and dst bounds, adjusting dstPoint if necessary | |
| if (clippedSrcRect->fLeft < 0) { | |
| clippedDstPoint->fX -= clippedSrcRect->fLeft; | |
| clippedSrcRect->fLeft = 0; | |
| } | |
| if (clippedDstPoint->fX < 0) { | |
| clippedSrcRect->fLeft -= clippedDstPoint->fX; | |
| clippedDstPoint->fX = 0; | |
| } | |
| // clip the top edge to src and dst bounds, adjusting dstPoint if necessary | |
| if (clippedSrcRect->fTop < 0) { | |
| clippedDstPoint->fY -= clippedSrcRect->fTop; | |
| clippedSrcRect->fTop = 0; | |
| } | |
| if (clippedDstPoint->fY < 0) { | |
| clippedSrcRect->fTop -= clippedDstPoint->fY; | |
| clippedDstPoint->fY = 0; | |
| } | |
| // clip the right edge to the src and dst bounds. | |
| if (clippedSrcRect->fRight > src->width()) { | |
| clippedSrcRect->fRight = src->width(); | |
| } | |
| if (clippedDstPoint->fX + clippedSrcRect->width() > dst->width()) { | |
| clippedSrcRect->fRight = clippedSrcRect->fLeft + dst->width() - clippedDstPoint->fX; | |
| } | |
| // clip the bottom edge to the src and dst bounds. | |
| if (clippedSrcRect->fBottom > src->height()) { | |
| clippedSrcRect->fBottom = src->height(); | |
| } | |
| if (clippedDstPoint->fY + clippedSrcRect->height() > dst->height()) { | |
| clippedSrcRect->fBottom = clippedSrcRect->fTop + dst->height() - clippedDstPoint->fY; | |
| } | |
| // The above clipping steps may have inverted the rect if it didn't intersect either the src or | |
| // dst bounds. | |
| return !clippedSrcRect->isEmpty(); | |
| } | |
| } | |
| bool GrDrawTarget::copySurface(GrSurface* dst, | |
| GrSurface* src, | |
| const SkIRect& srcRect, | |
| const SkIPoint& dstPoint) { | |
| SkASSERT(dst); | |
| SkASSERT(src); | |
| SkIRect clippedSrcRect; | |
| SkIPoint clippedDstPoint; | |
| // If the rect is outside the src or dst then we've already succeeded. | |
| if (!clip_srcrect_and_dstpoint(dst, | |
| src, | |
| srcRect, | |
| dstPoint, | |
| &clippedSrcRect, | |
| &clippedDstPoint)) { | |
| return true; | |
| } | |
| if (this->getGpu()->canCopySurface(dst, src, clippedSrcRect, clippedDstPoint)) { | |
| this->onCopySurface(dst, src, clippedSrcRect, clippedDstPoint); | |
| return true; | |
| } | |
| GrRenderTarget* rt = dst->asRenderTarget(); | |
| GrTexture* tex = src->asTexture(); | |
| if ((dst == src) || !rt || !tex) { | |
| return false; | |
| } | |
| GrPipelineBuilder pipelineBuilder; | |
| pipelineBuilder.setRenderTarget(rt); | |
| SkMatrix matrix; | |
| matrix.setTranslate(SkIntToScalar(clippedSrcRect.fLeft - clippedDstPoint.fX), | |
| SkIntToScalar(clippedSrcRect.fTop - clippedDstPoint.fY)); | |
| matrix.postIDiv(tex->width(), tex->height()); | |
| pipelineBuilder.addColorTextureProcessor(tex, matrix); | |
| SkIRect dstRect = SkIRect::MakeXYWH(clippedDstPoint.fX, | |
| clippedDstPoint.fY, | |
| clippedSrcRect.width(), | |
| clippedSrcRect.height()); | |
| this->drawSimpleRect(&pipelineBuilder, GrColor_WHITE, SkMatrix::I(), dstRect); | |
| return true; | |
| } | |
| bool GrDrawTarget::canCopySurface(const GrSurface* dst, | |
| const GrSurface* src, | |
| const SkIRect& srcRect, | |
| const SkIPoint& dstPoint) { | |
| SkASSERT(dst); | |
| SkASSERT(src); | |
| SkIRect clippedSrcRect; | |
| SkIPoint clippedDstPoint; | |
| // If the rect is outside the src or dst then we're guaranteed success | |
| if (!clip_srcrect_and_dstpoint(dst, | |
| src, | |
| srcRect, | |
| dstPoint, | |
| &clippedSrcRect, | |
| &clippedDstPoint)) { | |
| return true; | |
| } | |
| return ((dst != src) && dst->asRenderTarget() && src->asTexture()) || | |
| this->getGpu()->canCopySurface(dst, src, clippedSrcRect, clippedDstPoint); | |
| } | |
| void GrDrawTarget::setupPipeline(const PipelineInfo& pipelineInfo, | |
| GrPipeline* pipeline) { | |
| SkNEW_PLACEMENT_ARGS(pipeline, GrPipeline, (*pipelineInfo.fPipelineBuilder, | |
| pipelineInfo.fColorPOI, | |
| pipelineInfo.fCoveragePOI, | |
| *this->caps(), | |
| *pipelineInfo.fScissor, | |
| &pipelineInfo.fDstTexture)); | |
| } | |
| /////////////////////////////////////////////////////////////////////////////// | |
| GrDrawTarget::PipelineInfo::PipelineInfo(GrPipelineBuilder* pipelineBuilder, | |
| GrScissorState* scissor, | |
| const GrPrimitiveProcessor* primProc, | |
| const SkRect* devBounds, | |
| GrDrawTarget* target) | |
| : fPipelineBuilder(pipelineBuilder) | |
| , fScissor(scissor) { | |
| fColorPOI = fPipelineBuilder->colorProcInfo(primProc); | |
| fCoveragePOI = fPipelineBuilder->coverageProcInfo(primProc); | |
| if (!target->setupDstReadIfNecessary(*fPipelineBuilder, fColorPOI, fCoveragePOI, | |
| &fDstTexture, devBounds)) { | |
| fPipelineBuilder = NULL; | |
| } | |
| } | |
| GrDrawTarget::PipelineInfo::PipelineInfo(GrPipelineBuilder* pipelineBuilder, | |
| GrScissorState* scissor, | |
| const GrBatch* batch, | |
| const SkRect* devBounds, | |
| GrDrawTarget* target) | |
| : fPipelineBuilder(pipelineBuilder) | |
| , fScissor(scissor) { | |
| fColorPOI = fPipelineBuilder->colorProcInfo(batch); | |
| fCoveragePOI = fPipelineBuilder->coverageProcInfo(batch); | |
| if (!target->setupDstReadIfNecessary(*fPipelineBuilder, fColorPOI, fCoveragePOI, | |
| &fDstTexture, devBounds)) { | |
| fPipelineBuilder = NULL; | |
| } | |
| } | |
| /////////////////////////////////////////////////////////////////////////////// | |
| bool GrClipTarget::setupClip(GrPipelineBuilder* pipelineBuilder, | |
| GrPipelineBuilder::AutoRestoreFragmentProcessors* arfp, | |
| GrPipelineBuilder::AutoRestoreStencil* ars, | |
| GrScissorState* scissorState, | |
| const SkRect* devBounds) { | |
| return fClipMaskManager.setupClipping(pipelineBuilder, | |
| arfp, | |
| ars, | |
| scissorState, | |
| devBounds); | |
| } |