cache_miss问题排查

SurfaceFlinger RenderEngine

现象如下:

加了skiatrace之后

可以看到,主要是在绘制startingwindow及其平滑圆角

cachemiss代码

https://source-u.dun.mi.com/opengrok-u/xref/missi_u_qcom/external/skia/src/gpu/ganesh/gl/builders/GrGLProgramBuilder.cpp?r=63975fe5b1984d413029ec2f331ec1d9505996e4#316

renderengine源码

void SkiaRenderEngine::drawLayersInternal(
701          const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
702          const DisplaySettings& display, const std::vector<LayerSettings>& layers,
703          const std::shared_ptr<ExternalTexture>& buffer, const bool /*useFramebufferCache*/,
704          base::unique_fd&& bufferFence) {
705      ATRACE_FORMAT("%s for %s", __func__, display.namePlusId.c_str());
799      for (const auto& layer : layers) {
800          ATRACE_FORMAT("DrawLayer: %s", layer.name.c_str());
801  
802          if (kPrintLayerSettings) {
803              logSettings(layer);
804          }
805  
806          sk_sp<SkImage> blurInput;
1123         if (layer.source.buffer.buffer) {
1124              ATRACE_NAME("DrawImage");
1125              validateInputBufferUsage(layer.source.buffer.buffer->getBuffer());
1126              const auto& item = layer.source.buffer;
1127              auto imageTextureRef = getOrCreateBackendTexture(item.buffer->getBuffer(), false);
1128  
1129              // if the layer's buffer has a fence, then we must must respect the fence prior to using
1130              // the buffer.
1131              if (layer.source.buffer.fence != nullptr) {
1132                  waitFence(grContext, layer.source.buffer.fence->get());
1133              }
1294  
1295          if (!roundRectClip.isEmpty()) {
1296              // MIUI MOD: SF_MIUIRoundedCorner_2
1297              // canvas->clipRRect(roundRectClip, true);
1298          #ifdef MI_SF_FEATURE
1299              if (mMiRenderEngine) {
1300                  mMiRenderEngine->clipSmoothCorner(canvas, roundRectClip, true);
1301              } else {
1302                  canvas->clipRRect(roundRectClip, true);
1303              }
1304          #else
1305              canvas->clipRRect(roundRectClip, true);
1306          #endif
1307              // END SF_MIUIRoundedCorner_2
1308          }
1351          if (needDrawRect) {
1352              if (!bounds.isRect()) {
1353                  if (mMiRenderEngine) {
1354                      mMiRenderEngine->drawSmoothCorner(canvas, &paint, bounds);
1355                  } else {
1356                      paint.setAntiAlias(true);
1357                      canvas->drawRRect(bounds, paint);
1358                  }
1359              } else {
1360                  canvas->drawRect(bounds.rect(), paint);
1361              }
1362          }
1372  // END SF_MIUIRenderEngine
1422      surfaceAutoSaveRestore.restore();
1423      mCapture->endCapture();
1424      {
1425          ATRACE_NAME("flush surface");
1426          LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface);
1427          activeSurface->flush();
1428      }
1429  
1430      auto drawFence = sp<Fence>::make(flushAndSubmit(grContext));
1431  
1432      if (ATRACE_ENABLED()) {
1433          static gui::FenceMonitor sMonitor("RE Completion");
1434          sMonitor.queueFence(drawFence);
1435      }
1436      resultPromise->set_value(std::move(drawFence));
1437  }

drawSmoothCorner是MiRenderEngine添加的方法,在如下change中

https://gerrit.pt.mioffice.cn/c/platform/frameworks/native/+/3205284

mMiuiSmoothCornerEnabled = property_get_bool("persist.sys.support_window_smoothcorner", false);
 
void MiRenderEngine::drawSmoothCorner(SkCanvas* canvas, SkPaint* paint, const SkRRect& bounds) {
    if (!getMiuiSmoothCornerEnabled() || isStretchCorners(bounds)) {
        // if(MiSurfaceFlingerStub::isDynamicSfLog()) {
        //     ATRACE_NAME("sf-drawRRect");
        // }
        paint->setAntiAlias(true);
        canvas->drawRRect(bounds, *paint);
        return;
    }
    if(paint->getStrokeWidth() != 0) {
        drawSmoothCornerStroke(canvas, paint, bounds);
        return;
    }
    // if(MiSurfaceFlingerStub::isDynamicSfLog()) {
    //     ATRACE_NAME("sf-drawSmoothCorner");
    // }
    sk_sp<SkShader> smoothCornerShader;
    sk_sp<SkShader> image = paint->refShader();
    if (!mSmoothCornerImage){
        mSmoothCornerImage = getSmoothCornerImage(canvas);
    }
    smoothCornerShader = makeSmoothCornerShader(image, bounds);
    paint->setShader(smoothCornerShader);
    canvas->drawRect(bounds.rect(), *paint);
    // if(MiSurfaceFlingerStub::isDynamicSfLog()) {
    //     ALOGD("MiRenderEngine::drawSmoothCorner bounds[%f %f %f %f], radius[%f]", bounds.rect().fLeft, bounds.rect().fTop,
    //         bounds.rect().fRight, bounds.rect().fBottom, bounds.radii(SkRRect::kUpperLeft_Corner).x());
    // }
}
 
sk_sp<SkShader> MiRenderEngine::makeSmoothCornerShader(sk_sp<SkShader> inputShader, const SkRRect& effectRegion) {
    SkRuntimeShaderBuilder smoothCornerBuilder(mSmoothCornerEffect);
    SkRect rect = effectRegion.rect();
    SkVector radius = effectRegion.radii(SkRRect::kUpperLeft_Corner) * SmoothCornerWeight;
    float smoothCornerLevel = 2.0 * (radius.x() > radius.y() ? radius.x() : radius.y());
    SkRect rectSrc = SkRect::Make(mSmoothCornerImage->dimensions());
    SkRect rectDst = SkRect::MakeWH(smoothCornerLevel, smoothCornerLevel);
    SkMatrix scale = SkMatrix::RectToRect(rectSrc, rectDst);
    auto maskShader = mSmoothCornerImage->makeShader(
                                    SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear), scale);
    SkRect newRect = SkRect::MakeLTRB(rect.fLeft, rect.fTop, (int)(rect.fRight + 1.0) >> 1 << 1, (int)(rect.fBottom + 1.0) >> 1 << 1);
 
    if (inputShader && maskShader) {
        smoothCornerBuilder.child("image") = inputShader;
        smoothCornerBuilder.child("maskShader") = maskShader;
        smoothCornerBuilder.uniform("cropCenter") = SkV2{(float)newRect.centerX(), (float)newRect.centerY()};
        float leftRectWidth = newRect.width() * 0.5;
        float leftRectHeight = newRect.height() * 0.5;
        float leftCenterRectWidth = (newRect.width() - smoothCornerLevel) * 0.5;
        float leftCenterRectHeight = (newRect.height() - smoothCornerLevel) * 0.5;
        smoothCornerBuilder.uniform("leftRect") = SkV2{leftRectWidth, leftRectHeight};
        smoothCornerBuilder.uniform("leftCenterRect") = SkV2{leftCenterRectWidth, leftCenterRectHeight};
        // if(MiSurfaceFlingerStub::isDynamicSfLog()) {
        //     ALOGD("MiRenderEngine::makeSmoothCornerShader rectSrc[%f %f %f %f] rectDst[%f %f %f %f] rect[%f %f %f %f]",
        //         rectSrc.fLeft, rectSrc.fTop, rectSrc.fRight, rectSrc.fBottom,
        //         rectDst.fLeft, rectDst.fTop, rectDst.fRight, rectDst.fBottom, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
        // }
    }
    return smoothCornerBuilder.makeShader();;
}
 
