Implement dynamic MSAA on OpenGL

This is an initial attempt. We use a few coverage ops that haven't
been updated to handle MSAA yet, and other times we trigger MSAA when
we shouldn't, but it sets the basic functionality in place.

Bug: skia:11396
Change-Id: I8acfe4283bccf5543d4b774692e39427142b3228
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/395996
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
This commit is contained in:
Chris Dalton 2021-04-23 13:07:52 -06:00 committed by Skia Commit-Bot
parent b0ada77326
commit 710e1c9226
15 changed files with 258 additions and 55 deletions

View File

@ -431,3 +431,8 @@ GrDstSampleType GrCaps::getDstSampleTypeForProxy(const GrRenderTargetProxy* rt)
}
return GrDstSampleType::kAsTextureCopy;
}
bool GrCaps::supportsDynamicMSAA(const GrRenderTargetProxy* rtProxy) const {
return this->internalMultisampleCount(rtProxy->backendFormat()) > 1 &&
this->onSupportsDynamicMSAA(rtProxy);
}

View File

@ -473,6 +473,8 @@ public:
return GrInternalSurfaceFlags::kNone;
}
bool supportsDynamicMSAA(const GrRenderTargetProxy*) const;
#if GR_TEST_UTILS
struct TestFormatColorTypeCombination {
GrColorType fColorType;
@ -488,6 +490,8 @@ protected:
// NOTE: this method will only reduce the caps, never expand them.
void finishInitialization(const GrContextOptions& options);
virtual bool onSupportsDynamicMSAA(const GrRenderTargetProxy*) const { return false; }
sk_sp<GrShaderCaps> fShaderCaps;
bool fNPOTTextureTileSupport : 1;

View File

@ -391,8 +391,9 @@ void GrOpsTask::addOp(GrDrawingManager* drawingMgr, GrOp::Owner op,
}
void GrOpsTask::addDrawOp(GrDrawingManager* drawingMgr, GrOp::Owner op,
const GrProcessorSet::Analysis& processorAnalysis,
GrAppliedClip&& clip, const DstProxyView& dstProxyView,
GrDrawOp::FixedFunctionFlags fixedFunctionFlags,
const GrProcessorSet::Analysis& processorAnalysis, GrAppliedClip&& clip,
const DstProxyView& dstProxyView,
GrTextureResolveManager textureResolveManager, const GrCaps& caps) {
auto addDependency = [&](GrSurfaceProxy* p, GrMipmapped mipmapped) {
this->addSampledTexture(p);
@ -420,6 +421,16 @@ void GrOpsTask::addDrawOp(GrDrawingManager* drawingMgr, GrOp::Owner op,
fRenderPassXferBarriers |= GrXferBarrierFlags::kBlend;
}
#ifdef SK_DEBUG
// Ensure we can support dynamic msaa if the caller is trying to trigger it.
GrRenderTargetProxy* rtProxy = this->target(0)->asRenderTargetProxy();
if (rtProxy->numSamples() == 1 &&
(fixedFunctionFlags & GrDrawOp::FixedFunctionFlags::kUsesHWAA)) {
SkASSERT(caps.supportsDynamicMSAA(rtProxy));
}
#endif
fUsesMSAASurface |= (fixedFunctionFlags & GrDrawOp::FixedFunctionFlags::kUsesHWAA);
this->recordOp(std::move(op), processorAnalysis, clip.doesClip() ? &clip : nullptr,
&dstProxyView, caps);
}
@ -715,6 +726,7 @@ int GrOpsTask::mergeFrom(SkSpan<const sk_sp<GrRenderTask>> tasks) {
fClippedContentBounds.join(opsTask->fClippedContentBounds);
fTotalBounds.join(opsTask->fTotalBounds);
fRenderPassXferBarriers |= opsTask->fRenderPassXferBarriers;
fUsesMSAASurface |= opsTask->fUsesMSAASurface;
SkDEBUGCODE(fNumClips += opsTask->fNumClips);
}

View File

@ -70,8 +70,9 @@ public:
void addOp(GrDrawingManager*, GrOp::Owner, GrTextureResolveManager, const GrCaps&);
void addDrawOp(GrDrawingManager*, GrOp::Owner, const GrProcessorSet::Analysis&,
GrAppliedClip&&, const DstProxyView&, GrTextureResolveManager, const GrCaps&);
void addDrawOp(GrDrawingManager*, GrOp::Owner, GrDrawOp::FixedFunctionFlags,
const GrProcessorSet::Analysis&, GrAppliedClip&&, const DstProxyView&,
GrTextureResolveManager, const GrCaps&);
void discard();

View File

@ -493,11 +493,20 @@ sk_sp<GrGpuBuffer> GrResourceProvider::createBuffer(size_t size, GrGpuBufferType
return buffer;
}
static int num_stencil_samples(const GrRenderTarget* rt, bool useMSAASurface, const GrCaps& caps) {
int numSamples = rt->numSamples();
if (numSamples == 1 && useMSAASurface) { // Are we using dynamic msaa?
numSamples = caps.internalMultisampleCount(rt->backendFormat());
SkASSERT(numSamples > 1); // Caller must ensure dmsaa is supported before trying to use it.
}
return numSamples;
}
bool GrResourceProvider::attachStencilAttachment(GrRenderTarget* rt, bool useMSAASurface) {
SkASSERT(rt);
GrAttachment* stencil = rt->getStencilAttachment(useMSAASurface);
if (stencil) {
SkASSERT(stencil->numSamples() == rt->numSamples());
SkASSERT(stencil->numSamples() == num_stencil_samples(rt, useMSAASurface, *this->caps()));
return true;
}
@ -515,15 +524,16 @@ bool GrResourceProvider::attachStencilAttachment(GrRenderTarget* rt, bool useMSA
return false;
}
GrProtected isProtected = rt->isProtected() ? GrProtected::kYes : GrProtected::kNo;
int numStencilSamples = num_stencil_samples(rt, useMSAASurface, *this->caps());
GrAttachment::ComputeSharedAttachmentUniqueKey(
*this->caps(), stencilFormat, rt->dimensions(),
GrAttachment::UsageFlags::kStencilAttachment, rt->numSamples(), GrMipmapped::kNo,
GrAttachment::UsageFlags::kStencilAttachment, numStencilSamples, GrMipmapped::kNo,
isProtected, &sbKey);
auto stencil = this->findByUniqueKey<GrAttachment>(sbKey);
if (!stencil) {
// Need to try and create a new stencil
stencil = this->gpu()->makeStencilAttachment(rt->backendFormat(), rt->dimensions(),
rt->numSamples());
numStencilSamples);
if (!stencil) {
return false;
}
@ -532,7 +542,8 @@ bool GrResourceProvider::attachStencilAttachment(GrRenderTarget* rt, bool useMSA
rt->attachStencilAttachment(std::move(stencil), useMSAASurface);
}
stencil = rt->getStencilAttachment(useMSAASurface);
SkASSERT(!stencil || stencil->numSamples() == rt->numSamples());
SkASSERT(!stencil ||
stencil->numSamples() == num_stencil_samples(rt, useMSAASurface, *this->caps()));
return stencil != nullptr;
}

View File

@ -299,6 +299,9 @@ GrSurfaceDrawContext::GrSurfaceDrawContext(GrRecordingContext* context,
{colorType, kPremul_SkAlphaType, std::move(colorSpace)},
flushTimeOpsTask)
, fSurfaceProps(surfaceProps)
, fCanUseDynamicMSAA(
(fSurfaceProps.flags() & kDMSAA_SkSurfacePropsPrivateFlag) &&
context->priv().caps()->supportsDynamicMSAA(this->asRenderTargetProxy()))
, fGlyphPainter(*this) {
SkDEBUGCODE(this->validate();)
}
@ -319,6 +322,12 @@ void GrSurfaceDrawContext::willReplaceOpsTask(GrOpsTask* prevTask, GrOpsTask* ne
}
inline GrAAType GrSurfaceDrawContext::chooseAAType(GrAA aa) {
if (fCanUseDynamicMSAA) {
// Trigger dmsaa by default if the render target has it. Coverage ops that know how to
// handle both single and multisample targets without popping will do so without calling
// chooseAAType.
return GrAAType::kMSAA;
}
if (GrAA::kNo == aa) {
// On some devices we cannot disable MSAA if it is enabled so we make the AA type reflect
// that.
@ -619,8 +628,17 @@ void GrSurfaceDrawContext::drawFilledQuad(const GrClip* clip,
if (opt >= QuadOptimization::kClipApplied) {
// These optimizations require caller to add an op themselves
const GrClip* finalClip = opt == QuadOptimization::kClipApplied ? nullptr : clip;
GrAAType aaType = ss ? (aa == GrAA::kYes ? GrAAType::kMSAA : GrAAType::kNone)
: this->chooseAAType(aa);
GrAAType aaType;
if (ss) {
aaType = (aa == GrAA::kYes) ? GrAAType::kMSAA : GrAAType::kNone;
} else if (fCanUseDynamicMSAA && aa == GrAA::kNo) {
// The SkGpuDevice ensures GrAA is always kYes when using dmsaa. If the caller calls
// into here with GrAA::kNo, trust that they know what they're doing and that the
// rendering will be equal with or without msaa.
aaType = GrAAType::kNone;
} else {
aaType = this->chooseAAType(aa);
}
this->addDrawOp(finalClip, GrFillRectOp::Make(fContext, std::move(paint), aaType,
quad, ss));
}
@ -743,7 +761,7 @@ void GrSurfaceDrawContext::drawRect(const GrClip* clip,
(rect.width() && rect.height())) {
// Only use the StrokeRectOp for non-empty rectangles. Empty rectangles will be processed by
// GrStyledShape to handle stroke caps and dashing properly.
GrAAType aaType = this->chooseAAType(aa);
GrAAType aaType = (fCanUseDynamicMSAA) ? GrAAType::kCoverage : this->chooseAAType(aa);
GrOp::Owner op = GrStrokeRectOp::Make(
fContext, std::move(paint), aaType, viewMatrix, rect, stroke);
// op may be null if the stroke is not supported or if using coverage aa and the view matrix
@ -1079,7 +1097,7 @@ void GrSurfaceDrawContext::drawRRect(const GrClip* origClip,
op = GrFillRRectOp::Make(fContext, std::move(paint), viewMatrix, rrect, rrect.rect(),
GrAA(aaType != GrAAType::kNone));
}
if (!op && GrAAType::kCoverage == aaType) {
if (!op && (GrAAType::kCoverage == aaType || fCanUseDynamicMSAA)) {
assert_alive(paint);
op = GrOvalOpFactory::MakeRRectOp(
fContext, std::move(paint), viewMatrix, rrect, stroke, this->caps()->shaderCaps());
@ -1392,7 +1410,7 @@ void GrSurfaceDrawContext::drawOval(const GrClip* clip,
op = GrFillRRectOp::Make(fContext, std::move(paint), viewMatrix, SkRRect::MakeOval(oval),
oval, GrAA(aaType != GrAAType::kNone));
}
if (!op && GrAAType::kCoverage == aaType) {
if (!op && (GrAAType::kCoverage == aaType || fCanUseDynamicMSAA)) {
assert_alive(paint);
op = GrOvalOpFactory::MakeOvalOp(fContext, std::move(paint), viewMatrix, oval, style,
this->caps()->shaderCaps());
@ -1425,7 +1443,7 @@ void GrSurfaceDrawContext::drawArc(const GrClip* clip,
AutoCheckFlush acf(this->drawingManager());
GrAAType aaType = this->chooseAAType(aa);
if (GrAAType::kCoverage == aaType) {
if (GrAAType::kCoverage == aaType || fCanUseDynamicMSAA) {
const GrShaderCaps* shaderCaps = this->caps()->shaderCaps();
GrOp::Owner op = GrOvalOpFactory::MakeArcOp(fContext,
std::move(paint),
@ -1938,9 +1956,9 @@ void GrSurfaceDrawContext::addDrawOp(const GrClip* clip,
if (willAddFn) {
willAddFn(op.get(), opsTask->uniqueID());
}
opsTask->addDrawOp(this->drawingManager(), std::move(op), analysis, std::move(appliedClip),
dstProxyView, GrTextureResolveManager(this->drawingManager()),
*this->caps());
opsTask->addDrawOp(this->drawingManager(), std::move(op), fixedFunctionFlags, analysis,
std::move(appliedClip), dstProxyView,
GrTextureResolveManager(this->drawingManager()), *this->caps());
}
bool GrSurfaceDrawContext::setupDstProxyView(const GrOp& op,

View File

@ -729,7 +729,8 @@ private:
SkGlyphRunListPainter* glyphPainter() { return &fGlyphPainter; }
SkSurfaceProps fSurfaceProps;
const SkSurfaceProps fSurfaceProps;
const bool fCanUseDynamicMSAA;
bool fNeedsStencil = false;

View File

@ -306,6 +306,7 @@ void GrSurfaceFillContext::addDrawOp(GrOp::Owner owner) {
GrXferProcessor::DstProxyView dstProxyView;
this->getOpsTask()->addDrawOp(fContext->priv().drawingManager(),
std::move(owner),
op->fixedFunctionFlags(),
analysis,
std::move(clip),
dstProxyView,

View File

@ -25,7 +25,8 @@ public:
GrProtected::kNo)
, fFormat(format)
, fRenderbufferID(idDesc.fRenderbufferID) {
SkASSERT(supportedUsages == UsageFlags::kStencilAttachment);
SkASSERT(supportedUsages == UsageFlags::kStencilAttachment ||
supportedUsages == UsageFlags::kColorAttachment);
this->registerWithCache(SkBudgeted::kYes);
}

View File

@ -511,6 +511,20 @@ private:
GrDstSampleType onGetDstSampleTypeForProxy(const GrRenderTargetProxy*) const override;
bool onSupportsDynamicMSAA(const GrRenderTargetProxy*) const override {
switch (fMSFBOType) {
// The Apple extension doesn't support blitting from single to multisample.
case kES_Apple_MSFBOType:
case kNone_MSFBOType:
return false;
case kStandard_MSFBOType:
case kES_IMG_MsToTexture_MSFBOType:
case kES_EXT_MsToTexture_MSFBOType:
return true;
}
SkUNREACHABLE;
}
GrGLStandard fStandard = kNone_GrGLStandard;
SkTArray<GrGLFormat, true> fStencilFormats;

View File

@ -1728,6 +1728,25 @@ sk_sp<GrAttachment> GrGLGpu::makeStencilAttachment(const GrBackendFormat& colorF
numStencilSamples, sFmt));
}
sk_sp<GrAttachment> GrGLGpu::makeMSAAAttachment(SkISize dimensions, const GrBackendFormat& format,
int numSamples, GrProtected isProtected) {
GrGLAttachment::IDDesc desc;
GL_CALL(GenRenderbuffers(1, &desc.fRenderbufferID));
if (!desc.fRenderbufferID) {
return nullptr;
}
GL_CALL(BindRenderbuffer(GR_GL_RENDERBUFFER, desc.fRenderbufferID));
GrGLenum glFormat = this->glCaps().getRenderbufferInternalFormat(format.asGLFormat());
if (!this->renderbufferStorageMSAA(*fGLContext, numSamples, glFormat, dimensions.width(),
dimensions.height())) {
GL_CALL(DeleteRenderbuffers(1, &desc.fRenderbufferID));
return nullptr;
}
return sk_sp<GrAttachment>(new GrGLAttachment(this, desc, dimensions,
GrAttachment::UsageFlags::kColorAttachment,
numSamples, format.asGLFormat()));
}
////////////////////////////////////////////////////////////////////////////////
sk_sp<GrGpuBuffer> GrGLGpu::onCreateBuffer(size_t size, GrGpuBufferType intendedType,
@ -1943,20 +1962,19 @@ static bool use_tiled_rendering(const GrGLCaps& glCaps,
GrStoreOp::kDiscard == stencilLoadStore.fStoreOp;
}
void GrGLGpu::beginCommandBuffer(GrRenderTarget* rt, bool useMultisampleFBO, const SkIRect& bounds,
GrSurfaceOrigin origin,
void GrGLGpu::beginCommandBuffer(GrGLRenderTarget* rt, bool useMultisampleFBO,
const SkIRect& bounds, GrSurfaceOrigin origin,
const GrOpsRenderPass::LoadAndStoreInfo& colorLoadStore,
const GrOpsRenderPass::StencilLoadAndStoreInfo& stencilLoadStore) {
SkASSERT(!fIsExecutingCommandBuffer_DebugOnly);
this->handleDirtyContext();
auto glRT = static_cast<GrGLRenderTarget*>(rt);
this->flushRenderTarget(glRT, useMultisampleFBO);
this->flushRenderTarget(rt, useMultisampleFBO);
SkDEBUGCODE(fIsExecutingCommandBuffer_DebugOnly = true);
if (use_tiled_rendering(this->glCaps(), stencilLoadStore)) {
auto nativeBounds = GrNativeRect::MakeRelativeTo(origin, glRT->height(), bounds);
auto nativeBounds = GrNativeRect::MakeRelativeTo(origin, rt->height(), bounds);
GrGLbitfield preserveMask = (GrLoadOp::kLoad == colorLoadStore.fLoadOp)
? GR_GL_COLOR_BUFFER_BIT0 : GR_GL_NONE;
SkASSERT(GrLoadOp::kLoad != stencilLoadStore.fLoadOp); // Handled by use_tiled_rendering().
@ -1984,31 +2002,30 @@ void GrGLGpu::beginCommandBuffer(GrRenderTarget* rt, bool useMultisampleFBO, con
}
}
void GrGLGpu::endCommandBuffer(GrRenderTarget* rt, bool useMultisampleFBO,
void GrGLGpu::endCommandBuffer(GrGLRenderTarget* rt, bool useMultisampleFBO,
const GrOpsRenderPass::LoadAndStoreInfo& colorLoadStore,
const GrOpsRenderPass::StencilLoadAndStoreInfo& stencilLoadStore) {
SkASSERT(fIsExecutingCommandBuffer_DebugOnly);
this->handleDirtyContext();
if (rt->uniqueID() != fHWBoundRenderTargetUniqueID) {
if (rt->uniqueID() != fHWBoundRenderTargetUniqueID ||
useMultisampleFBO != fHWBoundFramebufferIsMSAA) {
// The framebuffer binding changed in the middle of a command buffer. We should have already
// printed a warning during onFBOChanged.
return;
}
if (GrGLCaps::kNone_InvalidateFBType != this->glCaps().invalidateFBType()) {
auto glRT = static_cast<GrGLRenderTarget*>(rt);
SkSTArray<2, GrGLenum> discardAttachments;
if (GrStoreOp::kDiscard == colorLoadStore.fStoreOp) {
GrGLuint renderFBOID = (useMultisampleFBO) ? glRT->multisampleFBOID()
: glRT->singleSampleFBOID();
GrGLuint renderFBOID = (useMultisampleFBO) ? rt->multisampleFBOID()
: rt->singleSampleFBOID();
discardAttachments.push_back((!renderFBOID) ? GR_GL_COLOR : GR_GL_COLOR_ATTACHMENT0);
}
if (GrStoreOp::kDiscard == stencilLoadStore.fStoreOp) {
GrGLuint renderFBOID = (useMultisampleFBO) ? glRT->multisampleFBOID()
: glRT->singleSampleFBOID();
GrGLuint renderFBOID = (useMultisampleFBO) ? rt->multisampleFBOID()
: rt->singleSampleFBOID();
discardAttachments.push_back((!renderFBOID) ? GR_GL_STENCIL : GR_GL_STENCIL_ATTACHMENT);
}
@ -2173,7 +2190,14 @@ GrOpsRenderPass* GrGLGpu::onGetOpsRenderPass(
if (!fCachedOpsRenderPass) {
fCachedOpsRenderPass = std::make_unique<GrGLOpsRenderPass>(this);
}
if (useMultisampleFBO && rt->numSamples() == 1) {
// We will be using dynamic msaa. Ensure there is an attachment.
auto glRT = static_cast<GrGLRenderTarget*>(rt);
if (!glRT->ensureDynamicMSAAAttachment()) {
SkDebugf("WARNING: Failed to make dmsaa attachment. Render pass will be dropped.");
return nullptr;
}
}
fCachedOpsRenderPass->set(rt, useMultisampleFBO, bounds, origin, colorInfo, stencilInfo);
return fCachedOpsRenderPass.get();
}
@ -2284,19 +2308,34 @@ GrGLenum GrGLGpu::prepareToDraw(GrPrimitiveType primitiveType) {
}
void GrGLGpu::onResolveRenderTarget(GrRenderTarget* target, const SkIRect& resolveRect) {
// Some extensions automatically resolves the texture when it is read.
SkASSERT(this->glCaps().usesMSAARenderBuffers());
this->resolveRenderFBOs(static_cast<GrGLRenderTarget*>(target), resolveRect,
ResolveDirection::kMSAAToSingle);
}
GrGLRenderTarget* rt = static_cast<GrGLRenderTarget*>(target);
SkASSERT(rt->requiresManualMSAAResolve());
SkASSERT(rt->singleSampleFBOID() != 0 && rt->multisampleFBOID() != 0);
this->bindFramebuffer(GR_GL_READ_FRAMEBUFFER, rt->multisampleFBOID());
this->bindFramebuffer(GR_GL_DRAW_FRAMEBUFFER, rt->singleSampleFBOID());
void GrGLGpu::resolveRenderFBOs(GrGLRenderTarget* rt, const SkIRect& resolveRect,
ResolveDirection resolveDirection,
bool invalidateReadBufferAfterBlit) {
this->handleDirtyContext();
// If the multisample FBO is nonzero, it means we always have something to resolve (even if the
// single sample buffer is FBO 0). If it's zero, then there's nothing to resolve.
SkASSERT(rt->multisampleFBOID() != 0);
if (resolveDirection == ResolveDirection::kMSAAToSingle) {
this->bindFramebuffer(GR_GL_READ_FRAMEBUFFER, rt->multisampleFBOID());
this->bindFramebuffer(GR_GL_DRAW_FRAMEBUFFER, rt->singleSampleFBOID());
} else {
SkASSERT(resolveDirection == ResolveDirection::kSingleToMSAA);
this->bindFramebuffer(GR_GL_READ_FRAMEBUFFER, rt->singleSampleFBOID());
this->bindFramebuffer(GR_GL_DRAW_FRAMEBUFFER, rt->multisampleFBOID());
}
// make sure we go through flushRenderTarget() since we've modified
// the bound DRAW FBO ID.
fHWBoundRenderTargetUniqueID.makeInvalid();
if (GrGLCaps::kES_Apple_MSFBOType == this->glCaps().msFBOType()) {
// The Apple extension doesn't support blitting from single to multisample.
SkASSERT(resolveDirection != ResolveDirection::kSingleToMSAA);
// Apple's extension uses the scissor as the blit bounds.
// Passing in kTopLeft_GrSurfaceOrigin will make sure no transformation of the rect
// happens inside flushScissor since resolveRect is already in native device coordinates.
@ -2311,8 +2350,8 @@ void GrGLGpu::onResolveRenderTarget(GrRenderTarget* target, const SkIRect& resol
this->glCaps().blitFramebufferSupportFlags()) {
l = 0;
b = 0;
r = target->width();
t = target->height();
r = rt->width();
t = rt->height();
} else {
l = resolveRect.x();
b = resolveRect.y();
@ -2325,6 +2364,20 @@ void GrGLGpu::onResolveRenderTarget(GrRenderTarget* target, const SkIRect& resol
this->disableWindowRectangles();
GL_CALL(BlitFramebuffer(l, b, r, t, l, b, r, t, GR_GL_COLOR_BUFFER_BIT, GR_GL_NEAREST));
}
if (this->glCaps().invalidateFBType() != GrGLCaps::kNone_InvalidateFBType &&
invalidateReadBufferAfterBlit) {
// Invalidate the read FBO attachment after the blit, in hopes that this allows the driver
// to perform tiling optimizations.
GrGLenum colorDiscardAttachment = (rt->multisampleFBOID() == 0) ? GR_GL_COLOR
: GR_GL_COLOR_ATTACHMENT0;
if (this->glCaps().invalidateFBType() == GrGLCaps::kInvalidate_InvalidateFBType) {
GL_CALL(InvalidateFramebuffer(GR_GL_READ_FRAMEBUFFER, 1, &colorDiscardAttachment));
} else {
SkASSERT(this->glCaps().invalidateFBType() == GrGLCaps::kDiscard_InvalidateFBType);
GL_CALL(DiscardFramebuffer(GR_GL_READ_FRAMEBUFFER, 1, &colorDiscardAttachment));
}
}
}
namespace {
@ -2414,7 +2467,8 @@ void GrGLGpu::disableStencil() {
void GrGLGpu::flushHWAAState(GrRenderTarget* rt, bool useHWAA) {
// rt is only optional if useHWAA is false.
SkASSERT(rt || !useHWAA);
SkASSERT(!useHWAA || rt->numSamples() > 1);
SkASSERT(!useHWAA || rt->numSamples() > 1 ||
static_cast<GrGLRenderTarget*>(rt)->multisampleFBOID());
if (this->caps()->multisampleDisableSupport()) {
if (useHWAA) {

View File

@ -103,6 +103,14 @@ public:
// Applies any necessary workarounds and returns the GL primitive type to use in draw calls.
GrGLenum prepareToDraw(GrPrimitiveType primitiveType);
enum class ResolveDirection : bool {
kSingleToMSAA,
kMSAAToSingle
};
void resolveRenderFBOs(GrGLRenderTarget*, const SkIRect& resolveRect, ResolveDirection,
bool invalidateReadBufferAfterBlit = false);
// The GrGLOpsRenderPass does not buffer up draws before submitting them to the gpu.
// Thus this is the implementation of the clear call for the corresponding passthrough function
// on GrGLOpsRenderPass.
@ -115,12 +123,12 @@ public:
void clearStencilClip(const GrScissorState&, bool insideStencilMask,
GrRenderTarget*, bool useMultisampleFBO, GrSurfaceOrigin);
void beginCommandBuffer(GrRenderTarget*, bool useMultisampleFBO,
void beginCommandBuffer(GrGLRenderTarget*, bool useMultisampleFBO,
const SkIRect& bounds, GrSurfaceOrigin,
const GrOpsRenderPass::LoadAndStoreInfo& colorLoadStore,
const GrOpsRenderPass::StencilLoadAndStoreInfo& stencilLoadStore);
void endCommandBuffer(GrRenderTarget*, bool useMultisampleFBO,
void endCommandBuffer(GrGLRenderTarget*, bool useMultisampleFBO,
const GrOpsRenderPass::LoadAndStoreInfo& colorLoadStore,
const GrOpsRenderPass::StencilLoadAndStoreInfo& stencilLoadStore);
@ -134,9 +142,7 @@ public:
sk_sp<GrAttachment> makeMSAAAttachment(SkISize dimensions,
const GrBackendFormat& format,
int numSamples,
GrProtected isProtected) override {
return nullptr;
}
GrProtected isProtected) override;
void deleteBackendTexture(const GrBackendTexture&) override;

View File

@ -32,13 +32,34 @@ void GrGLOpsRenderPass::set(GrRenderTarget* rt, bool useMSAASurface, const SkIRe
}
void GrGLOpsRenderPass::onBegin() {
fGpu->beginCommandBuffer(fRenderTarget, fUseMultisampleFBO, fContentBounds, fOrigin,
auto glRT = static_cast<GrGLRenderTarget*>(fRenderTarget);
if (fUseMultisampleFBO && fColorLoadAndStoreInfo.fLoadOp == GrLoadOp::kLoad &&
glRT->hasDynamicMSAAAttachment()) {
// Blit the single sample fbo into the dmsaa attachment.
auto nativeBounds = GrNativeRect::MakeRelativeTo(fOrigin, fRenderTarget->height(),
fContentBounds);
fGpu->resolveRenderFBOs(glRT, nativeBounds.asSkIRect(),
GrGLGpu::ResolveDirection::kSingleToMSAA);
}
fGpu->beginCommandBuffer(glRT, fUseMultisampleFBO, fContentBounds, fOrigin,
fColorLoadAndStoreInfo, fStencilLoadAndStoreInfo);
}
void GrGLOpsRenderPass::onEnd() {
fGpu->endCommandBuffer(fRenderTarget, fUseMultisampleFBO, fColorLoadAndStoreInfo,
auto glRT = static_cast<GrGLRenderTarget*>(fRenderTarget);
fGpu->endCommandBuffer(glRT, fUseMultisampleFBO, fColorLoadAndStoreInfo,
fStencilLoadAndStoreInfo);
if (fUseMultisampleFBO && fColorLoadAndStoreInfo.fStoreOp == GrStoreOp::kStore &&
glRT->hasDynamicMSAAAttachment()) {
// Blit the msaa attachment into the single sample fbo.
auto nativeBounds = GrNativeRect::MakeRelativeTo(fOrigin, fRenderTarget->height(),
fContentBounds);
fGpu->resolveRenderFBOs(glRT, nativeBounds.asSkIRect(),
GrGLGpu::ResolveDirection::kMSAAToSingle,
true /*invalidateReadBufferAfterBlit*/);
}
}
bool GrGLOpsRenderPass::onBindPipeline(const GrProgramInfo& programInfo,

View File

@ -125,12 +125,17 @@ size_t GrGLRenderTarget::onGpuMemorySize() const {
}
bool GrGLRenderTarget::completeStencilAttachment(GrAttachment* stencil, bool useMultisampleFBO) {
SkASSERT(useMultisampleFBO == (this->numSamples() > 1));
GrGLGpu* gpu = this->getGLGpu();
const GrGLInterface* interface = gpu->glInterface();
GrGLuint stencilFBOID = (useMultisampleFBO) ? fMultisampleFBOID : fSingleSampleFBOID;
gpu->invalidateBoundRenderTarget();
if (this->numSamples() == 1 && useMultisampleFBO) {
// We will be rendering to the dynamic msaa fbo. Make sure to initialize it first.
if (!this->ensureDynamicMSAAAttachment()) {
return false;
}
}
GrGLuint stencilFBOID = (useMultisampleFBO) ? fMultisampleFBOID : fSingleSampleFBOID;
gpu->bindFramebuffer(GR_GL_FRAMEBUFFER, stencilFBOID);
if (nullptr == stencil) {
@ -170,6 +175,49 @@ bool GrGLRenderTarget::completeStencilAttachment(GrAttachment* stencil, bool use
return true;
}
bool GrGLRenderTarget::ensureDynamicMSAAAttachment() {
SkASSERT(this->numSamples() == 1);
if (fMultisampleFBOID) {
return true;
}
SkASSERT(!fDynamicMSAAAttachment);
GrResourceProvider* resourceProvider = this->getContext()->priv().resourceProvider();
const GrCaps& caps = *this->getGpu()->caps();
int internalSampleCount = caps.internalMultisampleCount(this->backendFormat());
if (internalSampleCount <= 1) {
return false;
}
GL_CALL(GenFramebuffers(1, &fMultisampleFBOID));
if (!fMultisampleFBOID) {
return false;
}
this->getGLGpu()->bindFramebuffer(GR_GL_FRAMEBUFFER, fMultisampleFBOID);
if (resourceProvider->caps()->msaaResolvesAutomatically()) {
if (GrGLTexture* glTex = static_cast<GrGLTexture*>(this->asTexture())) {
GL_CALL(FramebufferTexture2DMultisample(GR_GL_FRAMEBUFFER, GR_GL_COLOR_ATTACHMENT0,
glTex->target(), glTex->textureID(),
0 /*mipMapLevel*/, internalSampleCount));
return true;
}
}
fDynamicMSAAAttachment.reset(static_cast<GrGLAttachment*>(resourceProvider->makeMSAAAttachment(
this->dimensions(), this->backendFormat(), internalSampleCount,
GrProtected(this->isProtected())).release()));
if (!fDynamicMSAAAttachment) {
return false;
}
GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER, GR_GL_COLOR_ATTACHMENT0, GR_GL_RENDERBUFFER,
fDynamicMSAAAttachment->renderbufferID()));
return true;
}
void GrGLRenderTarget::onRelease() {
if (GrBackendObjectOwnership::kBorrowed != fRTFBOOwnership) {
GrGLGpu* gpu = this->getGLGpu();
@ -204,7 +252,6 @@ GrGLGpu* GrGLRenderTarget::getGLGpu() const {
}
bool GrGLRenderTarget::canAttemptStencilAttachment(bool useMultisampleFBO) const {
SkASSERT(useMultisampleFBO == (this->numSamples() > 1));
if (this->getGpu()->getContext()->priv().caps()->avoidStencilBuffers()) {
return false;
}
@ -212,7 +259,9 @@ bool GrGLRenderTarget::canAttemptStencilAttachment(bool useMultisampleFBO) const
// Only modify the FBO's attachments if we have created the FBO. Public APIs do not currently
// allow for borrowed FBO ownership, so we can safely assume that if an object is owned,
// Skia created it.
return this->fRTFBOOwnership == GrBackendObjectOwnership::kOwned;
return this->fRTFBOOwnership == GrBackendObjectOwnership::kOwned ||
// The dmsaa attachment is always owned and always supports adding stencil.
(this->numSamples() == 1 && useMultisampleFBO);
}
void GrGLRenderTarget::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const {

View File

@ -56,6 +56,9 @@ public:
GrGLFormat format() const { return fRTFormat; }
bool hasDynamicMSAAAttachment() const { return SkToBool(fDynamicMSAAAttachment); }
bool ensureDynamicMSAAAttachment();
protected:
// Constructor for subclasses.
GrGLRenderTarget(GrGLGpu*,
@ -83,6 +86,8 @@ private:
size_t onGpuMemorySize() const override;
sk_sp<GrGLAttachment> fDynamicMSAAAttachment;
GrGLuint fMultisampleFBOID;
GrGLuint fSingleSampleFBOID;
GrGLuint fMSColorRenderbufferID;