• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2011 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/gpu/gl/GrGLRenderTarget.h"
9 
10 #include "include/core/SkTraceMemoryDump.h"
11 #include "include/gpu/GrDirectContext.h"
12 #include "src/gpu/GrBackendUtils.h"
13 #include "src/gpu/GrDirectContextPriv.h"
14 #include "src/gpu/GrGpuResourcePriv.h"
15 #include "src/gpu/GrResourceProvider.h"
16 #include "src/gpu/gl/GrGLGpu.h"
17 #include "src/gpu/gl/GrGLUtil.h"
18 
19 #define GPUGL static_cast<GrGLGpu*>(this->getGpu())
20 #define GL_CALL(X) GR_GL_CALL(GPUGL->glInterface(), X)
21 #define GL_CALL_RET(RET, X) GR_GL_CALL_RET(GPUGL->glInterface(), RET, X)
22 
23 // Because this class is virtually derived from GrSurface we must explicitly call its constructor.
24 // Constructor for wrapped render targets.
GrGLRenderTarget(GrGLGpu * gpu,const SkISize & dimensions,GrGLFormat format,int sampleCount,const IDs & ids,sk_sp<GrGLAttachment> stencil)25 GrGLRenderTarget::GrGLRenderTarget(GrGLGpu* gpu,
26                                    const SkISize& dimensions,
27                                    GrGLFormat format,
28                                    int sampleCount,
29                                    const IDs& ids,
30                                    sk_sp<GrGLAttachment> stencil)
31         : GrSurface(gpu, dimensions, GrProtected::kNo)
32         , INHERITED(gpu, dimensions, sampleCount, GrProtected::kNo, std::move(stencil)) {
33     this->init(format, ids);
34     this->setFlags(gpu->glCaps(), ids);
35     this->registerWithCacheWrapped(GrWrapCacheable::kNo);
36 }
37 
GrGLRenderTarget(GrGLGpu * gpu,const SkISize & dimensions,GrGLFormat format,int sampleCount,const IDs & ids)38 GrGLRenderTarget::GrGLRenderTarget(GrGLGpu* gpu,
39                                    const SkISize& dimensions,
40                                    GrGLFormat format,
41                                    int sampleCount,
42                                    const IDs& ids)
43         : GrSurface(gpu, dimensions, GrProtected::kNo)
44         , INHERITED(gpu, dimensions, sampleCount, GrProtected::kNo) {
45     this->init(format, ids);
46     this->setFlags(gpu->glCaps(), ids);
47 }
48 
setFlags(const GrGLCaps & glCaps,const IDs & idDesc)49 inline void GrGLRenderTarget::setFlags(const GrGLCaps& glCaps, const IDs& idDesc) {
50     if ((fMultisampleFBOID | fSingleSampleFBOID) == 0) {
51         this->setGLRTFBOIDIs0();
52     }
53 }
54 
init(GrGLFormat format,const IDs & idDesc)55 void GrGLRenderTarget::init(GrGLFormat format, const IDs& idDesc) {
56     fMultisampleFBOID = idDesc.fMultisampleFBOID;
57     fSingleSampleFBOID = idDesc.fSingleSampleFBOID;
58     fMSColorRenderbufferID = idDesc.fMSColorRenderbufferID;
59     fRTFBOOwnership = idDesc.fRTFBOOwnership;
60     fRTFormat = format;
61     fTotalMemorySamplesPerPixel = idDesc.fTotalMemorySamplesPerPixel;
62 }
63 
stencil_bits_to_format(int stencilBits)64 GrGLFormat stencil_bits_to_format(int stencilBits) {
65     SkASSERT(stencilBits);
66     switch (stencilBits) {
67         case 8:
68             // We pick the packed format here so when we query total size we are at least not
69             // underestimating the total size of the stencil buffer. However, in reality this
70             // rarely matters since we usually don't care about the size of wrapped objects.
71             return GrGLFormat::kDEPTH24_STENCIL8;
72         case 16:
73             return GrGLFormat::kSTENCIL_INDEX16;
74         default:
75             SkASSERT(false);
76             return GrGLFormat::kUnknown;
77     }
78 }
79 
MakeWrapped(GrGLGpu * gpu,const SkISize & dimensions,GrGLFormat format,int sampleCount,const IDs & idDesc,int stencilBits)80 sk_sp<GrGLRenderTarget> GrGLRenderTarget::MakeWrapped(GrGLGpu* gpu,
81                                                       const SkISize& dimensions,
82                                                       GrGLFormat format,
83                                                       int sampleCount,
84                                                       const IDs& idDesc,
85                                                       int stencilBits) {
86     sk_sp<GrGLAttachment> sb;
87     if (stencilBits) {
88         // We pick a "fake" actual format that matches the number of stencil bits. When wrapping
89         // an FBO with some number of stencil bits all we care about in the future is that we have
90         // a format with the same number of stencil bits. We don't even directly use the format or
91         // any other properties. Thus it is fine for us to just assign an arbitrary format that
92         // matches the stencil bit count.
93         GrGLFormat sFmt = stencil_bits_to_format(stencilBits);
94 
95         // We don't have the actual renderbufferID but we need to make an attachment for the stencil
96         // so we just set it to an invalid value of 0 to make sure we don't explicitly use it or try
97         // and delete it.
98         sb = GrGLAttachment::MakeWrappedRenderBuffer(gpu,
99                                                      /*renderbufferID=*/0,
100                                                      dimensions,
101                                                      GrAttachment::UsageFlags::kStencilAttachment,
102                                                      sampleCount,
103                                                      sFmt);
104     }
105     return sk_sp<GrGLRenderTarget>(
106             new GrGLRenderTarget(gpu, dimensions, format, sampleCount, idDesc, std::move(sb)));
107 }
108 
getBackendRenderTarget() const109 GrBackendRenderTarget GrGLRenderTarget::getBackendRenderTarget() const {
110     bool useMultisampleFBO = (this->numSamples() > 1);
111     GrGLFramebufferInfo fbi;
112     fbi.fFBOID = (useMultisampleFBO) ? fMultisampleFBOID : fSingleSampleFBOID;
113     fbi.fFormat = GrGLFormatToEnum(this->format());
114     int numStencilBits = 0;
115     if (GrAttachment* stencil = this->getStencilAttachment(useMultisampleFBO)) {
116         numStencilBits = GrBackendFormatStencilBits(stencil->backendFormat());
117     }
118 
119     return GrBackendRenderTarget(
120             this->width(), this->height(), this->numSamples(), numStencilBits, fbi);
121 }
122 
backendFormat() const123 GrBackendFormat GrGLRenderTarget::backendFormat() const {
124     // We should never have a GrGLRenderTarget (even a textureable one with a target that is not
125     // texture 2D.
126     return GrBackendFormat::MakeGL(GrGLFormatToEnum(fRTFormat), GR_GL_TEXTURE_2D);
127 }
128 
onGpuMemorySize() const129 size_t GrGLRenderTarget::onGpuMemorySize() const {
130     return GrSurface::ComputeSize(this->backendFormat(), this->dimensions(),
131                                   fTotalMemorySamplesPerPixel, GrMipmapped::kNo);
132 }
133 
completeStencilAttachment(GrAttachment * stencil,bool useMultisampleFBO)134 bool GrGLRenderTarget::completeStencilAttachment(GrAttachment* stencil, bool useMultisampleFBO) {
135     // We defer attaching the new stencil buffer until the next time our framebuffer is bound.
136     if (this->getStencilAttachment(useMultisampleFBO) != stencil) {
137         fNeedsStencilAttachmentBind[useMultisampleFBO] = true;
138     }
139     return true;
140 }
141 
ensureDynamicMSAAAttachment()142 bool GrGLRenderTarget::ensureDynamicMSAAAttachment() {
143     SkASSERT(this->numSamples() == 1);
144     if (fMultisampleFBOID) {
145         return true;
146     }
147     SkASSERT(!fDynamicMSAAAttachment);
148 
149     GrResourceProvider* resourceProvider = this->getContext()->priv().resourceProvider();
150     const GrCaps& caps = *this->getGpu()->caps();
151 
152     int internalSampleCount = caps.internalMultisampleCount(this->backendFormat());
153     if (internalSampleCount <= 1) {
154         return false;
155     }
156 
157     if (resourceProvider->caps()->msaaResolvesAutomatically() && this->asTexture()) {
158         // We can use EXT_multisampled_render_to_texture for MSAA. We will configure the FBO as MSAA
159         // or not during bindFBO().
160         fMultisampleFBOID = fSingleSampleFBOID;
161         return true;
162     }
163 
164     GL_CALL(GenFramebuffers(1, &fMultisampleFBOID));
165     if (!fMultisampleFBOID) {
166         return false;
167     }
168 
169     this->getGLGpu()->bindFramebuffer(GR_GL_FRAMEBUFFER, fMultisampleFBOID);
170 
171     fDynamicMSAAAttachment.reset(
172             static_cast<GrGLAttachment*>(resourceProvider->getDiscardableMSAAAttachment(
173                     this->dimensions(), this->backendFormat(), internalSampleCount,
174                     GrProtected(this->isProtected()), GrMemoryless::kNo).release()));
175     if (!fDynamicMSAAAttachment) {
176         return false;
177     }
178 
179     GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER, GR_GL_COLOR_ATTACHMENT0, GR_GL_RENDERBUFFER,
180                                     fDynamicMSAAAttachment->renderbufferID()));
181     return true;
182 }
183 
bindInternal(GrGLenum fboTarget,bool useMultisampleFBO)184 void GrGLRenderTarget::bindInternal(GrGLenum fboTarget, bool useMultisampleFBO) {
185     GrGLuint fboId = useMultisampleFBO ? fMultisampleFBOID : fSingleSampleFBOID;
186     this->getGLGpu()->bindFramebuffer(fboTarget, fboId);
187 
188     if (fSingleSampleFBOID != 0 &&
189         fSingleSampleFBOID == fMultisampleFBOID &&
190         useMultisampleFBO != fDMSAARenderToTextureFBOIsMultisample) {
191         auto* glTex = static_cast<GrGLTexture*>(this->asTexture());
192         if (this->getGLGpu()->glCaps().bindTexture0WhenChangingTextureFBOMultisampleCount()) {
193             GL_CALL(FramebufferTexture2D(fboTarget,
194                                          GR_GL_COLOR_ATTACHMENT0,
195                                          GR_GL_TEXTURE_2D,
196                                          0 /*texture*/,
197                                          0 /*mipMapLevel*/));
198         }
199         if (useMultisampleFBO) {
200             int internalSampleCount =
201                     this->getGpu()->caps()->internalMultisampleCount(this->backendFormat());
202             GL_CALL(FramebufferTexture2DMultisample(fboTarget,
203                                                     GR_GL_COLOR_ATTACHMENT0,
204                                                     glTex->target(),
205                                                     glTex->textureID(),
206                                                     0 /*mipMapLevel*/,
207                                                     internalSampleCount));
208         } else {
209             GL_CALL(FramebufferTexture2D(fboTarget,
210                                          GR_GL_COLOR_ATTACHMENT0,
211                                          glTex->target(),
212                                          glTex->textureID(),
213                                          0 /*mipMapLevel*/));
214         }
215         fDMSAARenderToTextureFBOIsMultisample = useMultisampleFBO;
216         fNeedsStencilAttachmentBind[useMultisampleFBO] = true;
217     }
218 
219     // Make sure the stencil attachment is valid. Even though a color buffer op doesn't use stencil,
220     // our FBO still needs to be "framebuffer complete".
221     if (fNeedsStencilAttachmentBind[useMultisampleFBO]) {
222         if (auto stencil = this->getStencilAttachment(useMultisampleFBO)) {
223             const GrGLAttachment* glStencil = static_cast<const GrGLAttachment*>(stencil);
224             GL_CALL(FramebufferRenderbuffer(fboTarget,
225                                             GR_GL_STENCIL_ATTACHMENT,
226                                             GR_GL_RENDERBUFFER,
227                                             glStencil->renderbufferID()));
228             if (GrGLFormatIsPackedDepthStencil(glStencil->format())) {
229                 GL_CALL(FramebufferRenderbuffer(fboTarget,
230                                                 GR_GL_DEPTH_ATTACHMENT,
231                                                 GR_GL_RENDERBUFFER,
232                                                 glStencil->renderbufferID()));
233             } else {
234                 GL_CALL(FramebufferRenderbuffer(fboTarget,
235                                                 GR_GL_DEPTH_ATTACHMENT,
236                                                 GR_GL_RENDERBUFFER,
237                                                 0));
238             }
239         } else {
240             GL_CALL(FramebufferRenderbuffer(fboTarget,
241                                             GR_GL_STENCIL_ATTACHMENT,
242                                             GR_GL_RENDERBUFFER,
243                                             0));
244             GL_CALL(FramebufferRenderbuffer(fboTarget,
245                                             GR_GL_DEPTH_ATTACHMENT,
246                                             GR_GL_RENDERBUFFER,
247                                             0));
248         }
249 #ifdef SK_DEBUG
250         if (!this->getGLGpu()->glCaps().skipErrorChecks() &&
251             !this->getGLGpu()->glCaps().rebindColorAttachmentAfterCheckFramebufferStatus()) {
252             GrGLenum status;
253             GL_CALL_RET(status, CheckFramebufferStatus(fboTarget));
254             if (status != GR_GL_FRAMEBUFFER_COMPLETE) {
255                 // This can fail if the context has been asynchronously abandoned (see
256                 // skbug.com/5200).
257                 SkDebugf("WARNING: failed to attach stencil.\n");
258             }
259         }
260 #endif
261         fNeedsStencilAttachmentBind[useMultisampleFBO] = false;
262     }
263 }
264 
bindForResolve(GrGLGpu::ResolveDirection resolveDirection)265 void GrGLRenderTarget::bindForResolve(GrGLGpu::ResolveDirection resolveDirection) {
266     // If the multisample FBO is nonzero, it means we always have something to resolve (even if the
267     // single sample buffer is FBO 0). If it's zero, then there's nothing to resolve.
268     SkASSERT(fMultisampleFBOID != 0);
269 
270     // In the EXT_multisampled_render_to_texture case, we shouldn't be resolving anything.
271     SkASSERT(!this->isMultisampledRenderToTexture());
272 
273     if (resolveDirection == GrGLGpu::ResolveDirection::kMSAAToSingle) {
274         this->bindInternal(GR_GL_READ_FRAMEBUFFER, true);
275         this->bindInternal(GR_GL_DRAW_FRAMEBUFFER, false);
276     } else {
277         SkASSERT(resolveDirection == GrGLGpu::ResolveDirection::kSingleToMSAA);
278         SkASSERT(this->getGLGpu()->glCaps().canResolveSingleToMSAA());
279         this->bindInternal(GR_GL_READ_FRAMEBUFFER, false);
280         this->bindInternal(GR_GL_DRAW_FRAMEBUFFER, true);
281     }
282 }
283 
onRelease()284 void GrGLRenderTarget::onRelease() {
285     if (GrBackendObjectOwnership::kBorrowed != fRTFBOOwnership) {
286         GrGLGpu* gpu = this->getGLGpu();
287         if (fSingleSampleFBOID) {
288             gpu->deleteFramebuffer(fSingleSampleFBOID);
289         }
290         if (fMultisampleFBOID && fMultisampleFBOID != fSingleSampleFBOID) {
291             gpu->deleteFramebuffer(fMultisampleFBOID);
292         }
293         if (fMSColorRenderbufferID) {
294             GL_CALL(DeleteRenderbuffers(1, &fMSColorRenderbufferID));
295         }
296     }
297     fMultisampleFBOID       = 0;
298     fSingleSampleFBOID      = 0;
299     fMSColorRenderbufferID  = 0;
300     INHERITED::onRelease();
301 }
302 
onAbandon()303 void GrGLRenderTarget::onAbandon() {
304     fMultisampleFBOID       = 0;
305     fSingleSampleFBOID      = 0;
306     fMSColorRenderbufferID  = 0;
307     INHERITED::onAbandon();
308 }
309 
getGLGpu() const310 GrGLGpu* GrGLRenderTarget::getGLGpu() const {
311     SkASSERT(!this->wasDestroyed());
312     return static_cast<GrGLGpu*>(this->getGpu());
313 }
314 
canAttemptStencilAttachment(bool useMultisampleFBO) const315 bool GrGLRenderTarget::canAttemptStencilAttachment(bool useMultisampleFBO) const {
316     // This cap should have been handled at a higher level.
317     SkASSERT(!this->getGpu()->getContext()->priv().caps()->avoidStencilBuffers());
318     // Only modify the FBO's attachments if we have created the FBO. Public APIs do not currently
319     // allow for borrowed FBO ownership, so we can safely assume that if an object is owned,
320     // Skia created it.
321     return this->fRTFBOOwnership == GrBackendObjectOwnership::kOwned ||
322            // The dmsaa attachment is always owned and always supports adding stencil.
323            (this->numSamples() == 1 && useMultisampleFBO);
324 }
325 
dumpMemoryStatistics(SkTraceMemoryDump * traceMemoryDump) const326 void GrGLRenderTarget::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const {
327     // Don't check this->fRefsWrappedObjects, as we might be the base of a GrGLTextureRenderTarget
328     // which is multiply inherited from both ourselves and a texture. In these cases, one part
329     // (texture, rt) may be wrapped, while the other is owned by Skia.
330     bool refsWrappedRenderTargetObjects =
331             this->fRTFBOOwnership == GrBackendObjectOwnership::kBorrowed;
332     if (refsWrappedRenderTargetObjects && !traceMemoryDump->shouldDumpWrappedObjects()) {
333         return;
334     }
335 
336     int numSamplesNotInTexture = fTotalMemorySamplesPerPixel;
337     if (this->asTexture()) {
338         --numSamplesNotInTexture;  // GrGLTexture::dumpMemoryStatistics accounts for 1 sample.
339     }
340     if (numSamplesNotInTexture >= 1) {
341         size_t size = GrSurface::ComputeSize(this->backendFormat(), this->dimensions(),
342                                              numSamplesNotInTexture, GrMipmapped::kNo);
343 
344         // Due to this resource having both a texture and a renderbuffer component, dump as
345         // skia/gpu_resources/resource_#/renderbuffer
346         SkString resourceName = this->getResourceName();
347         resourceName.append("/renderbuffer");
348 
349         this->dumpMemoryStatisticsPriv(traceMemoryDump, resourceName, "RenderTarget", size);
350 
351         SkString renderbuffer_id;
352         renderbuffer_id.appendU32(fMSColorRenderbufferID);
353         traceMemoryDump->setMemoryBacking(resourceName.c_str(), "gl_renderbuffer",
354                                           renderbuffer_id.c_str());
355     }
356 }
357