前面幾篇文章介紹了繪制相關(guān)的組件,主要是SkSurface和GrSurface以及他們的代理類,這些都代表著GPU上的資源,對應(yīng)的是紋理對象。繪制的時候,繪制函數(shù)會轉(zhuǎn)換成繪制指令對象記錄起來,到現(xiàn)在為止,繪制都是在做指令記錄。當前部分的GPU資源也準備就緒,需要開始將繪制指令提交到GPU去做真正的像素處理。這是通過SkSurface的flush函數(shù)觸發(fā)的。我們從RenderPipeLine看起
1. SkSurface.flushAndSubmit
frameworks/base/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
const LightGeometry& lightGeometry,
LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds,
bool opaque, const LightInfo& lightInfo,
const std::vector<sp<RenderNode>>& renderNodes,
FrameInfoVisualizer* profiler) {
...
renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface,
SkMatrix::I());
{
ATRACE_NAME("flush commands");
surface->flushAndSubmit();
}
...
}
renderFrame記錄完一幀的繪制命令后,進一步調(diào)用flushAndSubmit方法
external/skia/include/core/SkSurface.h
void flushAndSubmit(bool syncCpu = false);
external/skia/src/image/SkSurface.cpp
void SkSurface::flushAndSubmit(bool syncCpu) {
this->flush(BackendSurfaceAccess::kNoAccess, GrFlushInfo());
}
GrSemaphoresSubmitted SkSurface::flush(BackendSurfaceAccess access, const GrFlushInfo& flushInfo) {
return asSB(this)->onFlush(access, flushInfo, nullptr);
}
GrSemaphoresSubmitted SkSurface::flush(const GrFlushInfo& info,
const GrBackendSurfaceMutableState* newState) {
return asSB(this)->onFlush(BackendSurfaceAccess::kNoAccess, info, newState);
}
然后進入子類的onFlush方法
external/skia/src/image/SkSurface_Gpu.cpp
GrSemaphoresSubmitted SkSurface_Gpu::onFlush(BackendSurfaceAccess access, const GrFlushInfo& info,
const GrBackendSurfaceMutableState* newState) {
auto dContext = fDevice->recordingContext()->asDirectContext();
if (!dContext) {
return GrSemaphoresSubmitted::kNo;
}
GrSurfaceDrawContext* sdc = fDevice->surfaceDrawContext();
return dContext->priv().flushSurface(sdc->asSurfaceProxy(), access, info, newState);
}
fDevice是一個GpuDevice類型的對象,前面介紹過,構(gòu)造一個GpuDevice對象需要一個RecordingContext對象,它是在RenderThread初始化的時候創(chuàng)建的GrDirectContext對象,同時也需要一個GrSurfaceDrawContext對象,它實際持有GrSurface對象,因此這里通過調(diào)用asSurfaceProxy方法返回的就是這個GrSurface的代理對象。然后到調(diào)用flushSurface來提交指令。
external/skia/src/gpu/GrDirectContextPriv.h
/** Version of above that flushes for a single proxy. Null is allowed. */
GrSemaphoresSubmitted flushSurface(
GrSurfaceProxy* proxy,
SkSurface::BackendSurfaceAccess access = SkSurface::BackendSurfaceAccess::kNoAccess,
const GrFlushInfo& info = {},
const GrBackendSurfaceMutableState* newState = nullptr) {
size_t size = proxy ? 1 : 0;
return this->flushSurfaces({&proxy, size}, access, info, newState);
}
GrSemaphoresSubmitted flushSurfaces(
SkSpan<GrSurfaceProxy*>,
SkSurface::BackendSurfaceAccess = SkSurface::BackendSurfaceAccess::kNoAccess,
const GrFlushInfo& = {},
const GrBackendSurfaceMutableState* newState = nullptr);
實現(xiàn)如下:
GrSemaphoresSubmitted GrDirectContextPriv::flushSurfaces(
SkSpan<GrSurfaceProxy*> proxies,
SkSurface::BackendSurfaceAccess access,
const GrFlushInfo& info,
const GrBackendSurfaceMutableState* newState) {
ASSERT_SINGLE_OWNER
GR_CREATE_TRACE_MARKER_CONTEXT("GrDirectContextPriv", "flushSurfaces", fContext);
if (fContext->abandoned()) {
if (info.fSubmittedProc) {
info.fSubmittedProc(info.fSubmittedContext, false);
}
if (info.fFinishedProc) {
info.fFinishedProc(info.fFinishedContext);
}
return GrSemaphoresSubmitted::kNo;
}
...
return fContext->drawingManager()->flushSurfaces(proxies, access, info, newState);
}
通過調(diào)用drawingManager的flushSurfaces來提交指令,這里就進入到本文的主要內(nèi)容了。
2 . GrDrawingManager.flushSurfaces
先來看看下一下這個drawingManager是哪里來的。GrDirectContext繼承自GrRecordingContext,在GrRecordingContext初始化時,初始化了一個drawingManager
external/skia/src/gpu/GrRecordingContext.cpp
bool GrRecordingContext::init() {
...
if (this->options().fDisableDistanceFieldPaths) {
prcOptions.fGpuPathRenderers &= ~GpuPathRenderers::kSmall;
}
bool reduceOpsTaskSplitting = false;
if (this->caps()->avoidReorderingRenderTasks()) {
reduceOpsTaskSplitting = false;
} else if (GrContextOptions::Enable::kYes == this->options().fReduceOpsTaskSplitting) {
reduceOpsTaskSplitting = true;
} else if (GrContextOptions::Enable::kNo == this->options().fReduceOpsTaskSplitting) {
reduceOpsTaskSplitting = false;
}
fDrawingManager.reset(new GrDrawingManager(this,
prcOptions,
reduceOpsTaskSplitting));
return true;
在這里new出現(xiàn)一個GrDrawingManager,并用fDrawingManager引用它。 它的flushSurfaces方法如下
GrSemaphoresSubmitted GrDrawingManager::flushSurfaces(
SkSpan<GrSurfaceProxy*> proxies,
SkSurface::BackendSurfaceAccess access,
const GrFlushInfo& info,
const GrBackendSurfaceMutableState* newState) {
...
auto direct = fContext->asDirectContext();
GrGpu* gpu = direct->priv().getGpu();
SkASSERT(gpu);
...
bool didFlush = this->flush(proxies, access, info, newState);
...
if (!didFlush || (!direct->priv().caps()->semaphoreSupport() && info.fNumSemaphores)) {
return GrSemaphoresSubmitted::kNo;
}
return GrSemaphoresSubmitted::kYes;
}
繼續(xù)調(diào)用了flush方法,然后didflush表示以及提交完畢,返回GrSemaphoresSubmitted::kYes。 flush方法非常復(fù)雜,需要詳細捋一捋。
bool GrDrawingManager::flush(
SkSpan<GrSurfaceProxy*> proxies,
SkSurface::BackendSurfaceAccess access,
const GrFlushInfo& info,
const GrBackendSurfaceMutableState* newState) {
...
auto dContext = fContext->asDirectContext();
SkASSERT(dContext);
dContext->priv().clientMappedBufferManager()->process();
GrGpu* gpu = dContext->priv().getGpu();
// We have a non abandoned and direct GrContext. It must have a GrGpu.
SkASSERT(gpu);
fFlushing = true;
auto resourceProvider = dContext->priv().resourceProvider();
auto resourceCache = dContext->priv().getResourceCache();
this->sortTasks();
if (!fCpuBufferCache) {
// We cache more buffers when the backend is using client side arrays. Otherwise, we
// expect each pool will use a CPU buffer as a staging buffer before uploading to a GPU
// buffer object. Each pool only requires one staging buffer at a time.
int maxCachedBuffers = fContext->priv().caps()->preferClientSideDynamicBuffers() ? 2 : 6;
fCpuBufferCache = GrBufferAllocPool::CpuBufferCache::Make(maxCachedBuffers);
}
GrOpFlushState flushState(gpu, resourceProvider, &fTokenTracker, fCpuBufferCache);
GrOnFlushResourceProvider onFlushProvider(this);
....
bool usingReorderedDAG = false;
GrResourceAllocator resourceAllocator(dContext);
if (fReduceOpsTaskSplitting) {
usingReorderedDAG = this->reorderTasks(&resourceAllocator);
if (!usingReorderedDAG) {
resourceAllocator.reset();
}
}
if (!resourceAllocator.failedInstantiation()) {
if (!usingReorderedDAG) {
for (const auto& task : fDAG) {
SkASSERT(task);
task->gatherProxyIntervals(&resourceAllocator);
}
resourceAllocator.planAssignment();
}
resourceAllocator.assign();
}
bool flushed = !resourceAllocator.failedInstantiation() &&
this->executeRenderTasks(&flushState);
this->removeRenderTasks();
gpu->executeFlushInfo(proxies, access, info, newState);
// Give the cache a chance to purge resources that become purgeable due to flushing.
if (flushed) {
resourceCache->purgeAsNeeded();
flushed = false;
}
if (flushed) {
resourceCache->purgeAsNeeded();
}
fFlushingRenderTaskIDs.reset();
fFlushing = false;
return true;
}
flush函數(shù)主要在做幾件事件,
- 1調(diào)用sortTasks對task進行topo排序 ,這里的task是來自于fDAG變量,它保存的是利用SkCanvas繪制的指令。
- 2 創(chuàng)建f在CPU側(cè)的buffer緩存CpuBufferCache,
- 3 如果task對應(yīng)的GrSurface沒有分配資源的話,利用resourceProvider和resourceAllocator來為task分配GPU寄存器資源,
- 4 如果分配資源沒有失敗,則調(diào)用executeRenderTasks來執(zhí)行繪制任務(wù),并提交的GPU。
- 5 最后調(diào)用gpu->executeFlushInfo來完成flush。
分配寄存器資源的原理就不再這里進一步分析。我們來看一下executeRenderTasks的流程
3. GrDrawingManager.executeRenderTasks
bool GrDrawingManager::executeRenderTasks(GrOpFlushState* flushState) {
bool anyRenderTasksExecuted = false;
for (const auto& renderTask : fDAG) {
if (!renderTask || !renderTask->isInstantiated()) {
continue;
}
SkASSERT(renderTask->deferredProxiesAreInstantiated());
renderTask->prepare(flushState);
}
// Upload all data to the GPU
flushState->preExecuteDraws();
// For Vulkan, if we have too many oplists to be flushed we end up allocating a lot of resources
// for each command buffer associated with the oplists. If this gets too large we can cause the
// devices to go OOM. In practice we usually only hit this case in our tests, but to be safe we
// put a cap on the number of oplists we will execute before flushing to the GPU to relieve some
// memory pressure.
static constexpr int kMaxRenderTasksBeforeFlush = 100;
int numRenderTasksExecuted = 0;
// Execute the onFlush renderTasks first, if any.
for (sk_sp<GrRenderTask>& onFlushRenderTask : fOnFlushRenderTasks) {
if (!onFlushRenderTask->execute(flushState)) {
SkDebugf("WARNING: onFlushRenderTask failed to execute.\n");
}
SkASSERT(onFlushRenderTask->unique());
onFlushRenderTask->disown(this);
onFlushRenderTask = nullptr;
if (++numRenderTasksExecuted >= kMaxRenderTasksBeforeFlush) {
flushState->gpu()->submitToGpu(false);
numRenderTasksExecuted = 0;
}
}
fOnFlushRenderTasks.reset();
// Execute the normal op lists.
for (const auto& renderTask : fDAG) {
SkASSERT(renderTask);
if (!renderTask->isInstantiated()) {
continue;
}
if (renderTask->execute(flushState)) {
anyRenderTasksExecuted = true;
}
if (++numRenderTasksExecuted >= kMaxRenderTasksBeforeFlush) {
flushState->gpu()->submitToGpu(false);
numRenderTasksExecuted = 0;
}
}
SkASSERT(!flushState->opsRenderPass());
SkASSERT(fTokenTracker.nextDrawToken() == fTokenTracker.nextTokenToFlush());
// We reset the flush state before the RenderTasks so that the last resources to be freed are
// those that are written to in the RenderTasks. This helps to make sure the most recently used
// resources are the last to be purged by the resource cache.
flushState->reset();
return anyRenderTasksExecuted;
}
這里的邏輯相對來說不是很復(fù)雜。它這執(zhí)行的任務(wù)有兩種。一種是fOnFlushRenderTasks中的GrRenderTask,它是在flush過程中產(chǎn)生的新的rendertask,可能是空的。另外一種是fDAG中的GrRenderTask,這是繪制時產(chǎn)生的rendertask。他們的實際類型是GrOpsTask,是GrRenderTask的子類.
external/skia/src/gpu/GrOpsTask.h
class GrOpsTask : public GrRenderTask {}
他們是在GrDrawingManager的newOpsTask方法中生成的,SkCanas中繪制時調(diào)用的就是這個方法,因此skcanvas中的繪制生成的Ops最終其實就是保存在GrDrawingManager的fDAG中
sk_sp<GrOpsTask> GrDrawingManager::newOpsTask(GrSurfaceProxyView surfaceView,
sk_sp<GrArenas> arenas,
bool flushTimeOpsTask) {
SkDEBUGCODE(this->validate());
SkASSERT(fContext);
this->closeActiveOpsTask();
sk_sp<GrOpsTask> opsTask(new GrOpsTask(this,
std::move(surfaceView),
fContext->priv().auditTrail(),
std::move(arenas)));
SkASSERT(this->getLastRenderTask(opsTask->target(0)) == opsTask.get());
if (flushTimeOpsTask) {
fOnFlushRenderTasks.push_back(opsTask);
} else {
this->appendTask(opsTask);
fActiveOpsTask = opsTask.get();
}
SkDEBUGCODE(this->validate());
return opsTask;
}
對于這兩種task,都會通過調(diào)用GrRenderTask::isInstantiated()去檢查是否已經(jīng)初始化分配好了GPU寄存器資源,沒有分配的會被過濾掉。然后對有效的task的執(zhí)行execute方法,最后進入到實現(xiàn)類GrOpsTask的onExecute方法。每執(zhí)行完kMaxRenderTasksBeforeFlush個任務(wù)后再調(diào)用 flushState->gpu()->submitToGpu(false);來提交到GPU。
4. 總結(jié)
本文分析了繪制流程中一個非常重要的類GrDrawingManager,它保存著GPU渲染前的繪制命令最后形態(tài)的集合fDAG。flush為fDAG
中的繪制任務(wù)(task中持有一個GrSurface或者GrTexture)中需要分配資源的,會分配GPU寄存器資源,然后調(diào)用Gpu的submit方法來提交GPU渲染?;趥€人的理解,如有疏誤,歡迎同行指正。