Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; 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 "WebGLContext.h"
7 : #include "WebGL2Context.h"
8 :
9 : #include "WebGLActiveInfo.h"
10 : #include "WebGLContextUtils.h"
11 : #include "WebGLBuffer.h"
12 : #include "WebGLVertexAttribData.h"
13 : #include "WebGLShader.h"
14 : #include "WebGLProgram.h"
15 : #include "WebGLUniformLocation.h"
16 : #include "WebGLFormats.h"
17 : #include "WebGLFramebuffer.h"
18 : #include "WebGLRenderbuffer.h"
19 : #include "WebGLShaderPrecisionFormat.h"
20 : #include "WebGLTexture.h"
21 : #include "WebGLExtensions.h"
22 : #include "WebGLVertexArray.h"
23 :
24 : #include "nsDebug.h"
25 : #include "nsReadableUtils.h"
26 : #include "nsString.h"
27 :
28 : #include "gfxContext.h"
29 : #include "gfxPlatform.h"
30 : #include "GLContext.h"
31 :
32 : #include "nsContentUtils.h"
33 : #include "nsError.h"
34 : #include "nsLayoutUtils.h"
35 :
36 : #include "CanvasUtils.h"
37 : #include "gfxUtils.h"
38 :
39 : #include "jsfriendapi.h"
40 :
41 : #include "WebGLTexelConversions.h"
42 : #include "WebGLValidateStrings.h"
43 : #include <algorithm>
44 :
45 : // needed to check if current OS is lower than 10.7
46 : #if defined(MOZ_WIDGET_COCOA)
47 : #include "nsCocoaFeatures.h"
48 : #endif
49 :
50 : #include "mozilla/DebugOnly.h"
51 : #include "mozilla/dom/BindingUtils.h"
52 : #include "mozilla/dom/ImageData.h"
53 : #include "mozilla/dom/ToJSValue.h"
54 : #include "mozilla/EndianUtils.h"
55 : #include "mozilla/RefPtr.h"
56 : #include "mozilla/UniquePtrExtensions.h"
57 :
58 : namespace mozilla {
59 :
60 : bool
61 0 : WebGLContext::ValidateObject(const char* funcName, const WebGLProgram& object)
62 : {
63 0 : return ValidateObject(funcName, object, true);
64 : }
65 :
66 : bool
67 0 : WebGLContext::ValidateObject(const char* funcName, const WebGLShader& object)
68 : {
69 0 : return ValidateObject(funcName, object, true);
70 : }
71 :
72 : using namespace mozilla::dom;
73 : using namespace mozilla::gfx;
74 : using namespace mozilla::gl;
75 :
76 : //
77 : // WebGL API
78 : //
79 :
80 : void
81 0 : WebGLContext::ActiveTexture(GLenum texture)
82 : {
83 0 : if (IsContextLost())
84 0 : return;
85 :
86 0 : if (texture < LOCAL_GL_TEXTURE0 ||
87 0 : texture >= LOCAL_GL_TEXTURE0 + uint32_t(mGLMaxTextureUnits))
88 : {
89 0 : return ErrorInvalidEnum(
90 : "ActiveTexture: texture unit %d out of range. "
91 : "Accepted values range from TEXTURE0 to TEXTURE0 + %d. "
92 : "Notice that TEXTURE0 != 0.",
93 0 : texture, mGLMaxTextureUnits);
94 : }
95 :
96 0 : MakeContextCurrent();
97 0 : mActiveTexture = texture - LOCAL_GL_TEXTURE0;
98 0 : gl->fActiveTexture(texture);
99 : }
100 :
101 : void
102 0 : WebGLContext::AttachShader(WebGLProgram& program, WebGLShader& shader)
103 : {
104 0 : if (IsContextLost())
105 0 : return;
106 :
107 0 : if (!ValidateObject("attachShader: program", program) ||
108 0 : !ValidateObject("attachShader: shader", shader))
109 : {
110 0 : return;
111 : }
112 :
113 0 : program.AttachShader(&shader);
114 : }
115 :
116 : void
117 0 : WebGLContext::BindAttribLocation(WebGLProgram& prog, GLuint location,
118 : const nsAString& name)
119 : {
120 0 : if (IsContextLost())
121 0 : return;
122 :
123 0 : if (!ValidateObject("bindAttribLocation: program", prog))
124 0 : return;
125 :
126 0 : prog.BindAttribLocation(location, name);
127 : }
128 :
129 : void
130 0 : WebGLContext::BindFramebuffer(GLenum target, WebGLFramebuffer* wfb)
131 : {
132 0 : if (IsContextLost())
133 0 : return;
134 :
135 0 : if (!ValidateFramebufferTarget(target, "bindFramebuffer"))
136 0 : return;
137 :
138 0 : if (wfb && !ValidateObject("bindFramebuffer", *wfb))
139 0 : return;
140 :
141 0 : MakeContextCurrent();
142 :
143 0 : if (!wfb) {
144 0 : gl->fBindFramebuffer(target, 0);
145 : } else {
146 0 : GLuint framebuffername = wfb->mGLName;
147 0 : gl->fBindFramebuffer(target, framebuffername);
148 : #ifdef ANDROID
149 : wfb->mIsFB = true;
150 : #endif
151 : }
152 :
153 0 : switch (target) {
154 : case LOCAL_GL_FRAMEBUFFER:
155 0 : mBoundDrawFramebuffer = wfb;
156 0 : mBoundReadFramebuffer = wfb;
157 0 : break;
158 : case LOCAL_GL_DRAW_FRAMEBUFFER:
159 0 : mBoundDrawFramebuffer = wfb;
160 0 : break;
161 : case LOCAL_GL_READ_FRAMEBUFFER:
162 0 : mBoundReadFramebuffer = wfb;
163 0 : break;
164 : default:
165 0 : break;
166 : }
167 : }
168 :
169 : void
170 0 : WebGLContext::BindRenderbuffer(GLenum target, WebGLRenderbuffer* wrb)
171 : {
172 0 : if (IsContextLost())
173 0 : return;
174 :
175 0 : if (target != LOCAL_GL_RENDERBUFFER)
176 0 : return ErrorInvalidEnumInfo("bindRenderbuffer: target", target);
177 :
178 0 : if (wrb && !ValidateObject("bindRenderbuffer", *wrb))
179 0 : return;
180 :
181 : // Usually, we would now call into glBindRenderbuffer. However, since we have to
182 : // potentially emulate packed-depth-stencil, there's not a specific renderbuffer that
183 : // we know we should bind here.
184 : // Instead, we do all renderbuffer binding lazily.
185 :
186 0 : if (wrb) {
187 0 : wrb->mHasBeenBound = true;
188 : }
189 :
190 0 : mBoundRenderbuffer = wrb;
191 : }
192 :
193 0 : void WebGLContext::BlendEquation(GLenum mode)
194 : {
195 0 : if (IsContextLost())
196 0 : return;
197 :
198 0 : if (!ValidateBlendEquationEnum(mode, "blendEquation: mode"))
199 0 : return;
200 :
201 0 : MakeContextCurrent();
202 0 : gl->fBlendEquation(mode);
203 : }
204 :
205 0 : void WebGLContext::BlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha)
206 : {
207 0 : if (IsContextLost())
208 0 : return;
209 :
210 0 : if (!ValidateBlendEquationEnum(modeRGB, "blendEquationSeparate: modeRGB") ||
211 0 : !ValidateBlendEquationEnum(modeAlpha, "blendEquationSeparate: modeAlpha"))
212 0 : return;
213 :
214 0 : MakeContextCurrent();
215 0 : gl->fBlendEquationSeparate(modeRGB, modeAlpha);
216 : }
217 :
218 0 : void WebGLContext::BlendFunc(GLenum sfactor, GLenum dfactor)
219 : {
220 0 : if (IsContextLost())
221 0 : return;
222 :
223 0 : if (!ValidateBlendFuncSrcEnum(sfactor, "blendFunc: sfactor") ||
224 0 : !ValidateBlendFuncDstEnum(dfactor, "blendFunc: dfactor"))
225 0 : return;
226 :
227 0 : if (!ValidateBlendFuncEnumsCompatibility(sfactor, dfactor, "blendFuncSeparate: srcRGB and dstRGB"))
228 0 : return;
229 :
230 0 : MakeContextCurrent();
231 0 : gl->fBlendFunc(sfactor, dfactor);
232 : }
233 :
234 : void
235 0 : WebGLContext::BlendFuncSeparate(GLenum srcRGB, GLenum dstRGB,
236 : GLenum srcAlpha, GLenum dstAlpha)
237 : {
238 0 : if (IsContextLost())
239 0 : return;
240 :
241 0 : if (!ValidateBlendFuncSrcEnum(srcRGB, "blendFuncSeparate: srcRGB") ||
242 0 : !ValidateBlendFuncSrcEnum(srcAlpha, "blendFuncSeparate: srcAlpha") ||
243 0 : !ValidateBlendFuncDstEnum(dstRGB, "blendFuncSeparate: dstRGB") ||
244 0 : !ValidateBlendFuncDstEnum(dstAlpha, "blendFuncSeparate: dstAlpha"))
245 0 : return;
246 :
247 : // note that we only check compatibity for the RGB enums, no need to for the Alpha enums, see
248 : // "Section 6.8 forgetting to mention alpha factors?" thread on the public_webgl mailing list
249 0 : if (!ValidateBlendFuncEnumsCompatibility(srcRGB, dstRGB, "blendFuncSeparate: srcRGB and dstRGB"))
250 0 : return;
251 :
252 0 : MakeContextCurrent();
253 0 : gl->fBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha);
254 : }
255 :
256 : GLenum
257 0 : WebGLContext::CheckFramebufferStatus(GLenum target)
258 : {
259 0 : const char funcName[] = "checkFramebufferStatus";
260 0 : if (IsContextLost())
261 0 : return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED;
262 :
263 0 : if (!ValidateFramebufferTarget(target, funcName))
264 0 : return 0;
265 :
266 : WebGLFramebuffer* fb;
267 0 : switch (target) {
268 : case LOCAL_GL_FRAMEBUFFER:
269 : case LOCAL_GL_DRAW_FRAMEBUFFER:
270 0 : fb = mBoundDrawFramebuffer;
271 0 : break;
272 :
273 : case LOCAL_GL_READ_FRAMEBUFFER:
274 0 : fb = mBoundReadFramebuffer;
275 0 : break;
276 :
277 : default:
278 0 : MOZ_CRASH("GFX: Bad target.");
279 : }
280 :
281 0 : if (!fb)
282 0 : return LOCAL_GL_FRAMEBUFFER_COMPLETE;
283 :
284 0 : return fb->CheckFramebufferStatus(funcName).get();
285 : }
286 :
287 : already_AddRefed<WebGLProgram>
288 0 : WebGLContext::CreateProgram()
289 : {
290 0 : if (IsContextLost())
291 0 : return nullptr;
292 0 : RefPtr<WebGLProgram> globj = new WebGLProgram(this);
293 0 : return globj.forget();
294 : }
295 :
296 : already_AddRefed<WebGLShader>
297 0 : WebGLContext::CreateShader(GLenum type)
298 : {
299 0 : if (IsContextLost())
300 0 : return nullptr;
301 :
302 0 : if (type != LOCAL_GL_VERTEX_SHADER &&
303 : type != LOCAL_GL_FRAGMENT_SHADER)
304 : {
305 0 : ErrorInvalidEnumInfo("createShader: type", type);
306 0 : return nullptr;
307 : }
308 :
309 0 : RefPtr<WebGLShader> shader = new WebGLShader(this, type);
310 0 : return shader.forget();
311 : }
312 :
313 : void
314 0 : WebGLContext::CullFace(GLenum face)
315 : {
316 0 : if (IsContextLost())
317 0 : return;
318 :
319 0 : if (!ValidateFaceEnum(face, "cullFace"))
320 0 : return;
321 :
322 0 : MakeContextCurrent();
323 0 : gl->fCullFace(face);
324 : }
325 :
326 : void
327 0 : WebGLContext::DeleteFramebuffer(WebGLFramebuffer* fbuf)
328 : {
329 0 : if (!ValidateDeleteObject("deleteFramebuffer", fbuf))
330 0 : return;
331 :
332 0 : fbuf->RequestDelete();
333 :
334 0 : if (mBoundReadFramebuffer == mBoundDrawFramebuffer) {
335 0 : if (mBoundDrawFramebuffer == fbuf) {
336 : BindFramebuffer(LOCAL_GL_FRAMEBUFFER,
337 0 : static_cast<WebGLFramebuffer*>(nullptr));
338 : }
339 0 : } else if (mBoundDrawFramebuffer == fbuf) {
340 : BindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER,
341 0 : static_cast<WebGLFramebuffer*>(nullptr));
342 0 : } else if (mBoundReadFramebuffer == fbuf) {
343 : BindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER,
344 0 : static_cast<WebGLFramebuffer*>(nullptr));
345 : }
346 : }
347 :
348 : void
349 0 : WebGLContext::DeleteRenderbuffer(WebGLRenderbuffer* rbuf)
350 : {
351 0 : const char funcName[] = "deleteRenderbuffer";
352 0 : if (!ValidateDeleteObject(funcName, rbuf))
353 0 : return;
354 :
355 0 : if (mBoundDrawFramebuffer)
356 0 : mBoundDrawFramebuffer->DetachRenderbuffer(funcName, rbuf);
357 :
358 0 : if (mBoundReadFramebuffer)
359 0 : mBoundReadFramebuffer->DetachRenderbuffer(funcName, rbuf);
360 :
361 0 : rbuf->InvalidateStatusOfAttachedFBs(funcName);
362 :
363 0 : if (mBoundRenderbuffer == rbuf)
364 0 : BindRenderbuffer(LOCAL_GL_RENDERBUFFER, nullptr);
365 :
366 0 : rbuf->RequestDelete();
367 : }
368 :
369 : void
370 0 : WebGLContext::DeleteTexture(WebGLTexture* tex)
371 : {
372 0 : const char funcName[] = "deleteTexture";
373 0 : if (!ValidateDeleteObject(funcName, tex))
374 0 : return;
375 :
376 0 : if (mBoundDrawFramebuffer)
377 0 : mBoundDrawFramebuffer->DetachTexture(funcName, tex);
378 :
379 0 : if (mBoundReadFramebuffer)
380 0 : mBoundReadFramebuffer->DetachTexture(funcName, tex);
381 :
382 0 : GLuint activeTexture = mActiveTexture;
383 0 : for (int32_t i = 0; i < mGLMaxTextureUnits; i++) {
384 0 : if (mBound2DTextures[i] == tex ||
385 0 : mBoundCubeMapTextures[i] == tex ||
386 0 : mBound3DTextures[i] == tex ||
387 0 : mBound2DArrayTextures[i] == tex)
388 : {
389 0 : ActiveTexture(LOCAL_GL_TEXTURE0 + i);
390 0 : BindTexture(tex->Target().get(), nullptr);
391 : }
392 : }
393 0 : ActiveTexture(LOCAL_GL_TEXTURE0 + activeTexture);
394 :
395 0 : tex->RequestDelete();
396 : }
397 :
398 : void
399 0 : WebGLContext::DeleteProgram(WebGLProgram* prog)
400 : {
401 0 : if (!ValidateDeleteObject("deleteProgram", prog))
402 0 : return;
403 :
404 0 : prog->RequestDelete();
405 : }
406 :
407 : void
408 0 : WebGLContext::DeleteShader(WebGLShader* shader)
409 : {
410 0 : if (!ValidateDeleteObject("deleteShader", shader))
411 0 : return;
412 :
413 0 : shader->RequestDelete();
414 : }
415 :
416 : void
417 0 : WebGLContext::DetachShader(WebGLProgram& program, const WebGLShader& shader)
418 : {
419 0 : if (IsContextLost())
420 0 : return;
421 :
422 : // It's valid to attempt to detach a deleted shader, since it's still a
423 : // shader.
424 0 : if (!ValidateObject("detachShader: program", program) ||
425 0 : !ValidateObjectAllowDeleted("detachShader: shader", shader))
426 : {
427 0 : return;
428 : }
429 :
430 0 : program.DetachShader(&shader);
431 : }
432 :
433 : void
434 0 : WebGLContext::DepthFunc(GLenum func)
435 : {
436 0 : if (IsContextLost())
437 0 : return;
438 :
439 0 : if (!ValidateComparisonEnum(func, "depthFunc"))
440 0 : return;
441 :
442 0 : MakeContextCurrent();
443 0 : gl->fDepthFunc(func);
444 : }
445 :
446 : void
447 0 : WebGLContext::DepthRange(GLfloat zNear, GLfloat zFar)
448 : {
449 0 : if (IsContextLost())
450 0 : return;
451 :
452 0 : if (zNear > zFar)
453 0 : return ErrorInvalidOperation("depthRange: the near value is greater than the far value!");
454 :
455 0 : MakeContextCurrent();
456 0 : gl->fDepthRange(zNear, zFar);
457 : }
458 :
459 : void
460 0 : WebGLContext::FramebufferRenderbuffer(GLenum target, GLenum attachment,
461 : GLenum rbtarget, WebGLRenderbuffer* wrb)
462 : {
463 0 : const char funcName[] = "framebufferRenderbuffer";
464 0 : if (IsContextLost())
465 0 : return;
466 :
467 0 : if (!ValidateFramebufferTarget(target, funcName))
468 0 : return;
469 :
470 : WebGLFramebuffer* fb;
471 0 : switch (target) {
472 : case LOCAL_GL_FRAMEBUFFER:
473 : case LOCAL_GL_DRAW_FRAMEBUFFER:
474 0 : fb = mBoundDrawFramebuffer;
475 0 : break;
476 :
477 : case LOCAL_GL_READ_FRAMEBUFFER:
478 0 : fb = mBoundReadFramebuffer;
479 0 : break;
480 :
481 : default:
482 0 : MOZ_CRASH("GFX: Bad target.");
483 : }
484 :
485 0 : if (!fb)
486 0 : return ErrorInvalidOperation("%s: Cannot modify framebuffer 0.", funcName);
487 :
488 0 : fb->FramebufferRenderbuffer(funcName, attachment, rbtarget, wrb);
489 : }
490 :
491 : void
492 0 : WebGLContext::FramebufferTexture2D(GLenum target,
493 : GLenum attachment,
494 : GLenum textarget,
495 : WebGLTexture* tobj,
496 : GLint level)
497 : {
498 0 : const char funcName[] = "framebufferTexture2D";
499 0 : if (IsContextLost())
500 0 : return;
501 :
502 0 : if (!ValidateFramebufferTarget(target, funcName))
503 0 : return;
504 :
505 : WebGLFramebuffer* fb;
506 0 : switch (target) {
507 : case LOCAL_GL_FRAMEBUFFER:
508 : case LOCAL_GL_DRAW_FRAMEBUFFER:
509 0 : fb = mBoundDrawFramebuffer;
510 0 : break;
511 :
512 : case LOCAL_GL_READ_FRAMEBUFFER:
513 0 : fb = mBoundReadFramebuffer;
514 0 : break;
515 :
516 : default:
517 0 : MOZ_CRASH("GFX: Bad target.");
518 : }
519 :
520 0 : if (!fb)
521 0 : return ErrorInvalidOperation("%s: Cannot modify framebuffer 0.", funcName);
522 :
523 0 : fb->FramebufferTexture2D(funcName, attachment, textarget, tobj, level);
524 : }
525 :
526 : void
527 0 : WebGLContext::FrontFace(GLenum mode)
528 : {
529 0 : if (IsContextLost())
530 0 : return;
531 :
532 0 : switch (mode) {
533 : case LOCAL_GL_CW:
534 : case LOCAL_GL_CCW:
535 0 : break;
536 : default:
537 0 : return ErrorInvalidEnumInfo("frontFace: mode", mode);
538 : }
539 :
540 0 : MakeContextCurrent();
541 0 : gl->fFrontFace(mode);
542 : }
543 :
544 : already_AddRefed<WebGLActiveInfo>
545 0 : WebGLContext::GetActiveAttrib(const WebGLProgram& prog, GLuint index)
546 : {
547 0 : if (IsContextLost())
548 0 : return nullptr;
549 :
550 0 : if (!ValidateObject("getActiveAttrib: program", prog))
551 0 : return nullptr;
552 :
553 0 : return prog.GetActiveAttrib(index);
554 : }
555 :
556 : already_AddRefed<WebGLActiveInfo>
557 0 : WebGLContext::GetActiveUniform(const WebGLProgram& prog, GLuint index)
558 : {
559 0 : if (IsContextLost())
560 0 : return nullptr;
561 :
562 0 : if (!ValidateObject("getActiveUniform: program", prog))
563 0 : return nullptr;
564 :
565 0 : return prog.GetActiveUniform(index);
566 : }
567 :
568 : void
569 0 : WebGLContext::GetAttachedShaders(const WebGLProgram& prog,
570 : dom::Nullable<nsTArray<RefPtr<WebGLShader>>>& retval)
571 : {
572 0 : retval.SetNull();
573 0 : if (IsContextLost())
574 0 : return;
575 :
576 0 : if (!ValidateObject("getAttachedShaders", prog))
577 0 : return;
578 :
579 0 : prog.GetAttachedShaders(&retval.SetValue());
580 : }
581 :
582 : GLint
583 0 : WebGLContext::GetAttribLocation(const WebGLProgram& prog, const nsAString& name)
584 : {
585 0 : if (IsContextLost())
586 0 : return -1;
587 :
588 0 : if (!ValidateObject("getAttribLocation: program", prog))
589 0 : return -1;
590 :
591 0 : return prog.GetAttribLocation(name);
592 : }
593 :
594 : JS::Value
595 0 : WebGLContext::GetBufferParameter(GLenum target, GLenum pname)
596 : {
597 0 : const char funcName[] = "getBufferParameter";
598 0 : if (IsContextLost())
599 0 : return JS::NullValue();
600 :
601 0 : const auto& slot = ValidateBufferSlot(funcName, target);
602 0 : if (!slot)
603 0 : return JS::NullValue();
604 0 : const auto& buffer = *slot;
605 :
606 0 : if (!buffer) {
607 0 : ErrorInvalidOperation("%s: Buffer for `target` is null.", funcName);
608 0 : return JS::NullValue();
609 : }
610 :
611 0 : switch (pname) {
612 : case LOCAL_GL_BUFFER_SIZE:
613 0 : return JS::NumberValue(buffer->ByteLength());
614 :
615 : case LOCAL_GL_BUFFER_USAGE:
616 0 : return JS::NumberValue(buffer->Usage());
617 :
618 : default:
619 0 : ErrorInvalidEnumInfo("getBufferParameter: parameter", pname);
620 0 : return JS::NullValue();
621 : }
622 : }
623 :
624 : JS::Value
625 0 : WebGLContext::GetFramebufferAttachmentParameter(JSContext* cx,
626 : GLenum target,
627 : GLenum attachment,
628 : GLenum pname,
629 : ErrorResult& rv)
630 : {
631 0 : const char funcName[] = "getFramebufferAttachmentParameter";
632 :
633 0 : if (IsContextLost())
634 0 : return JS::NullValue();
635 :
636 0 : if (!ValidateFramebufferTarget(target, funcName))
637 0 : return JS::NullValue();
638 :
639 : WebGLFramebuffer* fb;
640 0 : switch (target) {
641 : case LOCAL_GL_FRAMEBUFFER:
642 : case LOCAL_GL_DRAW_FRAMEBUFFER:
643 0 : fb = mBoundDrawFramebuffer;
644 0 : break;
645 :
646 : case LOCAL_GL_READ_FRAMEBUFFER:
647 0 : fb = mBoundReadFramebuffer;
648 0 : break;
649 :
650 : default:
651 0 : MOZ_CRASH("GFX: Bad target.");
652 : }
653 :
654 0 : MakeContextCurrent();
655 :
656 0 : if (fb)
657 0 : return fb->GetAttachmentParameter(funcName, cx, target, attachment, pname, &rv);
658 :
659 : ////////////////////////////////////
660 :
661 0 : if (!IsWebGL2()) {
662 : ErrorInvalidOperation("%s: Querying against the default framebuffer is not"
663 : " allowed in WebGL 1.",
664 0 : funcName);
665 0 : return JS::NullValue();
666 : }
667 :
668 0 : switch (attachment) {
669 : case LOCAL_GL_BACK:
670 : case LOCAL_GL_DEPTH:
671 : case LOCAL_GL_STENCIL:
672 0 : break;
673 :
674 : default:
675 : ErrorInvalidEnum("%s: For the default framebuffer, can only query COLOR, DEPTH,"
676 : " or STENCIL.",
677 0 : funcName);
678 0 : return JS::NullValue();
679 : }
680 :
681 0 : switch (pname) {
682 : case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
683 0 : switch (attachment) {
684 : case LOCAL_GL_BACK:
685 0 : break;
686 : case LOCAL_GL_DEPTH:
687 0 : if (!mOptions.depth) {
688 0 : return JS::Int32Value(LOCAL_GL_NONE);
689 : }
690 0 : break;
691 : case LOCAL_GL_STENCIL:
692 0 : if (!mOptions.stencil) {
693 0 : return JS::Int32Value(LOCAL_GL_NONE);
694 : }
695 0 : break;
696 : default:
697 : ErrorInvalidEnum("%s: With the default framebuffer, can only query COLOR, DEPTH,"
698 : " or STENCIL for GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE",
699 0 : funcName);
700 0 : return JS::NullValue();
701 : }
702 0 : return JS::Int32Value(LOCAL_GL_FRAMEBUFFER_DEFAULT);
703 :
704 : ////////////////
705 :
706 : case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE:
707 : case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE:
708 : case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE:
709 0 : if (attachment == LOCAL_GL_BACK)
710 0 : return JS::NumberValue(8);
711 0 : return JS::NumberValue(0);
712 :
713 : case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE:
714 0 : if (attachment == LOCAL_GL_BACK) {
715 0 : if (mOptions.alpha) {
716 0 : return JS::NumberValue(8);
717 : }
718 0 : ErrorInvalidOperation("The default framebuffer doesn't contain an alpha buffer");
719 0 : return JS::NullValue();
720 : }
721 0 : return JS::NumberValue(0);
722 :
723 : case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE:
724 0 : if (attachment == LOCAL_GL_DEPTH) {
725 0 : if (mOptions.depth) {
726 0 : return JS::NumberValue(24);
727 : }
728 0 : ErrorInvalidOperation("The default framebuffer doesn't contain an depth buffer");
729 0 : return JS::NullValue();
730 : }
731 0 : return JS::NumberValue(0);
732 :
733 : case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE:
734 0 : if (attachment == LOCAL_GL_STENCIL) {
735 0 : if (mOptions.stencil) {
736 0 : return JS::NumberValue(8);
737 : }
738 0 : ErrorInvalidOperation("The default framebuffer doesn't contain an stencil buffer");
739 0 : return JS::NullValue();
740 : }
741 0 : return JS::NumberValue(0);
742 :
743 : case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE:
744 0 : if (attachment == LOCAL_GL_STENCIL) {
745 0 : if (mOptions.stencil) {
746 0 : return JS::NumberValue(LOCAL_GL_UNSIGNED_INT);
747 : }
748 0 : ErrorInvalidOperation("The default framebuffer doesn't contain an stencil buffer");
749 0 : } else if (attachment == LOCAL_GL_DEPTH) {
750 0 : if (mOptions.depth) {
751 0 : return JS::NumberValue(LOCAL_GL_UNSIGNED_NORMALIZED);
752 : }
753 0 : ErrorInvalidOperation("The default framebuffer doesn't contain an depth buffer");
754 : } else { // LOCAL_GL_BACK
755 0 : return JS::NumberValue(LOCAL_GL_UNSIGNED_NORMALIZED);
756 : }
757 0 : return JS::NullValue();
758 :
759 : case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:
760 0 : if (attachment == LOCAL_GL_STENCIL) {
761 0 : if (!mOptions.stencil) {
762 0 : ErrorInvalidOperation("The default framebuffer doesn't contain an stencil buffer");
763 0 : return JS::NullValue();
764 : }
765 0 : } else if (attachment == LOCAL_GL_DEPTH) {
766 0 : if (!mOptions.depth) {
767 0 : ErrorInvalidOperation("The default framebuffer doesn't contain an depth buffer");
768 0 : return JS::NullValue();
769 : }
770 : }
771 0 : return JS::NumberValue(LOCAL_GL_LINEAR);
772 : }
773 :
774 0 : ErrorInvalidEnum("%s: Invalid pname: 0x%04x", funcName, pname);
775 0 : return JS::NullValue();
776 : }
777 :
778 : JS::Value
779 0 : WebGLContext::GetRenderbufferParameter(GLenum target, GLenum pname)
780 : {
781 0 : if (IsContextLost())
782 0 : return JS::NullValue();
783 :
784 0 : if (target != LOCAL_GL_RENDERBUFFER) {
785 0 : ErrorInvalidEnumInfo("getRenderbufferParameter: target", target);
786 0 : return JS::NullValue();
787 : }
788 :
789 0 : if (!mBoundRenderbuffer) {
790 0 : ErrorInvalidOperation("getRenderbufferParameter: no render buffer is bound");
791 0 : return JS::NullValue();
792 : }
793 :
794 0 : MakeContextCurrent();
795 :
796 0 : switch (pname) {
797 : case LOCAL_GL_RENDERBUFFER_SAMPLES:
798 0 : if (!IsWebGL2())
799 0 : break;
800 : MOZ_FALLTHROUGH;
801 :
802 : case LOCAL_GL_RENDERBUFFER_WIDTH:
803 : case LOCAL_GL_RENDERBUFFER_HEIGHT:
804 : case LOCAL_GL_RENDERBUFFER_RED_SIZE:
805 : case LOCAL_GL_RENDERBUFFER_GREEN_SIZE:
806 : case LOCAL_GL_RENDERBUFFER_BLUE_SIZE:
807 : case LOCAL_GL_RENDERBUFFER_ALPHA_SIZE:
808 : case LOCAL_GL_RENDERBUFFER_DEPTH_SIZE:
809 : case LOCAL_GL_RENDERBUFFER_STENCIL_SIZE:
810 : case LOCAL_GL_RENDERBUFFER_INTERNAL_FORMAT:
811 : {
812 : // RB emulation means we have to ask the RB itself.
813 0 : GLint i = mBoundRenderbuffer->GetRenderbufferParameter(target, pname);
814 0 : return JS::Int32Value(i);
815 : }
816 :
817 : default:
818 0 : break;
819 : }
820 :
821 0 : ErrorInvalidEnumInfo("getRenderbufferParameter: parameter", pname);
822 0 : return JS::NullValue();
823 : }
824 :
825 : already_AddRefed<WebGLTexture>
826 0 : WebGLContext::CreateTexture()
827 : {
828 0 : if (IsContextLost())
829 0 : return nullptr;
830 :
831 0 : GLuint tex = 0;
832 0 : MakeContextCurrent();
833 0 : gl->fGenTextures(1, &tex);
834 :
835 0 : RefPtr<WebGLTexture> globj = new WebGLTexture(this, tex);
836 0 : return globj.forget();
837 : }
838 :
839 : static GLenum
840 0 : GetAndClearError(GLenum* errorVar)
841 : {
842 0 : MOZ_ASSERT(errorVar);
843 0 : GLenum ret = *errorVar;
844 0 : *errorVar = LOCAL_GL_NO_ERROR;
845 0 : return ret;
846 : }
847 :
848 : GLenum
849 0 : WebGLContext::GetError()
850 : {
851 : /* WebGL 1.0: Section 5.14.3: Setting and getting state:
852 : * If the context's webgl context lost flag is set, returns
853 : * CONTEXT_LOST_WEBGL the first time this method is called.
854 : * Afterward, returns NO_ERROR until the context has been
855 : * restored.
856 : *
857 : * WEBGL_lose_context:
858 : * [When this extension is enabled: ] loseContext and
859 : * restoreContext are allowed to generate INVALID_OPERATION errors
860 : * even when the context is lost.
861 : */
862 :
863 0 : if (IsContextLost()) {
864 0 : if (mEmitContextLostErrorOnce) {
865 0 : mEmitContextLostErrorOnce = false;
866 0 : return LOCAL_GL_CONTEXT_LOST_WEBGL;
867 : }
868 : // Don't return yet, since WEBGL_lose_contexts contradicts the
869 : // original spec, and allows error generation while lost.
870 : }
871 :
872 0 : GLenum err = GetAndClearError(&mWebGLError);
873 0 : if (err != LOCAL_GL_NO_ERROR)
874 0 : return err;
875 :
876 0 : if (IsContextLost())
877 0 : return LOCAL_GL_NO_ERROR;
878 :
879 : // Either no WebGL-side error, or it's already been cleared.
880 : // UnderlyingGL-side errors, now.
881 :
882 0 : MakeContextCurrent();
883 0 : GetAndFlushUnderlyingGLErrors();
884 :
885 0 : err = GetAndClearError(&mUnderlyingGLError);
886 0 : return err;
887 : }
888 :
889 : JS::Value
890 0 : WebGLContext::GetProgramParameter(const WebGLProgram& prog, GLenum pname)
891 : {
892 0 : if (IsContextLost())
893 0 : return JS::NullValue();
894 :
895 0 : if (!ValidateObjectAllowDeleted("getProgramParameter: program", prog))
896 0 : return JS::NullValue();
897 :
898 0 : return prog.GetProgramParameter(pname);
899 : }
900 :
901 : void
902 0 : WebGLContext::GetProgramInfoLog(const WebGLProgram& prog, nsAString& retval)
903 : {
904 0 : retval.SetIsVoid(true);
905 :
906 0 : if (IsContextLost())
907 0 : return;
908 :
909 0 : if (!ValidateObject("getProgramInfoLog: program", prog))
910 0 : return;
911 :
912 0 : prog.GetProgramInfoLog(&retval);
913 : }
914 :
915 : JS::Value
916 0 : WebGLContext::GetUniform(JSContext* js, const WebGLProgram& prog,
917 : const WebGLUniformLocation& loc)
918 : {
919 0 : if (IsContextLost())
920 0 : return JS::NullValue();
921 :
922 0 : if (!ValidateObject("getUniform: `program`", prog))
923 0 : return JS::NullValue();
924 :
925 0 : if (!ValidateObjectAllowDeleted("getUniform: `location`", loc))
926 0 : return JS::NullValue();
927 :
928 0 : if (!loc.ValidateForProgram(&prog, "getUniform"))
929 0 : return JS::NullValue();
930 :
931 0 : return loc.GetUniform(js);
932 : }
933 :
934 : already_AddRefed<WebGLUniformLocation>
935 0 : WebGLContext::GetUniformLocation(const WebGLProgram& prog, const nsAString& name)
936 : {
937 0 : if (IsContextLost())
938 0 : return nullptr;
939 :
940 0 : if (!ValidateObject("getUniformLocation: program", prog))
941 0 : return nullptr;
942 :
943 0 : return prog.GetUniformLocation(name);
944 : }
945 :
946 : void
947 0 : WebGLContext::Hint(GLenum target, GLenum mode)
948 : {
949 0 : if (IsContextLost())
950 0 : return;
951 :
952 0 : bool isValid = false;
953 :
954 0 : switch (target) {
955 : case LOCAL_GL_GENERATE_MIPMAP_HINT:
956 0 : mGenerateMipmapHint = mode;
957 :
958 : // Deprecated and removed in desktop GL Core profiles.
959 0 : if (gl->IsCoreProfile())
960 0 : return;
961 :
962 0 : isValid = true;
963 0 : break;
964 :
965 : case LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT:
966 0 : if (IsWebGL2() ||
967 0 : IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives))
968 : {
969 0 : isValid = true;
970 : }
971 0 : break;
972 : }
973 :
974 0 : if (!isValid)
975 0 : return ErrorInvalidEnum("hint: invalid hint");
976 :
977 0 : MakeContextCurrent();
978 0 : gl->fHint(target, mode);
979 : }
980 :
981 : bool
982 0 : WebGLContext::IsFramebuffer(const WebGLFramebuffer* fb)
983 : {
984 0 : if (!ValidateIsObject("isFramebuffer", fb))
985 0 : return false;
986 :
987 : #ifdef ANDROID
988 : if (gl->WorkAroundDriverBugs() &&
989 : gl->Renderer() == GLRenderer::AndroidEmulator)
990 : {
991 : return fb->mIsFB;
992 : }
993 : #endif
994 :
995 0 : MakeContextCurrent();
996 0 : return gl->fIsFramebuffer(fb->mGLName);
997 : }
998 :
999 : bool
1000 0 : WebGLContext::IsProgram(const WebGLProgram* prog)
1001 : {
1002 0 : if (!ValidateIsObject("isProgram", prog))
1003 0 : return false;
1004 :
1005 0 : return true;
1006 : }
1007 :
1008 : bool
1009 0 : WebGLContext::IsRenderbuffer(const WebGLRenderbuffer* rb)
1010 : {
1011 0 : if (!ValidateIsObject("isRenderbuffer", rb))
1012 0 : return false;
1013 :
1014 0 : return rb->mHasBeenBound;
1015 : }
1016 :
1017 : bool
1018 0 : WebGLContext::IsShader(const WebGLShader* shader)
1019 : {
1020 0 : if (!ValidateIsObject("isShader", shader))
1021 0 : return false;
1022 :
1023 0 : return true;
1024 : }
1025 :
1026 : void
1027 0 : WebGLContext::LinkProgram(WebGLProgram& prog)
1028 : {
1029 0 : if (IsContextLost())
1030 0 : return;
1031 :
1032 0 : if (!ValidateObject("linkProgram", prog))
1033 0 : return;
1034 :
1035 0 : prog.LinkProgram();
1036 :
1037 0 : if (!prog.IsLinked()) {
1038 : // If we failed to link, but `prog == mCurrentProgram`, we are *not* supposed to
1039 : // null out mActiveProgramLinkInfo.
1040 0 : return;
1041 : }
1042 :
1043 0 : if (&prog == mCurrentProgram) {
1044 0 : mActiveProgramLinkInfo = prog.LinkInfo();
1045 :
1046 0 : if (gl->WorkAroundDriverBugs() &&
1047 0 : gl->Vendor() == gl::GLVendor::NVIDIA)
1048 : {
1049 0 : gl->fUseProgram(prog.mGLName);
1050 : }
1051 : }
1052 : }
1053 :
1054 : void
1055 0 : WebGLContext::PixelStorei(GLenum pname, GLint param)
1056 : {
1057 0 : if (IsContextLost())
1058 0 : return;
1059 :
1060 0 : if (IsWebGL2()) {
1061 0 : uint32_t* pValueSlot = nullptr;
1062 0 : switch (pname) {
1063 : case LOCAL_GL_UNPACK_IMAGE_HEIGHT:
1064 0 : pValueSlot = &mPixelStore_UnpackImageHeight;
1065 0 : break;
1066 :
1067 : case LOCAL_GL_UNPACK_SKIP_IMAGES:
1068 0 : pValueSlot = &mPixelStore_UnpackSkipImages;
1069 0 : break;
1070 :
1071 : case LOCAL_GL_UNPACK_ROW_LENGTH:
1072 0 : pValueSlot = &mPixelStore_UnpackRowLength;
1073 0 : break;
1074 :
1075 : case LOCAL_GL_UNPACK_SKIP_ROWS:
1076 0 : pValueSlot = &mPixelStore_UnpackSkipRows;
1077 0 : break;
1078 :
1079 : case LOCAL_GL_UNPACK_SKIP_PIXELS:
1080 0 : pValueSlot = &mPixelStore_UnpackSkipPixels;
1081 0 : break;
1082 :
1083 : case LOCAL_GL_PACK_ROW_LENGTH:
1084 0 : pValueSlot = &mPixelStore_PackRowLength;
1085 0 : break;
1086 :
1087 : case LOCAL_GL_PACK_SKIP_ROWS:
1088 0 : pValueSlot = &mPixelStore_PackSkipRows;
1089 0 : break;
1090 :
1091 : case LOCAL_GL_PACK_SKIP_PIXELS:
1092 0 : pValueSlot = &mPixelStore_PackSkipPixels;
1093 0 : break;
1094 : }
1095 :
1096 0 : if (pValueSlot) {
1097 0 : if (param < 0) {
1098 0 : ErrorInvalidValue("pixelStorei: param must be >= 0.");
1099 0 : return;
1100 : }
1101 :
1102 0 : MakeContextCurrent();
1103 0 : gl->fPixelStorei(pname, param);
1104 0 : *pValueSlot = param;
1105 0 : return;
1106 : }
1107 : }
1108 :
1109 0 : switch (pname) {
1110 : case UNPACK_FLIP_Y_WEBGL:
1111 0 : mPixelStore_FlipY = bool(param);
1112 0 : return;
1113 :
1114 : case UNPACK_PREMULTIPLY_ALPHA_WEBGL:
1115 0 : mPixelStore_PremultiplyAlpha = bool(param);
1116 0 : return;
1117 :
1118 : case UNPACK_COLORSPACE_CONVERSION_WEBGL:
1119 0 : switch (param) {
1120 : case LOCAL_GL_NONE:
1121 : case BROWSER_DEFAULT_WEBGL:
1122 0 : mPixelStore_ColorspaceConversion = param;
1123 0 : return;
1124 :
1125 : default:
1126 0 : ErrorInvalidEnumInfo("pixelStorei: colorspace conversion parameter",
1127 0 : param);
1128 0 : return;
1129 : }
1130 :
1131 : case UNPACK_REQUIRE_FASTPATH:
1132 0 : if (IsExtensionEnabled(WebGLExtensionID::MOZ_debug)) {
1133 0 : mPixelStore_RequireFastPath = bool(param);
1134 0 : return;
1135 : }
1136 0 : break;
1137 :
1138 : case LOCAL_GL_PACK_ALIGNMENT:
1139 : case LOCAL_GL_UNPACK_ALIGNMENT:
1140 0 : switch (param) {
1141 : case 1:
1142 : case 2:
1143 : case 4:
1144 : case 8:
1145 0 : if (pname == LOCAL_GL_PACK_ALIGNMENT)
1146 0 : mPixelStore_PackAlignment = param;
1147 0 : else if (pname == LOCAL_GL_UNPACK_ALIGNMENT)
1148 0 : mPixelStore_UnpackAlignment = param;
1149 :
1150 0 : MakeContextCurrent();
1151 0 : gl->fPixelStorei(pname, param);
1152 0 : return;
1153 :
1154 : default:
1155 0 : ErrorInvalidValue("pixelStorei: invalid pack/unpack alignment value");
1156 0 : return;
1157 : }
1158 :
1159 :
1160 :
1161 : default:
1162 0 : break;
1163 : }
1164 :
1165 0 : ErrorInvalidEnumInfo("pixelStorei: parameter", pname);
1166 : }
1167 :
1168 : bool
1169 0 : WebGLContext::DoReadPixelsAndConvert(const webgl::FormatInfo* srcFormat, GLint x, GLint y,
1170 : GLsizei width, GLsizei height, GLenum format,
1171 : GLenum destType, void* dest, uint32_t destSize,
1172 : uint32_t rowStride)
1173 : {
1174 : // On at least Win+NV, we'll get PBO errors if we don't have at least
1175 : // `rowStride * height` bytes available to read into.
1176 0 : const auto naiveBytesNeeded = CheckedUint32(rowStride) * height;
1177 0 : const bool isDangerCloseToEdge = (!naiveBytesNeeded.isValid() ||
1178 0 : naiveBytesNeeded.value() > destSize);
1179 0 : const bool useParanoidHandling = (gl->WorkAroundDriverBugs() &&
1180 0 : isDangerCloseToEdge &&
1181 0 : mBoundPixelPackBuffer);
1182 0 : if (!useParanoidHandling) {
1183 0 : gl->fReadPixels(x, y, width, height, format, destType, dest);
1184 0 : return true;
1185 : }
1186 :
1187 : // Read everything but the last row.
1188 0 : const auto bodyHeight = height - 1;
1189 0 : if (bodyHeight) {
1190 0 : gl->fReadPixels(x, y, width, bodyHeight, format, destType, dest);
1191 : }
1192 :
1193 : // Now read the last row.
1194 0 : gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 1);
1195 0 : gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, 0);
1196 0 : gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, 0);
1197 :
1198 0 : const auto tailRowOffset = (char*)dest + rowStride * bodyHeight;
1199 0 : gl->fReadPixels(x, y+bodyHeight, width, 1, format, destType, tailRowOffset);
1200 :
1201 0 : gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, mPixelStore_PackAlignment);
1202 0 : gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, mPixelStore_PackRowLength);
1203 0 : gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, mPixelStore_PackSkipRows);
1204 0 : return true;
1205 : }
1206 :
1207 : static bool
1208 0 : GetJSScalarFromGLType(GLenum type, js::Scalar::Type* const out_scalarType)
1209 : {
1210 0 : switch (type) {
1211 : case LOCAL_GL_BYTE:
1212 0 : *out_scalarType = js::Scalar::Int8;
1213 0 : return true;
1214 :
1215 : case LOCAL_GL_UNSIGNED_BYTE:
1216 0 : *out_scalarType = js::Scalar::Uint8;
1217 0 : return true;
1218 :
1219 : case LOCAL_GL_SHORT:
1220 0 : *out_scalarType = js::Scalar::Int16;
1221 0 : return true;
1222 :
1223 : case LOCAL_GL_HALF_FLOAT:
1224 : case LOCAL_GL_HALF_FLOAT_OES:
1225 : case LOCAL_GL_UNSIGNED_SHORT:
1226 : case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
1227 : case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
1228 : case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
1229 0 : *out_scalarType = js::Scalar::Uint16;
1230 0 : return true;
1231 :
1232 : case LOCAL_GL_UNSIGNED_INT:
1233 : case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV:
1234 : case LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV:
1235 : case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV:
1236 : case LOCAL_GL_UNSIGNED_INT_24_8:
1237 0 : *out_scalarType = js::Scalar::Uint32;
1238 0 : return true;
1239 : case LOCAL_GL_INT:
1240 0 : *out_scalarType = js::Scalar::Int32;
1241 0 : return true;
1242 :
1243 : case LOCAL_GL_FLOAT:
1244 0 : *out_scalarType = js::Scalar::Float32;
1245 0 : return true;
1246 :
1247 : default:
1248 0 : return false;
1249 : }
1250 : }
1251 :
1252 : bool
1253 0 : WebGLContext::ReadPixels_SharedPrecheck(CallerType aCallerType,
1254 : ErrorResult& out_error)
1255 : {
1256 0 : if (IsContextLost())
1257 0 : return false;
1258 :
1259 0 : if (mCanvasElement &&
1260 0 : mCanvasElement->IsWriteOnly() &&
1261 : aCallerType != CallerType::System)
1262 : {
1263 0 : GenerateWarning("readPixels: Not allowed");
1264 0 : out_error.Throw(NS_ERROR_DOM_SECURITY_ERR);
1265 0 : return false;
1266 : }
1267 :
1268 0 : return true;
1269 : }
1270 :
1271 : bool
1272 0 : WebGLContext::ValidatePackSize(const char* funcName, uint32_t width, uint32_t height,
1273 : uint8_t bytesPerPixel, uint32_t* const out_rowStride,
1274 : uint32_t* const out_endOffset)
1275 : {
1276 0 : if (!width || !height) {
1277 0 : *out_rowStride = 0;
1278 0 : *out_endOffset = 0;
1279 0 : return true;
1280 : }
1281 :
1282 : // GLES 3.0.4, p116 (PACK_ functions like UNPACK_)
1283 :
1284 0 : const auto rowLength = (mPixelStore_PackRowLength ? mPixelStore_PackRowLength
1285 0 : : width);
1286 0 : const auto skipPixels = mPixelStore_PackSkipPixels;
1287 0 : const auto skipRows = mPixelStore_PackSkipRows;
1288 0 : const auto alignment = mPixelStore_PackAlignment;
1289 :
1290 0 : const auto usedPixelsPerRow = CheckedUint32(skipPixels) + width;
1291 0 : const auto usedRowsPerImage = CheckedUint32(skipRows) + height;
1292 :
1293 0 : if (!usedPixelsPerRow.isValid() || usedPixelsPerRow.value() > rowLength) {
1294 0 : ErrorInvalidOperation("%s: SKIP_PIXELS + width > ROW_LENGTH.", funcName);
1295 0 : return false;
1296 : }
1297 :
1298 0 : const auto rowLengthBytes = CheckedUint32(rowLength) * bytesPerPixel;
1299 0 : const auto rowStride = RoundUpToMultipleOf(rowLengthBytes, alignment);
1300 :
1301 0 : const auto usedBytesPerRow = usedPixelsPerRow * bytesPerPixel;
1302 0 : const auto usedBytesPerImage = (usedRowsPerImage - 1) * rowStride + usedBytesPerRow;
1303 :
1304 0 : if (!rowStride.isValid() || !usedBytesPerImage.isValid()) {
1305 0 : ErrorInvalidOperation("%s: Invalid UNPACK_ params.", funcName);
1306 0 : return false;
1307 : }
1308 :
1309 0 : *out_rowStride = rowStride.value();
1310 0 : *out_endOffset = usedBytesPerImage.value();
1311 0 : return true;
1312 : }
1313 :
1314 : void
1315 0 : WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format,
1316 : GLenum type, const dom::ArrayBufferView& dstView,
1317 : GLuint dstElemOffset, CallerType aCallerType,
1318 : ErrorResult& out_error)
1319 : {
1320 0 : const char funcName[] = "readPixels";
1321 0 : if (!ReadPixels_SharedPrecheck(aCallerType, out_error))
1322 0 : return;
1323 :
1324 0 : if (mBoundPixelPackBuffer) {
1325 0 : ErrorInvalidOperation("%s: PIXEL_PACK_BUFFER must be null.", funcName);
1326 0 : return;
1327 : }
1328 :
1329 : ////
1330 :
1331 : js::Scalar::Type reqScalarType;
1332 0 : if (!GetJSScalarFromGLType(type, &reqScalarType)) {
1333 0 : ErrorInvalidEnum("%s: Bad `type`.", funcName);
1334 0 : return;
1335 : }
1336 :
1337 0 : const auto& viewElemType = dstView.Type();
1338 0 : if (viewElemType != reqScalarType) {
1339 0 : ErrorInvalidOperation("%s: `pixels` type does not match `type`.", funcName);
1340 0 : return;
1341 : }
1342 :
1343 : ////
1344 :
1345 : uint8_t* bytes;
1346 : size_t byteLen;
1347 0 : if (!ValidateArrayBufferView(funcName, dstView, dstElemOffset, 0, &bytes, &byteLen))
1348 0 : return;
1349 :
1350 : ////
1351 :
1352 0 : ReadPixelsImpl(x, y, width, height, format, type, bytes, byteLen);
1353 : }
1354 :
1355 : void
1356 0 : WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format,
1357 : GLenum type, WebGLsizeiptr offset,
1358 : CallerType aCallerType, ErrorResult& out_error)
1359 : {
1360 0 : const char funcName[] = "readPixels";
1361 0 : if (!ReadPixels_SharedPrecheck(aCallerType, out_error))
1362 0 : return;
1363 :
1364 0 : const auto& buffer = ValidateBufferSelection(funcName, LOCAL_GL_PIXEL_PACK_BUFFER);
1365 0 : if (!buffer)
1366 0 : return;
1367 :
1368 : //////
1369 :
1370 0 : if (!ValidateNonNegative(funcName, "offset", offset))
1371 0 : return;
1372 :
1373 : {
1374 0 : const auto bytesPerType = webgl::BytesPerPixel({LOCAL_GL_RED, type});
1375 :
1376 0 : if (offset % bytesPerType != 0) {
1377 : ErrorInvalidOperation("%s: `offset` must be divisible by the size of `type`"
1378 : " in bytes.",
1379 0 : funcName);
1380 0 : return;
1381 : }
1382 : }
1383 :
1384 : //////
1385 :
1386 0 : const auto bytesAvailable = buffer->ByteLength();
1387 0 : const auto checkedBytesAfterOffset = CheckedUint32(bytesAvailable) - offset;
1388 :
1389 0 : uint32_t bytesAfterOffset = 0;
1390 0 : if (checkedBytesAfterOffset.isValid()) {
1391 0 : bytesAfterOffset = checkedBytesAfterOffset.value();
1392 : }
1393 :
1394 0 : gl->MakeCurrent();
1395 0 : const ScopedLazyBind lazyBind(gl, LOCAL_GL_PIXEL_PACK_BUFFER, buffer);
1396 :
1397 0 : ReadPixelsImpl(x, y, width, height, format, type, (void*)offset, bytesAfterOffset);
1398 : }
1399 :
1400 : static webgl::PackingInfo
1401 0 : DefaultReadPixelPI(const webgl::FormatUsageInfo* usage)
1402 : {
1403 0 : MOZ_ASSERT(usage->IsRenderable());
1404 :
1405 0 : switch (usage->format->componentType) {
1406 : case webgl::ComponentType::NormUInt:
1407 0 : return { LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE };
1408 :
1409 : case webgl::ComponentType::Int:
1410 0 : return { LOCAL_GL_RGBA_INTEGER, LOCAL_GL_INT };
1411 :
1412 : case webgl::ComponentType::UInt:
1413 0 : return { LOCAL_GL_RGBA_INTEGER, LOCAL_GL_UNSIGNED_INT };
1414 :
1415 : case webgl::ComponentType::Float:
1416 0 : return { LOCAL_GL_RGBA, LOCAL_GL_FLOAT };
1417 :
1418 : default:
1419 0 : MOZ_CRASH();
1420 : }
1421 : }
1422 :
1423 : static bool
1424 0 : ArePossiblePackEnums(const WebGLContext* webgl, const webgl::PackingInfo& pi)
1425 : {
1426 : // OpenGL ES 2.0 $4.3.1 - IMPLEMENTATION_COLOR_READ_{TYPE/FORMAT} is a valid
1427 : // combination for glReadPixels()...
1428 :
1429 : // So yeah, we are actually checking that these are valid as /unpack/ formats, instead
1430 : // of /pack/ formats here, but it should cover the INVALID_ENUM cases.
1431 0 : if (!webgl->mFormatUsage->AreUnpackEnumsValid(pi.format, pi.type))
1432 0 : return false;
1433 :
1434 : // Only valid when pulled from:
1435 : // * GLES 2.0.25 p105:
1436 : // "table 3.4, excluding formats LUMINANCE and LUMINANCE_ALPHA."
1437 : // * GLES 3.0.4 p193:
1438 : // "table 3.2, excluding formats DEPTH_COMPONENT and DEPTH_STENCIL."
1439 0 : switch (pi.format) {
1440 : case LOCAL_GL_LUMINANCE:
1441 : case LOCAL_GL_LUMINANCE_ALPHA:
1442 : case LOCAL_GL_DEPTH_COMPONENT:
1443 : case LOCAL_GL_DEPTH_STENCIL:
1444 0 : return false;
1445 : }
1446 :
1447 0 : if (pi.type == LOCAL_GL_UNSIGNED_INT_24_8)
1448 0 : return false;
1449 :
1450 0 : return true;
1451 : }
1452 :
1453 : webgl::PackingInfo
1454 0 : WebGLContext::ValidImplementationColorReadPI(const webgl::FormatUsageInfo* usage) const
1455 : {
1456 0 : const auto defaultPI = DefaultReadPixelPI(usage);
1457 :
1458 : // ES2_compatibility always returns RGBA/UNSIGNED_BYTE, so branch on actual IsGLES().
1459 : // Also OSX+NV generates an error here.
1460 0 : if (!gl->IsGLES())
1461 0 : return defaultPI;
1462 :
1463 : webgl::PackingInfo implPI;
1464 0 : gl->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT, (GLint*)&implPI.format);
1465 0 : gl->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE, (GLint*)&implPI.type);
1466 :
1467 0 : if (!ArePossiblePackEnums(this, implPI))
1468 0 : return defaultPI;
1469 :
1470 0 : return implPI;
1471 : }
1472 :
1473 : static bool
1474 0 : ValidateReadPixelsFormatAndType(const webgl::FormatUsageInfo* srcUsage,
1475 : const webgl::PackingInfo& pi, gl::GLContext* gl,
1476 : WebGLContext* webgl)
1477 : {
1478 0 : const char funcName[] = "readPixels";
1479 :
1480 0 : if (!ArePossiblePackEnums(webgl, pi)) {
1481 0 : webgl->ErrorInvalidEnum("%s: Unexpected format or type.", funcName);
1482 0 : return false;
1483 : }
1484 :
1485 0 : const auto defaultPI = DefaultReadPixelPI(srcUsage);
1486 0 : if (pi == defaultPI)
1487 0 : return true;
1488 :
1489 : ////
1490 :
1491 : // OpenGL ES 3.0.4 p194 - When the internal format of the rendering surface is
1492 : // RGB10_A2, a third combination of format RGBA and type UNSIGNED_INT_2_10_10_10_REV
1493 : // is accepted.
1494 :
1495 0 : if (webgl->IsWebGL2() &&
1496 0 : srcUsage->format->effectiveFormat == webgl::EffectiveFormat::RGB10_A2 &&
1497 0 : pi.format == LOCAL_GL_RGBA &&
1498 0 : pi.type == LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV)
1499 : {
1500 0 : return true;
1501 : }
1502 :
1503 : ////
1504 :
1505 0 : MOZ_ASSERT(gl->IsCurrent());
1506 0 : const auto implPI = webgl->ValidImplementationColorReadPI(srcUsage);
1507 0 : if (pi == implPI)
1508 0 : return true;
1509 :
1510 : ////
1511 :
1512 0 : webgl->ErrorInvalidOperation("%s: Incompatible format or type.", funcName);
1513 0 : return false;
1514 : }
1515 :
1516 : void
1517 0 : WebGLContext::ReadPixelsImpl(GLint x, GLint y, GLsizei rawWidth, GLsizei rawHeight,
1518 : GLenum packFormat, GLenum packType, void* dest,
1519 : uint32_t dataLen)
1520 : {
1521 0 : if (rawWidth < 0 || rawHeight < 0) {
1522 0 : ErrorInvalidValue("readPixels: negative size passed");
1523 0 : return;
1524 : }
1525 :
1526 0 : const uint32_t width(rawWidth);
1527 0 : const uint32_t height(rawHeight);
1528 :
1529 : //////
1530 :
1531 0 : MakeContextCurrent();
1532 :
1533 : const webgl::FormatUsageInfo* srcFormat;
1534 : uint32_t srcWidth;
1535 : uint32_t srcHeight;
1536 0 : if (!ValidateCurFBForRead("readPixels", &srcFormat, &srcWidth, &srcHeight))
1537 0 : return;
1538 :
1539 : //////
1540 :
1541 0 : const webgl::PackingInfo pi = {packFormat, packType};
1542 0 : if (!ValidateReadPixelsFormatAndType(srcFormat, pi, gl, this))
1543 0 : return;
1544 :
1545 : uint8_t bytesPerPixel;
1546 0 : if (!webgl::GetBytesPerPixel(pi, &bytesPerPixel)) {
1547 0 : ErrorInvalidOperation("readPixels: Unsupported format and type.");
1548 0 : return;
1549 : }
1550 :
1551 : //////
1552 :
1553 : uint32_t rowStride;
1554 : uint32_t bytesNeeded;
1555 0 : if (!ValidatePackSize("readPixels", width, height, bytesPerPixel, &rowStride,
1556 : &bytesNeeded))
1557 : {
1558 0 : return;
1559 : }
1560 :
1561 0 : if (bytesNeeded > dataLen) {
1562 0 : ErrorInvalidOperation("readPixels: buffer too small");
1563 0 : return;
1564 : }
1565 :
1566 : ////
1567 :
1568 : int32_t readX, readY;
1569 : int32_t writeX, writeY;
1570 : int32_t rwWidth, rwHeight;
1571 0 : if (!Intersect(srcWidth, x, width, &readX, &writeX, &rwWidth) ||
1572 0 : !Intersect(srcHeight, y, height, &readY, &writeY, &rwHeight))
1573 : {
1574 0 : ErrorOutOfMemory("readPixels: Bad subrect selection.");
1575 0 : return;
1576 : }
1577 :
1578 : ////////////////
1579 : // Now that the errors are out of the way, on to actually reading!
1580 :
1581 0 : OnBeforeReadCall();
1582 :
1583 0 : if (!rwWidth || !rwHeight) {
1584 : // Disjoint rects, so we're done already.
1585 0 : DummyReadFramebufferOperation("readPixels");
1586 0 : return;
1587 : }
1588 :
1589 0 : if (uint32_t(rwWidth) == width &&
1590 0 : uint32_t(rwHeight) == height)
1591 : {
1592 0 : DoReadPixelsAndConvert(srcFormat->format, x, y, width, height, packFormat,
1593 0 : packType, dest, dataLen, rowStride);
1594 0 : return;
1595 : }
1596 :
1597 : // Read request contains out-of-bounds pixels. Unfortunately:
1598 : // GLES 3.0.4 p194 "Obtaining Pixels from the Framebuffer":
1599 : // "If any of these pixels lies outside of the window allocated to the current GL
1600 : // context, or outside of the image attached to the currently bound framebuffer
1601 : // object, then the values obtained for those pixels are undefined."
1602 :
1603 : // This is a slow-path, so warn people away!
1604 : GenerateWarning("readPixels: Out-of-bounds reads with readPixels are deprecated, and"
1605 0 : " may be slow.");
1606 :
1607 : ////////////////////////////////////
1608 : // Read only the in-bounds pixels.
1609 :
1610 0 : if (IsWebGL2()) {
1611 0 : if (!mPixelStore_PackRowLength) {
1612 0 : gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH,
1613 0 : mPixelStore_PackSkipPixels + width);
1614 : }
1615 0 : gl->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, mPixelStore_PackSkipPixels + writeX);
1616 0 : gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, mPixelStore_PackSkipRows + writeY);
1617 :
1618 0 : DoReadPixelsAndConvert(srcFormat->format, readX, readY, rwWidth, rwHeight,
1619 0 : packFormat, packType, dest, dataLen, rowStride);
1620 :
1621 0 : gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, mPixelStore_PackRowLength);
1622 0 : gl->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, mPixelStore_PackSkipPixels);
1623 0 : gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, mPixelStore_PackSkipRows);
1624 : } else {
1625 : // I *did* say "hilariously slow".
1626 :
1627 0 : uint8_t* row = (uint8_t*)dest + writeX * bytesPerPixel;
1628 0 : row += writeY * rowStride;
1629 0 : for (uint32_t j = 0; j < uint32_t(rwHeight); j++) {
1630 0 : DoReadPixelsAndConvert(srcFormat->format, readX, readY+j, rwWidth, 1,
1631 0 : packFormat, packType, row, dataLen, rowStride);
1632 0 : row += rowStride;
1633 : }
1634 : }
1635 : }
1636 :
1637 : void
1638 0 : WebGLContext::RenderbufferStorage_base(const char* funcName, GLenum target,
1639 : GLsizei samples, GLenum internalFormat,
1640 : GLsizei width, GLsizei height)
1641 : {
1642 0 : if (IsContextLost())
1643 0 : return;
1644 :
1645 0 : if (target != LOCAL_GL_RENDERBUFFER) {
1646 0 : ErrorInvalidEnumInfo("`target`", funcName, target);
1647 0 : return;
1648 : }
1649 :
1650 0 : if (!mBoundRenderbuffer) {
1651 0 : ErrorInvalidOperation("%s: Called on renderbuffer 0.", funcName);
1652 0 : return;
1653 : }
1654 :
1655 0 : if (samples < 0) {
1656 0 : ErrorInvalidValue("%s: `samples` must be >= 0.", funcName);
1657 0 : return;
1658 : }
1659 :
1660 0 : if (width < 0 || height < 0) {
1661 0 : ErrorInvalidValue("%s: `width` and `height` must be >= 0.", funcName);
1662 0 : return;
1663 : }
1664 :
1665 0 : mBoundRenderbuffer->RenderbufferStorage(funcName, uint32_t(samples), internalFormat,
1666 0 : uint32_t(width), uint32_t(height));
1667 : }
1668 :
1669 : void
1670 0 : WebGLContext::RenderbufferStorage(GLenum target, GLenum internalFormat, GLsizei width, GLsizei height)
1671 : {
1672 : RenderbufferStorage_base("renderbufferStorage", target, 0, internalFormat, width,
1673 0 : height);
1674 0 : }
1675 :
1676 : void
1677 0 : WebGLContext::Scissor(GLint x, GLint y, GLsizei width, GLsizei height)
1678 : {
1679 0 : if (IsContextLost())
1680 0 : return;
1681 :
1682 0 : if (width < 0 || height < 0)
1683 0 : return ErrorInvalidValue("scissor: negative size");
1684 :
1685 0 : MakeContextCurrent();
1686 0 : gl->fScissor(x, y, width, height);
1687 : }
1688 :
1689 : void
1690 0 : WebGLContext::StencilFunc(GLenum func, GLint ref, GLuint mask)
1691 : {
1692 0 : if (IsContextLost())
1693 0 : return;
1694 :
1695 0 : if (!ValidateComparisonEnum(func, "stencilFunc: func"))
1696 0 : return;
1697 :
1698 0 : mStencilRefFront = ref;
1699 0 : mStencilRefBack = ref;
1700 0 : mStencilValueMaskFront = mask;
1701 0 : mStencilValueMaskBack = mask;
1702 :
1703 0 : MakeContextCurrent();
1704 0 : gl->fStencilFunc(func, ref, mask);
1705 : }
1706 :
1707 : void
1708 0 : WebGLContext::StencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask)
1709 : {
1710 0 : if (IsContextLost())
1711 0 : return;
1712 :
1713 0 : if (!ValidateFaceEnum(face, "stencilFuncSeparate: face") ||
1714 0 : !ValidateComparisonEnum(func, "stencilFuncSeparate: func"))
1715 0 : return;
1716 :
1717 0 : switch (face) {
1718 : case LOCAL_GL_FRONT_AND_BACK:
1719 0 : mStencilRefFront = ref;
1720 0 : mStencilRefBack = ref;
1721 0 : mStencilValueMaskFront = mask;
1722 0 : mStencilValueMaskBack = mask;
1723 0 : break;
1724 : case LOCAL_GL_FRONT:
1725 0 : mStencilRefFront = ref;
1726 0 : mStencilValueMaskFront = mask;
1727 0 : break;
1728 : case LOCAL_GL_BACK:
1729 0 : mStencilRefBack = ref;
1730 0 : mStencilValueMaskBack = mask;
1731 0 : break;
1732 : }
1733 :
1734 0 : MakeContextCurrent();
1735 0 : gl->fStencilFuncSeparate(face, func, ref, mask);
1736 : }
1737 :
1738 : void
1739 0 : WebGLContext::StencilOp(GLenum sfail, GLenum dpfail, GLenum dppass)
1740 : {
1741 0 : if (IsContextLost())
1742 0 : return;
1743 :
1744 0 : if (!ValidateStencilOpEnum(sfail, "stencilOp: sfail") ||
1745 0 : !ValidateStencilOpEnum(dpfail, "stencilOp: dpfail") ||
1746 0 : !ValidateStencilOpEnum(dppass, "stencilOp: dppass"))
1747 0 : return;
1748 :
1749 0 : MakeContextCurrent();
1750 0 : gl->fStencilOp(sfail, dpfail, dppass);
1751 : }
1752 :
1753 : void
1754 0 : WebGLContext::StencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass)
1755 : {
1756 0 : if (IsContextLost())
1757 0 : return;
1758 :
1759 0 : if (!ValidateFaceEnum(face, "stencilOpSeparate: face") ||
1760 0 : !ValidateStencilOpEnum(sfail, "stencilOpSeparate: sfail") ||
1761 0 : !ValidateStencilOpEnum(dpfail, "stencilOpSeparate: dpfail") ||
1762 0 : !ValidateStencilOpEnum(dppass, "stencilOpSeparate: dppass"))
1763 0 : return;
1764 :
1765 0 : MakeContextCurrent();
1766 0 : gl->fStencilOpSeparate(face, sfail, dpfail, dppass);
1767 : }
1768 :
1769 : ////////////////////////////////////////////////////////////////////////////////
1770 : // Uniform setters.
1771 :
1772 : class ValidateIfSampler
1773 : {
1774 : const WebGLUniformLocation* const mLoc;
1775 : const size_t mDataCount;
1776 : const GLint* const mData;
1777 : bool mIsValidatedSampler;
1778 :
1779 : public:
1780 0 : ValidateIfSampler(WebGLContext* webgl, const char* funcName,
1781 : WebGLUniformLocation* loc, size_t dataCount, const GLint* data,
1782 : bool* const out_error)
1783 0 : : mLoc(loc)
1784 : , mDataCount(dataCount)
1785 : , mData(data)
1786 0 : , mIsValidatedSampler(false)
1787 : {
1788 0 : if (!mLoc->mInfo->mSamplerTexList) {
1789 0 : *out_error = false;
1790 0 : return;
1791 : }
1792 :
1793 0 : for (size_t i = 0; i < mDataCount; i++) {
1794 0 : const auto& val = mData[i];
1795 0 : if (val < 0 || uint32_t(val) >= webgl->GLMaxTextureUnits()) {
1796 0 : webgl->ErrorInvalidValue("%s: This uniform location is a sampler, but %d"
1797 : " is not a valid texture unit.",
1798 0 : funcName, val);
1799 0 : *out_error = true;
1800 0 : return;
1801 : }
1802 : }
1803 :
1804 0 : mIsValidatedSampler = true;
1805 0 : *out_error = false;
1806 : }
1807 :
1808 0 : ~ValidateIfSampler() {
1809 0 : if (!mIsValidatedSampler)
1810 0 : return;
1811 :
1812 0 : auto& samplerValues = mLoc->mInfo->mSamplerValues;
1813 :
1814 0 : for (size_t i = 0; i < mDataCount; i++) {
1815 0 : const size_t curIndex = mLoc->mArrayIndex + i;
1816 0 : if (curIndex >= samplerValues.size())
1817 0 : break;
1818 :
1819 0 : samplerValues[curIndex] = mData[i];
1820 : }
1821 0 : }
1822 : };
1823 :
1824 : ////////////////////
1825 :
1826 : void
1827 0 : WebGLContext::Uniform1i(WebGLUniformLocation* loc, GLint a1)
1828 : {
1829 0 : const char funcName[] = "uniform1i";
1830 0 : if (!ValidateUniformSetter(loc, 1, LOCAL_GL_INT, funcName))
1831 0 : return;
1832 :
1833 : bool error;
1834 0 : const ValidateIfSampler validate(this, funcName, loc, 1, &a1, &error);
1835 0 : if (error)
1836 0 : return;
1837 :
1838 0 : MakeContextCurrent();
1839 0 : gl->fUniform1i(loc->mLoc, a1);
1840 : }
1841 :
1842 : void
1843 0 : WebGLContext::Uniform2i(WebGLUniformLocation* loc, GLint a1, GLint a2)
1844 : {
1845 0 : const char funcName[] = "uniform2i";
1846 0 : if (!ValidateUniformSetter(loc, 2, LOCAL_GL_INT, funcName))
1847 0 : return;
1848 :
1849 0 : MakeContextCurrent();
1850 0 : gl->fUniform2i(loc->mLoc, a1, a2);
1851 : }
1852 :
1853 : void
1854 0 : WebGLContext::Uniform3i(WebGLUniformLocation* loc, GLint a1, GLint a2, GLint a3)
1855 : {
1856 0 : const char funcName[] = "uniform3i";
1857 0 : if (!ValidateUniformSetter(loc, 3, LOCAL_GL_INT, funcName))
1858 0 : return;
1859 :
1860 0 : MakeContextCurrent();
1861 0 : gl->fUniform3i(loc->mLoc, a1, a2, a3);
1862 : }
1863 :
1864 : void
1865 0 : WebGLContext::Uniform4i(WebGLUniformLocation* loc, GLint a1, GLint a2, GLint a3,
1866 : GLint a4)
1867 : {
1868 0 : const char funcName[] = "uniform4i";
1869 0 : if (!ValidateUniformSetter(loc, 4, LOCAL_GL_INT, funcName))
1870 0 : return;
1871 :
1872 0 : MakeContextCurrent();
1873 0 : gl->fUniform4i(loc->mLoc, a1, a2, a3, a4);
1874 : }
1875 :
1876 : //////////
1877 :
1878 : void
1879 0 : WebGLContext::Uniform1f(WebGLUniformLocation* loc, GLfloat a1)
1880 : {
1881 0 : const char funcName[] = "uniform1f";
1882 0 : if (!ValidateUniformSetter(loc, 1, LOCAL_GL_FLOAT, funcName))
1883 0 : return;
1884 :
1885 0 : MakeContextCurrent();
1886 0 : gl->fUniform1f(loc->mLoc, a1);
1887 : }
1888 :
1889 : void
1890 0 : WebGLContext::Uniform2f(WebGLUniformLocation* loc, GLfloat a1, GLfloat a2)
1891 : {
1892 0 : const char funcName[] = "uniform2f";
1893 0 : if (!ValidateUniformSetter(loc, 2, LOCAL_GL_FLOAT, funcName))
1894 0 : return;
1895 :
1896 0 : MakeContextCurrent();
1897 0 : gl->fUniform2f(loc->mLoc, a1, a2);
1898 : }
1899 :
1900 : void
1901 0 : WebGLContext::Uniform3f(WebGLUniformLocation* loc, GLfloat a1, GLfloat a2,
1902 : GLfloat a3)
1903 : {
1904 0 : const char funcName[] = "uniform3f";
1905 0 : if (!ValidateUniformSetter(loc, 3, LOCAL_GL_FLOAT, funcName))
1906 0 : return;
1907 :
1908 0 : MakeContextCurrent();
1909 0 : gl->fUniform3f(loc->mLoc, a1, a2, a3);
1910 : }
1911 :
1912 : void
1913 0 : WebGLContext::Uniform4f(WebGLUniformLocation* loc, GLfloat a1, GLfloat a2,
1914 : GLfloat a3, GLfloat a4)
1915 : {
1916 0 : const char funcName[] = "uniform4f";
1917 0 : if (!ValidateUniformSetter(loc, 4, LOCAL_GL_FLOAT, funcName))
1918 0 : return;
1919 :
1920 0 : MakeContextCurrent();
1921 0 : gl->fUniform4f(loc->mLoc, a1, a2, a3, a4);
1922 : }
1923 :
1924 : ////////////////////////////////////////
1925 : // Array
1926 :
1927 : static bool
1928 0 : ValidateArrOffsetAndCount(WebGLContext* webgl, const char* funcName, size_t elemsAvail,
1929 : GLuint elemOffset, GLuint elemCountOverride,
1930 : size_t* const out_elemCount)
1931 : {
1932 0 : if (elemOffset > elemsAvail) {
1933 0 : webgl->ErrorInvalidValue("%s: Bad offset into list.", funcName);
1934 0 : return false;
1935 : }
1936 0 : elemsAvail -= elemOffset;
1937 :
1938 0 : if (elemCountOverride) {
1939 0 : if (elemCountOverride > elemsAvail) {
1940 0 : webgl->ErrorInvalidValue("%s: Bad count override for sub-list.", funcName);
1941 0 : return false;
1942 : }
1943 0 : elemsAvail = elemCountOverride;
1944 : }
1945 :
1946 0 : *out_elemCount = elemsAvail;
1947 0 : return true;
1948 : }
1949 :
1950 : void
1951 0 : WebGLContext::UniformNiv(const char* funcName, uint8_t N, WebGLUniformLocation* loc,
1952 : const Int32Arr& arr, GLuint elemOffset, GLuint elemCountOverride)
1953 : {
1954 : size_t elemCount;
1955 0 : if (!ValidateArrOffsetAndCount(this, funcName, arr.elemCount, elemOffset,
1956 : elemCountOverride, &elemCount))
1957 : {
1958 0 : return;
1959 : }
1960 0 : const auto elemBytes = arr.elemBytes + elemOffset;
1961 :
1962 : uint32_t numElementsToUpload;
1963 0 : if (!ValidateUniformArraySetter(loc, N, LOCAL_GL_INT, elemCount, funcName,
1964 : &numElementsToUpload))
1965 : {
1966 0 : return;
1967 : }
1968 :
1969 : bool error;
1970 : const ValidateIfSampler samplerValidator(this, funcName, loc, numElementsToUpload,
1971 0 : elemBytes, &error);
1972 0 : if (error)
1973 0 : return;
1974 :
1975 : static const decltype(&gl::GLContext::fUniform1iv) kFuncList[] = {
1976 : &gl::GLContext::fUniform1iv,
1977 : &gl::GLContext::fUniform2iv,
1978 : &gl::GLContext::fUniform3iv,
1979 : &gl::GLContext::fUniform4iv
1980 : };
1981 0 : const auto func = kFuncList[N-1];
1982 :
1983 0 : MakeContextCurrent();
1984 0 : (gl->*func)(loc->mLoc, numElementsToUpload, elemBytes);
1985 : }
1986 :
1987 : void
1988 0 : WebGLContext::UniformNuiv(const char* funcName, uint8_t N, WebGLUniformLocation* loc,
1989 : const Uint32Arr& arr, GLuint elemOffset,
1990 : GLuint elemCountOverride)
1991 : {
1992 : size_t elemCount;
1993 0 : if (!ValidateArrOffsetAndCount(this, funcName, arr.elemCount, elemOffset,
1994 : elemCountOverride, &elemCount))
1995 : {
1996 0 : return;
1997 : }
1998 0 : const auto elemBytes = arr.elemBytes + elemOffset;
1999 :
2000 : uint32_t numElementsToUpload;
2001 0 : if (!ValidateUniformArraySetter(loc, N, LOCAL_GL_UNSIGNED_INT, elemCount, funcName,
2002 : &numElementsToUpload))
2003 : {
2004 0 : return;
2005 : }
2006 0 : MOZ_ASSERT(!loc->mInfo->mSamplerTexList, "Should not be a sampler.");
2007 :
2008 : static const decltype(&gl::GLContext::fUniform1uiv) kFuncList[] = {
2009 : &gl::GLContext::fUniform1uiv,
2010 : &gl::GLContext::fUniform2uiv,
2011 : &gl::GLContext::fUniform3uiv,
2012 : &gl::GLContext::fUniform4uiv
2013 : };
2014 0 : const auto func = kFuncList[N-1];
2015 :
2016 0 : MakeContextCurrent();
2017 0 : (gl->*func)(loc->mLoc, numElementsToUpload, elemBytes);
2018 : }
2019 :
2020 : void
2021 0 : WebGLContext::UniformNfv(const char* funcName, uint8_t N, WebGLUniformLocation* loc,
2022 : const Float32Arr& arr, GLuint elemOffset,
2023 : GLuint elemCountOverride)
2024 : {
2025 : size_t elemCount;
2026 0 : if (!ValidateArrOffsetAndCount(this, funcName, arr.elemCount, elemOffset,
2027 : elemCountOverride, &elemCount))
2028 : {
2029 0 : return;
2030 : }
2031 0 : const auto elemBytes = arr.elemBytes + elemOffset;
2032 :
2033 : uint32_t numElementsToUpload;
2034 0 : if (!ValidateUniformArraySetter(loc, N, LOCAL_GL_FLOAT, elemCount, funcName,
2035 : &numElementsToUpload))
2036 : {
2037 0 : return;
2038 : }
2039 0 : MOZ_ASSERT(!loc->mInfo->mSamplerTexList, "Should not be a sampler.");
2040 :
2041 : static const decltype(&gl::GLContext::fUniform1fv) kFuncList[] = {
2042 : &gl::GLContext::fUniform1fv,
2043 : &gl::GLContext::fUniform2fv,
2044 : &gl::GLContext::fUniform3fv,
2045 : &gl::GLContext::fUniform4fv
2046 : };
2047 0 : const auto func = kFuncList[N-1];
2048 :
2049 0 : MakeContextCurrent();
2050 0 : (gl->*func)(loc->mLoc, numElementsToUpload, elemBytes);
2051 : }
2052 :
2053 : static inline void
2054 0 : MatrixAxBToRowMajor(const uint8_t width, const uint8_t height,
2055 : const float* __restrict srcColMajor,
2056 : float* __restrict dstRowMajor)
2057 : {
2058 0 : for (uint8_t x = 0; x < width; ++x) {
2059 0 : for (uint8_t y = 0; y < height; ++y) {
2060 0 : dstRowMajor[y * width + x] = srcColMajor[x * height + y];
2061 : }
2062 : }
2063 0 : }
2064 :
2065 : void
2066 0 : WebGLContext::UniformMatrixAxBfv(const char* funcName, uint8_t A, uint8_t B,
2067 : WebGLUniformLocation* loc, const bool transpose,
2068 : const Float32Arr& arr, GLuint elemOffset,
2069 : GLuint elemCountOverride)
2070 : {
2071 : size_t elemCount;
2072 0 : if (!ValidateArrOffsetAndCount(this, funcName, arr.elemCount, elemOffset,
2073 : elemCountOverride, &elemCount))
2074 : {
2075 0 : return;
2076 : }
2077 0 : const auto elemBytes = arr.elemBytes + elemOffset;
2078 :
2079 : uint32_t numMatsToUpload;
2080 0 : if (!ValidateUniformMatrixArraySetter(loc, A, B, LOCAL_GL_FLOAT, elemCount,
2081 : transpose, funcName, &numMatsToUpload))
2082 : {
2083 0 : return;
2084 : }
2085 0 : MOZ_ASSERT(!loc->mInfo->mSamplerTexList, "Should not be a sampler.");
2086 :
2087 : ////
2088 :
2089 0 : bool uploadTranspose = transpose;
2090 0 : const float* uploadBytes = elemBytes;
2091 :
2092 0 : UniqueBuffer temp;
2093 0 : if (!transpose && gl->WorkAroundDriverBugs() && gl->IsANGLE() &&
2094 0 : gl->IsAtLeast(gl::ContextProfile::OpenGLES, 300))
2095 : {
2096 : // ANGLE is really slow at non-GL-transposed matrices.
2097 0 : const size_t kElemsPerMat = A * B;
2098 :
2099 0 : temp = malloc(numMatsToUpload * kElemsPerMat * sizeof(float));
2100 0 : if (!temp) {
2101 : ErrorOutOfMemory("%s: Failed to alloc temporary buffer for transposition.",
2102 0 : funcName);
2103 0 : return;
2104 : }
2105 :
2106 0 : auto srcItr = (const float*)elemBytes;
2107 0 : auto dstItr = (float*)temp.get();
2108 0 : const auto srcEnd = srcItr + numMatsToUpload * kElemsPerMat;
2109 :
2110 0 : while (srcItr != srcEnd) {
2111 0 : MatrixAxBToRowMajor(A, B, srcItr, dstItr);
2112 0 : srcItr += kElemsPerMat;
2113 0 : dstItr += kElemsPerMat;
2114 : }
2115 :
2116 0 : uploadBytes = (const float*)temp.get();
2117 0 : uploadTranspose = true;
2118 : }
2119 :
2120 : ////
2121 :
2122 : static const decltype(&gl::GLContext::fUniformMatrix2fv) kFuncList[] = {
2123 : &gl::GLContext::fUniformMatrix2fv,
2124 : &gl::GLContext::fUniformMatrix2x3fv,
2125 : &gl::GLContext::fUniformMatrix2x4fv,
2126 :
2127 : &gl::GLContext::fUniformMatrix3x2fv,
2128 : &gl::GLContext::fUniformMatrix3fv,
2129 : &gl::GLContext::fUniformMatrix3x4fv,
2130 :
2131 : &gl::GLContext::fUniformMatrix4x2fv,
2132 : &gl::GLContext::fUniformMatrix4x3fv,
2133 : &gl::GLContext::fUniformMatrix4fv
2134 : };
2135 0 : const auto func = kFuncList[3*(A-2) + (B-2)];
2136 :
2137 0 : MakeContextCurrent();
2138 0 : (gl->*func)(loc->mLoc, numMatsToUpload, uploadTranspose, uploadBytes);
2139 : }
2140 :
2141 : ////////////////////////////////////////////////////////////////////////////////
2142 :
2143 : void
2144 0 : WebGLContext::UseProgram(WebGLProgram* prog)
2145 : {
2146 0 : if (IsContextLost())
2147 0 : return;
2148 :
2149 0 : if (!prog) {
2150 0 : mCurrentProgram = nullptr;
2151 0 : mActiveProgramLinkInfo = nullptr;
2152 0 : return;
2153 : }
2154 :
2155 0 : if (!ValidateObject("useProgram", *prog))
2156 0 : return;
2157 :
2158 0 : if (prog->UseProgram()) {
2159 0 : mCurrentProgram = prog;
2160 0 : mActiveProgramLinkInfo = mCurrentProgram->LinkInfo();
2161 : }
2162 : }
2163 :
2164 : void
2165 0 : WebGLContext::ValidateProgram(const WebGLProgram& prog)
2166 : {
2167 0 : if (IsContextLost())
2168 0 : return;
2169 :
2170 0 : if (!ValidateObject("validateProgram", prog))
2171 0 : return;
2172 :
2173 0 : prog.ValidateProgram();
2174 : }
2175 :
2176 : already_AddRefed<WebGLFramebuffer>
2177 0 : WebGLContext::CreateFramebuffer()
2178 : {
2179 0 : if (IsContextLost())
2180 0 : return nullptr;
2181 :
2182 0 : GLuint fbo = 0;
2183 0 : MakeContextCurrent();
2184 0 : gl->fGenFramebuffers(1, &fbo);
2185 :
2186 0 : RefPtr<WebGLFramebuffer> globj = new WebGLFramebuffer(this, fbo);
2187 0 : return globj.forget();
2188 : }
2189 :
2190 : already_AddRefed<WebGLRenderbuffer>
2191 0 : WebGLContext::CreateRenderbuffer()
2192 : {
2193 0 : if (IsContextLost())
2194 0 : return nullptr;
2195 :
2196 0 : MakeContextCurrent();
2197 0 : RefPtr<WebGLRenderbuffer> globj = new WebGLRenderbuffer(this);
2198 0 : return globj.forget();
2199 : }
2200 :
2201 : void
2202 0 : WebGLContext::Viewport(GLint x, GLint y, GLsizei width, GLsizei height)
2203 : {
2204 0 : if (IsContextLost())
2205 0 : return;
2206 :
2207 0 : if (width < 0 || height < 0)
2208 0 : return ErrorInvalidValue("viewport: negative size");
2209 :
2210 0 : width = std::min(width, (GLsizei)mImplMaxViewportDims[0]);
2211 0 : height = std::min(height, (GLsizei)mImplMaxViewportDims[1]);
2212 :
2213 0 : MakeContextCurrent();
2214 0 : gl->fViewport(x, y, width, height);
2215 :
2216 0 : mViewportX = x;
2217 0 : mViewportY = y;
2218 0 : mViewportWidth = width;
2219 0 : mViewportHeight = height;
2220 : }
2221 :
2222 : void
2223 0 : WebGLContext::CompileShader(WebGLShader& shader)
2224 : {
2225 0 : if (IsContextLost())
2226 0 : return;
2227 :
2228 0 : if (!ValidateObject("compileShader", shader))
2229 0 : return;
2230 :
2231 0 : shader.CompileShader();
2232 : }
2233 :
2234 : JS::Value
2235 0 : WebGLContext::GetShaderParameter(const WebGLShader& shader, GLenum pname)
2236 : {
2237 0 : if (IsContextLost())
2238 0 : return JS::NullValue();
2239 :
2240 0 : if (!ValidateObjectAllowDeleted("getShaderParameter: shader", shader))
2241 0 : return JS::NullValue();
2242 :
2243 0 : return shader.GetShaderParameter(pname);
2244 : }
2245 :
2246 : void
2247 0 : WebGLContext::GetShaderInfoLog(const WebGLShader& shader, nsAString& retval)
2248 : {
2249 0 : retval.SetIsVoid(true);
2250 :
2251 0 : if (IsContextLost())
2252 0 : return;
2253 :
2254 0 : if (!ValidateObject("getShaderInfoLog: shader", shader))
2255 0 : return;
2256 :
2257 0 : shader.GetShaderInfoLog(&retval);
2258 : }
2259 :
2260 : already_AddRefed<WebGLShaderPrecisionFormat>
2261 0 : WebGLContext::GetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype)
2262 : {
2263 0 : if (IsContextLost())
2264 0 : return nullptr;
2265 :
2266 0 : switch (shadertype) {
2267 : case LOCAL_GL_FRAGMENT_SHADER:
2268 : case LOCAL_GL_VERTEX_SHADER:
2269 0 : break;
2270 : default:
2271 0 : ErrorInvalidEnumInfo("getShaderPrecisionFormat: shadertype", shadertype);
2272 0 : return nullptr;
2273 : }
2274 :
2275 0 : switch (precisiontype) {
2276 : case LOCAL_GL_LOW_FLOAT:
2277 : case LOCAL_GL_MEDIUM_FLOAT:
2278 : case LOCAL_GL_HIGH_FLOAT:
2279 : case LOCAL_GL_LOW_INT:
2280 : case LOCAL_GL_MEDIUM_INT:
2281 : case LOCAL_GL_HIGH_INT:
2282 0 : break;
2283 : default:
2284 0 : ErrorInvalidEnumInfo("getShaderPrecisionFormat: precisiontype", precisiontype);
2285 0 : return nullptr;
2286 : }
2287 :
2288 0 : MakeContextCurrent();
2289 : GLint range[2], precision;
2290 :
2291 0 : if (mDisableFragHighP &&
2292 0 : shadertype == LOCAL_GL_FRAGMENT_SHADER &&
2293 0 : (precisiontype == LOCAL_GL_HIGH_FLOAT ||
2294 : precisiontype == LOCAL_GL_HIGH_INT))
2295 : {
2296 0 : precision = 0;
2297 0 : range[0] = 0;
2298 0 : range[1] = 0;
2299 : } else {
2300 0 : gl->fGetShaderPrecisionFormat(shadertype, precisiontype, range, &precision);
2301 : }
2302 :
2303 : RefPtr<WebGLShaderPrecisionFormat> retShaderPrecisionFormat
2304 0 : = new WebGLShaderPrecisionFormat(this, range[0], range[1], precision);
2305 0 : return retShaderPrecisionFormat.forget();
2306 : }
2307 :
2308 : void
2309 0 : WebGLContext::GetShaderSource(const WebGLShader& shader, nsAString& retval)
2310 : {
2311 0 : retval.SetIsVoid(true);
2312 :
2313 0 : if (IsContextLost())
2314 0 : return;
2315 :
2316 0 : if (!ValidateObject("getShaderSource: shader", shader))
2317 0 : return;
2318 :
2319 0 : shader.GetShaderSource(&retval);
2320 : }
2321 :
2322 : void
2323 0 : WebGLContext::ShaderSource(WebGLShader& shader, const nsAString& source)
2324 : {
2325 0 : if (IsContextLost())
2326 0 : return;
2327 :
2328 0 : if (!ValidateObject("shaderSource: shader", shader))
2329 0 : return;
2330 :
2331 0 : shader.ShaderSource(source);
2332 : }
2333 :
2334 : void
2335 0 : WebGLContext::LoseContext()
2336 : {
2337 0 : if (IsContextLost())
2338 0 : return ErrorInvalidOperation("loseContext: Context is already lost.");
2339 :
2340 0 : ForceLoseContext(true);
2341 : }
2342 :
2343 : void
2344 0 : WebGLContext::RestoreContext()
2345 : {
2346 0 : if (!IsContextLost())
2347 0 : return ErrorInvalidOperation("restoreContext: Context is not lost.");
2348 :
2349 0 : if (!mLastLossWasSimulated) {
2350 : return ErrorInvalidOperation("restoreContext: Context loss was not simulated."
2351 0 : " Cannot simulate restore.");
2352 : }
2353 : // If we're currently lost, and the last loss was simulated, then
2354 : // we're currently only simulated-lost, allowing us to call
2355 : // restoreContext().
2356 :
2357 0 : if (!mAllowContextRestore)
2358 0 : return ErrorInvalidOperation("restoreContext: Context cannot be restored.");
2359 :
2360 0 : ForceRestoreContext();
2361 : }
2362 :
2363 : void
2364 0 : WebGLContext::BlendColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) {
2365 0 : if (IsContextLost())
2366 0 : return;
2367 0 : MakeContextCurrent();
2368 0 : gl->fBlendColor(r, g, b, a);
2369 : }
2370 :
2371 : void
2372 0 : WebGLContext::Flush() {
2373 0 : if (IsContextLost())
2374 0 : return;
2375 0 : MakeContextCurrent();
2376 0 : gl->fFlush();
2377 : }
2378 :
2379 : void
2380 0 : WebGLContext::Finish() {
2381 0 : if (IsContextLost())
2382 0 : return;
2383 0 : MakeContextCurrent();
2384 0 : gl->fFinish();
2385 : }
2386 :
2387 : void
2388 0 : WebGLContext::LineWidth(GLfloat width)
2389 : {
2390 0 : if (IsContextLost())
2391 0 : return;
2392 :
2393 : // Doing it this way instead of `if (width <= 0.0)` handles NaNs.
2394 0 : const bool isValid = width > 0.0;
2395 0 : if (!isValid) {
2396 0 : ErrorInvalidValue("lineWidth: `width` must be positive and non-zero.");
2397 0 : return;
2398 : }
2399 :
2400 0 : mLineWidth = width;
2401 :
2402 0 : if (gl->IsCoreProfile() && width > 1.0) {
2403 0 : width = 1.0;
2404 : }
2405 :
2406 0 : MakeContextCurrent();
2407 0 : gl->fLineWidth(width);
2408 : }
2409 :
2410 : void
2411 0 : WebGLContext::PolygonOffset(GLfloat factor, GLfloat units) {
2412 0 : if (IsContextLost())
2413 0 : return;
2414 0 : MakeContextCurrent();
2415 0 : gl->fPolygonOffset(factor, units);
2416 : }
2417 :
2418 : void
2419 0 : WebGLContext::SampleCoverage(GLclampf value, WebGLboolean invert) {
2420 0 : if (IsContextLost())
2421 0 : return;
2422 0 : MakeContextCurrent();
2423 0 : gl->fSampleCoverage(value, invert);
2424 : }
2425 :
2426 : } // namespace mozilla
|