diff --git a/bench/CompositingImagesBench.cpp b/bench/CompositingImagesBench.cpp new file mode 100644 index 0000000000..7052bb685e --- /dev/null +++ b/bench/CompositingImagesBench.cpp @@ -0,0 +1,99 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Benchmark.h" + +#include "SkCanvas.h" +#include "SkImage.h" +#include "SkRandom.h" +#include "SkSurface.h" + +/** + * Simulates drawing layers images in a grid a la a tile based compositor. The layers are all + * untransformed. + */ +class CompositingImages : public Benchmark { +public: + CompositingImages(SkISize tileSize, SkISize tileGridSize, int layerCnt) + : fTileSize(tileSize), fTileGridSize(tileGridSize), fLayerCnt(layerCnt) { + fName.appendf("compositing_images_tile_size_%dx%d_tile_cnt_%dx%d_layers_%d", + fTileSize.fWidth, fTileSize.fHeight, fTileGridSize.fWidth, + fTileGridSize.fHeight, fLayerCnt); + } + + bool isSuitableFor(Backend backend) override { return kGPU_Backend == backend; } + +protected: + const char* onGetName() override { return fName.c_str(); } + + void onPerCanvasPreDraw(SkCanvas* canvas) override { + auto ii = SkImageInfo::Make(fTileSize.fWidth, fTileSize.fHeight, kRGBA_8888_SkColorType, + kPremul_SkAlphaType, nullptr); + SkRandom random; + int numImages = fLayerCnt * fTileGridSize.fWidth * fTileGridSize.fHeight; + fImages.reset(new sk_sp[numImages]); + for (int i = 0; i < numImages; ++i) { + auto surf = canvas->makeSurface(ii); + SkColor color = random.nextU(); + surf->getCanvas()->clear(color); + SkPaint paint; + paint.setColor(~color); + paint.setBlendMode(SkBlendMode::kSrc); + surf->getCanvas()->drawRect( + SkRect::MakeLTRB(3, 3, fTileSize.fWidth - 3, fTileSize.fHeight - 3), paint); + fImages[i] = surf->makeImageSnapshot(); + } + } + + void onPerCanvasPostDraw(SkCanvas*) override { fImages.reset(); } + + void onDraw(int loops, SkCanvas* canvas) override { + SkPaint paint; + paint.setFilterQuality(kNone_SkFilterQuality); + // TODO: Use per-edge AA flags for tiles when API available. + paint.setAntiAlias(true); + for (int i = 0; i < loops; ++i) { + int imgIdx = 0; + for (int l = 0; l < fLayerCnt; ++l) { + for (int y = 0; y < fTileGridSize.fHeight; ++y) { + for (int x = 0; x < fTileGridSize.fWidth; ++x) { + canvas->drawImage(fImages[imgIdx++].get(), x * fTileSize.fWidth, + y * fTileSize.fHeight, &paint); + } + } + } + // Prevent any batching between composited "frames". + canvas->flush(); + } + } + +private: + SkIPoint onGetSize() override { + return SkIPoint::Make(fTileSize.fWidth * fTileGridSize.fWidth, + fTileSize.fHeight * fTileGridSize.fHeight); + } + + std::unique_ptr[]> fImages; + SkString fName; + SkISize fTileSize; + SkISize fTileGridSize; + int fLayerCnt; + + typedef Benchmark INHERITED; +}; + +DEF_BENCH(return new CompositingImages({256, 256}, {8, 8}, 1)); +DEF_BENCH(return new CompositingImages({512, 512}, {4, 4}, 1)); +DEF_BENCH(return new CompositingImages({1024, 512}, {2, 4}, 1)); + +DEF_BENCH(return new CompositingImages({256, 256}, {8, 8}, 4)); +DEF_BENCH(return new CompositingImages({512, 512}, {4, 4}, 4)); +DEF_BENCH(return new CompositingImages({1024, 512}, {2, 4}, 4)); + +DEF_BENCH(return new CompositingImages({256, 256}, {8, 8}, 16)); +DEF_BENCH(return new CompositingImages({512, 512}, {4, 4}, 16)); +DEF_BENCH(return new CompositingImages({1024, 512}, {2, 4}, 16)); diff --git a/bench/ImageCycleBench.cpp b/bench/ImageCycleBench.cpp new file mode 100644 index 0000000000..e7e00251b8 --- /dev/null +++ b/bench/ImageCycleBench.cpp @@ -0,0 +1,94 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Benchmark.h" + +#include "SkCanvas.h" +#include "SkImage.h" +#include "SkRandom.h" +#include "SkSurface.h" + +/** + * Draws a small set of small images multiple times each with no overlaps so that each image could + * be batched. This was originally added to detect regressions as GrTextureOp is refactored to + * use "dynamic state" for texture bindings. Everything is kept small as we're mostly interested in + * CPU overhead. + */ +class ImageCycle : public Benchmark { +public: + /** + * imageCnt is the number of images and repeat cnt is how many times each image is drawn per + * logical "frame." + */ + ImageCycle(int imageCnt, int repeatCnt) : fImageCnt(imageCnt), fRepeatCnt(repeatCnt) { + fName.appendf("image_cycle_image_cnt_%d_repeat_cnt_%d", fImageCnt, fRepeatCnt); + } + + bool isSuitableFor(Backend backend) override { return kGPU_Backend == backend; } + +protected: + const char* onGetName() override { return fName.c_str(); } + + void onPerCanvasPreDraw(SkCanvas* canvas) override { + auto ii = SkImageInfo::Make(kImageSize.fWidth, kImageSize.fHeight, kRGBA_8888_SkColorType, + kPremul_SkAlphaType, nullptr); + SkRandom random; + fImages.reset(new sk_sp[fImageCnt]); + for (int i = 0; i < fImageCnt; ++i) { + auto surf = canvas->makeSurface(ii); + SkColor color = random.nextU(); + surf->getCanvas()->clear(color); + SkPaint paint; + paint.setColor(~color); + paint.setBlendMode(SkBlendMode::kSrc); + surf->getCanvas()->drawRect( + SkRect::MakeLTRB(1, 1, kImageSize.fWidth - 1, kImageSize.fHeight - 1), paint); + fImages[i] = surf->makeImageSnapshot(); + } + } + + void onPerCanvasPostDraw(SkCanvas*) override { fImages.reset(); } + + void onDraw(int loops, SkCanvas* canvas) override { + SkPaint paint; + paint.setFilterQuality(kNone_SkFilterQuality); + paint.setAntiAlias(true); + static constexpr SkScalar kPad = 2; + // To avoid tripping up bounds tracking we position the draws such that all the + // draws of image 0 are above those of image 1, etc. + static const int imagesPerRow = + SkScalarFloorToInt(kDeviceSize.fWidth / (kImageSize.fWidth + kPad)); + int rowsPerImage = SkScalarCeilToInt((SkScalar)fRepeatCnt / imagesPerRow); + for (int l = 0; l < loops; ++l) { + for (int r = 0; r < fRepeatCnt; ++r) { + for (int i = 0; i < fImageCnt; ++i) { + SkScalar imageYOffset = i * rowsPerImage * (kImageSize.fHeight + kPad); + SkScalar rowYOffset = (r / imagesPerRow) * (kImageSize.fHeight + kPad); + SkScalar x = (r % imagesPerRow) * (kImageSize.fWidth + kPad); + canvas->drawImage(fImages[i].get(), x, imageYOffset + rowYOffset, &paint); + } + } + // Prevent any batching between "frames". + canvas->flush(); + } + } + +private: + SkIPoint onGetSize() override { return {kDeviceSize.fWidth, kDeviceSize.fHeight}; } + + static constexpr SkISize kImageSize{4, 4}; + static constexpr SkISize kDeviceSize{64, 64}; + + std::unique_ptr[]> fImages; + SkString fName; + int fImageCnt; + int fRepeatCnt; + + typedef Benchmark INHERITED; +}; + +DEF_BENCH(return new ImageCycle(5, 10)); diff --git a/bench/MultitextureImageBench.cpp b/bench/MultitextureImageBench.cpp deleted file mode 100644 index edbb4de4e9..0000000000 --- a/bench/MultitextureImageBench.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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 "Benchmark.h" - -#include "GrContextOptions.h" -#include "SkCanvas.h" -#include "SkImage.h" -#include "SkRandom.h" -#include "SkSurface.h" - -class MultitextureImages : public Benchmark { -public: - MultitextureImages(int imageSize, int dstRectSize, bool disableMultitexturing, bool aa) - : fImageSize(imageSize) - , fDstRectSize(dstRectSize) - , fDisableMultitexturing(disableMultitexturing) - , fAA(aa) { - fName.appendf("multitexture_images_%dx%d_image_%dx%d_rect", imageSize, imageSize, - dstRectSize, dstRectSize); - if (aa) { - fName.append("_aa"); - } - if (disableMultitexturing) { - fName.append("_disable_multitexturing"); - } - } - - bool isSuitableFor(Backend backend) override { return kGPU_Backend == backend; } - -protected: - const char* onGetName() override { return fName.c_str(); } - - void onPerCanvasPreDraw(SkCanvas* canvas) override { - auto ii = SkImageInfo::Make(fImageSize, fImageSize, kRGBA_8888_SkColorType, - kPremul_SkAlphaType, nullptr); - SkRandom random; - for (int i = 0; i < kNumImages; ++i) { - auto surf = canvas->makeSurface(ii); - SkColor color = random.nextU(); - surf->getCanvas()->clear(color); - SkPaint paint; - paint.setColor(~color); - paint.setBlendMode(SkBlendMode::kSrc); - surf->getCanvas()->drawRect(SkRect::MakeLTRB(3, 3, fImageSize - 3, fImageSize - 3), - paint); - fImages[i] = surf->makeImageSnapshot(); - } - } - - void onPerCanvasPostDraw(SkCanvas*) override { - for (int i = 0; i < kNumImages; ++i) { - fImages[i].reset(); - } - } - - void onDraw(int loops, SkCanvas* canvas) override { - SkRect rect = SkRect::MakeWH(fDstRectSize, fDstRectSize); - SkPaint paint; - paint.setAlpha(0x40); - paint.setFilterQuality(kLow_SkFilterQuality); - paint.setAntiAlias(fAA); - for (int i = 0; i < loops; i++) { - for (int j = 0; j < kNumImages; ++j) { - SkVector translate = this->translation(i * kNumImages + j); - canvas->drawImageRect(fImages[j].get(), rect.makeOffset(translate.fX, translate.fY), - &paint); - } - // Prevent any batching except without multitexturing since we're trying to measure - // drawing distinct images and just repeating images here to increase the workload for - // timing reasons. - canvas->flush(); - } - } - - void modifyGrContextOptions(GrContextOptions* options) override { - options->fDisableImageMultitexturing = fDisableMultitexturing; - } - -private: - SkIPoint onGetSize() override { - // The rows and columns are spaced by kTranslate, but the images may overlap if they are - // larger than kTranslate and extend beyond the last row/column. - return SkIPoint::Make(kTranslate * (kNumColumns - 1) + fDstRectSize, - kTranslate * (kNumRows - 1) + fDstRectSize); - } - - SkVector translation(int i) const { - SkVector offset; - // Fractional offsets to ensure we can't ignore antialiasing. - offset.fX = i % kNumColumns * kTranslate + 0.1f; - offset.fY = (i / kNumColumns) % kNumRows * kTranslate + 0.1f; - return offset; - } - - static const int kTranslate = 200; - static const int kNumColumns = 5; - static const int kNumRows = 5; - static const int kNumImages = 8; - - sk_sp fImages[kNumImages]; - SkString fName; - int fImageSize; - int fDstRectSize; - bool fDisableMultitexturing; - bool fAA; - - typedef Benchmark INHERITED; -}; - -// Non-AA -DEF_BENCH(return new MultitextureImages(128, 32, false, false)); -DEF_BENCH(return new MultitextureImages(128, 32, true, false)); -DEF_BENCH(return new MultitextureImages(128, 128, false, false)); -DEF_BENCH(return new MultitextureImages(128, 128, true, false)); -DEF_BENCH(return new MultitextureImages(128, 256, false, false)); -DEF_BENCH(return new MultitextureImages(128, 256, true, false)); - -DEF_BENCH(return new MultitextureImages(512, 32, false, false)); -DEF_BENCH(return new MultitextureImages(512, 32, true, false)); -DEF_BENCH(return new MultitextureImages(512, 128, false, false)); -DEF_BENCH(return new MultitextureImages(512, 128, true, false)); -DEF_BENCH(return new MultitextureImages(512, 256, false, false)); -DEF_BENCH(return new MultitextureImages(512, 256, true, false)); -DEF_BENCH(return new MultitextureImages(512, 512, false, false)); -DEF_BENCH(return new MultitextureImages(512, 512, true, false)); - -// AA -DEF_BENCH(return new MultitextureImages(512, 512, true, true)); -DEF_BENCH(return new MultitextureImages(512, 512, false, true)); -DEF_BENCH(return new MultitextureImages(128, 32, true, true)); -DEF_BENCH(return new MultitextureImages(128, 32, false, true)); diff --git a/gn/bench.gni b/gn/bench.gni index b3b25611ec..f8dce13ddd 100644 --- a/gn/bench.gni +++ b/gn/bench.gni @@ -34,6 +34,7 @@ bench_sources = [ "$_bench/ColorCanvasDrawBitmapBench.cpp", "$_bench/ColorFilterBench.cpp", "$_bench/ColorPrivBench.cpp", + "$_bench/CompositingImagesBench.cpp", "$_bench/ControlBench.cpp", "$_bench/CoverageBench.cpp", "$_bench/CubicKLMBench.cpp", @@ -60,6 +61,7 @@ bench_sources = [ "$_bench/ImageBench.cpp", "$_bench/ImageCacheBench.cpp", "$_bench/ImageCacheBudgetBench.cpp", + "$_bench/ImageCycleBench.cpp", "$_bench/ImageFilterCollapse.cpp", "$_bench/ImageFilterDAGBench.cpp", "$_bench/InterpBench.cpp", @@ -75,7 +77,6 @@ bench_sources = [ "$_bench/MergeBench.cpp", "$_bench/MipMapBench.cpp", "$_bench/MorphologyBench.cpp", - "$_bench/MultitextureImageBench.cpp", "$_bench/MutexBench.cpp", "$_bench/PatchBench.cpp", "$_bench/PathBench.cpp", diff --git a/src/gpu/GrShaderCaps.cpp b/src/gpu/GrShaderCaps.cpp index 9ab5f90882..d29fe58729 100644 --- a/src/gpu/GrShaderCaps.cpp +++ b/src/gpu/GrShaderCaps.cpp @@ -64,10 +64,6 @@ GrShaderCaps::GrShaderCaps(const GrContextOptions& options) { fMaxFragmentSamplers = 0; fMaxCombinedSamplers = 0; fAdvBlendEqInteraction = kNotSupported_AdvBlendEqInteraction; - - // TODO: Default this to 0 and only enable image multitexturing when a "safe" threshold is - // known for a GPU class. - fDisableImageMultitexturingDstRectAreaThreshold = std::numeric_limits::max(); } void GrShaderCaps::dumpJSON(SkJSONWriter* writer) const { @@ -123,8 +119,6 @@ void GrShaderCaps::dumpJSON(SkJSONWriter* writer) const { writer->appendS32("Max Combined Samplers", fMaxFragmentSamplers); writer->appendString("Advanced blend equation interaction", kAdvBlendEqInteractionStr[fAdvBlendEqInteraction]); - writer->appendU64("Disable image multitexturing dst area threshold", - fDisableImageMultitexturingDstRectAreaThreshold); writer->endObject(); } @@ -145,8 +139,5 @@ void GrShaderCaps::applyOptionsOverrides(const GrContextOptions& options) { } #if GR_TEST_UTILS fDualSourceBlendingSupport = fDualSourceBlendingSupport && !options.fSuppressDualSourceBlending; - if (options.fDisableImageMultitexturing) { - fDisableImageMultitexturingDstRectAreaThreshold = 0; - } #endif } diff --git a/src/gpu/GrShaderCaps.h b/src/gpu/GrShaderCaps.h index a599f1deab..d8422c7c4c 100644 --- a/src/gpu/GrShaderCaps.h +++ b/src/gpu/GrShaderCaps.h @@ -204,15 +204,6 @@ public: int maxCombinedSamplers() const { return fMaxCombinedSamplers; } - /** - * In general using multiple texture units for image rendering seems to be a win at smaller - * sizes of dst rects and a loss at larger sizes. Dst rects above this pixel area threshold will - * not use multitexturing. - */ - size_t disableImageMultitexturingDstRectAreaThreshold() const { - return fDisableImageMultitexturingDstRectAreaThreshold; - } - /** * Given a texture's config, this determines what swizzle must be appended to accesses to the * texture in generated shader code. Swizzling may be implemented in texture parameters or a diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp index c0549e98f7..f4960446f1 100644 --- a/src/gpu/gl/GrGLCaps.cpp +++ b/src/gpu/gl/GrGLCaps.cpp @@ -364,30 +364,6 @@ void GrGLCaps::init(const GrContextOptions& contextOptions, GR_GL_GetIntegerv(gli, GR_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxSamplers); shaderCaps->fMaxCombinedSamplers = SkTMin(kMaxSaneSamplers, maxSamplers); - // This is all *very* approximate. - switch (ctxInfo.vendor()) { - case kNVIDIA_GrGLVendor: - // We've seen a range from 100 x 100 (TegraK1, GTX660) up to 300 x 300 (GTX 1070) - // but it doesn't clearly align with Pascal vs Maxwell vs Kepler. - fShaderCaps->fDisableImageMultitexturingDstRectAreaThreshold = 150 * 150; - break; - case kImagination_GrGLVendor: - // Two PowerVR Rogues, Nexus Player and Chromebook Cb5-312T (PowerVR GX6250), show that - // it is always a win to use multitexturing. - if (kPowerVRRogue_GrGLRenderer == ctxInfo.renderer()) { - fShaderCaps->fDisableImageMultitexturingDstRectAreaThreshold = - std::numeric_limits::max(); - } - break; - case kATI_GrGLVendor: - // So far no AMD GPU shows a performance difference. A tie goes to disabling - // multitexturing for simplicity's sake. - fShaderCaps->fDisableImageMultitexturingDstRectAreaThreshold = 0; - break; - default: - break; - } - // SGX and Mali GPUs that are based on a tiled-deferred architecture that have trouble with // frequently changing VBOs. We've measured a performance increase using non-VBO vertex // data for dynamic content on these GPUs. Perhaps we should read the renderer string and diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp index 89a890e4d9..2a8fadabe3 100644 --- a/src/gpu/ops/GrTextureOp.cpp +++ b/src/gpu/ops/GrTextureOp.cpp @@ -37,8 +37,6 @@ namespace { -enum class MultiTexture : bool { kNo = false, kYes = true }; - enum class Domain : bool { kNo = false, kYes = true }; /** @@ -55,78 +53,40 @@ public: SkPoint fTextureCoords; }; - template struct OptionalMultiTextureVertex; + template struct OptionalDomainVertex; template - struct OptionalMultiTextureVertex : VertexCommon { - static constexpr MultiTexture kMultiTexture = MultiTexture::kNo; - }; - template - struct OptionalMultiTextureVertex : VertexCommon { - static constexpr MultiTexture kMultiTexture = MultiTexture::kYes; - int fTextureIdx; - }; - - template struct OptionalDomainVertex; - template - struct OptionalDomainVertex : OptionalMultiTextureVertex { + struct OptionalDomainVertex : VertexCommon { static constexpr Domain kDomain = Domain::kNo; }; - template - struct OptionalDomainVertex : OptionalMultiTextureVertex { + template + struct OptionalDomainVertex : VertexCommon { static constexpr Domain kDomain = Domain::kYes; SkRect fTextureDomain; }; - template struct OptionalAAVertex; - template - struct OptionalAAVertex : OptionalDomainVertex { + template struct OptionalAAVertex; + template + struct OptionalAAVertex : OptionalDomainVertex { static constexpr GrAA kAA = GrAA::kNo; }; - template - struct OptionalAAVertex : OptionalDomainVertex { + template + struct OptionalAAVertex : OptionalDomainVertex { static constexpr GrAA kAA = GrAA::kYes; SkPoint3 fEdges[4]; }; - template - using Vertex = OptionalAAVertex; - - // Maximum number of textures supported by this op. Must also be checked against the caps - // limit. These numbers were based on some limited experiments on a HP Z840 and Pixel XL 2016 - // and could probably use more tuning. -#ifdef SK_BUILD_FOR_ANDROID - static constexpr int kMaxTextures = 4; -#else - static constexpr int kMaxTextures = 8; -#endif - - static int SupportsMultitexture(const GrShaderCaps& caps) { - return caps.integerSupport() && caps.maxFragmentSamplers() > 1; - } + template + using Vertex = OptionalAAVertex; static sk_sp Make(GrTextureType textureType, GrPixelConfig textureConfig, - int textureCnt, + const GrSamplerState::Filter filter, sk_sp textureColorSpaceXform, sk_sp paintColorSpaceXform, bool coverageAA, bool perspective, Domain domain, - const GrSamplerState::Filter filters[], const GrShaderCaps& caps) { - // We use placement new to avoid always allocating space for kMaxTextures TextureSampler - // instances. - int samplerCnt = NumSamplersToUse(textureCnt, caps); - size_t size = sizeof(TextureGeometryProcessor) + sizeof(TextureSampler) * (samplerCnt - 1); - void* mem = GrGeometryProcessor::operator new(size); - return sk_sp(new (mem) TextureGeometryProcessor( - textureType, textureConfig, textureCnt, samplerCnt, - std::move(textureColorSpaceXform), std::move(paintColorSpaceXform), coverageAA, - perspective, domain, filters, caps)); - } - - ~TextureGeometryProcessor() override { - int cnt = this->numTextureSamplers(); - for (int i = 1; i < cnt; ++i) { - fSamplers[i].~TextureSampler(); - } + return sk_sp(new TextureGeometryProcessor( + textureType, textureConfig, filter, std::move(textureColorSpaceXform), + std::move(paintColorSpaceXform), coverageAA, perspective, domain, caps)); } const char* name() const override { return "TextureGeometryProcessor"; } @@ -196,28 +156,10 @@ public: args.fFragBuilder->codeAppend( "texCoord = clamp(texCoord, domain.xy, domain.zw);"); } - if (textureGP.numTextureSamplers() > 1) { - // If this changes to float, reconsider Interpolation::kMustBeFlat. - SkASSERT(kInt_GrVertexAttribType == textureGP.fTextureIdx.type()); - SkASSERT(args.fShaderCaps->integerSupport()); - args.fFragBuilder->codeAppend("int texIdx;"); - args.fVaryingHandler->addPassThroughAttribute(textureGP.fTextureIdx, "texIdx", - Interpolation::kMustBeFlat); - args.fFragBuilder->codeAppend("switch (texIdx) {"); - for (int i = 0; i < textureGP.numTextureSamplers(); ++i) { - args.fFragBuilder->codeAppendf("case %d: %s = ", i, args.fOutputColor); - args.fFragBuilder->appendTextureLookupAndModulate( - args.fOutputColor, args.fTexSamplers[i], "texCoord", - kFloat2_GrSLType, &fTextureColorSpaceXformHelper); - args.fFragBuilder->codeAppend("; break;"); - } - args.fFragBuilder->codeAppend("}"); - } else { - args.fFragBuilder->codeAppendf("%s = ", args.fOutputColor); - args.fFragBuilder->appendTextureLookupAndModulate( - args.fOutputColor, args.fTexSamplers[0], "texCoord", - kFloat2_GrSLType, &fTextureColorSpaceXformHelper); - } + args.fFragBuilder->codeAppendf("%s = ", args.fOutputColor); + args.fFragBuilder->appendTextureLookupAndModulate( + args.fOutputColor, args.fTexSamplers[0], "texCoord", kFloat2_GrSLType, + &fTextureColorSpaceXformHelper); args.fFragBuilder->codeAppend(";"); if (textureGP.usesCoverageEdgeAA()) { bool mulByFragCoordW = false; @@ -270,36 +212,16 @@ public: bool usesCoverageEdgeAA() const { return SkToBool(fAAEdges[0].isInitialized()); } private: - // This exists to reduce the number of shaders generated. It does some rounding of sampler - // counts. - static int NumSamplersToUse(int numRealProxies, const GrShaderCaps& caps) { - SkASSERT(numRealProxies > 0 && numRealProxies <= kMaxTextures && - numRealProxies <= caps.maxFragmentSamplers()); - if (1 == numRealProxies) { - return 1; - } - if (numRealProxies <= 4) { - return 4; - } - // Round to the next power of 2 and then clamp to kMaxTextures and the max allowed by caps. - return SkTMin(SkNextPow2(numRealProxies), SkTMin(kMaxTextures, caps.maxFragmentSamplers())); - } - - TextureGeometryProcessor(GrTextureType textureType, GrPixelConfig textureConfig, int textureCnt, - int samplerCnt, sk_sp textureColorSpaceXform, + TextureGeometryProcessor(GrTextureType textureType, GrPixelConfig textureConfig, + GrSamplerState::Filter filter, + sk_sp textureColorSpaceXform, sk_sp paintColorSpaceXform, bool coverageAA, - bool perspective, Domain domain, - const GrSamplerState::Filter filters[], const GrShaderCaps& caps) + bool perspective, Domain domain, const GrShaderCaps& caps) : INHERITED(kTextureGeometryProcessor_ClassID) , fTextureColorSpaceXform(std::move(textureColorSpaceXform)) - , fPaintColorSpaceXform(std::move(paintColorSpaceXform)) { - SkASSERT(textureCnt > 0 && samplerCnt >= textureCnt); - fSamplers[0].reset(textureType, textureConfig, filters[0]); - for (int i = 1; i < textureCnt; ++i) { - // This class has one sampler built in, the rest come from memory this processor was - // placement-newed into and so haven't been constructed. - new (&fSamplers[i]) TextureSampler(textureType, textureConfig, filters[i]); - } + , fPaintColorSpaceXform(std::move(paintColorSpaceXform)) + , fSampler(textureType, textureConfig, filter) { + this->setTextureSamplerCnt(1); if (perspective) { fPositions = {"position", kFloat3_GrVertexAttribType}; @@ -310,17 +232,6 @@ private: fTextureCoords = {"textureCoords", kFloat2_GrVertexAttribType}; int vertexAttributeCnt = 3; - if (samplerCnt > 1) { - // Here we initialize any extra samplers. We repeat the first filter because our caller - // will specify the first texture for all the extra samplers. In GL the filter is - // implemented as a texture parameter and the last sampler will win. - for (int i = textureCnt; i < samplerCnt; ++i) { - new (&fSamplers[i]) TextureSampler(textureType, textureConfig, filters[0]); - } - SkASSERT(caps.integerSupport()); - fTextureIdx = {"textureIdx", kInt_GrVertexAttribType}; - ++vertexAttributeCnt; - } if (domain == Domain::kYes) { fDomain = {"domain", kFloat4_GrVertexAttribType}; ++vertexAttributeCnt; @@ -333,25 +244,23 @@ private: vertexAttributeCnt += 4; } this->setVertexAttributeCnt(vertexAttributeCnt); - this->setTextureSamplerCnt(samplerCnt); } const Attribute& onVertexAttribute(int i) const override { - return IthInitializedAttribute(i, fPositions, fColors, fTextureCoords, fTextureIdx, fDomain, - fAAEdges[0], fAAEdges[1], fAAEdges[2], fAAEdges[3]); + return IthInitializedAttribute(i, fPositions, fColors, fTextureCoords, fDomain, fAAEdges[0], + fAAEdges[1], fAAEdges[2], fAAEdges[3]); } - const TextureSampler& onTextureSampler(int i) const override { return fSamplers[i]; } + const TextureSampler& onTextureSampler(int) const override { return fSampler; } Attribute fPositions; Attribute fColors; Attribute fTextureCoords; - Attribute fTextureIdx; Attribute fDomain; Attribute fAAEdges[4]; sk_sp fTextureColorSpaceXform; sk_sp fPaintColorSpaceXform; - TextureSampler fSamplers[1]; + TextureSampler fSampler; typedef GrGeometryProcessor INHERITED; }; @@ -526,20 +435,6 @@ private: } }; -template struct TexIdAssigner; - -template struct TexIdAssigner { - static void Assign(V* vertices, int textureIdx) { - for (int i = 0; i < 4; ++i) { - vertices[i].fTextureIdx = textureIdx; - } - } -}; - -template struct TexIdAssigner { - static void Assign(V* vertices, int textureIdx) {} -}; - template struct DomainAssigner; template struct DomainAssigner { @@ -584,7 +479,7 @@ template struct DomainAssigner { template static void tessellate_quad(const GrPerspQuad& devQuad, const SkRect& srcRect, GrColor color, GrSurfaceOrigin origin, GrSamplerState::Filter filter, V* vertices, - SkScalar iw, SkScalar ih, int textureIdx, Domain domain) { + SkScalar iw, SkScalar ih, Domain domain) { SkRect texRect = { iw * srcRect.fLeft, ih * srcRect.fTop, @@ -600,7 +495,6 @@ static void tessellate_quad(const GrPerspQuad& devQuad, const SkRect& srcRect, G vertices[1].fColor = color; vertices[2].fColor = color; vertices[3].fColor = color; - TexIdAssigner::Assign(vertices, textureIdx); DomainAssigner::Assign(vertices, domain, filter, srcRect, origin, iw, ih); } @@ -631,42 +525,27 @@ public: ~TextureOp() override { if (fFinalized) { - auto proxies = this->proxies(); - for (int i = 0; i < fProxyCnt; ++i) { - proxies[i]->completedRead(); - } - if (fProxyCnt > 1) { - delete[] reinterpret_cast(proxies); - } + fProxy->completedRead(); } else { - SkASSERT(1 == fProxyCnt); - fProxy0->unref(); + fProxy->unref(); } } const char* name() const override { return "TextureOp"; } - void visitProxies(const VisitProxyFunc& func) const override { - auto proxies = this->proxies(); - for (int i = 0; i < fProxyCnt; ++i) { - func(proxies[i]); - } - } + void visitProxies(const VisitProxyFunc& func) const override { func(fProxy); } SkString dumpInfo() const override { SkString str; str.appendf("# draws: %d\n", fDraws.count()); - auto proxies = this->proxies(); - for (int i = 0; i < fProxyCnt; ++i) { - str.appendf("Proxy ID %d: %d, Filter: %d\n", i, proxies[i]->uniqueID().asUInt(), - static_cast(this->filters()[i])); - } + str.appendf("Proxy ID: %d, Filter: %d\n", fProxy->uniqueID().asUInt(), + static_cast(fFilter)); for (int i = 0; i < fDraws.count(); ++i) { const Draw& draw = fDraws[i]; str.appendf( - "%d: Color: 0x%08x, ProxyIdx: %d, TexRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f] " + "%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.color(), draw.textureIdx(), draw.srcRect().fLeft, draw.srcRect().fTop, + i, draw.color(), draw.srcRect().fLeft, draw.srcRect().fTop, draw.srcRect().fRight, draw.srcRect().fBottom, draw.quad().point(0).fX, draw.quad().point(0).fY, draw.quad().point(1).fX, draw.quad().point(1).fY, draw.quad().point(2).fX, draw.quad().point(2).fY, draw.quad().point(3).fX, @@ -678,10 +557,9 @@ public: RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override { SkASSERT(!fFinalized); - SkASSERT(1 == fProxyCnt); fFinalized = true; - fProxy0->addPendingRead(); - fProxy0->unref(); + fProxy->addPendingRead(); + fProxy->unref(); return RequiresDstTexture::kNo; } @@ -695,17 +573,6 @@ public: private: friend class ::GrOpMemoryPool; - // This is used in a heursitic for choosing a code path. We don't care what happens with - // really large rects, infs, nans, etc. -#if defined(__clang__) && (__clang_major__ * 1000 + __clang_minor__) >= 3007 -__attribute__((no_sanitize("float-cast-overflow"))) -#endif - size_t RectSizeAsSizeT(const SkRect& rect) {; - return static_cast(SkTMax(rect.width(), 1.f) * SkTMax(rect.height(), 1.f)); - } - - static constexpr int kMaxTextures = TextureGeometryProcessor::kMaxTextures; - TextureOp(sk_sp proxy, GrSamplerState::Filter filter, GrColor color, const SkRect& srcRect, const SkRect& dstRect, GrAAType aaType, SkCanvas::SrcRectConstraint constraint, const SkMatrix& viewMatrix, @@ -714,9 +581,8 @@ __attribute__((no_sanitize("float-cast-overflow"))) : INHERITED(ClassID()) , fTextureColorSpaceXform(std::move(textureColorSpaceXform)) , fPaintColorSpaceXform(std::move(paintColorSpaceXform)) - , fProxy0(proxy.release()) - , fFilter0(filter) - , fProxyCnt(1) + , fProxy(proxy.release()) + , fFilter(filter) , fAAType(static_cast(aaType)) , fFinalized(0) { SkASSERT(aaType != GrAAType::kMixedSamples); @@ -739,44 +605,39 @@ __attribute__((no_sanitize("float-cast-overflow"))) } } #endif - const auto& draw = fDraws.emplace_back(srcRect, 0, quad, constraint, color); + const auto& draw = fDraws.emplace_back(srcRect, quad, constraint, color); this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo); fDomain = static_cast(draw.domain()); - fMaxApproxDstPixelArea = RectSizeAsSizeT(bounds); } - template - void tess(void* v, const float iw[], const float ih[], const GrGeometryProcessor* gp) { - using Vertex = TextureGeometryProcessor::Vertex; + template + void tess(void* v, const GrGeometryProcessor* gp) { + using Vertex = TextureGeometryProcessor::Vertex; SkASSERT(gp->debugOnly_vertexStride() == sizeof(Vertex)); auto vertices = static_cast(v); - auto proxies = this->proxies(); - auto filters = this->filters(); + auto origin = fProxy->origin(); + const auto* texture = fProxy->peekTexture(); + float iw = 1.f / texture->width(); + float ih = 1.f / texture->height(); + for (const auto& draw : fDraws) { - auto textureIdx = draw.textureIdx(); - auto origin = proxies[textureIdx]->origin(); - tessellate_quad(draw.quad(), draw.srcRect(), draw.color(), origin, - filters[textureIdx], vertices, iw[textureIdx], ih[textureIdx], - textureIdx, draw.domain()); + tessellate_quad(draw.quad(), draw.srcRect(), draw.color(), origin, fFilter, + vertices, iw, ih, draw.domain()); vertices += 4; } } void onPrepareDraws(Target* target) override { - auto proxies = this->proxies(); - auto filters = this->filters(); - for (int i = 0; i < fProxyCnt; ++i) { - if (!proxies[i]->instantiate(target->resourceProvider())) { - return; - } + if (!fProxy->instantiate(target->resourceProvider())) { + return; } Domain domain = fDomain ? Domain::kYes : Domain::kNo; bool coverageAA = GrAAType::kCoverage == this->aaType(); sk_sp gp = TextureGeometryProcessor::Make( - proxies[0]->textureType(), proxies[0]->config(), fProxyCnt, + fProxy->textureType(), fProxy->config(), fFilter, std::move(fTextureColorSpaceXform), std::move(fPaintColorSpaceXform), coverageAA, - fPerspective, domain, filters, *target->caps().shaderCaps()); + fPerspective, domain, *target->caps().shaderCaps()); GrPipeline::InitArgs args; args.fProxy = target->proxy(); args.fCaps = &target->caps(); @@ -787,53 +648,34 @@ __attribute__((no_sanitize("float-cast-overflow"))) } auto clip = target->detachAppliedClip(); - auto* fixedDynamicState = target->allocFixedDynamicState(clip.scissorState().rect(), - gp->numTextureSamplers()); - for (int i = 0; i < fProxyCnt; ++i) { - SkASSERT(proxies[i]->textureType() == proxies[0]->textureType()); - SkASSERT(proxies[i]->config() == proxies[0]->config()); - fixedDynamicState->fPrimitiveProcessorTextures[i] = proxies[i]; - } - // Rebind texture proxy 0 to the extra samplers. - for (int i = fProxyCnt; i < gp->numTextureSamplers(); ++i) { - fixedDynamicState->fPrimitiveProcessorTextures[i] = proxies[0]; - } + auto* fixedDynamicState = target->allocFixedDynamicState(clip.scissorState().rect(), 1); + fixedDynamicState->fPrimitiveProcessorTextures[0] = fProxy; const auto* pipeline = target->allocPipeline(args, GrProcessorSet::MakeEmptySet(), std::move(clip)); - using TessFn = - decltype(&TextureOp::tess); -#define TESS_FN_AND_VERTEX_SIZE(Point, MT, Domain, AA) \ - { \ - &TextureOp::tess, \ - sizeof(TextureGeometryProcessor::Vertex) \ + using TessFn = decltype(&TextureOp::tess); +#define TESS_FN_AND_VERTEX_SIZE(Point, Domain, AA) \ + { \ + &TextureOp::tess, \ + sizeof(TextureGeometryProcessor::Vertex) \ } static constexpr struct { TessFn fTessFn; size_t fVertexSize; } kTessFnsAndVertexSizes[] = { - TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kNo, Domain::kNo, GrAA::kNo), - TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kNo, Domain::kNo, GrAA::kYes), - TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kNo, Domain::kYes, GrAA::kNo), - TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kNo, Domain::kYes, GrAA::kYes), - TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kYes, Domain::kNo, GrAA::kNo), - TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kYes, Domain::kNo, GrAA::kYes), - TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kYes, Domain::kYes, GrAA::kNo), - TESS_FN_AND_VERTEX_SIZE(SkPoint, MultiTexture::kYes, Domain::kYes, GrAA::kYes), - TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kNo, Domain::kNo, GrAA::kNo), - TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kNo, Domain::kNo, GrAA::kYes), - TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kNo, Domain::kYes, GrAA::kNo), - TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kNo, Domain::kYes, GrAA::kYes), - TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kYes, Domain::kNo, GrAA::kNo), - TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kYes, Domain::kNo, GrAA::kYes), - TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kYes, Domain::kYes, GrAA::kNo), - TESS_FN_AND_VERTEX_SIZE(SkPoint3, MultiTexture::kYes, Domain::kYes, GrAA::kYes), + TESS_FN_AND_VERTEX_SIZE(SkPoint, Domain::kNo, GrAA::kNo), + TESS_FN_AND_VERTEX_SIZE(SkPoint, Domain::kNo, GrAA::kYes), + TESS_FN_AND_VERTEX_SIZE(SkPoint, Domain::kYes, GrAA::kNo), + TESS_FN_AND_VERTEX_SIZE(SkPoint, Domain::kYes, GrAA::kYes), + TESS_FN_AND_VERTEX_SIZE(SkPoint3, Domain::kNo, GrAA::kNo), + TESS_FN_AND_VERTEX_SIZE(SkPoint3, Domain::kNo, GrAA::kYes), + TESS_FN_AND_VERTEX_SIZE(SkPoint3, Domain::kYes, GrAA::kNo), + TESS_FN_AND_VERTEX_SIZE(SkPoint3, Domain::kYes, GrAA::kYes), }; #undef TESS_FN_AND_VERTEX_SIZE int tessFnIdx = 0; - tessFnIdx |= coverageAA ? 0x1 : 0x0; - tessFnIdx |= fDomain ? 0x2 : 0x0; - tessFnIdx |= (fProxyCnt > 1) ? 0x4 : 0x0; - tessFnIdx |= fPerspective ? 0x8 : 0x0; + tessFnIdx |= coverageAA ? 0x1 : 0x0; + tessFnIdx |= fDomain ? 0x2 : 0x0; + tessFnIdx |= fPerspective ? 0x4 : 0x0; SkASSERT(kTessFnsAndVertexSizes[tessFnIdx].fVertexSize == gp->debugOnly_vertexStride()); @@ -846,15 +688,7 @@ __attribute__((no_sanitize("float-cast-overflow"))) return; } - float iw[kMaxTextures]; - float ih[kMaxTextures]; - for (int t = 0; t < fProxyCnt; ++t) { - const auto* texture = proxies[t]->peekTexture(); - iw[t] = 1.f / texture->width(); - ih[t] = 1.f / texture->height(); - } - - (this->*(kTessFnsAndVertexSizes[tessFnIdx].fTessFn))(vdata, iw, ih, gp.get()); + (this->*(kTessFnsAndVertexSizes[tessFnIdx].fTessFn))(vdata, gp.get()); GrPrimitiveType primitiveType = fDraws.count() > 1 ? GrPrimitiveType::kTriangles : GrPrimitiveType::kTriangleStrip; @@ -874,9 +708,8 @@ __attribute__((no_sanitize("float-cast-overflow"))) target->draw(std::move(gp), pipeline, fixedDynamicState, mesh); } - bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { + bool onCombineIfPossible(GrOp* t, const GrCaps&) override { const auto* that = t->cast(); - const auto& shaderCaps = *caps.shaderCaps(); if (!GrColorSpaceXform::Equals(fTextureColorSpaceXform.get(), that->fTextureColorSpaceXform.get())) { return false; @@ -888,170 +721,42 @@ __attribute__((no_sanitize("float-cast-overflow"))) if (this->aaType() != that->aaType()) { return false; } - // Because of an issue where GrColorSpaceXform adds the same function every time it is used - // in a texture lookup, we only allow multiple textures when there is no transform. - if (TextureGeometryProcessor::SupportsMultitexture(shaderCaps) && - !fTextureColorSpaceXform && - fMaxApproxDstPixelArea <= shaderCaps.disableImageMultitexturingDstRectAreaThreshold() && - that->fMaxApproxDstPixelArea <= - shaderCaps.disableImageMultitexturingDstRectAreaThreshold()) { - int map[kMaxTextures]; - int numNewProxies = this->mergeProxies(that, map, shaderCaps); - if (numNewProxies < 0) { - return false; - } - if (1 == fProxyCnt && numNewProxies) { - void* mem = new char[(sizeof(GrSamplerState::Filter) + sizeof(GrTextureProxy*)) * - kMaxTextures]; - auto proxies = reinterpret_cast(mem); - auto filters = reinterpret_cast(proxies + kMaxTextures); - proxies[0] = fProxy0; - filters[0] = fFilter0; - fProxyArray = proxies; - } - fProxyCnt += numNewProxies; - auto thisProxies = fProxyArray; - auto thatProxies = that->proxies(); - auto thatFilters = that->filters(); - auto thisFilters = reinterpret_cast(thisProxies + - kMaxTextures); - for (int i = 0; i < that->fProxyCnt; ++i) { - if (map[i] < 0) { - thatProxies[i]->addPendingRead(); - - thisProxies[-map[i]] = thatProxies[i]; - thisFilters[-map[i]] = thatFilters[i]; - map[i] = -map[i]; - } - } - int firstNewDraw = fDraws.count(); - fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin()); - for (int i = firstNewDraw; i < fDraws.count(); ++i) { - fDraws[i].setTextureIdx(map[fDraws[i].textureIdx()]); - } - } else { - // We can get here when one of the ops is already multitextured but the other cannot - // be because of the dst rect size. - if (fProxyCnt > 1 || that->fProxyCnt > 1) { - return false; - } - if (fProxy0->uniqueID() != that->fProxy0->uniqueID() || fFilter0 != that->fFilter0) { - return false; - } - fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin()); + if (fProxy->uniqueID() != that->fProxy->uniqueID() || fFilter != that->fFilter) { + return false; } + fDraws.push_back_n(that->fDraws.count(), that->fDraws.begin()); this->joinBounds(*that); - fMaxApproxDstPixelArea = SkTMax(that->fMaxApproxDstPixelArea, fMaxApproxDstPixelArea); fPerspective |= that->fPerspective; fDomain |= that->fDomain; return true; } - /** - * Determines a mapping of indices from that's proxy array to this's proxy array. A negative map - * value means that's proxy should be added to this's proxy array at the absolute value of - * the map entry. If it is determined that the ops shouldn't combine their proxies then a - * negative value is returned. Otherwise, return value indicates the number of proxies that have - * to be added to this op or, equivalently, the number of negative entries in map. - */ - int mergeProxies(const TextureOp* that, int map[kMaxTextures], const GrShaderCaps& caps) const { - std::fill_n(map, kMaxTextures, -kMaxTextures); - int sharedProxyCnt = 0; - auto thisProxies = this->proxies(); - auto thisFilters = this->filters(); - auto thatProxies = that->proxies(); - auto thatFilters = that->filters(); - for (int i = 0; i < fProxyCnt; ++i) { - for (int j = 0; j < that->fProxyCnt; ++j) { - if (thisProxies[i]->uniqueID() == thatProxies[j]->uniqueID()) { - if (thisFilters[i] != thatFilters[j]) { - // In GL we don't currently support using the same texture with different - // samplers. If we added support for sampler objects and a cap bit to know - // it's ok to use different filter modes then we could support this. - // Otherwise, we could also only allow a single filter mode for each op - // instance. - return -1; - } - map[j] = i; - ++sharedProxyCnt; - break; - } - } - } - int actualMaxTextures = SkTMin(caps.maxFragmentSamplers(), kMaxTextures); - int newProxyCnt = that->fProxyCnt - sharedProxyCnt; - if (newProxyCnt + fProxyCnt > actualMaxTextures) { - return -1; - } - GrPixelConfig config = thisProxies[0]->config(); - int nextSlot = fProxyCnt; - for (int j = 0; j < that->fProxyCnt; ++j) { - // We want to avoid making many shaders because of different permutations of shader - // based swizzle and sampler types. The approach taken here is to require the configs to - // be the same and to only allow already instantiated proxies that have the most - // common sampler type. Otherwise we don't merge. - if (thatProxies[j]->config() != config) { - return -1; - } - if (GrTexture* tex = thatProxies[j]->peekTexture()) { - if (tex->texturePriv().textureType() != GrTextureType::k2D) { - return -1; - } - } - if (map[j] < 0) { - map[j] = -(nextSlot++); - } - } - return newProxyCnt; - } - GrAAType aaType() const { return static_cast(fAAType); } - GrTextureProxy* const* proxies() const { return fProxyCnt > 1 ? fProxyArray : &fProxy0; } - - const GrSamplerState::Filter* filters() const { - if (fProxyCnt > 1) { - return reinterpret_cast(fProxyArray + kMaxTextures); - } - return &fFilter0; - } - class Draw { public: - Draw(const SkRect& srcRect, int textureIdx, const GrPerspQuad& quad, - SkCanvas::SrcRectConstraint constraint, GrColor color) + Draw(const SkRect& srcRect, const GrPerspQuad& quad, SkCanvas::SrcRectConstraint constraint, + GrColor color) : fSrcRect(srcRect) - , fHasDomain(constraint == SkCanvas::kStrict_SrcRectConstraint) - , fTextureIdx(SkToUInt(textureIdx)) , fQuad(quad) - , fColor(color) {} + , fColor(color) + , fHasDomain(constraint == SkCanvas::kStrict_SrcRectConstraint) {} const GrPerspQuad& quad() const { return fQuad; } - int textureIdx() const { return SkToInt(fTextureIdx); } const SkRect& srcRect() const { return fSrcRect; } GrColor color() const { return fColor; } Domain domain() const { return Domain(fHasDomain); } - void setTextureIdx(int i) { fTextureIdx = SkToUInt(i); } private: SkRect fSrcRect; - unsigned fHasDomain : 1; - unsigned fTextureIdx : 31; GrPerspQuad fQuad; GrColor fColor; + bool fHasDomain; }; SkSTArray<1, Draw, true> fDraws; sk_sp fTextureColorSpaceXform; sk_sp fPaintColorSpaceXform; - // Initially we store a single proxy ptr and a single filter. If we grow to have more than - // one proxy we instead store pointers to dynamically allocated arrays of size kMaxTextures - // followed by kMaxTextures filters. - union { - GrTextureProxy* fProxy0; - GrTextureProxy** fProxyArray; - }; - size_t fMaxApproxDstPixelArea; - GrSamplerState::Filter fFilter0; - uint8_t fProxyCnt; + GrTextureProxy* fProxy; + GrSamplerState::Filter fFilter; unsigned fAAType : 2; unsigned fPerspective : 1; unsigned fDomain : 1; @@ -1061,9 +766,6 @@ __attribute__((no_sanitize("float-cast-overflow"))) typedef GrMeshDrawOp INHERITED; }; -constexpr int TextureGeometryProcessor::kMaxTextures; -constexpr int TextureOp::kMaxTextures; - } // anonymous namespace namespace GrTextureOp {