| |
| /* |
| * 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 "GrGpu.h" |
| #include "GrPath.h" |
| #include "GrPipeline.h" |
| #include "GrMemoryPool.h" |
| #include "GrRectBatch.h" |
| #include "GrRenderTarget.h" |
| #include "GrResourceProvider.h" |
| #include "GrRenderTargetPriv.h" |
| #include "GrSurfacePriv.h" |
| #include "GrTexture.h" |
| #include "GrVertexBuffer.h" |
| |
| #include "SkStrokeRec.h" |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| GrDrawTarget::GrDrawTarget(GrGpu* gpu, GrResourceProvider* resourceProvider) |
| : fGpu(SkRef(gpu)) |
| , fCaps(SkRef(gpu->caps())) |
| , fResourceProvider(resourceProvider) |
| , fGpuTraceMarkerCount(0) |
| , fFlushing(false) { |
| } |
| |
| GrDrawTarget::~GrDrawTarget() { |
| fGpu->unref(); |
| fCaps->unref(); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| 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( |
| fResourceProvider->refScratchTexture(desc, GrTextureProvider::kApprox_ScratchTexMatch)); |
| |
| if (!copy) { |
| SkDebugf("Failed to create temporary copy of destination texture.\n"); |
| return false; |
| } |
| SkIPoint dstPoint = {0, 0}; |
| this->copySurface(copy, rt, copyRect, dstPoint); |
| dstTexture->setTexture(copy); |
| dstTexture->setOffset(copyRect.fLeft, copyRect.fTop); |
| return true; |
| } |
| |
| void GrDrawTarget::flush() { |
| if (fFlushing) { |
| return; |
| } |
| fFlushing = true; |
| |
| this->getGpu()->saveActiveTraceMarkers(); |
| |
| this->onFlush(); |
| |
| this->getGpu()->restoreActiveTraceMarkers(); |
| |
| fFlushing = false; |
| this->reset(); |
| } |
| |
| void GrDrawTarget::drawBatch(const GrPipelineBuilder& pipelineBuilder, GrBatch* batch) { |
| // TODO some kind of checkdraw, but not at this level |
| |
| // Setup clip |
| GrScissorState scissorState; |
| GrPipelineBuilder::AutoRestoreFragmentProcessorState arfps; |
| GrPipelineBuilder::AutoRestoreStencil ars; |
| if (!this->setupClip(pipelineBuilder, &arfps, &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(const 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()); |
| |
| // Setup clip |
| GrScissorState scissorState; |
| GrPipelineBuilder::AutoRestoreFragmentProcessorState arfps; |
| GrPipelineBuilder::AutoRestoreStencil ars; |
| if (!this->setupClip(pipelineBuilder, &arfps, &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(const 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()); |
| |
| SkRect devBounds = path->getBounds(); |
| pathProc->viewMatrix().mapRect(&devBounds); |
| |
| // Setup clip |
| GrScissorState scissorState; |
| GrPipelineBuilder::AutoRestoreFragmentProcessorState arfps; |
| GrPipelineBuilder::AutoRestoreStencil ars; |
| if (!this->setupClip(pipelineBuilder, &arfps, &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(const 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); |
| |
| // Setup clip |
| GrScissorState scissorState; |
| GrPipelineBuilder::AutoRestoreFragmentProcessorState arfps; |
| GrPipelineBuilder::AutoRestoreStencil ars; |
| if (!this->setupClip(pipelineBuilder, &arfps, &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(const 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(const 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(); |
| } |
| } |
| |
| void 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; |
| } |
| |
| this->onCopySurface(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(const 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(const 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; |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| GrClipTarget::GrClipTarget(GrContext* context) |
| : INHERITED(context->getGpu(), context->resourceProvider()) |
| , fContext(context) { |
| fClipMaskManager.reset(SkNEW_ARGS(GrClipMaskManager, (this))); |
| } |
| |
| |
| bool GrClipTarget::setupClip(const GrPipelineBuilder& pipelineBuilder, |
| GrPipelineBuilder::AutoRestoreFragmentProcessorState* arfps, |
| GrPipelineBuilder::AutoRestoreStencil* ars, |
| GrScissorState* scissorState, |
| const SkRect* devBounds) { |
| return fClipMaskManager->setupClipping(pipelineBuilder, |
| arfps, |
| ars, |
| scissorState, |
| devBounds); |
| } |
| |
| void GrClipTarget::purgeResources() { |
| // The clip mask manager can rebuild all its clip masks so just |
| // get rid of them all. |
| fClipMaskManager->purgeResources(); |
| }; |