From 986f64c601f3ed99f84f0c392d1a42e298f6d618 Mon Sep 17 00:00:00 2001 From: Brian Salomon Date: Mon, 6 Aug 2018 15:25:15 -0400 Subject: [PATCH] Remove multitexturing support from GrTextureOp. This turned out to be an optimization mostly for draws with small device space areas. Moreover, to be an optimization rather than deoptimization requires complicated per-GPU tuning where even different devices within the same architecture require different tuning and tuning is different between GL and VK. We've decided to go another direction where we *don't* coalesce draws but rather make it possible to switch textures quickly from within an op. This should be a GPU-independent optimization that is also independent of device space area covered. Replaces the multitexturing benchmarks with a pair of benchmarks. composting_images* simulates a layered tile-based compositor. image_cycle draws tiny images N times each such that they can be batched. This is to catch a particular possible regression in a planned change to GrTextureOp where it will use "dynamic state" to batch across textures. We want to catch a slowdown that might result from putting draws that use the same texture into the same op but no longer batching the actual GL level draws and instead using the dynamic state to "switch" textures between draws. Change-Id: Ib1cc437525b0b0d56969c30dcb66bb1effb42dc5 Reviewed-on: https://skia-review.googlesource.com/145423 Commit-Queue: Brian Salomon Reviewed-by: Brian Osman --- bench/CompositingImagesBench.cpp | 99 +++++++ bench/ImageCycleBench.cpp | 94 ++++++ bench/MultitextureImageBench.cpp | 136 --------- gn/bench.gni | 3 +- src/gpu/GrShaderCaps.cpp | 9 - src/gpu/GrShaderCaps.h | 9 - src/gpu/gl/GrGLCaps.cpp | 24 -- src/gpu/ops/GrTextureOp.cpp | 472 ++++++------------------------- 8 files changed, 282 insertions(+), 564 deletions(-) create mode 100644 bench/CompositingImagesBench.cpp create mode 100644 bench/ImageCycleBench.cpp delete mode 100644 bench/MultitextureImageBench.cpp 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 {