Fix DMSAA loads on ES/ANGLE
ES and ANGLE don't support framebuffer blits from single to multisample. On these platforms we now perform a draw instead. Bug: skia:12069 Bug: angleproject:6030 Change-Id: Ida0a5e9b556c4715fa63cb98aba185e64c72b28e Reviewed-on: https://skia-review.googlesource.com/c/skia/+/415767 Reviewed-by: Brian Salomon <bsalomon@google.com> Commit-Queue: Chris Dalton <csmartdalton@google.com>
This commit is contained in:
parent
89d460f27b
commit
7ad3aa20c9
@ -55,6 +55,7 @@ tests_sources = [
|
||||
"$_tests/CopySurfaceTest.cpp",
|
||||
"$_tests/CubicMapTest.cpp",
|
||||
"$_tests/CullTestTest.cpp",
|
||||
"$_tests/DMSAATest.cpp",
|
||||
"$_tests/DSLFPTest.cpp",
|
||||
"$_tests/DashPathEffectTest.cpp",
|
||||
"$_tests/DataRefTest.cpp",
|
||||
|
@ -58,6 +58,7 @@ GrGLCaps::GrGLCaps(const GrContextOptions& contextOptions,
|
||||
fNeverDisableColorWrites = false;
|
||||
fMustSetAnyTexParameterToEnableMipmapping = false;
|
||||
fAllowBGRA8CopyTexSubImage = false;
|
||||
fDisallowDynamicMSAA = false;
|
||||
fProgramBinarySupport = false;
|
||||
fProgramParameterSupport = false;
|
||||
fSamplerObjectSupport = false;
|
||||
@ -3496,6 +3497,12 @@ void GrGLCaps::applyDriverCorrectnessWorkarounds(const GrGLContextInfo& ctxInfo,
|
||||
fAllowBGRA8CopyTexSubImage = true;
|
||||
}
|
||||
|
||||
// anglebug.com/6030
|
||||
if (fMSFBOType == kES_EXT_MsToTexture_MSFBOType &&
|
||||
ctxInfo.angleBackend() == GrGLANGLEBackend::kD3D11) {
|
||||
fDisallowDynamicMSAA = true;
|
||||
}
|
||||
|
||||
#if defined(__has_feature)
|
||||
#if defined(SK_BUILD_FOR_MAC) && __has_feature(thread_sanitizer)
|
||||
// See skbug.com/7058
|
||||
|
@ -506,17 +506,7 @@ private:
|
||||
GrDstSampleFlags onGetDstSampleFlagsForProxy(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;
|
||||
return !fDisallowDynamicMSAA;
|
||||
}
|
||||
|
||||
GrGLStandard fStandard = kNone_GrGLStandard;
|
||||
@ -569,6 +559,7 @@ private:
|
||||
bool fNeverDisableColorWrites : 1;
|
||||
bool fMustSetAnyTexParameterToEnableMipmapping : 1;
|
||||
bool fAllowBGRA8CopyTexSubImage : 1;
|
||||
bool fDisallowDynamicMSAA : 1;
|
||||
int fMaxInstancesPerDrawWithoutCrashing = 0;
|
||||
|
||||
uint32_t fBlitFramebufferFlags = kNoSupport_BlitFramebufferFlag;
|
||||
|
@ -2343,11 +2343,20 @@ void GrGLGpu::resolveRenderFBOs(GrGLRenderTarget* rt, const SkIRect& resolveRect
|
||||
// single sample buffer is FBO 0). If it's zero, then there's nothing to resolve.
|
||||
SkASSERT(rt->multisampleFBOID() != 0);
|
||||
|
||||
const GrGLCaps& caps = this->glCaps();
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
this->bindFramebuffer(GR_GL_READ_FRAMEBUFFER, rt->singleSampleFBOID());
|
||||
this->bindFramebuffer(GR_GL_DRAW_FRAMEBUFFER, rt->multisampleFBOID());
|
||||
}
|
||||
@ -2355,7 +2364,7 @@ void GrGLGpu::resolveRenderFBOs(GrGLRenderTarget* rt, const SkIRect& resolveRect
|
||||
// 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()) {
|
||||
if (GrGLCaps::kES_Apple_MSFBOType == caps.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.
|
||||
@ -2368,8 +2377,7 @@ void GrGLGpu::resolveRenderFBOs(GrGLRenderTarget* rt, const SkIRect& resolveRect
|
||||
GL_CALL(ResolveMultisampleFramebuffer());
|
||||
} else {
|
||||
int l, b, r, t;
|
||||
if (GrGLCaps::kResolveMustBeFull_BlitFrambufferFlag &
|
||||
this->glCaps().blitFramebufferSupportFlags()) {
|
||||
if (caps.blitFramebufferSupportFlags() & GrGLCaps::kResolveMustBeFull_BlitFrambufferFlag) {
|
||||
l = 0;
|
||||
b = 0;
|
||||
r = rt->width();
|
||||
@ -2387,17 +2395,21 @@ void GrGLGpu::resolveRenderFBOs(GrGLRenderTarget* rt, const SkIRect& resolveRect
|
||||
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 &&
|
||||
if (caps.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) {
|
||||
if (caps.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));
|
||||
SkASSERT(caps.invalidateFBType() == GrGLCaps::kDiscard_InvalidateFBType);
|
||||
// glDiscardFramebuffer only accepts GL_FRAMEBUFFER.
|
||||
GrGLuint discardFBO = (resolveDirection == ResolveDirection::kMSAAToSingle)
|
||||
? rt->multisampleFBOID() : rt->singleSampleFBOID();
|
||||
this->bindFramebuffer(GR_GL_FRAMEBUFFER, discardFBO);
|
||||
GL_CALL(DiscardFramebuffer(GR_GL_FRAMEBUFFER, 1, &colorDiscardAttachment));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2925,7 +2937,6 @@ static inline bool can_copy_texsubimage(const GrSurface* dst, const GrSurface* s
|
||||
srcFormat, srcHasMSAARenderBuffer, srcTexTypePtr);
|
||||
}
|
||||
|
||||
// If a temporary FBO was created, its non-zero ID is returned.
|
||||
void GrGLGpu::bindSurfaceFBOForPixelOps(GrSurface* surface, int mipLevel, GrGLenum fboTarget,
|
||||
TempFBOTarget tempFBOTarget) {
|
||||
GrGLRenderTarget* rt = static_cast<GrGLRenderTarget*>(surface->asRenderTarget());
|
||||
@ -3021,7 +3032,9 @@ bool GrGLGpu::onCopySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRe
|
||||
bool preferCopy = SkToBool(dst->asRenderTarget());
|
||||
auto dstFormat = dst->backendFormat().asGLFormat();
|
||||
if (preferCopy && this->glCaps().canCopyAsDraw(dstFormat, SkToBool(src->asTexture()))) {
|
||||
if (this->copySurfaceAsDraw(dst, src, srcRect, dstPoint)) {
|
||||
GrRenderTarget* dstRT = dst->asRenderTarget();
|
||||
bool drawToMultisampleFBO = dstRT && dstRT->numSamples() > 1;
|
||||
if (this->copySurfaceAsDraw(dst, drawToMultisampleFBO, src, srcRect, dstPoint)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -3036,7 +3049,9 @@ bool GrGLGpu::onCopySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRe
|
||||
}
|
||||
|
||||
if (!preferCopy && this->glCaps().canCopyAsDraw(dstFormat, SkToBool(src->asTexture()))) {
|
||||
if (this->copySurfaceAsDraw(dst, src, srcRect, dstPoint)) {
|
||||
GrRenderTarget* dstRT = dst->asRenderTarget();
|
||||
bool drawToMultisampleFBO = dstRT && dstRT->numSamples() > 1;
|
||||
if (this->copySurfaceAsDraw(dst, drawToMultisampleFBO, src, srcRect, dstPoint)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -3311,36 +3326,38 @@ bool GrGLGpu::createMipmapProgram(int progIdx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GrGLGpu::copySurfaceAsDraw(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
|
||||
const SkIPoint& dstPoint) {
|
||||
bool GrGLGpu::copySurfaceAsDraw(GrSurface* dst, bool drawToMultisampleFBO, GrSurface* src,
|
||||
const SkIRect& srcRect, const SkIPoint& dstPoint) {
|
||||
auto* srcTex = static_cast<GrGLTexture*>(src->asTexture());
|
||||
auto* dstTex = static_cast<GrGLTexture*>(src->asTexture());
|
||||
auto* dstRT = static_cast<GrGLRenderTarget*>(src->asRenderTarget());
|
||||
if (!srcTex) {
|
||||
return false;
|
||||
}
|
||||
int progIdx = TextureToCopyProgramIdx(srcTex);
|
||||
if (!dstRT) {
|
||||
// We don't swizzle at all in our copies.
|
||||
this->bindTexture(0, GrSamplerState::Filter::kNearest, GrSwizzle::RGBA(), srcTex);
|
||||
if (auto* dstRT = static_cast<GrGLRenderTarget*>(dst->asRenderTarget())) {
|
||||
this->flushRenderTargetNoColorWrites(dstRT, drawToMultisampleFBO);
|
||||
} else {
|
||||
auto* dstTex = static_cast<GrGLTexture*>(src->asTexture());
|
||||
SkASSERT(dstTex);
|
||||
SkASSERT(!drawToMultisampleFBO);
|
||||
if (!this->glCaps().isFormatRenderable(dstTex->format(), 1)) {
|
||||
return false;
|
||||
}
|
||||
this->bindSurfaceFBOForPixelOps(dst, 0, GR_GL_FRAMEBUFFER, kDst_TempFBOTarget);
|
||||
fHWBoundRenderTargetUniqueID.makeInvalid();
|
||||
}
|
||||
int progIdx = TextureToCopyProgramIdx(srcTex);
|
||||
if (!fCopyPrograms[progIdx].fProgram) {
|
||||
if (!this->createCopyProgram(srcTex)) {
|
||||
SkDebugf("Failed to create copy program.\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
int w = srcRect.width();
|
||||
int h = srcRect.height();
|
||||
// We don't swizzle at all in our copies.
|
||||
this->bindTexture(0, GrSamplerState::Filter::kNearest, GrSwizzle::RGBA(), srcTex);
|
||||
this->bindSurfaceFBOForPixelOps(dst, 0, GR_GL_FRAMEBUFFER, kDst_TempFBOTarget);
|
||||
this->flushViewport(SkIRect::MakeSize(dst->dimensions()),
|
||||
dst->height(),
|
||||
kTopLeft_GrSurfaceOrigin); // the origin is irrelevant in this case
|
||||
fHWBoundRenderTargetUniqueID.makeInvalid();
|
||||
int w = srcRect.width();
|
||||
int h = srcRect.height();
|
||||
SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.fX, dstPoint.fY, w, h);
|
||||
this->flushProgram(fCopyPrograms[progIdx].fProgram);
|
||||
fHWVertexArrayState.setVertexArrayID(this, 0);
|
||||
|
@ -368,8 +368,8 @@ private:
|
||||
|
||||
bool waitSync(GrGLsync, uint64_t timeout, bool flush);
|
||||
|
||||
bool copySurfaceAsDraw(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
|
||||
const SkIPoint& dstPoint);
|
||||
bool copySurfaceAsDraw(GrSurface* dst, bool drawToMultisampleFBO, GrSurface* src,
|
||||
const SkIRect& srcRect, const SkIPoint& dstPoint);
|
||||
void copySurfaceAsCopyTexSubImage(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
|
||||
const SkIPoint& dstPoint);
|
||||
bool copySurfaceAsBlitFramebuffer(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
|
||||
|
91
tests/DMSAATest.cpp
Normal file
91
tests/DMSAATest.cpp
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2021 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "tests/Test.h"
|
||||
|
||||
#include "include/core/SkVertices.h"
|
||||
#include "src/core/SkBlendModePriv.h"
|
||||
#include "src/core/SkMatrixProvider.h"
|
||||
#include "src/core/SkSurfacePriv.h"
|
||||
#include "src/gpu/GrStyle.h"
|
||||
#include "src/gpu/GrSurfaceDrawContext.h"
|
||||
|
||||
static SkSurfaceProps kDMSAAProps(kDMSAA_SkSurfacePropsPrivateFlag, kUnknown_SkPixelGeometry);
|
||||
constexpr static SkPMColor4f kTransYellow = {.5f,.5f,.0f,.5f};
|
||||
constexpr static SkPMColor4f kTransCyan = {.0f,.5f,.5f,.5f};
|
||||
constexpr static int w=10, h=10;
|
||||
|
||||
static void draw_paint_with_dmsaa(GrSurfaceDrawContext* sdc, const SkPMColor4f& color,
|
||||
SkBlendMode blendMode) {
|
||||
// drawVertices should always trigger dmsaa, but draw something non-rectangular just to be 100%
|
||||
// certain.
|
||||
static const SkPoint kVertices[3] = {{-.5f,-.5f}, {w * 2.1f, 0}, {0, h * 2.1f}};
|
||||
SkVertices::Builder builder(SkVertices::kTriangles_VertexMode, 3, 0, 0);
|
||||
memcpy(builder.positions(), kVertices, sizeof(kVertices));
|
||||
auto vertices = builder.detach();
|
||||
|
||||
GrPaint paint;
|
||||
paint.setColor4f(color);
|
||||
paint.setXPFactory(SkBlendMode_AsXPFactory(blendMode));
|
||||
sdc->drawVertices(nullptr, std::move(paint), SkSimpleMatrixProvider(SkMatrix::I()), vertices);
|
||||
}
|
||||
|
||||
static bool fuzzy_equals(const float a[4], const SkPMColor4f& b) {
|
||||
constexpr static float kTolerance = 2.5f / 256;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (!SkScalarNearlyEqual(a[i], b.vec()[i], kTolerance)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void check_sdc_color(skiatest::Reporter* reporter, GrSurfaceDrawContext* sdc,
|
||||
GrDirectContext* ctx, const SkPMColor4f& color) {
|
||||
auto info = SkImageInfo::Make(w, h, kRGBA_F32_SkColorType, kPremul_SkAlphaType);
|
||||
GrPixmap pixmap = GrPixmap::Allocate(info);
|
||||
sdc->readPixels(ctx, pixmap, {0, 0});
|
||||
auto pix = static_cast<const float*>(pixmap.addr());
|
||||
for (int y = 0; y < h; ++y) {
|
||||
for (int x = 0; x < w; ++x) {
|
||||
if (!fuzzy_equals(pix, color)) {
|
||||
ERRORF(reporter, "SDC color mismatch.\n"
|
||||
"Got [%0.3f, %0.3f, %0.3f, %0.3f]\n"
|
||||
"Expected [%0.3f, %0.3f, %0.3f, %0.3f]",
|
||||
pix[0], pix[1], pix[2], pix[3], color.fR, color.fG, color.fB, color.fA);
|
||||
return;
|
||||
}
|
||||
pix += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEF_GPUTEST_FOR_CONTEXTS(DMSAA_preserve_contents,
|
||||
&sk_gpu_test::GrContextFactory::IsRenderingContext, reporter, ctxInfo,
|
||||
nullptr) {
|
||||
auto ctx = ctxInfo.directContext();
|
||||
auto sdc = GrSurfaceDrawContext::Make(ctx, GrColorType::kRGBA_8888, nullptr,
|
||||
SkBackingFit::kApprox, {w, h}, kDMSAAProps);
|
||||
|
||||
// Initialize the texture and dmsaa attachment with transparent.
|
||||
draw_paint_with_dmsaa(sdc.get(), SK_PMColor4fTRANSPARENT, SkBlendMode::kSrc);
|
||||
check_sdc_color(reporter, sdc.get(), ctx, SK_PMColor4fTRANSPARENT);
|
||||
|
||||
// Clear the main texture to yellow.
|
||||
sdc->clear(kTransYellow);
|
||||
|
||||
// Close the opsTask by doing a readback.
|
||||
check_sdc_color(reporter, sdc.get(), ctx, kTransYellow);
|
||||
|
||||
// Now the DMSAA attachment is clear and the texture is yellow. Blend cyan into the DMSAA
|
||||
// attachment. This will fail if the yellow from the main texture doesn't get copied into the
|
||||
// DMSAA attachment before the renderPass.
|
||||
draw_paint_with_dmsaa(sdc.get(), kTransCyan, SkBlendMode::kSrcOver);
|
||||
SkPMColor4f dstColor = SkBlendMode_Apply(SkBlendMode::kSrcOver, kTransCyan, kTransYellow);
|
||||
|
||||
check_sdc_color(reporter, sdc.get(), ctx, dstColor);
|
||||
}
|
Loading…
Reference in New Issue
Block a user