/src/mozilla-central/dom/canvas/WebGLRenderbuffer.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 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 "WebGLRenderbuffer.h" |
7 | | |
8 | | #include "GLContext.h" |
9 | | #include "mozilla/dom/WebGLRenderingContextBinding.h" |
10 | | #include "ScopedGLHelpers.h" |
11 | | #include "WebGLContext.h" |
12 | | #include "WebGLStrongTypes.h" |
13 | | #include "WebGLTexture.h" |
14 | | |
15 | | namespace mozilla { |
16 | | |
17 | | static GLenum |
18 | | DepthFormatForDepthStencilEmu(gl::GLContext* gl) |
19 | 0 | { |
20 | 0 | // We might not be able to get 24-bit, so let's pretend! |
21 | 0 | if (gl->IsGLES() && !gl->IsExtensionSupported(gl::GLContext::OES_depth24)) |
22 | 0 | return LOCAL_GL_DEPTH_COMPONENT16; |
23 | 0 | |
24 | 0 | return LOCAL_GL_DEPTH_COMPONENT24; |
25 | 0 | } |
26 | | |
27 | | JSObject* |
28 | | WebGLRenderbuffer::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) |
29 | 0 | { |
30 | 0 | return dom::WebGLRenderbuffer_Binding::Wrap(cx, this, givenProto); |
31 | 0 | } |
32 | | |
33 | | static GLuint |
34 | | DoCreateRenderbuffer(gl::GLContext* gl) |
35 | 0 | { |
36 | 0 | MOZ_ASSERT(gl->IsCurrent()); |
37 | 0 |
|
38 | 0 | GLuint ret = 0; |
39 | 0 | gl->fGenRenderbuffers(1, &ret); |
40 | 0 | return ret; |
41 | 0 | } |
42 | | |
43 | | static bool |
44 | | EmulatePackedDepthStencil(gl::GLContext* gl) |
45 | 0 | { |
46 | 0 | return !gl->IsSupported(gl::GLFeature::packed_depth_stencil); |
47 | 0 | } |
48 | | |
49 | | WebGLRenderbuffer::WebGLRenderbuffer(WebGLContext* webgl) |
50 | | : WebGLRefCountedObject(webgl) |
51 | | , mPrimaryRB( DoCreateRenderbuffer(webgl->gl) ) |
52 | | , mEmulatePackedDepthStencil( EmulatePackedDepthStencil(webgl->gl) ) |
53 | | , mSecondaryRB(0) |
54 | | , mFormat(nullptr) |
55 | | , mSamples(0) |
56 | | , mImageDataStatus(WebGLImageDataStatus::NoImageData) |
57 | | , mHasBeenBound(false) |
58 | 0 | { |
59 | 0 | mContext->mRenderbuffers.insertBack(this); |
60 | 0 | } |
61 | | |
62 | | void |
63 | | WebGLRenderbuffer::Delete() |
64 | 0 | { |
65 | 0 | mContext->gl->fDeleteRenderbuffers(1, &mPrimaryRB); |
66 | 0 | if (mSecondaryRB) |
67 | 0 | mContext->gl->fDeleteRenderbuffers(1, &mSecondaryRB); |
68 | 0 |
|
69 | 0 | LinkedListElement<WebGLRenderbuffer>::removeFrom(mContext->mRenderbuffers); |
70 | 0 | } |
71 | | |
72 | | int64_t |
73 | | WebGLRenderbuffer::MemoryUsage() const |
74 | 0 | { |
75 | 0 | // If there is no defined format, we're not taking up any memory |
76 | 0 | if (!mFormat) |
77 | 0 | return 0; |
78 | 0 | |
79 | 0 | const auto bytesPerPixel = mFormat->format->estimatedBytesPerPixel; |
80 | 0 | const int64_t pixels = int64_t(mWidth) * int64_t(mHeight); |
81 | 0 |
|
82 | 0 | const int64_t totalSize = pixels * bytesPerPixel; |
83 | 0 | return totalSize; |
84 | 0 | } |
85 | | |
86 | | static GLenum |
87 | | DoRenderbufferStorageMaybeMultisample(gl::GLContext* gl, GLsizei samples, |
88 | | GLenum internalFormat, GLsizei width, |
89 | | GLsizei height) |
90 | 0 | { |
91 | 0 | MOZ_ASSERT_IF(samples >= 1, gl->IsSupported(gl::GLFeature::framebuffer_multisample)); |
92 | 0 |
|
93 | 0 | // Certain OpenGL ES renderbuffer formats may not exist on desktop OpenGL. |
94 | 0 | switch (internalFormat) { |
95 | 0 | case LOCAL_GL_RGBA4: |
96 | 0 | case LOCAL_GL_RGB5_A1: |
97 | 0 | // 16-bit RGBA formats are not supported on desktop GL. |
98 | 0 | if (!gl->IsGLES()) |
99 | 0 | internalFormat = LOCAL_GL_RGBA8; |
100 | 0 | break; |
101 | 0 |
|
102 | 0 | case LOCAL_GL_RGB565: |
103 | 0 | // RGB565 is not supported on desktop GL. |
104 | 0 | if (!gl->IsGLES()) |
105 | 0 | internalFormat = LOCAL_GL_RGB8; |
106 | 0 | break; |
107 | 0 |
|
108 | 0 | case LOCAL_GL_DEPTH_COMPONENT16: |
109 | 0 | if (!gl->IsGLES() || gl->IsExtensionSupported(gl::GLContext::OES_depth24)) |
110 | 0 | internalFormat = LOCAL_GL_DEPTH_COMPONENT24; |
111 | 0 | else if (gl->IsSupported(gl::GLFeature::packed_depth_stencil)) |
112 | 0 | internalFormat = LOCAL_GL_DEPTH24_STENCIL8; |
113 | 0 | break; |
114 | 0 |
|
115 | 0 | case LOCAL_GL_DEPTH_STENCIL: |
116 | 0 | MOZ_CRASH("GFX: GL_DEPTH_STENCIL is not valid here."); |
117 | 0 | break; |
118 | 0 |
|
119 | 0 | default: |
120 | 0 | break; |
121 | 0 | } |
122 | 0 | |
123 | 0 | gl::GLContext::LocalErrorScope errorScope(*gl); |
124 | 0 |
|
125 | 0 | if (samples > 0) { |
126 | 0 | gl->fRenderbufferStorageMultisample(LOCAL_GL_RENDERBUFFER, samples, |
127 | 0 | internalFormat, width, height); |
128 | 0 | } else { |
129 | 0 | gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, internalFormat, width, height); |
130 | 0 | } |
131 | 0 |
|
132 | 0 | return errorScope.GetError(); |
133 | 0 | } |
134 | | |
135 | | GLenum |
136 | | WebGLRenderbuffer::DoRenderbufferStorage(uint32_t samples, |
137 | | const webgl::FormatUsageInfo* format, |
138 | | uint32_t width, uint32_t height) |
139 | 0 | { |
140 | 0 | MOZ_ASSERT(mContext->mBoundRenderbuffer == this); |
141 | 0 |
|
142 | 0 | gl::GLContext* gl = mContext->gl; |
143 | 0 | MOZ_ASSERT(samples <= 256); // Sanity check. |
144 | 0 |
|
145 | 0 | GLenum primaryFormat = format->format->sizedFormat; |
146 | 0 | GLenum secondaryFormat = 0; |
147 | 0 |
|
148 | 0 | if (mEmulatePackedDepthStencil && primaryFormat == LOCAL_GL_DEPTH24_STENCIL8) { |
149 | 0 | primaryFormat = DepthFormatForDepthStencilEmu(gl); |
150 | 0 | secondaryFormat = LOCAL_GL_STENCIL_INDEX8; |
151 | 0 | } |
152 | 0 |
|
153 | 0 | gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mPrimaryRB); |
154 | 0 | GLenum error = DoRenderbufferStorageMaybeMultisample(gl, samples, primaryFormat, |
155 | 0 | width, height); |
156 | 0 | if (error) |
157 | 0 | return error; |
158 | 0 | |
159 | 0 | if (secondaryFormat) { |
160 | 0 | if (!mSecondaryRB) { |
161 | 0 | gl->fGenRenderbuffers(1, &mSecondaryRB); |
162 | 0 | } |
163 | 0 |
|
164 | 0 | gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mSecondaryRB); |
165 | 0 | error = DoRenderbufferStorageMaybeMultisample(gl, samples, secondaryFormat, |
166 | 0 | width, height); |
167 | 0 | if (error) |
168 | 0 | return error; |
169 | 0 | } else if (mSecondaryRB) { |
170 | 0 | gl->fDeleteRenderbuffers(1, &mSecondaryRB); |
171 | 0 | mSecondaryRB = 0; |
172 | 0 | } |
173 | 0 |
|
174 | 0 | return 0; |
175 | 0 | } |
176 | | |
177 | | void |
178 | | WebGLRenderbuffer::RenderbufferStorage(uint32_t samples, GLenum internalFormat, |
179 | | uint32_t width, uint32_t height) |
180 | 0 | { |
181 | 0 | const auto usage = mContext->mFormatUsage->GetRBUsage(internalFormat); |
182 | 0 | if (!usage) { |
183 | 0 | mContext->ErrorInvalidEnum("Invalid `internalFormat`: 0x%04x.", |
184 | 0 | internalFormat); |
185 | 0 | return; |
186 | 0 | } |
187 | 0 | |
188 | 0 | if (width > mContext->mGLMaxRenderbufferSize || |
189 | 0 | height > mContext->mGLMaxRenderbufferSize) |
190 | 0 | { |
191 | 0 | mContext->ErrorInvalidValue("Width or height exceeds maximum renderbuffer" |
192 | 0 | " size."); |
193 | 0 | return; |
194 | 0 | } |
195 | 0 | |
196 | 0 | if (!usage->maxSamplesKnown) { |
197 | 0 | const_cast<webgl::FormatUsageInfo*>(usage)->ResolveMaxSamples(mContext->gl); |
198 | 0 | } |
199 | 0 | MOZ_ASSERT(usage->maxSamplesKnown); |
200 | 0 |
|
201 | 0 | if (samples > usage->maxSamples) { |
202 | 0 | mContext->ErrorInvalidOperation("`samples` is out of the valid range."); |
203 | 0 | return; |
204 | 0 | } |
205 | 0 | |
206 | 0 | // Validation complete. |
207 | 0 | |
208 | 0 | const GLenum error = DoRenderbufferStorage(samples, usage, width, height); |
209 | 0 | if (error) { |
210 | 0 | mContext->GenerateWarning("Unexpected error %s", EnumString(error).c_str()); |
211 | 0 | return; |
212 | 0 | } |
213 | 0 | |
214 | 0 | mContext->OnDataAllocCall(); |
215 | 0 |
|
216 | 0 | mSamples = samples; |
217 | 0 | mFormat = usage; |
218 | 0 | mWidth = width; |
219 | 0 | mHeight = height; |
220 | 0 | mImageDataStatus = WebGLImageDataStatus::UninitializedImageData; |
221 | 0 |
|
222 | 0 | InvalidateStatusOfAttachedFBs(); |
223 | 0 | } |
224 | | |
225 | | void |
226 | | WebGLRenderbuffer::DoFramebufferRenderbuffer(FBTarget target, GLenum attachment) const |
227 | 0 | { |
228 | 0 | gl::GLContext* gl = mContext->gl; |
229 | 0 |
|
230 | 0 | if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { |
231 | 0 | const GLuint stencilRB = (mSecondaryRB ? mSecondaryRB : mPrimaryRB); |
232 | 0 | gl->fFramebufferRenderbuffer(target.get(), LOCAL_GL_DEPTH_ATTACHMENT, |
233 | 0 | LOCAL_GL_RENDERBUFFER, mPrimaryRB); |
234 | 0 | gl->fFramebufferRenderbuffer(target.get(), LOCAL_GL_STENCIL_ATTACHMENT, |
235 | 0 | LOCAL_GL_RENDERBUFFER, stencilRB); |
236 | 0 | return; |
237 | 0 | } |
238 | 0 |
|
239 | 0 | gl->fFramebufferRenderbuffer(target.get(), attachment, |
240 | 0 | LOCAL_GL_RENDERBUFFER, mPrimaryRB); |
241 | 0 | } |
242 | | |
243 | | GLint |
244 | | WebGLRenderbuffer::GetRenderbufferParameter(RBTarget target, |
245 | | RBParam pname) const |
246 | 0 | { |
247 | 0 | gl::GLContext* gl = mContext->gl; |
248 | 0 |
|
249 | 0 | switch (pname.get()) { |
250 | 0 | case LOCAL_GL_RENDERBUFFER_STENCIL_SIZE: |
251 | 0 | if (!mFormat) |
252 | 0 | return 0; |
253 | 0 | |
254 | 0 | if (!mFormat->format->s) |
255 | 0 | return 0; |
256 | 0 | |
257 | 0 | return 8; |
258 | 0 |
|
259 | 0 | case LOCAL_GL_RENDERBUFFER_SAMPLES: |
260 | 0 | case LOCAL_GL_RENDERBUFFER_WIDTH: |
261 | 0 | case LOCAL_GL_RENDERBUFFER_HEIGHT: |
262 | 0 | case LOCAL_GL_RENDERBUFFER_RED_SIZE: |
263 | 0 | case LOCAL_GL_RENDERBUFFER_GREEN_SIZE: |
264 | 0 | case LOCAL_GL_RENDERBUFFER_BLUE_SIZE: |
265 | 0 | case LOCAL_GL_RENDERBUFFER_ALPHA_SIZE: |
266 | 0 | case LOCAL_GL_RENDERBUFFER_DEPTH_SIZE: |
267 | 0 | { |
268 | 0 | gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mPrimaryRB); |
269 | 0 | GLint i = 0; |
270 | 0 | gl->fGetRenderbufferParameteriv(target.get(), pname.get(), &i); |
271 | 0 | return i; |
272 | 0 | } |
273 | 0 |
|
274 | 0 | case LOCAL_GL_RENDERBUFFER_INTERNAL_FORMAT: |
275 | 0 | { |
276 | 0 | GLenum ret = LOCAL_GL_RGBA4; |
277 | 0 | if (mFormat) { |
278 | 0 | ret = mFormat->format->sizedFormat; |
279 | 0 |
|
280 | 0 | if (!mContext->IsWebGL2() && ret == LOCAL_GL_DEPTH24_STENCIL8) { |
281 | 0 | ret = LOCAL_GL_DEPTH_STENCIL; |
282 | 0 | } |
283 | 0 | } |
284 | 0 | return ret; |
285 | 0 | } |
286 | 0 | } |
287 | 0 |
|
288 | 0 | MOZ_ASSERT(false, "This function should only be called with valid `pname`."); |
289 | 0 | return 0; |
290 | 0 | } |
291 | | |
292 | | NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLRenderbuffer) |
293 | | |
294 | | NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLRenderbuffer, AddRef) |
295 | | NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLRenderbuffer, Release) |
296 | | |
297 | | } // namespace mozilla |