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/. */
6 #include "WebGLContext.h"
9 #include "mozilla/CheckedInt.h"
10 #include "WebGLBuffer.h"
11 #include "WebGLContextUtils.h"
12 #include "WebGLFramebuffer.h"
13 #include "WebGLProgram.h"
14 #include "WebGLRenderbuffer.h"
15 #include "WebGLShader.h"
16 #include "WebGLTexture.h"
17 #include "WebGLUniformInfo.h"
18 #include "WebGLVertexArray.h"
19 #include "WebGLVertexAttribData.h"
21 using namespace mozilla
;
22 using namespace mozilla::dom
;
23 using namespace mozilla::gl
;
25 // For a Tegra workaround.
26 static const int MAX_DRAW_CALLS_SINCE_FLUSH
= 100;
29 WebGLContext::DrawInstanced_check(const char* info
)
31 // This restriction was removed in GLES3, so WebGL2 shouldn't have it.
33 IsExtensionEnabled(WebGLExtensionID::ANGLE_instanced_arrays
) &&
34 !mBufferFetchingHasPerVertex
)
36 /* http://www.khronos.org/registry/gles/extensions/ANGLE/ANGLE_instanced_arrays.txt
37 * If all of the enabled vertex attribute arrays that are bound to active
38 * generic attributes in the program have a non-zero divisor, the draw
39 * call should return INVALID_OPERATION.
41 * NB: This also appears to apply to NV_instanced_arrays, though the
42 * INVALID_OPERATION emission is not explicitly stated.
43 * ARB_instanced_arrays does not have this restriction.
45 ErrorInvalidOperation("%s: at least one vertex attribute divisor should be 0", info
);
52 bool WebGLContext::DrawArrays_check(GLint first
, GLsizei count
, GLsizei primcount
, const char* info
)
54 if (first
< 0 || count
< 0) {
55 ErrorInvalidValue("%s: negative first or count", info
);
60 ErrorInvalidValue("%s: negative primcount", info
);
64 if (!ValidateStencilParamsForDrawCall()) {
68 // If count is 0, there's nothing to do.
69 if (count
== 0 || primcount
== 0) {
73 // Any checks below this depend on a program being available.
74 if (!mCurrentProgram
) {
75 ErrorInvalidOperation("%s: null CURRENT_PROGRAM", info
);
79 if (!ValidateBufferFetching(info
)) {
83 CheckedInt
<GLsizei
> checked_firstPlusCount
= CheckedInt
<GLsizei
>(first
) + count
;
85 if (!checked_firstPlusCount
.isValid()) {
86 ErrorInvalidOperation("%s: overflow in first+count", info
);
90 if (uint32_t(checked_firstPlusCount
.value()) > mMaxFetchedVertices
) {
91 ErrorInvalidOperation("%s: bound vertex attribute buffers do not have sufficient size for given first and count", info
);
95 if (uint32_t(primcount
) > mMaxFetchedInstances
) {
96 ErrorInvalidOperation("%s: bound instance attribute buffers do not have sufficient size for given primcount", info
);
100 MakeContextCurrent();
102 if (mBoundFramebuffer
) {
103 if (!mBoundFramebuffer
->CheckAndInitializeAttachments()) {
104 ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info
);
108 ClearBackbufferIfNeeded();
111 if (!DoFakeVertexAttrib0(checked_firstPlusCount
.value())) {
115 if (!DrawInstanced_check(info
)) {
119 BindFakeBlackTextures();
125 WebGLContext::DrawArrays(GLenum mode
, GLint first
, GLsizei count
)
130 if (!ValidateDrawModeEnum(mode
, "drawArrays: mode"))
133 if (!DrawArrays_check(first
, count
, 1, "drawArrays"))
136 RunContextLossTimer();
139 ScopedMaskWorkaround
autoMask(*this);
140 gl
->fDrawArrays(mode
, first
, count
);
147 WebGLContext::DrawArraysInstanced(GLenum mode
, GLint first
, GLsizei count
, GLsizei primcount
)
152 if (!ValidateDrawModeEnum(mode
, "drawArraysInstanced: mode"))
155 if (!DrawArrays_check(first
, count
, primcount
, "drawArraysInstanced"))
158 RunContextLossTimer();
161 ScopedMaskWorkaround
autoMask(*this);
162 gl
->fDrawArraysInstanced(mode
, first
, count
, primcount
);
169 WebGLContext::DrawElements_check(GLsizei count
, GLenum type
,
170 WebGLintptr byteOffset
, GLsizei primcount
,
171 const char* info
, GLuint
* out_upperBound
)
173 if (count
< 0 || byteOffset
< 0) {
174 ErrorInvalidValue("%s: negative count or offset", info
);
179 ErrorInvalidValue("%s: negative primcount", info
);
183 if (!ValidateStencilParamsForDrawCall()) {
187 // If count is 0, there's nothing to do.
188 if (count
== 0 || primcount
== 0) {
192 CheckedUint32 checked_byteCount
;
196 if (type
== LOCAL_GL_UNSIGNED_SHORT
) {
197 checked_byteCount
= 2 * CheckedUint32(count
);
198 if (byteOffset
% 2 != 0) {
199 ErrorInvalidOperation("%s: invalid byteOffset for UNSIGNED_SHORT (must be a multiple of 2)", info
);
202 first
= byteOffset
/ 2;
204 else if (type
== LOCAL_GL_UNSIGNED_BYTE
) {
205 checked_byteCount
= count
;
208 else if (type
== LOCAL_GL_UNSIGNED_INT
&& IsExtensionEnabled(WebGLExtensionID::OES_element_index_uint
)) {
209 checked_byteCount
= 4 * CheckedUint32(count
);
210 if (byteOffset
% 4 != 0) {
211 ErrorInvalidOperation("%s: invalid byteOffset for UNSIGNED_INT (must be a multiple of 4)", info
);
214 first
= byteOffset
/ 4;
217 ErrorInvalidEnum("%s: type must be UNSIGNED_SHORT or UNSIGNED_BYTE", info
);
221 if (!checked_byteCount
.isValid()) {
222 ErrorInvalidValue("%s: overflow in byteCount", info
);
226 // Any checks below this depend on a program being available.
227 if (!mCurrentProgram
) {
228 ErrorInvalidOperation("%s: null CURRENT_PROGRAM", info
);
232 if (!mBoundVertexArray
->mElementArrayBuffer
) {
233 ErrorInvalidOperation("%s: must have element array buffer binding", info
);
237 WebGLBuffer
& elemArrayBuffer
= *mBoundVertexArray
->mElementArrayBuffer
;
239 if (!elemArrayBuffer
.ByteLength()) {
240 ErrorInvalidOperation("%s: bound element array buffer doesn't have any data", info
);
244 CheckedInt
<GLsizei
> checked_neededByteCount
= checked_byteCount
.toChecked
<GLsizei
>() + byteOffset
;
246 if (!checked_neededByteCount
.isValid()) {
247 ErrorInvalidOperation("%s: overflow in byteOffset+byteCount", info
);
251 if (uint32_t(checked_neededByteCount
.value()) > elemArrayBuffer
.ByteLength()) {
252 ErrorInvalidOperation("%s: bound element array buffer is too small for given count and offset", info
);
256 if (!ValidateBufferFetching(info
))
259 if (!mMaxFetchedVertices
||
260 !elemArrayBuffer
.Validate(type
, mMaxFetchedVertices
- 1, first
, count
, out_upperBound
))
262 ErrorInvalidOperation(
263 "%s: bound vertex attribute buffers do not have sufficient "
264 "size for given indices from the bound element array", info
);
268 if (uint32_t(primcount
) > mMaxFetchedInstances
) {
269 ErrorInvalidOperation("%s: bound instance attribute buffers do not have sufficient size for given primcount", info
);
273 // Bug 1008310 - Check if buffer has been used with a different previous type
274 if (elemArrayBuffer
.IsElementArrayUsedWithMultipleTypes()) {
275 GenerateWarning("%s: bound element array buffer previously used with a type other than "
276 "%s, this will affect performance.",
278 WebGLContext::EnumName(type
));
281 MakeContextCurrent();
283 if (mBoundFramebuffer
) {
284 if (!mBoundFramebuffer
->CheckAndInitializeAttachments()) {
285 ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info
);
289 ClearBackbufferIfNeeded();
292 if (!DoFakeVertexAttrib0(mMaxFetchedVertices
)) {
296 if (!DrawInstanced_check(info
)) {
300 BindFakeBlackTextures();
306 WebGLContext::DrawElements(GLenum mode
, GLsizei count
, GLenum type
,
307 WebGLintptr byteOffset
)
312 if (!ValidateDrawModeEnum(mode
, "drawElements: mode"))
315 GLuint upperBound
= 0;
316 if (!DrawElements_check(count
, type
, byteOffset
, 1, "drawElements",
322 RunContextLossTimer();
325 ScopedMaskWorkaround
autoMask(*this);
327 if (gl
->IsSupported(gl::GLFeature::draw_range_elements
)) {
328 gl
->fDrawRangeElements(mode
, 0, upperBound
, count
, type
,
329 reinterpret_cast<GLvoid
*>(byteOffset
));
331 gl
->fDrawElements(mode
, count
, type
,
332 reinterpret_cast<GLvoid
*>(byteOffset
));
340 WebGLContext::DrawElementsInstanced(GLenum mode
, GLsizei count
, GLenum type
,
341 WebGLintptr byteOffset
, GLsizei primcount
)
346 if (!ValidateDrawModeEnum(mode
, "drawElementsInstanced: mode"))
349 GLuint upperBound
= 0;
350 if (!DrawElements_check(count
, type
, byteOffset
, primcount
,
351 "drawElementsInstanced", &upperBound
))
356 RunContextLossTimer();
359 ScopedMaskWorkaround
autoMask(*this);
360 gl
->fDrawElementsInstanced(mode
, count
, type
,
361 reinterpret_cast<GLvoid
*>(byteOffset
),
368 void WebGLContext::Draw_cleanup()
370 UndoFakeVertexAttrib0();
371 UnbindFakeBlackTextures();
373 if (!mBoundFramebuffer
) {
375 mShouldPresent
= true;
376 MOZ_ASSERT(!mBackbufferNeedsClear
);
379 if (gl
->WorkAroundDriverBugs()) {
380 if (gl
->Renderer() == gl::GLRenderer::Tegra
) {
381 mDrawCallsSinceLastFlush
++;
383 if (mDrawCallsSinceLastFlush
>= MAX_DRAW_CALLS_SINCE_FLUSH
) {
385 mDrawCallsSinceLastFlush
= 0;
390 // Let's check the viewport
391 const WebGLRectangleObject
* rect
= CurValidFBRectObject();
393 if (mViewportWidth
> rect
->Width() ||
394 mViewportHeight
> rect
->Height())
396 if (!mAlreadyWarnedAboutViewportLargerThanDest
) {
397 GenerateWarning("Drawing to a destination rect smaller than the viewport rect. "
398 "(This warning will only be given once)");
399 mAlreadyWarnedAboutViewportLargerThanDest
= true;
406 * Verify that state is consistent for drawing, and compute max number of elements (maxAllowedCount)
407 * that will be legal to be read from bound VBOs.
411 WebGLContext::ValidateBufferFetching(const char* info
)
414 GLint currentProgram
= 0;
415 MakeContextCurrent();
416 gl
->fGetIntegerv(LOCAL_GL_CURRENT_PROGRAM
, ¤tProgram
);
417 MOZ_ASSERT(GLuint(currentProgram
) == mCurrentProgram
->GLName(),
418 "WebGL: current program doesn't agree with GL state");
421 if (mBufferFetchingIsVerified
)
424 bool hasPerVertex
= false;
425 uint32_t maxVertices
= UINT32_MAX
;
426 uint32_t maxInstances
= UINT32_MAX
;
427 uint32_t attribs
= mBoundVertexArray
->mAttribs
.Length();
429 for (uint32_t i
= 0; i
< attribs
; ++i
) {
430 const WebGLVertexAttribData
& vd
= mBoundVertexArray
->mAttribs
[i
];
432 // If the attrib array isn't enabled, there's nothing to check;
433 // it's a static value.
437 if (vd
.buf
== nullptr) {
438 ErrorInvalidOperation("%s: no VBO bound to enabled vertex attrib index %d!", info
, i
);
442 // If the attrib is not in use, then we don't have to validate
443 // it, just need to make sure that the binding is non-null.
444 if (!mCurrentProgram
->IsAttribInUse(i
))
448 CheckedUint32 checked_byteLength
= CheckedUint32(vd
.buf
->ByteLength()) - vd
.byteOffset
;
449 CheckedUint32 checked_sizeOfLastElement
= CheckedUint32(vd
.componentSize()) * vd
.size
;
451 if (!checked_byteLength
.isValid() ||
452 !checked_sizeOfLastElement
.isValid())
454 ErrorInvalidOperation("%s: integer overflow occured while checking vertex attrib %d", info
, i
);
458 if (checked_byteLength
.value() < checked_sizeOfLastElement
.value()) {
464 CheckedUint32 checked_maxAllowedCount
= ((checked_byteLength
- checked_sizeOfLastElement
) / vd
.actualStride()) + 1;
466 if (!checked_maxAllowedCount
.isValid()) {
467 ErrorInvalidOperation("%s: integer overflow occured while checking vertex attrib %d", info
, i
);
471 if (vd
.divisor
== 0) {
472 maxVertices
= std::min(maxVertices
, checked_maxAllowedCount
.value());
475 CheckedUint32 checked_curMaxInstances
= checked_maxAllowedCount
* vd
.divisor
;
477 uint32_t curMaxInstances
= UINT32_MAX
;
478 // If this isn't valid, it's because we overflowed our
479 // uint32 above. Just leave this as UINT32_MAX, since
480 // sizeof(uint32) becomes our limiting factor.
481 if (checked_curMaxInstances
.isValid()) {
482 curMaxInstances
= checked_curMaxInstances
.value();
485 maxInstances
= std::min(maxInstances
, curMaxInstances
);
489 mBufferFetchingIsVerified
= true;
490 mBufferFetchingHasPerVertex
= hasPerVertex
;
491 mMaxFetchedVertices
= maxVertices
;
492 mMaxFetchedInstances
= maxInstances
;
497 WebGLVertexAttrib0Status
498 WebGLContext::WhatDoesVertexAttrib0Need()
500 MOZ_ASSERT(mCurrentProgram
);
502 // work around Mac OSX crash, see bug 631420
504 if (gl
->WorkAroundDriverBugs() &&
505 mBoundVertexArray
->IsAttribArrayEnabled(0) &&
506 !mCurrentProgram
->IsAttribInUse(0))
508 return WebGLVertexAttrib0Status::EmulatedUninitializedArray
;
512 if (MOZ_LIKELY(gl
->IsGLES() ||
513 mBoundVertexArray
->IsAttribArrayEnabled(0)))
515 return WebGLVertexAttrib0Status::Default
;
518 return mCurrentProgram
->IsAttribInUse(0)
519 ? WebGLVertexAttrib0Status::EmulatedInitializedArray
520 : WebGLVertexAttrib0Status::EmulatedUninitializedArray
;
524 WebGLContext::DoFakeVertexAttrib0(GLuint vertexCount
)
526 WebGLVertexAttrib0Status whatDoesAttrib0Need
= WhatDoesVertexAttrib0Need();
528 if (MOZ_LIKELY(whatDoesAttrib0Need
== WebGLVertexAttrib0Status::Default
))
531 if (!mAlreadyWarnedAboutFakeVertexAttrib0
) {
532 GenerateWarning("Drawing without vertex attrib 0 array enabled forces the browser "
533 "to do expensive emulation work when running on desktop OpenGL "
534 "platforms, for example on Mac. It is preferable to always draw "
535 "with vertex attrib 0 array enabled, by using bindAttribLocation "
536 "to bind some always-used attribute to location 0.");
537 mAlreadyWarnedAboutFakeVertexAttrib0
= true;
540 CheckedUint32 checked_dataSize
= CheckedUint32(vertexCount
) * 4 * sizeof(GLfloat
);
542 if (!checked_dataSize
.isValid()) {
543 ErrorOutOfMemory("Integer overflow trying to construct a fake vertex attrib 0 array for a draw-operation "
544 "with %d vertices. Try reducing the number of vertices.", vertexCount
);
548 GLuint dataSize
= checked_dataSize
.value();
550 if (!mFakeVertexAttrib0BufferObject
) {
551 gl
->fGenBuffers(1, &mFakeVertexAttrib0BufferObject
);
554 // if the VBO status is already exactly what we need, or if the only difference is that it's initialized and
555 // we don't need it to be, then consider it OK
556 bool vertexAttrib0BufferStatusOK
=
557 mFakeVertexAttrib0BufferStatus
== whatDoesAttrib0Need
||
558 (mFakeVertexAttrib0BufferStatus
== WebGLVertexAttrib0Status::EmulatedInitializedArray
&&
559 whatDoesAttrib0Need
== WebGLVertexAttrib0Status::EmulatedUninitializedArray
);
561 if (!vertexAttrib0BufferStatusOK
||
562 mFakeVertexAttrib0BufferObjectSize
< dataSize
||
563 mFakeVertexAttrib0BufferObjectVector
[0] != mVertexAttrib0Vector
[0] ||
564 mFakeVertexAttrib0BufferObjectVector
[1] != mVertexAttrib0Vector
[1] ||
565 mFakeVertexAttrib0BufferObjectVector
[2] != mVertexAttrib0Vector
[2] ||
566 mFakeVertexAttrib0BufferObjectVector
[3] != mVertexAttrib0Vector
[3])
568 mFakeVertexAttrib0BufferStatus
= whatDoesAttrib0Need
;
569 mFakeVertexAttrib0BufferObjectSize
= dataSize
;
570 mFakeVertexAttrib0BufferObjectVector
[0] = mVertexAttrib0Vector
[0];
571 mFakeVertexAttrib0BufferObjectVector
[1] = mVertexAttrib0Vector
[1];
572 mFakeVertexAttrib0BufferObjectVector
[2] = mVertexAttrib0Vector
[2];
573 mFakeVertexAttrib0BufferObjectVector
[3] = mVertexAttrib0Vector
[3];
575 gl
->fBindBuffer(LOCAL_GL_ARRAY_BUFFER
, mFakeVertexAttrib0BufferObject
);
577 GetAndFlushUnderlyingGLErrors();
579 if (mFakeVertexAttrib0BufferStatus
== WebGLVertexAttrib0Status::EmulatedInitializedArray
) {
580 UniquePtr
<GLfloat
[]> array(new ((fallible_t())) GLfloat
[4 * vertexCount
]);
582 ErrorOutOfMemory("Fake attrib0 array.");
585 for(size_t i
= 0; i
< vertexCount
; ++i
) {
586 array
[4 * i
+ 0] = mVertexAttrib0Vector
[0];
587 array
[4 * i
+ 1] = mVertexAttrib0Vector
[1];
588 array
[4 * i
+ 2] = mVertexAttrib0Vector
[2];
589 array
[4 * i
+ 3] = mVertexAttrib0Vector
[3];
591 gl
->fBufferData(LOCAL_GL_ARRAY_BUFFER
, dataSize
, array
.get(), LOCAL_GL_DYNAMIC_DRAW
);
593 gl
->fBufferData(LOCAL_GL_ARRAY_BUFFER
, dataSize
, nullptr, LOCAL_GL_DYNAMIC_DRAW
);
595 GLenum error
= GetAndFlushUnderlyingGLErrors();
597 gl
->fBindBuffer(LOCAL_GL_ARRAY_BUFFER
, mBoundArrayBuffer
? mBoundArrayBuffer
->GLName() : 0);
599 // note that we do this error checking and early return AFTER having restored the buffer binding above
601 ErrorOutOfMemory("Ran out of memory trying to construct a fake vertex attrib 0 array for a draw-operation "
602 "with %d vertices. Try reducing the number of vertices.", vertexCount
);
607 gl
->fBindBuffer(LOCAL_GL_ARRAY_BUFFER
, mFakeVertexAttrib0BufferObject
);
608 gl
->fVertexAttribPointer(0, 4, LOCAL_GL_FLOAT
, LOCAL_GL_FALSE
, 0, 0);
614 WebGLContext::UndoFakeVertexAttrib0()
616 WebGLVertexAttrib0Status whatDoesAttrib0Need
= WhatDoesVertexAttrib0Need();
618 if (MOZ_LIKELY(whatDoesAttrib0Need
== WebGLVertexAttrib0Status::Default
))
621 if (mBoundVertexArray
->HasAttrib(0) && mBoundVertexArray
->mAttribs
[0].buf
) {
622 const WebGLVertexAttribData
& attrib0
= mBoundVertexArray
->mAttribs
[0];
623 gl
->fBindBuffer(LOCAL_GL_ARRAY_BUFFER
, attrib0
.buf
->GLName());
624 if (attrib0
.integer
) {
625 gl
->fVertexAttribIPointer(0,
629 reinterpret_cast<const GLvoid
*>(attrib0
.byteOffset
));
631 gl
->fVertexAttribPointer(0,
636 reinterpret_cast<const GLvoid
*>(attrib0
.byteOffset
));
639 gl
->fBindBuffer(LOCAL_GL_ARRAY_BUFFER
, 0);
642 gl
->fBindBuffer(LOCAL_GL_ARRAY_BUFFER
, mBoundArrayBuffer
? mBoundArrayBuffer
->GLName() : 0);
645 WebGLContextFakeBlackStatus
646 WebGLContext::ResolvedFakeBlackStatus()
648 // handle this case first, it's the generic case
649 if (MOZ_LIKELY(mFakeBlackStatus
== WebGLContextFakeBlackStatus::NotNeeded
))
650 return mFakeBlackStatus
;
652 if (mFakeBlackStatus
== WebGLContextFakeBlackStatus::Needed
)
653 return mFakeBlackStatus
;
655 for (int32_t i
= 0; i
< mGLMaxTextureUnits
; ++i
) {
656 if ((mBound2DTextures
[i
] && mBound2DTextures
[i
]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded
) ||
657 (mBoundCubeMapTextures
[i
] && mBoundCubeMapTextures
[i
]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded
))
659 mFakeBlackStatus
= WebGLContextFakeBlackStatus::Needed
;
660 return mFakeBlackStatus
;
664 // we have exhausted all cases where we do need fakeblack, so if the status is still unknown,
665 // that means that we do NOT need it.
666 mFakeBlackStatus
= WebGLContextFakeBlackStatus::NotNeeded
;
667 return mFakeBlackStatus
;
671 WebGLContext::BindFakeBlackTexturesHelper(
673 const nsTArray
<WebGLRefPtr
<WebGLTexture
> > & boundTexturesArray
,
674 UniquePtr
<FakeBlackTexture
> & opaqueTextureScopedPtr
,
675 UniquePtr
<FakeBlackTexture
> & transparentTextureScopedPtr
)
677 for (int32_t i
= 0; i
< mGLMaxTextureUnits
; ++i
) {
678 if (!boundTexturesArray
[i
]) {
682 WebGLTextureFakeBlackStatus s
= boundTexturesArray
[i
]->ResolvedFakeBlackStatus();
683 MOZ_ASSERT(s
!= WebGLTextureFakeBlackStatus::Unknown
);
685 if (MOZ_LIKELY(s
== WebGLTextureFakeBlackStatus::NotNeeded
)) {
689 bool alpha
= s
== WebGLTextureFakeBlackStatus::UninitializedImageData
&&
690 FormatHasAlpha(boundTexturesArray
[i
]->ImageInfoBase().EffectiveInternalFormat());
691 UniquePtr
<FakeBlackTexture
>&
692 blackTexturePtr
= alpha
693 ? transparentTextureScopedPtr
694 : opaqueTextureScopedPtr
;
696 if (!blackTexturePtr
) {
697 GLenum format
= alpha
? LOCAL_GL_RGBA
: LOCAL_GL_RGB
;
698 blackTexturePtr
= MakeUnique
<FakeBlackTexture
>(gl
, target
, format
);
701 gl
->fActiveTexture(LOCAL_GL_TEXTURE0
+ i
);
702 gl
->fBindTexture(target
,
703 blackTexturePtr
->GLName());
708 WebGLContext::BindFakeBlackTextures()
710 // this is the generic case: try to return early
711 if (MOZ_LIKELY(ResolvedFakeBlackStatus() == WebGLContextFakeBlackStatus::NotNeeded
))
714 BindFakeBlackTexturesHelper(LOCAL_GL_TEXTURE_2D
,
716 mBlackOpaqueTexture2D
,
717 mBlackTransparentTexture2D
);
718 BindFakeBlackTexturesHelper(LOCAL_GL_TEXTURE_CUBE_MAP
,
719 mBoundCubeMapTextures
,
720 mBlackOpaqueTextureCubeMap
,
721 mBlackTransparentTextureCubeMap
);
725 WebGLContext::UnbindFakeBlackTextures()
727 // this is the generic case: try to return early
728 if (MOZ_LIKELY(ResolvedFakeBlackStatus() == WebGLContextFakeBlackStatus::NotNeeded
))
731 for (int32_t i
= 0; i
< mGLMaxTextureUnits
; ++i
) {
732 if (mBound2DTextures
[i
] && mBound2DTextures
[i
]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded
) {
733 gl
->fActiveTexture(LOCAL_GL_TEXTURE0
+ i
);
734 gl
->fBindTexture(LOCAL_GL_TEXTURE_2D
, mBound2DTextures
[i
]->GLName());
736 if (mBoundCubeMapTextures
[i
] && mBoundCubeMapTextures
[i
]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded
) {
737 gl
->fActiveTexture(LOCAL_GL_TEXTURE0
+ i
);
738 gl
->fBindTexture(LOCAL_GL_TEXTURE_CUBE_MAP
, mBoundCubeMapTextures
[i
]->GLName());
742 gl
->fActiveTexture(LOCAL_GL_TEXTURE0
+ mActiveTexture
);
745 WebGLContext::FakeBlackTexture::FakeBlackTexture(GLContext
* gl
, TexTarget target
, GLenum format
)
749 MOZ_ASSERT(format
== LOCAL_GL_RGB
|| format
== LOCAL_GL_RGBA
);
752 GLuint formerBinding
= 0;
753 gl
->GetUIntegerv(target
== LOCAL_GL_TEXTURE_2D
754 ? LOCAL_GL_TEXTURE_BINDING_2D
755 : LOCAL_GL_TEXTURE_BINDING_CUBE_MAP
,
757 gl
->fGenTextures(1, &mGLName
);
758 gl
->fBindTexture(target
.get(), mGLName
);
760 // we allocate our zeros on the heap, and we overallocate (16 bytes instead of 4)
761 // to minimize the risk of running into a driver bug in texImage2D, as it is
762 // a bit unusual maybe to create 1x1 textures, and the stack may not have the alignment
763 // that texImage2D expects.
764 UniquePtr
<uint8_t> zeros((uint8_t*)moz_xcalloc(1, 16));
765 if (target
== LOCAL_GL_TEXTURE_2D
) {
766 gl
->fTexImage2D(target
.get(), 0, format
, 1, 1,
767 0, format
, LOCAL_GL_UNSIGNED_BYTE
, zeros
.get());
769 for (GLuint i
= 0; i
< 6; ++i
) {
770 gl
->fTexImage2D(LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X
+ i
, 0, format
, 1, 1,
771 0, format
, LOCAL_GL_UNSIGNED_BYTE
, zeros
.get());
775 gl
->fBindTexture(target
.get(), formerBinding
);
778 WebGLContext::FakeBlackTexture::~FakeBlackTexture()
782 mGL
->fDeleteTextures(1, &mGLName
);