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