cache_miss问题排查
SurfaceFlinger RenderEngine
现象如下:


加了skiatrace之后

可以看到,主要是在绘制startingwindow及其平滑圆角
cachemiss代码
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就有预编译机制