1958  sk_sp<SkShader> SkRuntimeShaderBuilder::makeShader(const SkMatrix* localMatrix) {
1959      return this->effect()->makeShader(this->uniforms(), this->children(), localMatrix);
1960  }
 
1737  sk_sp<SkShader> SkRuntimeEffect::makeShader(sk_sp<const SkData> uniforms,
1738                                              sk_sp<SkShader> childShaders[],
1739                                              size_t childCount,
1740                                              const SkMatrix* localMatrix) const {
1745      return this->makeShader(std::move(uniforms), SkSpan(children), localMatrix);
1746  }
1747  
1748  sk_sp<SkShader> SkRuntimeEffect::makeShader(sk_sp<const SkData> uniforms,
1749                                              SkSpan<ChildPtr> children,
1750                                              const SkMatrix* localMatrix) const {
1773      return SkLocalMatrixShader::MakeWrapped<SkRTShader>(localMatrix,
1774                                                          sk_ref_sp(this),
1775                                                          /*debugTrace=*/nullptr,
1776                                                          std::move(uniforms),
1777                                                          children);
1778  }
 
22      MakeWrapped(const SkMatrix* localMatrix, Args&&... args) {
27          return sk_make_sp<SkLocalMatrixShader>(sk_sp<SkShader>(std::move(t)), *localMatrix);
28      }
 
30      SkLocalMatrixShader(sk_sp<SkShader> wrapped, const SkMatrix& localMatrix)
31              : fLocalMatrix(localMatrix), fWrappedShader(std::move(wrapped)) {}
 

可以看到MiRenderEngine的修改有很多关于Shader的部分,频繁cachemiss考虑与平滑圆角功能有关。

功能开关 persist.sys.support_window_smoothcorner 关掉之后cachemiss不复现。

cachemiss分析

再看下cachemiss代码

未发生cachemiss时

发生时

未发生时未有shader_compile trace信息,该trace代码位置如下

 sk_sp<GrGLProgram> GrGLProgramBuilder::finalize(const GrGLPrecompiledProgram* precompiledProgram) {
207      TRACE_EVENT0("skia.shaders", TRACE_FUNC);
239      bool cached = fCached.get() != nullptr;
240      bool usedProgramBinaries = false;
246      std::string cached_sksl[kGrShaderTypeCount];
         //发生cachemiss precompiledProgram必然是false,cached也必定为为false,故cachemiss
247      if (precompiledProgram) {
252          usedProgramBinaries = true;
253      } else if (cached) {
             //没有该trace信息,cached定为false
254          TRACE_EVENT0_ALWAYS("skia.shaders", "cache_hit");
314      }
         //usedProgramBinaries为false触发cachemiss
315      if (!usedProgramBinaries) {
316          TRACE_EVENT0_ALWAYS("skia.shaders", "cache_miss");
317          // Either a cache miss, or we got something other than binaries from the cache
319          /*
320             Fragment Shader
321          */
322          if (glsl[kFragment_GrShaderType].empty()) {
323              // Don't have cached GLSL, need to compile SkSL->GLSL
324              if (fFS.fForceHighPrecision) {
325                  settings.fForceHighPrecision = true;
326              }
327              std::unique_ptr<SkSL::Program> fs = GrSkSLtoGLSL(this->gpu(),
328                                                               SkSL::ProgramKind::kFragment,
329                                                               *sksl[kFragment_GrShaderType],
330                                                               settings,
331                                                               &glsl[kFragment_GrShaderType],
332                                                               errorHandler);
333              if (!fs) {
334                  cleanup_program(fGpu, programID, shadersToDelete);
335                  return nullptr;
336              }
337              inputs = fs->fInputs;
338          }
339  
340          this->addInputVars(inputs);
341          if (!this->compileAndAttachShaders(glsl[kFragment_GrShaderType], programID,
342                                             GR_GL_FRAGMENT_SHADER, &shadersToDelete, errorHandler)) {
343              cleanup_program(fGpu, programID, shadersToDelete);
344              return nullptr;
345          }
346  
347          /*
348             Vertex Shader
349          */
350          if (glsl[kVertex_GrShaderType].empty()) {
351              // Don't have cached GLSL, need to compile SkSL->GLSL
352              std::unique_ptr<SkSL::Program> vs = GrSkSLtoGLSL(this->gpu(),
353                                                               SkSL::ProgramKind::kVertex,
354                                                               *sksl[kVertex_GrShaderType],
355                                                               settings,
356                                                               &glsl[kVertex_GrShaderType],
357                                                               errorHandler);
358              if (!vs) {
359                  cleanup_program(fGpu, programID, shadersToDelete);
360                  return nullptr;
361              }
362          }
363          if (!this->compileAndAttachShaders(glsl[kVertex_GrShaderType], programID,
364                                             GR_GL_VERTEX_SHADER, &shadersToDelete, errorHandler)) {
365              cleanup_program(fGpu, programID, shadersToDelete);
366              return nullptr;
367          }
368  
369          // This also binds vertex attribute locations.
370          this->computeCountsAndStrides(programID, geomProc, true);
371  
372          this->bindProgramResourceLocations(programID);
373  
374          {
375              TRACE_EVENT0_ALWAYS("skia.shaders", "driver_link_program");
376              GL_CALL(LinkProgram(programID));
377              if (!GrGLCheckLinkStatus(fGpu, programID, errorHandler, sksl, glsl)) {
378                  cleanup_program(fGpu, programID, shadersToDelete);
379                  return nullptr;
380              }
381          }
382      }
400      return this->createProgram(programID);
401  }
 
