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:
parent
19db24059b
commit
fe19203eb7
@ -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",
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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() {
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
64
src/gpu/GrTextureResolveRenderTask.cpp
Normal file
64
src/gpu/GrTextureResolveRenderTask.cpp
Normal 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;
|
||||
}
|
37
src/gpu/GrTextureResolveRenderTask.h
Normal file
37
src/gpu/GrTextureResolveRenderTask.h
Normal 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
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user