1 /* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4; -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "MozFramebuffer.h"
7 
8 #include "GLContext.h"
9 #include "mozilla/gfx/Logging.h"
10 #include "ScopedGLHelpers.h"
11 
12 namespace mozilla {
13 namespace gl {
14 
DeleteByTarget(GLContext * const gl,const GLenum target,const GLuint name)15 static void DeleteByTarget(GLContext* const gl, const GLenum target,
16                            const GLuint name) {
17   if (target == LOCAL_GL_RENDERBUFFER) {
18     gl->DeleteRenderbuffer(name);
19   } else {
20     gl->DeleteTexture(name);
21   }
22 }
23 
Create(GLContext * const gl,const gfx::IntSize & size,const uint32_t samples,const bool depthStencil)24 UniquePtr<MozFramebuffer> MozFramebuffer::Create(GLContext* const gl,
25                                                  const gfx::IntSize& size,
26                                                  const uint32_t samples,
27                                                  const bool depthStencil) {
28   if (samples && !gl->IsSupported(GLFeature::framebuffer_multisample))
29     return nullptr;
30 
31   if (uint32_t(size.width) > gl->MaxTexOrRbSize() ||
32       uint32_t(size.height) > gl->MaxTexOrRbSize() ||
33       samples > gl->MaxSamples()) {
34     return nullptr;
35   }
36 
37   gl->MakeCurrent();
38 
39   GLContext::LocalErrorScope errorScope(*gl);
40 
41   GLenum colorTarget;
42   GLuint colorName;
43   if (samples) {
44     colorTarget = LOCAL_GL_RENDERBUFFER;
45     colorName = gl->CreateRenderbuffer();
46     const ScopedBindRenderbuffer bindRB(gl, colorName);
47     gl->fRenderbufferStorageMultisample(colorTarget, samples, LOCAL_GL_RGBA8,
48                                         size.width, size.height);
49   } else {
50     colorTarget = LOCAL_GL_TEXTURE_2D;
51     colorName = gl->CreateTexture();
52     const ScopedBindTexture bindTex(gl, colorName);
53     gl->TexParams_SetClampNoMips();
54     gl->fTexImage2D(colorTarget, 0, LOCAL_GL_RGBA, size.width, size.height, 0,
55                     LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, nullptr);
56   }
57 
58   const auto err = errorScope.GetError();
59   if (err) {
60     if (err != LOCAL_GL_OUT_OF_MEMORY) {
61       gfxCriticalNote << "Unexpected error: " << gfx::hexa(err) << ": "
62                       << GLContext::GLErrorToString(err);
63     }
64     DeleteByTarget(gl, colorTarget, colorName);
65     return nullptr;
66   }
67 
68   return CreateWith(gl, size, samples, depthStencil, colorTarget, colorName);
69 }
70 
CreateWith(GLContext * const gl,const gfx::IntSize & size,const uint32_t samples,const bool depthStencil,const GLenum colorTarget,const GLuint colorName)71 UniquePtr<MozFramebuffer> MozFramebuffer::CreateWith(
72     GLContext* const gl, const gfx::IntSize& size, const uint32_t samples,
73     const bool depthStencil, const GLenum colorTarget, const GLuint colorName) {
74   UniquePtr<MozFramebuffer> mozFB(new MozFramebuffer(
75       gl, size, samples, depthStencil, colorTarget, colorName));
76 
77   const ScopedBindFramebuffer bindFB(gl, mozFB->mFB);
78 
79   if (colorTarget == LOCAL_GL_RENDERBUFFER) {
80     gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
81                                  LOCAL_GL_COLOR_ATTACHMENT0, colorTarget,
82                                  colorName);
83   } else {
84     gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
85                               colorTarget, colorName, 0);
86   }
87 
88   const auto fnAllocRB = [&](GLuint rb, GLenum format) {
89     const ScopedBindRenderbuffer bindRB(gl, rb);
90     if (samples) {
91       gl->fRenderbufferStorageMultisample(LOCAL_GL_RENDERBUFFER, samples,
92                                           format, size.width, size.height);
93     } else {
94       gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, format, size.width,
95                                size.height);
96     }
97     return rb;
98   };
99 
100   if (depthStencil) {
101     GLuint depthRB, stencilRB;
102 
103     {
104       GLContext::LocalErrorScope errorScope(*gl);
105 
106       if (gl->IsSupported(GLFeature::packed_depth_stencil)) {
107         depthRB = fnAllocRB(mozFB->mDepthRB, LOCAL_GL_DEPTH24_STENCIL8);
108         stencilRB = depthRB;  // Ignore unused mStencilRB.
109       } else {
110         depthRB = fnAllocRB(mozFB->mDepthRB, LOCAL_GL_DEPTH_COMPONENT24);
111         stencilRB = fnAllocRB(mozFB->mStencilRB, LOCAL_GL_STENCIL_INDEX8);
112       }
113 
114       const auto err = errorScope.GetError();
115       if (err) {
116         MOZ_ASSERT(err == LOCAL_GL_OUT_OF_MEMORY);
117         return nullptr;
118       }
119     }
120 
121     gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
122                                  LOCAL_GL_DEPTH_ATTACHMENT,
123                                  LOCAL_GL_RENDERBUFFER, depthRB);
124     gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
125                                  LOCAL_GL_STENCIL_ATTACHMENT,
126                                  LOCAL_GL_RENDERBUFFER, stencilRB);
127   }
128 
129   const auto status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
130   if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
131     MOZ_ASSERT(false);
132     return nullptr;
133   }
134 
135   return Move(mozFB);
136 }
137 
138 ////////////////////
139 
MozFramebuffer(GLContext * const gl,const gfx::IntSize & size,const uint32_t samples,const bool depthStencil,const GLenum colorTarget,const GLuint colorName)140 MozFramebuffer::MozFramebuffer(GLContext* const gl, const gfx::IntSize& size,
141                                const uint32_t samples, const bool depthStencil,
142                                const GLenum colorTarget, const GLuint colorName)
143     : mWeakGL(gl),
144       mSize(size),
145       mSamples(samples),
146       mFB(gl->CreateFramebuffer()),
147       mColorTarget(colorTarget),
148       mColorName(colorName),
149       mDepthRB(depthStencil ? gl->CreateRenderbuffer() : 0),
150       mStencilRB(depthStencil ? gl->CreateRenderbuffer() : 0) {
151   MOZ_ASSERT(mColorTarget);
152   MOZ_ASSERT(mColorName);
153 }
154 
~MozFramebuffer()155 MozFramebuffer::~MozFramebuffer() {
156   GLContext* const gl = mWeakGL;
157   if (!gl || !gl->MakeCurrent()) return;
158 
159   gl->DeleteFramebuffer(mFB);
160   gl->DeleteRenderbuffer(mDepthRB);
161   gl->DeleteRenderbuffer(mStencilRB);
162 
163   DeleteByTarget(gl, mColorTarget, mColorName);
164 }
165 
166 }  // namespace gl
167 }  // namespace mozilla
168