50  sk_sp<GrGLProgram> GrGLProgramBuilder::CreateProgram(
51                                                 GrDirectContext* dContext,
52                                                 const GrProgramDesc& desc,
53                                                 const GrProgramInfo& programInfo,
54                                                 const GrGLPrecompiledProgram* precompiledProgram) {
55      TRACE_EVENT0_ALWAYS("skia.shaders", "shader_compile");
56      GrAutoLocaleSetter als("C");
57  
58      GrGLGpu* glGpu = static_cast<GrGLGpu*>(dContext->priv().getGpu());
59  
60      // create a builder.  This will be handed off to effects so they can use it to add
61      // uniforms, varyings, textures, etc
62      GrGLProgramBuilder builder(glGpu, desc, programInfo);
63  
64      auto persistentCache = dContext->priv().getPersistentCache();
65      if (persistentCache && !precompiledProgram) {
66          sk_sp<SkData> key = SkData::MakeWithoutCopy(desc.asKey(), desc.keyLength());
            fCached没get到
67          builder.fCached = persistentCache->load(*key);
68          // the eventual end goal is to completely skip emitAndInstallProcs on a cache hit, but it's
69          // doing necessary setup in addition to generating the SkSL code. Currently we are only able
70          // to skip the SkSL->GLSL step on a cache hit.
71      }
72      if (!builder.emitAndInstallProcs()) {
73          return nullptr;
74      }
75      return builder.finalize(precompiledProgram);
76  }
 
 
109      GrContextOptions::PersistentCache* getPersistentCache() {
110          return this->context()->fPersistentCache;
111      }
 

fPersistentCache load不到

sf中的定义在SkSLCacheMonitor,看他的load函数

  sk_sp<SkData> SkiaRenderEngine::SkSLCacheMonitor::load(const SkData& key) {
258      // This "cache" does not actually cache anything. It just allows us to
259      // monitor Skia's internal cache. So this method always returns null.
260      return nullptr;
261  }

考虑与SkSLCacheMonitor::load返回空有关,根本没有进行缓存!接下来加trace验证猜测

确为无缓存导致

void SkiaRenderEngine::SkSLCacheMonitor::store(const SkData& key, const SkData& data,
264                                                 const SkString& description) {
265      mShadersCachedSinceLastCall++;
266      mTotalShadersCompiled++;
267      ATRACE_FORMAT("SF cache: %i shaders", mTotalShadersCompiled);
268  }
 
每次都有store,但是返回为null,缓存未生效

shader_compile时机

findOrCreateProgramImpl过程中如果!(*entry)fProgram会CreateProgram即shader_compile

sk_sp<GrGLProgram> GrGLProgramBuilder::CreateProgram(
51                                                 GrDirectContext* dContext,
52                                                 const GrProgramDesc& desc,
53                                                 const GrProgramInfo& programInfo,
54                                                 const GrGLPrecompiledProgram* precompiledProgram) {
55      TRACE_EVENT0_ALWAYS("skia.shaders", "shader_compile");
76  }
 
//每个op都会进来
86  sk_sp<GrGLProgram> GrGLGpu::ProgramCache::findOrCreateProgramImpl(GrDirectContext* dContext,
87                                                                    const GrProgramDesc& desc,
88                                                                    const GrProgramInfo& programInfo,
89                                                                    Stats::ProgramCacheResult* stat) {
90      *stat = Stats::ProgramCacheResult::kHit;
        //根据GrProgramDesc找entry,考虑加trace验证是否为新的desc
91      std::unique_ptr<Entry>* entry = fMap.find(desc);
        // !(*entry)->fProgram 会走 CreateProgram
92      if (entry && !(*entry)->fProgram) {
96          (*entry)->fProgram = GrGLProgramBuilder::CreateProgram(dContext, desc, programInfo,
97                                                                 precompiledProgram);
106      } else if (!entry) {
107          // We have a cache miss
108          sk_sp<GrGLProgram> program = GrGLProgramBuilder::CreateProgram(dContext, desc, programInfo);
116      }
117      
118      return (*entry)->fProgram;
119  }

那么问题的关键在于**(*entry)fProgram**为啥是空,找不到对应的图形处理程序

打印下key及fMap size:

前面的帧是相同的key但是获取到了,cachemiss这帧size没变,但是get不到,非常奇怪

49      V* find(const K& key) {
50          Entry** value = fMap.find(key);
51          if (!value) {
52              return nullptr;
53          }
54          Entry* entry = *value;
55          if (entry != fLRU.head()) {
56              fLRU.remove(entry);
57              fLRU.addToHead(entry);
58          } // else it's already at head position, don't need to do anything
59          return &entry->fValue;
60      }

hash不一样,所以get不到,再看看desc从哪来的

sk_sp<GrGLProgram> GrGLGpu::ProgramCache::findOrCreateProgram(GrDirectContext* dContext,
73                                                                const GrProgramDesc& desc,
74                                                                const GrProgramInfo& programInfo,
75                                                                Stats::ProgramCacheResult* stat) {
76      sk_sp<GrGLProgram> tmp = this->findOrCreateProgramImpl(dContext, desc, programInfo, stat);
77      if (!tmp) {
78          fStats.incNumPreCompilationFailures();
79      } else {
80          fStats.incNumPreProgramCacheResult(*stat);
81      }
82  
83      return tmp;
84  }
 
bool GrGLGpu::compile(const GrProgramDesc& desc, const GrProgramInfo& programInfo) {
    GrThreadSafePipelineBuilder::Stats::ProgramCacheResult stat;
 
    sk_sp<GrGLProgram> tmp = fProgramCache->findOrCreateProgram(this->getContext(),
                                                                desc, programInfo, &stat);
    if (!tmp) {
        return false;
    }
 
    return stat != GrThreadSafePipelineBuilder::Stats::ProgramCacheResult::kHit;
}
 
bool SkDeferredDisplayList::ProgramIterator::compile() {
    if (!fDContext || fIndex < 0 || fIndex >= (int) fProgramData.size()) {
        return false;
    }
 
    return fDContext->priv().compile(fProgramData[fIndex].desc(), fProgramData[fIndex].info());
}
 
//打印trace发现是这个流程
51  sk_sp<GrGLProgram> GrGLGpu::ProgramCache::findOrCreateProgram(GrDirectContext* dContext,
52                                                                const GrProgramInfo& programInfo) {
53      const GrCaps* caps = dContext->priv().caps();
54  
        //新建desc
55      GrProgramDesc desc = caps->makeDesc(/*renderTarget*/nullptr, programInfo);
62      sk_sp<GrGLProgram> tmp = this->findOrCreateProgramImpl(dContext, desc, programInfo, &stat);
69      return tmp;
70  }
 
