Add submittedProc to GrFlushInfo.

Bug: skia:10118
Change-Id: Iad848310d0f2fb22f19e9890209548fda103bd27
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/291078
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Greg Daniel <egdaniel@google.com>
This commit is contained in:
Greg Daniel 2020-05-26 11:26:45 -04:00 committed by Skia Commit-Bot
parent 1303a1a15c
commit 55822f17bd
11 changed files with 194 additions and 13 deletions

View File

@ -9,6 +9,11 @@ Milestone 85
* <insert new release notes here>
* Adds submittedProc callback to GrFlushInfo which will be called when the work
from the flush call is submitted to the GPU. This is specifically useful for knowing
when semahpores sent with the flush have been submitted and can be waiting on.
https://review.skia.org/291078
* GrContext submit is now required to be called in order to send GPU work to the
actual GPU. The flush calls simply produces 3D API specific objects that are ready
to be submitted (e.g. command buffers). For the GL backend, the flush will still

View File

@ -112,6 +112,7 @@ tests_sources = [
"$_tests/GrQuadBufferTest.cpp",
"$_tests/GrQuadCropTest.cpp",
"$_tests/GrStyledShapeTest.cpp",
"$_tests/GrSubmittedFlushTest.cpp",
"$_tests/GrSurfaceTest.cpp",
"$_tests/GrTAllocatorTest.cpp",
"$_tests/GrTRecorderTest.cpp",

View File

@ -234,6 +234,9 @@ enum GrFlushFlags {
typedef void* GrGpuFinishedContext;
typedef void (*GrGpuFinishedProc)(GrGpuFinishedContext finishedContext);
typedef void* GrGpuSubmittedContext;
typedef void (*GrGpuSubmittedProc)(GrGpuSubmittedContext submittedContext, bool success);
/**
* Struct to supply options to flush calls.
*
@ -251,13 +254,31 @@ typedef void (*GrGpuFinishedProc)(GrGpuFinishedContext finishedContext);
* from this flush call and all previous flush calls has finished on the GPU. If the flush call
* fails due to an error and nothing ends up getting sent to the GPU, the finished proc is called
* immediately.
*
* If a submittedProc is provided, the submittedProc will be called when all work from this flush
* call is submitted to the GPU. If the flush call fails due to an error and nothing will get sent
* to the GPU, the submitted proc is called immediately. It is possibly that when work is finally
* submitted, that the submission actual fails. In this case we will not reattempt to do the
* submission. Skia notifies the client of these via the success bool passed into the submittedProc.
* The submittedProc is useful to the client to know when semaphores that were sent with the flush
* have actually been submitted to the GPU so that they can be waited on (or deleted if the submit
* fails).
* Note about GL: In GL work gets sent to the driver immediately during the flush call, but we don't
* really know when the driver sends the work to the GPU. Therefore, we treat the submitted proc as
* we do in other backends. It will be called when the next GrContext::submit is called after the
* flush (or possibly during the flush if there is no work to be done for the flush). The main use
* case for the submittedProc is to know when semaphores have been sent to the GPU and even in GL
* it is required to call GrContext::submit to flush them. So a client should be able to treat all
* backend APIs the same in terms of how the submitted procs are treated.
*/
struct GrFlushInfo {
GrFlushFlags fFlags = kNone_GrFlushFlags;
int fNumSemaphores = 0;
GrBackendSemaphore* fSignalSemaphores = nullptr;
GrGpuFinishedProc fFinishedProc = nullptr;
GrFlushFlags fFlags = kNone_GrFlushFlags;
int fNumSemaphores = 0;
GrBackendSemaphore* fSignalSemaphores = nullptr;
GrGpuFinishedProc fFinishedProc = nullptr;
GrGpuFinishedContext fFinishedContext = nullptr;
GrGpuSubmittedProc fSubmittedProc = nullptr;
GrGpuSubmittedContext fSubmittedContext = nullptr;
};
/**

View File

@ -316,6 +316,12 @@ GrSemaphoresSubmitted GrContext::flush(const GrFlushInfo& info,
const GrPrepareForExternalIORequests& externalRequests) {
ASSERT_SINGLE_OWNER
if (this->abandoned()) {
if (info.fFinishedProc) {
info.fFinishedProc(info.fFinishedContext);
}
if (info.fSubmittedProc) {
info.fSubmittedProc(info.fSubmittedContext, false);
}
return GrSemaphoresSubmitted::kNo;
}

View File

@ -229,6 +229,9 @@ bool GrDrawingManager::flush(GrSurfaceProxy* proxies[], int numProxies,
GR_CREATE_TRACE_MARKER_CONTEXT("GrDrawingManager", "flush", fContext);
if (fFlushing || this->wasAbandoned()) {
if (info.fSubmittedProc) {
info.fSubmittedProc(info.fSubmittedContext, false);
}
if (info.fFinishedProc) {
info.fFinishedProc(info.fFinishedContext);
}
@ -245,12 +248,18 @@ bool GrDrawingManager::flush(GrSurfaceProxy* proxies[], int numProxies,
canSkip = !fDAG.isUsed(proxies[i]) && !this->isDDLTarget(proxies[i]);
}
if (canSkip) {
if (info.fSubmittedProc) {
info.fSubmittedProc(info.fSubmittedContext, true);
}
return false;
}
}
auto direct = fContext->priv().asDirectContext();
if (!direct) {
if (info.fSubmittedProc) {
info.fSubmittedProc(info.fSubmittedContext, false);
}
if (info.fFinishedProc) {
info.fFinishedProc(info.fFinishedContext);
}
@ -259,12 +268,8 @@ bool GrDrawingManager::flush(GrSurfaceProxy* proxies[], int numProxies,
direct->priv().clientMappedBufferManager()->process();
GrGpu* gpu = direct->priv().getGpu();
if (!gpu) {
if (info.fFinishedProc) {
info.fFinishedProc(info.fFinishedContext);
}
return false; // Can't flush while DDL recording
}
// We have a non abandoned and direct GrContext. It must have a GrGpu.
SkASSERT(gpu);
fFlushing = true;
@ -515,6 +520,12 @@ GrSemaphoresSubmitted GrDrawingManager::flushSurfaces(GrSurfaceProxy* proxies[],
SkSurface::BackendSurfaceAccess access,
const GrFlushInfo& info) {
if (this->wasAbandoned()) {
if (info.fSubmittedProc) {
info.fSubmittedProc(info.fSubmittedContext, false);
}
if (info.fFinishedProc) {
info.fFinishedProc(info.fFinishedContext);
}
return GrSemaphoresSubmitted::kNo;
}
SkDEBUGCODE(this->validate());
@ -523,13 +534,18 @@ GrSemaphoresSubmitted GrDrawingManager::flushSurfaces(GrSurfaceProxy* proxies[],
auto direct = fContext->priv().asDirectContext();
if (!direct) {
if (info.fSubmittedProc) {
info.fSubmittedProc(info.fSubmittedContext, false);
}
if (info.fFinishedProc) {
info.fFinishedProc(info.fFinishedContext);
}
return GrSemaphoresSubmitted::kNo; // Can't flush while DDL recording
}
GrGpu* gpu = direct->priv().getGpu();
if (!gpu) {
return GrSemaphoresSubmitted::kNo; // Can't flush while DDL recording
}
// We have a non abandoned and direct GrContext. It must have a GrGpu.
SkASSERT(gpu);
// TODO: It is important to upgrade the drawingmanager to just flushing the
// portion of the DAG required by 'proxies' in order to restore some of the

View File

@ -42,6 +42,7 @@ static const size_t kMinStagingBufferSize = 32 * 1024;
GrGpu::GrGpu(GrContext* context) : fResetBits(kAll_GrBackendState), fContext(context) {}
GrGpu::~GrGpu() {
this->callSubmittedProcs(false);
SkASSERT(fBusyStagingBuffers.isEmpty());
}
@ -685,6 +686,11 @@ void GrGpu::executeFlushInfo(GrSurfaceProxy* proxies[],
if (info.fFinishedProc) {
this->addFinishedProc(info.fFinishedProc, info.fFinishedContext);
}
if (info.fSubmittedProc) {
fSubmittedProcs.emplace_back(info.fSubmittedProc, info.fSubmittedContext);
}
this->prepareSurfacesForBackendAccessAndExternalIO(proxies, numProxies, access,
externalRequests);
}
@ -699,9 +705,18 @@ bool GrGpu::submitToGpu(bool syncCpu) {
bool submitted = this->onSubmitToGpu(syncCpu);
this->callSubmittedProcs(submitted);
return submitted;
}
void GrGpu::callSubmittedProcs(bool success) {
for (int i = 0; i < fSubmittedProcs.count(); ++i) {
fSubmittedProcs[i].fProc(fSubmittedProcs[i].fContext, success);
}
fSubmittedProcs.reset();
}
#ifdef SK_ENABLE_DUMP_GPU
void GrGpu::dumpJSON(SkJSONWriter* writer) const {
writer->beginObject();

View File

@ -849,6 +849,8 @@ private:
void validateStagingBuffers() const;
#endif
void callSubmittedProcs(bool success);
uint32_t fResetBits;
// The context owns us, not vice-versa, so this ptr is not ref'ed by Gpu.
GrContext* fContext;
@ -860,6 +862,15 @@ private:
StagingBufferList fActiveStagingBuffers;
StagingBufferList fBusyStagingBuffers;
struct SubmittedProc {
SubmittedProc(GrGpuSubmittedProc proc, GrGpuSubmittedContext context)
: fProc(proc), fContext(context) {}
GrGpuSubmittedProc fProc;
GrGpuSubmittedContext fContext;
};
SkSTArray<4, SubmittedProc> fSubmittedProcs;
friend class GrPathRendering;
typedef SkRefCnt INHERITED;
};

View File

@ -2166,6 +2166,12 @@ GrSemaphoresSubmitted GrRenderTargetContext::flush(SkSurface::BackendSurfaceAcce
const GrFlushInfo& info) {
ASSERT_SINGLE_OWNER
if (fContext->priv().abandoned()) {
if (info.fSubmittedProc) {
info.fSubmittedProc(info.fSubmittedContext, false);
}
if (info.fFinishedProc) {
info.fFinishedProc(info.fFinishedContext);
}
return GrSemaphoresSubmitted::kNo;
}
SkDEBUGCODE(this->validate();)

View File

@ -67,6 +67,12 @@ SkImage_Gpu::~SkImage_Gpu() {}
GrSemaphoresSubmitted SkImage_Gpu::onFlush(GrContext* context, const GrFlushInfo& info) {
if (!context || !fContext->priv().matches(context) || fContext->abandoned()) {
if (info.fSubmittedProc) {
info.fSubmittedProc(info.fSubmittedContext, false);
}
if (info.fFinishedProc) {
info.fFinishedProc(info.fFinishedContext);
}
return GrSemaphoresSubmitted::kNo;
}

View File

@ -117,6 +117,12 @@ bool SkImage_GpuYUVA::setupMipmapsForPlanes(GrRecordingContext* context) const {
GrSemaphoresSubmitted SkImage_GpuYUVA::onFlush(GrContext* context, const GrFlushInfo& info) {
if (!context || !fContext->priv().matches(context) || fContext->abandoned()) {
if (info.fSubmittedProc) {
info.fSubmittedProc(info.fSubmittedContext, false);
}
if (info.fFinishedProc) {
info.fFinishedProc(info.fFinishedContext);
}
return GrSemaphoresSubmitted::kNo;
}

View File

@ -0,0 +1,88 @@
/*
* Copyright 2020 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "tests/Test.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkSurface.h"
#include "include/gpu/GrContext.h"
using namespace sk_gpu_test;
namespace {
struct SubmittedInfo {
int* fCount;
bool* fSuccess;
};
};
static void testing_submitted_proc(void* ctx, bool success) {
SubmittedInfo* info = (SubmittedInfo*)ctx;
*info->fCount += 1;
*info->fSuccess = success;
}
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(FlushSubmittedProcTest, reporter, ctxInfo) {
GrContext* ctx = ctxInfo.grContext();
SkImageInfo info = SkImageInfo::Make(8, 8, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info);
SkCanvas* canvas = surface->getCanvas();
canvas->clear(SK_ColorGREEN);
int submittedCount = 0;
bool submittedSuccess = false;
SubmittedInfo submittedInfo = { &submittedCount, &submittedSuccess };
GrFlushInfo flushInfo;
flushInfo.fSubmittedProc = testing_submitted_proc;
flushInfo.fSubmittedContext = &submittedInfo;
ctx->flush(flushInfo);
REPORTER_ASSERT(reporter, submittedCount == 0);
ctx->submit();
REPORTER_ASSERT(reporter, submittedCount == 1);
REPORTER_ASSERT(reporter, submittedSuccess);
// There should be no work so if we flush again the submittedProc should be called immediately
surface->flush(SkSurface::BackendSurfaceAccess::kNoAccess, flushInfo);
REPORTER_ASSERT(reporter, submittedCount == 2);
REPORTER_ASSERT(reporter, submittedSuccess);
// However, flushing the context we don't do any checks of work so we still require submit to be
// called in order for the callback to trigger.
ctx->flush(flushInfo);
REPORTER_ASSERT(reporter, submittedCount == 2);
ctx->submit();
REPORTER_ASSERT(reporter, submittedCount == 3);
REPORTER_ASSERT(reporter, submittedSuccess);
// Testing that doing multiple flushes before a submit triggers both submittedProcs to be called
canvas->clear(SK_ColorBLUE);
surface->flush(SkSurface::BackendSurfaceAccess::kNoAccess, flushInfo);
REPORTER_ASSERT(reporter, submittedCount == 3);
canvas->clear(SK_ColorRED);
surface->flush(SkSurface::BackendSurfaceAccess::kNoAccess, flushInfo);
REPORTER_ASSERT(reporter, submittedCount == 3);
ctx->submit();
REPORTER_ASSERT(reporter, submittedCount == 5);
REPORTER_ASSERT(reporter, submittedSuccess);
// Test an abandoned context to get a failed submit immediately when flush is called
canvas->clear(SK_ColorCYAN);
ctx->abandonContext();
surface->flush(SkSurface::BackendSurfaceAccess::kNoAccess, flushInfo);
REPORTER_ASSERT(reporter, submittedCount == 6);
REPORTER_ASSERT(reporter, !submittedSuccess);
ctx->flush(flushInfo);
REPORTER_ASSERT(reporter, submittedCount == 7);
REPORTER_ASSERT(reporter, !submittedSuccess);
}