Defer the attachment of GL stencil buffers
This will allow us to use a single FBO for EXT_multisampled_render_to_texture, that we modify on-demand depending on whether we need MSAA. Bug: chromium:1222095 Change-Id: Ife2d743e28833521d785e4bf0e20de593c492a9a Reviewed-on: https://skia-review.googlesource.com/c/skia/+/442736 Commit-Queue: Jim Van Verth <jvanverth@google.com> Reviewed-by: Brian Salomon <bsalomon@google.com>
This commit is contained in:
parent
14cc21fd99
commit
c50fefbba7
@ -4277,8 +4277,7 @@ GrCaps::SurfaceReadPixelsSupport GrGLCaps::surfaceSupportsReadPixels(
|
||||
} else if (auto rt = static_cast<const GrGLRenderTarget*>(surface->asRenderTarget())) {
|
||||
// glReadPixels does not allow reading back from a MSAA framebuffer. If the underlying
|
||||
// GrSurface doesn't have a second FBO to resolve to then we must make a copy.
|
||||
if (rt->numSamples() > 1 &&
|
||||
rt->singleSampleFBOID() == GrGLRenderTarget::kUnresolvableFBOID) {
|
||||
if (rt->numSamples() > 1 && !rt->asTexture()) {
|
||||
return SurfaceReadPixelsSupport::kCopyToTexture2D;
|
||||
}
|
||||
}
|
||||
|
@ -1996,14 +1996,12 @@ void GrGLGpu::endCommandBuffer(GrGLRenderTarget* rt, bool useMultisampleFBO,
|
||||
if (GrGLCaps::kNone_InvalidateFBType != this->glCaps().invalidateFBType()) {
|
||||
SkSTArray<2, GrGLenum> discardAttachments;
|
||||
if (GrStoreOp::kDiscard == colorLoadStore.fStoreOp) {
|
||||
GrGLuint renderFBOID = (useMultisampleFBO) ? rt->multisampleFBOID()
|
||||
: rt->singleSampleFBOID();
|
||||
discardAttachments.push_back((!renderFBOID) ? GR_GL_COLOR : GR_GL_COLOR_ATTACHMENT0);
|
||||
discardAttachments.push_back(
|
||||
rt->isFBO0(useMultisampleFBO) ? GR_GL_COLOR : GR_GL_COLOR_ATTACHMENT0);
|
||||
}
|
||||
if (GrStoreOp::kDiscard == stencilLoadStore.fStoreOp) {
|
||||
GrGLuint renderFBOID = (useMultisampleFBO) ? rt->multisampleFBOID()
|
||||
: rt->singleSampleFBOID();
|
||||
discardAttachments.push_back((!renderFBOID) ? GR_GL_STENCIL : GR_GL_STENCIL_ATTACHMENT);
|
||||
discardAttachments.push_back(
|
||||
rt->isFBO0(useMultisampleFBO) ? GR_GL_STENCIL : GR_GL_STENCIL_ATTACHMENT);
|
||||
|
||||
}
|
||||
|
||||
@ -2101,11 +2099,11 @@ bool GrGLGpu::readOrTransferPixelsFrom(GrSurface* surface,
|
||||
|
||||
if (renderTarget) {
|
||||
// Always bind the single sample FBO since we can't read pixels from an MSAA framebuffer.
|
||||
if (renderTarget->numSamples() > 1 &&
|
||||
renderTarget->singleSampleFBOID() == GrGLRenderTarget::kUnresolvableFBOID) {
|
||||
constexpr bool useMultisampleFBO = false;
|
||||
if (renderTarget->numSamples() > 1 && renderTarget->isFBO0(useMultisampleFBO)) {
|
||||
return false;
|
||||
}
|
||||
this->flushRenderTargetNoColorWrites(renderTarget, false/*useMultisampleFBO*/);
|
||||
this->flushRenderTargetNoColorWrites(renderTarget, useMultisampleFBO);
|
||||
} else {
|
||||
// Use a temporary FBO.
|
||||
this->bindSurfaceFBOForPixelOps(surface, 0, GR_GL_FRAMEBUFFER, kSrc_TempFBOTarget);
|
||||
@ -2206,8 +2204,7 @@ void GrGLGpu::flushRenderTargetNoColorWrites(GrGLRenderTarget* target, bool useM
|
||||
SkASSERT(target);
|
||||
GrGpuResource::UniqueID rtID = target->uniqueID();
|
||||
if (fHWBoundRenderTargetUniqueID != rtID || fHWBoundFramebufferIsMSAA != useMultisampleFBO) {
|
||||
this->bindFramebuffer(GR_GL_FRAMEBUFFER, (useMultisampleFBO) ? target->multisampleFBOID()
|
||||
: target->singleSampleFBOID());
|
||||
target->bind(useMultisampleFBO);
|
||||
#ifdef SK_DEBUG
|
||||
// don't do this check in Chromium -- this is causing
|
||||
// lots of repeated command buffer flushes when the compositor is
|
||||
@ -2310,23 +2307,10 @@ 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);
|
||||
rt->bindForResolve(resolveDirection);
|
||||
|
||||
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);
|
||||
SkASSERT(this->glCaps().canResolveSingleToMSAA());
|
||||
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();
|
||||
@ -2360,16 +2344,15 @@ void GrGLGpu::resolveRenderFBOs(GrGLRenderTarget* rt, const SkIRect& resolveRect
|
||||
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;
|
||||
bool readBufferIsMSAA = resolveDirection == ResolveDirection::kMSAAToSingle;
|
||||
GrGLenum colorDiscardAttachment = rt->isFBO0(readBufferIsMSAA) ? GR_GL_COLOR
|
||||
: GR_GL_COLOR_ATTACHMENT0;
|
||||
if (caps.invalidateFBType() == GrGLCaps::kInvalidate_InvalidateFBType) {
|
||||
GL_CALL(InvalidateFramebuffer(GR_GL_READ_FRAMEBUFFER, 1, &colorDiscardAttachment));
|
||||
} else {
|
||||
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);
|
||||
rt->bind(readBufferIsMSAA);
|
||||
GL_CALL(DiscardFramebuffer(GR_GL_FRAMEBUFFER, 1, &colorDiscardAttachment));
|
||||
}
|
||||
}
|
||||
@ -2858,7 +2841,7 @@ static bool rt_has_msaa_render_buffer(const GrGLRenderTarget* rt, const GrGLCaps
|
||||
// 1) It's multisampled
|
||||
// 2) We're using an extension with separate MSAA renderbuffers
|
||||
// 3) It's not FBO 0, which is special and always auto-resolves
|
||||
return rt->numSamples() > 1 && glCaps.usesMSAARenderBuffers() && rt->multisampleFBOID() != 0;
|
||||
return rt->numSamples() > 1 && glCaps.usesMSAARenderBuffers() && !rt->isFBO0(true/*msaa*/);
|
||||
}
|
||||
|
||||
static inline bool can_copy_texsubimage(const GrSurface* dst, const GrSurface* src,
|
||||
@ -2914,10 +2897,8 @@ void GrGLGpu::bindSurfaceFBOForPixelOps(GrSurface* surface, int mipLevel, GrGLen
|
||||
if (mipLevel == 0) {
|
||||
texture->baseLevelWasBoundToFBO();
|
||||
}
|
||||
} else if (rt->numSamples() > 1) {
|
||||
this->bindFramebuffer(fboTarget, rt->multisampleFBOID());
|
||||
} else {
|
||||
this->bindFramebuffer(fboTarget, rt->singleSampleFBOID());
|
||||
rt->bindForPixelOps(fboTarget);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3540,7 +3521,7 @@ void GrGLGpu::xferBarrier(GrRenderTarget* rt, GrXferBarrierType type) {
|
||||
case kTexture_GrXferBarrierType: {
|
||||
GrGLRenderTarget* glrt = static_cast<GrGLRenderTarget*>(rt);
|
||||
SkASSERT(glrt->asTexture());
|
||||
SkASSERT(glrt->singleSampleFBOID() != 0);
|
||||
SkASSERT(!glrt->isFBO0(false/*multisample*/));
|
||||
if (glrt->requiresManualMSAAResolve()) {
|
||||
// The render target uses separate storage so no need for glTextureBarrier.
|
||||
// FIXME: The render target will resolve automatically when its texture is bound,
|
||||
|
@ -103,10 +103,7 @@ 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, // glCaps.canResolveSingleToMSAA() must be true.
|
||||
kMSAAToSingle
|
||||
};
|
||||
using ResolveDirection = GrGLRenderTarget::ResolveDirection;
|
||||
|
||||
// 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
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
#define GPUGL static_cast<GrGLGpu*>(this->getGpu())
|
||||
#define GL_CALL(X) GR_GL_CALL(GPUGL->glInterface(), X)
|
||||
#define GL_CALL_RET(RET, X) GR_GL_CALL_RET(GPUGL->glInterface(), RET, X)
|
||||
|
||||
// Because this class is virtually derived from GrSurface we must explicitly call its constructor.
|
||||
// Constructor for wrapped render targets.
|
||||
@ -131,54 +132,8 @@ size_t GrGLRenderTarget::onGpuMemorySize() const {
|
||||
}
|
||||
|
||||
bool GrGLRenderTarget::completeStencilAttachment(GrAttachment* stencil, bool useMultisampleFBO) {
|
||||
GrGLGpu* gpu = this->getGLGpu();
|
||||
const GrGLInterface* interface = gpu->glInterface();
|
||||
|
||||
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) {
|
||||
GR_GL_CALL(interface, FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
|
||||
GR_GL_STENCIL_ATTACHMENT,
|
||||
GR_GL_RENDERBUFFER, 0));
|
||||
GR_GL_CALL(interface, FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
|
||||
GR_GL_DEPTH_ATTACHMENT,
|
||||
GR_GL_RENDERBUFFER, 0));
|
||||
} else {
|
||||
const GrGLAttachment* glStencil = static_cast<const GrGLAttachment*>(stencil);
|
||||
GrGLuint rb = glStencil->renderbufferID();
|
||||
GR_GL_CALL(interface, FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
|
||||
GR_GL_STENCIL_ATTACHMENT,
|
||||
GR_GL_RENDERBUFFER, rb));
|
||||
if (GrGLFormatIsPackedDepthStencil(glStencil->format())) {
|
||||
GR_GL_CALL(interface, FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
|
||||
GR_GL_DEPTH_ATTACHMENT,
|
||||
GR_GL_RENDERBUFFER, rb));
|
||||
} else {
|
||||
GR_GL_CALL(interface, FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
|
||||
GR_GL_DEPTH_ATTACHMENT,
|
||||
GR_GL_RENDERBUFFER, 0));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
if (!gpu->glCaps().skipErrorChecks()) {
|
||||
GrGLenum status;
|
||||
GR_GL_CALL_RET(interface, status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
|
||||
if (status != GR_GL_FRAMEBUFFER_COMPLETE) {
|
||||
// This can fail if the context has been asynchronously abandoned (see skbug.com/5200).
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// We defer attaching the new stencil buffer until the next time our framebuffer is bound.
|
||||
fStencilAttachmentIsValid[useMultisampleFBO] = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -226,6 +181,73 @@ bool GrGLRenderTarget::ensureDynamicMSAAAttachment() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void GrGLRenderTarget::bindInternal(GrGLenum fboTarget, bool useMultisampleFBO) {
|
||||
GrGLuint fboId = useMultisampleFBO ? fMultisampleFBOID : fSingleSampleFBOID;
|
||||
this->getGLGpu()->bindFramebuffer(fboTarget, fboId);
|
||||
|
||||
// Make sure the stencil attachment is valid. Even though a color buffer op doesn't use stencil,
|
||||
// our FBO still needs to be "framebuffer complete".
|
||||
if (!fStencilAttachmentIsValid[useMultisampleFBO]) {
|
||||
if (auto stencil = this->getStencilAttachment(useMultisampleFBO)) {
|
||||
const GrGLAttachment* glStencil = static_cast<const GrGLAttachment*>(stencil);
|
||||
GL_CALL(FramebufferRenderbuffer(fboTarget,
|
||||
GR_GL_STENCIL_ATTACHMENT,
|
||||
GR_GL_RENDERBUFFER,
|
||||
glStencil->renderbufferID()));
|
||||
if (GrGLFormatIsPackedDepthStencil(glStencil->format())) {
|
||||
GL_CALL(FramebufferRenderbuffer(fboTarget,
|
||||
GR_GL_DEPTH_ATTACHMENT,
|
||||
GR_GL_RENDERBUFFER,
|
||||
glStencil->renderbufferID()));
|
||||
} else {
|
||||
GL_CALL(FramebufferRenderbuffer(fboTarget,
|
||||
GR_GL_DEPTH_ATTACHMENT,
|
||||
GR_GL_RENDERBUFFER,
|
||||
0));
|
||||
}
|
||||
} else {
|
||||
GL_CALL(FramebufferRenderbuffer(fboTarget,
|
||||
GR_GL_STENCIL_ATTACHMENT,
|
||||
GR_GL_RENDERBUFFER,
|
||||
0));
|
||||
GL_CALL(FramebufferRenderbuffer(fboTarget,
|
||||
GR_GL_DEPTH_ATTACHMENT,
|
||||
GR_GL_RENDERBUFFER,
|
||||
0));
|
||||
}
|
||||
#ifdef SK_DEBUG
|
||||
if (!this->getGLGpu()->glCaps().skipErrorChecks()) {
|
||||
GrGLenum status;
|
||||
GL_CALL_RET(status, CheckFramebufferStatus(fboTarget));
|
||||
if (status != GR_GL_FRAMEBUFFER_COMPLETE) {
|
||||
// This can fail if the context has been asynchronously abandoned (see
|
||||
// skbug.com/5200).
|
||||
SkDebugf("WARNING: failed to attach stencil.\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void GrGLRenderTarget::bindForResolve(GrGLGpu::ResolveDirection resolveDirection) {
|
||||
// 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(fMultisampleFBOID != 0);
|
||||
|
||||
// In the EXT_multisampled_render_to_texture case, we shouldn't be resolving anything.
|
||||
SkASSERT(!this->isMultisampledRenderToTexture());
|
||||
|
||||
if (resolveDirection == GrGLGpu::ResolveDirection::kMSAAToSingle) {
|
||||
this->bindInternal(GR_GL_READ_FRAMEBUFFER, true);
|
||||
this->bindInternal(GR_GL_DRAW_FRAMEBUFFER, false);
|
||||
} else {
|
||||
SkASSERT(resolveDirection == GrGLGpu::ResolveDirection::kSingleToMSAA);
|
||||
SkASSERT(this->getGLGpu()->glCaps().canResolveSingleToMSAA());
|
||||
this->bindInternal(GR_GL_READ_FRAMEBUFFER, false);
|
||||
this->bindInternal(GR_GL_DRAW_FRAMEBUFFER, true);
|
||||
}
|
||||
}
|
||||
|
||||
void GrGLRenderTarget::onRelease() {
|
||||
if (GrBackendObjectOwnership::kBorrowed != fRTFBOOwnership) {
|
||||
GrGLGpu* gpu = this->getGLGpu();
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "include/core/SkScalar.h"
|
||||
#include "include/gpu/GrBackendSurface.h"
|
||||
#include "src/gpu/GrRenderTarget.h"
|
||||
#include "src/gpu/gl/GrGLDefines.h"
|
||||
|
||||
class GrGLCaps;
|
||||
class GrGLGpu;
|
||||
@ -41,8 +42,13 @@ public:
|
||||
const IDs&,
|
||||
int stencilBits);
|
||||
|
||||
GrGLuint singleSampleFBOID() const { return fSingleSampleFBOID; }
|
||||
GrGLuint multisampleFBOID() const { return fMultisampleFBOID; }
|
||||
bool isFBO0(bool multisample) const {
|
||||
return (multisample ? fMultisampleFBOID : fSingleSampleFBOID) == 0;
|
||||
}
|
||||
|
||||
bool isMultisampledRenderToTexture() const {
|
||||
return fMultisampleFBOID != 0 && fMultisampleFBOID == fSingleSampleFBOID;
|
||||
}
|
||||
|
||||
GrBackendRenderTarget getBackendRenderTarget() const override;
|
||||
|
||||
@ -59,6 +65,28 @@ public:
|
||||
bool hasDynamicMSAAAttachment() const { return SkToBool(fDynamicMSAAAttachment); }
|
||||
bool ensureDynamicMSAAAttachment();
|
||||
|
||||
// Binds the render target to GL_FRAMEBUFFER for rendering.
|
||||
void bind(bool useMultisampleFBO) {
|
||||
this->bindInternal(GR_GL_FRAMEBUFFER, useMultisampleFBO);
|
||||
}
|
||||
|
||||
// Binds the render target for copying, reading, or clearing pixel values. If we are an MSAA
|
||||
// render target with a separate resolve texture, we bind the multisampled FBO. Otherwise we
|
||||
// bind the single sample FBO.
|
||||
void bindForPixelOps(GrGLenum fboTarget) {
|
||||
this->bindInternal(fboTarget,
|
||||
this->numSamples() > 1 && !this->isMultisampledRenderToTexture());
|
||||
}
|
||||
|
||||
enum class ResolveDirection : bool {
|
||||
kSingleToMSAA, // glCaps.canResolveSingleToMSAA() must be true.
|
||||
kMSAAToSingle
|
||||
};
|
||||
|
||||
// Binds the multisampled and single sample FBOs, one to GL_DRAW_FRAMEBUFFER and the other to
|
||||
// GL_READ_FRAMEBUFFER, depending on ResolveDirection.
|
||||
void bindForResolve(ResolveDirection);
|
||||
|
||||
protected:
|
||||
// Constructor for subclasses.
|
||||
GrGLRenderTarget(GrGLGpu*,
|
||||
@ -69,6 +97,9 @@ protected:
|
||||
|
||||
void init(GrGLFormat, const IDs&);
|
||||
|
||||
// Binds the render target to the given target and ensures its stencil attachment is valid.
|
||||
void bindInternal(GrGLenum fboTarget, bool useMultisampleFBO);
|
||||
|
||||
void onAbandon() override;
|
||||
void onRelease() override;
|
||||
|
||||
@ -93,6 +124,7 @@ private:
|
||||
GrGLuint fSingleSampleFBOID;
|
||||
GrGLuint fMSColorRenderbufferID;
|
||||
GrGLFormat fRTFormat;
|
||||
bool fStencilAttachmentIsValid[2] = {false, false};
|
||||
|
||||
GrBackendObjectOwnership fRTFBOOwnership;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user