2006  bool GrGLGpu::flushGLState(GrRenderTarget* renderTarget, bool useMultisampleFBO,
2007                             const GrProgramInfo& programInfo) {
2008      this->handleDirtyContext();
2009  
2010      sk_sp<GrGLProgram> program = fProgramCache->findOrCreateProgram(this->getContext(),
2011                                                                      programInfo);
2045  }
 
bool GrGLOpsRenderPass::onBindPipeline(const GrProgramInfo& programInfo,
                                       const SkRect& drawBounds) {
    fPrimitiveType = programInfo.primitiveType();
    return fGpu->flushGLState(fRenderTarget, fUseMultisampleFBO, programInfo);
}

每次都是重新make,估计是hash值计算逻辑导致的

external/skia/src/gpu/gl/GrGLGpuProgramCache.cpp
sk_sp<GrGLProgram> GrGLGpu::ProgramCache::findOrCreateProgramImpl(GrDirectContext* dContext,
                                                                  const GrProgramDesc& desc,
                                                                  const GrProgramInfo& programInfo,
                                                                  Stats::ProgramCacheResult* stat) {
    *stat = Stats::ProgramCacheResult::kHit;
    std::unique_ptr<Entry>* entry = fMap.find(desc);
}
 
class GrProgramDesc {
    // Returns this as a uint32_t array to be used as a key in the program cache.
    const uint32_t* asKey() const {return fKey.data();}
    bool operator== (const GrProgramDesc& that) const {return this->fKey == that.fKey;}
}
 
SkLRUCache<GrProgramDesc, std::unique_ptr<Entry>, DescHash> fMap;
 
external/skia/src/core/SkLRUCache.h
template <typename K, typename V, typename HashK = SkGoodHash>
V* find(const K& key) {
    Entry** value = fMap.find(key);
    if (!value) {
        return nullptr;
    }
    Entry* entry = *value;
    if (entry != fLRU.head()) {
        fLRU.remove(entry);
        fLRU.addToHead(entry);
    } // else it's already at head position, don't need to do anything
    return &entry->fValue;
}
 
SkTHashTable<Entry*, K, Traits> fMap;
 
struct Traits {
    static uint32_t Hash(const K& k) {
        return HashK()(k);
    }
};
 
external/skia/include/private/SkTHash.h
T* find(const K& key) const {
    uint32_t hash = Hash(key);
    int index = hash & (fCapacity-1);
    for (int n = 0; n < fCapacity; n++) {
        Slot& s = fSlots[index];
        if (s.empty()) {
            return nullptr;
        }
        if (hash == s.hash && key == Traits::GetKey(*s)) {
            return &*s;
        }
        index = this->next(index);
    }
    SkASSERT(fCapacity == 0);
    return nullptr;
}
 
struct DescHash {
    uint32_t operator()(CacheKey& desc) const {
        return SkOpts::hash_fn(desc.asKey(), desc.keyLength(), 0);
    }
};

打印了以string打印desc的参数如表cachemiss参数 ,发生cachemiss的帧fpSamplerKey发生变化,可见参数变化会导致hash变化重新编译。下面看下shader参数与desc参数的关系。

shader参数与GrProgramDesc关系

GrProgramDesc的key其实就是skstring转的二进制数据

SkString GrProgramDesc::Describe(const GrProgramInfo& programInfo,
186                                   const GrCaps& caps) {
187      GrProgramDesc desc;
188      skgpu::StringKeyBuilder b(desc.key());
189      gen_key(&b, programInfo, caps);
190      b.flush();
191      return b.description();
192  }

getSmoothCornerShader

getSmoothCornerShader返回的也是skstring

const SkString MiShaderUtil::getSmoothCornerShader() {
    return SkString(R"(
        uniform shader maskShader;
        uniform shader image;
        uniform vec2 cropCenter;        // Coordinates of the center point of the rectangle
        uniform vec2 leftRect;          // Size of the top left quarter rectangle (w, h)
        uniform vec2 leftCenterRect;    // The side length of the upper left 1/4 center rectangle
                                        // which is used to determine whether it is in the range of corners.
        half4 main(vec2 xy) {
            vec4 color = image.eval(xy);
 
            // Determine whether the current coordinates are within the region
            vec2 position = abs(xy - cropCenter);
            if (any(lessThan(position, leftCenterRect))) {
                //不在范围内
                return color;
            }
 
            // Calculate the coordinates of the current coordinate
            // relative to the upper left corner, and then directly sample.
            // The four corners are symmetrical.
            float radius = leftRect.x - leftCenterRect.x;
            vec2 uv = (leftRect - position) / radius;
 
            uv *= 512.0;
 
            vec4 mask = maskShader.eval(uv);
 
            //透明
            color *= mask.a;
            return color;
        }
    )");
}
 
54  void MiRenderEngine::setSmoothCorner() {
55      const SkString smoothCornerString = MiShaderUtil::getSmoothCornerShader();
56      auto [smoothCornerEffect, smoothCornerError] = SkRuntimeEffect::MakeForShader(smoothCornerString);
57      if (!smoothCornerEffect) {
58          LOG_ALWAYS_FATAL("RuntimeShader error: %s", smoothCornerError.c_str());
59      }
60      mSmoothCornerEffect = std::move(smoothCornerEffect);
61  }

缓存change代码如下:

static void drawImageLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
                            const std::shared_ptr<ExternalTexture>& dstTexture,
                            const std::shared_ptr<ExternalTexture>& srcTexture) {
    const Rect& displayRect = display.physicalDisplay;
    FloatRect rect(0, 0, displayRect.width(), displayRect.height());
    LayerSettings layer{
            .geometry =
                    Geometry{
                            // The position transform doesn't matter when the reduced shader mode
                            // in in effect. A matrix transform stage is always included.
                            .positionTransform = mat4(),
                            .boundaries = rect,
                            .roundedCornersCrop = rect,
                    },
            .source = PixelSource{.buffer =
                                          Buffer{
                                                  .buffer = srcTexture,
                                                  .maxLuminanceNits = 1000.f,
                                          }},
    };
 
    std::vector<float> roundedCornersRadiuss = {0.0f, 10.0f, 50.0f};
    if(kSize) {
        roundedCornersRadiuss.push_back(65.0f);
    }
 
    for (auto dataspace : {kDestDataSpace, kOtherDataSpace}) {
        layer.sourceDataspace = dataspace;
        // Cache shaders for both rects and round rects.
        // In reduced shader mode, all non-zero round rect radii get the same code path.
        // MIUI MOD: SF_MIUIRoundedCorner_2
        // for (float roundedCornersRadius : {0.0f, 50.0f}) {
        for (float roundedCornersRadius : {0.0f, 10.0f, 50.0f, 65.0f}) {
            // END SF_MIUIRoundedCorner_2
            // roundedCornersCrop is always set, but the radius triggers the behavior
            layer.geometry.roundedCornersRadius = {roundedCornersRadius, roundedCornersRadius};
            for (bool isOpaque : {true, false}) {
                layer.source.buffer.isOpaque = isOpaque;
                for (auto alpha : {half(.2f), half(1.0f)}) {
                    layer.alpha = alpha;
                    auto layers = std::vector<LayerSettings>{layer};
                    renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
                                             base::unique_fd());
                }
            }
        }
    }
}

