Reland "Initiate MSAA resolves during DAG generation"
This is a reland of 804f6a0fe7
Original change's description:
> Initiate MSAA resolves during DAG generation
>
> Adds an "fIsMSAADirty" flag to GrRenderTargetProxy and switches to
> resolving MSAA in GrTextureResolveRenderTask. This completes our push
> to resolve textures outside of render passes.
>
> For the time being, we only store a dirty flag on the proxy and still
> rely on the GrRenderTarget itself to track the actual dirty rect. This
> will be followed by a CL that moves the dirty rect out of
> GrRenderTarget and into the proxy.
>
> Bug: skia:
> Change-Id: I21219a58028bdb4590940210e565133093cd34b3
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/235672
> Commit-Queue: Chris Dalton <csmartdalton@google.com>
> Reviewed-by: Robert Phillips <robertphillips@google.com>
Bug: skia:
Change-Id: I805b3af1404eb7919ae937cff3dfa97921e32c69
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/237482
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
This commit is contained in:
parent
095f5b4de8
commit
4ece96d76d
@ -1464,17 +1464,6 @@ 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) {
|
||||
|
@ -300,6 +300,10 @@ GrSemaphoresSubmitted GrDrawingManager::flush(GrSurfaceProxy* proxies[], int num
|
||||
[](GrSurfaceProxy* p, GrMipMapped mipMapped) {
|
||||
SkASSERT(!p->asTextureProxy() || !p->asTextureProxy()->texPriv().isDeferred());
|
||||
SkASSERT(GrSurfaceProxy::LazyState::kNot == p->lazyInstantiationState());
|
||||
if (p->requiresManualMSAAResolve()) {
|
||||
// The onFlush callback is responsible for ensuring MSAA gets resolved.
|
||||
SkASSERT(p->asRenderTargetProxy() && !p->asRenderTargetProxy()->isMSAADirty());
|
||||
}
|
||||
if (GrMipMapped::kYes == mipMapped) {
|
||||
// The onFlush callback is responsible for regenerating mips if needed.
|
||||
SkASSERT(p->asTextureProxy() && !p->asTextureProxy()->mipMapsAreDirty());
|
||||
@ -515,19 +519,30 @@ GrSemaphoresSubmitted GrDrawingManager::flushSurfaces(GrSurfaceProxy* proxies[],
|
||||
GrSemaphoresSubmitted result = this->flush(proxies, numProxies, access, info,
|
||||
GrPrepareForExternalIORequests());
|
||||
for (int i = 0; i < numProxies; ++i) {
|
||||
if (!proxies[i]->isInstantiated()) {
|
||||
GrSurfaceProxy* proxy = proxies[i];
|
||||
if (!proxy->isInstantiated()) {
|
||||
return result;
|
||||
}
|
||||
GrSurface* surface = proxies[i]->peekSurface();
|
||||
if (auto* rt = surface->asRenderTarget()) {
|
||||
gpu->resolveRenderTarget(rt);
|
||||
// In the flushSurfaces case, we need to resolve MSAA immediately after flush. This is
|
||||
// because the client will call through to this method when drawing into a target created by
|
||||
// wrapBackendTextureAsRenderTarget, and will expect the original texture to be fully
|
||||
// resolved upon return.
|
||||
if (proxy->requiresManualMSAAResolve()) {
|
||||
auto* rtProxy = proxy->asRenderTargetProxy();
|
||||
SkASSERT(rtProxy);
|
||||
if (rtProxy->isMSAADirty()) {
|
||||
SkASSERT(rtProxy->peekRenderTarget());
|
||||
gpu->resolveRenderTarget(rtProxy->peekRenderTarget());
|
||||
rtProxy->markMSAAResolved();
|
||||
}
|
||||
}
|
||||
// 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 (auto* textureProxy = proxy->asTextureProxy()) {
|
||||
if (textureProxy->mipMapsAreDirty()) {
|
||||
SkASSERT(textureProxy->peekTexture());
|
||||
gpu->regenerateMipMapLevels(textureProxy->peekTexture());
|
||||
textureProxy->markMipMapsClean();
|
||||
}
|
||||
@ -666,20 +681,20 @@ sk_sp<GrOpsTask> GrDrawingManager::newOpsTask(sk_sp<GrRenderTargetProxy> rtp, bo
|
||||
}
|
||||
|
||||
GrRenderTask* GrDrawingManager::newTextureResolveRenderTask(
|
||||
sk_sp<GrTextureProxy> textureProxy, GrTextureResolveFlags flags, const GrCaps& caps) {
|
||||
SkDEBUGCODE(auto* previousTaskBeforeMipsResolve = textureProxy->getLastRenderTask();)
|
||||
sk_sp<GrSurfaceProxy> proxy, GrSurfaceProxy::ResolveFlags flags, const GrCaps& caps) {
|
||||
SkDEBUGCODE(auto* previousTaskBeforeMipsResolve = proxy->getLastRenderTask();)
|
||||
|
||||
// Unlike in the "new opsTask" case, we do not want to close the active opsTask, nor (if we are
|
||||
// in sorting and opsTask reduction mode) the render tasks that depend on the proxy's current
|
||||
// state. This is because those opsTasks 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.
|
||||
// the mipmapped version of 'proxy', they will then come to depend on the render task being
|
||||
// created here.
|
||||
//
|
||||
// Add the new textureResolveTask before the fActiveOpsTask (if not in
|
||||
// sorting/opsTask-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.
|
||||
auto* resolveTask = static_cast<GrTextureResolveRenderTask*>(fDAG.addBeforeLast(
|
||||
sk_make_sp<GrTextureResolveRenderTask>(std::move(textureProxy), flags)));
|
||||
sk_make_sp<GrTextureResolveRenderTask>(std::move(proxy), flags)));
|
||||
resolveTask->init(caps);
|
||||
|
||||
// GrTextureResolveRenderTask::init should have closed the texture proxy's previous task.
|
||||
|
@ -50,11 +50,11 @@ public:
|
||||
sk_sp<GrOpsTask> newOpsTask(sk_sp<GrRenderTargetProxy>, bool managedOpsTask);
|
||||
|
||||
// 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.
|
||||
// MSAA (depending on ResolveFlags). 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&);
|
||||
sk_sp<GrSurfaceProxy>, GrSurfaceProxy::ResolveFlags, const GrCaps&);
|
||||
|
||||
// Create a new render task which copies the pixels from the srcProxy into the dstBuffer. This
|
||||
// is used to support the asynchronous readback API. The srcRect is the region of the srcProxy
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "src/gpu/GrRecordingContextPriv.h"
|
||||
#include "src/gpu/GrRenderTargetContext.h"
|
||||
#include "src/gpu/GrSurfaceProxy.h"
|
||||
#include "src/gpu/GrTextureResolveRenderTask.h"
|
||||
|
||||
std::unique_ptr<GrRenderTargetContext> GrOnFlushResourceProvider::makeRenderTargetContext(
|
||||
sk_sp<GrSurfaceProxy> proxy, GrColorType colorType, sk_sp<SkColorSpace> colorSpace,
|
||||
@ -40,6 +41,19 @@ std::unique_ptr<GrRenderTargetContext> GrOnFlushResourceProvider::makeRenderTarg
|
||||
return renderTargetContext;
|
||||
}
|
||||
|
||||
void GrOnFlushResourceProvider::addTextureResolveTask(sk_sp<GrTextureProxy> textureProxy,
|
||||
GrSurfaceProxy::ResolveFlags resolveFlags) {
|
||||
// Since we are bypassing normal DAG operation, we need to ensure the textureProxy's last render
|
||||
// task gets closed before making a texture resolve task. makeClosed is what will mark msaa and
|
||||
// mipmaps dirty.
|
||||
if (GrRenderTask* renderTask = textureProxy->getLastRenderTask()) {
|
||||
renderTask->makeClosed(*this->caps());
|
||||
}
|
||||
auto task = static_cast<GrTextureResolveRenderTask*>(fDrawingMgr->fOnFlushRenderTasks.push_back(
|
||||
sk_make_sp<GrTextureResolveRenderTask>(std::move(textureProxy), resolveFlags)).get());
|
||||
task->init(*this->caps());
|
||||
}
|
||||
|
||||
bool GrOnFlushResourceProvider::assignUniqueKeyToProxy(const GrUniqueKey& key,
|
||||
GrTextureProxy* proxy) {
|
||||
auto proxyProvider = fDrawingMgr->getContext()->priv().proxyProvider();
|
||||
|
@ -63,10 +63,10 @@ class GrOnFlushResourceProvider {
|
||||
public:
|
||||
explicit GrOnFlushResourceProvider(GrDrawingManager* drawingMgr) : fDrawingMgr(drawingMgr) {}
|
||||
|
||||
std::unique_ptr<GrRenderTargetContext> makeRenderTargetContext(sk_sp<GrSurfaceProxy>,
|
||||
GrColorType,
|
||||
sk_sp<SkColorSpace>,
|
||||
const SkSurfaceProps*);
|
||||
std::unique_ptr<GrRenderTargetContext> makeRenderTargetContext(
|
||||
sk_sp<GrSurfaceProxy>, GrColorType, sk_sp<SkColorSpace>, const SkSurfaceProps*);
|
||||
|
||||
void addTextureResolveTask(sk_sp<GrTextureProxy>, GrSurfaceProxy::ResolveFlags);
|
||||
|
||||
// Proxy unique key management. See GrProxyProvider.h.
|
||||
bool assignUniqueKeyToProxy(const GrUniqueKey&, GrTextureProxy*);
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "src/gpu/GrPrimitiveProcessor.h"
|
||||
#include "src/gpu/GrRenderTarget.h"
|
||||
#include "src/gpu/GrRenderTargetPriv.h"
|
||||
#include "src/gpu/GrTexturePriv.h"
|
||||
|
||||
void GrOpsRenderPass::clear(const GrFixedClip& clip, const SkPMColor4f& color) {
|
||||
SkASSERT(fRenderTarget);
|
||||
@ -33,6 +34,51 @@ void GrOpsRenderPass::clearStencilClip(const GrFixedClip& clip, bool insideStenc
|
||||
this->onClearStencilClip(clip, insideStencilMask);
|
||||
}
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
static void assert_msaa_and_mips_are_resolved(
|
||||
const GrPrimitiveProcessor& primProc, const GrPipeline& pipeline,
|
||||
const GrPipeline::FixedDynamicState* fixedDynamicState,
|
||||
const GrPipeline::DynamicStateArrays* dynamicStateArrays, int meshCount) {
|
||||
auto assertResolved = [](GrTexture* tex, const GrSamplerState& sampler) {
|
||||
SkASSERT(tex);
|
||||
|
||||
// Ensure msaa was resolved ahead of time by the DAG.
|
||||
SkASSERT(!tex->asRenderTarget() || !tex->asRenderTarget()->needsResolve());
|
||||
|
||||
// Ensure mipmaps were all resolved ahead of time by the DAG.
|
||||
if (GrSamplerState::Filter::kMipMap == sampler.filter() &&
|
||||
(tex->width() != 1 || tex->height() != 1)) {
|
||||
// There are some cases where we might be given a non-mipmapped texture with a mipmap
|
||||
// filter. See skbug.com/7094.
|
||||
SkASSERT(tex->texturePriv().mipMapped() != GrMipMapped::kYes ||
|
||||
!tex->texturePriv().mipMapsAreDirty());
|
||||
}
|
||||
};
|
||||
|
||||
if (dynamicStateArrays && dynamicStateArrays->fPrimitiveProcessorTextures) {
|
||||
for (int m = 0, i = 0; m < meshCount; ++m) {
|
||||
for (int s = 0; s < primProc.numTextureSamplers(); ++s, ++i) {
|
||||
auto* tex = dynamicStateArrays->fPrimitiveProcessorTextures[i]->peekTexture();
|
||||
assertResolved(tex, primProc.textureSampler(s).samplerState());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < primProc.numTextureSamplers(); ++i) {
|
||||
auto* tex = fixedDynamicState->fPrimitiveProcessorTextures[i]->peekTexture();
|
||||
assertResolved(tex, primProc.textureSampler(i).samplerState());
|
||||
}
|
||||
}
|
||||
|
||||
GrFragmentProcessor::Iter iter(pipeline);
|
||||
while (const GrFragmentProcessor* fp = iter.next()) {
|
||||
for (int i = 0; i < fp->numTextureSamplers(); ++i) {
|
||||
const auto& textureSampler = fp->textureSampler(i);
|
||||
assertResolved(textureSampler.peekTexture(), textureSampler.samplerState());
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool GrOpsRenderPass::draw(const GrPrimitiveProcessor& primProc, const GrPipeline& pipeline,
|
||||
const GrPipeline::FixedDynamicState* fixedDynamicState,
|
||||
const GrPipeline::DynamicStateArrays* dynamicStateArrays,
|
||||
@ -45,13 +91,12 @@ bool GrOpsRenderPass::draw(const GrPrimitiveProcessor& primProc, const GrPipelin
|
||||
SkASSERT(primProc.hasVertexAttributes() == meshes[i].hasVertexData());
|
||||
SkASSERT(primProc.hasInstanceAttributes() == meshes[i].hasInstanceData());
|
||||
}
|
||||
#endif
|
||||
|
||||
SkASSERT(!pipeline.isScissorEnabled() || fixedDynamicState ||
|
||||
(dynamicStateArrays && dynamicStateArrays->fScissorRects));
|
||||
|
||||
SkASSERT(!pipeline.isBad());
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
if (fixedDynamicState && fixedDynamicState->fPrimitiveProcessorTextures) {
|
||||
GrTextureProxy** processorProxies = fixedDynamicState->fPrimitiveProcessorTextures;
|
||||
for (int i = 0; i < primProc.numTextureSamplers(); ++i) {
|
||||
@ -80,6 +125,9 @@ bool GrOpsRenderPass::draw(const GrPrimitiveProcessor& primProc, const GrPipelin
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert_msaa_and_mips_are_resolved(
|
||||
primProc, pipeline, fixedDynamicState, dynamicStateArrays, meshCount);
|
||||
#endif
|
||||
|
||||
if (primProc.numVertexAttributes() > this->gpu()->caps()->maxVertexAttributes()) {
|
||||
|
@ -63,6 +63,19 @@ public:
|
||||
|
||||
bool wrapsVkSecondaryCB() const { return fWrapsVkSecondaryCB == WrapsVkSecondaryCB::kYes; }
|
||||
|
||||
void markMSAADirty() {
|
||||
SkASSERT(this->requiresManualMSAAResolve());
|
||||
fIsMSAADirty = true;
|
||||
}
|
||||
void markMSAAResolved() {
|
||||
SkASSERT(this->requiresManualMSAAResolve());
|
||||
fIsMSAADirty = false;
|
||||
}
|
||||
bool isMSAADirty() const {
|
||||
SkASSERT(!fIsMSAADirty || this->requiresManualMSAAResolve());
|
||||
return fIsMSAADirty;
|
||||
}
|
||||
|
||||
// TODO: move this to a priv class!
|
||||
bool refsWrappedObjects() const;
|
||||
|
||||
@ -128,6 +141,10 @@ private:
|
||||
int8_t fNumStencilSamples = 0;
|
||||
WrapsVkSecondaryCB fWrapsVkSecondaryCB;
|
||||
GrSwizzle fOutputSwizzle;
|
||||
// Indicates whether some sub-rectangle of the render target requires MSAA resolve. We currently
|
||||
// rely on the GrRenderTarget itself to track the actual dirty rect.
|
||||
// TODO: In the future, convert the flag to a dirty rect and quit tracking it in GrRenderTarget.
|
||||
bool fIsMSAADirty = false;
|
||||
// This is to fix issue in large comment above. Without the padding we end 6 bytes into a 16
|
||||
// byte range, so the GrTextureProxy ends up starting 8 byte aligned by not 16. We add the
|
||||
// padding here to get us right up to the 16 byte alignment (technically any padding of 3-10
|
||||
|
@ -51,6 +51,10 @@ void GrRenderTask::makeClosed(const GrCaps& caps) {
|
||||
}
|
||||
|
||||
if (ExpectedOutcome::kTargetDirty == this->onMakeClosed(caps)) {
|
||||
if (fTarget->requiresManualMSAAResolve()) {
|
||||
SkASSERT(fTarget->asRenderTargetProxy());
|
||||
fTarget->asRenderTargetProxy()->markMSAADirty();
|
||||
}
|
||||
GrTextureProxy* textureProxy = fTarget->asTextureProxy();
|
||||
if (textureProxy && GrMipMapped::kYes == textureProxy->mipMapped()) {
|
||||
textureProxy->markMipMapsDirty();
|
||||
@ -60,7 +64,6 @@ void GrRenderTask::makeClosed(const GrCaps& caps) {
|
||||
this->setFlag(kClosed_Flag);
|
||||
}
|
||||
|
||||
|
||||
void GrRenderTask::prepare(GrOpFlushState* flushState) {
|
||||
for (int i = 0; i < fDeferredProxies.count(); ++i) {
|
||||
fDeferredProxies[i]->texPriv().scheduleUpload(flushState);
|
||||
@ -93,8 +96,11 @@ void GrRenderTask::addDependency(GrSurfaceProxy* dependedOn, GrMipMapped mipMapp
|
||||
GrRenderTask* dependedOnTask = dependedOn->getLastRenderTask();
|
||||
|
||||
if (dependedOnTask == this) {
|
||||
// self-read - presumably for dst reads. We can't make it closed in the self-read case.
|
||||
// self-read - presumably for dst reads. We don't need to do anything in this case. The
|
||||
// XferProcessor will detect what is happening and insert a texture barrier.
|
||||
SkASSERT(GrMipMapped::kNo == mipMapped);
|
||||
// We should never attempt a self-read on a surface that has a separate MSAA renderbuffer.
|
||||
SkASSERT(!dependedOn->requiresManualMSAAResolve());
|
||||
SkASSERT(!dependedOn->asTextureProxy() ||
|
||||
!dependedOn->asTextureProxy()->texPriv().isDeferred());
|
||||
return;
|
||||
@ -107,6 +113,16 @@ void GrRenderTask::addDependency(GrSurfaceProxy* dependedOn, GrMipMapped mipMapp
|
||||
dependedOnTask->makeClosed(caps);
|
||||
}
|
||||
|
||||
auto resolveFlags = GrSurfaceProxy::ResolveFlags::kNone;
|
||||
|
||||
if (dependedOn->requiresManualMSAAResolve()) {
|
||||
auto* renderTargetProxy = dependedOn->asRenderTargetProxy();
|
||||
SkASSERT(renderTargetProxy);
|
||||
if (renderTargetProxy->isMSAADirty()) {
|
||||
resolveFlags |= GrSurfaceProxy::ResolveFlags::kMSAA;
|
||||
}
|
||||
}
|
||||
|
||||
GrTextureProxy* textureProxy = dependedOn->asTextureProxy();
|
||||
if (GrMipMapped::kYes == mipMapped) {
|
||||
SkASSERT(textureProxy);
|
||||
@ -114,26 +130,38 @@ void GrRenderTask::addDependency(GrSurfaceProxy* dependedOn, GrMipMapped mipMapp
|
||||
// There are some cases where we might be given a non-mipmapped texture with a mipmap
|
||||
// filter. See skbug.com/7094.
|
||||
mipMapped = GrMipMapped::kNo;
|
||||
} else if (textureProxy->mipMapsAreDirty()) {
|
||||
resolveFlags |= GrSurfaceProxy::ResolveFlags::kMipMaps;
|
||||
}
|
||||
}
|
||||
|
||||
// Does this proxy have mipmaps that need to be regenerated?
|
||||
if (GrMipMapped::kYes == mipMapped && textureProxy->mipMapsAreDirty()) {
|
||||
// Does this proxy have msaa to resolve and/or mipmaps to regenerate?
|
||||
if (GrSurfaceProxy::ResolveFlags::kNone != resolveFlags) {
|
||||
// Create a renderTask that resolves the texture's mipmap data.
|
||||
GrRenderTask* textureResolveTask = textureResolveManager.newTextureResolveRenderTask(
|
||||
sk_ref_sp(textureProxy), GrTextureResolveFlags::kMipMaps, caps);
|
||||
sk_ref_sp(dependedOn), resolveFlags, caps);
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
// GrTextureResolveRenderTask::init should have called addDependency (in this instance,
|
||||
// recursively) on the textureProxy.
|
||||
SkASSERT(!dependedOnTask || textureResolveTask->dependsOn(dependedOnTask));
|
||||
SkASSERT(!textureProxy->texPriv().isDeferred() ||
|
||||
textureResolveTask->fDeferredProxies.back() == textureProxy);
|
||||
// recursively) on the textureResolveTask.
|
||||
if (dependedOnTask) {
|
||||
SkASSERT(textureResolveTask->dependsOn(dependedOnTask));
|
||||
}
|
||||
if (textureProxy && textureProxy->texPriv().isDeferred()) {
|
||||
SkASSERT(textureResolveTask->fDeferredProxies.back() == textureProxy);
|
||||
}
|
||||
|
||||
// The GrTextureResolveRenderTask factory should have also marked the mipmaps clean, set the
|
||||
// The GrTextureResolveRenderTask factory should have also marked the proxy clean, set the
|
||||
// last renderTask on the textureProxy to textureResolveTask, and closed textureResolveTask.
|
||||
if (GrRenderTargetProxy* renderTargetProxy = dependedOn->asRenderTargetProxy()) {
|
||||
SkASSERT(!renderTargetProxy->isMSAADirty());
|
||||
}
|
||||
if (textureProxy) {
|
||||
SkASSERT(!textureProxy->mipMapsAreDirty());
|
||||
SkASSERT(textureProxy->getLastRenderTask() == textureResolveTask);
|
||||
}
|
||||
SkASSERT(dependedOn->getLastRenderTask() == textureResolveTask);
|
||||
SkASSERT(textureResolveTask->isClosed());
|
||||
#endif
|
||||
|
||||
// Fall through and add textureResolveTask as a dependency of "this".
|
||||
dependedOnTask = textureResolveTask;
|
||||
|
@ -370,6 +370,9 @@ sk_sp<GrTexture> GrResourceProvider::refScratchTexture(const GrSurfaceDesc& desc
|
||||
if (resource) {
|
||||
fGpu->stats()->incNumScratchTexturesReused();
|
||||
GrSurface* surface = static_cast<GrSurface*>(resource);
|
||||
if (GrRenderTarget* rt = surface->asRenderTarget()) {
|
||||
rt->flagAsResolved(); // Scratch textures always start without dirty MSAA.
|
||||
}
|
||||
return sk_sp<GrTexture>(surface->asTexture());
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,16 @@ class GrSurfaceProxy : public GrNonAtomicRef<GrSurfaceProxy> {
|
||||
public:
|
||||
virtual ~GrSurfaceProxy();
|
||||
|
||||
/**
|
||||
* Indicates "resolutions" that need to be done on a surface before its pixels can be accessed.
|
||||
* If both types of resolve are requested, the MSAA resolve will happen first.
|
||||
*/
|
||||
enum class ResolveFlags {
|
||||
kNone = 0,
|
||||
kMSAA = 1 << 0, // Blit and resolve an internal MSAA render buffer into the texture.
|
||||
kMipMaps = 1 << 1, // Regenerate all mipmap levels.
|
||||
};
|
||||
|
||||
/**
|
||||
* Some lazy proxy callbacks want to set their own (or no key) on the GrSurfaces they return.
|
||||
* Others want the GrSurface's key to be kept in sync with the proxy's key. This enum controls
|
||||
@ -416,4 +426,6 @@ private:
|
||||
GrRenderTask* fLastRenderTask;
|
||||
};
|
||||
|
||||
GR_MAKE_BITFIELD_CLASS_OPS(GrSurfaceProxy::ResolveFlags)
|
||||
|
||||
#endif
|
||||
|
@ -14,7 +14,6 @@
|
||||
class GrCaps;
|
||||
class GrDrawingManager;
|
||||
class GrRenderTask;
|
||||
class GrTextureProxy;
|
||||
|
||||
/*
|
||||
* This class is a shallow view of the drawing manager. It is passed to render tasks when setting up
|
||||
@ -26,10 +25,11 @@ public:
|
||||
explicit GrTextureResolveManager(GrDrawingManager* drawingManager)
|
||||
: fDrawingManager(drawingManager) {}
|
||||
|
||||
GrRenderTask* newTextureResolveRenderTask(
|
||||
sk_sp<GrTextureProxy> proxy, GrTextureResolveFlags flags, const GrCaps& caps) const {
|
||||
GrRenderTask* newTextureResolveRenderTask(sk_sp<GrSurfaceProxy> proxy,
|
||||
GrSurfaceProxy::ResolveFlags resolveFlags,
|
||||
const GrCaps& caps) const {
|
||||
SkASSERT(fDrawingManager);
|
||||
return fDrawingManager->newTextureResolveRenderTask(std::move(proxy), flags, caps);
|
||||
return fDrawingManager->newTextureResolveRenderTask(std::move(proxy), resolveFlags, caps);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -10,10 +10,25 @@
|
||||
#include "src/gpu/GrGpu.h"
|
||||
#include "src/gpu/GrMemoryPool.h"
|
||||
#include "src/gpu/GrOpFlushState.h"
|
||||
#include "src/gpu/GrRenderTarget.h"
|
||||
#include "src/gpu/GrResourceAllocator.h"
|
||||
#include "src/gpu/GrTexturePriv.h"
|
||||
|
||||
void GrTextureResolveRenderTask::init(const GrCaps& caps) {
|
||||
if (GrSurfaceProxy::ResolveFlags::kMSAA & fResolveFlags) {
|
||||
GrRenderTargetProxy* renderTargetProxy = fTarget->asRenderTargetProxy();
|
||||
SkASSERT(renderTargetProxy);
|
||||
SkASSERT(renderTargetProxy->isMSAADirty());
|
||||
renderTargetProxy->markMSAAResolved();
|
||||
}
|
||||
|
||||
if (GrSurfaceProxy::ResolveFlags::kMipMaps & fResolveFlags) {
|
||||
GrTextureProxy* textureProxy = fTarget->asTextureProxy();
|
||||
SkASSERT(GrMipMapped::kYes == textureProxy->mipMapped());
|
||||
SkASSERT(textureProxy->mipMapsAreDirty());
|
||||
textureProxy->markMipMapsClean();
|
||||
}
|
||||
|
||||
// Add the target as a dependency: We will read the existing contents of this texture while
|
||||
// generating mipmap levels and/or resolving MSAA.
|
||||
//
|
||||
@ -23,13 +38,6 @@ void GrTextureResolveRenderTask::init(const GrCaps& caps) {
|
||||
|
||||
// We only resolve the texture; nobody should try to do anything else with this opsTask.
|
||||
this->makeClosed(caps);
|
||||
|
||||
if (GrTextureResolveFlags::kMipMaps & fResolveFlags) {
|
||||
GrTextureProxy* textureProxy = fTarget->asTextureProxy();
|
||||
SkASSERT(GrMipMapped::kYes == textureProxy->mipMapped());
|
||||
SkASSERT(textureProxy->mipMapsAreDirty());
|
||||
textureProxy->markMipMapsClean();
|
||||
}
|
||||
}
|
||||
|
||||
void GrTextureResolveRenderTask::gatherProxyIntervals(GrResourceAllocator* alloc) const {
|
||||
@ -42,13 +50,22 @@ void GrTextureResolveRenderTask::gatherProxyIntervals(GrResourceAllocator* alloc
|
||||
}
|
||||
|
||||
bool GrTextureResolveRenderTask::onExecute(GrOpFlushState* flushState) {
|
||||
// Resolve msaa before regenerating mipmaps.
|
||||
if (GrSurfaceProxy::ResolveFlags::kMSAA & fResolveFlags) {
|
||||
GrRenderTarget* renderTarget = fTarget->peekRenderTarget();
|
||||
SkASSERT(renderTarget);
|
||||
if (renderTarget->needsResolve()) {
|
||||
flushState->gpu()->resolveRenderTarget(renderTarget);
|
||||
}
|
||||
}
|
||||
|
||||
if (GrSurfaceProxy::ResolveFlags::kMipMaps & fResolveFlags) {
|
||||
GrTexture* texture = fTarget->peekTexture();
|
||||
SkASSERT(texture);
|
||||
|
||||
if ((GrTextureResolveFlags::kMipMaps & fResolveFlags) &&
|
||||
texture->texturePriv().mipMapsAreDirty()) {
|
||||
if (texture->texturePriv().mipMapsAreDirty()) {
|
||||
flushState->gpu()->regenerateMipMapLevels(texture);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -12,10 +12,14 @@
|
||||
|
||||
class GrTextureResolveRenderTask final : public GrRenderTask {
|
||||
public:
|
||||
GrTextureResolveRenderTask(sk_sp<GrTextureProxy> textureProxy, GrTextureResolveFlags flags)
|
||||
: GrRenderTask(std::move(textureProxy))
|
||||
, fResolveFlags(flags) {
|
||||
SkASSERT(GrTextureResolveFlags::kNone != fResolveFlags);
|
||||
GrTextureResolveRenderTask(sk_sp<GrSurfaceProxy> proxy,
|
||||
GrSurfaceProxy::ResolveFlags resolveFlags)
|
||||
: GrRenderTask(std::move(proxy))
|
||||
, fResolveFlags(resolveFlags) {
|
||||
// Ensure the last render task that operated on the target is closed. That's where msaa and
|
||||
// mipmaps should have been marked dirty.
|
||||
SkASSERT(!fTarget->getLastRenderTask() || fTarget->getLastRenderTask()->isClosed());
|
||||
SkASSERT(GrSurfaceProxy::ResolveFlags::kNone != fResolveFlags);
|
||||
}
|
||||
|
||||
void init(const GrCaps&);
|
||||
@ -40,7 +44,7 @@ private:
|
||||
void visitProxies_debugOnly(const VisitSurfaceProxyFunc& fn) const override {}
|
||||
#endif
|
||||
|
||||
const GrTextureResolveFlags fResolveFlags;
|
||||
const GrSurfaceProxy::ResolveFlags fResolveFlags;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -588,6 +588,10 @@ bool GrCCPerFlushResources::finalize(GrOnFlushResourceProvider* onFlushRP) {
|
||||
atlas->getStrokeBatchID(), atlas->drawBounds());
|
||||
}
|
||||
rtc->addDrawOp(GrNoClip(), std::move(op));
|
||||
if (rtc->proxy()->requiresManualMSAAResolve()) {
|
||||
onFlushRP->addTextureResolveTask(sk_ref_sp(rtc->proxy()->asTextureProxy()),
|
||||
GrSurfaceProxy::ResolveFlags::kMSAA);
|
||||
}
|
||||
}
|
||||
|
||||
SkASSERT(atlas->getEndStencilResolveInstance() >= baseStencilResolveInstance);
|
||||
|
@ -1868,51 +1868,6 @@ void GrGLGpu::disableWindowRectangles() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void GrGLGpu::resolveAndGenerateMipMapsForProcessorTextures(
|
||||
const GrPrimitiveProcessor& primProc,
|
||||
const GrPipeline& pipeline,
|
||||
const GrTextureProxy* const primProcTextures[],
|
||||
int numPrimitiveProcessorTextureSets) {
|
||||
auto genLevelsIfNeeded = [this](GrTexture* tex, const GrSamplerState& sampler) {
|
||||
SkASSERT(tex);
|
||||
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 opsTask 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 opsTask.
|
||||
if (GrSamplerState::Filter::kMipMap == sampler.filter() &&
|
||||
(tex->width() != 1 || tex->height() != 1)) {
|
||||
// There are some cases where we might be given a non-mipmapped texture with a mipmap
|
||||
// filter. See skbug.com/7094.
|
||||
SkASSERT(tex->texturePriv().mipMapped() != GrMipMapped::kYes ||
|
||||
!tex->texturePriv().mipMapsAreDirty());
|
||||
}
|
||||
};
|
||||
|
||||
for (int set = 0, tex = 0; set < numPrimitiveProcessorTextureSets; ++set) {
|
||||
for (int sampler = 0; sampler < primProc.numTextureSamplers(); ++sampler, ++tex) {
|
||||
GrTexture* texture = primProcTextures[tex]->peekTexture();
|
||||
genLevelsIfNeeded(texture, primProc.textureSampler(sampler).samplerState());
|
||||
}
|
||||
}
|
||||
|
||||
GrFragmentProcessor::Iter iter(pipeline);
|
||||
while (const GrFragmentProcessor* fp = iter.next()) {
|
||||
for (int i = 0; i < fp->numTextureSamplers(); ++i) {
|
||||
const auto& textureSampler = fp->textureSampler(i);
|
||||
genLevelsIfNeeded(textureSampler.peekTexture(), textureSampler.samplerState());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GrGLGpu::flushGLState(GrRenderTarget* renderTarget,
|
||||
GrSurfaceOrigin origin,
|
||||
const GrPrimitiveProcessor& primProc,
|
||||
@ -1921,28 +1876,23 @@ bool GrGLGpu::flushGLState(GrRenderTarget* renderTarget,
|
||||
const GrPipeline::DynamicStateArrays* dynamicStateArrays,
|
||||
int dynamicStateArraysLength,
|
||||
bool willDrawPoints) {
|
||||
const GrTextureProxy* const* primProcProxiesForMipRegen = nullptr;
|
||||
const GrTextureProxy* const* primProcProxies = nullptr;
|
||||
const GrTextureProxy* const* primProcProxiesToBind = nullptr;
|
||||
int numPrimProcTextureSets = 1; // number of texture per prim proc sampler.
|
||||
if (dynamicStateArrays && dynamicStateArrays->fPrimitiveProcessorTextures) {
|
||||
primProcProxiesForMipRegen = dynamicStateArrays->fPrimitiveProcessorTextures;
|
||||
numPrimProcTextureSets = dynamicStateArraysLength;
|
||||
primProcProxies = dynamicStateArrays->fPrimitiveProcessorTextures;
|
||||
} else if (fixedDynamicState && fixedDynamicState->fPrimitiveProcessorTextures) {
|
||||
primProcProxiesForMipRegen = fixedDynamicState->fPrimitiveProcessorTextures;
|
||||
primProcProxies = fixedDynamicState->fPrimitiveProcessorTextures;
|
||||
primProcProxiesToBind = fixedDynamicState->fPrimitiveProcessorTextures;
|
||||
}
|
||||
|
||||
SkASSERT(SkToBool(primProcProxiesForMipRegen) == SkToBool(primProc.numTextureSamplers()));
|
||||
SkASSERT(SkToBool(primProcProxies) == SkToBool(primProc.numTextureSamplers()));
|
||||
|
||||
sk_sp<GrGLProgram> program(fProgramCache->refProgram(this, renderTarget, origin, primProc,
|
||||
primProcProxiesForMipRegen,
|
||||
pipeline, willDrawPoints));
|
||||
sk_sp<GrGLProgram> program(fProgramCache->refProgram(
|
||||
this, renderTarget, origin, primProc, primProcProxies, pipeline, willDrawPoints));
|
||||
if (!program) {
|
||||
GrCapsDebugf(this->caps(), "Failed to create program!\n");
|
||||
return false;
|
||||
}
|
||||
this->resolveAndGenerateMipMapsForProcessorTextures(
|
||||
primProc, pipeline, primProcProxiesForMipRegen, numPrimProcTextureSets);
|
||||
|
||||
this->flushProgram(std::move(program));
|
||||
|
||||
|
@ -270,15 +270,6 @@ private:
|
||||
// binds texture unit in GL
|
||||
void setTextureUnit(int unitIdx);
|
||||
|
||||
/**
|
||||
* primitiveProcessorTextures must contain GrPrimitiveProcessor::numTextureSamplers() *
|
||||
* numPrimitiveProcessorTextureSets entries.
|
||||
*/
|
||||
void resolveAndGenerateMipMapsForProcessorTextures(
|
||||
const GrPrimitiveProcessor&, const GrPipeline&,
|
||||
const GrTextureProxy* const primitiveProcessorTextures[],
|
||||
int numPrimitiveProcessorTextureSets);
|
||||
|
||||
// Flushes state from GrPipeline to GL. Returns false if the state couldn't be set.
|
||||
// willDrawPoints must be true if point primitives will be rendered after setting the GL state.
|
||||
// If DynamicStateArrays is not null then dynamicStateArraysLength is the number of dynamic
|
||||
|
@ -115,7 +115,7 @@ private:
|
||||
|
||||
bool onRegenerateMipMapLevels(GrTexture*) override { return true; }
|
||||
|
||||
void onResolveRenderTarget(GrRenderTarget* target) override { return; }
|
||||
void onResolveRenderTarget(GrRenderTarget* target) override { target->flagAsResolved(); }
|
||||
|
||||
void onFinishFlush(GrSurfaceProxy*[], int n, SkSurface::BackendSurfaceAccess access,
|
||||
const GrFlushInfo& info, const GrPrepareForExternalIORequests&) override {
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include "src/gpu/GrColor.h"
|
||||
#include "src/gpu/GrFixedClip.h"
|
||||
#include "src/gpu/GrRenderTargetPriv.h"
|
||||
#include "src/gpu/GrTexturePriv.h"
|
||||
#include "src/gpu/mtl/GrMtlCommandBuffer.h"
|
||||
#include "src/gpu/mtl/GrMtlPipelineState.h"
|
||||
#include "src/gpu/mtl/GrMtlPipelineStateBuilder.h"
|
||||
@ -93,45 +92,6 @@ void GrMtlOpsRenderPass::onDraw(const GrPrimitiveProcessor& primProc,
|
||||
return;
|
||||
}
|
||||
|
||||
auto prepareSampledImage = [&](GrTexture* texture, GrSamplerState::Filter filter) {
|
||||
GrMtlTexture* mtlTexture = static_cast<GrMtlTexture*>(texture);
|
||||
// We may need to resolve the texture first if it is also a render target
|
||||
GrMtlRenderTarget* texRT = static_cast<GrMtlRenderTarget*>(mtlTexture->asRenderTarget());
|
||||
if (texRT) {
|
||||
fGpu->resolveRenderTargetNoFlush(texRT);
|
||||
}
|
||||
|
||||
// Check if we need to regenerate any mip maps
|
||||
if (GrSamplerState::Filter::kMipMap == filter &&
|
||||
(texture->width() != 1 || texture->height() != 1)) {
|
||||
SkASSERT(texture->texturePriv().mipMapped() == GrMipMapped::kYes);
|
||||
if (texture->texturePriv().mipMapsAreDirty()) {
|
||||
fGpu->regenerateMipMapLevels(texture);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (dynamicStateArrays && dynamicStateArrays->fPrimitiveProcessorTextures) {
|
||||
for (int m = 0, i = 0; m < meshCount; ++m) {
|
||||
for (int s = 0; s < primProc.numTextureSamplers(); ++s, ++i) {
|
||||
auto texture = dynamicStateArrays->fPrimitiveProcessorTextures[i]->peekTexture();
|
||||
prepareSampledImage(texture, primProc.textureSampler(s).samplerState().filter());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < primProc.numTextureSamplers(); ++i) {
|
||||
auto texture = fixedDynamicState->fPrimitiveProcessorTextures[i]->peekTexture();
|
||||
prepareSampledImage(texture, primProc.textureSampler(i).samplerState().filter());
|
||||
}
|
||||
}
|
||||
GrFragmentProcessor::Iter iter(pipeline);
|
||||
while (const GrFragmentProcessor* fp = iter.next()) {
|
||||
for (int i = 0; i < fp->numTextureSamplers(); ++i) {
|
||||
const GrFragmentProcessor::TextureSampler& sampler = fp->textureSampler(i);
|
||||
prepareSampledImage(sampler.peekTexture(), sampler.samplerState().filter());
|
||||
}
|
||||
}
|
||||
|
||||
GrPrimitiveType primitiveType = meshes[0].primitiveType();
|
||||
GrMtlPipelineState* pipelineState = this->prepareDrawState(primProc, pipeline,
|
||||
fixedDynamicState, primitiveType);
|
||||
|
@ -16,7 +16,6 @@
|
||||
#include "src/gpu/GrOpFlushState.h"
|
||||
#include "src/gpu/GrPipeline.h"
|
||||
#include "src/gpu/GrRenderTargetPriv.h"
|
||||
#include "src/gpu/GrTexturePriv.h"
|
||||
#include "src/gpu/vk/GrVkCommandBuffer.h"
|
||||
#include "src/gpu/vk/GrVkCommandPool.h"
|
||||
#include "src/gpu/vk/GrVkGpu.h"
|
||||
@ -589,42 +588,16 @@ void GrVkOpsRenderPass::onDraw(const GrPrimitiveProcessor& primProc,
|
||||
|
||||
CommandBufferInfo& cbInfo = fCommandBufferInfos[fCurrentCmdInfo];
|
||||
|
||||
auto prepareSampledImage = [&](GrTexture* texture, GrSamplerState::Filter filter) {
|
||||
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 && texRT->needsResolve()) {
|
||||
fGpu->resolveRenderTargetNoFlush(texRT);
|
||||
// TEMPORARY: MSAA resolve will have dirtied mipmaps. This goes away once we switch
|
||||
// to resolving MSAA from the opsTask 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);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure mip maps were all resolved ahead of time by the opsTask.
|
||||
if (GrSamplerState::Filter::kMipMap == filter &&
|
||||
(vkTexture->width() != 1 || vkTexture->height() != 1)) {
|
||||
SkASSERT(vkTexture->texturePriv().mipMapped() == GrMipMapped::kYes);
|
||||
SkASSERT(!vkTexture->texturePriv().mipMapsAreDirty());
|
||||
}
|
||||
};
|
||||
|
||||
if (dynamicStateArrays && dynamicStateArrays->fPrimitiveProcessorTextures) {
|
||||
for (int m = 0, i = 0; m < meshCount; ++m) {
|
||||
for (int s = 0; s < primProc.numTextureSamplers(); ++s, ++i) {
|
||||
auto texture = dynamicStateArrays->fPrimitiveProcessorTextures[i]->peekTexture();
|
||||
prepareSampledImage(texture, primProc.textureSampler(s).samplerState().filter());
|
||||
this->appendSampledTexture(texture);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < primProc.numTextureSamplers(); ++i) {
|
||||
auto texture = fixedDynamicState->fPrimitiveProcessorTextures[i]->peekTexture();
|
||||
prepareSampledImage(texture, primProc.textureSampler(i).samplerState().filter());
|
||||
this->appendSampledTexture(texture);
|
||||
}
|
||||
}
|
||||
@ -632,7 +605,6 @@ void GrVkOpsRenderPass::onDraw(const GrPrimitiveProcessor& primProc,
|
||||
while (const GrFragmentProcessor* fp = iter.next()) {
|
||||
for (int i = 0; i < fp->numTextureSamplers(); ++i) {
|
||||
const GrFragmentProcessor::TextureSampler& sampler = fp->textureSampler(i);
|
||||
prepareSampledImage(sampler.peekTexture(), sampler.samplerState().filter());
|
||||
this->appendSampledTexture(sampler.peekTexture());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user