Bumping gaia.json for 1 gaia revision(s) a=gaia-bump
[gecko.git] / dom / canvas / WebGLContextDraw.cpp
blobc535e45eb2885aee2ede69a16bfdc8973e6ea932
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();
137 gl->fDrawArrays(mode, first, count);
139 Draw_cleanup();
142 void
143 WebGLContext::DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount)
145 if (IsContextLost())
146 return;
148 if (!ValidateDrawModeEnum(mode, "drawArraysInstanced: mode"))
149 return;
151 if (!DrawArrays_check(first, count, primcount, "drawArraysInstanced"))
152 return;
154 RunContextLossTimer();
155 gl->fDrawArraysInstanced(mode, first, count, primcount);
157 Draw_cleanup();
160 bool
161 WebGLContext::DrawElements_check(GLsizei count, GLenum type,
162 WebGLintptr byteOffset, GLsizei primcount,
163 const char* info, GLuint* out_upperBound)
165 if (count < 0 || byteOffset < 0) {
166 ErrorInvalidValue("%s: negative count or offset", info);
167 return false;
170 if (primcount < 0) {
171 ErrorInvalidValue("%s: negative primcount", info);
172 return false;
175 if (!ValidateStencilParamsForDrawCall()) {
176 return false;
179 // If count is 0, there's nothing to do.
180 if (count == 0 || primcount == 0) {
181 return false;
184 CheckedUint32 checked_byteCount;
186 GLsizei first = 0;
188 if (type == LOCAL_GL_UNSIGNED_SHORT) {
189 checked_byteCount = 2 * CheckedUint32(count);
190 if (byteOffset % 2 != 0) {
191 ErrorInvalidOperation("%s: invalid byteOffset for UNSIGNED_SHORT (must be a multiple of 2)", info);
192 return false;
194 first = byteOffset / 2;
196 else if (type == LOCAL_GL_UNSIGNED_BYTE) {
197 checked_byteCount = count;
198 first = byteOffset;
200 else if (type == LOCAL_GL_UNSIGNED_INT && IsExtensionEnabled(WebGLExtensionID::OES_element_index_uint)) {
201 checked_byteCount = 4 * CheckedUint32(count);
202 if (byteOffset % 4 != 0) {
203 ErrorInvalidOperation("%s: invalid byteOffset for UNSIGNED_INT (must be a multiple of 4)", info);
204 return false;
206 first = byteOffset / 4;
208 else {
209 ErrorInvalidEnum("%s: type must be UNSIGNED_SHORT or UNSIGNED_BYTE", info);
210 return false;
213 if (!checked_byteCount.isValid()) {
214 ErrorInvalidValue("%s: overflow in byteCount", info);
215 return false;
218 // Any checks below this depend on a program being available.
219 if (!mCurrentProgram) {
220 ErrorInvalidOperation("%s: null CURRENT_PROGRAM", info);
221 return false;
224 if (!mBoundVertexArray->mElementArrayBuffer) {
225 ErrorInvalidOperation("%s: must have element array buffer binding", info);
226 return false;
229 WebGLBuffer& elemArrayBuffer = *mBoundVertexArray->mElementArrayBuffer;
231 if (!elemArrayBuffer.ByteLength()) {
232 ErrorInvalidOperation("%s: bound element array buffer doesn't have any data", info);
233 return false;
236 CheckedInt<GLsizei> checked_neededByteCount = checked_byteCount.toChecked<GLsizei>() + byteOffset;
238 if (!checked_neededByteCount.isValid()) {
239 ErrorInvalidOperation("%s: overflow in byteOffset+byteCount", info);
240 return false;
243 if (uint32_t(checked_neededByteCount.value()) > elemArrayBuffer.ByteLength()) {
244 ErrorInvalidOperation("%s: bound element array buffer is too small for given count and offset", info);
245 return false;
248 if (!ValidateBufferFetching(info))
249 return false;
251 if (!mMaxFetchedVertices ||
252 !elemArrayBuffer.Validate(type, mMaxFetchedVertices - 1, first, count, out_upperBound))
254 ErrorInvalidOperation(
255 "%s: bound vertex attribute buffers do not have sufficient "
256 "size for given indices from the bound element array", info);
257 return false;
260 if (uint32_t(primcount) > mMaxFetchedInstances) {
261 ErrorInvalidOperation("%s: bound instance attribute buffers do not have sufficient size for given primcount", info);
262 return false;
265 // Bug 1008310 - Check if buffer has been used with a different previous type
266 if (elemArrayBuffer.IsElementArrayUsedWithMultipleTypes()) {
267 GenerateWarning("%s: bound element array buffer previously used with a type other than "
268 "%s, this will affect performance.",
269 info,
270 WebGLContext::EnumName(type));
273 MakeContextCurrent();
275 if (mBoundFramebuffer) {
276 if (!mBoundFramebuffer->CheckAndInitializeAttachments()) {
277 ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info);
278 return false;
280 } else {
281 ClearBackbufferIfNeeded();
284 if (!DoFakeVertexAttrib0(mMaxFetchedVertices)) {
285 return false;
288 if (!DrawInstanced_check(info)) {
289 return false;
292 BindFakeBlackTextures();
294 return true;
297 void
298 WebGLContext::DrawElements(GLenum mode, GLsizei count, GLenum type,
299 WebGLintptr byteOffset)
301 if (IsContextLost())
302 return;
304 if (!ValidateDrawModeEnum(mode, "drawElements: mode"))
305 return;
307 GLuint upperBound = 0;
308 if (!DrawElements_check(count, type, byteOffset, 1, "drawElements",
309 &upperBound))
311 return;
314 RunContextLossTimer();
316 if (gl->IsSupported(gl::GLFeature::draw_range_elements)) {
317 gl->fDrawRangeElements(mode, 0, upperBound,
318 count, type, reinterpret_cast<GLvoid*>(byteOffset));
319 } else {
320 gl->fDrawElements(mode, count, type, reinterpret_cast<GLvoid*>(byteOffset));
323 Draw_cleanup();
326 void
327 WebGLContext::DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type,
328 WebGLintptr byteOffset, GLsizei primcount)
330 if (IsContextLost())
331 return;
333 if (!ValidateDrawModeEnum(mode, "drawElementsInstanced: mode"))
334 return;
336 GLuint upperBound = 0;
337 if (!DrawElements_check(count, type, byteOffset, primcount, "drawElementsInstanced",
338 &upperBound))
339 return;
341 RunContextLossTimer();
342 gl->fDrawElementsInstanced(mode, count, type, reinterpret_cast<GLvoid*>(byteOffset), primcount);
344 Draw_cleanup();
347 void WebGLContext::Draw_cleanup()
349 UndoFakeVertexAttrib0();
350 UnbindFakeBlackTextures();
352 if (!mBoundFramebuffer) {
353 Invalidate();
354 mShouldPresent = true;
355 MOZ_ASSERT(!mBackbufferNeedsClear);
358 if (gl->WorkAroundDriverBugs()) {
359 if (gl->Renderer() == gl::GLRenderer::Tegra) {
360 mDrawCallsSinceLastFlush++;
362 if (mDrawCallsSinceLastFlush >= MAX_DRAW_CALLS_SINCE_FLUSH) {
363 gl->fFlush();
364 mDrawCallsSinceLastFlush = 0;
369 // Let's check the viewport
370 const WebGLRectangleObject* rect = CurValidFBRectObject();
371 if (rect) {
372 if (mViewportWidth > rect->Width() ||
373 mViewportHeight > rect->Height())
375 if (!mAlreadyWarnedAboutViewportLargerThanDest) {
376 GenerateWarning("Drawing to a destination rect smaller than the viewport rect. "
377 "(This warning will only be given once)");
378 mAlreadyWarnedAboutViewportLargerThanDest = true;
385 * Verify that state is consistent for drawing, and compute max number of elements (maxAllowedCount)
386 * that will be legal to be read from bound VBOs.
389 bool
390 WebGLContext::ValidateBufferFetching(const char *info)
392 #ifdef DEBUG
393 GLint currentProgram = 0;
394 MakeContextCurrent();
395 gl->fGetIntegerv(LOCAL_GL_CURRENT_PROGRAM, &currentProgram);
396 MOZ_ASSERT(GLuint(currentProgram) == mCurrentProgram->GLName(),
397 "WebGL: current program doesn't agree with GL state");
398 #endif
400 if (mBufferFetchingIsVerified) {
401 return true;
404 bool hasPerVertex = false;
405 uint32_t maxVertices = UINT32_MAX;
406 uint32_t maxInstances = UINT32_MAX;
407 uint32_t attribs = mBoundVertexArray->mAttribs.Length();
409 for (uint32_t i = 0; i < attribs; ++i) {
410 const WebGLVertexAttribData& vd = mBoundVertexArray->mAttribs[i];
412 // If the attrib array isn't enabled, there's nothing to check;
413 // it's a static value.
414 if (!vd.enabled)
415 continue;
417 if (vd.buf == nullptr) {
418 ErrorInvalidOperation("%s: no VBO bound to enabled vertex attrib index %d!", info, i);
419 return false;
422 // If the attrib is not in use, then we don't have to validate
423 // it, just need to make sure that the binding is non-null.
424 if (!mCurrentProgram->IsAttribInUse(i))
425 continue;
427 // the base offset
428 CheckedUint32 checked_byteLength = CheckedUint32(vd.buf->ByteLength()) - vd.byteOffset;
429 CheckedUint32 checked_sizeOfLastElement = CheckedUint32(vd.componentSize()) * vd.size;
431 if (!checked_byteLength.isValid() ||
432 !checked_sizeOfLastElement.isValid())
434 ErrorInvalidOperation("%s: integer overflow occured while checking vertex attrib %d", info, i);
435 return false;
438 if (checked_byteLength.value() < checked_sizeOfLastElement.value()) {
439 maxVertices = 0;
440 maxInstances = 0;
441 break;
444 CheckedUint32 checked_maxAllowedCount = ((checked_byteLength - checked_sizeOfLastElement) / vd.actualStride()) + 1;
446 if (!checked_maxAllowedCount.isValid()) {
447 ErrorInvalidOperation("%s: integer overflow occured while checking vertex attrib %d", info, i);
448 return false;
451 if (vd.divisor == 0) {
452 maxVertices = std::min(maxVertices, checked_maxAllowedCount.value());
453 hasPerVertex = true;
454 } else {
455 CheckedUint32 checked_curMaxInstances = checked_maxAllowedCount * vd.divisor;
457 uint32_t curMaxInstances = UINT32_MAX;
458 // If this isn't valid, it's because we overflowed our
459 // uint32 above. Just leave this as UINT32_MAX, since
460 // sizeof(uint32) becomes our limiting factor.
461 if (checked_curMaxInstances.isValid()) {
462 curMaxInstances = checked_curMaxInstances.value();
465 maxInstances = std::min(maxInstances, curMaxInstances);
469 mBufferFetchingIsVerified = true;
470 mBufferFetchingHasPerVertex = hasPerVertex;
471 mMaxFetchedVertices = maxVertices;
472 mMaxFetchedInstances = maxInstances;
474 return true;
477 WebGLVertexAttrib0Status
478 WebGLContext::WhatDoesVertexAttrib0Need()
480 MOZ_ASSERT(mCurrentProgram);
482 // work around Mac OSX crash, see bug 631420
483 #ifdef XP_MACOSX
484 if (gl->WorkAroundDriverBugs() &&
485 mBoundVertexArray->IsAttribArrayEnabled(0) &&
486 !mCurrentProgram->IsAttribInUse(0))
488 return WebGLVertexAttrib0Status::EmulatedUninitializedArray;
490 #endif
492 if (MOZ_LIKELY(gl->IsGLES() ||
493 mBoundVertexArray->IsAttribArrayEnabled(0)))
495 return WebGLVertexAttrib0Status::Default;
498 return mCurrentProgram->IsAttribInUse(0)
499 ? WebGLVertexAttrib0Status::EmulatedInitializedArray
500 : WebGLVertexAttrib0Status::EmulatedUninitializedArray;
503 bool
504 WebGLContext::DoFakeVertexAttrib0(GLuint vertexCount)
506 WebGLVertexAttrib0Status whatDoesAttrib0Need = WhatDoesVertexAttrib0Need();
508 if (MOZ_LIKELY(whatDoesAttrib0Need == WebGLVertexAttrib0Status::Default))
509 return true;
511 if (!mAlreadyWarnedAboutFakeVertexAttrib0) {
512 GenerateWarning("Drawing without vertex attrib 0 array enabled forces the browser "
513 "to do expensive emulation work when running on desktop OpenGL "
514 "platforms, for example on Mac. It is preferable to always draw "
515 "with vertex attrib 0 array enabled, by using bindAttribLocation "
516 "to bind some always-used attribute to location 0.");
517 mAlreadyWarnedAboutFakeVertexAttrib0 = true;
520 CheckedUint32 checked_dataSize = CheckedUint32(vertexCount) * 4 * sizeof(GLfloat);
522 if (!checked_dataSize.isValid()) {
523 ErrorOutOfMemory("Integer overflow trying to construct a fake vertex attrib 0 array for a draw-operation "
524 "with %d vertices. Try reducing the number of vertices.", vertexCount);
525 return false;
528 GLuint dataSize = checked_dataSize.value();
530 if (!mFakeVertexAttrib0BufferObject) {
531 gl->fGenBuffers(1, &mFakeVertexAttrib0BufferObject);
534 // if the VBO status is already exactly what we need, or if the only difference is that it's initialized and
535 // we don't need it to be, then consider it OK
536 bool vertexAttrib0BufferStatusOK =
537 mFakeVertexAttrib0BufferStatus == whatDoesAttrib0Need ||
538 (mFakeVertexAttrib0BufferStatus == WebGLVertexAttrib0Status::EmulatedInitializedArray &&
539 whatDoesAttrib0Need == WebGLVertexAttrib0Status::EmulatedUninitializedArray);
541 if (!vertexAttrib0BufferStatusOK ||
542 mFakeVertexAttrib0BufferObjectSize < dataSize ||
543 mFakeVertexAttrib0BufferObjectVector[0] != mVertexAttrib0Vector[0] ||
544 mFakeVertexAttrib0BufferObjectVector[1] != mVertexAttrib0Vector[1] ||
545 mFakeVertexAttrib0BufferObjectVector[2] != mVertexAttrib0Vector[2] ||
546 mFakeVertexAttrib0BufferObjectVector[3] != mVertexAttrib0Vector[3])
548 mFakeVertexAttrib0BufferStatus = whatDoesAttrib0Need;
549 mFakeVertexAttrib0BufferObjectSize = dataSize;
550 mFakeVertexAttrib0BufferObjectVector[0] = mVertexAttrib0Vector[0];
551 mFakeVertexAttrib0BufferObjectVector[1] = mVertexAttrib0Vector[1];
552 mFakeVertexAttrib0BufferObjectVector[2] = mVertexAttrib0Vector[2];
553 mFakeVertexAttrib0BufferObjectVector[3] = mVertexAttrib0Vector[3];
555 gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mFakeVertexAttrib0BufferObject);
557 GetAndFlushUnderlyingGLErrors();
559 if (mFakeVertexAttrib0BufferStatus == WebGLVertexAttrib0Status::EmulatedInitializedArray) {
560 UniquePtr<GLfloat[]> array(new ((fallible_t())) GLfloat[4 * vertexCount]);
561 if (!array) {
562 ErrorOutOfMemory("Fake attrib0 array.");
563 return false;
565 for(size_t i = 0; i < vertexCount; ++i) {
566 array[4 * i + 0] = mVertexAttrib0Vector[0];
567 array[4 * i + 1] = mVertexAttrib0Vector[1];
568 array[4 * i + 2] = mVertexAttrib0Vector[2];
569 array[4 * i + 3] = mVertexAttrib0Vector[3];
571 gl->fBufferData(LOCAL_GL_ARRAY_BUFFER, dataSize, array.get(), LOCAL_GL_DYNAMIC_DRAW);
572 } else {
573 gl->fBufferData(LOCAL_GL_ARRAY_BUFFER, dataSize, nullptr, LOCAL_GL_DYNAMIC_DRAW);
575 GLenum error = GetAndFlushUnderlyingGLErrors();
577 gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mBoundArrayBuffer ? mBoundArrayBuffer->GLName() : 0);
579 // note that we do this error checking and early return AFTER having restored the buffer binding above
580 if (error) {
581 ErrorOutOfMemory("Ran out of memory trying to construct a fake vertex attrib 0 array for a draw-operation "
582 "with %d vertices. Try reducing the number of vertices.", vertexCount);
583 return false;
587 gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mFakeVertexAttrib0BufferObject);
588 gl->fVertexAttribPointer(0, 4, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, 0);
590 return true;
593 void
594 WebGLContext::UndoFakeVertexAttrib0()
596 WebGLVertexAttrib0Status whatDoesAttrib0Need = WhatDoesVertexAttrib0Need();
598 if (MOZ_LIKELY(whatDoesAttrib0Need == WebGLVertexAttrib0Status::Default))
599 return;
601 if (mBoundVertexArray->HasAttrib(0) && mBoundVertexArray->mAttribs[0].buf) {
602 const WebGLVertexAttribData& attrib0 = mBoundVertexArray->mAttribs[0];
603 gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, attrib0.buf->GLName());
604 gl->fVertexAttribPointer(0,
605 attrib0.size,
606 attrib0.type,
607 attrib0.normalized,
608 attrib0.stride,
609 reinterpret_cast<const GLvoid *>(attrib0.byteOffset));
610 } else {
611 gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
614 gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mBoundArrayBuffer ? mBoundArrayBuffer->GLName() : 0);
617 WebGLContextFakeBlackStatus
618 WebGLContext::ResolvedFakeBlackStatus()
620 // handle this case first, it's the generic case
621 if (MOZ_LIKELY(mFakeBlackStatus == WebGLContextFakeBlackStatus::NotNeeded))
622 return mFakeBlackStatus;
624 if (mFakeBlackStatus == WebGLContextFakeBlackStatus::Needed)
625 return mFakeBlackStatus;
627 for (int32_t i = 0; i < mGLMaxTextureUnits; ++i) {
628 if ((mBound2DTextures[i] && mBound2DTextures[i]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded) ||
629 (mBoundCubeMapTextures[i] && mBoundCubeMapTextures[i]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded))
631 mFakeBlackStatus = WebGLContextFakeBlackStatus::Needed;
632 return mFakeBlackStatus;
636 // we have exhausted all cases where we do need fakeblack, so if the status is still unknown,
637 // that means that we do NOT need it.
638 mFakeBlackStatus = WebGLContextFakeBlackStatus::NotNeeded;
639 return mFakeBlackStatus;
642 void
643 WebGLContext::BindFakeBlackTexturesHelper(
644 GLenum target,
645 const nsTArray<WebGLRefPtr<WebGLTexture> > & boundTexturesArray,
646 UniquePtr<FakeBlackTexture> & opaqueTextureScopedPtr,
647 UniquePtr<FakeBlackTexture> & transparentTextureScopedPtr)
649 for (int32_t i = 0; i < mGLMaxTextureUnits; ++i) {
650 if (!boundTexturesArray[i]) {
651 continue;
654 WebGLTextureFakeBlackStatus s = boundTexturesArray[i]->ResolvedFakeBlackStatus();
655 MOZ_ASSERT(s != WebGLTextureFakeBlackStatus::Unknown);
657 if (MOZ_LIKELY(s == WebGLTextureFakeBlackStatus::NotNeeded)) {
658 continue;
661 bool alpha = s == WebGLTextureFakeBlackStatus::UninitializedImageData &&
662 FormatHasAlpha(boundTexturesArray[i]->ImageInfoBase().WebGLFormat());
663 UniquePtr<FakeBlackTexture>&
664 blackTexturePtr = alpha
665 ? transparentTextureScopedPtr
666 : opaqueTextureScopedPtr;
668 if (!blackTexturePtr) {
669 GLenum format = alpha ? LOCAL_GL_RGBA : LOCAL_GL_RGB;
670 blackTexturePtr = MakeUnique<FakeBlackTexture>(gl, target, format);
673 gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
674 gl->fBindTexture(target,
675 blackTexturePtr->GLName());
679 void
680 WebGLContext::BindFakeBlackTextures()
682 // this is the generic case: try to return early
683 if (MOZ_LIKELY(ResolvedFakeBlackStatus() == WebGLContextFakeBlackStatus::NotNeeded))
684 return;
686 BindFakeBlackTexturesHelper(LOCAL_GL_TEXTURE_2D,
687 mBound2DTextures,
688 mBlackOpaqueTexture2D,
689 mBlackTransparentTexture2D);
690 BindFakeBlackTexturesHelper(LOCAL_GL_TEXTURE_CUBE_MAP,
691 mBoundCubeMapTextures,
692 mBlackOpaqueTextureCubeMap,
693 mBlackTransparentTextureCubeMap);
696 void
697 WebGLContext::UnbindFakeBlackTextures()
699 // this is the generic case: try to return early
700 if (MOZ_LIKELY(ResolvedFakeBlackStatus() == WebGLContextFakeBlackStatus::NotNeeded))
701 return;
703 for (int32_t i = 0; i < mGLMaxTextureUnits; ++i) {
704 if (mBound2DTextures[i] && mBound2DTextures[i]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded) {
705 gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
706 gl->fBindTexture(LOCAL_GL_TEXTURE_2D, mBound2DTextures[i]->GLName());
708 if (mBoundCubeMapTextures[i] && mBoundCubeMapTextures[i]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded) {
709 gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
710 gl->fBindTexture(LOCAL_GL_TEXTURE_CUBE_MAP, mBoundCubeMapTextures[i]->GLName());
714 gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mActiveTexture);
717 WebGLContext::FakeBlackTexture::FakeBlackTexture(GLContext *gl, GLenum target, GLenum format)
718 : mGL(gl)
719 , mGLName(0)
721 MOZ_ASSERT(target == LOCAL_GL_TEXTURE_2D || target == LOCAL_GL_TEXTURE_CUBE_MAP);
722 MOZ_ASSERT(format == LOCAL_GL_RGB || format == LOCAL_GL_RGBA);
724 mGL->MakeCurrent();
725 GLuint formerBinding = 0;
726 gl->GetUIntegerv(target == LOCAL_GL_TEXTURE_2D
727 ? LOCAL_GL_TEXTURE_BINDING_2D
728 : LOCAL_GL_TEXTURE_BINDING_CUBE_MAP,
729 &formerBinding);
730 gl->fGenTextures(1, &mGLName);
731 gl->fBindTexture(target, mGLName);
733 // we allocate our zeros on the heap, and we overallocate (16 bytes instead of 4)
734 // to minimize the risk of running into a driver bug in texImage2D, as it is
735 // a bit unusual maybe to create 1x1 textures, and the stack may not have the alignment
736 // that texImage2D expects.
737 UniquePtr<uint8_t> zeros((uint8_t*)moz_xcalloc(1, 16));
738 if (target == LOCAL_GL_TEXTURE_2D) {
739 gl->fTexImage2D(target, 0, format, 1, 1,
740 0, format, LOCAL_GL_UNSIGNED_BYTE, zeros.get());
741 } else {
742 for (GLuint i = 0; i < 6; ++i) {
743 gl->fTexImage2D(LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, format, 1, 1,
744 0, format, LOCAL_GL_UNSIGNED_BYTE, zeros.get());
748 gl->fBindTexture(target, formerBinding);
751 WebGLContext::FakeBlackTexture::~FakeBlackTexture()
753 if (mGL) {
754 mGL->MakeCurrent();
755 mGL->fDeleteTextures(1, &mGLName);