主要在于dataspace、roundedCornersRadius、isOpaque、alpha四个参数的排列组合

再回看绘帧流程renderengine里面的这四个layer参数怎么确定的

GrProgramDesc

sk_sp<GrGLProgram> GrGLGpu::ProgramCache::findOrCreateProgram(GrDirectContext* dContext,
                                                              const GrProgramInfo& programInfo) {
    const GrCaps* caps = dContext->priv().caps();
 
    GrProgramDesc desc = caps->makeDesc(/*renderTarget*/nullptr, programInfo);
    if (!desc.isValid()) {
        GrCapsDebugf(caps, "Failed to gl program descriptor!\n");
        return nullptr;
    }
 
    Stats::ProgramCacheResult stat;
    sk_sp<GrGLProgram> tmp = this->findOrCreateProgramImpl(dContext, desc, programInfo, &stat);
    if (!tmp) {
        fStats.incNumInlineCompilationFailures();
    } else {
        fStats.incNumInlineProgramCacheResult(stat);
    }
 
    return tmp;
}
 
bool GrGLOpsRenderPass::onBindPipeline(const GrProgramInfo& programInfo,
                                       const SkRect& drawBounds) {
    fPrimitiveType = programInfo.primitiveType();
    return fGpu->flushGLState(fRenderTarget, fUseMultisampleFBO, programInfo);
}
 
