Fix DMSAA loads when framebuffer blits must be full

If the final MSAA resolve must contain the entire render target (as is
the case in ANGLE ES2), then we also need to draw the entire proxy
bounds into the DMSAA attachment during load. This is because we will
have no other choice than to do a full size resolve at the end of the
render pass, so the entire DMSAA attachment needs valid content.

Bug: skia:12176
Bug: skia:12177
Bug: skia:12234
Bug: skia:12235
Change-Id: I106a8724de1c9cbf7106ed3720dabf37c0c17ab0
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/430256
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
This commit is contained in:
Chris Dalton 2021-07-20 11:32:58 -06:00
parent 65ec198696
commit 88dd356bf1
6 changed files with 78 additions and 37 deletions

View File

@ -3339,7 +3339,7 @@ bool GrGLCaps::canCopyAsBlit(GrGLFormat dstFormat, int dstSampleCnt,
const GrTextureType* srcTypeIfTexture,
const SkRect& srcBounds, bool srcBoundsExact,
const SkIRect& srcRect, const SkIPoint& dstPoint) const {
auto blitFramebufferFlags = this->blitFramebufferSupportFlags();
auto blitFramebufferFlags = fBlitFramebufferFlags;
if (!this->canFormatBeFBOColorAttachment(dstFormat) ||
!this->canFormatBeFBOColorAttachment(srcFormat)) {
return false;
@ -3473,11 +3473,11 @@ GrCaps::DstCopyRestrictions GrGLCaps::getDstCopyRestrictions(const GrRenderTarge
// creation. It isn't clear that avoiding temporary fbo creation is actually optimal.
DstCopyRestrictions blitFramebufferRestrictions = {};
if (src->numSamples() > 1 &&
(this->blitFramebufferSupportFlags() & kResolveMustBeFull_BlitFrambufferFlag)) {
(fBlitFramebufferFlags & kResolveMustBeFull_BlitFrambufferFlag)) {
blitFramebufferRestrictions.fRectsMustMatch = GrSurfaceProxy::RectsMustMatch::kYes;
blitFramebufferRestrictions.fMustCopyWholeSrc = true;
// Mirroring causes rects to mismatch later, don't allow it.
} else if (src->numSamples() > 1 && (this->blitFramebufferSupportFlags() &
} else if (src->numSamples() > 1 && (fBlitFramebufferFlags &
kRectsMustMatchForMSAASrc_BlitFramebufferFlag)) {
blitFramebufferRestrictions.fRectsMustMatch = GrSurfaceProxy::RectsMustMatch::kYes;
}

View File

@ -266,9 +266,22 @@ public:
}
/**
* What functionality is supported by glBlitFramebuffer.
* Is it unsupported to only resolve a sub-rectangle of a framebuffer?
*/
uint32_t blitFramebufferSupportFlags() const { return fBlitFramebufferFlags; }
bool framebufferResolvesMustBeFullSize() const {
SkASSERT(fMSFBOType != kNone_MSFBOType);
return fMSFBOType == kES_Apple_MSFBOType ||
(fBlitFramebufferFlags & kResolveMustBeFull_BlitFrambufferFlag);
}
/**
* Can we resolve a single-sample framebuffer into an MSAA framebuffer?
*/
bool canResolveSingleToMSAA() const {
SkASSERT(fMSFBOType != kNone_MSFBOType);
return fMSFBOType != kES_Apple_MSFBOType &&
!(fBlitFramebufferFlags & GrGLCaps::kNoMSAADst_BlitFramebufferFlag);
}
/**
* Is the MSAA FBO extension one where the texture is multisampled when bound to an FBO and

View File

@ -2295,8 +2295,13 @@ GrGLenum GrGLGpu::prepareToDraw(GrPrimitiveType primitiveType) {
}
void GrGLGpu::onResolveRenderTarget(GrRenderTarget* target, const SkIRect& resolveRect) {
this->resolveRenderFBOs(static_cast<GrGLRenderTarget*>(target), resolveRect,
ResolveDirection::kMSAAToSingle);
auto glRT = static_cast<GrGLRenderTarget*>(target);
if (this->glCaps().framebufferResolvesMustBeFullSize()) {
this->resolveRenderFBOs(glRT, SkIRect::MakeSize(glRT->dimensions()),
ResolveDirection::kMSAAToSingle);
} else {
this->resolveRenderFBOs(glRT, resolveRect, ResolveDirection::kMSAAToSingle);
}
}
void GrGLGpu::resolveRenderFBOs(GrGLRenderTarget* rt, const SkIRect& resolveRect,
@ -2315,13 +2320,7 @@ void GrGLGpu::resolveRenderFBOs(GrGLRenderTarget* rt, const SkIRect& resolveRect
this->bindFramebuffer(GR_GL_DRAW_FRAMEBUFFER, rt->singleSampleFBOID());
} else {
SkASSERT(resolveDirection == ResolveDirection::kSingleToMSAA);
if (caps.msFBOType() == GrGLCaps::kES_Apple_MSFBOType ||
(caps.blitFramebufferSupportFlags() & GrGLCaps::kNoMSAADst_BlitFramebufferFlag)) {
// Blitting from single to multisample is not supported. Make a draw instead.
this->copySurfaceAsDraw(rt, true/*drawToMultisampleFBO*/, rt, resolveRect,
resolveRect.topLeft());
return;
}
SkASSERT(this->glCaps().canResolveSingleToMSAA());
this->bindFramebuffer(GR_GL_READ_FRAMEBUFFER, rt->singleSampleFBOID());
this->bindFramebuffer(GR_GL_DRAW_FRAMEBUFFER, rt->multisampleFBOID());
}
@ -2332,6 +2331,7 @@ void GrGLGpu::resolveRenderFBOs(GrGLRenderTarget* rt, const SkIRect& resolveRect
if (GrGLCaps::kES_Apple_MSFBOType == caps.msFBOType()) {
// The Apple extension doesn't support blitting from single to multisample.
SkASSERT(resolveDirection != ResolveDirection::kSingleToMSAA);
SkASSERT(resolveRect == SkIRect::MakeSize(rt->dimensions()));
// 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.
@ -2341,18 +2341,12 @@ void GrGLGpu::resolveRenderFBOs(GrGLRenderTarget* rt, const SkIRect& resolveRect
this->disableWindowRectangles();
GL_CALL(ResolveMultisampleFramebuffer());
} else {
int l, b, r, t;
if (caps.blitFramebufferSupportFlags() & GrGLCaps::kResolveMustBeFull_BlitFrambufferFlag) {
l = 0;
b = 0;
r = rt->width();
t = rt->height();
} else {
l = resolveRect.x();
b = resolveRect.y();
r = resolveRect.x() + resolveRect.width();
t = resolveRect.y() + resolveRect.height();
}
SkASSERT(!caps.framebufferResolvesMustBeFullSize() ||
resolveRect == SkIRect::MakeSize(rt->dimensions()));
int l = resolveRect.x();
int b = resolveRect.y();
int r = resolveRect.x() + resolveRect.width();
int t = resolveRect.y() + resolveRect.height();
// BlitFrameBuffer respects the scissor, so disable it.
this->flushScissorTest(GrScissorTest::kDisabled);

View File

@ -104,13 +104,26 @@ public:
GrGLenum prepareToDraw(GrPrimitiveType primitiveType);
enum class ResolveDirection : bool {
kSingleToMSAA,
kSingleToMSAA, // glCaps.canResolveSingleToMSAA() must be true.
kMSAAToSingle
};
// Resolves the render target's single sample FBO into the MSAA, or vice versa.
// If glCaps.framebufferResolvesMustBeFullSize() is true, resolveRect must be equal the render
// target's bounds rect.
// If blitting single to MSAA, glCaps.canResolveSingleToMSAA() must be true.
void resolveRenderFBOs(GrGLRenderTarget*, const SkIRect& resolveRect, ResolveDirection,
bool invalidateReadBufferAfterBlit = false);
// For loading a dynamic MSAA framebuffer when glCaps.canResolveSingleToMSAA() is false.
// NOTE: If glCaps.framebufferResolvesMustBeFullSize() is also true, the drawBounds should be
// equal to the proxy bounds. This is because the render pass will have to do a full size
// resolve back into the single sample FBO when rendering is complete.
void drawSingleIntoMSAAFBO(GrGLRenderTarget* rt, const SkIRect& drawBounds) {
this->copySurfaceAsDraw(rt, true/*drawToMultisampleFBO*/, rt, drawBounds,
drawBounds.topLeft());
}
// 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.

View File

@ -31,15 +31,31 @@ void GrGLOpsRenderPass::set(GrRenderTarget* rt, bool useMSAASurface, const SkIRe
fStencilLoadAndStoreInfo = stencilInfo;
}
GrNativeRect GrGLOpsRenderPass::dmsaaLoadStoreBounds() const {
if (fGpu->glCaps().framebufferResolvesMustBeFullSize()) {
// If frambeffer resolves have to be full size, then resolve the entire render target during
// load and store both, even if we will be doing so with a draw. We do this because we will
// have no other choice than to do a full size resolve at the end of the render pass, so the
// full DMSAA attachment needs to have valid content.
return GrNativeRect::MakeRelativeTo(fOrigin, fRenderTarget->height(),
SkIRect::MakeSize(fRenderTarget->dimensions()));
} else {
return GrNativeRect::MakeRelativeTo(fOrigin, fRenderTarget->height(), fContentBounds);
}
}
void GrGLOpsRenderPass::onBegin() {
auto glRT = static_cast<GrGLRenderTarget*>(fRenderTarget);
if (fUseMultisampleFBO && fColorLoadAndStoreInfo.fLoadOp == GrLoadOp::kLoad &&
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);
// Load the single sample fbo into the dmsaa attachment.
if (fGpu->glCaps().canResolveSingleToMSAA()) {
fGpu->resolveRenderFBOs(glRT, this->dmsaaLoadStoreBounds().asSkIRect(),
GrGLGpu::ResolveDirection::kSingleToMSAA);
} else {
fGpu->drawSingleIntoMSAAFBO(glRT, this->dmsaaLoadStoreBounds().asSkIRect());
}
}
fGpu->beginCommandBuffer(glRT, fUseMultisampleFBO, fContentBounds, fOrigin,
@ -51,12 +67,11 @@ void GrGLOpsRenderPass::onEnd() {
fGpu->endCommandBuffer(glRT, fUseMultisampleFBO, fColorLoadAndStoreInfo,
fStencilLoadAndStoreInfo);
if (fUseMultisampleFBO && fColorLoadAndStoreInfo.fStoreOp == GrStoreOp::kStore &&
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(),
fGpu->resolveRenderFBOs(glRT, this->dmsaaLoadStoreBounds().asSkIRect(),
GrGLGpu::ResolveDirection::kMSAAToSingle,
true /*invalidateReadBufferAfterBlit*/);
}

View File

@ -51,6 +51,12 @@ private:
return fIndexPointer + baseIndex;
}
// Ideally we load and store DMSAA only within the content bounds of our render pass, but if
// the caps don't allow for partial framebuffer blits, we resolve the full target.
// We resolve the same bounds during load and store both because if we have to do a full size
// resolve at the end, the full DMSAA attachment needs to have valid content.
GrNativeRect dmsaaLoadStoreBounds() const;
void onBegin() override;
void onEnd() override;
bool onBindPipeline(const GrProgramInfo& programInfo, const SkRect& drawBounds) override;