Bumping manifests a=b2g-bump
[gecko.git] / dom / canvas / WebGLContextDraw.cpp
blob30a70593d40b3c955c0cc96bb05ad3e205d3d547
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"
8 #include "GLContext.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;
28 bool
29 WebGLContext::DrawInstanced_check(const char* info)
31 // This restriction was removed in GLES3, so WebGL2 shouldn't have it.
32 if (!IsWebGL2() &&
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);
46 return false;
49 return true;
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);
56 return false;
59 if (primcount < 0) {
60 ErrorInvalidValue("%s: negative primcount", info);
61 return false;
64 if (!ValidateStencilParamsForDrawCall()) {
65 return false;
68 // If count is 0, there's nothing to do.
69 if (count == 0 || primcount == 0) {
70 return false;
73 // Any checks below this depend on a program being available.
74 if (!mCurrentProgram) {
75 ErrorInvalidOperation("%s: null CURRENT_PROGRAM", info);
76 return false;
79 if (!ValidateBufferFetching(info)) {
80 return false;
83 CheckedInt<GLsizei> checked_firstPlusCount = CheckedInt<GLsizei>(first) + count;
85 if (!checked_firstPlusCount.isValid()) {
86 ErrorInvalidOperation("%s: overflow in first+count", info);
87 return false;
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);
92 return false;
95 if (uint32_t(primcount) > mMaxFetchedInstances) {
96 ErrorInvalidOperation("%s: bound instance attribute buffers do not have sufficient size for given primcount", info);
97 return false;
100 MakeContextCurrent();
102 if (mBoundFramebuffer) {
103 if (!mBoundFramebuffer->CheckAndInitializeAttachments()) {
104 ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info);
105 return false;
107 } else {
108 ClearBackbufferIfNeeded();
111 if (!DoFakeVertexAttrib0(checked_firstPlusCount.value())) {
112 return false;
115 if (!DrawInstanced_check(info)) {
116 return false;
119 BindFakeBlackTextures();
121 return true;
124 void
125 WebGLContext::DrawArrays(GLenum mode, GLint first, GLsizei count)
127 if (IsContextLost())
128 return;
130 if (!ValidateDrawModeEnum(mode, "drawArrays: mode"))
131 return;
133 if (!DrawArrays_check(first, count, 1, "drawArrays"))
134 return;
136 RunContextLossTimer();
139 ScopedMaskWorkaround autoMask(*this);
140 gl->fDrawArrays(mode, first, count);
143 Draw_cleanup();
146 void
147 WebGLContext::DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount)
149 if (IsContextLost())
150 return;
152 if (!ValidateDrawModeEnum(mode, "drawArraysInstanced: mode"))
153 return;
155 if (!DrawArrays_check(first, count, primcount, "drawArraysInstanced"))
156 return;
158 RunContextLossTimer();
161 ScopedMaskWorkaround autoMask(*this);
162 gl->fDrawArraysInstanced(mode, first, count, primcount);
165 Draw_cleanup();
168 bool
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);
175 return false;
178 if (primcount < 0) {
179 ErrorInvalidValue("%s: negative primcount", info);
180 return false;
183 if (!ValidateStencilParamsForDrawCall()) {
184 return false;
187 // If count is 0, there's nothing to do.
188 if (count == 0 || primcount == 0) {
189 return false;
192 CheckedUint32 checked_byteCount;
194 GLsizei first = 0;
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);
200 return false;
202 first = byteOffset / 2;
204 else if (type == LOCAL_GL_UNSIGNED_BYTE) {
205 checked_byteCount = count;
206 first = byteOffset;
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);
212 return false;
214 first = byteOffset / 4;
216 else {
217 ErrorInvalidEnum("%s: type must be UNSIGNED_SHORT or UNSIGNED_BYTE", info);
218 return false;
221 if (!checked_byteCount.isValid()) {
222 ErrorInvalidValue("%s: overflow in byteCount", info);
223 return false;
226 // Any checks below this depend on a program being available.
227 if (!mCurrentProgram) {
228 ErrorInvalidOperation("%s: null CURRENT_PROGRAM", info);
229 return false;
232 if (!mBoundVertexArray->mElementArrayBuffer) {
233 ErrorInvalidOperation("%s: must have element array buffer binding", info);
234 return false;
237 WebGLBuffer& elemArrayBuffer = *mBoundVertexArray->mElementArrayBuffer;
239 if (!elemArrayBuffer.ByteLength()) {
240 ErrorInvalidOperation("%s: bound element array buffer doesn't have any data", info);
241 return false;
244 CheckedInt<GLsizei> checked_neededByteCount = checked_byteCount.toChecked<GLsizei>() + byteOffset;
246 if (!checked_neededByteCount.isValid()) {
247 ErrorInvalidOperation("%s: overflow in byteOffset+byteCount", info);
248 return false;
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);
253 return false;
256 if (!ValidateBufferFetching(info))
257 return false;
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);
265 return false;
268 if (uint32_t(primcount) > mMaxFetchedInstances) {
269 ErrorInvalidOperation("%s: bound instance attribute buffers do not have sufficient size for given primcount", info);
270 return false;
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.",
277 info,
278 WebGLContext::EnumName(type));
281 MakeContextCurrent();
283 if (mBoundFramebuffer) {
284 if (!mBoundFramebuffer->CheckAndInitializeAttachments()) {
285 ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info);
286 return false;
288 } else {
289 ClearBackbufferIfNeeded();
292 if (!DoFakeVertexAttrib0(mMaxFetchedVertices)) {
293 return false;
296 if (!DrawInstanced_check(info)) {
297 return false;
300 BindFakeBlackTextures();
302 return true;
305 void
306 WebGLContext::DrawElements(GLenum mode, GLsizei count, GLenum type,
307 WebGLintptr byteOffset)
309 if (IsContextLost())
310 return;
312 if (!ValidateDrawModeEnum(mode, "drawElements: mode"))
313 return;
315 GLuint upperBound = 0;
316 if (!DrawElements_check(count, type, byteOffset, 1, "drawElements",
317 &upperBound))
319 return;
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));
330 } else {
331 gl->fDrawElements(mode, count, type,
332 reinterpret_cast<GLvoid*>(byteOffset));
336 Draw_cleanup();
339 void
340 WebGLContext::DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type,
341 WebGLintptr byteOffset, GLsizei primcount)
343 if (IsContextLost())
344 return;
346 if (!ValidateDrawModeEnum(mode, "drawElementsInstanced: mode"))
347 return;
349 GLuint upperBound = 0;
350 if (!DrawElements_check(count, type, byteOffset, primcount,
351 "drawElementsInstanced", &upperBound))
353 return;
356 RunContextLossTimer();
359 ScopedMaskWorkaround autoMask(*this);
360 gl->fDrawElementsInstanced(mode, count, type,
361 reinterpret_cast<GLvoid*>(byteOffset),
362 primcount);
365 Draw_cleanup();
368 void WebGLContext::Draw_cleanup()
370 UndoFakeVertexAttrib0();
371 UnbindFakeBlackTextures();
373 if (!mBoundFramebuffer) {
374 Invalidate();
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) {
384 gl->fFlush();
385 mDrawCallsSinceLastFlush = 0;
390 // Let's check the viewport
391 const WebGLRectangleObject* rect = CurValidFBRectObject();
392 if (rect) {
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.
410 bool
411 WebGLContext::ValidateBufferFetching(const char* info)
413 #ifdef DEBUG
414 GLint currentProgram = 0;
415 MakeContextCurrent();
416 gl->fGetIntegerv(LOCAL_GL_CURRENT_PROGRAM, &currentProgram);
417 MOZ_ASSERT(GLuint(currentProgram) == mCurrentProgram->GLName(),
418 "WebGL: current program doesn't agree with GL state");
419 #endif
421 if (mBufferFetchingIsVerified)
422 return true;
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.
434 if (!vd.enabled)
435 continue;
437 if (vd.buf == nullptr) {
438 ErrorInvalidOperation("%s: no VBO bound to enabled vertex attrib index %d!", info, i);
439 return false;
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))
445 continue;
447 // the base offset
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);
455 return false;
458 if (checked_byteLength.value() < checked_sizeOfLastElement.value()) {
459 maxVertices = 0;
460 maxInstances = 0;
461 break;
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);
468 return false;
471 if (vd.divisor == 0) {
472 maxVertices = std::min(maxVertices, checked_maxAllowedCount.value());
473 hasPerVertex = true;
474 } else {
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;
494 return true;
497 WebGLVertexAttrib0Status
498 WebGLContext::WhatDoesVertexAttrib0Need()
500 MOZ_ASSERT(mCurrentProgram);
502 // work around Mac OSX crash, see bug 631420
503 #ifdef XP_MACOSX
504 if (gl->WorkAroundDriverBugs() &&
505 mBoundVertexArray->IsAttribArrayEnabled(0) &&
506 !mCurrentProgram->IsAttribInUse(0))
508 return WebGLVertexAttrib0Status::EmulatedUninitializedArray;
510 #endif
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;
523 bool
524 WebGLContext::DoFakeVertexAttrib0(GLuint vertexCount)
526 WebGLVertexAttrib0Status whatDoesAttrib0Need = WhatDoesVertexAttrib0Need();
528 if (MOZ_LIKELY(whatDoesAttrib0Need == WebGLVertexAttrib0Status::Default))
529 return true;
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);
545 return false;
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]);
581 if (!array) {
582 ErrorOutOfMemory("Fake attrib0 array.");
583 return false;
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);
592 } else {
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
600 if (error) {
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);
603 return false;
607 gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mFakeVertexAttrib0BufferObject);
608 gl->fVertexAttribPointer(0, 4, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, 0);
610 return true;
613 void
614 WebGLContext::UndoFakeVertexAttrib0()
616 WebGLVertexAttrib0Status whatDoesAttrib0Need = WhatDoesVertexAttrib0Need();
618 if (MOZ_LIKELY(whatDoesAttrib0Need == WebGLVertexAttrib0Status::Default))
619 return;
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,
626 attrib0.size,
627 attrib0.type,
628 attrib0.stride,
629 reinterpret_cast<const GLvoid*>(attrib0.byteOffset));
630 } else {
631 gl->fVertexAttribPointer(0,
632 attrib0.size,
633 attrib0.type,
634 attrib0.normalized,
635 attrib0.stride,
636 reinterpret_cast<const GLvoid*>(attrib0.byteOffset));
638 } else {
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;
670 void
671 WebGLContext::BindFakeBlackTexturesHelper(
672 GLenum target,
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]) {
679 continue;
682 WebGLTextureFakeBlackStatus s = boundTexturesArray[i]->ResolvedFakeBlackStatus();
683 MOZ_ASSERT(s != WebGLTextureFakeBlackStatus::Unknown);
685 if (MOZ_LIKELY(s == WebGLTextureFakeBlackStatus::NotNeeded)) {
686 continue;
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());
707 void
708 WebGLContext::BindFakeBlackTextures()
710 // this is the generic case: try to return early
711 if (MOZ_LIKELY(ResolvedFakeBlackStatus() == WebGLContextFakeBlackStatus::NotNeeded))
712 return;
714 BindFakeBlackTexturesHelper(LOCAL_GL_TEXTURE_2D,
715 mBound2DTextures,
716 mBlackOpaqueTexture2D,
717 mBlackTransparentTexture2D);
718 BindFakeBlackTexturesHelper(LOCAL_GL_TEXTURE_CUBE_MAP,
719 mBoundCubeMapTextures,
720 mBlackOpaqueTextureCubeMap,
721 mBlackTransparentTextureCubeMap);
724 void
725 WebGLContext::UnbindFakeBlackTextures()
727 // this is the generic case: try to return early
728 if (MOZ_LIKELY(ResolvedFakeBlackStatus() == WebGLContextFakeBlackStatus::NotNeeded))
729 return;
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)
746 : mGL(gl)
747 , mGLName(0)
749 MOZ_ASSERT(format == LOCAL_GL_RGB || format == LOCAL_GL_RGBA);
751 mGL->MakeCurrent();
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,
756 &formerBinding);
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());
768 } else {
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()
780 if (mGL) {
781 mGL->MakeCurrent();
782 mGL->fDeleteTextures(1, &mGLName);