void GrOpsRenderPass::bindPipeline(const GrProgramInfo& programInfo, const SkRect& drawBounds) {
91      if (programInfo.geomProc().numVertexAttributes() > this->gpu()->caps()->maxVertexAttributes()) {
92          fDrawPipelineStatus = DrawPipelineStatus::kFailedToBind;
93          return;
94      }
95  
96      if (!this->onBindPipeline(programInfo, drawBounds)) {
97          fDrawPipelineStatus = DrawPipelineStatus::kFailedToBind;
98          return;
99      }
 
class FillRectOpImpl final : public GrMeshDrawOp {
300      void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
311          if (!fProgramInfo) {
312              this->createProgramInfo(flushState);
313          }
314  
315          const int totalNumVertices = fQuads.count() * vertexSpec.verticesPerQuad();
316  
317          flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
318          flushState->bindBuffers(std::move(fIndexBuffer), nullptr, std::move(fVertexBuffer));
319          flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
320          skgpu::v1::QuadPerEdgeAA::IssueDraw(flushState->caps(), flushState->opsRenderPass(),
321                                              vertexSpec, 0, fQuads.count(), totalNumVertices,
322                                              fBaseVertex);
323      }
 
 
19  void GrMeshDrawOp::createProgramInfo(GrMeshDrawTarget* target) {
20      this->createProgramInfo(&target->caps(),
21                              target->allocator(),
22                              target->writeView(),
23                              target->usesMSAASurface(),
24                              target->detachAppliedClip(),
25                              target->dstProxyView(),
26                              target->renderPassBarriers(),
27                              target->colorLoadOp());
28  }
 
15  GrProgramInfo::GrProgramInfo(const GrCaps& caps,
16                               const GrSurfaceProxyView& targetView,
17                               bool usesMSAASurface,
18                               const GrPipeline* pipeline,
19                               const GrUserStencilSettings* userStencilSettings,
20                               const GrGeometryProcessor* geomProc,
21                               GrPrimitiveType primitiveType,
22                               GrXferBarrierFlags renderPassXferBarriers,
23                               GrLoadOp colorLoadOp)
24          : fNeedsStencil(targetView.asRenderTargetProxy()->needsStencil())
25          , fBackendFormat(targetView.proxy()->backendFormat())
26          , fOrigin(targetView.origin())
27          , fTargetHasVkResolveAttachmentWithInput(
28                    targetView.asRenderTargetProxy()->supportsVkInputAttachment() &&
29                    ((targetView.asRenderTargetProxy()->numSamples() > 1 &&
30                      targetView.asTextureProxy()) ||
31                     targetView.asRenderTargetProxy()->numSamples() == 1))
32          , fTargetsNumSamples(targetView.asRenderTargetProxy()->numSamples())
33          , fPipeline(pipeline)
34          , fUserStencilSettings(userStencilSettings)
35          , fGeomProc(geomProc)
36          , fPrimitiveType(primitiveType)
37          , fRenderPassXferBarriers(renderPassXferBarriers)
38          , fColorLoadOp(colorLoadOp) {
39      SkASSERT(fTargetsNumSamples > 0);
40      fNumSamples = fTargetsNumSamples;
41      if (fNumSamples == 1 && usesMSAASurface) {
42          fNumSamples = caps.internalMultisampleCount(this->backendFormat());
43      }
44      SkDEBUGCODE(this->validate(false);)
45  }
 
176  void GrProgramDesc::Build(GrProgramDesc* desc,
177                            const GrProgramInfo& programInfo,
178                            const GrCaps& caps) {
179      desc->reset();
180      skgpu::KeyBuilder b(desc->key());
181      gen_key(&b, programInfo, caps);
182      desc->fInitialKeyLength = desc->keyLength();
183  }
 

key生成

GrFragmentProcessor图形碎片处理器
151  static void gen_key(skgpu::KeyBuilder* b,
152                      const GrProgramInfo& programInfo,
153                      const GrCaps& caps) {
154      gen_geomproc_key(programInfo.geomProc(), caps, b);
155  
156      const GrPipeline& pipeline = programInfo.pipeline();
         //图型片段个数
157      b->addBits(2, pipeline.numFragmentProcessors(),      "numFPs");
158      b->addBits(1, pipeline.numColorFragmentProcessors(), "numColorFPs");
159      for (int i = 0; i < pipeline.numFragmentProcessors(); ++i) {
             //逐片段生成key
160          gen_fp_key(pipeline.getFragmentProcessor(i), caps, b);
161      }
162  
163      gen_xp_key(pipeline.getXferProcessor(), caps, pipeline, b);
164  
165      b->addBits(16, pipeline.writeSwizzle().asKey(), "writeSwizzle");
166      b->addBool(pipeline.snapVerticesToPixelCenters(), "snapVertices");
167      // The base descriptor only stores whether or not the primitiveType is kPoints. Backend-
168      // specific versions (e.g., Vulkan) require more detail
169      b->addBool((programInfo.primitiveType() == GrPrimitiveType::kPoints), "isPoints");
170  
171      // Put a clean break between the "common" data written by this function, and any backend data
172      // appended later. The initial key length will just be this portion (rounded to 4 bytes).
173      b->flush();
174  }
 
这里也出现了上面抓trace看到的fpSamplerKey,以他为视角进一步分析
122  static void gen_fp_key(const GrFragmentProcessor& fp,
123                         const GrCaps& caps,
124                         skgpu::KeyBuilder* b) {
125      b->appendComment(fp.name());
126      b->addBits(kClassIDBits, fp.classID(), "fpClassID");
127      b->addBits(GrGeometryProcessor::kCoordTransformKeyBits,
128                 GrGeometryProcessor::ComputeCoordTransformsKey(fp), "fpTransforms");
129  
130      if (auto* te = fp.asTextureEffect()) {
131          const GrBackendFormat& backendFormat = te->view().proxy()->backendFormat();
132          uint32_t samplerKey = sampler_key(backendFormat.textureType(), te->view().swizzle(), caps);
133          b->add32(samplerKey, "fpSamplerKey");
134          caps.addExtraSamplerKey(b, te->samplerState(), backendFormat);
135      }
136  
137      fp.addToKey(*caps.shaderCaps(), b);
138      b->add32(fp.numChildProcessors(), "fpNumChildren");
139  
140      for (int i = 0; i < fp.numChildProcessors(); ++i) {
141          if (auto child = fp.childProcessor(i)) {
142              gen_fp_key(*child, caps, b);
143          } else {
144              // Fold in a sentinel value as the "class ID" for any null children
145              b->appendComment("Null");
146              b->addBits(kClassIDBits, GrProcessor::ClassID::kNull_ClassID, "fpClassID");
147          }
148      }
149  }
 
50  static uint32_t sampler_key(GrTextureType textureType, const skgpu::Swizzle& swizzle,
51                              const GrCaps& caps) {
52      int samplerTypeKey = texture_type_key(textureType);
53  
54      static_assert(2 == sizeof(swizzle.asKey()));
55      uint16_t swizzleKey = swizzle.asKey();
        //后几位是samplerTypeKey,前几位是swizzleKey
56      return SkToU32(samplerTypeKey | swizzleKey << kSamplerOrImageTypeKeyBits);
57  }

fpSamplerKey: 205057 fpSamplerKey: 205057 fpSamplerKey: 336129

swizzleKey发生了变化,Swizzle表示图像颜色格式

 /** Represents a rgba swizzle. It can be converted either into a string or a eight bit int. */
20  class Swizzle {
constexpr uint16_t asKey() const { return fKey; }
 
59      static constexpr Swizzle RGBA() { return Swizzle("rgba"); }
60      static constexpr Swizzle BGRA() { return Swizzle("bgra"); }
61      static constexpr Swizzle RRRA() { return Swizzle("rrra"); }
62      static constexpr Swizzle RGB1() { return Swizzle("rgb1"); }
 
78  constexpr Swizzle::Swizzle(const char c[4])
79          : fKey(static_cast<uint16_t>((CToI(c[0]) << 0) | (CToI(c[1]) << 4) | (CToI(c[2]) << 8) |
80                                       (CToI(c[3]) << 12))) {}

这一帧即为颜色格式发生变化,图像颜色变化为啥可以通过增加半径参数解决??

fpSamplerKey 是 Skia 图形库中的一个结构体,用于表示片段处理器(Fragment Processor)的采样器关键字。

在 Skia 中,片段处理器是用于处理图形渲染管道中每个像素的可编程处理器。片段处理器可以通过采样器来获取纹理或其他输入数据,并对其进行处理。fpSamplerKey 结构体用于描述片段处理器的采样器,以便在渲染过程中正确地设置和匹配纹理。

fpSamplerKey 结构体包含以下字段:

backendFormat:表示纹理的像素格式,用于指定纹理数据的存储方式。

isAlphaUsed:标志位,指示是否在采样器中使用了透明度。

usesBorder:标志位,指示采样器是否使用了边界模式(Border Mode)。

preservesOpaqueInput:标志位,指示采样器是否保留了不透明度。

流程分析

drawSmoothCorner

接着从drawSmoothCorner

void MiRenderEngine::drawSmoothCorner(SkCanvas* canvas, SkPaint* paint, const SkRRect& bounds) {
    if (!getMiuiSmoothCornerEnabled() || isStretchCorners(bounds)) {
        // if(MiSurfaceFlingerStub::isDynamicSfLog()) {
        //     ATRACE_NAME("sf-drawRRect");
        // }
        paint->setAntiAlias(true);
        canvas->drawRRect(bounds, *paint);
        return;
    }
    if(paint->getStrokeWidth() != 0) {
        drawSmoothCornerStroke(canvas, paint, bounds);
        return;
    }
    // if(MiSurfaceFlingerStub::isDynamicSfLog()) {
    //     ATRACE_NAME("sf-drawSmoothCorner");
    // }
    sk_sp<SkShader> smoothCornerShader;
    sk_sp<SkShader> image = paint->refShader();
    if (!mSmoothCornerImage){
        mSmoothCornerImage = getSmoothCornerImage(canvas);
    }
    smoothCornerShader = makeSmoothCornerShader(image, bounds);
    paint->setShader(smoothCornerShader);
    //在这里添加drawop,shader信息在paint里
    canvas->drawRect(bounds.rect(), *paint);
    // if(MiSurfaceFlingerStub::isDynamicSfLog()) {
    //     ALOGD("MiRenderEngine::drawSmoothCorner bounds[%f %f %f %f], radius[%f]", bounds.rect().fLeft, bounds.rect().fTop,
    //         bounds.rect().fRight, bounds.rect().fBottom, bounds.radii(SkRRect::kUpperLeft_Corner).x());
    // }
}
 
1862  void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1863      TRACE_EVENT0("skia", TRACE_FUNC);
1864      // To avoid redundant logic in our culling code and various backends, we always sort rects
1865      // before passing them along.
1866      this->onDrawRect(r.makeSorted(), paint);
1867  }
 
2111  void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
2112      SkASSERT(r.isSorted());
2117      auto layer = this->aboutToDraw(this, paint, &r, CheckForOverwrite::kYes);
2118      if (layer) {
2119          this->topDevice()->drawRect(r, layer->paint());
2120      }
2121  }
 
