1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2017 Google Inc. 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include <new> 9cb93a386Sopenharmony_ci 10cb93a386Sopenharmony_ci#include "include/core/SkPoint.h" 11cb93a386Sopenharmony_ci#include "include/core/SkPoint3.h" 12cb93a386Sopenharmony_ci#include "include/gpu/GrRecordingContext.h" 13cb93a386Sopenharmony_ci#include "include/private/SkFloatingPoint.h" 14cb93a386Sopenharmony_ci#include "include/private/SkTo.h" 15cb93a386Sopenharmony_ci#include "src/core/SkMathPriv.h" 16cb93a386Sopenharmony_ci#include "src/core/SkMatrixPriv.h" 17cb93a386Sopenharmony_ci#include "src/core/SkRectPriv.h" 18cb93a386Sopenharmony_ci#include "src/gpu/GrAppliedClip.h" 19cb93a386Sopenharmony_ci#include "src/gpu/GrCaps.h" 20cb93a386Sopenharmony_ci#include "src/gpu/GrDrawOpTest.h" 21cb93a386Sopenharmony_ci#include "src/gpu/GrGeometryProcessor.h" 22cb93a386Sopenharmony_ci#include "src/gpu/GrGpu.h" 23cb93a386Sopenharmony_ci#include "src/gpu/GrMemoryPool.h" 24cb93a386Sopenharmony_ci#include "src/gpu/GrOpFlushState.h" 25cb93a386Sopenharmony_ci#include "src/gpu/GrOpsTypes.h" 26cb93a386Sopenharmony_ci#include "src/gpu/GrRecordingContextPriv.h" 27cb93a386Sopenharmony_ci#include "src/gpu/GrResourceProvider.h" 28cb93a386Sopenharmony_ci#include "src/gpu/GrResourceProviderPriv.h" 29cb93a386Sopenharmony_ci#include "src/gpu/GrShaderCaps.h" 30cb93a386Sopenharmony_ci#include "src/gpu/GrTexture.h" 31cb93a386Sopenharmony_ci#include "src/gpu/GrTextureProxy.h" 32cb93a386Sopenharmony_ci#include "src/gpu/SkGr.h" 33cb93a386Sopenharmony_ci#include "src/gpu/effects/GrBlendFragmentProcessor.h" 34cb93a386Sopenharmony_ci#include "src/gpu/effects/GrTextureEffect.h" 35cb93a386Sopenharmony_ci#include "src/gpu/geometry/GrQuad.h" 36cb93a386Sopenharmony_ci#include "src/gpu/geometry/GrQuadBuffer.h" 37cb93a386Sopenharmony_ci#include "src/gpu/geometry/GrQuadUtils.h" 38cb93a386Sopenharmony_ci#include "src/gpu/geometry/GrRect.h" 39cb93a386Sopenharmony_ci#include "src/gpu/glsl/GrGLSLVarying.h" 40cb93a386Sopenharmony_ci#include "src/gpu/ops/FillRectOp.h" 41cb93a386Sopenharmony_ci#include "src/gpu/ops/GrMeshDrawOp.h" 42cb93a386Sopenharmony_ci#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h" 43cb93a386Sopenharmony_ci#include "src/gpu/ops/QuadPerEdgeAA.h" 44cb93a386Sopenharmony_ci#include "src/gpu/ops/TextureOp.h" 45cb93a386Sopenharmony_ci#include "src/gpu/v1/SurfaceDrawContext_v1.h" 46cb93a386Sopenharmony_ci 47cb93a386Sopenharmony_cinamespace { 48cb93a386Sopenharmony_ci 49cb93a386Sopenharmony_ciusing Subset = skgpu::v1::QuadPerEdgeAA::Subset; 50cb93a386Sopenharmony_ciusing VertexSpec = skgpu::v1::QuadPerEdgeAA::VertexSpec; 51cb93a386Sopenharmony_ciusing ColorType = skgpu::v1::QuadPerEdgeAA::ColorType; 52cb93a386Sopenharmony_ci 53cb93a386Sopenharmony_ci// Extracts lengths of vertical and horizontal edges of axis-aligned quad. "width" is the edge 54cb93a386Sopenharmony_ci// between v0 and v2 (or v1 and v3), "height" is the edge between v0 and v1 (or v2 and v3). 55cb93a386Sopenharmony_ciSkSize axis_aligned_quad_size(const GrQuad& quad) { 56cb93a386Sopenharmony_ci SkASSERT(quad.quadType() == GrQuad::Type::kAxisAligned); 57cb93a386Sopenharmony_ci // Simplification of regular edge length equation, since it's axis aligned and can avoid sqrt 58cb93a386Sopenharmony_ci float dw = sk_float_abs(quad.x(2) - quad.x(0)) + sk_float_abs(quad.y(2) - quad.y(0)); 59cb93a386Sopenharmony_ci float dh = sk_float_abs(quad.x(1) - quad.x(0)) + sk_float_abs(quad.y(1) - quad.y(0)); 60cb93a386Sopenharmony_ci return {dw, dh}; 61cb93a386Sopenharmony_ci} 62cb93a386Sopenharmony_ci 63cb93a386Sopenharmony_cistd::tuple<bool /* filter */, 64cb93a386Sopenharmony_ci bool /* mipmap */> 65cb93a386Sopenharmony_cifilter_and_mm_have_effect(const GrQuad& srcQuad, const GrQuad& dstQuad) { 66cb93a386Sopenharmony_ci // If not axis-aligned in src or dst, then always say it has an effect 67cb93a386Sopenharmony_ci if (srcQuad.quadType() != GrQuad::Type::kAxisAligned || 68cb93a386Sopenharmony_ci dstQuad.quadType() != GrQuad::Type::kAxisAligned) { 69cb93a386Sopenharmony_ci return {true, true}; 70cb93a386Sopenharmony_ci } 71cb93a386Sopenharmony_ci 72cb93a386Sopenharmony_ci SkRect srcRect; 73cb93a386Sopenharmony_ci SkRect dstRect; 74cb93a386Sopenharmony_ci if (srcQuad.asRect(&srcRect) && dstQuad.asRect(&dstRect)) { 75cb93a386Sopenharmony_ci // Disable filtering when there is no scaling (width and height are the same), and the 76cb93a386Sopenharmony_ci // top-left corners have the same fraction (so src and dst snap to the pixel grid 77cb93a386Sopenharmony_ci // identically). 78cb93a386Sopenharmony_ci SkASSERT(srcRect.isSorted()); 79cb93a386Sopenharmony_ci bool filter = srcRect.width() != dstRect.width() || srcRect.height() != dstRect.height() || 80cb93a386Sopenharmony_ci SkScalarFraction(srcRect.fLeft) != SkScalarFraction(dstRect.fLeft) || 81cb93a386Sopenharmony_ci SkScalarFraction(srcRect.fTop) != SkScalarFraction(dstRect.fTop); 82cb93a386Sopenharmony_ci bool mm = srcRect.width() > dstRect.width() || srcRect.height() > dstRect.height(); 83cb93a386Sopenharmony_ci return {filter, mm}; 84cb93a386Sopenharmony_ci } 85cb93a386Sopenharmony_ci // Extract edge lengths 86cb93a386Sopenharmony_ci SkSize srcSize = axis_aligned_quad_size(srcQuad); 87cb93a386Sopenharmony_ci SkSize dstSize = axis_aligned_quad_size(dstQuad); 88cb93a386Sopenharmony_ci // Although the quads are axis-aligned, the local coordinate system is transformed such 89cb93a386Sopenharmony_ci // that fractionally-aligned sample centers will not align with the device coordinate system 90cb93a386Sopenharmony_ci // So disable filtering when edges are the same length and both srcQuad and dstQuad 91cb93a386Sopenharmony_ci // 0th vertex is integer aligned. 92cb93a386Sopenharmony_ci bool filter = srcSize != dstSize || 93cb93a386Sopenharmony_ci !SkScalarIsInt(srcQuad.x(0)) || 94cb93a386Sopenharmony_ci !SkScalarIsInt(srcQuad.y(0)) || 95cb93a386Sopenharmony_ci !SkScalarIsInt(dstQuad.x(0)) || 96cb93a386Sopenharmony_ci !SkScalarIsInt(dstQuad.y(0)); 97cb93a386Sopenharmony_ci bool mm = srcSize.fWidth > dstSize.fWidth || srcSize.fHeight > dstSize.fHeight; 98cb93a386Sopenharmony_ci return {filter, mm}; 99cb93a386Sopenharmony_ci} 100cb93a386Sopenharmony_ci 101cb93a386Sopenharmony_ci// Describes function for normalizing src coords: [x * iw, y * ih + yOffset] can represent 102cb93a386Sopenharmony_ci// regular and rectangular textures, w/ or w/o origin correction. 103cb93a386Sopenharmony_cistruct NormalizationParams { 104cb93a386Sopenharmony_ci float fIW; // 1 / width of texture, or 1.0 for texture rectangles 105cb93a386Sopenharmony_ci float fInvH; // 1 / height of texture, or 1.0 for tex rects, X -1 if bottom-left origin 106cb93a386Sopenharmony_ci float fYOffset; // 0 for top-left origin, height of [normalized] tex if bottom-left 107cb93a386Sopenharmony_ci}; 108cb93a386Sopenharmony_ciNormalizationParams proxy_normalization_params(const GrSurfaceProxy* proxy, 109cb93a386Sopenharmony_ci GrSurfaceOrigin origin) { 110cb93a386Sopenharmony_ci // Whether or not the proxy is instantiated, this is the size its texture will be, so we can 111cb93a386Sopenharmony_ci // normalize the src coordinates up front. 112cb93a386Sopenharmony_ci SkISize dimensions = proxy->backingStoreDimensions(); 113cb93a386Sopenharmony_ci float iw, ih, h; 114cb93a386Sopenharmony_ci if (proxy->backendFormat().textureType() == GrTextureType::kRectangle) { 115cb93a386Sopenharmony_ci iw = ih = 1.f; 116cb93a386Sopenharmony_ci h = dimensions.height(); 117cb93a386Sopenharmony_ci } else { 118cb93a386Sopenharmony_ci iw = 1.f / dimensions.width(); 119cb93a386Sopenharmony_ci ih = 1.f / dimensions.height(); 120cb93a386Sopenharmony_ci h = 1.f; 121cb93a386Sopenharmony_ci } 122cb93a386Sopenharmony_ci 123cb93a386Sopenharmony_ci if (origin == kBottomLeft_GrSurfaceOrigin) { 124cb93a386Sopenharmony_ci return {iw, -ih, h}; 125cb93a386Sopenharmony_ci } else { 126cb93a386Sopenharmony_ci return {iw, ih, 0.0f}; 127cb93a386Sopenharmony_ci } 128cb93a386Sopenharmony_ci} 129cb93a386Sopenharmony_ci 130cb93a386Sopenharmony_ci// Normalize the subset. If 'subsetRect' is null, it is assumed no subset constraint is desired, 131cb93a386Sopenharmony_ci// so a sufficiently large rect is returned even if the quad ends up batched with an op that uses 132cb93a386Sopenharmony_ci// subsets overall. When there is a subset it will be inset based on the filter mode. Normalization 133cb93a386Sopenharmony_ci// and y-flipping are applied as indicated by NormalizationParams. 134cb93a386Sopenharmony_ciSkRect normalize_and_inset_subset(GrSamplerState::Filter filter, 135cb93a386Sopenharmony_ci const NormalizationParams& params, 136cb93a386Sopenharmony_ci const SkRect* subsetRect) { 137cb93a386Sopenharmony_ci static constexpr SkRect kLargeRect = {-100000, -100000, 1000000, 1000000}; 138cb93a386Sopenharmony_ci if (!subsetRect) { 139cb93a386Sopenharmony_ci // Either the quad has no subset constraint and is batched with a subset constrained op 140cb93a386Sopenharmony_ci // (in which case we want a subset that doesn't restrict normalized tex coords), or the 141cb93a386Sopenharmony_ci // entire op doesn't use the subset, in which case the returned value is ignored. 142cb93a386Sopenharmony_ci return kLargeRect; 143cb93a386Sopenharmony_ci } 144cb93a386Sopenharmony_ci 145cb93a386Sopenharmony_ci auto ltrb = skvx::Vec<4, float>::Load(subsetRect); 146cb93a386Sopenharmony_ci auto flipHi = skvx::Vec<4, float>({1.f, 1.f, -1.f, -1.f}); 147cb93a386Sopenharmony_ci if (filter == GrSamplerState::Filter::kNearest) { 148cb93a386Sopenharmony_ci // Make sure our insetting puts us at pixel centers. 149cb93a386Sopenharmony_ci ltrb = skvx::floor(ltrb*flipHi)*flipHi; 150cb93a386Sopenharmony_ci } 151cb93a386Sopenharmony_ci // Inset with pin to the rect center. 152cb93a386Sopenharmony_ci ltrb += skvx::Vec<4, float>({.5f, .5f, -.5f, -.5f}); 153cb93a386Sopenharmony_ci auto mid = (skvx::shuffle<2, 3, 0, 1>(ltrb) + ltrb)*0.5f; 154cb93a386Sopenharmony_ci ltrb = skvx::min(ltrb*flipHi, mid*flipHi)*flipHi; 155cb93a386Sopenharmony_ci 156cb93a386Sopenharmony_ci // Normalize and offset 157cb93a386Sopenharmony_ci ltrb = ltrb * skvx::Vec<4, float>{params.fIW, params.fInvH, params.fIW, params.fInvH} + 158cb93a386Sopenharmony_ci skvx::Vec<4, float>{0.f, params.fYOffset, 0.f, params.fYOffset}; 159cb93a386Sopenharmony_ci if (params.fInvH < 0.f) { 160cb93a386Sopenharmony_ci // Flip top and bottom to keep the rect sorted when loaded back to SkRect. 161cb93a386Sopenharmony_ci ltrb = skvx::shuffle<0, 3, 2, 1>(ltrb); 162cb93a386Sopenharmony_ci } 163cb93a386Sopenharmony_ci 164cb93a386Sopenharmony_ci SkRect out; 165cb93a386Sopenharmony_ci ltrb.store(&out); 166cb93a386Sopenharmony_ci return out; 167cb93a386Sopenharmony_ci} 168cb93a386Sopenharmony_ci 169cb93a386Sopenharmony_ci// Normalizes logical src coords and corrects for origin 170cb93a386Sopenharmony_civoid normalize_src_quad(const NormalizationParams& params, 171cb93a386Sopenharmony_ci GrQuad* srcQuad) { 172cb93a386Sopenharmony_ci // The src quad should not have any perspective 173cb93a386Sopenharmony_ci SkASSERT(!srcQuad->hasPerspective()); 174cb93a386Sopenharmony_ci skvx::Vec<4, float> xs = srcQuad->x4f() * params.fIW; 175cb93a386Sopenharmony_ci skvx::Vec<4, float> ys = srcQuad->y4f() * params.fInvH + params.fYOffset; 176cb93a386Sopenharmony_ci xs.store(srcQuad->xs()); 177cb93a386Sopenharmony_ci ys.store(srcQuad->ys()); 178cb93a386Sopenharmony_ci} 179cb93a386Sopenharmony_ci 180cb93a386Sopenharmony_ci// Count the number of proxy runs in the entry set. This usually is already computed by 181cb93a386Sopenharmony_ci// SkGpuDevice, but when the BatchLengthLimiter chops the set up it must determine a new proxy count 182cb93a386Sopenharmony_ci// for each split. 183cb93a386Sopenharmony_ciint proxy_run_count(const GrTextureSetEntry set[], int count) { 184cb93a386Sopenharmony_ci int actualProxyRunCount = 0; 185cb93a386Sopenharmony_ci const GrSurfaceProxy* lastProxy = nullptr; 186cb93a386Sopenharmony_ci for (int i = 0; i < count; ++i) { 187cb93a386Sopenharmony_ci if (set[i].fProxyView.proxy() != lastProxy) { 188cb93a386Sopenharmony_ci actualProxyRunCount++; 189cb93a386Sopenharmony_ci lastProxy = set[i].fProxyView.proxy(); 190cb93a386Sopenharmony_ci } 191cb93a386Sopenharmony_ci } 192cb93a386Sopenharmony_ci return actualProxyRunCount; 193cb93a386Sopenharmony_ci} 194cb93a386Sopenharmony_ci 195cb93a386Sopenharmony_cibool safe_to_ignore_subset_rect(GrAAType aaType, GrSamplerState::Filter filter, 196cb93a386Sopenharmony_ci const DrawQuad& quad, const SkRect& subsetRect) { 197cb93a386Sopenharmony_ci // If both the device and local quad are both axis-aligned, and filtering is off, the local quad 198cb93a386Sopenharmony_ci // can push all the way up to the edges of the the subset rect and the sampler shouldn't 199cb93a386Sopenharmony_ci // overshoot. Unfortunately, antialiasing adds enough jitter that we can only rely on this in 200cb93a386Sopenharmony_ci // the non-antialiased case. 201cb93a386Sopenharmony_ci SkRect localBounds = quad.fLocal.bounds(); 202cb93a386Sopenharmony_ci if (aaType == GrAAType::kNone && 203cb93a386Sopenharmony_ci filter == GrSamplerState::Filter::kNearest && 204cb93a386Sopenharmony_ci quad.fDevice.quadType() == GrQuad::Type::kAxisAligned && 205cb93a386Sopenharmony_ci quad.fLocal.quadType() == GrQuad::Type::kAxisAligned && 206cb93a386Sopenharmony_ci subsetRect.contains(localBounds)) { 207cb93a386Sopenharmony_ci 208cb93a386Sopenharmony_ci return true; 209cb93a386Sopenharmony_ci } 210cb93a386Sopenharmony_ci 211cb93a386Sopenharmony_ci // If the local quad is inset by at least 0.5 pixels into the subset rect's bounds, the 212cb93a386Sopenharmony_ci // sampler shouldn't overshoot, even when antialiasing and filtering is taken into account. 213cb93a386Sopenharmony_ci if (subsetRect.makeInset(0.5f, 0.5f).contains(localBounds)) { 214cb93a386Sopenharmony_ci return true; 215cb93a386Sopenharmony_ci } 216cb93a386Sopenharmony_ci 217cb93a386Sopenharmony_ci // The subset rect cannot be ignored safely. 218cb93a386Sopenharmony_ci return false; 219cb93a386Sopenharmony_ci} 220cb93a386Sopenharmony_ci 221cb93a386Sopenharmony_ci/** 222cb93a386Sopenharmony_ci * Op that implements TextureOp::Make. It draws textured quads. Each quad can modulate against a 223cb93a386Sopenharmony_ci * the texture by color. The blend with the destination is always src-over. The edges are non-AA. 224cb93a386Sopenharmony_ci */ 225cb93a386Sopenharmony_ciclass TextureOpImpl final : public GrMeshDrawOp { 226cb93a386Sopenharmony_cipublic: 227cb93a386Sopenharmony_ci using Saturate = skgpu::v1::TextureOp::Saturate; 228cb93a386Sopenharmony_ci 229cb93a386Sopenharmony_ci static GrOp::Owner Make(GrRecordingContext* context, 230cb93a386Sopenharmony_ci GrSurfaceProxyView proxyView, 231cb93a386Sopenharmony_ci sk_sp<GrColorSpaceXform> textureXform, 232cb93a386Sopenharmony_ci GrSamplerState::Filter filter, 233cb93a386Sopenharmony_ci GrSamplerState::MipmapMode mm, 234cb93a386Sopenharmony_ci const SkPMColor4f& color, 235cb93a386Sopenharmony_ci Saturate saturate, 236cb93a386Sopenharmony_ci GrAAType aaType, 237cb93a386Sopenharmony_ci DrawQuad* quad, 238cb93a386Sopenharmony_ci const SkRect* subset) { 239cb93a386Sopenharmony_ci 240cb93a386Sopenharmony_ci return GrOp::Make<TextureOpImpl>(context, std::move(proxyView), std::move(textureXform), 241cb93a386Sopenharmony_ci filter, mm, color, saturate, aaType, quad, subset); 242cb93a386Sopenharmony_ci } 243cb93a386Sopenharmony_ci 244cb93a386Sopenharmony_ci static GrOp::Owner Make(GrRecordingContext* context, 245cb93a386Sopenharmony_ci GrTextureSetEntry set[], 246cb93a386Sopenharmony_ci int cnt, 247cb93a386Sopenharmony_ci int proxyRunCnt, 248cb93a386Sopenharmony_ci GrSamplerState::Filter filter, 249cb93a386Sopenharmony_ci GrSamplerState::MipmapMode mm, 250cb93a386Sopenharmony_ci Saturate saturate, 251cb93a386Sopenharmony_ci GrAAType aaType, 252cb93a386Sopenharmony_ci SkCanvas::SrcRectConstraint constraint, 253cb93a386Sopenharmony_ci const SkMatrix& viewMatrix, 254cb93a386Sopenharmony_ci sk_sp<GrColorSpaceXform> textureColorSpaceXform) { 255cb93a386Sopenharmony_ci // Allocate size based on proxyRunCnt, since that determines number of ViewCountPairs. 256cb93a386Sopenharmony_ci SkASSERT(proxyRunCnt <= cnt); 257cb93a386Sopenharmony_ci return GrOp::MakeWithExtraMemory<TextureOpImpl>( 258cb93a386Sopenharmony_ci context, sizeof(ViewCountPair) * (proxyRunCnt - 1), 259cb93a386Sopenharmony_ci set, cnt, proxyRunCnt, filter, mm, saturate, aaType, constraint, 260cb93a386Sopenharmony_ci viewMatrix, std::move(textureColorSpaceXform)); 261cb93a386Sopenharmony_ci } 262cb93a386Sopenharmony_ci 263cb93a386Sopenharmony_ci ~TextureOpImpl() override { 264cb93a386Sopenharmony_ci for (unsigned p = 1; p < fMetadata.fProxyCount; ++p) { 265cb93a386Sopenharmony_ci fViewCountPairs[p].~ViewCountPair(); 266cb93a386Sopenharmony_ci } 267cb93a386Sopenharmony_ci } 268cb93a386Sopenharmony_ci 269cb93a386Sopenharmony_ci const char* name() const override { return "TextureOp"; } 270cb93a386Sopenharmony_ci 271cb93a386Sopenharmony_ci void visitProxies(const GrVisitProxyFunc& func) const override { 272cb93a386Sopenharmony_ci bool mipped = (fMetadata.mipmapMode() != GrSamplerState::MipmapMode::kNone); 273cb93a386Sopenharmony_ci for (unsigned p = 0; p < fMetadata.fProxyCount; ++p) { 274cb93a386Sopenharmony_ci func(fViewCountPairs[p].fProxy.get(), GrMipmapped(mipped)); 275cb93a386Sopenharmony_ci } 276cb93a386Sopenharmony_ci if (fDesc && fDesc->fProgramInfo) { 277cb93a386Sopenharmony_ci fDesc->fProgramInfo->visitFPProxies(func); 278cb93a386Sopenharmony_ci } 279cb93a386Sopenharmony_ci } 280cb93a386Sopenharmony_ci 281cb93a386Sopenharmony_ci#ifdef SK_DEBUG 282cb93a386Sopenharmony_ci static void ValidateResourceLimits() { 283cb93a386Sopenharmony_ci // The op implementation has an upper bound on the number of quads that it can represent. 284cb93a386Sopenharmony_ci // However, the resource manager imposes its own limit on the number of quads, which should 285cb93a386Sopenharmony_ci // always be lower than the numerical limit this op can hold. 286cb93a386Sopenharmony_ci using CountStorage = decltype(Metadata::fTotalQuadCount); 287cb93a386Sopenharmony_ci CountStorage maxQuadCount = std::numeric_limits<CountStorage>::max(); 288cb93a386Sopenharmony_ci // GrResourceProvider::Max...() is typed as int, so don't compare across signed/unsigned. 289cb93a386Sopenharmony_ci int resourceLimit = SkTo<int>(maxQuadCount); 290cb93a386Sopenharmony_ci SkASSERT(GrResourceProvider::MaxNumAAQuads() <= resourceLimit && 291cb93a386Sopenharmony_ci GrResourceProvider::MaxNumNonAAQuads() <= resourceLimit); 292cb93a386Sopenharmony_ci } 293cb93a386Sopenharmony_ci#endif 294cb93a386Sopenharmony_ci 295cb93a386Sopenharmony_ci GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip*, 296cb93a386Sopenharmony_ci GrClampType clampType) override { 297cb93a386Sopenharmony_ci SkASSERT(fMetadata.colorType() == ColorType::kNone); 298cb93a386Sopenharmony_ci auto iter = fQuads.metadata(); 299cb93a386Sopenharmony_ci while(iter.next()) { 300cb93a386Sopenharmony_ci auto colorType = skgpu::v1::QuadPerEdgeAA::MinColorType(iter->fColor); 301cb93a386Sopenharmony_ci colorType = std::max(static_cast<ColorType>(fMetadata.fColorType), 302cb93a386Sopenharmony_ci colorType); 303cb93a386Sopenharmony_ci if (caps.reducedShaderMode()) { 304cb93a386Sopenharmony_ci colorType = std::max(colorType, ColorType::kByte); 305cb93a386Sopenharmony_ci } 306cb93a386Sopenharmony_ci fMetadata.fColorType = static_cast<uint16_t>(colorType); 307cb93a386Sopenharmony_ci } 308cb93a386Sopenharmony_ci return GrProcessorSet::EmptySetAnalysis(); 309cb93a386Sopenharmony_ci } 310cb93a386Sopenharmony_ci 311cb93a386Sopenharmony_ci FixedFunctionFlags fixedFunctionFlags() const override { 312cb93a386Sopenharmony_ci return fMetadata.aaType() == GrAAType::kMSAA ? FixedFunctionFlags::kUsesHWAA 313cb93a386Sopenharmony_ci : FixedFunctionFlags::kNone; 314cb93a386Sopenharmony_ci } 315cb93a386Sopenharmony_ci 316cb93a386Sopenharmony_ci DEFINE_OP_CLASS_ID 317cb93a386Sopenharmony_ci 318cb93a386Sopenharmony_ciprivate: 319cb93a386Sopenharmony_ci friend class ::GrOp; 320cb93a386Sopenharmony_ci 321cb93a386Sopenharmony_ci struct ColorSubsetAndAA { 322cb93a386Sopenharmony_ci ColorSubsetAndAA(const SkPMColor4f& color, const SkRect& subsetRect, GrQuadAAFlags aaFlags) 323cb93a386Sopenharmony_ci : fColor(color) 324cb93a386Sopenharmony_ci , fSubsetRect(subsetRect) 325cb93a386Sopenharmony_ci , fAAFlags(static_cast<uint16_t>(aaFlags)) { 326cb93a386Sopenharmony_ci SkASSERT(fAAFlags == static_cast<uint16_t>(aaFlags)); 327cb93a386Sopenharmony_ci } 328cb93a386Sopenharmony_ci 329cb93a386Sopenharmony_ci SkPMColor4f fColor; 330cb93a386Sopenharmony_ci // If the op doesn't use subsets, this is ignored. If the op uses subsets and the specific 331cb93a386Sopenharmony_ci // entry does not, this rect will equal kLargeRect, so it automatically has no effect. 332cb93a386Sopenharmony_ci SkRect fSubsetRect; 333cb93a386Sopenharmony_ci unsigned fAAFlags : 4; 334cb93a386Sopenharmony_ci 335cb93a386Sopenharmony_ci GrQuadAAFlags aaFlags() const { return static_cast<GrQuadAAFlags>(fAAFlags); } 336cb93a386Sopenharmony_ci }; 337cb93a386Sopenharmony_ci 338cb93a386Sopenharmony_ci struct ViewCountPair { 339cb93a386Sopenharmony_ci // Normally this would be a GrSurfaceProxyView, but TextureOp applies the GrOrigin right 340cb93a386Sopenharmony_ci // away so it doesn't need to be stored, and all ViewCountPairs in an op have the same 341cb93a386Sopenharmony_ci // swizzle so that is stored in the op metadata. 342cb93a386Sopenharmony_ci sk_sp<GrSurfaceProxy> fProxy; 343cb93a386Sopenharmony_ci int fQuadCnt; 344cb93a386Sopenharmony_ci }; 345cb93a386Sopenharmony_ci 346cb93a386Sopenharmony_ci // TextureOp and ViewCountPair are 8 byte aligned. This is packed into 8 bytes to minimally 347cb93a386Sopenharmony_ci // increase the size of the op; increasing the op size can have a surprising impact on 348cb93a386Sopenharmony_ci // performance (since texture ops are one of the most commonly used in an app). 349cb93a386Sopenharmony_ci struct Metadata { 350cb93a386Sopenharmony_ci // AAType must be filled after initialization; ColorType is determined in finalize() 351cb93a386Sopenharmony_ci Metadata(const GrSwizzle& swizzle, 352cb93a386Sopenharmony_ci GrSamplerState::Filter filter, 353cb93a386Sopenharmony_ci GrSamplerState::MipmapMode mm, 354cb93a386Sopenharmony_ci Subset subset, 355cb93a386Sopenharmony_ci Saturate saturate) 356cb93a386Sopenharmony_ci : fSwizzle(swizzle) 357cb93a386Sopenharmony_ci , fProxyCount(1) 358cb93a386Sopenharmony_ci , fTotalQuadCount(1) 359cb93a386Sopenharmony_ci , fFilter(static_cast<uint16_t>(filter)) 360cb93a386Sopenharmony_ci , fMipmapMode(static_cast<uint16_t>(mm)) 361cb93a386Sopenharmony_ci , fAAType(static_cast<uint16_t>(GrAAType::kNone)) 362cb93a386Sopenharmony_ci , fColorType(static_cast<uint16_t>(ColorType::kNone)) 363cb93a386Sopenharmony_ci , fSubset(static_cast<uint16_t>(subset)) 364cb93a386Sopenharmony_ci , fSaturate(static_cast<uint16_t>(saturate)) {} 365cb93a386Sopenharmony_ci 366cb93a386Sopenharmony_ci GrSwizzle fSwizzle; // sizeof(GrSwizzle) == uint16_t 367cb93a386Sopenharmony_ci uint16_t fProxyCount; 368cb93a386Sopenharmony_ci // This will be >= fProxyCount, since a proxy may be drawn multiple times 369cb93a386Sopenharmony_ci uint16_t fTotalQuadCount; 370cb93a386Sopenharmony_ci 371cb93a386Sopenharmony_ci // These must be based on uint16_t to help MSVC's pack bitfields optimally 372cb93a386Sopenharmony_ci uint16_t fFilter : 2; // GrSamplerState::Filter 373cb93a386Sopenharmony_ci uint16_t fMipmapMode : 2; // GrSamplerState::MipmapMode 374cb93a386Sopenharmony_ci uint16_t fAAType : 2; // GrAAType 375cb93a386Sopenharmony_ci uint16_t fColorType : 2; // GrQuadPerEdgeAA::ColorType 376cb93a386Sopenharmony_ci uint16_t fSubset : 1; // bool 377cb93a386Sopenharmony_ci uint16_t fSaturate : 1; // bool 378cb93a386Sopenharmony_ci uint16_t fUnused : 6; // # of bits left before Metadata exceeds 8 bytes 379cb93a386Sopenharmony_ci 380cb93a386Sopenharmony_ci GrSamplerState::Filter filter() const { 381cb93a386Sopenharmony_ci return static_cast<GrSamplerState::Filter>(fFilter); 382cb93a386Sopenharmony_ci } 383cb93a386Sopenharmony_ci GrSamplerState::MipmapMode mipmapMode() const { 384cb93a386Sopenharmony_ci return static_cast<GrSamplerState::MipmapMode>(fMipmapMode); 385cb93a386Sopenharmony_ci } 386cb93a386Sopenharmony_ci GrAAType aaType() const { return static_cast<GrAAType>(fAAType); } 387cb93a386Sopenharmony_ci ColorType colorType() const { return static_cast<ColorType>(fColorType); } 388cb93a386Sopenharmony_ci Subset subset() const { return static_cast<Subset>(fSubset); } 389cb93a386Sopenharmony_ci Saturate saturate() const { return static_cast<Saturate>(fSaturate); } 390cb93a386Sopenharmony_ci 391cb93a386Sopenharmony_ci static_assert(GrSamplerState::kFilterCount <= 4); 392cb93a386Sopenharmony_ci static_assert(kGrAATypeCount <= 4); 393cb93a386Sopenharmony_ci static_assert(skgpu::v1::QuadPerEdgeAA::kColorTypeCount <= 4); 394cb93a386Sopenharmony_ci }; 395cb93a386Sopenharmony_ci static_assert(sizeof(Metadata) == 8); 396cb93a386Sopenharmony_ci 397cb93a386Sopenharmony_ci // This descriptor is used to store the draw info we decide on during on(Pre)PrepareDraws. We 398cb93a386Sopenharmony_ci // store the data in a separate struct in order to minimize the size of the TextureOp. 399cb93a386Sopenharmony_ci // Historically, increasing the TextureOp's size has caused surprising perf regressions, but we 400cb93a386Sopenharmony_ci // may want to re-evaluate whether this is still necessary. 401cb93a386Sopenharmony_ci // 402cb93a386Sopenharmony_ci // In the onPrePrepareDraws case it is allocated in the creation-time opData arena, and 403cb93a386Sopenharmony_ci // allocatePrePreparedVertices is also called. 404cb93a386Sopenharmony_ci // 405cb93a386Sopenharmony_ci // In the onPrepareDraws case this descriptor is allocated in the flush-time arena (i.e., as 406cb93a386Sopenharmony_ci // part of the flushState). 407cb93a386Sopenharmony_ci struct Desc { 408cb93a386Sopenharmony_ci VertexSpec fVertexSpec; 409cb93a386Sopenharmony_ci int fNumProxies = 0; 410cb93a386Sopenharmony_ci int fNumTotalQuads = 0; 411cb93a386Sopenharmony_ci 412cb93a386Sopenharmony_ci // This member variable is only used by 'onPrePrepareDraws'. 413cb93a386Sopenharmony_ci char* fPrePreparedVertices = nullptr; 414cb93a386Sopenharmony_ci 415cb93a386Sopenharmony_ci GrProgramInfo* fProgramInfo = nullptr; 416cb93a386Sopenharmony_ci 417cb93a386Sopenharmony_ci sk_sp<const GrBuffer> fIndexBuffer; 418cb93a386Sopenharmony_ci sk_sp<const GrBuffer> fVertexBuffer; 419cb93a386Sopenharmony_ci int fBaseVertex; 420cb93a386Sopenharmony_ci 421cb93a386Sopenharmony_ci // How big should 'fVertices' be to hold all the vertex data? 422cb93a386Sopenharmony_ci size_t totalSizeInBytes() const { 423cb93a386Sopenharmony_ci return this->totalNumVertices() * fVertexSpec.vertexSize(); 424cb93a386Sopenharmony_ci } 425cb93a386Sopenharmony_ci 426cb93a386Sopenharmony_ci int totalNumVertices() const { 427cb93a386Sopenharmony_ci return fNumTotalQuads * fVertexSpec.verticesPerQuad(); 428cb93a386Sopenharmony_ci } 429cb93a386Sopenharmony_ci 430cb93a386Sopenharmony_ci void allocatePrePreparedVertices(SkArenaAlloc* arena) { 431cb93a386Sopenharmony_ci fPrePreparedVertices = arena->makeArrayDefault<char>(this->totalSizeInBytes()); 432cb93a386Sopenharmony_ci } 433cb93a386Sopenharmony_ci }; 434cb93a386Sopenharmony_ci // If subsetRect is not null it will be used to apply a strict src rect-style constraint. 435cb93a386Sopenharmony_ci TextureOpImpl(GrSurfaceProxyView proxyView, 436cb93a386Sopenharmony_ci sk_sp<GrColorSpaceXform> textureColorSpaceXform, 437cb93a386Sopenharmony_ci GrSamplerState::Filter filter, 438cb93a386Sopenharmony_ci GrSamplerState::MipmapMode mm, 439cb93a386Sopenharmony_ci const SkPMColor4f& color, 440cb93a386Sopenharmony_ci Saturate saturate, 441cb93a386Sopenharmony_ci GrAAType aaType, 442cb93a386Sopenharmony_ci DrawQuad* quad, 443cb93a386Sopenharmony_ci const SkRect* subsetRect) 444cb93a386Sopenharmony_ci : INHERITED(ClassID()) 445cb93a386Sopenharmony_ci , fQuads(1, true /* includes locals */) 446cb93a386Sopenharmony_ci , fTextureColorSpaceXform(std::move(textureColorSpaceXform)) 447cb93a386Sopenharmony_ci , fDesc(nullptr) 448cb93a386Sopenharmony_ci , fMetadata(proxyView.swizzle(), filter, mm, Subset(!!subsetRect), saturate) { 449cb93a386Sopenharmony_ci // Clean up disparities between the overall aa type and edge configuration and apply 450cb93a386Sopenharmony_ci // optimizations based on the rect and matrix when appropriate 451cb93a386Sopenharmony_ci GrQuadUtils::ResolveAAType(aaType, quad->fEdgeFlags, quad->fDevice, 452cb93a386Sopenharmony_ci &aaType, &quad->fEdgeFlags); 453cb93a386Sopenharmony_ci fMetadata.fAAType = static_cast<uint16_t>(aaType); 454cb93a386Sopenharmony_ci 455cb93a386Sopenharmony_ci // We expect our caller to have already caught this optimization. 456cb93a386Sopenharmony_ci SkASSERT(!subsetRect || 457cb93a386Sopenharmony_ci !subsetRect->contains(proxyView.proxy()->backingStoreBoundsRect())); 458cb93a386Sopenharmony_ci 459cb93a386Sopenharmony_ci // We may have had a strict constraint with nearest filter solely due to possible AA bloat. 460cb93a386Sopenharmony_ci // Try to identify cases where the subsetting isn't actually necessary, and skip it. 461cb93a386Sopenharmony_ci if (subsetRect) { 462cb93a386Sopenharmony_ci if (safe_to_ignore_subset_rect(aaType, filter, *quad, *subsetRect)) { 463cb93a386Sopenharmony_ci subsetRect = nullptr; 464cb93a386Sopenharmony_ci fMetadata.fSubset = static_cast<uint16_t>(Subset::kNo); 465cb93a386Sopenharmony_ci } 466cb93a386Sopenharmony_ci } 467cb93a386Sopenharmony_ci 468cb93a386Sopenharmony_ci // Normalize src coordinates and the subset (if set) 469cb93a386Sopenharmony_ci NormalizationParams params = proxy_normalization_params(proxyView.proxy(), 470cb93a386Sopenharmony_ci proxyView.origin()); 471cb93a386Sopenharmony_ci normalize_src_quad(params, &quad->fLocal); 472cb93a386Sopenharmony_ci SkRect subset = normalize_and_inset_subset(filter, params, subsetRect); 473cb93a386Sopenharmony_ci 474cb93a386Sopenharmony_ci // Set bounds before clipping so we don't have to worry about unioning the bounds of 475cb93a386Sopenharmony_ci // the two potential quads (GrQuad::bounds() is perspective-safe). 476cb93a386Sopenharmony_ci bool hairline = GrQuadUtils::WillUseHairline(quad->fDevice, aaType, quad->fEdgeFlags); 477cb93a386Sopenharmony_ci this->setBounds(quad->fDevice.bounds(), HasAABloat(aaType == GrAAType::kCoverage), 478cb93a386Sopenharmony_ci hairline ? IsHairline::kYes : IsHairline::kNo); 479cb93a386Sopenharmony_ci int quadCount = this->appendQuad(quad, color, subset); 480cb93a386Sopenharmony_ci fViewCountPairs[0] = {proxyView.detachProxy(), quadCount}; 481cb93a386Sopenharmony_ci } 482cb93a386Sopenharmony_ci 483cb93a386Sopenharmony_ci TextureOpImpl(GrTextureSetEntry set[], 484cb93a386Sopenharmony_ci int cnt, 485cb93a386Sopenharmony_ci int proxyRunCnt, 486cb93a386Sopenharmony_ci const GrSamplerState::Filter filter, 487cb93a386Sopenharmony_ci const GrSamplerState::MipmapMode mm, 488cb93a386Sopenharmony_ci const Saturate saturate, 489cb93a386Sopenharmony_ci const GrAAType aaType, 490cb93a386Sopenharmony_ci const SkCanvas::SrcRectConstraint constraint, 491cb93a386Sopenharmony_ci const SkMatrix& viewMatrix, 492cb93a386Sopenharmony_ci sk_sp<GrColorSpaceXform> textureColorSpaceXform) 493cb93a386Sopenharmony_ci : INHERITED(ClassID()) 494cb93a386Sopenharmony_ci , fQuads(cnt, true /* includes locals */) 495cb93a386Sopenharmony_ci , fTextureColorSpaceXform(std::move(textureColorSpaceXform)) 496cb93a386Sopenharmony_ci , fDesc(nullptr) 497cb93a386Sopenharmony_ci , fMetadata(set[0].fProxyView.swizzle(), 498cb93a386Sopenharmony_ci GrSamplerState::Filter::kNearest, 499cb93a386Sopenharmony_ci GrSamplerState::MipmapMode::kNone, 500cb93a386Sopenharmony_ci Subset::kNo, 501cb93a386Sopenharmony_ci saturate) { 502cb93a386Sopenharmony_ci // Update counts to reflect the batch op 503cb93a386Sopenharmony_ci fMetadata.fProxyCount = SkToUInt(proxyRunCnt); 504cb93a386Sopenharmony_ci fMetadata.fTotalQuadCount = SkToUInt(cnt); 505cb93a386Sopenharmony_ci 506cb93a386Sopenharmony_ci SkRect bounds = SkRectPriv::MakeLargestInverted(); 507cb93a386Sopenharmony_ci 508cb93a386Sopenharmony_ci GrAAType netAAType = GrAAType::kNone; // aa type maximally compatible with all dst rects 509cb93a386Sopenharmony_ci Subset netSubset = Subset::kNo; 510cb93a386Sopenharmony_ci GrSamplerState::Filter netFilter = GrSamplerState::Filter::kNearest; 511cb93a386Sopenharmony_ci GrSamplerState::MipmapMode netMM = GrSamplerState::MipmapMode::kNone; 512cb93a386Sopenharmony_ci bool hasSubpixel = false; 513cb93a386Sopenharmony_ci 514cb93a386Sopenharmony_ci const GrSurfaceProxy* curProxy = nullptr; 515cb93a386Sopenharmony_ci 516cb93a386Sopenharmony_ci // 'q' is the index in 'set' and fQuadBuffer; 'p' is the index in fViewCountPairs and only 517cb93a386Sopenharmony_ci // increases when set[q]'s proxy changes. 518cb93a386Sopenharmony_ci int p = 0; 519cb93a386Sopenharmony_ci for (int q = 0; q < cnt; ++q) { 520cb93a386Sopenharmony_ci SkASSERT(mm == GrSamplerState::MipmapMode::kNone || 521cb93a386Sopenharmony_ci (set[0].fProxyView.proxy()->asTextureProxy()->mipmapped() == 522cb93a386Sopenharmony_ci GrMipmapped::kYes)); 523cb93a386Sopenharmony_ci if (q == 0) { 524cb93a386Sopenharmony_ci // We do not placement new the first ViewCountPair since that one is allocated and 525cb93a386Sopenharmony_ci // initialized as part of the TextureOp creation. 526cb93a386Sopenharmony_ci fViewCountPairs[0].fProxy = set[0].fProxyView.detachProxy(); 527cb93a386Sopenharmony_ci fViewCountPairs[0].fQuadCnt = 0; 528cb93a386Sopenharmony_ci curProxy = fViewCountPairs[0].fProxy.get(); 529cb93a386Sopenharmony_ci } else if (set[q].fProxyView.proxy() != curProxy) { 530cb93a386Sopenharmony_ci // We must placement new the ViewCountPairs here so that the sk_sps in the 531cb93a386Sopenharmony_ci // GrSurfaceProxyView get initialized properly. 532cb93a386Sopenharmony_ci new(&fViewCountPairs[++p])ViewCountPair({set[q].fProxyView.detachProxy(), 0}); 533cb93a386Sopenharmony_ci 534cb93a386Sopenharmony_ci curProxy = fViewCountPairs[p].fProxy.get(); 535cb93a386Sopenharmony_ci SkASSERT(GrTextureProxy::ProxiesAreCompatibleAsDynamicState( 536cb93a386Sopenharmony_ci curProxy, fViewCountPairs[0].fProxy.get())); 537cb93a386Sopenharmony_ci SkASSERT(fMetadata.fSwizzle == set[q].fProxyView.swizzle()); 538cb93a386Sopenharmony_ci } // else another quad referencing the same proxy 539cb93a386Sopenharmony_ci 540cb93a386Sopenharmony_ci SkMatrix ctm = viewMatrix; 541cb93a386Sopenharmony_ci if (set[q].fPreViewMatrix) { 542cb93a386Sopenharmony_ci ctm.preConcat(*set[q].fPreViewMatrix); 543cb93a386Sopenharmony_ci } 544cb93a386Sopenharmony_ci 545cb93a386Sopenharmony_ci // Use dstRect/srcRect unless dstClip is provided, in which case derive new source 546cb93a386Sopenharmony_ci // coordinates by mapping dstClipQuad by the dstRect to srcRect transform. 547cb93a386Sopenharmony_ci DrawQuad quad; 548cb93a386Sopenharmony_ci if (set[q].fDstClipQuad) { 549cb93a386Sopenharmony_ci quad.fDevice = GrQuad::MakeFromSkQuad(set[q].fDstClipQuad, ctm); 550cb93a386Sopenharmony_ci 551cb93a386Sopenharmony_ci SkPoint srcPts[4]; 552cb93a386Sopenharmony_ci GrMapRectPoints(set[q].fDstRect, set[q].fSrcRect, set[q].fDstClipQuad, srcPts, 4); 553cb93a386Sopenharmony_ci quad.fLocal = GrQuad::MakeFromSkQuad(srcPts, SkMatrix::I()); 554cb93a386Sopenharmony_ci } else { 555cb93a386Sopenharmony_ci quad.fDevice = GrQuad::MakeFromRect(set[q].fDstRect, ctm); 556cb93a386Sopenharmony_ci quad.fLocal = GrQuad(set[q].fSrcRect); 557cb93a386Sopenharmony_ci } 558cb93a386Sopenharmony_ci 559cb93a386Sopenharmony_ci // This may be reduced per-quad from the requested aggregate filtering level, and used 560cb93a386Sopenharmony_ci // to determine if the subset is needed for the entry as well. 561cb93a386Sopenharmony_ci GrSamplerState::Filter filterForQuad = filter; 562cb93a386Sopenharmony_ci if (netFilter != filter || netMM != mm) { 563cb93a386Sopenharmony_ci // The only way netFilter != filter is if linear is requested and we haven't yet 564cb93a386Sopenharmony_ci // found a quad that requires linear (so net is still nearest). Similar for mip 565cb93a386Sopenharmony_ci // mapping. 566cb93a386Sopenharmony_ci SkASSERT(filter == netFilter || 567cb93a386Sopenharmony_ci (netFilter == GrSamplerState::Filter::kNearest && filter > netFilter)); 568cb93a386Sopenharmony_ci SkASSERT(mm == netMM || 569cb93a386Sopenharmony_ci (netMM == GrSamplerState::MipmapMode::kNone && mm > netMM)); 570cb93a386Sopenharmony_ci auto [mustFilter, mustMM] = filter_and_mm_have_effect(quad.fLocal, quad.fDevice); 571cb93a386Sopenharmony_ci if (filter != GrSamplerState::Filter::kNearest) { 572cb93a386Sopenharmony_ci if (mustFilter) { 573cb93a386Sopenharmony_ci netFilter = filter; // upgrade batch to higher filter level 574cb93a386Sopenharmony_ci } else { 575cb93a386Sopenharmony_ci filterForQuad = GrSamplerState::Filter::kNearest; // downgrade entry 576cb93a386Sopenharmony_ci } 577cb93a386Sopenharmony_ci } 578cb93a386Sopenharmony_ci if (mustMM && mm != GrSamplerState::MipmapMode::kNone) { 579cb93a386Sopenharmony_ci netMM = mm; 580cb93a386Sopenharmony_ci } 581cb93a386Sopenharmony_ci } 582cb93a386Sopenharmony_ci 583cb93a386Sopenharmony_ci // Determine the AA type for the quad, then merge with net AA type 584cb93a386Sopenharmony_ci GrAAType aaForQuad; 585cb93a386Sopenharmony_ci GrQuadUtils::ResolveAAType(aaType, set[q].fAAFlags, quad.fDevice, 586cb93a386Sopenharmony_ci &aaForQuad, &quad.fEdgeFlags); 587cb93a386Sopenharmony_ci // Update overall bounds of the op as the union of all quads 588cb93a386Sopenharmony_ci bounds.joinPossiblyEmptyRect(quad.fDevice.bounds()); 589cb93a386Sopenharmony_ci hasSubpixel |= GrQuadUtils::WillUseHairline(quad.fDevice, aaForQuad, quad.fEdgeFlags); 590cb93a386Sopenharmony_ci 591cb93a386Sopenharmony_ci // Resolve sets aaForQuad to aaType or None, there is never a change between aa methods 592cb93a386Sopenharmony_ci SkASSERT(aaForQuad == GrAAType::kNone || aaForQuad == aaType); 593cb93a386Sopenharmony_ci if (netAAType == GrAAType::kNone && aaForQuad != GrAAType::kNone) { 594cb93a386Sopenharmony_ci netAAType = aaType; 595cb93a386Sopenharmony_ci } 596cb93a386Sopenharmony_ci 597cb93a386Sopenharmony_ci // Calculate metadata for the entry 598cb93a386Sopenharmony_ci const SkRect* subsetForQuad = nullptr; 599cb93a386Sopenharmony_ci if (constraint == SkCanvas::kStrict_SrcRectConstraint) { 600cb93a386Sopenharmony_ci // Check (briefly) if the subset rect is actually needed for this set entry. 601cb93a386Sopenharmony_ci SkRect* subsetRect = &set[q].fSrcRect; 602cb93a386Sopenharmony_ci if (!subsetRect->contains(curProxy->backingStoreBoundsRect())) { 603cb93a386Sopenharmony_ci if (!safe_to_ignore_subset_rect(aaForQuad, filterForQuad, quad, *subsetRect)) { 604cb93a386Sopenharmony_ci netSubset = Subset::kYes; 605cb93a386Sopenharmony_ci subsetForQuad = subsetRect; 606cb93a386Sopenharmony_ci } 607cb93a386Sopenharmony_ci } 608cb93a386Sopenharmony_ci } 609cb93a386Sopenharmony_ci 610cb93a386Sopenharmony_ci // Normalize the src quads and apply origin 611cb93a386Sopenharmony_ci NormalizationParams proxyParams = proxy_normalization_params( 612cb93a386Sopenharmony_ci curProxy, set[q].fProxyView.origin()); 613cb93a386Sopenharmony_ci normalize_src_quad(proxyParams, &quad.fLocal); 614cb93a386Sopenharmony_ci 615cb93a386Sopenharmony_ci // This subset may represent a no-op, otherwise it will have the origin and dimensions 616cb93a386Sopenharmony_ci // of the texture applied to it. 617cb93a386Sopenharmony_ci SkRect subset = normalize_and_inset_subset(filter, proxyParams, subsetForQuad); 618cb93a386Sopenharmony_ci 619cb93a386Sopenharmony_ci // Always append a quad (or 2 if perspective clipped), it just may refer back to a prior 620cb93a386Sopenharmony_ci // ViewCountPair (this frequently happens when Chrome draws 9-patches). 621cb93a386Sopenharmony_ci fViewCountPairs[p].fQuadCnt += this->appendQuad(&quad, set[q].fColor, subset); 622cb93a386Sopenharmony_ci } 623cb93a386Sopenharmony_ci // The # of proxy switches should match what was provided (+1 because we incremented p 624cb93a386Sopenharmony_ci // when a new proxy was encountered). 625cb93a386Sopenharmony_ci SkASSERT((p + 1) == fMetadata.fProxyCount); 626cb93a386Sopenharmony_ci SkASSERT(fQuads.count() == fMetadata.fTotalQuadCount); 627cb93a386Sopenharmony_ci 628cb93a386Sopenharmony_ci fMetadata.fAAType = static_cast<uint16_t>(netAAType); 629cb93a386Sopenharmony_ci fMetadata.fFilter = static_cast<uint16_t>(netFilter); 630cb93a386Sopenharmony_ci fMetadata.fSubset = static_cast<uint16_t>(netSubset); 631cb93a386Sopenharmony_ci 632cb93a386Sopenharmony_ci this->setBounds(bounds, HasAABloat(netAAType == GrAAType::kCoverage), 633cb93a386Sopenharmony_ci hasSubpixel ? IsHairline::kYes : IsHairline::kNo); 634cb93a386Sopenharmony_ci } 635cb93a386Sopenharmony_ci 636cb93a386Sopenharmony_ci int appendQuad(DrawQuad* quad, const SkPMColor4f& color, const SkRect& subset) { 637cb93a386Sopenharmony_ci DrawQuad extra; 638cb93a386Sopenharmony_ci // Always clip to W0 to stay consistent with GrQuad::bounds 639cb93a386Sopenharmony_ci int quadCount = GrQuadUtils::ClipToW0(quad, &extra); 640cb93a386Sopenharmony_ci if (quadCount == 0) { 641cb93a386Sopenharmony_ci // We can't discard the op at this point, but disable AA flags so it won't go through 642cb93a386Sopenharmony_ci // inset/outset processing 643cb93a386Sopenharmony_ci quad->fEdgeFlags = GrQuadAAFlags::kNone; 644cb93a386Sopenharmony_ci quadCount = 1; 645cb93a386Sopenharmony_ci } 646cb93a386Sopenharmony_ci fQuads.append(quad->fDevice, {color, subset, quad->fEdgeFlags}, &quad->fLocal); 647cb93a386Sopenharmony_ci if (quadCount > 1) { 648cb93a386Sopenharmony_ci fQuads.append(extra.fDevice, {color, subset, extra.fEdgeFlags}, &extra.fLocal); 649cb93a386Sopenharmony_ci fMetadata.fTotalQuadCount++; 650cb93a386Sopenharmony_ci } 651cb93a386Sopenharmony_ci return quadCount; 652cb93a386Sopenharmony_ci } 653cb93a386Sopenharmony_ci 654cb93a386Sopenharmony_ci GrProgramInfo* programInfo() override { 655cb93a386Sopenharmony_ci // Although this Op implements its own onPrePrepareDraws it calls GrMeshDrawOps' version so 656cb93a386Sopenharmony_ci // this entry point will be called. 657cb93a386Sopenharmony_ci return (fDesc) ? fDesc->fProgramInfo : nullptr; 658cb93a386Sopenharmony_ci } 659cb93a386Sopenharmony_ci 660cb93a386Sopenharmony_ci void onCreateProgramInfo(const GrCaps* caps, 661cb93a386Sopenharmony_ci SkArenaAlloc* arena, 662cb93a386Sopenharmony_ci const GrSurfaceProxyView& writeView, 663cb93a386Sopenharmony_ci bool usesMSAASurface, 664cb93a386Sopenharmony_ci GrAppliedClip&& appliedClip, 665cb93a386Sopenharmony_ci const GrDstProxyView& dstProxyView, 666cb93a386Sopenharmony_ci GrXferBarrierFlags renderPassXferBarriers, 667cb93a386Sopenharmony_ci GrLoadOp colorLoadOp) override { 668cb93a386Sopenharmony_ci SkASSERT(fDesc); 669cb93a386Sopenharmony_ci 670cb93a386Sopenharmony_ci GrGeometryProcessor* gp; 671cb93a386Sopenharmony_ci 672cb93a386Sopenharmony_ci { 673cb93a386Sopenharmony_ci const GrBackendFormat& backendFormat = 674cb93a386Sopenharmony_ci fViewCountPairs[0].fProxy->backendFormat(); 675cb93a386Sopenharmony_ci 676cb93a386Sopenharmony_ci GrSamplerState samplerState = GrSamplerState(GrSamplerState::WrapMode::kClamp, 677cb93a386Sopenharmony_ci fMetadata.filter()); 678cb93a386Sopenharmony_ci 679cb93a386Sopenharmony_ci gp = skgpu::v1::QuadPerEdgeAA::MakeTexturedProcessor( 680cb93a386Sopenharmony_ci arena, fDesc->fVertexSpec, *caps->shaderCaps(), backendFormat, samplerState, 681cb93a386Sopenharmony_ci fMetadata.fSwizzle, std::move(fTextureColorSpaceXform), fMetadata.saturate()); 682cb93a386Sopenharmony_ci 683cb93a386Sopenharmony_ci SkASSERT(fDesc->fVertexSpec.vertexSize() == gp->vertexStride()); 684cb93a386Sopenharmony_ci } 685cb93a386Sopenharmony_ci 686cb93a386Sopenharmony_ci fDesc->fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo( 687cb93a386Sopenharmony_ci caps, arena, writeView, usesMSAASurface, std::move(appliedClip), dstProxyView, gp, 688cb93a386Sopenharmony_ci GrProcessorSet::MakeEmptySet(), fDesc->fVertexSpec.primitiveType(), 689cb93a386Sopenharmony_ci renderPassXferBarriers, colorLoadOp, GrPipeline::InputFlags::kNone); 690cb93a386Sopenharmony_ci } 691cb93a386Sopenharmony_ci 692cb93a386Sopenharmony_ci void onPrePrepareDraws(GrRecordingContext* context, 693cb93a386Sopenharmony_ci const GrSurfaceProxyView& writeView, 694cb93a386Sopenharmony_ci GrAppliedClip* clip, 695cb93a386Sopenharmony_ci const GrDstProxyView& dstProxyView, 696cb93a386Sopenharmony_ci GrXferBarrierFlags renderPassXferBarriers, 697cb93a386Sopenharmony_ci GrLoadOp colorLoadOp) override { 698cb93a386Sopenharmony_ci TRACE_EVENT0("skia.gpu", TRACE_FUNC); 699cb93a386Sopenharmony_ci 700cb93a386Sopenharmony_ci SkDEBUGCODE(this->validate();) 701cb93a386Sopenharmony_ci SkASSERT(!fDesc); 702cb93a386Sopenharmony_ci 703cb93a386Sopenharmony_ci SkArenaAlloc* arena = context->priv().recordTimeAllocator(); 704cb93a386Sopenharmony_ci 705cb93a386Sopenharmony_ci fDesc = arena->make<Desc>(); 706cb93a386Sopenharmony_ci this->characterize(fDesc); 707cb93a386Sopenharmony_ci fDesc->allocatePrePreparedVertices(arena); 708cb93a386Sopenharmony_ci FillInVertices(*context->priv().caps(), this, fDesc, fDesc->fPrePreparedVertices); 709cb93a386Sopenharmony_ci 710cb93a386Sopenharmony_ci // This will call onCreateProgramInfo and register the created program with the DDL. 711cb93a386Sopenharmony_ci this->INHERITED::onPrePrepareDraws(context, writeView, clip, dstProxyView, 712cb93a386Sopenharmony_ci renderPassXferBarriers, colorLoadOp); 713cb93a386Sopenharmony_ci } 714cb93a386Sopenharmony_ci 715cb93a386Sopenharmony_ci static void FillInVertices(const GrCaps& caps, 716cb93a386Sopenharmony_ci TextureOpImpl* texOp, 717cb93a386Sopenharmony_ci Desc* desc, 718cb93a386Sopenharmony_ci char* vertexData) { 719cb93a386Sopenharmony_ci SkASSERT(vertexData); 720cb93a386Sopenharmony_ci 721cb93a386Sopenharmony_ci SkDEBUGCODE(int totQuadsSeen = 0;) 722cb93a386Sopenharmony_ci SkDEBUGCODE(int totVerticesSeen = 0;) 723cb93a386Sopenharmony_ci SkDEBUGCODE(const size_t vertexSize = desc->fVertexSpec.vertexSize()); 724cb93a386Sopenharmony_ci 725cb93a386Sopenharmony_ci skgpu::v1::QuadPerEdgeAA::Tessellator tessellator(desc->fVertexSpec, vertexData); 726cb93a386Sopenharmony_ci for (const auto& op : ChainRange<TextureOpImpl>(texOp)) { 727cb93a386Sopenharmony_ci auto iter = op.fQuads.iterator(); 728cb93a386Sopenharmony_ci for (unsigned p = 0; p < op.fMetadata.fProxyCount; ++p) { 729cb93a386Sopenharmony_ci const int quadCnt = op.fViewCountPairs[p].fQuadCnt; 730cb93a386Sopenharmony_ci SkDEBUGCODE(int meshVertexCnt = quadCnt * desc->fVertexSpec.verticesPerQuad()); 731cb93a386Sopenharmony_ci 732cb93a386Sopenharmony_ci for (int i = 0; i < quadCnt && iter.next(); ++i) { 733cb93a386Sopenharmony_ci SkASSERT(iter.isLocalValid()); 734cb93a386Sopenharmony_ci const ColorSubsetAndAA& info = iter.metadata(); 735cb93a386Sopenharmony_ci 736cb93a386Sopenharmony_ci tessellator.append(iter.deviceQuad(), iter.localQuad(), info.fColor, 737cb93a386Sopenharmony_ci info.fSubsetRect, info.aaFlags()); 738cb93a386Sopenharmony_ci } 739cb93a386Sopenharmony_ci 740cb93a386Sopenharmony_ci SkASSERT((totVerticesSeen + meshVertexCnt) * vertexSize 741cb93a386Sopenharmony_ci == (size_t)(tessellator.vertices() - vertexData)); 742cb93a386Sopenharmony_ci 743cb93a386Sopenharmony_ci SkDEBUGCODE(totQuadsSeen += quadCnt;) 744cb93a386Sopenharmony_ci SkDEBUGCODE(totVerticesSeen += meshVertexCnt); 745cb93a386Sopenharmony_ci SkASSERT(totQuadsSeen * desc->fVertexSpec.verticesPerQuad() == totVerticesSeen); 746cb93a386Sopenharmony_ci } 747cb93a386Sopenharmony_ci 748cb93a386Sopenharmony_ci // If quad counts per proxy were calculated correctly, the entire iterator 749cb93a386Sopenharmony_ci // should have been consumed. 750cb93a386Sopenharmony_ci SkASSERT(!iter.next()); 751cb93a386Sopenharmony_ci } 752cb93a386Sopenharmony_ci 753cb93a386Sopenharmony_ci SkASSERT(desc->totalSizeInBytes() == (size_t)(tessellator.vertices() - vertexData)); 754cb93a386Sopenharmony_ci SkASSERT(totQuadsSeen == desc->fNumTotalQuads); 755cb93a386Sopenharmony_ci SkASSERT(totVerticesSeen == desc->totalNumVertices()); 756cb93a386Sopenharmony_ci } 757cb93a386Sopenharmony_ci 758cb93a386Sopenharmony_ci#ifdef SK_DEBUG 759cb93a386Sopenharmony_ci static int validate_op(GrTextureType textureType, 760cb93a386Sopenharmony_ci GrAAType aaType, 761cb93a386Sopenharmony_ci GrSwizzle swizzle, 762cb93a386Sopenharmony_ci const TextureOpImpl* op) { 763cb93a386Sopenharmony_ci SkASSERT(op->fMetadata.fSwizzle == swizzle); 764cb93a386Sopenharmony_ci 765cb93a386Sopenharmony_ci int quadCount = 0; 766cb93a386Sopenharmony_ci for (unsigned p = 0; p < op->fMetadata.fProxyCount; ++p) { 767cb93a386Sopenharmony_ci auto* proxy = op->fViewCountPairs[p].fProxy->asTextureProxy(); 768cb93a386Sopenharmony_ci quadCount += op->fViewCountPairs[p].fQuadCnt; 769cb93a386Sopenharmony_ci SkASSERT(proxy); 770cb93a386Sopenharmony_ci SkASSERT(proxy->textureType() == textureType); 771cb93a386Sopenharmony_ci } 772cb93a386Sopenharmony_ci 773cb93a386Sopenharmony_ci SkASSERT(aaType == op->fMetadata.aaType()); 774cb93a386Sopenharmony_ci return quadCount; 775cb93a386Sopenharmony_ci } 776cb93a386Sopenharmony_ci 777cb93a386Sopenharmony_ci void validate() const override { 778cb93a386Sopenharmony_ci // NOTE: Since this is debug-only code, we use the virtual asTextureProxy() 779cb93a386Sopenharmony_ci auto textureType = fViewCountPairs[0].fProxy->asTextureProxy()->textureType(); 780cb93a386Sopenharmony_ci GrAAType aaType = fMetadata.aaType(); 781cb93a386Sopenharmony_ci GrSwizzle swizzle = fMetadata.fSwizzle; 782cb93a386Sopenharmony_ci 783cb93a386Sopenharmony_ci int quadCount = validate_op(textureType, aaType, swizzle, this); 784cb93a386Sopenharmony_ci 785cb93a386Sopenharmony_ci for (const GrOp* tmp = this->prevInChain(); tmp; tmp = tmp->prevInChain()) { 786cb93a386Sopenharmony_ci quadCount += validate_op(textureType, aaType, swizzle, 787cb93a386Sopenharmony_ci static_cast<const TextureOpImpl*>(tmp)); 788cb93a386Sopenharmony_ci } 789cb93a386Sopenharmony_ci 790cb93a386Sopenharmony_ci for (const GrOp* tmp = this->nextInChain(); tmp; tmp = tmp->nextInChain()) { 791cb93a386Sopenharmony_ci quadCount += validate_op(textureType, aaType, swizzle, 792cb93a386Sopenharmony_ci static_cast<const TextureOpImpl*>(tmp)); 793cb93a386Sopenharmony_ci } 794cb93a386Sopenharmony_ci 795cb93a386Sopenharmony_ci SkASSERT(quadCount == this->numChainedQuads()); 796cb93a386Sopenharmony_ci } 797cb93a386Sopenharmony_ci 798cb93a386Sopenharmony_ci#endif 799cb93a386Sopenharmony_ci 800cb93a386Sopenharmony_ci#if GR_TEST_UTILS 801cb93a386Sopenharmony_ci int numQuads() const final { return this->totNumQuads(); } 802cb93a386Sopenharmony_ci#endif 803cb93a386Sopenharmony_ci 804cb93a386Sopenharmony_ci void characterize(Desc* desc) const { 805cb93a386Sopenharmony_ci SkDEBUGCODE(this->validate();) 806cb93a386Sopenharmony_ci 807cb93a386Sopenharmony_ci GrQuad::Type quadType = GrQuad::Type::kAxisAligned; 808cb93a386Sopenharmony_ci ColorType colorType = ColorType::kNone; 809cb93a386Sopenharmony_ci GrQuad::Type srcQuadType = GrQuad::Type::kAxisAligned; 810cb93a386Sopenharmony_ci Subset subset = Subset::kNo; 811cb93a386Sopenharmony_ci GrAAType overallAAType = fMetadata.aaType(); 812cb93a386Sopenharmony_ci 813cb93a386Sopenharmony_ci desc->fNumProxies = 0; 814cb93a386Sopenharmony_ci desc->fNumTotalQuads = 0; 815cb93a386Sopenharmony_ci int maxQuadsPerMesh = 0; 816cb93a386Sopenharmony_ci 817cb93a386Sopenharmony_ci for (const auto& op : ChainRange<TextureOpImpl>(this)) { 818cb93a386Sopenharmony_ci if (op.fQuads.deviceQuadType() > quadType) { 819cb93a386Sopenharmony_ci quadType = op.fQuads.deviceQuadType(); 820cb93a386Sopenharmony_ci } 821cb93a386Sopenharmony_ci if (op.fQuads.localQuadType() > srcQuadType) { 822cb93a386Sopenharmony_ci srcQuadType = op.fQuads.localQuadType(); 823cb93a386Sopenharmony_ci } 824cb93a386Sopenharmony_ci if (op.fMetadata.subset() == Subset::kYes) { 825cb93a386Sopenharmony_ci subset = Subset::kYes; 826cb93a386Sopenharmony_ci } 827cb93a386Sopenharmony_ci colorType = std::max(colorType, op.fMetadata.colorType()); 828cb93a386Sopenharmony_ci desc->fNumProxies += op.fMetadata.fProxyCount; 829cb93a386Sopenharmony_ci 830cb93a386Sopenharmony_ci for (unsigned p = 0; p < op.fMetadata.fProxyCount; ++p) { 831cb93a386Sopenharmony_ci maxQuadsPerMesh = std::max(op.fViewCountPairs[p].fQuadCnt, maxQuadsPerMesh); 832cb93a386Sopenharmony_ci } 833cb93a386Sopenharmony_ci desc->fNumTotalQuads += op.totNumQuads(); 834cb93a386Sopenharmony_ci 835cb93a386Sopenharmony_ci if (op.fMetadata.aaType() == GrAAType::kCoverage) { 836cb93a386Sopenharmony_ci overallAAType = GrAAType::kCoverage; 837cb93a386Sopenharmony_ci } 838cb93a386Sopenharmony_ci } 839cb93a386Sopenharmony_ci 840cb93a386Sopenharmony_ci SkASSERT(desc->fNumTotalQuads == this->numChainedQuads()); 841cb93a386Sopenharmony_ci 842cb93a386Sopenharmony_ci SkASSERT(!CombinedQuadCountWillOverflow(overallAAType, false, desc->fNumTotalQuads)); 843cb93a386Sopenharmony_ci 844cb93a386Sopenharmony_ci auto indexBufferOption = skgpu::v1::QuadPerEdgeAA::CalcIndexBufferOption(overallAAType, 845cb93a386Sopenharmony_ci maxQuadsPerMesh); 846cb93a386Sopenharmony_ci 847cb93a386Sopenharmony_ci desc->fVertexSpec = VertexSpec(quadType, colorType, srcQuadType, /* hasLocal */ true, 848cb93a386Sopenharmony_ci subset, overallAAType, /* alpha as coverage */ true, 849cb93a386Sopenharmony_ci indexBufferOption); 850cb93a386Sopenharmony_ci 851cb93a386Sopenharmony_ci SkASSERT(desc->fNumTotalQuads <= skgpu::v1::QuadPerEdgeAA::QuadLimit(indexBufferOption)); 852cb93a386Sopenharmony_ci } 853cb93a386Sopenharmony_ci 854cb93a386Sopenharmony_ci int totNumQuads() const { 855cb93a386Sopenharmony_ci#ifdef SK_DEBUG 856cb93a386Sopenharmony_ci int tmp = 0; 857cb93a386Sopenharmony_ci for (unsigned p = 0; p < fMetadata.fProxyCount; ++p) { 858cb93a386Sopenharmony_ci tmp += fViewCountPairs[p].fQuadCnt; 859cb93a386Sopenharmony_ci } 860cb93a386Sopenharmony_ci SkASSERT(tmp == fMetadata.fTotalQuadCount); 861cb93a386Sopenharmony_ci#endif 862cb93a386Sopenharmony_ci 863cb93a386Sopenharmony_ci return fMetadata.fTotalQuadCount; 864cb93a386Sopenharmony_ci } 865cb93a386Sopenharmony_ci 866cb93a386Sopenharmony_ci int numChainedQuads() const { 867cb93a386Sopenharmony_ci int numChainedQuads = this->totNumQuads(); 868cb93a386Sopenharmony_ci 869cb93a386Sopenharmony_ci for (const GrOp* tmp = this->prevInChain(); tmp; tmp = tmp->prevInChain()) { 870cb93a386Sopenharmony_ci numChainedQuads += ((const TextureOpImpl*)tmp)->totNumQuads(); 871cb93a386Sopenharmony_ci } 872cb93a386Sopenharmony_ci 873cb93a386Sopenharmony_ci for (const GrOp* tmp = this->nextInChain(); tmp; tmp = tmp->nextInChain()) { 874cb93a386Sopenharmony_ci numChainedQuads += ((const TextureOpImpl*)tmp)->totNumQuads(); 875cb93a386Sopenharmony_ci } 876cb93a386Sopenharmony_ci 877cb93a386Sopenharmony_ci return numChainedQuads; 878cb93a386Sopenharmony_ci } 879cb93a386Sopenharmony_ci 880cb93a386Sopenharmony_ci // onPrePrepareDraws may or may not have been called at this point 881cb93a386Sopenharmony_ci void onPrepareDraws(GrMeshDrawTarget* target) override { 882cb93a386Sopenharmony_ci TRACE_EVENT0("skia.gpu", TRACE_FUNC); 883cb93a386Sopenharmony_ci 884cb93a386Sopenharmony_ci SkDEBUGCODE(this->validate();) 885cb93a386Sopenharmony_ci 886cb93a386Sopenharmony_ci SkASSERT(!fDesc || fDesc->fPrePreparedVertices); 887cb93a386Sopenharmony_ci 888cb93a386Sopenharmony_ci if (!fDesc) { 889cb93a386Sopenharmony_ci SkArenaAlloc* arena = target->allocator(); 890cb93a386Sopenharmony_ci fDesc = arena->make<Desc>(); 891cb93a386Sopenharmony_ci this->characterize(fDesc); 892cb93a386Sopenharmony_ci SkASSERT(!fDesc->fPrePreparedVertices); 893cb93a386Sopenharmony_ci } 894cb93a386Sopenharmony_ci 895cb93a386Sopenharmony_ci size_t vertexSize = fDesc->fVertexSpec.vertexSize(); 896cb93a386Sopenharmony_ci 897cb93a386Sopenharmony_ci void* vdata = target->makeVertexSpace(vertexSize, fDesc->totalNumVertices(), 898cb93a386Sopenharmony_ci &fDesc->fVertexBuffer, &fDesc->fBaseVertex); 899cb93a386Sopenharmony_ci if (!vdata) { 900cb93a386Sopenharmony_ci SkDebugf("Could not allocate vertices\n"); 901cb93a386Sopenharmony_ci return; 902cb93a386Sopenharmony_ci } 903cb93a386Sopenharmony_ci 904cb93a386Sopenharmony_ci if (fDesc->fVertexSpec.needsIndexBuffer()) { 905cb93a386Sopenharmony_ci fDesc->fIndexBuffer = skgpu::v1::QuadPerEdgeAA::GetIndexBuffer( 906cb93a386Sopenharmony_ci target, fDesc->fVertexSpec.indexBufferOption()); 907cb93a386Sopenharmony_ci if (!fDesc->fIndexBuffer) { 908cb93a386Sopenharmony_ci SkDebugf("Could not allocate indices\n"); 909cb93a386Sopenharmony_ci return; 910cb93a386Sopenharmony_ci } 911cb93a386Sopenharmony_ci } 912cb93a386Sopenharmony_ci 913cb93a386Sopenharmony_ci if (fDesc->fPrePreparedVertices) { 914cb93a386Sopenharmony_ci memcpy(vdata, fDesc->fPrePreparedVertices, fDesc->totalSizeInBytes()); 915cb93a386Sopenharmony_ci } else { 916cb93a386Sopenharmony_ci FillInVertices(target->caps(), this, fDesc, (char*) vdata); 917cb93a386Sopenharmony_ci } 918cb93a386Sopenharmony_ci } 919cb93a386Sopenharmony_ci 920cb93a386Sopenharmony_ci void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override { 921cb93a386Sopenharmony_ci if (!fDesc->fVertexBuffer) { 922cb93a386Sopenharmony_ci return; 923cb93a386Sopenharmony_ci } 924cb93a386Sopenharmony_ci 925cb93a386Sopenharmony_ci if (fDesc->fVertexSpec.needsIndexBuffer() && !fDesc->fIndexBuffer) { 926cb93a386Sopenharmony_ci return; 927cb93a386Sopenharmony_ci } 928cb93a386Sopenharmony_ci 929cb93a386Sopenharmony_ci if (!fDesc->fProgramInfo) { 930cb93a386Sopenharmony_ci this->createProgramInfo(flushState); 931cb93a386Sopenharmony_ci SkASSERT(fDesc->fProgramInfo); 932cb93a386Sopenharmony_ci } 933cb93a386Sopenharmony_ci 934cb93a386Sopenharmony_ci flushState->bindPipelineAndScissorClip(*fDesc->fProgramInfo, chainBounds); 935cb93a386Sopenharmony_ci flushState->bindBuffers(std::move(fDesc->fIndexBuffer), nullptr, 936cb93a386Sopenharmony_ci std::move(fDesc->fVertexBuffer)); 937cb93a386Sopenharmony_ci 938cb93a386Sopenharmony_ci int totQuadsSeen = 0; 939cb93a386Sopenharmony_ci SkDEBUGCODE(int numDraws = 0;) 940cb93a386Sopenharmony_ci for (const auto& op : ChainRange<TextureOpImpl>(this)) { 941cb93a386Sopenharmony_ci for (unsigned p = 0; p < op.fMetadata.fProxyCount; ++p) { 942cb93a386Sopenharmony_ci const int quadCnt = op.fViewCountPairs[p].fQuadCnt; 943cb93a386Sopenharmony_ci SkASSERT(numDraws < fDesc->fNumProxies); 944cb93a386Sopenharmony_ci flushState->bindTextures(fDesc->fProgramInfo->geomProc(), 945cb93a386Sopenharmony_ci *op.fViewCountPairs[p].fProxy, 946cb93a386Sopenharmony_ci fDesc->fProgramInfo->pipeline()); 947cb93a386Sopenharmony_ci skgpu::v1::QuadPerEdgeAA::IssueDraw(flushState->caps(), flushState->opsRenderPass(), 948cb93a386Sopenharmony_ci fDesc->fVertexSpec, totQuadsSeen, quadCnt, 949cb93a386Sopenharmony_ci fDesc->totalNumVertices(), fDesc->fBaseVertex); 950cb93a386Sopenharmony_ci totQuadsSeen += quadCnt; 951cb93a386Sopenharmony_ci SkDEBUGCODE(++numDraws;) 952cb93a386Sopenharmony_ci } 953cb93a386Sopenharmony_ci } 954cb93a386Sopenharmony_ci 955cb93a386Sopenharmony_ci SkASSERT(totQuadsSeen == fDesc->fNumTotalQuads); 956cb93a386Sopenharmony_ci SkASSERT(numDraws == fDesc->fNumProxies); 957cb93a386Sopenharmony_ci } 958cb93a386Sopenharmony_ci 959cb93a386Sopenharmony_ci void propagateCoverageAAThroughoutChain() { 960cb93a386Sopenharmony_ci fMetadata.fAAType = static_cast<uint16_t>(GrAAType::kCoverage); 961cb93a386Sopenharmony_ci 962cb93a386Sopenharmony_ci for (GrOp* tmp = this->prevInChain(); tmp; tmp = tmp->prevInChain()) { 963cb93a386Sopenharmony_ci auto tex = static_cast<TextureOpImpl*>(tmp); 964cb93a386Sopenharmony_ci SkASSERT(tex->fMetadata.aaType() == GrAAType::kCoverage || 965cb93a386Sopenharmony_ci tex->fMetadata.aaType() == GrAAType::kNone); 966cb93a386Sopenharmony_ci tex->fMetadata.fAAType = static_cast<uint16_t>(GrAAType::kCoverage); 967cb93a386Sopenharmony_ci } 968cb93a386Sopenharmony_ci 969cb93a386Sopenharmony_ci for (GrOp* tmp = this->nextInChain(); tmp; tmp = tmp->nextInChain()) { 970cb93a386Sopenharmony_ci auto tex = static_cast<TextureOpImpl*>(tmp); 971cb93a386Sopenharmony_ci SkASSERT(tex->fMetadata.aaType() == GrAAType::kCoverage || 972cb93a386Sopenharmony_ci tex->fMetadata.aaType() == GrAAType::kNone); 973cb93a386Sopenharmony_ci tex->fMetadata.fAAType = static_cast<uint16_t>(GrAAType::kCoverage); 974cb93a386Sopenharmony_ci } 975cb93a386Sopenharmony_ci } 976cb93a386Sopenharmony_ci 977cb93a386Sopenharmony_ci CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override { 978cb93a386Sopenharmony_ci TRACE_EVENT0("skia.gpu", TRACE_FUNC); 979cb93a386Sopenharmony_ci auto that = t->cast<TextureOpImpl>(); 980cb93a386Sopenharmony_ci 981cb93a386Sopenharmony_ci SkDEBUGCODE(this->validate();) 982cb93a386Sopenharmony_ci SkDEBUGCODE(that->validate();) 983cb93a386Sopenharmony_ci 984cb93a386Sopenharmony_ci if (fDesc || that->fDesc) { 985cb93a386Sopenharmony_ci // This should never happen (since only DDL recorded ops should be prePrepared) 986cb93a386Sopenharmony_ci // but, in any case, we should never combine ops that that been prePrepared 987cb93a386Sopenharmony_ci return CombineResult::kCannotCombine; 988cb93a386Sopenharmony_ci } 989cb93a386Sopenharmony_ci 990cb93a386Sopenharmony_ci if (fMetadata.subset() != that->fMetadata.subset()) { 991cb93a386Sopenharmony_ci // It is technically possible to combine operations across subset modes, but performance 992cb93a386Sopenharmony_ci // testing suggests it's better to make more draw calls where some take advantage of 993cb93a386Sopenharmony_ci // the more optimal shader path without coordinate clamping. 994cb93a386Sopenharmony_ci return CombineResult::kCannotCombine; 995cb93a386Sopenharmony_ci } 996cb93a386Sopenharmony_ci if (!GrColorSpaceXform::Equals(fTextureColorSpaceXform.get(), 997cb93a386Sopenharmony_ci that->fTextureColorSpaceXform.get())) { 998cb93a386Sopenharmony_ci return CombineResult::kCannotCombine; 999cb93a386Sopenharmony_ci } 1000cb93a386Sopenharmony_ci 1001cb93a386Sopenharmony_ci bool upgradeToCoverageAAOnMerge = false; 1002cb93a386Sopenharmony_ci if (fMetadata.aaType() != that->fMetadata.aaType()) { 1003cb93a386Sopenharmony_ci if (!CanUpgradeAAOnMerge(fMetadata.aaType(), that->fMetadata.aaType())) { 1004cb93a386Sopenharmony_ci return CombineResult::kCannotCombine; 1005cb93a386Sopenharmony_ci } 1006cb93a386Sopenharmony_ci upgradeToCoverageAAOnMerge = true; 1007cb93a386Sopenharmony_ci } 1008cb93a386Sopenharmony_ci 1009cb93a386Sopenharmony_ci if (CombinedQuadCountWillOverflow(fMetadata.aaType(), upgradeToCoverageAAOnMerge, 1010cb93a386Sopenharmony_ci this->numChainedQuads() + that->numChainedQuads())) { 1011cb93a386Sopenharmony_ci return CombineResult::kCannotCombine; 1012cb93a386Sopenharmony_ci } 1013cb93a386Sopenharmony_ci 1014cb93a386Sopenharmony_ci if (fMetadata.saturate() != that->fMetadata.saturate()) { 1015cb93a386Sopenharmony_ci return CombineResult::kCannotCombine; 1016cb93a386Sopenharmony_ci } 1017cb93a386Sopenharmony_ci if (fMetadata.filter() != that->fMetadata.filter()) { 1018cb93a386Sopenharmony_ci return CombineResult::kCannotCombine; 1019cb93a386Sopenharmony_ci } 1020cb93a386Sopenharmony_ci if (fMetadata.mipmapMode() != that->fMetadata.mipmapMode()) { 1021cb93a386Sopenharmony_ci return CombineResult::kCannotCombine; 1022cb93a386Sopenharmony_ci } 1023cb93a386Sopenharmony_ci if (fMetadata.fSwizzle != that->fMetadata.fSwizzle) { 1024cb93a386Sopenharmony_ci return CombineResult::kCannotCombine; 1025cb93a386Sopenharmony_ci } 1026cb93a386Sopenharmony_ci const auto* thisProxy = fViewCountPairs[0].fProxy.get(); 1027cb93a386Sopenharmony_ci const auto* thatProxy = that->fViewCountPairs[0].fProxy.get(); 1028cb93a386Sopenharmony_ci if (fMetadata.fProxyCount > 1 || that->fMetadata.fProxyCount > 1 || 1029cb93a386Sopenharmony_ci thisProxy != thatProxy) { 1030cb93a386Sopenharmony_ci // We can't merge across different proxies. Check if 'this' can be chained with 'that'. 1031cb93a386Sopenharmony_ci if (GrTextureProxy::ProxiesAreCompatibleAsDynamicState(thisProxy, thatProxy) && 1032cb93a386Sopenharmony_ci caps.dynamicStateArrayGeometryProcessorTextureSupport() && 1033cb93a386Sopenharmony_ci fMetadata.aaType() == that->fMetadata.aaType()) { 1034cb93a386Sopenharmony_ci // We only allow chaining when the aaTypes match bc otherwise the AA type 1035cb93a386Sopenharmony_ci // reported by the chain can be inconsistent. That is, since chaining doesn't 1036cb93a386Sopenharmony_ci // propagate revised AA information throughout the chain, the head of the chain 1037cb93a386Sopenharmony_ci // could have an AA setting of kNone while the chain as a whole could have a 1038cb93a386Sopenharmony_ci // setting of kCoverage. This inconsistency would then interfere with the validity 1039cb93a386Sopenharmony_ci // of the CombinedQuadCountWillOverflow calls. 1040cb93a386Sopenharmony_ci // This problem doesn't occur w/ merging bc we do propagate the AA information 1041cb93a386Sopenharmony_ci // (in propagateCoverageAAThroughoutChain) below. 1042cb93a386Sopenharmony_ci return CombineResult::kMayChain; 1043cb93a386Sopenharmony_ci } 1044cb93a386Sopenharmony_ci return CombineResult::kCannotCombine; 1045cb93a386Sopenharmony_ci } 1046cb93a386Sopenharmony_ci 1047cb93a386Sopenharmony_ci fMetadata.fSubset |= that->fMetadata.fSubset; 1048cb93a386Sopenharmony_ci fMetadata.fColorType = std::max(fMetadata.fColorType, that->fMetadata.fColorType); 1049cb93a386Sopenharmony_ci 1050cb93a386Sopenharmony_ci // Concatenate quad lists together 1051cb93a386Sopenharmony_ci fQuads.concat(that->fQuads); 1052cb93a386Sopenharmony_ci fViewCountPairs[0].fQuadCnt += that->fQuads.count(); 1053cb93a386Sopenharmony_ci fMetadata.fTotalQuadCount += that->fQuads.count(); 1054cb93a386Sopenharmony_ci 1055cb93a386Sopenharmony_ci if (upgradeToCoverageAAOnMerge) { 1056cb93a386Sopenharmony_ci // This merger may be the start of a concatenation of two chains. When one 1057cb93a386Sopenharmony_ci // of the chains mutates its AA the other must follow suit or else the above AA 1058cb93a386Sopenharmony_ci // check may prevent later ops from chaining together. A specific example of this is 1059cb93a386Sopenharmony_ci // when chain2 is prepended onto chain1: 1060cb93a386Sopenharmony_ci // chain1 (that): opA (non-AA/mergeable) opB (non-AA/non-mergeable) 1061cb93a386Sopenharmony_ci // chain2 (this): opC (cov-AA/non-mergeable) opD (cov-AA/mergeable) 1062cb93a386Sopenharmony_ci // W/o this propagation, after opD & opA merge, opB and opC would say they couldn't 1063cb93a386Sopenharmony_ci // chain - which would stop the concatenation process. 1064cb93a386Sopenharmony_ci this->propagateCoverageAAThroughoutChain(); 1065cb93a386Sopenharmony_ci that->propagateCoverageAAThroughoutChain(); 1066cb93a386Sopenharmony_ci } 1067cb93a386Sopenharmony_ci 1068cb93a386Sopenharmony_ci SkDEBUGCODE(this->validate();) 1069cb93a386Sopenharmony_ci 1070cb93a386Sopenharmony_ci return CombineResult::kMerged; 1071cb93a386Sopenharmony_ci } 1072cb93a386Sopenharmony_ci 1073cb93a386Sopenharmony_ci#if GR_TEST_UTILS 1074cb93a386Sopenharmony_ci SkString onDumpInfo() const override { 1075cb93a386Sopenharmony_ci SkString str = SkStringPrintf("# draws: %d\n", fQuads.count()); 1076cb93a386Sopenharmony_ci auto iter = fQuads.iterator(); 1077cb93a386Sopenharmony_ci for (unsigned p = 0; p < fMetadata.fProxyCount; ++p) { 1078cb93a386Sopenharmony_ci SkString proxyStr = fViewCountPairs[p].fProxy->dump(); 1079cb93a386Sopenharmony_ci str.append(proxyStr); 1080cb93a386Sopenharmony_ci str.appendf(", Filter: %d, MM: %d\n", 1081cb93a386Sopenharmony_ci static_cast<int>(fMetadata.fFilter), 1082cb93a386Sopenharmony_ci static_cast<int>(fMetadata.fMipmapMode)); 1083cb93a386Sopenharmony_ci for (int i = 0; i < fViewCountPairs[p].fQuadCnt && iter.next(); ++i) { 1084cb93a386Sopenharmony_ci const GrQuad* quad = iter.deviceQuad(); 1085cb93a386Sopenharmony_ci GrQuad uv = iter.isLocalValid() ? *(iter.localQuad()) : GrQuad(); 1086cb93a386Sopenharmony_ci const ColorSubsetAndAA& info = iter.metadata(); 1087cb93a386Sopenharmony_ci str.appendf( 1088cb93a386Sopenharmony_ci "%d: Color: 0x%08x, Subset(%d): [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n" 1089cb93a386Sopenharmony_ci " UVs [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n" 1090cb93a386Sopenharmony_ci " Quad [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n", 1091cb93a386Sopenharmony_ci i, info.fColor.toBytes_RGBA(), fMetadata.fSubset, info.fSubsetRect.fLeft, 1092cb93a386Sopenharmony_ci info.fSubsetRect.fTop, info.fSubsetRect.fRight, info.fSubsetRect.fBottom, 1093cb93a386Sopenharmony_ci quad->point(0).fX, quad->point(0).fY, quad->point(1).fX, quad->point(1).fY, 1094cb93a386Sopenharmony_ci quad->point(2).fX, quad->point(2).fY, quad->point(3).fX, quad->point(3).fY, 1095cb93a386Sopenharmony_ci uv.point(0).fX, uv.point(0).fY, uv.point(1).fX, uv.point(1).fY, 1096cb93a386Sopenharmony_ci uv.point(2).fX, uv.point(2).fY, uv.point(3).fX, uv.point(3).fY); 1097cb93a386Sopenharmony_ci } 1098cb93a386Sopenharmony_ci } 1099cb93a386Sopenharmony_ci return str; 1100cb93a386Sopenharmony_ci } 1101cb93a386Sopenharmony_ci#endif 1102cb93a386Sopenharmony_ci 1103cb93a386Sopenharmony_ci GrQuadBuffer<ColorSubsetAndAA> fQuads; 1104cb93a386Sopenharmony_ci sk_sp<GrColorSpaceXform> fTextureColorSpaceXform; 1105cb93a386Sopenharmony_ci // Most state of TextureOp is packed into these two field to minimize the op's size. 1106cb93a386Sopenharmony_ci // Historically, increasing the size of TextureOp has caused surprising perf regressions, so 1107cb93a386Sopenharmony_ci // consider/measure changes with care. 1108cb93a386Sopenharmony_ci Desc* fDesc; 1109cb93a386Sopenharmony_ci Metadata fMetadata; 1110cb93a386Sopenharmony_ci 1111cb93a386Sopenharmony_ci // This field must go last. When allocating this op, we will allocate extra space to hold 1112cb93a386Sopenharmony_ci // additional ViewCountPairs immediately after the op's allocation so we can treat this 1113cb93a386Sopenharmony_ci // as an fProxyCnt-length array. 1114cb93a386Sopenharmony_ci ViewCountPair fViewCountPairs[1]; 1115cb93a386Sopenharmony_ci 1116cb93a386Sopenharmony_ci using INHERITED = GrMeshDrawOp; 1117cb93a386Sopenharmony_ci}; 1118cb93a386Sopenharmony_ci 1119cb93a386Sopenharmony_ci} // anonymous namespace 1120cb93a386Sopenharmony_ci 1121cb93a386Sopenharmony_cinamespace skgpu::v1 { 1122cb93a386Sopenharmony_ci 1123cb93a386Sopenharmony_ci#if GR_TEST_UTILS 1124cb93a386Sopenharmony_ciuint32_t TextureOp::ClassID() { 1125cb93a386Sopenharmony_ci return TextureOpImpl::ClassID(); 1126cb93a386Sopenharmony_ci} 1127cb93a386Sopenharmony_ci#endif 1128cb93a386Sopenharmony_ci 1129cb93a386Sopenharmony_ciGrOp::Owner TextureOp::Make(GrRecordingContext* context, 1130cb93a386Sopenharmony_ci GrSurfaceProxyView proxyView, 1131cb93a386Sopenharmony_ci SkAlphaType alphaType, 1132cb93a386Sopenharmony_ci sk_sp<GrColorSpaceXform> textureXform, 1133cb93a386Sopenharmony_ci GrSamplerState::Filter filter, 1134cb93a386Sopenharmony_ci GrSamplerState::MipmapMode mm, 1135cb93a386Sopenharmony_ci const SkPMColor4f& color, 1136cb93a386Sopenharmony_ci Saturate saturate, 1137cb93a386Sopenharmony_ci SkBlendMode blendMode, 1138cb93a386Sopenharmony_ci GrAAType aaType, 1139cb93a386Sopenharmony_ci DrawQuad* quad, 1140cb93a386Sopenharmony_ci const SkRect* subset) { 1141cb93a386Sopenharmony_ci // Apply optimizations that are valid whether or not using TextureOp or FillRectOp 1142cb93a386Sopenharmony_ci if (subset && subset->contains(proxyView.proxy()->backingStoreBoundsRect())) { 1143cb93a386Sopenharmony_ci // No need for a shader-based subset if hardware clamping achieves the same effect 1144cb93a386Sopenharmony_ci subset = nullptr; 1145cb93a386Sopenharmony_ci } 1146cb93a386Sopenharmony_ci 1147cb93a386Sopenharmony_ci if (filter != GrSamplerState::Filter::kNearest || mm != GrSamplerState::MipmapMode::kNone) { 1148cb93a386Sopenharmony_ci auto [mustFilter, mustMM] = filter_and_mm_have_effect(quad->fLocal, quad->fDevice); 1149cb93a386Sopenharmony_ci if (!mustFilter) { 1150cb93a386Sopenharmony_ci filter = GrSamplerState::Filter::kNearest; 1151cb93a386Sopenharmony_ci } 1152cb93a386Sopenharmony_ci if (!mustMM) { 1153cb93a386Sopenharmony_ci mm = GrSamplerState::MipmapMode::kNone; 1154cb93a386Sopenharmony_ci } 1155cb93a386Sopenharmony_ci } 1156cb93a386Sopenharmony_ci 1157cb93a386Sopenharmony_ci if (blendMode == SkBlendMode::kSrcOver) { 1158cb93a386Sopenharmony_ci return TextureOpImpl::Make(context, std::move(proxyView), std::move(textureXform), filter, 1159cb93a386Sopenharmony_ci mm, color, saturate, aaType, std::move(quad), subset); 1160cb93a386Sopenharmony_ci } else { 1161cb93a386Sopenharmony_ci // Emulate complex blending using FillRectOp 1162cb93a386Sopenharmony_ci GrSamplerState samplerState(GrSamplerState::WrapMode::kClamp, filter, mm); 1163cb93a386Sopenharmony_ci GrPaint paint; 1164cb93a386Sopenharmony_ci paint.setColor4f(color); 1165cb93a386Sopenharmony_ci paint.setXPFactory(SkBlendMode_AsXPFactory(blendMode)); 1166cb93a386Sopenharmony_ci 1167cb93a386Sopenharmony_ci std::unique_ptr<GrFragmentProcessor> fp; 1168cb93a386Sopenharmony_ci const auto& caps = *context->priv().caps(); 1169cb93a386Sopenharmony_ci if (subset) { 1170cb93a386Sopenharmony_ci SkRect localRect; 1171cb93a386Sopenharmony_ci if (quad->fLocal.asRect(&localRect)) { 1172cb93a386Sopenharmony_ci fp = GrTextureEffect::MakeSubset(std::move(proxyView), alphaType, SkMatrix::I(), 1173cb93a386Sopenharmony_ci samplerState, *subset, localRect, caps); 1174cb93a386Sopenharmony_ci } else { 1175cb93a386Sopenharmony_ci fp = GrTextureEffect::MakeSubset(std::move(proxyView), alphaType, SkMatrix::I(), 1176cb93a386Sopenharmony_ci samplerState, *subset, caps); 1177cb93a386Sopenharmony_ci } 1178cb93a386Sopenharmony_ci } else { 1179cb93a386Sopenharmony_ci fp = GrTextureEffect::Make(std::move(proxyView), alphaType, SkMatrix::I(), samplerState, 1180cb93a386Sopenharmony_ci caps); 1181cb93a386Sopenharmony_ci } 1182cb93a386Sopenharmony_ci fp = GrColorSpaceXformEffect::Make(std::move(fp), std::move(textureXform)); 1183cb93a386Sopenharmony_ci fp = GrBlendFragmentProcessor::Make(std::move(fp), nullptr, SkBlendMode::kModulate); 1184cb93a386Sopenharmony_ci if (saturate == Saturate::kYes) { 1185cb93a386Sopenharmony_ci fp = GrFragmentProcessor::ClampOutput(std::move(fp)); 1186cb93a386Sopenharmony_ci } 1187cb93a386Sopenharmony_ci paint.setColorFragmentProcessor(std::move(fp)); 1188cb93a386Sopenharmony_ci return FillRectOp::Make(context, std::move(paint), aaType, quad); 1189cb93a386Sopenharmony_ci } 1190cb93a386Sopenharmony_ci} 1191cb93a386Sopenharmony_ci 1192cb93a386Sopenharmony_ci// A helper class that assists in breaking up bulk API quad draws into manageable chunks. 1193cb93a386Sopenharmony_ciclass TextureOp::BatchSizeLimiter { 1194cb93a386Sopenharmony_cipublic: 1195cb93a386Sopenharmony_ci BatchSizeLimiter(SurfaceDrawContext* sdc, 1196cb93a386Sopenharmony_ci const GrClip* clip, 1197cb93a386Sopenharmony_ci GrRecordingContext* rContext, 1198cb93a386Sopenharmony_ci int numEntries, 1199cb93a386Sopenharmony_ci GrSamplerState::Filter filter, 1200cb93a386Sopenharmony_ci GrSamplerState::MipmapMode mm, 1201cb93a386Sopenharmony_ci Saturate saturate, 1202cb93a386Sopenharmony_ci SkCanvas::SrcRectConstraint constraint, 1203cb93a386Sopenharmony_ci const SkMatrix& viewMatrix, 1204cb93a386Sopenharmony_ci sk_sp<GrColorSpaceXform> textureColorSpaceXform) 1205cb93a386Sopenharmony_ci : fSDC(sdc) 1206cb93a386Sopenharmony_ci , fClip(clip) 1207cb93a386Sopenharmony_ci , fContext(rContext) 1208cb93a386Sopenharmony_ci , fFilter(filter) 1209cb93a386Sopenharmony_ci , fMipmapMode(mm) 1210cb93a386Sopenharmony_ci , fSaturate(saturate) 1211cb93a386Sopenharmony_ci , fConstraint(constraint) 1212cb93a386Sopenharmony_ci , fViewMatrix(viewMatrix) 1213cb93a386Sopenharmony_ci , fTextureColorSpaceXform(textureColorSpaceXform) 1214cb93a386Sopenharmony_ci , fNumLeft(numEntries) {} 1215cb93a386Sopenharmony_ci 1216cb93a386Sopenharmony_ci void createOp(GrTextureSetEntry set[], int clumpSize, GrAAType aaType) { 1217cb93a386Sopenharmony_ci 1218cb93a386Sopenharmony_ci int clumpProxyCount = proxy_run_count(&set[fNumClumped], clumpSize); 1219cb93a386Sopenharmony_ci GrOp::Owner op = TextureOpImpl::Make(fContext, 1220cb93a386Sopenharmony_ci &set[fNumClumped], 1221cb93a386Sopenharmony_ci clumpSize, 1222cb93a386Sopenharmony_ci clumpProxyCount, 1223cb93a386Sopenharmony_ci fFilter, 1224cb93a386Sopenharmony_ci fMipmapMode, 1225cb93a386Sopenharmony_ci fSaturate, 1226cb93a386Sopenharmony_ci aaType, 1227cb93a386Sopenharmony_ci fConstraint, 1228cb93a386Sopenharmony_ci fViewMatrix, 1229cb93a386Sopenharmony_ci fTextureColorSpaceXform); 1230cb93a386Sopenharmony_ci fSDC->addDrawOp(fClip, std::move(op)); 1231cb93a386Sopenharmony_ci 1232cb93a386Sopenharmony_ci fNumLeft -= clumpSize; 1233cb93a386Sopenharmony_ci fNumClumped += clumpSize; 1234cb93a386Sopenharmony_ci } 1235cb93a386Sopenharmony_ci 1236cb93a386Sopenharmony_ci int numLeft() const { return fNumLeft; } 1237cb93a386Sopenharmony_ci int baseIndex() const { return fNumClumped; } 1238cb93a386Sopenharmony_ci 1239cb93a386Sopenharmony_ciprivate: 1240cb93a386Sopenharmony_ci SurfaceDrawContext* fSDC; 1241cb93a386Sopenharmony_ci const GrClip* fClip; 1242cb93a386Sopenharmony_ci GrRecordingContext* fContext; 1243cb93a386Sopenharmony_ci GrSamplerState::Filter fFilter; 1244cb93a386Sopenharmony_ci GrSamplerState::MipmapMode fMipmapMode; 1245cb93a386Sopenharmony_ci Saturate fSaturate; 1246cb93a386Sopenharmony_ci SkCanvas::SrcRectConstraint fConstraint; 1247cb93a386Sopenharmony_ci const SkMatrix& fViewMatrix; 1248cb93a386Sopenharmony_ci sk_sp<GrColorSpaceXform> fTextureColorSpaceXform; 1249cb93a386Sopenharmony_ci 1250cb93a386Sopenharmony_ci int fNumLeft; 1251cb93a386Sopenharmony_ci int fNumClumped = 0; // also the offset for the start of the next clump 1252cb93a386Sopenharmony_ci}; 1253cb93a386Sopenharmony_ci 1254cb93a386Sopenharmony_ci// Greedily clump quad draws together until the index buffer limit is exceeded. 1255cb93a386Sopenharmony_civoid TextureOp::AddTextureSetOps(SurfaceDrawContext* sdc, 1256cb93a386Sopenharmony_ci const GrClip* clip, 1257cb93a386Sopenharmony_ci GrRecordingContext* context, 1258cb93a386Sopenharmony_ci GrTextureSetEntry set[], 1259cb93a386Sopenharmony_ci int cnt, 1260cb93a386Sopenharmony_ci int proxyRunCnt, 1261cb93a386Sopenharmony_ci GrSamplerState::Filter filter, 1262cb93a386Sopenharmony_ci GrSamplerState::MipmapMode mm, 1263cb93a386Sopenharmony_ci Saturate saturate, 1264cb93a386Sopenharmony_ci SkBlendMode blendMode, 1265cb93a386Sopenharmony_ci GrAAType aaType, 1266cb93a386Sopenharmony_ci SkCanvas::SrcRectConstraint constraint, 1267cb93a386Sopenharmony_ci const SkMatrix& viewMatrix, 1268cb93a386Sopenharmony_ci sk_sp<GrColorSpaceXform> textureColorSpaceXform) { 1269cb93a386Sopenharmony_ci // Ensure that the index buffer limits are lower than the proxy and quad count limits of 1270cb93a386Sopenharmony_ci // the op's metadata so we don't need to worry about overflow. 1271cb93a386Sopenharmony_ci SkDEBUGCODE(TextureOpImpl::ValidateResourceLimits();) 1272cb93a386Sopenharmony_ci SkASSERT(proxy_run_count(set, cnt) == proxyRunCnt); 1273cb93a386Sopenharmony_ci 1274cb93a386Sopenharmony_ci // First check if we can support batches as a single op 1275cb93a386Sopenharmony_ci if (blendMode != SkBlendMode::kSrcOver || 1276cb93a386Sopenharmony_ci !context->priv().caps()->dynamicStateArrayGeometryProcessorTextureSupport()) { 1277cb93a386Sopenharmony_ci // Append each entry as its own op; these may still be GrTextureOps if the blend mode is 1278cb93a386Sopenharmony_ci // src-over but the backend doesn't support dynamic state changes. Otherwise Make() 1279cb93a386Sopenharmony_ci // automatically creates the appropriate FillRectOp to emulate TextureOp. 1280cb93a386Sopenharmony_ci SkMatrix ctm; 1281cb93a386Sopenharmony_ci for (int i = 0; i < cnt; ++i) { 1282cb93a386Sopenharmony_ci ctm = viewMatrix; 1283cb93a386Sopenharmony_ci if (set[i].fPreViewMatrix) { 1284cb93a386Sopenharmony_ci ctm.preConcat(*set[i].fPreViewMatrix); 1285cb93a386Sopenharmony_ci } 1286cb93a386Sopenharmony_ci 1287cb93a386Sopenharmony_ci DrawQuad quad; 1288cb93a386Sopenharmony_ci quad.fEdgeFlags = set[i].fAAFlags; 1289cb93a386Sopenharmony_ci if (set[i].fDstClipQuad) { 1290cb93a386Sopenharmony_ci quad.fDevice = GrQuad::MakeFromSkQuad(set[i].fDstClipQuad, ctm); 1291cb93a386Sopenharmony_ci 1292cb93a386Sopenharmony_ci SkPoint srcPts[4]; 1293cb93a386Sopenharmony_ci GrMapRectPoints(set[i].fDstRect, set[i].fSrcRect, set[i].fDstClipQuad, srcPts, 4); 1294cb93a386Sopenharmony_ci quad.fLocal = GrQuad::MakeFromSkQuad(srcPts, SkMatrix::I()); 1295cb93a386Sopenharmony_ci } else { 1296cb93a386Sopenharmony_ci quad.fDevice = GrQuad::MakeFromRect(set[i].fDstRect, ctm); 1297cb93a386Sopenharmony_ci quad.fLocal = GrQuad(set[i].fSrcRect); 1298cb93a386Sopenharmony_ci } 1299cb93a386Sopenharmony_ci 1300cb93a386Sopenharmony_ci const SkRect* subset = constraint == SkCanvas::kStrict_SrcRectConstraint 1301cb93a386Sopenharmony_ci ? &set[i].fSrcRect : nullptr; 1302cb93a386Sopenharmony_ci 1303cb93a386Sopenharmony_ci auto op = Make(context, set[i].fProxyView, set[i].fSrcAlphaType, textureColorSpaceXform, 1304cb93a386Sopenharmony_ci filter, mm, set[i].fColor, saturate, blendMode, aaType, &quad, subset); 1305cb93a386Sopenharmony_ci sdc->addDrawOp(clip, std::move(op)); 1306cb93a386Sopenharmony_ci } 1307cb93a386Sopenharmony_ci return; 1308cb93a386Sopenharmony_ci } 1309cb93a386Sopenharmony_ci 1310cb93a386Sopenharmony_ci // Second check if we can always just make a single op and avoid the extra iteration 1311cb93a386Sopenharmony_ci // needed to clump things together. 1312cb93a386Sopenharmony_ci if (cnt <= std::min(GrResourceProvider::MaxNumNonAAQuads(), 1313cb93a386Sopenharmony_ci GrResourceProvider::MaxNumAAQuads())) { 1314cb93a386Sopenharmony_ci auto op = TextureOpImpl::Make(context, set, cnt, proxyRunCnt, filter, mm, saturate, aaType, 1315cb93a386Sopenharmony_ci constraint, viewMatrix, std::move(textureColorSpaceXform)); 1316cb93a386Sopenharmony_ci sdc->addDrawOp(clip, std::move(op)); 1317cb93a386Sopenharmony_ci return; 1318cb93a386Sopenharmony_ci } 1319cb93a386Sopenharmony_ci 1320cb93a386Sopenharmony_ci BatchSizeLimiter state(sdc, clip, context, cnt, filter, mm, saturate, constraint, viewMatrix, 1321cb93a386Sopenharmony_ci std::move(textureColorSpaceXform)); 1322cb93a386Sopenharmony_ci 1323cb93a386Sopenharmony_ci // kNone and kMSAA never get altered 1324cb93a386Sopenharmony_ci if (aaType == GrAAType::kNone || aaType == GrAAType::kMSAA) { 1325cb93a386Sopenharmony_ci // Clump these into series of MaxNumNonAAQuads-sized GrTextureOps 1326cb93a386Sopenharmony_ci while (state.numLeft() > 0) { 1327cb93a386Sopenharmony_ci int clumpSize = std::min(state.numLeft(), GrResourceProvider::MaxNumNonAAQuads()); 1328cb93a386Sopenharmony_ci 1329cb93a386Sopenharmony_ci state.createOp(set, clumpSize, aaType); 1330cb93a386Sopenharmony_ci } 1331cb93a386Sopenharmony_ci } else { 1332cb93a386Sopenharmony_ci // kCoverage can be downgraded to kNone. Note that the following is conservative. kCoverage 1333cb93a386Sopenharmony_ci // can also get downgraded to kNone if all the quads are on integer coordinates and 1334cb93a386Sopenharmony_ci // axis-aligned. 1335cb93a386Sopenharmony_ci SkASSERT(aaType == GrAAType::kCoverage); 1336cb93a386Sopenharmony_ci 1337cb93a386Sopenharmony_ci while (state.numLeft() > 0) { 1338cb93a386Sopenharmony_ci GrAAType runningAA = GrAAType::kNone; 1339cb93a386Sopenharmony_ci bool clumped = false; 1340cb93a386Sopenharmony_ci 1341cb93a386Sopenharmony_ci for (int i = 0; i < state.numLeft(); ++i) { 1342cb93a386Sopenharmony_ci int absIndex = state.baseIndex() + i; 1343cb93a386Sopenharmony_ci 1344cb93a386Sopenharmony_ci if (set[absIndex].fAAFlags != GrQuadAAFlags::kNone || 1345cb93a386Sopenharmony_ci runningAA == GrAAType::kCoverage) { 1346cb93a386Sopenharmony_ci 1347cb93a386Sopenharmony_ci if (i >= GrResourceProvider::MaxNumAAQuads()) { 1348cb93a386Sopenharmony_ci // Here we either need to boost the AA type to kCoverage, but doing so with 1349cb93a386Sopenharmony_ci // all the accumulated quads would overflow, or we have a set of AA quads 1350cb93a386Sopenharmony_ci // that has just gotten too large. In either case, calve off the existing 1351cb93a386Sopenharmony_ci // quads as their own TextureOp. 1352cb93a386Sopenharmony_ci state.createOp( 1353cb93a386Sopenharmony_ci set, 1354cb93a386Sopenharmony_ci runningAA == GrAAType::kNone ? i : GrResourceProvider::MaxNumAAQuads(), 1355cb93a386Sopenharmony_ci runningAA); // maybe downgrading AA here 1356cb93a386Sopenharmony_ci clumped = true; 1357cb93a386Sopenharmony_ci break; 1358cb93a386Sopenharmony_ci } 1359cb93a386Sopenharmony_ci 1360cb93a386Sopenharmony_ci runningAA = GrAAType::kCoverage; 1361cb93a386Sopenharmony_ci } else if (runningAA == GrAAType::kNone) { 1362cb93a386Sopenharmony_ci 1363cb93a386Sopenharmony_ci if (i >= GrResourceProvider::MaxNumNonAAQuads()) { 1364cb93a386Sopenharmony_ci // Here we've found a consistent batch of non-AA quads that has gotten too 1365cb93a386Sopenharmony_ci // large. Calve it off as its own TextureOp. 1366cb93a386Sopenharmony_ci state.createOp(set, GrResourceProvider::MaxNumNonAAQuads(), 1367cb93a386Sopenharmony_ci GrAAType::kNone); // definitely downgrading AA here 1368cb93a386Sopenharmony_ci clumped = true; 1369cb93a386Sopenharmony_ci break; 1370cb93a386Sopenharmony_ci } 1371cb93a386Sopenharmony_ci } 1372cb93a386Sopenharmony_ci } 1373cb93a386Sopenharmony_ci 1374cb93a386Sopenharmony_ci if (!clumped) { 1375cb93a386Sopenharmony_ci // We ran through the above loop w/o hitting a limit. Spit out this last clump of 1376cb93a386Sopenharmony_ci // quads and call it a day. 1377cb93a386Sopenharmony_ci state.createOp(set, state.numLeft(), runningAA); // maybe downgrading AA here 1378cb93a386Sopenharmony_ci } 1379cb93a386Sopenharmony_ci } 1380cb93a386Sopenharmony_ci } 1381cb93a386Sopenharmony_ci} 1382cb93a386Sopenharmony_ci 1383cb93a386Sopenharmony_ci} // namespace skgpu::v1 1384cb93a386Sopenharmony_ci 1385cb93a386Sopenharmony_ci#if GR_TEST_UTILS 1386cb93a386Sopenharmony_ci#include "include/gpu/GrRecordingContext.h" 1387cb93a386Sopenharmony_ci#include "src/gpu/GrProxyProvider.h" 1388cb93a386Sopenharmony_ci#include "src/gpu/GrRecordingContextPriv.h" 1389cb93a386Sopenharmony_ci 1390cb93a386Sopenharmony_ciGR_DRAW_OP_TEST_DEFINE(TextureOpImpl) { 1391cb93a386Sopenharmony_ci SkISize dims; 1392cb93a386Sopenharmony_ci dims.fHeight = random->nextULessThan(90) + 10; 1393cb93a386Sopenharmony_ci dims.fWidth = random->nextULessThan(90) + 10; 1394cb93a386Sopenharmony_ci auto origin = random->nextBool() ? kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin; 1395cb93a386Sopenharmony_ci GrMipmapped mipMapped = random->nextBool() ? GrMipmapped::kYes : GrMipmapped::kNo; 1396cb93a386Sopenharmony_ci SkBackingFit fit = SkBackingFit::kExact; 1397cb93a386Sopenharmony_ci if (mipMapped == GrMipmapped::kNo) { 1398cb93a386Sopenharmony_ci fit = random->nextBool() ? SkBackingFit::kApprox : SkBackingFit::kExact; 1399cb93a386Sopenharmony_ci } 1400cb93a386Sopenharmony_ci const GrBackendFormat format = 1401cb93a386Sopenharmony_ci context->priv().caps()->getDefaultBackendFormat(GrColorType::kRGBA_8888, 1402cb93a386Sopenharmony_ci GrRenderable::kNo); 1403cb93a386Sopenharmony_ci GrProxyProvider* proxyProvider = context->priv().proxyProvider(); 1404cb93a386Sopenharmony_ci sk_sp<GrTextureProxy> proxy = proxyProvider->createProxy( 1405cb93a386Sopenharmony_ci format, dims, GrRenderable::kNo, 1, mipMapped, fit, SkBudgeted::kNo, GrProtected::kNo, 1406cb93a386Sopenharmony_ci GrInternalSurfaceFlags::kNone); 1407cb93a386Sopenharmony_ci 1408cb93a386Sopenharmony_ci SkRect rect = GrTest::TestRect(random); 1409cb93a386Sopenharmony_ci SkRect srcRect; 1410cb93a386Sopenharmony_ci srcRect.fLeft = random->nextRangeScalar(0.f, proxy->width() / 2.f); 1411cb93a386Sopenharmony_ci srcRect.fRight = random->nextRangeScalar(0.f, proxy->width()) + proxy->width() / 2.f; 1412cb93a386Sopenharmony_ci srcRect.fTop = random->nextRangeScalar(0.f, proxy->height() / 2.f); 1413cb93a386Sopenharmony_ci srcRect.fBottom = random->nextRangeScalar(0.f, proxy->height()) + proxy->height() / 2.f; 1414cb93a386Sopenharmony_ci SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random); 1415cb93a386Sopenharmony_ci SkPMColor4f color = SkPMColor4f::FromBytes_RGBA(SkColorToPremulGrColor(random->nextU())); 1416cb93a386Sopenharmony_ci GrSamplerState::Filter filter = (GrSamplerState::Filter)random->nextULessThan( 1417cb93a386Sopenharmony_ci static_cast<uint32_t>(GrSamplerState::Filter::kLast) + 1); 1418cb93a386Sopenharmony_ci GrSamplerState::MipmapMode mm = GrSamplerState::MipmapMode::kNone; 1419cb93a386Sopenharmony_ci if (mipMapped == GrMipmapped::kYes) { 1420cb93a386Sopenharmony_ci mm = (GrSamplerState::MipmapMode)random->nextULessThan( 1421cb93a386Sopenharmony_ci static_cast<uint32_t>(GrSamplerState::MipmapMode::kLast) + 1); 1422cb93a386Sopenharmony_ci } 1423cb93a386Sopenharmony_ci 1424cb93a386Sopenharmony_ci auto texXform = GrTest::TestColorXform(random); 1425cb93a386Sopenharmony_ci GrAAType aaType = GrAAType::kNone; 1426cb93a386Sopenharmony_ci if (random->nextBool()) { 1427cb93a386Sopenharmony_ci aaType = (numSamples > 1) ? GrAAType::kMSAA : GrAAType::kCoverage; 1428cb93a386Sopenharmony_ci } 1429cb93a386Sopenharmony_ci GrQuadAAFlags aaFlags = GrQuadAAFlags::kNone; 1430cb93a386Sopenharmony_ci aaFlags |= random->nextBool() ? GrQuadAAFlags::kLeft : GrQuadAAFlags::kNone; 1431cb93a386Sopenharmony_ci aaFlags |= random->nextBool() ? GrQuadAAFlags::kTop : GrQuadAAFlags::kNone; 1432cb93a386Sopenharmony_ci aaFlags |= random->nextBool() ? GrQuadAAFlags::kRight : GrQuadAAFlags::kNone; 1433cb93a386Sopenharmony_ci aaFlags |= random->nextBool() ? GrQuadAAFlags::kBottom : GrQuadAAFlags::kNone; 1434cb93a386Sopenharmony_ci bool useSubset = random->nextBool(); 1435cb93a386Sopenharmony_ci auto saturate = random->nextBool() ? skgpu::v1::TextureOp::Saturate::kYes 1436cb93a386Sopenharmony_ci : skgpu::v1::TextureOp::Saturate::kNo; 1437cb93a386Sopenharmony_ci GrSurfaceProxyView proxyView( 1438cb93a386Sopenharmony_ci std::move(proxy), origin, 1439cb93a386Sopenharmony_ci context->priv().caps()->getReadSwizzle(format, GrColorType::kRGBA_8888)); 1440cb93a386Sopenharmony_ci auto alphaType = static_cast<SkAlphaType>( 1441cb93a386Sopenharmony_ci random->nextRangeU(kUnknown_SkAlphaType + 1, kLastEnum_SkAlphaType)); 1442cb93a386Sopenharmony_ci 1443cb93a386Sopenharmony_ci DrawQuad quad = {GrQuad::MakeFromRect(rect, viewMatrix), GrQuad(srcRect), aaFlags}; 1444cb93a386Sopenharmony_ci return skgpu::v1::TextureOp::Make(context, std::move(proxyView), alphaType, 1445cb93a386Sopenharmony_ci std::move(texXform), filter, mm, color, saturate, 1446cb93a386Sopenharmony_ci SkBlendMode::kSrcOver, aaType, &quad, 1447cb93a386Sopenharmony_ci useSubset ? &srcRect : nullptr); 1448cb93a386Sopenharmony_ci} 1449cb93a386Sopenharmony_ci 1450cb93a386Sopenharmony_ci#endif // GR_TEST_UTILS 1451