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