548  void Device::drawRect(const SkRect& rect, const SkPaint& paint) {
549      ASSERT_SINGLE_OWNER
550      GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::v1::Device", "drawRect", fContext.get());
551  
552      GrStyle style(paint);
562  
563      GrPaint grPaint;
564      if (!SkPaintToGrPaint(this->recordingContext(),
565                            fSurfaceDrawContext->colorInfo(),
566                            paint,
567                            this->localToDevice(),
568                            fSurfaceDrawContext->surfaceProps(),
569                            &grPaint)) {
570          return;
571      }
572  
573      fSurfaceDrawContext->drawRect(this->clip(), std::move(grPaint),
574                                    fSurfaceDrawContext->chooseAA(paint), this->localToDevice(), rect,
575                                    &style);
576  }
 
667  void SurfaceDrawContext::drawRect(const GrClip* clip,
668                                    GrPaint&& paint,
669                                    GrAA aa,
670                                    const SkMatrix& viewMatrix,
671                                    const SkRect& rect,
672                                    const GrStyle* style) {
679      GR_CREATE_TRACE_MARKER_CONTEXT("SurfaceDrawContext", "drawRect", fContext);
680  
685  
686      const SkStrokeRec& stroke = style->strokeRec();
687      if (stroke.getStyle() == SkStrokeRec::kFill_Style) {
688          // Fills the rect, using rect as its own local coordinates
689          this->fillRectToRect(clip, std::move(paint), aa, viewMatrix, rect, rect);
690          return;
691      }
718  }
719  
720  void SurfaceDrawContext::fillRectToRect(const GrClip* clip,
721                                          GrPaint&& paint,
722                                          GrAA aa,
723                                          const SkMatrix& viewMatrix,
724                                          const SkRect& rectToDraw,
725                                          const SkRect& localRect) {
773      this->drawFilledQuad(clip, std::move(paint), &quad);
774  }
 
546  void SurfaceDrawContext::drawFilledQuad(const GrClip* clip,
547                                          GrPaint&& paint,
548                                          DrawQuad* quad,
549                                          const GrUserStencilSettings* ss) {
553      GR_CREATE_TRACE_MARKER_CONTEXT("SurfaceDrawContext", "drawFilledQuad", fContext);
554  
555      AutoCheckFlush acf(this->drawingManager());
556  
557      QuadOptimization opt = this->attemptQuadOptimization(clip, ss, quad, &paint);
558      if (opt >= QuadOptimization::kClipApplied) {
574          this->addDrawOp(finalClip, FillRectOp::Make(fContext, std::move(paint), aaType,
575                                                      quad, ss));
576      }
577      // All other optimization levels were completely handled inside attempt(), so no extra op needed
578  }
 
67      static GrOp::Owner Make(GrRecordingContext* context,
68                              GrPaint&& paint,
69                              GrAAType aaType,
70                              DrawQuad* quad,
71                              const GrUserStencilSettings* stencilSettings,
72                              Helper::InputFlags inputFlags) {
73          // Clean up deviations between aaType and edgeAA
74          GrQuadUtils::ResolveAAType(aaType, quad->fEdgeFlags, quad->fDevice,
75                                     &aaType, &quad->fEdgeFlags);
76          return Helper::FactoryHelper<FillRectOpImpl>(context, std::move(paint), aaType, quad,
77                                                       stencilSettings, inputFlags);
78      }
 
207  template <typename Op, typename... OpArgs>
208  GrOp::Owner GrSimpleMeshDrawOpHelper::FactoryHelper(GrRecordingContext* context,
209                                                      GrPaint&& paint,
210                                                      OpArgs&& ... opArgs) {
211      auto color = paint.getColor4f();
212      if (paint.isTrivial()) {
213          return GrOp::Make<Op>(context, nullptr, color, std::forward<OpArgs>(opArgs)...);
214      } else {
215          return GrOp::MakeWithProcessorSet<Op>(
216                  context, color, std::move(paint), std::forward<OpArgs>(opArgs)...);
217      }
218  }
 
197  template<typename Op, typename... Args>
198  GrOp::Owner GrOp::MakeWithProcessorSet(
199          GrRecordingContext* context, const SkPMColor4f& color,
200          GrPaint&& paint, Args&&... args) {
201      char* bytes = (char*)::operator new(sizeof(Op) + sizeof(GrProcessorSet));
202      char* setMem = bytes + sizeof(Op);
203      GrProcessorSet* processorSet = new (setMem)  GrProcessorSet{std::move(paint)};
204      return Owner{new (bytes) Op(processorSet, color, std::forward<Args>(args)...)};
205  }
 
25  GrProcessorSet::GrProcessorSet(GrPaint&& paint) : fXP(paint.getXPFactory()) {
26      fColorFragmentProcessor = std::move(paint.fColorFragmentProcessor);
27      fCoverageFragmentProcessor = std::move(paint.fCoverageFragmentProcessor);
28  
29      SkDEBUGCODE(paint.fAlive = false;)
30  }
 
82      FillRectOpImpl(GrProcessorSet* processorSet, SkPMColor4f paintColor, GrAAType aaType,
83                     DrawQuad* quad, const GrUserStencilSettings* stencil,
84                     Helper::InputFlags inputFlags)
85              : INHERITED(ClassID())
86              , fHelper(processorSet, aaType, stencil, inputFlags)
87              , fQuads(1, !fHelper.isTrivial()) {
88          // Set bounds before clipping so we don't have to worry about unioning the bounds of
89          // the two potential quads (GrQuad::bounds() is perspective-safe).
90          bool hairline = GrQuadUtils::WillUseHairline(quad->fDevice, aaType, quad->fEdgeFlags);
91          this->setBounds(quad->fDevice.bounds(), HasAABloat(aaType == GrAAType::kCoverage),
92                          hairline ? IsHairline::kYes : IsHairline::kNo);
93          DrawQuad extra;
94          // Always crop to W>0 to remain consistent with GrQuad::bounds()
95          int count = GrQuadUtils::ClipToW0(quad, &extra);
96          if (count == 0) {
97              // We can't discard the op at this point, but disable AA flags so it won't go through
98              // inset/outset processing
99              quad->fEdgeFlags = GrQuadAAFlags::kNone;
100              count = 1;
101          }
102  
103          // Conservatively keep track of the local coordinates; it may be that the paint doesn't
104          // need them after analysis is finished. If the paint is known to be solid up front they
105          // can be skipped entirely.
106          fQuads.append(quad->fDevice, {paintColor, quad->fEdgeFlags},
107                        fHelper.isTrivial() ? nullptr : &quad->fLocal);
108          if (count > 1) {
109              fQuads.append(extra.fDevice, { paintColor, extra.fEdgeFlags },
110                            fHelper.isTrivial() ? nullptr : &extra.fLocal);
111          }
112      }
 
         target就是flushstate
