Initiate regeneration of mipmaps from proxy DAG land

This allows us to resolve all the textures before executing a command
buffer, rather than interrupting execution as is currently done in the
GL backend.

Change-Id: I998546b312d24cf47fc2c837e7c94fbf95571af0
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/230636
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
This commit is contained in:
Chris Dalton 2019-08-09 18:06:39 -06:00 committed by Skia Commit-Bot
parent 19db24059b
commit fe19203eb7
15 changed files with 355 additions and 44 deletions

View File

@ -232,6 +232,8 @@ skia_gpu_sources = [
"$_src/gpu/GrTextureRenderTargetProxy.cpp",
"$_src/gpu/GrTextureRenderTargetProxy.h",
"$_src/gpu/GrTextureResolveManager.h",
"$_src/gpu/GrTextureResolveRenderTask.cpp",
"$_src/gpu/GrTextureResolveRenderTask.h",
"$_src/gpu/GrTRecorder.h",
"$_src/gpu/GrUserStencilSettings.h",
"$_src/gpu/GrWindowRectangles.h",

View File

@ -1570,6 +1570,17 @@ private:
Context fReleaseCtx;
};
/**
* Indicates "resolutions" that need to be done on a texture before it can be sampled from.
*/
enum class GrTextureResolveFlags {
kNone = 0,
kMipMaps = 1 << 0, // Regenerate all mipmap levels.
// TODO: kMSAA = 1 << 1 // Blit the MSAA render buffer into a standard texture.
};
GR_MAKE_BITFIELD_CLASS_OPS(GrTextureResolveFlags)
#if GR_TEST_UTILS || defined(SK_ENABLE_DUMP_GPU)
static constexpr const char* GrBackendApiToStr(GrBackendApi api) {
switch (api) {

View File

@ -30,6 +30,7 @@
#include "src/gpu/GrTexturePriv.h"
#include "src/gpu/GrTextureProxy.h"
#include "src/gpu/GrTextureProxyPriv.h"
#include "src/gpu/GrTextureResolveRenderTask.h"
#include "src/gpu/GrTracing.h"
#include "src/gpu/ccpr/GrCoverageCountingPathRenderer.h"
#include "src/gpu/text/GrTextContext.h"
@ -81,8 +82,16 @@ bool GrDrawingManager::RenderTaskDAG::isUsed(GrSurfaceProxy* proxy) const {
return false;
}
void GrDrawingManager::RenderTaskDAG::add(sk_sp<GrRenderTask> renderTask) {
fRenderTasks.emplace_back(std::move(renderTask));
GrRenderTask* GrDrawingManager::RenderTaskDAG::add(sk_sp<GrRenderTask> renderTask) {
return fRenderTasks.emplace_back(std::move(renderTask)).get();
}
GrRenderTask* GrDrawingManager::RenderTaskDAG::addBeforeLast(sk_sp<GrRenderTask> renderTask) {
SkASSERT(!fRenderTasks.empty());
// Release 'fRenderTasks.back()' and grab the raw pointer. If we pass in a reference to an array
// element, and the array grows during emplace_back, then the reference will become invalidated.
fRenderTasks.emplace_back(fRenderTasks.back().release());
return (fRenderTasks[fRenderTasks.count() - 2] = std::move(renderTask)).get();
}
void GrDrawingManager::RenderTaskDAG::add(const SkTArray<sk_sp<GrRenderTask>>& opLists) {
@ -499,17 +508,18 @@ GrSemaphoresSubmitted GrDrawingManager::flushSurfaces(GrSurfaceProxy* proxies[],
if (!proxies[i]->isInstantiated()) {
return result;
}
}
for (int i = 0; i < numProxies; ++i) {
GrSurface* surface = proxies[i]->peekSurface();
if (auto* rt = surface->asRenderTarget()) {
gpu->resolveRenderTarget(rt);
}
if (auto* tex = surface->asTexture()) {
if (tex->texturePriv().mipMapped() == GrMipMapped::kYes &&
tex->texturePriv().mipMapsAreDirty()) {
gpu->regenerateMipMapLevels(tex);
// If, after a flush, any of the proxies of interest have dirty mipmaps, regenerate them in
// case their backend textures are being stolen.
// (This special case is exercised by the ReimportImageTextureWithMipLevels test.)
// FIXME: It may be more ideal to plumb down a "we're going to steal the backends" flag.
if (auto* textureProxy = proxies[i]->asTextureProxy()) {
if (textureProxy->mipMapsAreDirty()) {
gpu->regenerateMipMapLevels(textureProxy->peekTexture());
textureProxy->markMipMapsClean();
}
}
}
@ -669,6 +679,24 @@ sk_sp<GrTextureOpList> GrDrawingManager::newTextureOpList(sk_sp<GrTextureProxy>
return opList;
}
GrRenderTask* GrDrawingManager::newTextureResolveRenderTask(
sk_sp<GrTextureProxy> textureProxy, GrTextureResolveFlags flags, const GrCaps& caps) {
// Unlike in the "new opList" cases, we do not want to close the active opList, nor (if we are
// in sorting and opList reduction mode) the render tasks that depend on the proxy's current
// state. This is because those opLists can still receive new ops and because if they refer to
// the mipmapped version of 'textureProxy', they will then come to depend on the render task
// being created here.
// NOTE: In either case, 'textureProxy' should already be closed at this point (i.e., its state
// is finished))
SkASSERT(!textureProxy->getLastRenderTask() || textureProxy->getLastRenderTask()->isClosed());
auto textureResolveTask = GrTextureResolveRenderTask::Make(textureProxy, flags, caps);
SkASSERT(textureProxy->getLastRenderTask() == textureResolveTask.get());
// Add the new textureResolveTask before the fActiveOpList (if not in
// sorting/opList-splitting-reduction mode) because it will depend upon this resolve task.
// NOTE: Putting it here will also reduce the amount of work required by the topological sort.
return fDAG.addBeforeLast(std::move(textureResolveTask));
}
GrTextContext* GrDrawingManager::getTextContext() {
if (!fTextContext) {
fTextContext = GrTextContext::Make(fOptionsForTextContext);

View File

@ -52,6 +52,13 @@ public:
sk_sp<GrRenderTargetOpList> newRTOpList(sk_sp<GrRenderTargetProxy>, bool managedOpList);
sk_sp<GrTextureOpList> newTextureOpList(sk_sp<GrTextureProxy>);
// Create a new, specialized, render task that will regenerate mipmap levels and/or resolve
// MSAA (depending on GrTextureResolveFlags). This method will add the new render task to the
// list of render tasks and make it depend on the target texture proxy. It is up to the caller
// to add any dependencies on the new render task.
GrRenderTask* newTextureResolveRenderTask(
sk_sp<GrTextureProxy>, GrTextureResolveFlags, const GrCaps&);
GrRecordingContext* getContext() { return fContext; }
GrTextContext* getTextContext();
@ -130,7 +137,8 @@ private:
GrRenderTask* back() { return fRenderTasks.back().get(); }
const GrRenderTask* back() const { return fRenderTasks.back().get(); }
void add(sk_sp<GrRenderTask>);
GrRenderTask* add(sk_sp<GrRenderTask>);
GrRenderTask* addBeforeLast(sk_sp<GrRenderTask>);
void add(const SkTArray<sk_sp<GrRenderTask>>&);
void swap(SkTArray<sk_sp<GrRenderTask>>* renderTasks);

View File

@ -502,6 +502,7 @@ public:
#if GR_TEST_UTILS
bool testingOnly_IsInstantiated() const { return fRenderTargetProxy->isInstantiated(); }
void testingOnly_SetPreserveOpsOnFullClear() { fPreserveOpsOnFullClear_TestingOnly = true; }
GrRenderTargetOpList* testingOnly_PeekLastOpList() { return fOpList.get(); }
#endif
protected:

View File

@ -356,6 +356,12 @@ GrRenderTargetOpList::GrRenderTargetOpList(sk_sp<GrOpMemoryPool> opMemoryPool,
: INHERITED(std::move(opMemoryPool), std::move(proxy), auditTrail)
, fLastClipStackGenID(SK_InvalidUniqueID)
SkDEBUGCODE(, fNumClips(0)) {
if (GrTextureProxy* textureProxy = fTarget->asTextureProxy()) {
if (GrMipMapped::kYes == textureProxy->mipMapped()) {
textureProxy->markMipMapsDirty();
}
}
fTarget->setLastRenderTask(this);
}
void GrRenderTargetOpList::deleteOps() {

View File

@ -24,7 +24,6 @@ GrRenderTask::GrRenderTask(sk_sp<GrSurfaceProxy> target)
: fTarget(std::move(target))
, fUniqueID(CreateUniqueID())
, fFlags(0) {
fTarget->setLastRenderTask(this);
}
GrRenderTask::~GrRenderTask() {
@ -69,13 +68,44 @@ void GrRenderTask::addDependency(GrRenderTask* dependedOn) {
}
// Convert from a GrSurface-based dependency to a GrRenderTask one
void GrRenderTask::addDependency(GrSurfaceProxy* dependedOn, GrMipMapped, GrTextureResolveManager,
void GrRenderTask::addDependency(GrSurfaceProxy* dependedOn, GrMipMapped mipMapped,
GrTextureResolveManager textureResolveManager,
const GrCaps& caps) {
if (dependedOn->getLastRenderTask()) {
GrRenderTask* dependedOnTask = dependedOn->getLastRenderTask();
GrTextureProxy* textureProxy = dependedOn->asTextureProxy();
SkASSERT(GrMipMapped::kNo == mipMapped || textureProxy);
// Does this proxy have mipmaps that need to be regenerated?
if (GrMipMapped::kYes == mipMapped && textureProxy->mipMapsAreDirty()) {
// We only read our own target during dst reads, and we shouldn't use mipmaps in that case.
SkASSERT(dependedOnTask != this);
// Create an opList that resolves the texture's mipmap data.
GrRenderTask* textureResolveTask = textureResolveManager.newTextureResolveRenderTask(
sk_ref_sp(textureProxy), GrTextureResolveFlags::kMipMaps, caps);
// The GrTextureResolveRenderTask factory should have called addDependency (in this
// instance, recursively) on the textureProxy.
SkASSERT(!dependedOnTask || textureResolveTask->dependsOn(dependedOnTask));
SkASSERT(!textureProxy->texPriv().isDeferred() ||
textureResolveTask->fDeferredProxies.back() == textureProxy);
// The GrTextureResolveRenderTask factory should have also marked the mipmaps clean and set
// the last opList on the textureProxy to textureResolveTask.
SkASSERT(!textureProxy->mipMapsAreDirty());
SkASSERT(textureProxy->getLastRenderTask() == textureResolveTask);
// Fall through and add textureResolveTask as a dependency of "this".
dependedOnTask = textureResolveTask;
} else if (textureProxy && textureProxy->texPriv().isDeferred()) {
fDeferredProxies.push_back(textureProxy);
}
if (dependedOnTask) {
// If it is still receiving dependencies, this GrRenderTask shouldn't be closed
SkASSERT(!this->isClosed());
GrRenderTask* dependedOnTask = dependedOn->getLastRenderTask();
if (dependedOnTask == this) {
// self-read - presumably for dst reads. We can't make it closed in the self-read case.
} else {
@ -87,12 +117,6 @@ void GrRenderTask::addDependency(GrSurfaceProxy* dependedOn, GrMipMapped, GrText
dependedOnTask->makeClosed(caps);
}
}
if (GrTextureProxy* textureProxy = dependedOn->asTextureProxy()) {
if (textureProxy->texPriv().isDeferred()) {
fDeferredProxies.push_back(textureProxy);
}
}
}
bool GrRenderTask::dependsOn(const GrRenderTask* dependedOn) const {

View File

@ -29,6 +29,10 @@ GrTextureOpList::GrTextureOpList(sk_sp<GrOpMemoryPool> opMemoryPool,
: INHERITED(std::move(opMemoryPool), proxy, auditTrail) {
SkASSERT(fOpMemoryPool);
SkASSERT(!proxy->readOnly());
if (GrMipMapped::kYes == proxy->mipMapped()) {
proxy->markMipMapsDirty();
}
fTarget->setLastRenderTask(this);
}
void GrTextureOpList::deleteOp(int index) {

View File

@ -41,6 +41,14 @@ public:
(GrMipMapsStatus::kNotAllocated == fMipMapsStatus));
return GrMipMapped::kYes == fMipMapped && GrMipMapsStatus::kValid != fMipMapsStatus;
}
void markMipMapsDirty() {
SkASSERT(GrMipMapped::kYes == fMipMapped);
fMipMapsStatus = GrMipMapsStatus::kDirty;
}
void markMipMapsClean() {
SkASSERT(GrMipMapped::kYes == fMipMapped);
fMipMapsStatus = GrMipMapsStatus::kValid;
}
// Returns the GrMipMapped value of the proxy from creation time regardless of whether it has
// been instantiated or not.

View File

@ -9,7 +9,7 @@
#define GrTextureResolveManager_DEFINED
#include "include/core/SkRefCnt.h"
#include "include/gpu/GrTypes.h"
#include "src/gpu/GrDrawingManager.h"
class GrCaps;
class GrDrawingManager;
@ -26,19 +26,14 @@ public:
explicit GrTextureResolveManager(GrDrawingManager* drawingManager)
: fDrawingManager(drawingManager) {}
enum class ResolveFlags : bool {
kNone = 0,
kMipMaps = 1 << 0,
// TODO: kMSAA = 1 << 1
};
GrRenderTask* newTextureResolveRenderTask(
sk_sp<GrTextureProxy>, ResolveFlags, const GrCaps&) const;
sk_sp<GrTextureProxy> proxy, GrTextureResolveFlags flags, const GrCaps& caps) const {
SkASSERT(fDrawingManager);
return fDrawingManager->newTextureResolveRenderTask(std::move(proxy), flags, caps);
}
private:
GrDrawingManager* fDrawingManager;
};
GR_MAKE_BITFIELD_CLASS_OPS(GrTextureResolveManager::ResolveFlags);
#endif

View File

@ -0,0 +1,64 @@
/*
* Copyright 2019 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/gpu/GrTextureResolveRenderTask.h"
#include "src/gpu/GrGpu.h"
#include "src/gpu/GrMemoryPool.h"
#include "src/gpu/GrOpFlushState.h"
#include "src/gpu/GrResourceAllocator.h"
#include "src/gpu/GrTexturePriv.h"
sk_sp<GrRenderTask> GrTextureResolveRenderTask::Make(
sk_sp<GrTextureProxy> textureProxyPtr, GrTextureResolveFlags flags, const GrCaps& caps) {
GrTextureProxy* textureProxy = textureProxyPtr.get();
sk_sp<GrTextureResolveRenderTask> resolveTask(
new GrTextureResolveRenderTask(std::move(textureProxyPtr), flags));
// Add the target as a dependency: We will read the existing contents of this texture while
// generating mipmap levels and/or resolving MSAA.
//
// NOTE: This must be called before makeClosed.
resolveTask->addDependency(
textureProxy, GrMipMapped::kNo, GrTextureResolveManager(nullptr), caps);
textureProxy->setLastRenderTask(resolveTask.get());
// We only resolve the texture; nobody should try to do anything else with this opList.
resolveTask->makeClosed(caps);
if (GrTextureResolveFlags::kMipMaps & flags) {
SkASSERT(GrMipMapped::kYes == textureProxy->mipMapped());
SkASSERT(textureProxy->mipMapsAreDirty());
textureProxy->markMipMapsClean();
}
// On some old ISO C++11 compilers, 'resolveTask' will require an explicit std::move() when
// returned from this function. This is because its type (sk_sp<GrTextureResolveRenderTask>)
// does not match the return type (sk_sp<GrRenderTask>).
return std::move(resolveTask);
}
void GrTextureResolveRenderTask::gatherProxyIntervals(GrResourceAllocator* alloc) const {
// This renderTask doesn't have "normal" ops. In this case we still need to add an interval (so
// fEndOfOpListOpIndices will remain in sync), so we create a fake op# to capture the fact that
// we manipulate fTarget.
alloc->addInterval(fTarget.get(), alloc->curOp(), alloc->curOp(),
GrResourceAllocator::ActualUse::kYes);
alloc->incOps();
}
bool GrTextureResolveRenderTask::onExecute(GrOpFlushState* flushState) {
GrTexture* texture = fTarget->peekTexture();
SkASSERT(texture);
if (GrTextureResolveFlags::kMipMaps & fResolveFlags) {
SkASSERT(texture->texturePriv().mipMapsAreDirty());
flushState->gpu()->regenerateMipMapLevels(texture);
}
return true;
}

View File

@ -0,0 +1,37 @@
/*
* Copyright 2019 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrTextureResolveRenderTask_DEFINED
#define GrTextureResolveRenderTask_DEFINED
#include "src/gpu/GrRenderTask.h"
class GrTextureResolveRenderTask final : public GrRenderTask {
public:
static sk_sp<GrRenderTask> Make(
sk_sp<GrTextureProxy>, GrTextureResolveFlags, const GrCaps&);
private:
GrTextureResolveRenderTask(sk_sp<GrTextureProxy> textureProxy, GrTextureResolveFlags flags)
: GrRenderTask(std::move(textureProxy))
, fResolveFlags(flags) {
SkASSERT(GrTextureResolveFlags::kNone != fResolveFlags);
}
void onPrepare(GrOpFlushState*) override {}
bool onIsUsed(GrSurfaceProxy* proxy) const override {
SkASSERT(proxy != fTarget.get()); // This case should be handled by GrRenderTask.
return false;
}
void handleInternalAllocationFailure() override {}
void gatherProxyIntervals(GrResourceAllocator*) const override;
bool onExecute(GrOpFlushState*) override;
const GrTextureResolveFlags fResolveFlags;
};
#endif

View File

@ -1859,17 +1859,24 @@ void GrGLGpu::resolveAndGenerateMipMapsForProcessorTextures(
int numPrimitiveProcessorTextureSets) {
auto genLevelsIfNeeded = [this](GrTexture* tex, const GrSamplerState& sampler) {
SkASSERT(tex);
if (sampler.filter() == GrSamplerState::Filter::kMipMap &&
tex->texturePriv().mipMapped() == GrMipMapped::kYes &&
tex->texturePriv().mipMapsAreDirty()) {
SkASSERT(this->caps()->mipMapSupport());
this->regenerateMipMapLevels(static_cast<GrGLTexture*>(tex));
SkASSERT(!tex->asRenderTarget() || !tex->asRenderTarget()->needsResolve());
} else if (auto* rt = tex->asRenderTarget()) {
if (rt->needsResolve()) {
this->resolveRenderTarget(rt);
auto* rt = tex->asRenderTarget();
if (rt && rt->needsResolve()) {
this->resolveRenderTarget(rt);
// TEMPORARY: MSAA resolve will have dirtied mipmaps. This goes away once we switch
// to resolving MSAA from the opList as well.
if (GrSamplerState::Filter::kMipMap == sampler.filter() &&
(tex->width() != 1 || tex->height() != 1)) {
SkASSERT(tex->texturePriv().mipMapped() == GrMipMapped::kYes);
SkASSERT(tex->texturePriv().mipMapsAreDirty());
this->regenerateMipMapLevels(tex);
}
}
// Ensure mipmaps were all resolved ahead of time by the opList.
if (GrSamplerState::Filter::kMipMap == sampler.filter() &&
(tex->width() != 1 || tex->height() != 1)) {
SkASSERT(tex->texturePriv().mipMapped() == GrMipMapped::kYes);
SkASSERT(!tex->texturePriv().mipMapsAreDirty());
}
};
for (int set = 0, tex = 0; set < numPrimitiveProcessorTextureSets; ++set) {

View File

@ -744,17 +744,23 @@ void GrVkGpuRTCommandBuffer::onDraw(const GrPrimitiveProcessor& primProc,
GrVkTexture* vkTexture = static_cast<GrVkTexture*>(texture);
// We may need to resolve the texture first if it is also a render target
GrVkRenderTarget* texRT = static_cast<GrVkRenderTarget*>(vkTexture->asRenderTarget());
if (texRT) {
if (texRT && texRT->needsResolve()) {
fGpu->resolveRenderTargetNoFlush(texRT);
// TEMPORARY: MSAA resolve will have dirtied mipmaps. This goes away once we switch
// to resolving MSAA from the opList as well.
if (GrSamplerState::Filter::kMipMap == filter &&
(vkTexture->width() != 1 || vkTexture->height() != 1)) {
SkASSERT(vkTexture->texturePriv().mipMapped() == GrMipMapped::kYes);
SkASSERT(vkTexture->texturePriv().mipMapsAreDirty());
fGpu->regenerateMipMapLevels(vkTexture);
}
}
// Check if we need to regenerate any mip maps
// Ensure mip maps were all resolved ahead of time by the opList.
if (GrSamplerState::Filter::kMipMap == filter &&
(vkTexture->width() != 1 || vkTexture->height() != 1)) {
SkASSERT(vkTexture->texturePriv().mipMapped() == GrMipMapped::kYes);
if (vkTexture->texturePriv().mipMapsAreDirty()) {
fGpu->regenerateMipMapLevels(vkTexture);
}
SkASSERT(!vkTexture->texturePriv().mipMapsAreDirty());
}
cbInfo.fSampledTextures.push_back(vkTexture);

View File

@ -14,6 +14,7 @@
#include "include/gpu/GrContext.h"
#include "src/gpu/GrBackendTextureImageGenerator.h"
#include "src/gpu/GrContextPriv.h"
#include "src/gpu/GrDrawingManager.h"
#include "src/gpu/GrGpu.h"
#include "src/gpu/GrRenderTargetContext.h"
#include "src/gpu/GrSemaphore.h"
@ -336,3 +337,112 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(Gr1x1TextureMipMappedTest, reporter, ctxInfo)
surface->flush();
}
// Create a new render target and draw 'mipmapProxy' into it using the provided 'filter'.
static sk_sp<GrRenderTargetContext> draw_mipmap_into_new_render_target(
GrDrawingManager* drawingManager, GrProxyProvider* proxyProvider, GrColorType colorType,
sk_sp<GrTextureProxy> mipmapProxy, GrSamplerState::Filter filter) {
GrSurfaceDesc desc;
desc.fWidth = 1;
desc.fHeight = 1;
desc.fConfig = mipmapProxy->config();
sk_sp<GrSurfaceProxy> renderTarget = proxyProvider->createProxy(
mipmapProxy->backendFormat(), desc, GrRenderable::kYes, 1, kTopLeft_GrSurfaceOrigin,
SkBackingFit::kApprox, SkBudgeted::kYes, GrProtected::kNo);
sk_sp<GrRenderTargetContext> rtc = drawingManager->makeRenderTargetContext(
std::move(renderTarget), colorType, nullptr, nullptr, true);
rtc->drawTexture(GrNoClip(), mipmapProxy, filter, SkBlendMode::kSrcOver, {1,1,1,1},
SkRect::MakeWH(4, 4), SkRect::MakeWH(1,1), GrAA::kYes, GrQuadAAFlags::kAll,
SkCanvas::kFast_SrcRectConstraint, SkMatrix::I(), nullptr);
return rtc;
}
// Test that two opLists using the same mipmaps both depend on the same GrTextureResolveRenderTask.
DEF_GPUTEST_FOR_ALL_CONTEXTS(GrManyDependentsMipMappedTest, reporter, ctxInfo) {
GrContext* context = ctxInfo.grContext();
if (!context->priv().caps()->mipMapSupport()) {
return;
}
GrBackendFormat format = context->defaultBackendFormat(
kRGBA_8888_SkColorType, GrRenderable::kYes);
GrPixelConfig config = kRGBA_8888_GrPixelConfig;
GrColorType colorType = GrColorType::kRGBA_8888;
GrDrawingManager* drawingManager = context->priv().drawingManager();
GrProxyProvider* proxyProvider = context->priv().proxyProvider();
// Create a mipmapped render target.
GrSurfaceDesc desc;
desc.fWidth = 4;
desc.fHeight = 4;
desc.fConfig = config;
sk_sp<GrTextureProxy> mipmapProxy = proxyProvider->createMipMapProxy(
format, desc, GrRenderable::kYes, 1, kTopLeft_GrSurfaceOrigin, SkBudgeted::kYes,
GrProtected::kNo);
// Render something to dirty the mips.
sk_sp<GrRenderTargetContext> mipmapRTC = drawingManager->makeRenderTargetContext(
mipmapProxy, colorType, nullptr, nullptr, true);
mipmapRTC->clear(nullptr, {.1f,.2f,.3f,.4f}, GrRenderTargetContext::CanClearFullscreen::kYes);
REPORTER_ASSERT(reporter, mipmapProxy->mipMapsAreDirty());
REPORTER_ASSERT(reporter, mipmapProxy->getLastRenderTask());
// mipmapProxy's last render task should just be the opList containing the clear at this point.
REPORTER_ASSERT(
reporter, mipmapRTC->testingOnly_PeekLastOpList() == mipmapProxy->getLastRenderTask());
// Draw the dirty mipmap texture into a render target.
sk_sp<GrRenderTargetContext> rtc1 = draw_mipmap_into_new_render_target(
drawingManager, proxyProvider, colorType, mipmapProxy, GrSamplerState::Filter::kMipMap);
// Make sure the texture's mipmaps are now clean, and its last render task has switched from the
// opList that drew to it, to the task that resolved its mips.
GrRenderTask* initialMipmapRegenTask = mipmapProxy->getLastRenderTask();
REPORTER_ASSERT(reporter, initialMipmapRegenTask);
REPORTER_ASSERT(reporter, initialMipmapRegenTask != mipmapRTC->testingOnly_PeekLastOpList());
REPORTER_ASSERT(
reporter, rtc1->testingOnly_PeekLastOpList()->dependsOn(initialMipmapRegenTask));
REPORTER_ASSERT(reporter, !mipmapProxy->mipMapsAreDirty());
SkASSERT(!mipmapProxy->mipMapsAreDirty());
// Draw the now-clean mipmap texture into a second target.
sk_sp<GrRenderTargetContext> rtc2 = draw_mipmap_into_new_render_target(
drawingManager, proxyProvider, colorType, mipmapProxy, GrSamplerState::Filter::kMipMap);
// Make sure the mipmap texture still has the same regen task.
REPORTER_ASSERT(reporter, mipmapProxy->getLastRenderTask() == initialMipmapRegenTask);
REPORTER_ASSERT(
reporter, rtc2->testingOnly_PeekLastOpList()->dependsOn(initialMipmapRegenTask));
SkASSERT(!mipmapProxy->mipMapsAreDirty());
// Reset everything so we can go again, this time with the first draw not mipmapped.
context->flush();
// Render something to dirty the mips.
mipmapRTC->clear(nullptr, {.1f,.2f,.3f,.4f}, GrRenderTargetContext::CanClearFullscreen::kYes);
REPORTER_ASSERT(reporter, mipmapProxy->mipMapsAreDirty());
REPORTER_ASSERT(reporter, mipmapProxy->getLastRenderTask());
// mipmapProxy's last render task should just be the opList containing the clear at this point.
REPORTER_ASSERT(
reporter, mipmapRTC->testingOnly_PeekLastOpList() == mipmapProxy->getLastRenderTask());
// Draw the dirty mipmap texture into a render target, but don't do mipmap filtering.
rtc1 = draw_mipmap_into_new_render_target(
drawingManager, proxyProvider, colorType, mipmapProxy, GrSamplerState::Filter::kBilerp);
// Make sure the texture's mipmaps are still dirty, and its last render task has not changed.
REPORTER_ASSERT(reporter, mipmapProxy->mipMapsAreDirty());
REPORTER_ASSERT(
reporter, mipmapRTC->testingOnly_PeekLastOpList() == mipmapProxy->getLastRenderTask());
// Draw the stil-dirty mipmap texture into a second target.
rtc2 = draw_mipmap_into_new_render_target(
drawingManager, proxyProvider, colorType, mipmapProxy, GrSamplerState::Filter::kMipMap);
// Make sure the mipmap texture now has a new last render task.
REPORTER_ASSERT(reporter, mipmapProxy->getLastRenderTask());
REPORTER_ASSERT(reporter,
mipmapRTC->testingOnly_PeekLastOpList() != mipmapProxy->getLastRenderTask());
REPORTER_ASSERT(reporter,
rtc2->testingOnly_PeekLastOpList()->dependsOn(mipmapProxy->getLastRenderTask()));
SkASSERT(!mipmapProxy->mipMapsAreDirty());
}