|
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 <stdarg.h> |
|
7 |
|
8 #include "WebGLContext.h" |
|
9 #include "GLContext.h" |
|
10 |
|
11 #include "prprf.h" |
|
12 |
|
13 #include "jsapi.h" |
|
14 #include "nsIScriptSecurityManager.h" |
|
15 #include "nsServiceManagerUtils.h" |
|
16 #include "nsIVariant.h" |
|
17 #include "nsCxPusher.h" |
|
18 |
|
19 #include "nsIDOMEvent.h" |
|
20 #include "nsIDOMDataContainerEvent.h" |
|
21 |
|
22 #include "mozilla/Preferences.h" |
|
23 |
|
24 using namespace mozilla; |
|
25 |
|
26 namespace mozilla { |
|
27 |
|
28 using namespace gl; |
|
29 |
|
30 bool |
|
31 IsGLDepthFormat(GLenum webGLFormat) |
|
32 { |
|
33 return (webGLFormat == LOCAL_GL_DEPTH_COMPONENT || |
|
34 webGLFormat == LOCAL_GL_DEPTH_COMPONENT16 || |
|
35 webGLFormat == LOCAL_GL_DEPTH_COMPONENT32); |
|
36 } |
|
37 |
|
38 bool |
|
39 IsGLDepthStencilFormat(GLenum webGLFormat) |
|
40 { |
|
41 return (webGLFormat == LOCAL_GL_DEPTH_STENCIL || |
|
42 webGLFormat == LOCAL_GL_DEPTH24_STENCIL8); |
|
43 } |
|
44 |
|
45 bool |
|
46 FormatHasAlpha(GLenum webGLFormat) |
|
47 { |
|
48 return webGLFormat == LOCAL_GL_RGBA || |
|
49 webGLFormat == LOCAL_GL_LUMINANCE_ALPHA || |
|
50 webGLFormat == LOCAL_GL_ALPHA || |
|
51 webGLFormat == LOCAL_GL_RGBA4 || |
|
52 webGLFormat == LOCAL_GL_RGB5_A1 || |
|
53 webGLFormat == LOCAL_GL_SRGB_ALPHA; |
|
54 } |
|
55 |
|
56 /** |
|
57 * Convert WebGL/ES format and type into GL format and GL internal |
|
58 * format valid for underlying driver. |
|
59 */ |
|
60 void |
|
61 DriverFormatsFromFormatAndType(GLContext* gl, GLenum webGLFormat, GLenum webGLType, |
|
62 GLenum* out_driverInternalFormat, GLenum* out_driverFormat) |
|
63 { |
|
64 MOZ_ASSERT(out_driverInternalFormat, "out_driverInternalFormat can't be nullptr."); |
|
65 MOZ_ASSERT(out_driverFormat, "out_driverFormat can't be nullptr."); |
|
66 if (!out_driverInternalFormat || !out_driverFormat) |
|
67 return; |
|
68 |
|
69 // ES2 requires that format == internalformat; floating-point is |
|
70 // indicated purely by the type that's loaded. For desktop GL, we |
|
71 // have to specify a floating point internal format. |
|
72 if (gl->IsGLES()) { |
|
73 *out_driverInternalFormat = webGLFormat; |
|
74 *out_driverFormat = webGLFormat; |
|
75 |
|
76 return; |
|
77 } |
|
78 |
|
79 GLenum format = webGLFormat; |
|
80 GLenum internalFormat = LOCAL_GL_NONE; |
|
81 |
|
82 if (format == LOCAL_GL_DEPTH_COMPONENT) { |
|
83 if (webGLType == LOCAL_GL_UNSIGNED_SHORT) |
|
84 internalFormat = LOCAL_GL_DEPTH_COMPONENT16; |
|
85 else if (webGLType == LOCAL_GL_UNSIGNED_INT) |
|
86 internalFormat = LOCAL_GL_DEPTH_COMPONENT32; |
|
87 } else if (format == LOCAL_GL_DEPTH_STENCIL) { |
|
88 if (webGLType == LOCAL_GL_UNSIGNED_INT_24_8_EXT) |
|
89 internalFormat = LOCAL_GL_DEPTH24_STENCIL8; |
|
90 } else { |
|
91 switch (webGLType) { |
|
92 case LOCAL_GL_UNSIGNED_BYTE: |
|
93 case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4: |
|
94 case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1: |
|
95 case LOCAL_GL_UNSIGNED_SHORT_5_6_5: |
|
96 internalFormat = format; |
|
97 break; |
|
98 |
|
99 case LOCAL_GL_FLOAT: |
|
100 switch (format) { |
|
101 case LOCAL_GL_RGBA: |
|
102 internalFormat = LOCAL_GL_RGBA32F; |
|
103 break; |
|
104 |
|
105 case LOCAL_GL_RGB: |
|
106 internalFormat = LOCAL_GL_RGB32F; |
|
107 break; |
|
108 |
|
109 case LOCAL_GL_ALPHA: |
|
110 internalFormat = LOCAL_GL_ALPHA32F_ARB; |
|
111 break; |
|
112 |
|
113 case LOCAL_GL_LUMINANCE: |
|
114 internalFormat = LOCAL_GL_LUMINANCE32F_ARB; |
|
115 break; |
|
116 |
|
117 case LOCAL_GL_LUMINANCE_ALPHA: |
|
118 internalFormat = LOCAL_GL_LUMINANCE_ALPHA32F_ARB; |
|
119 break; |
|
120 } |
|
121 break; |
|
122 |
|
123 case LOCAL_GL_HALF_FLOAT_OES: |
|
124 switch (format) { |
|
125 case LOCAL_GL_RGBA: |
|
126 internalFormat = LOCAL_GL_RGBA16F; |
|
127 break; |
|
128 |
|
129 case LOCAL_GL_RGB: |
|
130 internalFormat = LOCAL_GL_RGB16F; |
|
131 break; |
|
132 |
|
133 case LOCAL_GL_ALPHA: |
|
134 internalFormat = LOCAL_GL_ALPHA16F_ARB; |
|
135 break; |
|
136 |
|
137 case LOCAL_GL_LUMINANCE: |
|
138 internalFormat = LOCAL_GL_LUMINANCE16F_ARB; |
|
139 break; |
|
140 |
|
141 case LOCAL_GL_LUMINANCE_ALPHA: |
|
142 internalFormat = LOCAL_GL_LUMINANCE_ALPHA16F_ARB; |
|
143 break; |
|
144 } |
|
145 break; |
|
146 |
|
147 default: |
|
148 break; |
|
149 } |
|
150 |
|
151 // Handle ES2 and GL differences when supporting sRGB internal formats. GL ES |
|
152 // requires that format == internalformat, but GL will fail in this case. |
|
153 // GL requires: |
|
154 // format -> internalformat |
|
155 // GL_RGB GL_SRGB_EXT |
|
156 // GL_RGBA GL_SRGB_ALPHA_EXT |
|
157 switch (format) { |
|
158 case LOCAL_GL_SRGB: |
|
159 internalFormat = format; |
|
160 format = LOCAL_GL_RGB; |
|
161 break; |
|
162 |
|
163 case LOCAL_GL_SRGB_ALPHA: |
|
164 internalFormat = format; |
|
165 format = LOCAL_GL_RGBA; |
|
166 break; |
|
167 } |
|
168 } |
|
169 |
|
170 MOZ_ASSERT(format != LOCAL_GL_NONE && internalFormat != LOCAL_GL_NONE, |
|
171 "Coding mistake -- bad format/type passed?"); |
|
172 |
|
173 *out_driverInternalFormat = internalFormat; |
|
174 *out_driverFormat = format; |
|
175 } |
|
176 |
|
177 GLenum |
|
178 DriverTypeFromType(GLContext* gl, GLenum webGLType) |
|
179 { |
|
180 if (gl->IsGLES()) |
|
181 return webGLType; |
|
182 |
|
183 // convert type for half float if not on GLES2 |
|
184 GLenum type = webGLType; |
|
185 if (type == LOCAL_GL_HALF_FLOAT_OES) { |
|
186 if (gl->IsSupported(gl::GLFeature::texture_half_float)) { |
|
187 return LOCAL_GL_HALF_FLOAT; |
|
188 } else { |
|
189 MOZ_ASSERT(gl->IsExtensionSupported(gl::GLContext::OES_texture_half_float)); |
|
190 } |
|
191 } |
|
192 |
|
193 return webGLType; |
|
194 } |
|
195 |
|
196 } // namespace mozilla |
|
197 |
|
198 void |
|
199 WebGLContext::GenerateWarning(const char *fmt, ...) |
|
200 { |
|
201 va_list ap; |
|
202 va_start(ap, fmt); |
|
203 |
|
204 GenerateWarning(fmt, ap); |
|
205 |
|
206 va_end(ap); |
|
207 } |
|
208 |
|
209 void |
|
210 WebGLContext::GenerateWarning(const char *fmt, va_list ap) |
|
211 { |
|
212 if (!ShouldGenerateWarnings()) |
|
213 return; |
|
214 |
|
215 mAlreadyGeneratedWarnings++; |
|
216 |
|
217 char buf[1024]; |
|
218 PR_vsnprintf(buf, 1024, fmt, ap); |
|
219 |
|
220 // no need to print to stderr, as JS_ReportWarning takes care of this for us. |
|
221 |
|
222 AutoJSContext cx; |
|
223 JS_ReportWarning(cx, "WebGL: %s", buf); |
|
224 if (!ShouldGenerateWarnings()) { |
|
225 JS_ReportWarning(cx, |
|
226 "WebGL: No further warnings will be reported for this WebGL context " |
|
227 "(already reported %d warnings)", mAlreadyGeneratedWarnings); |
|
228 } |
|
229 } |
|
230 |
|
231 bool |
|
232 WebGLContext::ShouldGenerateWarnings() const |
|
233 { |
|
234 if (mMaxWarnings == -1) { |
|
235 return true; |
|
236 } |
|
237 |
|
238 return mAlreadyGeneratedWarnings < mMaxWarnings; |
|
239 } |
|
240 |
|
241 CheckedUint32 |
|
242 WebGLContext::GetImageSize(GLsizei height, |
|
243 GLsizei width, |
|
244 uint32_t pixelSize, |
|
245 uint32_t packOrUnpackAlignment) |
|
246 { |
|
247 CheckedUint32 checked_plainRowSize = CheckedUint32(width) * pixelSize; |
|
248 |
|
249 // alignedRowSize = row size rounded up to next multiple of packAlignment |
|
250 CheckedUint32 checked_alignedRowSize = RoundedToNextMultipleOf(checked_plainRowSize, packOrUnpackAlignment); |
|
251 |
|
252 // if height is 0, we don't need any memory to store this; without this check, we'll get an overflow |
|
253 CheckedUint32 checked_neededByteLength |
|
254 = height <= 0 ? 0 : (height-1) * checked_alignedRowSize + checked_plainRowSize; |
|
255 |
|
256 return checked_neededByteLength; |
|
257 } |
|
258 |
|
259 void |
|
260 WebGLContext::SynthesizeGLError(GLenum err) |
|
261 { |
|
262 /* ES2 section 2.5 "GL Errors" states that implementations can have |
|
263 * multiple 'flags', as errors might be caught in different parts of |
|
264 * a distributed implementation. |
|
265 * We're signing up as a distributed implementation here, with |
|
266 * separate flags for WebGL and the underlying GLContext. |
|
267 */ |
|
268 if (!mWebGLError) |
|
269 mWebGLError = err; |
|
270 } |
|
271 |
|
272 void |
|
273 WebGLContext::SynthesizeGLError(GLenum err, const char *fmt, ...) |
|
274 { |
|
275 va_list va; |
|
276 va_start(va, fmt); |
|
277 GenerateWarning(fmt, va); |
|
278 va_end(va); |
|
279 |
|
280 return SynthesizeGLError(err); |
|
281 } |
|
282 |
|
283 void |
|
284 WebGLContext::ErrorInvalidEnum(const char *fmt, ...) |
|
285 { |
|
286 va_list va; |
|
287 va_start(va, fmt); |
|
288 GenerateWarning(fmt, va); |
|
289 va_end(va); |
|
290 |
|
291 return SynthesizeGLError(LOCAL_GL_INVALID_ENUM); |
|
292 } |
|
293 |
|
294 void |
|
295 WebGLContext::ErrorInvalidEnumInfo(const char *info, GLenum enumvalue) |
|
296 { |
|
297 return ErrorInvalidEnum("%s: invalid enum value 0x%x", info, enumvalue); |
|
298 } |
|
299 |
|
300 void |
|
301 WebGLContext::ErrorInvalidOperation(const char *fmt, ...) |
|
302 { |
|
303 va_list va; |
|
304 va_start(va, fmt); |
|
305 GenerateWarning(fmt, va); |
|
306 va_end(va); |
|
307 |
|
308 return SynthesizeGLError(LOCAL_GL_INVALID_OPERATION); |
|
309 } |
|
310 |
|
311 void |
|
312 WebGLContext::ErrorInvalidValue(const char *fmt, ...) |
|
313 { |
|
314 va_list va; |
|
315 va_start(va, fmt); |
|
316 GenerateWarning(fmt, va); |
|
317 va_end(va); |
|
318 |
|
319 return SynthesizeGLError(LOCAL_GL_INVALID_VALUE); |
|
320 } |
|
321 |
|
322 void |
|
323 WebGLContext::ErrorInvalidFramebufferOperation(const char *fmt, ...) |
|
324 { |
|
325 va_list va; |
|
326 va_start(va, fmt); |
|
327 GenerateWarning(fmt, va); |
|
328 va_end(va); |
|
329 |
|
330 return SynthesizeGLError(LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION); |
|
331 } |
|
332 |
|
333 void |
|
334 WebGLContext::ErrorOutOfMemory(const char *fmt, ...) |
|
335 { |
|
336 va_list va; |
|
337 va_start(va, fmt); |
|
338 GenerateWarning(fmt, va); |
|
339 va_end(va); |
|
340 |
|
341 return SynthesizeGLError(LOCAL_GL_OUT_OF_MEMORY); |
|
342 } |
|
343 |
|
344 const char * |
|
345 WebGLContext::ErrorName(GLenum error) |
|
346 { |
|
347 switch(error) { |
|
348 case LOCAL_GL_INVALID_ENUM: |
|
349 return "INVALID_ENUM"; |
|
350 case LOCAL_GL_INVALID_OPERATION: |
|
351 return "INVALID_OPERATION"; |
|
352 case LOCAL_GL_INVALID_VALUE: |
|
353 return "INVALID_VALUE"; |
|
354 case LOCAL_GL_OUT_OF_MEMORY: |
|
355 return "OUT_OF_MEMORY"; |
|
356 case LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION: |
|
357 return "INVALID_FRAMEBUFFER_OPERATION"; |
|
358 case LOCAL_GL_NO_ERROR: |
|
359 return "NO_ERROR"; |
|
360 default: |
|
361 MOZ_ASSERT(false); |
|
362 return "[unknown WebGL error!]"; |
|
363 } |
|
364 } |
|
365 |
|
366 bool |
|
367 WebGLContext::IsTextureFormatCompressed(GLenum format) |
|
368 { |
|
369 switch (format) { |
|
370 case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT: |
|
371 case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: |
|
372 case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: |
|
373 case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: |
|
374 case LOCAL_GL_ATC_RGB: |
|
375 case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA: |
|
376 case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA: |
|
377 case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1: |
|
378 case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1: |
|
379 case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1: |
|
380 case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1: |
|
381 case LOCAL_GL_ETC1_RGB8_OES: |
|
382 return true; |
|
383 default: |
|
384 return false; |
|
385 } |
|
386 } |
|
387 |
|
388 GLenum |
|
389 WebGLContext::GetAndFlushUnderlyingGLErrors() |
|
390 { |
|
391 // Get and clear GL error in ALL cases. |
|
392 GLenum error = gl->GetAndClearError(); |
|
393 |
|
394 // Only store in mUnderlyingGLError if is hasn't already recorded an |
|
395 // error. |
|
396 if (!mUnderlyingGLError) |
|
397 mUnderlyingGLError = error; |
|
398 |
|
399 return error; |
|
400 } |