263      void onPrepareDraws(GrMeshDrawTarget* target) override {
264          TRACE_EVENT0("skia.gpu", TRACE_FUNC);
265  
266          const VertexSpec vertexSpec = this->vertexSpec();
267  
268          // Make sure that if the op thought it was a solid color, the vertex spec does not use
269          // local coords.
270          SkASSERT(!fHelper.isTrivial() || !fHelper.usesLocalCoords());
271  
272          const int totalNumVertices = fQuads.count() * vertexSpec.verticesPerQuad();
273  
274          // Fill the allocated vertex data
275          void* vdata = target->makeVertexSpace(vertexSpec.vertexSize(), totalNumVertices,
276                                                &fVertexBuffer, &fBaseVertex);
277          if (!vdata) {
278              SkDebugf("Could not allocate vertices\n");
279              return;
280          }
281  
282          if (fPrePreparedVertices) {
283              const size_t totalVertexSizeInBytes = vertexSpec.vertexSize() * totalNumVertices;
284  
285              memcpy(vdata, fPrePreparedVertices, totalVertexSizeInBytes);
286          } else {
287              this->tessellate(vertexSpec, (char*) vdata);
288          }
289  
290          if (vertexSpec.needsIndexBuffer()) {
291              fIndexBuffer = skgpu::v1::QuadPerEdgeAA::GetIndexBuffer(target,
292                                                                      vertexSpec.indexBufferOption());
293              if (!fIndexBuffer) {
294                  SkDebugf("Could not allocate indices\n");
295                  return;
296              }
297          }
298      }
 
97  void GrRenderTask::prepare(GrOpFlushState* flushState) {
98      for (int i = 0; i < fDeferredProxies.size(); ++i) {
99          fDeferredProxies[i]->texPriv().scheduleUpload(flushState);
100      }
101  
102      this->onPrepare(flushState);
103  }

prepare

主要是从pipeline拿的信息
165  GrProgramInfo* GrSimpleMeshDrawOpHelper::CreateProgramInfo(
166              const GrCaps* caps,
167              SkArenaAlloc* arena,
168              const GrSurfaceProxyView& writeView,
169              bool usesMSAASurface,
170              GrAppliedClip&& appliedClip,
171              const GrDstProxyView& dstProxyView,
172              GrGeometryProcessor* geometryProcessor,
173              GrProcessorSet&& processorSet,
174              GrPrimitiveType primitiveType,
175              GrXferBarrierFlags renderPassXferBarriers,
176              GrLoadOp colorLoadOp,
177              GrPipeline::InputFlags pipelineFlags,
178              const GrUserStencilSettings* stencilSettings) {
         //在这里创建
179      auto pipeline = CreatePipeline(caps,
180                                     arena,
181                                     writeView.swizzle(),
182                                     std::move(appliedClip),
183                                     dstProxyView,
184                                     std::move(processorSet),
185                                     pipelineFlags);
186  
187      return CreateProgramInfo(caps, arena, pipeline, writeView, usesMSAASurface, geometryProcessor,
188                               primitiveType, renderPassXferBarriers, colorLoadOp, stencilSettings);
189  }
 
//主要的信息还是从flushState里获取的
class FillRectOpImpl final : public GrMeshDrawOp {
300      void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
311          if (!fProgramInfo) {
312              this->createProgramInfo(flushState);
313          }
314  
315          const int totalNumVertices = fQuads.count() * vertexSpec.verticesPerQuad();
316  
317          flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
318          flushState->bindBuffers(std::move(fIndexBuffer), nullptr, std::move(fVertexBuffer));
319          flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
320          skgpu::v1::QuadPerEdgeAA::IssueDraw(flushState->caps(), flushState->opsRenderPass(),
321                                              vertexSpec, 0, fQuads.count(), totalNumVertices,
322                                              fBaseVertex);
323      }
 
 
 
GrSemaphoresSubmitted GrDrawingManager::flush(GrSurfaceProxy* proxies[], int numProxies,
        SkSurface::BackendSurfaceAccess access, const GrFlushInfo& info,
        const GrPrepareForExternalIORequests& externalRequests) {
    GrOpFlushState flushState(gpu, resourceProvider, &fTokenTracker, fCpuBufferCache);
    bool flushed = false;
    {
        while (alloc.assign(&startIndex, &stopIndex, &error)) {
            //执行executeRenderTasks
            if (this->executeRenderTasks(
                    startIndex, stopIndex, &flushState, &numRenderTasksExecuted)) {
                flushed = true;
            }
        }
    }
    return result;
}
 
SkTArray<sk_sp<GrRenderTask>> fDAG;
bool GrDrawingManager::executeRenderTasks(int startIndex, int stopIndex, GrOpFlushState* flushState,
                                          int* numRenderTasksExecuted) {
    for (int i = startIndex; i < stopIndex; ++i) {
        GrRenderTask* renderTask = fDAG.renderTask(i);
        if (!renderTask || !renderTask->isInstantiated()) {
             continue;
        }
        SkASSERT(renderTask->deferredProxiesAreInstantiated());
        renderTask->prepare(flushState);
    }
    for (int i = startIndex; i < stopIndex; ++i) {
        GrRenderTask* renderTask = fDAG.renderTask(i);
        if (!renderTask || !renderTask->isInstantiated()) {
            continue;
        }
        if (renderTask->execute(flushState)) {
            anyRenderTasksExecuted = true;
        }
}

考虑下预编译

bool GrGLGpu::ProgramCache::precompileShader(GrDirectContext* dContext,
122                                               const SkData& key,
123                                               const SkData& data) {
124      GrProgramDesc desc;
125      if (!GrProgramDesc::BuildFromData(&desc, key.data(), key.size())) {
126          return false;
127      }
128  
129      std::unique_ptr<Entry>* entry = fMap.find(desc);
130      if (entry) {
131          // We've already seen/compiled this shader
132          return true;
133      }
134  
135      GrGLPrecompiledProgram precompiledProgram;
136      if (!GrGLProgramBuilder::PrecompileProgram(dContext, &precompiledProgram, data)) {
137          return false;
138      }
139  
140      fMap.insert(desc, std::make_unique<Entry>(precompiledProgram));
141      return true;
142  }

https://blog.csdn.net/weixin_61845324/article/details/131494349

flutter就有预编译机制