1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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/. */
6 #include "WebGLContext.h"
7 #include "WebGL2Context.h"
9 #include "WebGLContextUtils.h"
10 #include "WebGLBuffer.h"
11 #include "WebGLShader.h"
12 #include "WebGLProgram.h"
13 #include "WebGLFormats.h"
14 #include "WebGLFramebuffer.h"
15 #include "WebGLQuery.h"
16 #include "WebGLRenderbuffer.h"
17 #include "WebGLTexture.h"
18 #include "WebGLExtensions.h"
19 #include "WebGLVertexArray.h"
22 #include "nsReadableUtils.h"
25 #include "gfxContext.h"
26 #include "gfxPlatform.h"
27 #include "GLContext.h"
29 #include "nsContentUtils.h"
31 #include "nsLayoutUtils.h"
33 #include "CanvasUtils.h"
35 #include "MozFramebuffer.h"
37 #include "jsfriendapi.h"
39 #include "WebGLTexelConversions.h"
40 #include "WebGLValidateStrings.h"
43 // needed to check if current OS is lower than 10.7
44 #if defined(MOZ_WIDGET_COCOA)
45 # include "nsCocoaFeatures.h"
48 #include "mozilla/DebugOnly.h"
49 #include "mozilla/dom/BindingUtils.h"
50 #include "mozilla/dom/ImageData.h"
51 #include "mozilla/dom/WebGLRenderingContextBinding.h"
52 #include "mozilla/EndianUtils.h"
53 #include "mozilla/RefPtr.h"
54 #include "mozilla/UniquePtrExtensions.h"
55 #include "mozilla/StaticPrefs_webgl.h"
59 using namespace mozilla::dom
;
60 using namespace mozilla::gfx
;
61 using namespace mozilla::gl
;
67 void WebGLContext::ActiveTexture(uint32_t texUnit
) {
68 FuncScope
funcScope(*this, "activeTexture");
69 if (IsContextLost()) return;
70 funcScope
.mBindFailureGuard
= true;
72 if (texUnit
>= Limits().maxTexUnits
) {
73 return ErrorInvalidEnum("Texture unit %u out of range (%u).", texUnit
,
74 Limits().maxTexUnits
);
77 mActiveTexture
= texUnit
;
78 gl
->fActiveTexture(LOCAL_GL_TEXTURE0
+ texUnit
);
80 funcScope
.mBindFailureGuard
= false;
83 void WebGLContext::AttachShader(WebGLProgram
& prog
, WebGLShader
& shader
) {
84 FuncScope
funcScope(*this, "attachShader");
85 if (IsContextLost()) return;
86 funcScope
.mBindFailureGuard
= true;
88 prog
.AttachShader(shader
);
90 funcScope
.mBindFailureGuard
= false;
93 void WebGLContext::BindAttribLocation(WebGLProgram
& prog
, GLuint location
,
94 const std::string
& name
) const {
95 const FuncScope
funcScope(*this, "bindAttribLocation");
96 if (IsContextLost()) return;
98 prog
.BindAttribLocation(location
, name
);
101 void WebGLContext::BindFramebuffer(GLenum target
, WebGLFramebuffer
* wfb
) {
102 FuncScope
funcScope(*this, "bindFramebuffer");
103 if (IsContextLost()) return;
104 funcScope
.mBindFailureGuard
= true;
106 if (!ValidateFramebufferTarget(target
)) return;
109 gl
->fBindFramebuffer(target
, 0);
111 GLuint framebuffername
= wfb
->mGLName
;
112 gl
->fBindFramebuffer(target
, framebuffername
);
113 wfb
->mHasBeenBound
= true;
117 case LOCAL_GL_FRAMEBUFFER
:
118 mBoundDrawFramebuffer
= wfb
;
119 mBoundReadFramebuffer
= wfb
;
121 case LOCAL_GL_DRAW_FRAMEBUFFER
:
122 mBoundDrawFramebuffer
= wfb
;
124 case LOCAL_GL_READ_FRAMEBUFFER
:
125 mBoundReadFramebuffer
= wfb
;
130 funcScope
.mBindFailureGuard
= false;
133 void WebGLContext::BlendEquationSeparate(Maybe
<GLuint
> i
, GLenum modeRGB
,
135 const FuncScope
funcScope(*this, "blendEquationSeparate");
136 if (IsContextLost()) return;
138 if (!ValidateBlendEquationEnum(modeRGB
, "modeRGB") ||
139 !ValidateBlendEquationEnum(modeAlpha
, "modeAlpha")) {
145 IsExtensionEnabled(WebGLExtensionID::OES_draw_buffers_indexed
));
146 const auto limit
= MaxValidDrawBuffers();
148 ErrorInvalidValue("`index` (%u) must be < %s (%u)", *i
,
149 "MAX_DRAW_BUFFERS", limit
);
153 gl
->fBlendEquationSeparatei(*i
, modeRGB
, modeAlpha
);
155 gl
->fBlendEquationSeparate(modeRGB
, modeAlpha
);
159 static bool ValidateBlendFuncEnum(WebGLContext
* webgl
, GLenum factor
,
160 const char* varName
) {
164 case LOCAL_GL_SRC_COLOR
:
165 case LOCAL_GL_ONE_MINUS_SRC_COLOR
:
166 case LOCAL_GL_DST_COLOR
:
167 case LOCAL_GL_ONE_MINUS_DST_COLOR
:
168 case LOCAL_GL_SRC_ALPHA
:
169 case LOCAL_GL_ONE_MINUS_SRC_ALPHA
:
170 case LOCAL_GL_DST_ALPHA
:
171 case LOCAL_GL_ONE_MINUS_DST_ALPHA
:
172 case LOCAL_GL_CONSTANT_COLOR
:
173 case LOCAL_GL_ONE_MINUS_CONSTANT_COLOR
:
174 case LOCAL_GL_CONSTANT_ALPHA
:
175 case LOCAL_GL_ONE_MINUS_CONSTANT_ALPHA
:
176 case LOCAL_GL_SRC_ALPHA_SATURATE
:
180 webgl
->ErrorInvalidEnumInfo(varName
, factor
);
185 static bool ValidateBlendFuncEnums(WebGLContext
* webgl
, GLenum srcRGB
,
186 GLenum srcAlpha
, GLenum dstRGB
,
188 if (!webgl
->IsWebGL2()) {
189 if (dstRGB
== LOCAL_GL_SRC_ALPHA_SATURATE
||
190 dstAlpha
== LOCAL_GL_SRC_ALPHA_SATURATE
) {
191 webgl
->ErrorInvalidEnum(
192 "LOCAL_GL_SRC_ALPHA_SATURATE as a destination"
193 " blend function is disallowed in WebGL 1 (dstRGB ="
194 " 0x%04x, dstAlpha = 0x%04x).",
200 if (!ValidateBlendFuncEnum(webgl
, srcRGB
, "srcRGB") ||
201 !ValidateBlendFuncEnum(webgl
, srcAlpha
, "srcAlpha") ||
202 !ValidateBlendFuncEnum(webgl
, dstRGB
, "dstRGB") ||
203 !ValidateBlendFuncEnum(webgl
, dstAlpha
, "dstAlpha")) {
210 void WebGLContext::BlendFuncSeparate(Maybe
<GLuint
> i
, GLenum srcRGB
,
211 GLenum dstRGB
, GLenum srcAlpha
,
213 const FuncScope
funcScope(*this, "blendFuncSeparate");
214 if (IsContextLost()) return;
216 if (!ValidateBlendFuncEnums(this, srcRGB
, srcAlpha
, dstRGB
, dstAlpha
)) return;
218 // note that we only check compatibity for the RGB enums, no need to for the
219 // Alpha enums, see "Section 6.8 forgetting to mention alpha factors?" thread
220 // on the public_webgl mailing list
221 if (!ValidateBlendFuncEnumsCompatibility(srcRGB
, dstRGB
, "srcRGB and dstRGB"))
226 IsExtensionEnabled(WebGLExtensionID::OES_draw_buffers_indexed
));
227 const auto limit
= MaxValidDrawBuffers();
229 ErrorInvalidValue("`index` (%u) must be < %s (%u)", *i
,
230 "MAX_DRAW_BUFFERS", limit
);
234 gl
->fBlendFuncSeparatei(*i
, srcRGB
, dstRGB
, srcAlpha
, dstAlpha
);
236 gl
->fBlendFuncSeparate(srcRGB
, dstRGB
, srcAlpha
, dstAlpha
);
240 GLenum
WebGLContext::CheckFramebufferStatus(GLenum target
) {
241 const FuncScope
funcScope(*this, "checkFramebufferStatus");
242 if (IsContextLost()) return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED
;
244 if (!ValidateFramebufferTarget(target
)) return 0;
246 WebGLFramebuffer
* fb
;
248 case LOCAL_GL_FRAMEBUFFER
:
249 case LOCAL_GL_DRAW_FRAMEBUFFER
:
250 fb
= mBoundDrawFramebuffer
;
253 case LOCAL_GL_READ_FRAMEBUFFER
:
254 fb
= mBoundReadFramebuffer
;
258 MOZ_CRASH("GFX: Bad target.");
261 if (!fb
) return LOCAL_GL_FRAMEBUFFER_COMPLETE
;
263 return fb
->CheckFramebufferStatus().get();
266 RefPtr
<WebGLProgram
> WebGLContext::CreateProgram() {
267 const FuncScope
funcScope(*this, "createProgram");
268 if (IsContextLost()) return nullptr;
270 return new WebGLProgram(this);
273 RefPtr
<WebGLShader
> WebGLContext::CreateShader(GLenum type
) {
274 const FuncScope
funcScope(*this, "createShader");
275 if (IsContextLost()) return nullptr;
277 if (type
!= LOCAL_GL_VERTEX_SHADER
&& type
!= LOCAL_GL_FRAGMENT_SHADER
) {
278 ErrorInvalidEnumInfo("type", type
);
282 return new WebGLShader(this, type
);
285 void WebGLContext::CullFace(GLenum face
) {
286 const FuncScope
funcScope(*this, "cullFace");
287 if (IsContextLost()) return;
289 if (!ValidateFaceEnum(face
)) return;
294 void WebGLContext::DetachShader(WebGLProgram
& prog
, const WebGLShader
& shader
) {
295 FuncScope
funcScope(*this, "detachShader");
296 if (IsContextLost()) return;
297 funcScope
.mBindFailureGuard
= true;
299 prog
.DetachShader(shader
);
301 funcScope
.mBindFailureGuard
= false;
304 static bool ValidateComparisonEnum(WebGLContext
& webgl
, const GLenum func
) {
308 case LOCAL_GL_LEQUAL
:
309 case LOCAL_GL_GREATER
:
310 case LOCAL_GL_GEQUAL
:
312 case LOCAL_GL_NOTEQUAL
:
313 case LOCAL_GL_ALWAYS
:
317 webgl
.ErrorInvalidEnumInfo("func", func
);
322 void WebGLContext::DepthFunc(GLenum func
) {
323 const FuncScope
funcScope(*this, "depthFunc");
324 if (IsContextLost()) return;
326 if (!ValidateComparisonEnum(*this, func
)) return;
328 gl
->fDepthFunc(func
);
331 void WebGLContext::DepthRange(GLfloat zNear
, GLfloat zFar
) {
332 const FuncScope
funcScope(*this, "depthRange");
333 if (IsContextLost()) return;
336 return ErrorInvalidOperation(
337 "the near value is greater than the far value!");
339 gl
->fDepthRange(zNear
, zFar
);
344 void WebGLContext::FramebufferAttach(const GLenum target
,
345 const GLenum attachSlot
,
346 const GLenum bindImageTarget
,
347 const webgl::FbAttachInfo
& toAttach
) {
348 FuncScope
funcScope(*this, "framebufferAttach");
349 funcScope
.mBindFailureGuard
= true;
350 const auto& limits
= *mLimits
;
352 if (!ValidateFramebufferTarget(target
)) return;
354 auto fb
= mBoundDrawFramebuffer
;
355 if (target
== LOCAL_GL_READ_FRAMEBUFFER
) {
356 fb
= mBoundReadFramebuffer
;
360 // `rb` needs no validation.
363 const auto& tex
= toAttach
.tex
;
365 const auto err
= CheckFramebufferAttach(bindImageTarget
, tex
->mTarget
.get(),
366 toAttach
.mipLevel
, toAttach
.zLayer
,
367 toAttach
.zLayerCount
, limits
);
371 auto safeToAttach
= toAttach
;
372 if (!toAttach
.rb
&& !toAttach
.tex
) {
376 !IsExtensionEnabled(WebGLExtensionID::OES_fbo_render_mipmap
)) {
377 safeToAttach
.mipLevel
= 0;
379 if (!IsExtensionEnabled(WebGLExtensionID::OVR_multiview2
)) {
380 safeToAttach
.isMultiview
= false;
383 if (!fb
->FramebufferAttach(attachSlot
, safeToAttach
)) return;
385 funcScope
.mBindFailureGuard
= false;
390 void WebGLContext::FrontFace(GLenum mode
) {
391 const FuncScope
funcScope(*this, "frontFace");
392 if (IsContextLost()) return;
399 return ErrorInvalidEnumInfo("mode", mode
);
402 gl
->fFrontFace(mode
);
405 Maybe
<double> WebGLContext::GetBufferParameter(GLenum target
, GLenum pname
) {
406 const FuncScope
funcScope(*this, "getBufferParameter");
407 if (IsContextLost()) return Nothing();
409 const auto& slot
= ValidateBufferSlot(target
);
410 if (!slot
) return Nothing();
411 const auto& buffer
= *slot
;
414 ErrorInvalidOperation("Buffer for `target` is null.");
419 case LOCAL_GL_BUFFER_SIZE
:
420 return Some(buffer
->ByteLength());
422 case LOCAL_GL_BUFFER_USAGE
:
423 return Some(buffer
->Usage());
426 ErrorInvalidEnumInfo("pname", pname
);
431 Maybe
<double> WebGLContext::GetFramebufferAttachmentParameter(
432 WebGLFramebuffer
* const fb
, GLenum attachment
, GLenum pname
) const {
433 const FuncScope
funcScope(*this, "getFramebufferAttachmentParameter");
434 if (IsContextLost()) return Nothing();
436 if (fb
) return fb
->GetAttachmentParameter(attachment
, pname
);
438 ////////////////////////////////////
441 ErrorInvalidOperation(
442 "Querying against the default framebuffer is not"
443 " allowed in WebGL 1.");
447 switch (attachment
) {
450 case LOCAL_GL_STENCIL
:
455 "For the default framebuffer, can only query COLOR, DEPTH,"
461 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE
:
462 switch (attachment
) {
466 if (!mOptions
.depth
) {
467 return Some(LOCAL_GL_NONE
);
470 case LOCAL_GL_STENCIL
:
471 if (!mOptions
.stencil
) {
472 return Some(LOCAL_GL_NONE
);
477 "With the default framebuffer, can only query COLOR, DEPTH,"
478 " or STENCIL for GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE");
481 return Some(LOCAL_GL_FRAMEBUFFER_DEFAULT
);
485 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE
:
486 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE
:
487 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE
:
488 if (attachment
== LOCAL_GL_BACK
) return Some(8);
491 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE
:
492 if (attachment
== LOCAL_GL_BACK
) {
493 if (mOptions
.alpha
) {
496 ErrorInvalidOperation(
497 "The default framebuffer doesn't contain an alpha buffer");
502 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE
:
503 if (attachment
== LOCAL_GL_DEPTH
) {
504 if (mOptions
.depth
) {
507 ErrorInvalidOperation(
508 "The default framebuffer doesn't contain an depth buffer");
513 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE
:
514 if (attachment
== LOCAL_GL_STENCIL
) {
515 if (mOptions
.stencil
) {
518 ErrorInvalidOperation(
519 "The default framebuffer doesn't contain an stencil buffer");
524 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE
:
525 if (attachment
== LOCAL_GL_STENCIL
) {
526 if (mOptions
.stencil
) {
527 return Some(LOCAL_GL_UNSIGNED_INT
);
529 ErrorInvalidOperation(
530 "The default framebuffer doesn't contain an stencil buffer");
531 } else if (attachment
== LOCAL_GL_DEPTH
) {
532 if (mOptions
.depth
) {
533 return Some(LOCAL_GL_UNSIGNED_NORMALIZED
);
535 ErrorInvalidOperation(
536 "The default framebuffer doesn't contain an depth buffer");
537 } else { // LOCAL_GL_BACK
538 return Some(LOCAL_GL_UNSIGNED_NORMALIZED
);
542 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING
:
543 if (attachment
== LOCAL_GL_STENCIL
) {
544 if (!mOptions
.stencil
) {
545 ErrorInvalidOperation(
546 "The default framebuffer doesn't contain an stencil buffer");
549 } else if (attachment
== LOCAL_GL_DEPTH
) {
550 if (!mOptions
.depth
) {
551 ErrorInvalidOperation(
552 "The default framebuffer doesn't contain an depth buffer");
556 return Some(LOCAL_GL_LINEAR
);
559 ErrorInvalidEnumInfo("pname", pname
);
563 Maybe
<double> WebGLContext::GetRenderbufferParameter(
564 const WebGLRenderbuffer
& rb
, GLenum pname
) const {
565 const FuncScope
funcScope(*this, "getRenderbufferParameter");
566 if (IsContextLost()) return Nothing();
569 case LOCAL_GL_RENDERBUFFER_SAMPLES
:
570 if (!IsWebGL2()) break;
573 case LOCAL_GL_RENDERBUFFER_WIDTH
:
574 case LOCAL_GL_RENDERBUFFER_HEIGHT
:
575 case LOCAL_GL_RENDERBUFFER_RED_SIZE
:
576 case LOCAL_GL_RENDERBUFFER_GREEN_SIZE
:
577 case LOCAL_GL_RENDERBUFFER_BLUE_SIZE
:
578 case LOCAL_GL_RENDERBUFFER_ALPHA_SIZE
:
579 case LOCAL_GL_RENDERBUFFER_DEPTH_SIZE
:
580 case LOCAL_GL_RENDERBUFFER_STENCIL_SIZE
:
581 case LOCAL_GL_RENDERBUFFER_INTERNAL_FORMAT
: {
582 // RB emulation means we have to ask the RB itself.
583 GLint i
= rb
.GetRenderbufferParameter(pname
);
591 ErrorInvalidEnumInfo("pname", pname
);
595 RefPtr
<WebGLTexture
> WebGLContext::CreateTexture() {
596 const FuncScope
funcScope(*this, "createTexture");
597 if (IsContextLost()) return nullptr;
600 gl
->fGenTextures(1, &tex
);
602 return new WebGLTexture(this, tex
);
605 GLenum
WebGLContext::GetError() {
606 const FuncScope
funcScope(*this, "getError");
608 /* WebGL 1.0: Section 5.14.3: Setting and getting state:
609 * If the context's webgl context lost flag is set, returns
610 * CONTEXT_LOST_WEBGL the first time this method is called.
611 * Afterward, returns NO_ERROR until the context has been
614 * WEBGL_lose_context:
615 * [When this extension is enabled: ] loseContext and
616 * restoreContext are allowed to generate INVALID_OPERATION errors
617 * even when the context is lost.
620 auto err
= mWebGLError
;
622 if (IsContextLost() || err
) // Must check IsContextLost in all flow paths.
625 // Either no WebGL-side error, or it's already been cleared.
626 // UnderlyingGL-side errors, now.
627 err
= gl
->fGetError();
628 if (gl
->IsContextLost()) {
629 CheckForContextLoss();
632 MOZ_ASSERT(err
!= LOCAL_GL_CONTEXT_LOST
);
635 GenerateWarning("Driver error unexpected by WebGL: 0x%04x", err
);
637 // - INVALID_OPERATION from ANGLE due to incomplete RBAB implementation for
639 // with DYNAMIC_DRAW index buffer.
644 webgl::GetUniformData
WebGLContext::GetUniform(const WebGLProgram
& prog
,
645 const uint32_t loc
) const {
646 const FuncScope
funcScope(*this, "getUniform");
647 webgl::GetUniformData ret
;
649 if (IsContextLost()) return;
651 const auto& info
= prog
.LinkInfo();
654 const auto locInfo
= MaybeFind(info
->locationMap
, loc
);
655 if (!locInfo
) return;
657 ret
.type
= locInfo
->info
.info
.elemType
;
660 case LOCAL_GL_FLOAT_VEC2
:
661 case LOCAL_GL_FLOAT_VEC3
:
662 case LOCAL_GL_FLOAT_VEC4
:
663 case LOCAL_GL_FLOAT_MAT2
:
664 case LOCAL_GL_FLOAT_MAT3
:
665 case LOCAL_GL_FLOAT_MAT4
:
666 case LOCAL_GL_FLOAT_MAT2x3
:
667 case LOCAL_GL_FLOAT_MAT2x4
:
668 case LOCAL_GL_FLOAT_MAT3x2
:
669 case LOCAL_GL_FLOAT_MAT3x4
:
670 case LOCAL_GL_FLOAT_MAT4x2
:
671 case LOCAL_GL_FLOAT_MAT4x3
:
672 gl
->fGetUniformfv(prog
.mGLName
, loc
,
673 reinterpret_cast<float*>(ret
.data
));
677 case LOCAL_GL_INT_VEC2
:
678 case LOCAL_GL_INT_VEC3
:
679 case LOCAL_GL_INT_VEC4
:
680 case LOCAL_GL_SAMPLER_2D
:
681 case LOCAL_GL_SAMPLER_3D
:
682 case LOCAL_GL_SAMPLER_CUBE
:
683 case LOCAL_GL_SAMPLER_2D_SHADOW
:
684 case LOCAL_GL_SAMPLER_2D_ARRAY
:
685 case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW
:
686 case LOCAL_GL_SAMPLER_CUBE_SHADOW
:
687 case LOCAL_GL_INT_SAMPLER_2D
:
688 case LOCAL_GL_INT_SAMPLER_3D
:
689 case LOCAL_GL_INT_SAMPLER_CUBE
:
690 case LOCAL_GL_INT_SAMPLER_2D_ARRAY
:
691 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D
:
692 case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D
:
693 case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE
:
694 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY
:
696 case LOCAL_GL_BOOL_VEC2
:
697 case LOCAL_GL_BOOL_VEC3
:
698 case LOCAL_GL_BOOL_VEC4
:
699 gl
->fGetUniformiv(prog
.mGLName
, loc
,
700 reinterpret_cast<int32_t*>(ret
.data
));
703 case LOCAL_GL_UNSIGNED_INT
:
704 case LOCAL_GL_UNSIGNED_INT_VEC2
:
705 case LOCAL_GL_UNSIGNED_INT_VEC3
:
706 case LOCAL_GL_UNSIGNED_INT_VEC4
:
707 gl
->fGetUniformuiv(prog
.mGLName
, loc
,
708 reinterpret_cast<uint32_t*>(ret
.data
));
712 MOZ_CRASH("GFX: Invalid elemType.");
718 void WebGLContext::Hint(GLenum target
, GLenum mode
) {
719 const FuncScope
funcScope(*this, "hint");
720 if (IsContextLost()) return;
723 case LOCAL_GL_FASTEST
:
724 case LOCAL_GL_NICEST
:
725 case LOCAL_GL_DONT_CARE
:
728 return ErrorInvalidEnumArg("mode", mode
);
733 bool isValid
= false;
736 case LOCAL_GL_GENERATE_MIPMAP_HINT
:
737 mGenerateMipmapHint
= mode
;
740 // Deprecated and removed in desktop GL Core profiles.
741 if (gl
->IsCoreProfile()) return;
745 case LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT
:
747 IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives
)) {
752 if (!isValid
) return ErrorInvalidEnumInfo("target", target
);
756 gl
->fHint(target
, mode
);
761 void WebGLContext::LinkProgram(WebGLProgram
& prog
) {
762 const FuncScope
funcScope(*this, "linkProgram");
763 if (IsContextLost()) return;
767 if (&prog
== mCurrentProgram
) {
768 if (!prog
.IsLinked()) {
769 // We use to simply early-out here, and preserve the GL behavior that
770 // failed relink doesn't invalidate the current active program link info.
771 // The new behavior was changed for WebGL here:
772 // https://github.com/KhronosGroup/WebGL/pull/3371
773 mActiveProgramLinkInfo
= nullptr;
774 gl
->fUseProgram(0); // Shouldn't be needed, but let's be safe.
777 mActiveProgramLinkInfo
= prog
.LinkInfo();
778 gl
->fUseProgram(prog
.mGLName
); // Uncontionally re-use.
779 // Previously, we needed this re-use on nvidia as a driver workaround,
780 // but we might as well do it unconditionally.
784 Maybe
<webgl::ErrorInfo
> SetPixelUnpack(
785 const bool isWebgl2
, webgl::PixelUnpackStateWebgl
* const unpacking
,
786 const GLenum pname
, const GLint param
) {
788 uint32_t* pValueSlot
= nullptr;
790 case LOCAL_GL_UNPACK_IMAGE_HEIGHT
:
791 pValueSlot
= &unpacking
->imageHeight
;
794 case LOCAL_GL_UNPACK_SKIP_IMAGES
:
795 pValueSlot
= &unpacking
->skipImages
;
798 case LOCAL_GL_UNPACK_ROW_LENGTH
:
799 pValueSlot
= &unpacking
->rowLength
;
802 case LOCAL_GL_UNPACK_SKIP_ROWS
:
803 pValueSlot
= &unpacking
->skipRows
;
806 case LOCAL_GL_UNPACK_SKIP_PIXELS
:
807 pValueSlot
= &unpacking
->skipPixels
;
812 *pValueSlot
= static_cast<uint32_t>(param
);
818 case dom::WebGLRenderingContext_Binding::UNPACK_FLIP_Y_WEBGL
:
819 unpacking
->flipY
= bool(param
);
822 case dom::WebGLRenderingContext_Binding::UNPACK_PREMULTIPLY_ALPHA_WEBGL
:
823 unpacking
->premultiplyAlpha
= bool(param
);
826 case dom::WebGLRenderingContext_Binding::UNPACK_COLORSPACE_CONVERSION_WEBGL
:
829 case dom::WebGLRenderingContext_Binding::BROWSER_DEFAULT_WEBGL
:
833 const nsPrintfCString
text("Bad UNPACK_COLORSPACE_CONVERSION: %s",
834 EnumString(param
).c_str());
835 return Some(webgl::ErrorInfo
{LOCAL_GL_INVALID_VALUE
, ToString(text
)});
838 unpacking
->colorspaceConversion
= param
;
841 case dom::MOZ_debug_Binding::UNPACK_REQUIRE_FASTPATH
:
842 unpacking
->requireFastPath
= bool(param
);
845 case LOCAL_GL_UNPACK_ALIGNMENT
:
854 const nsPrintfCString
text(
855 "UNPACK_ALIGNMENT must be [1,2,4,8], was %i", param
);
856 return Some(webgl::ErrorInfo
{LOCAL_GL_INVALID_VALUE
, ToString(text
)});
859 unpacking
->alignmentInTypeElems
= param
;
865 const nsPrintfCString
text("Bad `pname`: %s", EnumString(pname
).c_str());
866 return Some(webgl::ErrorInfo
{LOCAL_GL_INVALID_ENUM
, ToString(text
)});
869 bool WebGLContext::DoReadPixelsAndConvert(
870 const webgl::FormatInfo
* const srcFormat
, const webgl::ReadPixelsDesc
& desc
,
871 const uintptr_t dest
, const uint64_t destSize
, const uint32_t rowStride
) {
872 const auto& x
= desc
.srcOffset
.x
;
873 const auto& y
= desc
.srcOffset
.y
;
874 const auto size
= *ivec2::From(desc
.size
);
875 const auto& pi
= desc
.pi
;
877 // On at least Win+NV, we'll get PBO errors if we don't have at least
878 // `rowStride * height` bytes available to read into.
879 const auto naiveBytesNeeded
= CheckedInt
<uint64_t>(rowStride
) * size
.y
;
880 const bool isDangerCloseToEdge
=
881 (!naiveBytesNeeded
.isValid() || naiveBytesNeeded
.value() > destSize
);
882 const bool useParanoidHandling
=
883 (gl
->WorkAroundDriverBugs() && isDangerCloseToEdge
&&
884 mBoundPixelPackBuffer
);
885 if (!useParanoidHandling
) {
886 gl
->fReadPixels(x
, y
, size
.x
, size
.y
, pi
.format
, pi
.type
,
887 reinterpret_cast<void*>(dest
));
891 // Read everything but the last row.
892 const auto bodyHeight
= size
.y
- 1;
894 gl
->fReadPixels(x
, y
, size
.x
, bodyHeight
, pi
.format
, pi
.type
,
895 reinterpret_cast<void*>(dest
));
898 // Now read the last row.
899 gl
->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT
, 1);
900 gl
->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH
, 0);
901 gl
->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS
, 0);
903 const auto tailRowOffset
=
904 reinterpret_cast<uint8_t*>(dest
) + rowStride
* bodyHeight
;
905 gl
->fReadPixels(x
, y
+ bodyHeight
, size
.x
, 1, pi
.format
, pi
.type
,
911 webgl::ReadPixelsResult
WebGLContext::ReadPixelsInto(
912 const webgl::ReadPixelsDesc
& desc
, const Range
<uint8_t>& dest
) {
913 const FuncScope
funcScope(*this, "readPixels");
914 if (IsContextLost()) return {};
916 if (mBoundPixelPackBuffer
) {
917 ErrorInvalidOperation("PIXEL_PACK_BUFFER must be null.");
921 return ReadPixelsImpl(desc
, reinterpret_cast<uintptr_t>(dest
.begin().get()),
925 void WebGLContext::ReadPixelsPbo(const webgl::ReadPixelsDesc
& desc
,
926 const uint64_t offset
) {
927 const FuncScope
funcScope(*this, "readPixels");
928 if (IsContextLost()) return;
930 const auto& buffer
= ValidateBufferSelection(LOCAL_GL_PIXEL_PACK_BUFFER
);
936 const auto pii
= webgl::PackingInfoInfo::For(desc
.pi
);
938 GLenum err
= LOCAL_GL_INVALID_OPERATION
;
939 if (!desc
.pi
.format
|| !desc
.pi
.type
) {
940 err
= LOCAL_GL_INVALID_ENUM
;
942 GenerateError(err
, "`format` (%s) and/or `type` (%s) not acceptable.",
943 EnumString(desc
.pi
.format
).c_str(),
944 EnumString(desc
.pi
.type
).c_str());
948 if (offset
% pii
->bytesPerElement
!= 0) {
949 ErrorInvalidOperation(
950 "`offset` must be divisible by the size of `type`"
958 auto bytesAvailable
= buffer
->ByteLength();
959 if (offset
> bytesAvailable
) {
960 ErrorInvalidOperation("`offset` too large for bound PIXEL_PACK_BUFFER.");
963 bytesAvailable
-= offset
;
967 const ScopedLazyBind
lazyBind(gl
, LOCAL_GL_PIXEL_PACK_BUFFER
, buffer
);
969 ReadPixelsImpl(desc
, offset
, bytesAvailable
);
971 buffer
->ResetLastUpdateFenceId();
974 static webgl::PackingInfo
DefaultReadPixelPI(
975 const webgl::FormatUsageInfo
* usage
) {
976 MOZ_ASSERT(usage
->IsRenderable());
977 const auto& format
= *usage
->format
;
978 switch (format
.componentType
) {
979 case webgl::ComponentType::NormUInt
:
980 if (format
.r
== 16) {
981 return {LOCAL_GL_RGBA
, LOCAL_GL_UNSIGNED_SHORT
};
983 return {LOCAL_GL_RGBA
, LOCAL_GL_UNSIGNED_BYTE
};
985 case webgl::ComponentType::Int
:
986 return {LOCAL_GL_RGBA_INTEGER
, LOCAL_GL_INT
};
988 case webgl::ComponentType::UInt
:
989 return {LOCAL_GL_RGBA_INTEGER
, LOCAL_GL_UNSIGNED_INT
};
991 case webgl::ComponentType::Float
:
992 return {LOCAL_GL_RGBA
, LOCAL_GL_FLOAT
};
999 static bool ArePossiblePackEnums(const WebGLContext
* webgl
,
1000 const webgl::PackingInfo
& pi
) {
1001 // OpenGL ES 2.0 $4.3.1 - IMPLEMENTATION_COLOR_READ_{TYPE/FORMAT} is a valid
1002 // combination for glReadPixels()...
1004 // Only valid when pulled from:
1005 // * GLES 2.0.25 p105:
1006 // "table 3.4, excluding formats LUMINANCE and LUMINANCE_ALPHA."
1007 // * GLES 3.0.4 p193:
1008 // "table 3.2, excluding formats DEPTH_COMPONENT and DEPTH_STENCIL."
1009 switch (pi
.format
) {
1010 case LOCAL_GL_LUMINANCE
:
1011 case LOCAL_GL_LUMINANCE_ALPHA
:
1012 case LOCAL_GL_DEPTH_COMPONENT
:
1013 case LOCAL_GL_DEPTH_STENCIL
:
1017 if (pi
.type
== LOCAL_GL_UNSIGNED_INT_24_8
) return false;
1019 const auto pii
= webgl::PackingInfoInfo::For(pi
);
1020 if (!pii
) return false;
1025 webgl::PackingInfo
WebGLContext::ValidImplementationColorReadPI(
1026 const webgl::FormatUsageInfo
* usage
) const {
1027 const auto defaultPI
= DefaultReadPixelPI(usage
);
1029 // ES2_compatibility always returns RGBA/UNSIGNED_BYTE, so branch on actual
1030 // IsGLES(). Also OSX+NV generates an error here.
1031 if (!gl
->IsGLES()) return defaultPI
;
1033 webgl::PackingInfo implPI
;
1034 gl
->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT
,
1035 (GLint
*)&implPI
.format
);
1036 gl
->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE
,
1037 (GLint
*)&implPI
.type
);
1039 if (!ArePossiblePackEnums(this, implPI
)) return defaultPI
;
1044 static bool ValidateReadPixelsFormatAndType(
1045 const webgl::FormatUsageInfo
* srcUsage
, const webgl::PackingInfo
& pi
,
1046 gl::GLContext
* gl
, WebGLContext
* webgl
) {
1047 if (!ArePossiblePackEnums(webgl
, pi
)) {
1048 webgl
->ErrorInvalidEnum("Unexpected format or type.");
1052 const auto defaultPI
= DefaultReadPixelPI(srcUsage
);
1053 if (pi
== defaultPI
) return true;
1057 // OpenGL ES 3.0.4 p194 - When the internal format of the rendering surface is
1058 // RGB10_A2, a third combination of format RGBA and type
1059 // UNSIGNED_INT_2_10_10_10_REV is accepted.
1061 if (webgl
->IsWebGL2() &&
1062 srcUsage
->format
->effectiveFormat
== webgl::EffectiveFormat::RGB10_A2
&&
1063 pi
.format
== LOCAL_GL_RGBA
&&
1064 pi
.type
== LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV
) {
1070 MOZ_ASSERT(gl
->IsCurrent());
1071 const auto implPI
= webgl
->ValidImplementationColorReadPI(srcUsage
);
1072 if (pi
== implPI
) return true;
1077 webgl
->ErrorInvalidOperation(
1078 "Format and type %s/%s incompatible with this %s attachment."
1079 " This framebuffer requires either %s/%s or"
1080 " getParameter(IMPLEMENTATION_COLOR_READ_FORMAT/_TYPE) %s/%s.",
1081 EnumString(pi
.format
).c_str(), EnumString(pi
.type
).c_str(),
1082 srcUsage
->format
->name
,
1083 EnumString(defaultPI
.format
).c_str(), EnumString(defaultPI
.type
).c_str(),
1084 EnumString(implPI
.format
).c_str(), EnumString(implPI
.type
).c_str());
1090 webgl::ReadPixelsResult
WebGLContext::ReadPixelsImpl(
1091 const webgl::ReadPixelsDesc
& desc
, const uintptr_t dest
,
1092 const uint64_t availBytes
) {
1093 const webgl::FormatUsageInfo
* srcFormat
;
1096 if (!BindCurFBForColorRead(&srcFormat
, &srcWidth
, &srcHeight
)) return {};
1100 if (!ValidateReadPixelsFormatAndType(srcFormat
, desc
.pi
, gl
, this)) return {};
1104 const auto& srcOffset
= desc
.srcOffset
;
1105 const auto& size
= desc
.size
;
1107 if (!ivec2::From(size
)) {
1108 ErrorInvalidValue("width and height must be non-negative.");
1112 const auto& packing
= desc
.packState
;
1113 const auto explicitPackingRes
= webgl::ExplicitPixelPackingState::ForUseWith(
1114 packing
, LOCAL_GL_TEXTURE_2D
, {size
.x
, size
.y
, 1}, desc
.pi
, {});
1115 if (!explicitPackingRes
.isOk()) {
1116 ErrorInvalidOperation("%s", explicitPackingRes
.inspectErr().c_str());
1119 const auto& explicitPacking
= explicitPackingRes
.inspect();
1120 const auto& rowStride
= explicitPacking
.metrics
.bytesPerRowStride
;
1121 const auto& bytesNeeded
= explicitPacking
.metrics
.totalBytesUsed
;
1122 if (bytesNeeded
> availBytes
) {
1123 ErrorInvalidOperation("buffer too small");
1129 int32_t readX
, readY
;
1130 int32_t writeX
, writeY
;
1131 int32_t rwWidth
, rwHeight
;
1132 if (!Intersect(srcWidth
, srcOffset
.x
, size
.x
, &readX
, &writeX
, &rwWidth
) ||
1133 !Intersect(srcHeight
, srcOffset
.y
, size
.y
, &readY
, &writeY
, &rwHeight
)) {
1134 ErrorOutOfMemory("Bad subrect selection.");
1139 // Now that the errors are out of the way, on to actually reading!
1141 gl
->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT
, packing
.alignmentInTypeElems
);
1143 gl
->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH
, packing
.rowLength
);
1144 gl
->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS
, packing
.skipPixels
);
1145 gl
->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS
, packing
.skipRows
);
1148 if (!rwWidth
|| !rwHeight
) {
1149 // Disjoint rects, so we're done already.
1150 DummyReadFramebufferOperation();
1153 const auto rwSize
= *uvec2::From(rwWidth
, rwHeight
);
1155 const auto res
= webgl::ReadPixelsResult
{
1156 {{writeX
, writeY
}, {rwSize
.x
, rwSize
.y
}}, rowStride
};
1158 if (rwSize
== size
) {
1159 DoReadPixelsAndConvert(srcFormat
->format
, desc
, dest
, bytesNeeded
,
1164 // Read request contains out-of-bounds pixels. Unfortunately:
1165 // GLES 3.0.4 p194 "Obtaining Pixels from the Framebuffer":
1166 // "If any of these pixels lies outside of the window allocated to the current
1167 // GL context, or outside of the image attached to the currently bound
1168 // framebuffer object, then the values obtained for those pixels are
1171 // This is a slow-path, so warn people away!
1173 "Out-of-bounds reads with readPixels are deprecated, and"
1176 ////////////////////////////////////
1177 // Read only the in-bounds pixels.
1180 if (!packing
.rowLength
) {
1181 gl
->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH
, packing
.skipPixels
+ size
.x
);
1183 gl
->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS
, packing
.skipPixels
+ writeX
);
1184 gl
->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS
, packing
.skipRows
+ writeY
);
1187 desc2
.srcOffset
= {readX
, readY
};
1188 desc2
.size
= rwSize
;
1190 DoReadPixelsAndConvert(srcFormat
->format
, desc2
, dest
, bytesNeeded
,
1193 // I *did* say "hilariously slow".
1196 desc2
.srcOffset
= {readX
, readY
};
1197 desc2
.size
= {rwSize
.x
, 1};
1199 const auto skipBytes
= writeX
* explicitPacking
.metrics
.bytesPerPixel
;
1200 const auto usedRowBytes
= rwSize
.x
* explicitPacking
.metrics
.bytesPerPixel
;
1201 for (const auto j
: IntegerRange(rwSize
.y
)) {
1202 desc2
.srcOffset
.y
= readY
+ j
;
1203 const auto destWriteBegin
= dest
+ skipBytes
+ (writeY
+ j
) * rowStride
;
1204 MOZ_RELEASE_ASSERT(dest
<= destWriteBegin
);
1205 MOZ_RELEASE_ASSERT(destWriteBegin
<= dest
+ availBytes
);
1207 const auto destWriteEnd
= destWriteBegin
+ usedRowBytes
;
1208 MOZ_RELEASE_ASSERT(dest
<= destWriteEnd
);
1209 MOZ_RELEASE_ASSERT(destWriteEnd
<= dest
+ availBytes
);
1211 DoReadPixelsAndConvert(srcFormat
->format
, desc2
, destWriteBegin
,
1212 destWriteEnd
- destWriteBegin
, rowStride
);
1219 void WebGLContext::RenderbufferStorageMultisample(WebGLRenderbuffer
& rb
,
1221 GLenum internalFormat
,
1223 uint32_t height
) const {
1224 const FuncScope
funcScope(*this, "renderbufferStorage(Multisample)?");
1225 if (IsContextLost()) return;
1227 rb
.RenderbufferStorage(samples
, internalFormat
, width
, height
);
1230 void WebGLContext::Scissor(GLint x
, GLint y
, GLsizei width
, GLsizei height
) {
1231 const FuncScope
funcScope(*this, "scissor");
1232 if (IsContextLost()) return;
1234 if (!ValidateNonNegative("width", width
) ||
1235 !ValidateNonNegative("height", height
)) {
1239 mScissorRect
= {x
, y
, width
, height
};
1240 mScissorRect
.Apply(*gl
);
1243 void WebGLContext::StencilFuncSeparate(GLenum face
, GLenum func
, GLint ref
,
1245 const FuncScope
funcScope(*this, "stencilFuncSeparate");
1246 if (IsContextLost()) return;
1248 if (!ValidateFaceEnum(face
) || !ValidateComparisonEnum(*this, func
)) {
1253 case LOCAL_GL_FRONT_AND_BACK
:
1254 mStencilRefFront
= ref
;
1255 mStencilRefBack
= ref
;
1256 mStencilValueMaskFront
= mask
;
1257 mStencilValueMaskBack
= mask
;
1259 case LOCAL_GL_FRONT
:
1260 mStencilRefFront
= ref
;
1261 mStencilValueMaskFront
= mask
;
1264 mStencilRefBack
= ref
;
1265 mStencilValueMaskBack
= mask
;
1269 gl
->fStencilFuncSeparate(face
, func
, ref
, mask
);
1272 void WebGLContext::StencilOpSeparate(GLenum face
, GLenum sfail
, GLenum dpfail
,
1274 const FuncScope
funcScope(*this, "stencilOpSeparate");
1275 if (IsContextLost()) return;
1277 if (!ValidateFaceEnum(face
) || !ValidateStencilOpEnum(sfail
, "sfail") ||
1278 !ValidateStencilOpEnum(dpfail
, "dpfail") ||
1279 !ValidateStencilOpEnum(dppass
, "dppass"))
1282 gl
->fStencilOpSeparate(face
, sfail
, dpfail
, dppass
);
1285 ////////////////////////////////////////////////////////////////////////////////
1288 void WebGLContext::UniformData(
1289 const uint32_t loc
, const bool transpose
,
1290 const Range
<const webgl::UniformDataVal
>& data
) const {
1291 const FuncScope
funcScope(*this, "uniform setter");
1293 if (!IsWebGL2() && transpose
) {
1294 GenerateError(LOCAL_GL_INVALID_VALUE
, "`transpose`:true requires WebGL 2.");
1300 const auto& link
= mActiveProgramLinkInfo
;
1303 const auto locInfo
= MaybeFind(link
->locationMap
, loc
);
1305 // Null WebGLUniformLocations become -1, which will end up here.
1309 const auto& validationInfo
= locInfo
->info
;
1310 const auto& activeInfo
= validationInfo
.info
;
1311 const auto& channels
= validationInfo
.channelsPerElem
;
1312 const auto& pfn
= validationInfo
.pfn
;
1316 const auto lengthInType
= data
.length();
1317 const auto elemCount
= lengthInType
/ channels
;
1318 if (elemCount
> 1 && !validationInfo
.isArray
) {
1320 LOCAL_GL_INVALID_OPERATION
,
1321 "(uniform %s) `values` length (%u) must exactly match size of %s.",
1322 activeInfo
.name
.c_str(), lengthInType
,
1323 EnumString(activeInfo
.elemType
).c_str());
1329 const auto& samplerInfo
= locInfo
->samplerInfo
;
1331 const auto idata
= reinterpret_cast<const uint32_t*>(data
.begin().get());
1332 const auto maxTexUnits
= GLMaxTextureUnits();
1333 for (const auto& val
: Range
<const uint32_t>(idata
, elemCount
)) {
1334 if (val
>= maxTexUnits
) {
1336 "This uniform location is a sampler, but %d"
1337 " is not a valid texture unit.",
1346 // This is a little galaxy-brain, sorry!
1347 const auto ptr
= static_cast<const void*>(data
.begin().get());
1348 (*pfn
)(*gl
, static_cast<GLint
>(loc
), elemCount
, transpose
, ptr
);
1353 auto& texUnits
= samplerInfo
->texUnits
;
1355 const auto srcBegin
= reinterpret_cast<const uint32_t*>(data
.begin().get());
1356 auto destIndex
= locInfo
->indexIntoUniform
;
1357 for (const auto& val
: Range
<const uint32_t>(srcBegin
, elemCount
)) {
1358 if (destIndex
>= texUnits
.Length()) break;
1359 texUnits
[destIndex
] = AssertedCast
<uint8_t>(val
);
1365 ////////////////////////////////////////////////////////////////////////////////
1367 void WebGLContext::UseProgram(WebGLProgram
* prog
) {
1368 FuncScope
funcScope(*this, "useProgram");
1369 if (IsContextLost()) return;
1370 funcScope
.mBindFailureGuard
= true;
1373 mCurrentProgram
= nullptr;
1374 mActiveProgramLinkInfo
= nullptr;
1375 funcScope
.mBindFailureGuard
= false;
1379 if (!ValidateObject("prog", *prog
)) return;
1381 if (!prog
->UseProgram()) return;
1383 mCurrentProgram
= prog
;
1384 mActiveProgramLinkInfo
= mCurrentProgram
->LinkInfo();
1386 funcScope
.mBindFailureGuard
= false;
1389 bool WebGLContext::ValidateProgram(const WebGLProgram
& prog
) const {
1390 const FuncScope
funcScope(*this, "validateProgram");
1391 if (IsContextLost()) return false;
1393 return prog
.ValidateProgram();
1396 RefPtr
<WebGLFramebuffer
> WebGLContext::CreateFramebuffer() {
1397 const FuncScope
funcScope(*this, "createFramebuffer");
1398 if (IsContextLost()) return nullptr;
1401 gl
->fGenFramebuffers(1, &fbo
);
1403 return new WebGLFramebuffer(this, fbo
);
1406 RefPtr
<WebGLFramebuffer
> WebGLContext::CreateOpaqueFramebuffer(
1407 const webgl::OpaqueFramebufferOptions
& options
) {
1408 const FuncScope
funcScope(*this, "createOpaqueFramebuffer");
1409 if (IsContextLost()) return nullptr;
1411 uint32_t samples
= options
.antialias
? StaticPrefs::webgl_msaa_samples() : 0;
1412 samples
= std::min(samples
, gl
->MaxSamples());
1413 const gfx::IntSize size
= {options
.width
, options
.height
};
1416 gl::MozFramebuffer::Create(gl
, size
, samples
, options
.depthStencil
);
1421 return new WebGLFramebuffer(this, std::move(fbo
));
1424 RefPtr
<WebGLRenderbuffer
> WebGLContext::CreateRenderbuffer() {
1425 const FuncScope
funcScope(*this, "createRenderbuffer");
1426 if (IsContextLost()) return nullptr;
1428 return new WebGLRenderbuffer(this);
1431 void WebGLContext::Viewport(GLint x
, GLint y
, GLsizei width
, GLsizei height
) {
1432 const FuncScope
funcScope(*this, "viewport");
1433 if (IsContextLost()) return;
1435 if (!ValidateNonNegative("width", width
) ||
1436 !ValidateNonNegative("height", height
)) {
1440 const auto& limits
= Limits();
1441 width
= std::min(width
, static_cast<GLsizei
>(limits
.maxViewportDim
));
1442 height
= std::min(height
, static_cast<GLsizei
>(limits
.maxViewportDim
));
1444 gl
->fViewport(x
, y
, width
, height
);
1448 mViewportWidth
= width
;
1449 mViewportHeight
= height
;
1452 void WebGLContext::CompileShader(WebGLShader
& shader
) {
1453 const FuncScope
funcScope(*this, "compileShader");
1454 if (IsContextLost()) return;
1456 if (!ValidateObject("shader", shader
)) return;
1458 shader
.CompileShader();
1461 Maybe
<webgl::ShaderPrecisionFormat
> WebGLContext::GetShaderPrecisionFormat(
1462 GLenum shadertype
, GLenum precisiontype
) const {
1463 const FuncScope
funcScope(*this, "getShaderPrecisionFormat");
1464 if (IsContextLost()) return Nothing();
1466 switch (shadertype
) {
1467 case LOCAL_GL_FRAGMENT_SHADER
:
1468 case LOCAL_GL_VERTEX_SHADER
:
1471 ErrorInvalidEnumInfo("shadertype", shadertype
);
1475 switch (precisiontype
) {
1476 case LOCAL_GL_LOW_FLOAT
:
1477 case LOCAL_GL_MEDIUM_FLOAT
:
1478 case LOCAL_GL_HIGH_FLOAT
:
1479 case LOCAL_GL_LOW_INT
:
1480 case LOCAL_GL_MEDIUM_INT
:
1481 case LOCAL_GL_HIGH_INT
:
1484 ErrorInvalidEnumInfo("precisiontype", precisiontype
);
1488 GLint range
[2], precision
;
1490 if (mDisableFragHighP
&& shadertype
== LOCAL_GL_FRAGMENT_SHADER
&&
1491 (precisiontype
== LOCAL_GL_HIGH_FLOAT
||
1492 precisiontype
== LOCAL_GL_HIGH_INT
)) {
1497 gl
->fGetShaderPrecisionFormat(shadertype
, precisiontype
, range
, &precision
);
1500 return Some(webgl::ShaderPrecisionFormat
{range
[0], range
[1], precision
});
1503 void WebGLContext::ShaderSource(WebGLShader
& shader
,
1504 const std::string
& source
) const {
1505 const FuncScope
funcScope(*this, "shaderSource");
1506 if (IsContextLost()) return;
1508 shader
.ShaderSource(source
);
1511 void WebGLContext::BlendColor(GLfloat r
, GLfloat g
, GLfloat b
, GLfloat a
) {
1512 const FuncScope
funcScope(*this, "blendColor");
1513 if (IsContextLost()) return;
1515 gl
->fBlendColor(r
, g
, b
, a
);
1518 void WebGLContext::Flush() {
1519 const FuncScope
funcScope(*this, "flush");
1520 if (IsContextLost()) return;
1525 void WebGLContext::Finish() {
1526 const FuncScope
funcScope(*this, "finish");
1527 if (IsContextLost()) return;
1531 mCompletedFenceId
= mNextFenceId
;
1535 void WebGLContext::LineWidth(GLfloat width
) {
1536 const FuncScope
funcScope(*this, "lineWidth");
1537 if (IsContextLost()) return;
1539 // Doing it this way instead of `if (width <= 0.0)` handles NaNs.
1540 const bool isValid
= width
> 0.0;
1542 ErrorInvalidValue("`width` must be positive and non-zero.");
1548 if (gl
->IsCoreProfile() && width
> 1.0) {
1552 gl
->fLineWidth(width
);
1555 void WebGLContext::PolygonOffset(GLfloat factor
, GLfloat units
) {
1556 const FuncScope
funcScope(*this, "polygonOffset");
1557 if (IsContextLost()) return;
1559 gl
->fPolygonOffset(factor
, units
);
1562 void WebGLContext::SampleCoverage(GLclampf value
, WebGLboolean invert
) {
1563 const FuncScope
funcScope(*this, "sampleCoverage");
1564 if (IsContextLost()) return;
1566 gl
->fSampleCoverage(value
, invert
);
1569 } // namespace mozilla