1 /* -*- Mode: c++; c-basic-offset: 2; 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 CreateImpl(
69       gl, size, samples,
70       depthStencil ? DepthAndStencilBuffer::Create(gl, size, samples) : nullptr,
71       colorTarget, colorName);
72 }
73 
CreateForBacking(GLContext * const gl,const gfx::IntSize & size,const uint32_t samples,bool depthStencil,const GLenum colorTarget,const GLuint colorName)74 UniquePtr<MozFramebuffer> MozFramebuffer::CreateForBacking(
75     GLContext* const gl, const gfx::IntSize& size, const uint32_t samples,
76     bool depthStencil, const GLenum colorTarget, const GLuint colorName) {
77   return CreateImpl(
78       gl, size, samples,
79       depthStencil ? DepthAndStencilBuffer::Create(gl, size, samples) : nullptr,
80       colorTarget, colorName);
81 }
82 
83 /* static */ UniquePtr<MozFramebuffer>
CreateForBackingWithSharedDepthAndStencil(const gfx::IntSize & size,const uint32_t samples,GLenum colorTarget,GLuint colorName,const RefPtr<DepthAndStencilBuffer> & depthAndStencilBuffer)84 MozFramebuffer::CreateForBackingWithSharedDepthAndStencil(
85     const gfx::IntSize& size, const uint32_t samples, GLenum colorTarget,
86     GLuint colorName,
87     const RefPtr<DepthAndStencilBuffer>& depthAndStencilBuffer) {
88   auto gl = depthAndStencilBuffer->gl();
89   if (!gl || !gl->MakeCurrent()) {
90     return nullptr;
91   }
92   return CreateImpl(gl, size, samples, depthAndStencilBuffer, colorTarget,
93                     colorName);
94 }
95 
CreateImpl(GLContext * const gl,const gfx::IntSize & size,const uint32_t samples,const RefPtr<DepthAndStencilBuffer> & depthAndStencilBuffer,const GLenum colorTarget,const GLuint colorName)96 /* static */ UniquePtr<MozFramebuffer> MozFramebuffer::CreateImpl(
97     GLContext* const gl, const gfx::IntSize& size, const uint32_t samples,
98     const RefPtr<DepthAndStencilBuffer>& depthAndStencilBuffer,
99     const GLenum colorTarget, const GLuint colorName) {
100   GLuint fb = gl->CreateFramebuffer();
101   const ScopedBindFramebuffer bindFB(gl, fb);
102 
103   if (colorTarget == LOCAL_GL_RENDERBUFFER) {
104     gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
105                                  LOCAL_GL_COLOR_ATTACHMENT0, colorTarget,
106                                  colorName);
107   } else {
108     gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
109                               colorTarget, colorName, 0);
110   }
111 
112   if (depthAndStencilBuffer) {
113     gl->fFramebufferRenderbuffer(
114         LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, LOCAL_GL_RENDERBUFFER,
115         depthAndStencilBuffer->mDepthRB);
116     gl->fFramebufferRenderbuffer(
117         LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT,
118         LOCAL_GL_RENDERBUFFER, depthAndStencilBuffer->mStencilRB);
119   }
120 
121   const auto status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
122   if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
123     MOZ_ASSERT(false);
124     return nullptr;
125   }
126 
127   return UniquePtr<MozFramebuffer>(new MozFramebuffer(
128       gl, size, fb, samples, depthAndStencilBuffer, colorTarget, colorName));
129 }
130 
Create(GLContext * const gl,const gfx::IntSize & size,const uint32_t samples)131 /* static */ RefPtr<DepthAndStencilBuffer> DepthAndStencilBuffer::Create(
132     GLContext* const gl, const gfx::IntSize& size, const uint32_t samples) {
133   const auto fnAllocRB = [&](GLenum format) {
134     GLuint rb = gl->CreateRenderbuffer();
135     const ScopedBindRenderbuffer bindRB(gl, rb);
136     if (samples) {
137       gl->fRenderbufferStorageMultisample(LOCAL_GL_RENDERBUFFER, samples,
138                                           format, size.width, size.height);
139     } else {
140       gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, format, size.width,
141                                size.height);
142     }
143     return rb;
144   };
145 
146   GLuint depthRB, stencilRB;
147   {
148     GLContext::LocalErrorScope errorScope(*gl);
149 
150     if (gl->IsSupported(GLFeature::packed_depth_stencil)) {
151       depthRB = fnAllocRB(LOCAL_GL_DEPTH24_STENCIL8);
152       stencilRB = depthRB;  // Ignore unused mStencilRB.
153     } else {
154       depthRB = fnAllocRB(LOCAL_GL_DEPTH_COMPONENT24);
155       stencilRB = fnAllocRB(LOCAL_GL_STENCIL_INDEX8);
156     }
157 
158     const auto err = errorScope.GetError();
159     if (err) {
160       MOZ_ASSERT(err == LOCAL_GL_OUT_OF_MEMORY);
161       return nullptr;
162     }
163   }
164 
165   return new DepthAndStencilBuffer(gl, size, depthRB, stencilRB);
166 }
167 
168 ////////////////////
169 
MozFramebuffer(GLContext * const gl,const gfx::IntSize & size,GLuint fb,const uint32_t samples,RefPtr<DepthAndStencilBuffer> depthAndStencilBuffer,const GLenum colorTarget,const GLuint colorName)170 MozFramebuffer::MozFramebuffer(
171     GLContext* const gl, const gfx::IntSize& size, GLuint fb,
172     const uint32_t samples, RefPtr<DepthAndStencilBuffer> depthAndStencilBuffer,
173     const GLenum colorTarget, const GLuint colorName)
174     : mWeakGL(gl),
175       mSize(size),
176       mSamples(samples),
177       mFB(fb),
178       mColorTarget(colorTarget),
179       mDepthAndStencilBuffer(std::move(depthAndStencilBuffer)),
180       mColorName(colorName) {
181   MOZ_ASSERT(mColorTarget);
182   MOZ_ASSERT(mColorName);
183 }
184 
~MozFramebuffer()185 MozFramebuffer::~MozFramebuffer() {
186   GLContext* const gl = mWeakGL;
187   if (!gl || !gl->MakeCurrent()) {
188     return;
189   }
190 
191   gl->DeleteFramebuffer(mFB);
192 
193   DeleteByTarget(gl, mColorTarget, mColorName);
194 }
195 
HasDepth() const196 bool MozFramebuffer::HasDepth() const {
197   return mDepthAndStencilBuffer && mDepthAndStencilBuffer->mDepthRB;
198 }
199 
HasStencil() const200 bool MozFramebuffer::HasStencil() const {
201   return mDepthAndStencilBuffer && mDepthAndStencilBuffer->mStencilRB;
202 }
203 
DepthAndStencilBuffer(GLContext * gl,const gfx::IntSize & size,GLuint depthRB,GLuint stencilRB)204 DepthAndStencilBuffer::DepthAndStencilBuffer(GLContext* gl,
205                                              const gfx::IntSize& size,
206                                              GLuint depthRB, GLuint stencilRB)
207     : mWeakGL(gl), mSize(size), mDepthRB(depthRB), mStencilRB(stencilRB) {}
208 
~DepthAndStencilBuffer()209 DepthAndStencilBuffer::~DepthAndStencilBuffer() {
210   GLContext* const gl = mWeakGL;
211   if (!gl || !gl->MakeCurrent()) {
212     return;
213   }
214 
215   gl->DeleteRenderbuffer(mDepthRB);
216   gl->DeleteRenderbuffer(mStencilRB);
217 }
218 
219 }  // namespace gl
220 }  // namespace mozilla
221