diff --git a/include/private/GrTypesPriv.h b/include/private/GrTypesPriv.h index 037dce8f40..92c49faa13 100644 --- a/include/private/GrTypesPriv.h +++ b/include/private/GrTypesPriv.h @@ -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) { diff --git a/src/gpu/GrDrawingManager.cpp b/src/gpu/GrDrawingManager.cpp index 7e05f0bca8..4776484048 100644 --- a/src/gpu/GrDrawingManager.cpp +++ b/src/gpu/GrDrawingManager.cpp @@ -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 GrDrawingManager::newOpsTask(sk_sp rtp, bo } GrRenderTask* GrDrawingManager::newTextureResolveRenderTask( - sk_sp textureProxy, GrTextureResolveFlags flags, const GrCaps& caps) { - SkDEBUGCODE(auto* previousTaskBeforeMipsResolve = textureProxy->getLastRenderTask();) + sk_sp 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(fDAG.addBeforeLast( - sk_make_sp(std::move(textureProxy), flags))); + sk_make_sp(std::move(proxy), flags))); resolveTask->init(caps); // GrTextureResolveRenderTask::init should have closed the texture proxy's previous task. diff --git a/src/gpu/GrDrawingManager.h b/src/gpu/GrDrawingManager.h index 240c5463c0..a1e3c6d520 100644 --- a/src/gpu/GrDrawingManager.h +++ b/src/gpu/GrDrawingManager.h @@ -50,11 +50,11 @@ public: sk_sp newOpsTask(sk_sp, 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, GrTextureResolveFlags, const GrCaps&); + sk_sp, 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 diff --git a/src/gpu/GrOnFlushResourceProvider.cpp b/src/gpu/GrOnFlushResourceProvider.cpp index 8d7c95c636..74aa77b0fd 100644 --- a/src/gpu/GrOnFlushResourceProvider.cpp +++ b/src/gpu/GrOnFlushResourceProvider.cpp @@ -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 GrOnFlushResourceProvider::makeRenderTargetContext( sk_sp proxy, GrColorType colorType, sk_sp colorSpace, @@ -40,6 +41,19 @@ std::unique_ptr GrOnFlushResourceProvider::makeRenderTarg return renderTargetContext; } +void GrOnFlushResourceProvider::addTextureResolveTask(sk_sp 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(fDrawingMgr->fOnFlushRenderTasks.push_back( + sk_make_sp(std::move(textureProxy), resolveFlags)).get()); + task->init(*this->caps()); +} + bool GrOnFlushResourceProvider::assignUniqueKeyToProxy(const GrUniqueKey& key, GrTextureProxy* proxy) { auto proxyProvider = fDrawingMgr->getContext()->priv().proxyProvider(); diff --git a/src/gpu/GrOnFlushResourceProvider.h b/src/gpu/GrOnFlushResourceProvider.h index e4fe6f1d6e..7dd2847d42 100644 --- a/src/gpu/GrOnFlushResourceProvider.h +++ b/src/gpu/GrOnFlushResourceProvider.h @@ -63,10 +63,10 @@ class GrOnFlushResourceProvider { public: explicit GrOnFlushResourceProvider(GrDrawingManager* drawingMgr) : fDrawingMgr(drawingMgr) {} - std::unique_ptr makeRenderTargetContext(sk_sp, - GrColorType, - sk_sp, - const SkSurfaceProps*); + std::unique_ptr makeRenderTargetContext( + sk_sp, GrColorType, sk_sp, const SkSurfaceProps*); + + void addTextureResolveTask(sk_sp, GrSurfaceProxy::ResolveFlags); // Proxy unique key management. See GrProxyProvider.h. bool assignUniqueKeyToProxy(const GrUniqueKey&, GrTextureProxy*); diff --git a/src/gpu/GrOpsRenderPass.cpp b/src/gpu/GrOpsRenderPass.cpp index 3876a42445..dcc53b644e 100644 --- a/src/gpu/GrOpsRenderPass.cpp +++ b/src/gpu/GrOpsRenderPass.cpp @@ -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()) { diff --git a/src/gpu/GrRenderTargetProxy.h b/src/gpu/GrRenderTargetProxy.h index 33718502b0..1d40efd6a7 100644 --- a/src/gpu/GrRenderTargetProxy.h +++ b/src/gpu/GrRenderTargetProxy.h @@ -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 diff --git a/src/gpu/GrRenderTask.cpp b/src/gpu/GrRenderTask.cpp index 9541408a95..91528f21a2 100644 --- a/src/gpu/GrRenderTask.cpp +++ b/src/gpu/GrRenderTask.cpp @@ -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. - SkASSERT(!textureProxy->mipMapsAreDirty()); - SkASSERT(textureProxy->getLastRenderTask() == textureResolveTask); + if (GrRenderTargetProxy* renderTargetProxy = dependedOn->asRenderTargetProxy()) { + SkASSERT(!renderTargetProxy->isMSAADirty()); + } + if (textureProxy) { + SkASSERT(!textureProxy->mipMapsAreDirty()); + } + SkASSERT(dependedOn->getLastRenderTask() == textureResolveTask); SkASSERT(textureResolveTask->isClosed()); +#endif // Fall through and add textureResolveTask as a dependency of "this". dependedOnTask = textureResolveTask; diff --git a/src/gpu/GrResourceProvider.cpp b/src/gpu/GrResourceProvider.cpp index 9ffe1b5faf..aae01710dc 100644 --- a/src/gpu/GrResourceProvider.cpp +++ b/src/gpu/GrResourceProvider.cpp @@ -370,6 +370,9 @@ sk_sp GrResourceProvider::refScratchTexture(const GrSurfaceDesc& desc if (resource) { fGpu->stats()->incNumScratchTexturesReused(); GrSurface* surface = static_cast(resource); + if (GrRenderTarget* rt = surface->asRenderTarget()) { + rt->flagAsResolved(); // Scratch textures always start without dirty MSAA. + } return sk_sp(surface->asTexture()); } } diff --git a/src/gpu/GrSurfaceProxy.h b/src/gpu/GrSurfaceProxy.h index 673e10e187..e4bbe5248a 100644 --- a/src/gpu/GrSurfaceProxy.h +++ b/src/gpu/GrSurfaceProxy.h @@ -32,6 +32,16 @@ class GrSurfaceProxy : public GrNonAtomicRef { 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 diff --git a/src/gpu/GrTextureResolveManager.h b/src/gpu/GrTextureResolveManager.h index c0da50a781..a9b01823f0 100644 --- a/src/gpu/GrTextureResolveManager.h +++ b/src/gpu/GrTextureResolveManager.h @@ -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 proxy, GrTextureResolveFlags flags, const GrCaps& caps) const { + GrRenderTask* newTextureResolveRenderTask(sk_sp 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: diff --git a/src/gpu/GrTextureResolveRenderTask.cpp b/src/gpu/GrTextureResolveRenderTask.cpp index 9a4d6b46ac..0b7738320d 100644 --- a/src/gpu/GrTextureResolveRenderTask.cpp +++ b/src/gpu/GrTextureResolveRenderTask.cpp @@ -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,12 +50,21 @@ void GrTextureResolveRenderTask::gatherProxyIntervals(GrResourceAllocator* alloc } bool GrTextureResolveRenderTask::onExecute(GrOpFlushState* flushState) { - GrTexture* texture = fTarget->peekTexture(); - SkASSERT(texture); + // Resolve msaa before regenerating mipmaps. + if (GrSurfaceProxy::ResolveFlags::kMSAA & fResolveFlags) { + GrRenderTarget* renderTarget = fTarget->peekRenderTarget(); + SkASSERT(renderTarget); + if (renderTarget->needsResolve()) { + flushState->gpu()->resolveRenderTarget(renderTarget); + } + } - if ((GrTextureResolveFlags::kMipMaps & fResolveFlags) && - texture->texturePriv().mipMapsAreDirty()) { - flushState->gpu()->regenerateMipMapLevels(texture); + if (GrSurfaceProxy::ResolveFlags::kMipMaps & fResolveFlags) { + GrTexture* texture = fTarget->peekTexture(); + SkASSERT(texture); + if (texture->texturePriv().mipMapsAreDirty()) { + flushState->gpu()->regenerateMipMapLevels(texture); + } } return true; diff --git a/src/gpu/GrTextureResolveRenderTask.h b/src/gpu/GrTextureResolveRenderTask.h index 29d7eaa824..86365dc9c7 100644 --- a/src/gpu/GrTextureResolveRenderTask.h +++ b/src/gpu/GrTextureResolveRenderTask.h @@ -12,10 +12,14 @@ class GrTextureResolveRenderTask final : public GrRenderTask { public: - GrTextureResolveRenderTask(sk_sp textureProxy, GrTextureResolveFlags flags) - : GrRenderTask(std::move(textureProxy)) - , fResolveFlags(flags) { - SkASSERT(GrTextureResolveFlags::kNone != fResolveFlags); + GrTextureResolveRenderTask(sk_sp 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 diff --git a/src/gpu/ccpr/GrCCPerFlushResources.cpp b/src/gpu/ccpr/GrCCPerFlushResources.cpp index 72ff29c498..c5db56a6e0 100644 --- a/src/gpu/ccpr/GrCCPerFlushResources.cpp +++ b/src/gpu/ccpr/GrCCPerFlushResources.cpp @@ -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); diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp index 46607933dc..ed722c82d7 100644 --- a/src/gpu/gl/GrGLGpu.cpp +++ b/src/gpu/gl/GrGLGpu.cpp @@ -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 program(fProgramCache->refProgram(this, renderTarget, origin, primProc, - primProcProxiesForMipRegen, - pipeline, willDrawPoints)); + sk_sp 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)); diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h index d274a25360..d494be346a 100644 --- a/src/gpu/gl/GrGLGpu.h +++ b/src/gpu/gl/GrGLGpu.h @@ -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 diff --git a/src/gpu/mock/GrMockGpu.h b/src/gpu/mock/GrMockGpu.h index c0ab9c5b5b..86efe99744 100644 --- a/src/gpu/mock/GrMockGpu.h +++ b/src/gpu/mock/GrMockGpu.h @@ -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 { diff --git a/src/gpu/mtl/GrMtlOpsRenderPass.mm b/src/gpu/mtl/GrMtlOpsRenderPass.mm index ddcfe96e6f..b630a3afc1 100644 --- a/src/gpu/mtl/GrMtlOpsRenderPass.mm +++ b/src/gpu/mtl/GrMtlOpsRenderPass.mm @@ -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(texture); - // We may need to resolve the texture first if it is also a render target - GrMtlRenderTarget* texRT = static_cast(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); diff --git a/src/gpu/vk/GrVkOpsRenderPass.cpp b/src/gpu/vk/GrVkOpsRenderPass.cpp index ba72061b39..d6ca67e6ff 100644 --- a/src/gpu/vk/GrVkOpsRenderPass.cpp +++ b/src/gpu/vk/GrVkOpsRenderPass.cpp @@ -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(texture); - // We may need to resolve the texture first if it is also a render target - GrVkRenderTarget* texRT = static_cast(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()); } }