diff --git a/gn/gpu.gni b/gn/gpu.gni index 51ac125366..aa4ff52498 100644 --- a/gn/gpu.gni +++ b/gn/gpu.gni @@ -285,6 +285,8 @@ skia_gpu_sources = [ "$_src/gpu/ops/GrStencilPathOp.h", "$_src/gpu/ops/GrTessellatingPathRenderer.cpp", "$_src/gpu/ops/GrTessellatingPathRenderer.h", + "$_src/gpu/ops/GrTextureOp.cpp", + "$_src/gpu/ops/GrTextureOp.h", # coverage counting path renderer "$_src/gpu/ccpr/GrCCPRAtlas.cpp", diff --git a/src/gpu/GrGpuResourceRef.h b/src/gpu/GrGpuResourceRef.h index a56674b494..1d85d7df33 100644 --- a/src/gpu/GrGpuResourceRef.h +++ b/src/gpu/GrGpuResourceRef.h @@ -155,7 +155,8 @@ public: /** Adopts a ref from the caller. ioType expresses what type of IO operations will be marked as pending on the resource when markPendingIO is called. */ - GrTGpuResourceRef(T* resource, GrIOType ioType) : INHERITED(resource, ioType) { } + GrTGpuResourceRef(T* resource, GrIOType ioType) : INHERITED(resource, ioType) {} + GrTGpuResourceRef(sk_sp resource, GrIOType ioType) : INHERITED(resource, ioType) {} T* get() const { return static_cast(this->getResource()); } diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp index 39515adc0a..3f9dcbb0bd 100644 --- a/src/gpu/GrRenderTargetContext.cpp +++ b/src/gpu/GrRenderTargetContext.cpp @@ -43,6 +43,7 @@ #include "ops/GrSemaphoreOp.h" #include "ops/GrShadowRRectOp.h" #include "ops/GrStencilPathOp.h" +#include "ops/GrTextureOp.h" #include "text/GrAtlasTextContext.h" #include "text/GrStencilAndCoverTextContext.h" @@ -769,6 +770,21 @@ void GrRenderTargetContext::fillRectToRect(const GrClip& clip, this->internalDrawPath(clip, std::move(paint), aa, viewAndUnLocalMatrix, path, GrStyle()); } +void GrRenderTargetContext::drawTextureAffine(const GrClip& clip, sk_sp proxy, + GrSamplerParams::FilterMode filter, GrColor color, + const SkRect& srcRect, const SkRect& dstRect, + const SkMatrix& viewMatrix, + sk_sp colorSpaceXform) { + ASSERT_SINGLE_OWNER + RETURN_IF_ABANDONED + SkDEBUGCODE(this->validate();) + GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext", "drawTextureAffine", fContext); + SkASSERT(!viewMatrix.hasPerspective()); + bool allowSRGB = SkToBool(this->getColorSpace()); + this->addDrawOp(clip, GrTextureOp::Make(std::move(proxy), filter, color, srcRect, dstRect, + viewMatrix, std::move(colorSpaceXform), allowSRGB)); +} + void GrRenderTargetContext::fillRectWithLocalMatrix(const GrClip& clip, GrPaint&& paint, GrAA aa, diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h index a73f4c9f8f..084e1530a4 100644 --- a/src/gpu/GrRenderTargetContext.h +++ b/src/gpu/GrRenderTargetContext.h @@ -134,6 +134,17 @@ public: const SkRect& rect, const SkMatrix& localMatrix); + /** + * Creates an op that draws a subrectangle of a texture. The passed color is modulated by the + * texture's color. 'srcRect' specifies the rectangle of the texture to draw. 'dstRect' + * specifies the rectangle to draw in local coords which will be transformed by 'viewMatrix' to + * device space. The edges of the rendered rectangle are not antialiased. This asserts that the + * view matrix does not have perspective. + */ + void drawTextureAffine(const GrClip& clip, sk_sp, GrSamplerParams::FilterMode, + GrColor, const SkRect& srcRect, const SkRect& dstRect, + const SkMatrix& viewMatrix, sk_sp); + /** * Draw a roundrect using a paint. * diff --git a/src/gpu/GrResourceProvider.cpp b/src/gpu/GrResourceProvider.cpp index 1979570fe0..323139ea0a 100644 --- a/src/gpu/GrResourceProvider.cpp +++ b/src/gpu/GrResourceProvider.cpp @@ -362,14 +362,17 @@ const GrBuffer* GrResourceProvider::createPatternedIndexBuffer(const uint16_t* p return buffer; } +static constexpr int kMaxQuads = 1 << 12; // max possible: (1 << 14) - 1; + const GrBuffer* GrResourceProvider::createQuadIndexBuffer() { - static const int kMaxQuads = 1 << 12; // max possible: (1 << 14) - 1; GR_STATIC_ASSERT(4 * kMaxQuads <= 65535); static const uint16_t kPattern[] = { 0, 1, 2, 0, 2, 3 }; return this->createPatternedIndexBuffer(kPattern, 6, kMaxQuads, 4, fQuadIndexBufferKey); } +int GrResourceProvider::QuadCountOfQuadBuffer() { return kMaxQuads; } + sk_sp GrResourceProvider::createPath(const SkPath& path, const GrStyle& style) { SkASSERT(this->gpu()->pathRendering()); return this->gpu()->pathRendering()->createPath(path, style); diff --git a/src/gpu/GrResourceProvider.h b/src/gpu/GrResourceProvider.h index 28207b7a87..9616d7541d 100644 --- a/src/gpu/GrResourceProvider.h +++ b/src/gpu/GrResourceProvider.h @@ -152,6 +152,8 @@ public: return this->createQuadIndexBuffer(); } + static int QuadCountOfQuadBuffer(); + /** * Factories for GrPath and GrPathRange objects. It's an error to call these if path rendering * is not supported. diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index 2d024b014b..0cf2ae5893 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -6,7 +6,6 @@ */ #include "SkGpuDevice.h" - #include "GrBitmapTextureMaker.h" #include "GrBlurUtils.h" #include "GrContext.h" @@ -759,10 +758,8 @@ bool SkGpuDevice::shouldTileImage(const SkImage* image, const SkRect* srcRectPtr const SkMatrix& viewMatrix, const SkMatrix& srcToDstRect) const { ASSERT_SINGLE_OWNER - // if image is explictly texture backed then just use the texture - if (image->isTextureBacked()) { - return false; - } + // If image is explicitly texture backed then we shouldn't get here. + SkASSERT(!image->isTextureBacked()); GrSamplerParams params; bool doBicubic; @@ -843,7 +840,7 @@ void SkGpuDevice::drawBitmap(const SkBitmap& bitmap, } GrBitmapTextureMaker maker(fContext.get(), bitmap); this->drawTextureProducer(&maker, nullptr, nullptr, SkCanvas::kStrict_SrcRectConstraint, - viewMatrix, this->clip(), paint); + viewMatrix, paint); } // This method outsets 'iRect' by 'outset' all around and then clamps its extents to @@ -1202,7 +1199,7 @@ void SkGpuDevice::drawBitmapRect(const SkBitmap& bitmap, } } GrBitmapTextureMaker maker(fContext.get(), bitmap); - this->drawTextureProducer(&maker, src, dst, constraint, this->ctm(), this->clip(), paint); + this->drawTextureProducer(&maker, src, dst, constraint, this->ctm(), paint); } sk_sp SkGpuDevice::makeSpecial(const SkBitmap& bitmap) { @@ -1289,52 +1286,50 @@ void SkGpuDevice::drawDevice(SkBaseDevice* device, this->drawSpecial(srcImg.get(), left, top, paint, nullptr, SkMatrix::I()); } -void SkGpuDevice::drawImage(const SkImage* image, SkScalar x, SkScalar y, - const SkPaint& paint) { +void SkGpuDevice::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint& paint) { ASSERT_SINGLE_OWNER SkMatrix viewMatrix = this->ctm(); viewMatrix.preTranslate(x, y); uint32_t pinnedUniqueID; - if (sk_sp proxy = as_IB(image)->refPinnedTextureProxy(&pinnedUniqueID)) { - GrTextureAdjuster adjuster(this->context(), std::move(proxy), - image->alphaType(), image->bounds(), - pinnedUniqueID, as_IB(image)->onImageInfo().colorSpace()); - this->drawTextureProducer(&adjuster, nullptr, nullptr, SkCanvas::kFast_SrcRectConstraint, - viewMatrix, this->clip(), paint); + this->drawPinnedTextureProxy(std::move(proxy), pinnedUniqueID, as_IB(image)->colorSpace(), + image->alphaType(), nullptr, nullptr, + SkCanvas::kFast_SrcRectConstraint, viewMatrix, paint); return; - } else { - SkBitmap bm; - if (this->shouldTileImage(image, nullptr, SkCanvas::kFast_SrcRectConstraint, - paint.getFilterQuality(), this->ctm(), SkMatrix::I())) { - // only support tiling as bitmap at the moment, so force raster-version - if (!as_IB(image)->getROPixels(&bm, fRenderTargetContext->getColorSpace())) { - return; - } - this->drawBitmap(bm, x, y, paint); - } else if (image->isLazyGenerated()) { - GrImageTextureMaker maker(fContext.get(), image, SkImage::kAllow_CachingHint); - this->drawTextureProducer(&maker, nullptr, nullptr, SkCanvas::kFast_SrcRectConstraint, - viewMatrix, this->clip(), paint); - } else if (as_IB(image)->getROPixels(&bm, fRenderTargetContext->getColorSpace())) { - GrBitmapTextureMaker maker(fContext.get(), bm); - this->drawTextureProducer(&maker, nullptr, nullptr, SkCanvas::kFast_SrcRectConstraint, - viewMatrix, this->clip(), paint); + } + SkBitmap bm; + if (this->shouldTileImage(image, nullptr, SkCanvas::kFast_SrcRectConstraint, + paint.getFilterQuality(), viewMatrix, SkMatrix::I())) { + // only support tiling as bitmap at the moment, so force raster-version + if (!as_IB(image)->getROPixels(&bm, fRenderTargetContext->getColorSpace())) { + return; } + this->drawBitmap(bm, x, y, paint); + return; + } + if (image->isLazyGenerated()) { + GrImageTextureMaker maker(fContext.get(), image, SkImage::kAllow_CachingHint); + this->drawTextureMaker(&maker, image->width(), image->height(), nullptr, nullptr, + SkCanvas::kFast_SrcRectConstraint, viewMatrix, paint); + return; + } + if (as_IB(image)->getROPixels(&bm, fRenderTargetContext->getColorSpace())) { + GrBitmapTextureMaker maker(fContext.get(), bm); + this->drawTextureMaker(&maker, image->width(), image->height(), nullptr, nullptr, + SkCanvas::kFast_SrcRectConstraint, viewMatrix, paint); } } -void SkGpuDevice::drawImageRect(const SkImage* image, const SkRect* src, - const SkRect& dst, const SkPaint& paint, - SkCanvas::SrcRectConstraint constraint) { +void SkGpuDevice::drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst, + const SkPaint& paint, SkCanvas::SrcRectConstraint constraint) { ASSERT_SINGLE_OWNER uint32_t pinnedUniqueID; + if (!src || src->contains(image->bounds())) { + constraint = SkCanvas::kFast_SrcRectConstraint; + } if (sk_sp proxy = as_IB(image)->refPinnedTextureProxy(&pinnedUniqueID)) { - GrTextureAdjuster adjuster(this->context(), std::move(proxy), - image->alphaType(), image->bounds(), pinnedUniqueID, - as_IB(image)->onImageInfo().colorSpace()); - this->drawTextureProducer(&adjuster, src, &dst, constraint, this->ctm(), this->clip(), - paint); + this->drawPinnedTextureProxy(std::move(proxy), pinnedUniqueID, as_IB(image)->colorSpace(), + image->alphaType(), src, &dst, constraint, this->ctm(), paint); return; } SkBitmap bm; @@ -1348,12 +1343,18 @@ void SkGpuDevice::drawImageRect(const SkImage* image, const SkRect* src, return; } this->drawBitmapRect(bm, src, dst, paint, constraint); - } else if (image->isLazyGenerated()) { + return; + } + if (image->isLazyGenerated()) { GrImageTextureMaker maker(fContext.get(), image, SkImage::kAllow_CachingHint); - this->drawTextureProducer(&maker, src, &dst, constraint, this->ctm(), this->clip(), paint); - } else if (as_IB(image)->getROPixels(&bm, fRenderTargetContext->getColorSpace())) { + this->drawTextureMaker(&maker, image->width(), image->height(), src, &dst, constraint, + this->ctm(), paint); + return; + } + if (as_IB(image)->getROPixels(&bm, fRenderTargetContext->getColorSpace())) { GrBitmapTextureMaker maker(fContext.get(), bm); - this->drawTextureProducer(&maker, src, &dst, constraint, this->ctm(), this->clip(), paint); + this->drawTextureMaker(&maker, image->width(), image->height(), src, &dst, constraint, + this->ctm(), paint); } } @@ -1373,7 +1374,7 @@ void SkGpuDevice::drawProducerNine(GrTextureProducer* producer, SkRect srcR, dstR; while (iter.next(&srcR, &dstR)) { this->drawTextureProducer(producer, &srcR, &dstR, SkCanvas::kStrict_SrcRectConstraint, - this->ctm(), this->clip(), paint); + this->ctm(), paint); } return; } diff --git a/src/gpu/SkGpuDevice.h b/src/gpu/SkGpuDevice.h index f1bd937269..0b7e286aae 100644 --- a/src/gpu/SkGpuDevice.h +++ b/src/gpu/SkGpuDevice.h @@ -21,6 +21,7 @@ #include "GrTypes.h" class GrAccelData; +class GrTextureMaker; class GrTextureProducer; struct GrCachedLayer; @@ -205,12 +206,30 @@ private: bool bicubic, bool needsTextureDomain); + void drawPinnedTextureProxy(sk_sp, + uint32_t pinnedUniqueID, + SkColorSpace*, + SkAlphaType alphaType, + const SkRect* srcRect, + const SkRect* dstRect, + SkCanvas::SrcRectConstraint, + const SkMatrix& viewMatrix, + const SkPaint&); + + void drawTextureMaker(GrTextureMaker* maker, + int imageW, + int imageH, + const SkRect* srcRect, + const SkRect* dstRect, + SkCanvas::SrcRectConstraint, + const SkMatrix& viewMatrix, + const SkPaint&); + void drawTextureProducer(GrTextureProducer*, const SkRect* srcRect, const SkRect* dstRect, SkCanvas::SrcRectConstraint, const SkMatrix& viewMatrix, - const GrClip&, const SkPaint&); void drawTextureProducerImpl(GrTextureProducer*, @@ -219,7 +238,6 @@ private: SkCanvas::SrcRectConstraint, const SkMatrix& viewMatrix, const SkMatrix& srcToDstMatrix, - const GrClip&, const SkPaint&); bool drawFilledDRRect(const SkMatrix& viewMatrix, const SkRRect& outer, diff --git a/src/gpu/SkGpuDevice_drawTexture.cpp b/src/gpu/SkGpuDevice_drawTexture.cpp index 6fa171113b..c861cc919e 100644 --- a/src/gpu/SkGpuDevice_drawTexture.cpp +++ b/src/gpu/SkGpuDevice_drawTexture.cpp @@ -6,12 +6,12 @@ */ #include "SkGpuDevice.h" - #include "GrBlurUtils.h" #include "GrCaps.h" #include "GrRenderTargetContext.h" #include "GrStyle.h" #include "GrTextureAdjuster.h" +#include "GrTextureMaker.h" #include "SkDraw.h" #include "SkGr.h" #include "SkMaskFilter.h" @@ -85,14 +85,98 @@ static bool can_ignore_bilerp_constraint(const GrTextureProducer& producer, return false; } +/** + * Checks whether the paint, matrix, and constraint are compatible with using + * GrRenderTargetContext::drawTextureAffine. It is more effecient than the GrTextureProducer + * general case. + */ +static bool can_use_draw_texture_affine(const SkPaint& paint, const SkMatrix& ctm, + SkCanvas::SrcRectConstraint constraint) { + return (!paint.getColorFilter() && !paint.getShader() && !paint.getMaskFilter() && + !paint.getImageFilter() && !paint.isAntiAlias() && + paint.getFilterQuality() < kMedium_SkFilterQuality && + paint.getBlendMode() == SkBlendMode::kSrcOver && !ctm.hasPerspective() && + SkCanvas::kFast_SrcRectConstraint == constraint); +} + +static void draw_texture_affine(const SkPaint& paint, const SkMatrix& ctm, const SkRect* src, + const SkRect* dst, sk_sp proxy, + SkColorSpace* colorSpace, const GrClip& clip, + GrRenderTargetContext* rtc) { + SkASSERT(!(SkToBool(src) && !SkToBool(dst))); + SkRect srcRect = src ? *src : SkRect::MakeWH(proxy->width(), proxy->height()); + SkRect dstRect = dst ? *dst : srcRect; + if (src && !SkRect::MakeIWH(proxy->width(), proxy->height()).contains(srcRect)) { + // Shrink the src rect to be within bounds and proportionately shrink the dst rect. + SkMatrix srcToDst; + srcToDst.setRectToRect(srcRect, dstRect, SkMatrix::kFill_ScaleToFit); + SkAssertResult(srcRect.intersect(SkRect::MakeIWH(proxy->width(), proxy->height()))); + srcToDst.mapRect(&dstRect, srcRect); + } + auto csxf = GrColorSpaceXform::Make(colorSpace, rtc->getColorSpace()); + GrSamplerParams::FilterMode filter; + switch (paint.getFilterQuality()) { + case kNone_SkFilterQuality: + filter = GrSamplerParams::kNone_FilterMode; + break; + case kLow_SkFilterQuality: + filter = GrSamplerParams::kBilerp_FilterMode; + break; + case kMedium_SkFilterQuality: + case kHigh_SkFilterQuality: + SK_ABORT("Quality level not allowed."); + } + GrColor color = GrPixelConfigIsAlphaOnly(proxy->config()) + ? SkColorToPremulGrColor(paint.getColor()) + : SkColorAlphaToGrColor(paint.getColor()); + rtc->drawTextureAffine(clip, std::move(proxy), filter, color, srcRect, dstRect, ctm, + std::move(csxf)); +} + ////////////////////////////////////////////////////////////////////////////// +void SkGpuDevice::drawPinnedTextureProxy(sk_sp proxy, uint32_t pinnedUniqueID, + SkColorSpace* colorSpace, SkAlphaType alphaType, + const SkRect* srcRect, const SkRect* dstRect, + SkCanvas::SrcRectConstraint constraint, + const SkMatrix& viewMatrix, const SkPaint& paint) { + if (can_use_draw_texture_affine(paint, this->ctm(), constraint)) { + draw_texture_affine(paint, viewMatrix, srcRect, dstRect, std::move(proxy), colorSpace, + this->clip(), fRenderTargetContext.get()); + return; + } + auto contentRect = SkIRect::MakeWH(proxy->width(), proxy->height()); + GrTextureAdjuster adjuster(this->context(), std::move(proxy), alphaType, contentRect, + pinnedUniqueID, colorSpace); + this->drawTextureProducer(&adjuster, srcRect, dstRect, constraint, viewMatrix, paint); +} + +void SkGpuDevice::drawTextureMaker(GrTextureMaker* maker, int imageW, int imageH, + const SkRect* srcRect, const SkRect* dstRect, + SkCanvas::SrcRectConstraint constraint, + const SkMatrix& viewMatrix, const SkPaint& paint) { + if (can_use_draw_texture_affine(paint, viewMatrix, constraint)) { + sk_sp cs; + // We've done enough checks above to allow us to pass ClampNoFilter() and not check for + // scaling adjustments. + auto proxy = maker->refTextureProxyForParams(GrSamplerParams::ClampNoFilter(), + fRenderTargetContext->getColorSpace(), &cs, + nullptr); + if (!proxy) { + return; + } + draw_texture_affine(paint, viewMatrix, srcRect, dstRect, std::move(proxy), cs.get(), + this->clip(), fRenderTargetContext.get()); + return; + } + this->drawTextureProducer(maker, srcRect, dstRect, constraint, viewMatrix, paint); +} + void SkGpuDevice::drawTextureProducer(GrTextureProducer* producer, const SkRect* srcRect, const SkRect* dstRect, SkCanvas::SrcRectConstraint constraint, const SkMatrix& viewMatrix, - const GrClip& clip, const SkPaint& paint) { // This is the funnel for all non-tiled bitmap/image draw calls. Log a histogram entry. SK_HISTOGRAM_BOOLEAN("DrawTiled", false); @@ -141,7 +225,7 @@ void SkGpuDevice::drawTextureProducer(GrTextureProducer* producer, LogDrawScaleFactor(SkMatrix::Concat(viewMatrix, srcToDstMatrix), paint.getFilterQuality()); this->drawTextureProducerImpl(producer, clippedSrcRect, clippedDstRect, constraint, viewMatrix, - srcToDstMatrix, clip, paint); + srcToDstMatrix, paint); } void SkGpuDevice::drawTextureProducerImpl(GrTextureProducer* producer, @@ -150,7 +234,6 @@ void SkGpuDevice::drawTextureProducerImpl(GrTextureProducer* producer, SkCanvas::SrcRectConstraint constraint, const SkMatrix& viewMatrix, const SkMatrix& srcToDstMatrix, - const GrClip& clip, const SkPaint& paint) { // Specifying the texture coords as local coordinates is an attempt to enable more GrDrawOp // combining by not baking anything about the srcRect, dstRect, or viewMatrix, into the texture @@ -215,13 +298,14 @@ void SkGpuDevice::drawTextureProducerImpl(GrTextureProducer* producer, } GrAA aa = GrBoolToAA(paint.isAntiAlias()); if (canUseTextureCoordsAsLocalCoords) { - fRenderTargetContext->fillRectToRect(clip, std::move(grPaint), aa, viewMatrix, + fRenderTargetContext->fillRectToRect(this->clip(), std::move(grPaint), aa, viewMatrix, clippedDstRect, clippedSrcRect); return; } if (!mf) { - fRenderTargetContext->drawRect(clip, std::move(grPaint), aa, viewMatrix, clippedDstRect); + fRenderTargetContext->drawRect(this->clip(), std::move(grPaint), aa, viewMatrix, + clippedDstRect); return; } @@ -234,7 +318,7 @@ void SkGpuDevice::drawTextureProducerImpl(GrTextureProducer* producer, if (mf->directFilterRRectMaskGPU(fContext.get(), fRenderTargetContext.get(), std::move(grPaint), - clip, + this->clip(), viewMatrix, rec, SkRRect::MakeRect(clippedDstRect), diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp new file mode 100644 index 0000000000..686852b698 --- /dev/null +++ b/src/gpu/ops/GrTextureOp.cpp @@ -0,0 +1,352 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrTextureOp.h" +#include "GrAppliedClip.h" +#include "GrDrawOpTest.h" +#include "GrGeometryProcessor.h" +#include "GrMeshDrawOp.h" +#include "GrOpFlushState.h" +#include "GrQuad.h" +#include "GrResourceProvider.h" +#include "GrShaderCaps.h" +#include "GrTexture.h" +#include "GrTextureProxy.h" +#include "SkGr.h" +#include "glsl/GrGLSLColorSpaceXformHelper.h" +#include "glsl/GrGLSLGeometryProcessor.h" +#include "glsl/GrGLSLVarying.h" + +namespace { + +/** + * Geometry Processor that draws a texture modulated by a vertex color (though, this is meant to be + * the same value across all vertices of a quad and uses flat interpolation when available). This is + * used by TextureOp below. + */ +class TextureGeometryProcessor : public GrGeometryProcessor { +public: + struct Vertex { + SkPoint fPosition; + SkPoint fTextureCoords; + GrColor fColor; + }; + static sk_sp Make(sk_sp proxy, + sk_sp csxf, + GrSamplerParams::FilterMode filter) { + return sk_sp( + new TextureGeometryProcessor(std::move(proxy), std::move(csxf), filter)); + } + + const char* name() const override { return "TextureGeometryProcessor"; } + + void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override { + b->add32(GrColorSpaceXform::XformKey(fColorSpaceXform.get())); + } + + GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override { + class GLSLProcessor : public GrGLSLGeometryProcessor { + public: + void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc, + FPCoordTransformIter&& transformIter) override { + const auto& textureGP = proc.cast(); + this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter); + if (fColorSpaceXformHelper.isValid()) { + fColorSpaceXformHelper.setData(pdman, textureGP.fColorSpaceXform.get()); + } + } + + private: + void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { + const auto& textureGP = args.fGP.cast(); + fColorSpaceXformHelper.emitCode( + args.fUniformHandler, textureGP.fColorSpaceXform.get()); + args.fVaryingHandler->setNoPerspective(); + args.fVaryingHandler->emitAttributes(textureGP); + this->writeOutputPosition(args.fVertBuilder, gpArgs, textureGP.fPositions.fName); + this->emitTransforms(args.fVertBuilder, + args.fVaryingHandler, + args.fUniformHandler, + gpArgs->fPositionVar, + textureGP.fTextureCoords.fName, + args.fFPCoordTransformHandler); + if (args.fShaderCaps->flatInterpolationSupport()) { + args.fVaryingHandler->addFlatPassThroughAttribute(&textureGP.fColors, + args.fOutputColor); + } else { + args.fVaryingHandler->addPassThroughAttribute(&textureGP.fColors, + args.fOutputColor); + } + args.fFragBuilder->codeAppend("highp float2 texCoord;"); + args.fVaryingHandler->addPassThroughAttribute(&textureGP.fTextureCoords, + "texCoord"); + args.fFragBuilder->codeAppendf("%s = ", args.fOutputColor); + args.fFragBuilder->appendTextureLookupAndModulate(args.fOutputColor, + args.fTexSamplers[0], + "texCoord", + kVec2f_GrSLType, + &fColorSpaceXformHelper); + args.fFragBuilder->codeAppend(";"); + args.fFragBuilder->codeAppendf("%s = float4(1);", args.fOutputCoverage); + } + GrGLSLColorSpaceXformHelper fColorSpaceXformHelper; + }; + return new GLSLProcessor; + } + +private: + TextureGeometryProcessor(sk_sp proxy, sk_sp csxf, + GrSamplerParams::FilterMode filter) + : fSampler(std::move(proxy), filter), fColorSpaceXform(std::move(csxf)) { + this->initClassID(); + fPositions = + this->addVertexAttrib("position", kVec2f_GrVertexAttribType, kHigh_GrSLPrecision); + fTextureCoords = this->addVertexAttrib("textureCoords", kVec2f_GrVertexAttribType, + kHigh_GrSLPrecision); + fColors = this->addVertexAttrib("color", kVec4ub_GrVertexAttribType); + this->addTextureSampler(&fSampler); + } + + Attribute fPositions; + Attribute fTextureCoords; + Attribute fColors; + TextureSampler fSampler; + sk_sp fColorSpaceXform; +}; + +/** + * Op that implements GrTextureOp::Make. It draws textured quads. Each quad can modulate against a + * the texture by color. The blend with the destination is always src-over. The edges are non-AA. + */ +class TextureOp final : public GrMeshDrawOp { +public: + static std::unique_ptr Make(sk_sp proxy, + GrSamplerParams::FilterMode filter, GrColor color, + const SkRect srcRect, const SkRect dstRect, + const SkMatrix& viewMatrix, sk_sp csxf, + bool allowSRBInputs) { + return std::unique_ptr(new TextureOp(std::move(proxy), filter, color, srcRect, + dstRect, viewMatrix, std::move(csxf), + allowSRBInputs)); + } + + ~TextureOp() override { fFinalized ? fProxy->completedRead() : fProxy->unref(); } + + const char* name() const override { return "TextureOp"; } + + SkString dumpInfo() const override { + SkString str; + str.appendf("Filter: %d AllowSRGBInputs: %d\n", fFilter, fAllowSRGBInputs); + str.appendf("# draws: %d\n", fDraws.count()); + for (int i = 0; i < fDraws.count(); ++i) { + const Draw& draw = fDraws[i]; + str.appendf( + "%d: Color: 0x%08x, TexRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f] Quad [(%.2f, " + "%.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n", + i, draw.fColor, draw.fSrcRect.fLeft, draw.fSrcRect.fTop, draw.fSrcRect.fRight, + draw.fSrcRect.fBottom, draw.fQuad.points()[0].fX, draw.fQuad.points()[0].fY, + draw.fQuad.points()[1].fX, draw.fQuad.points()[1].fY, draw.fQuad.points()[2].fX, + draw.fQuad.points()[2].fY, draw.fQuad.points()[3].fX, + draw.fQuad.points()[3].fY); + } + str += INHERITED::dumpInfo(); + return str; + } + + RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override { + SkASSERT(!fFinalized); + fFinalized = true; + fProxy->addPendingRead(); + fProxy->unref(); + return RequiresDstTexture::kNo; + } + + FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; } + + DEFINE_OP_CLASS_ID + +private: + TextureOp(sk_sp proxy, GrSamplerParams::FilterMode filter, GrColor color, + const SkRect& srcRect, const SkRect& dstRect, const SkMatrix& viewMatrix, + sk_sp csxf, bool allowSRGBInputs) + : INHERITED(ClassID()) + , fProxy(proxy.release()) + , fFilter(filter) + , fColorSpaceXform(std::move(csxf)) + , fFinalized(false) + , fAllowSRGBInputs(allowSRGBInputs) { + Draw& draw = fDraws.push_back(); + draw.fSrcRect = srcRect; + draw.fColor = color; + draw.fQuad.setFromMappedRect(dstRect, viewMatrix); + SkRect bounds; + bounds.setBounds(draw.fQuad.points(), 4); + this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo); + } + + void onPrepareDraws(Target* target) override { + if (!fProxy->instantiate(target->resourceProvider())) { + return; + } + sk_sp gp = TextureGeometryProcessor::Make( + sk_ref_sp(fProxy), std::move(fColorSpaceXform), fFilter); + GrPipeline::InitArgs args; + args.fProxy = target->proxy(); + args.fCaps = &target->caps(); + args.fResourceProvider = target->resourceProvider(); + args.fFlags = fAllowSRGBInputs ? GrPipeline::kAllowSRGBInputs_Flag : 0; + const GrPipeline* pipeline = target->allocPipeline(args, GrProcessorSet::MakeEmptySet(), + target->detachAppliedClip()); + + using Vertex = TextureGeometryProcessor::Vertex; + SkASSERT(gp->getVertexStride() == sizeof(Vertex)); + + int vstart; + const GrBuffer* vbuffer; + auto vertices = (Vertex*)target->makeVertexSpace(sizeof(Vertex), 4 * fDraws.count(), + &vbuffer, &vstart); + if (!vertices) { + SkDebugf("Could not allocate vertices\n"); + return; + } + sk_sp ibuffer; + if (fDraws.count() > 1) { + ibuffer.reset(target->resourceProvider()->refQuadIndexBuffer()); + if (!ibuffer) { + SkDebugf("Could not allocate quad indices\n"); + return; + } + } + GrTexture* texture = fProxy->priv().peekTexture(); + float iw = 1.f / texture->width(); + float ih = 1.f / texture->height(); + if (fDraws.count() > 1) { + for (int i = 0; i < fDraws.count(); ++i) { + float tl = iw * fDraws[i].fSrcRect.fLeft; + float tr = iw * fDraws[i].fSrcRect.fRight; + float tt = ih * fDraws[i].fSrcRect.fTop; + float tb = ih * fDraws[i].fSrcRect.fBottom; + if (fProxy->origin() == kBottomLeft_GrSurfaceOrigin) { + tt = 1.f - tt; + tb = 1.f - tb; + } + vertices[0 + 4 * i].fPosition = fDraws[i].fQuad.points()[0]; + vertices[0 + 4 * i].fTextureCoords = {tl, tt}; + vertices[0 + 4 * i].fColor = fDraws[i].fColor; + vertices[1 + 4 * i].fPosition = fDraws[i].fQuad.points()[1]; + vertices[1 + 4 * i].fTextureCoords = {tl, tb}; + vertices[1 + 4 * i].fColor = fDraws[i].fColor; + vertices[2 + 4 * i].fPosition = fDraws[i].fQuad.points()[2]; + vertices[2 + 4 * i].fTextureCoords = {tr, tb}; + vertices[2 + 4 * i].fColor = fDraws[i].fColor; + vertices[3 + 4 * i].fPosition = fDraws[i].fQuad.points()[3]; + vertices[3 + 4 * i].fTextureCoords = {tr, tt}; + vertices[3 + 4 * i].fColor = fDraws[i].fColor; + } + GrMesh mesh(GrPrimitiveType::kTriangles); + mesh.setIndexedPatterned(ibuffer.get(), 6, 4, fDraws.count(), + GrResourceProvider::QuadCountOfQuadBuffer()); + mesh.setVertexData(vbuffer, vstart); + target->draw(gp.get(), pipeline, mesh); + } else { + float tl = iw * fDraws[0].fSrcRect.fLeft; + float tr = iw * fDraws[0].fSrcRect.fRight; + float tt = ih * fDraws[0].fSrcRect.fTop; + float tb = ih * fDraws[0].fSrcRect.fBottom; + if (fProxy->origin() == kBottomLeft_GrSurfaceOrigin) { + tt = 1.f - tt; + tb = 1.f - tb; + } + vertices[0].fPosition = fDraws[0].fQuad.points()[0]; + vertices[0].fTextureCoords = {tl, tt}; + vertices[0].fColor = fDraws[0].fColor; + vertices[1].fPosition = fDraws[0].fQuad.points()[3]; + vertices[1].fTextureCoords = {tr, tt}; + vertices[1].fColor = fDraws[0].fColor; + vertices[2].fPosition = fDraws[0].fQuad.points()[1]; + vertices[2].fTextureCoords = {tl, tb}; + vertices[2].fColor = fDraws[0].fColor; + vertices[3].fPosition = fDraws[0].fQuad.points()[2]; + vertices[3].fTextureCoords = {tr, tb}; + vertices[3].fColor = fDraws[0].fColor; + GrMesh mesh(GrPrimitiveType::kTriangleStrip); + mesh.setNonIndexedNonInstanced(4); + mesh.setVertexData(vbuffer, vstart); + target->draw(gp.get(), pipeline, mesh); + } + } + + bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { + const auto* that = t->cast(); + if (fProxy->uniqueID() != that->fProxy->uniqueID() || fFilter != that->fFilter || + !GrColorSpaceXform::Equals(fColorSpaceXform.get(), that->fColorSpaceXform.get())) { + return false; + } + fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin()); + this->joinBounds(*that); + return true; + } + + struct Draw { + SkRect fSrcRect; + GrQuad fQuad; + GrColor fColor; + }; + SkSTArray<1, Draw, true> fDraws; + GrTextureProxy* fProxy; + GrSamplerParams::FilterMode fFilter; + sk_sp fColorSpaceXform; + // Used to track whether fProxy is ref'ed or has a pending IO after finalize() is called. + bool fFinalized : 1; + bool fAllowSRGBInputs : 1; + typedef GrMeshDrawOp INHERITED; +}; + +} // anonymous namespace + +namespace GrTextureOp { + +std::unique_ptr Make(sk_sp proxy, GrSamplerParams::FilterMode filter, + GrColor color, const SkRect& srcRect, const SkRect& dstRect, + const SkMatrix& viewMatrix, sk_sp csxf, + bool allowSRGBInputs) { + SkASSERT(!viewMatrix.hasPerspective()); + return TextureOp::Make(std::move(proxy), filter, color, srcRect, dstRect, viewMatrix, + std::move(csxf), allowSRGBInputs); +} + +} // namespace GrTextureOp + +#if GR_TEST_UTILS +#include "GrContext.h" + +GR_DRAW_OP_TEST_DEFINE(TextureOp) { + GrSurfaceDesc desc; + desc.fConfig = kRGBA_8888_GrPixelConfig; + desc.fHeight = random->nextULessThan(90) + 10; + desc.fWidth = random->nextULessThan(90) + 10; + desc.fOrigin = random->nextBool() ? kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin; + SkBackingFit fit = random->nextBool() ? SkBackingFit::kApprox : SkBackingFit::kExact; + auto proxy = + GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc, fit, SkBudgeted::kNo); + SkRect rect = GrTest::TestRect(random); + SkRect srcRect; + srcRect.fLeft = random->nextRangeScalar(0.f, proxy->width() / 2.f); + srcRect.fRight = random->nextRangeScalar(0.f, proxy->width()) + proxy->width() / 2.f; + srcRect.fTop = random->nextRangeScalar(0.f, proxy->height() / 2.f); + srcRect.fBottom = random->nextRangeScalar(0.f, proxy->height()) + proxy->height() / 2.f; + SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random); + GrColor color = SkColorToPremulGrColor(random->nextU()); + GrSamplerParams::FilterMode filter = (GrSamplerParams::FilterMode)random->nextULessThan( + GrSamplerParams::kMipMap_FilterMode + 1); + auto csxf = GrTest::TestColorXform(random); + bool allowSRGBInputs = random->nextBool(); + return GrTextureOp::Make(std::move(proxy), filter, color, srcRect, rect, viewMatrix, + std::move(csxf), allowSRGBInputs); +} + +#endif diff --git a/src/gpu/ops/GrTextureOp.h b/src/gpu/ops/GrTextureOp.h new file mode 100644 index 0000000000..cf6a227769 --- /dev/null +++ b/src/gpu/ops/GrTextureOp.h @@ -0,0 +1,28 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrColor.h" +#include "GrColorSpaceXform.h" +#include "GrSamplerParams.h" + +class GrDrawOp; +class GrTextureProxy; +struct SkRect; +class SkMatrix; + +namespace GrTextureOp { +/** + * Creates an op that draws a sub-rectangle of a texture. The passed color is modulated by the + * texture's color. 'srcRect' specifies the rectangle of the texture to draw. 'dstRect' specifies + * the rectangle to draw in local coords which will be transformed by 'viewMatrix' to be in device + * space. 'viewMatrix' must be affine. + */ +std::unique_ptr Make(sk_sp, GrSamplerParams::FilterMode, GrColor, + const SkRect& srcRect, const SkRect& dstRect, + const SkMatrix& viewMatrix, sk_sp, + bool allowSRGBInputs); +} diff --git a/tools/gpu/GrTest.cpp b/tools/gpu/GrTest.cpp index c5af01e0df..57ff77e5e7 100644 --- a/tools/gpu/GrTest.cpp +++ b/tools/gpu/GrTest.cpp @@ -316,32 +316,34 @@ DRAW_OP_TEST_EXTERN(SmallPathOp); DRAW_OP_TEST_EXTERN(RegionOp); DRAW_OP_TEST_EXTERN(RRectOp); DRAW_OP_TEST_EXTERN(TesselatingPathOp); +DRAW_OP_TEST_EXTERN(TextureOp); void GrDrawRandomOp(SkRandom* random, GrRenderTargetContext* renderTargetContext, GrPaint&& paint) { GrContext* context = renderTargetContext->surfPriv().getContext(); using MakeDrawOpFn = std::unique_ptr(GrPaint&&, SkRandom*, GrContext*, GrFSAAType); static constexpr MakeDrawOpFn* gFactories[] = { - DRAW_OP_TEST_ENTRY(AAConvexPathOp), - DRAW_OP_TEST_ENTRY(AAFillRectOp), - DRAW_OP_TEST_ENTRY(AAFlatteningConvexPathOp), - DRAW_OP_TEST_ENTRY(AAHairlineOp), - DRAW_OP_TEST_ENTRY(AAStrokeRectOp), - DRAW_OP_TEST_ENTRY(CircleOp), - DRAW_OP_TEST_ENTRY(DashOp), - DRAW_OP_TEST_ENTRY(DefaultPathOp), - DRAW_OP_TEST_ENTRY(DIEllipseOp), - DRAW_OP_TEST_ENTRY(EllipseOp), - DRAW_OP_TEST_ENTRY(GrAtlasTextOp), - DRAW_OP_TEST_ENTRY(GrDrawAtlasOp), - DRAW_OP_TEST_ENTRY(GrDrawVerticesOp), - DRAW_OP_TEST_ENTRY(NonAAFillRectOp), - DRAW_OP_TEST_ENTRY(NonAALatticeOp), - DRAW_OP_TEST_ENTRY(NonAAStrokeRectOp), - DRAW_OP_TEST_ENTRY(ShadowRRectOp), - DRAW_OP_TEST_ENTRY(SmallPathOp), - DRAW_OP_TEST_ENTRY(RegionOp), - DRAW_OP_TEST_ENTRY(RRectOp), - DRAW_OP_TEST_ENTRY(TesselatingPathOp), + DRAW_OP_TEST_ENTRY(AAConvexPathOp), + DRAW_OP_TEST_ENTRY(AAFillRectOp), + DRAW_OP_TEST_ENTRY(AAFlatteningConvexPathOp), + DRAW_OP_TEST_ENTRY(AAHairlineOp), + DRAW_OP_TEST_ENTRY(AAStrokeRectOp), + DRAW_OP_TEST_ENTRY(CircleOp), + DRAW_OP_TEST_ENTRY(DashOp), + DRAW_OP_TEST_ENTRY(DefaultPathOp), + DRAW_OP_TEST_ENTRY(DIEllipseOp), + DRAW_OP_TEST_ENTRY(EllipseOp), + DRAW_OP_TEST_ENTRY(GrAtlasTextOp), + DRAW_OP_TEST_ENTRY(GrDrawAtlasOp), + DRAW_OP_TEST_ENTRY(GrDrawVerticesOp), + DRAW_OP_TEST_ENTRY(NonAAFillRectOp), + DRAW_OP_TEST_ENTRY(NonAALatticeOp), + DRAW_OP_TEST_ENTRY(NonAAStrokeRectOp), + DRAW_OP_TEST_ENTRY(ShadowRRectOp), + DRAW_OP_TEST_ENTRY(SmallPathOp), + DRAW_OP_TEST_ENTRY(RegionOp), + DRAW_OP_TEST_ENTRY(RRectOp), + DRAW_OP_TEST_ENTRY(TesselatingPathOp), + DRAW_OP_TEST_ENTRY(TextureOp), }; static constexpr size_t kTotal = SK_ARRAY_COUNT(gFactories);