Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
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)
30 {
31 // This restriction was removed in GLES3, so WebGL2 shouldn't have it.
32 if (!IsWebGL2() &&
33 IsExtensionEnabled(WebGLExtensionID::ANGLE_instanced_arrays) &&
34 !mBufferFetchingHasPerVertex)
35 {
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.
40 *
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.
44 */
45 ErrorInvalidOperation("%s: at least one vertex attribute divisor should be 0", info);
46 return false;
47 }
49 return true;
50 }
52 bool WebGLContext::DrawArrays_check(GLint first, GLsizei count, GLsizei primcount, const char* info)
53 {
54 if (first < 0 || count < 0) {
55 ErrorInvalidValue("%s: negative first or count", info);
56 return false;
57 }
59 if (primcount < 0) {
60 ErrorInvalidValue("%s: negative primcount", info);
61 return false;
62 }
64 if (!ValidateStencilParamsForDrawCall()) {
65 return false;
66 }
68 // If count is 0, there's nothing to do.
69 if (count == 0 || primcount == 0) {
70 return false;
71 }
73 // Any checks below this depend on a program being available.
74 if (!mCurrentProgram) {
75 ErrorInvalidOperation("%s: null CURRENT_PROGRAM", info);
76 return false;
77 }
79 if (!ValidateBufferFetching(info)) {
80 return false;
81 }
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;
88 }
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;
93 }
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;
98 }
100 MakeContextCurrent();
102 if (mBoundFramebuffer) {
103 if (!mBoundFramebuffer->CheckAndInitializeAttachments()) {
104 ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info);
105 return false;
106 }
107 } else {
108 ClearBackbufferIfNeeded();
109 }
111 if (!DoFakeVertexAttrib0(checked_firstPlusCount.value())) {
112 return false;
113 }
114 BindFakeBlackTextures();
116 return true;
117 }
119 void
120 WebGLContext::DrawArrays(GLenum mode, GLint first, GLsizei count)
121 {
122 if (IsContextLost())
123 return;
125 if (!ValidateDrawModeEnum(mode, "drawArrays: mode"))
126 return;
128 if (!DrawArrays_check(first, count, 1, "drawArrays"))
129 return;
131 SetupContextLossTimer();
132 gl->fDrawArrays(mode, first, count);
134 Draw_cleanup();
135 }
137 void
138 WebGLContext::DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount)
139 {
140 if (IsContextLost())
141 return;
143 if (!ValidateDrawModeEnum(mode, "drawArraysInstanced: mode"))
144 return;
146 if (!DrawArrays_check(first, count, primcount, "drawArraysInstanced"))
147 return;
149 if (!DrawInstanced_check("drawArraysInstanced"))
150 return;
152 SetupContextLossTimer();
153 gl->fDrawArraysInstanced(mode, first, count, primcount);
155 Draw_cleanup();
156 }
158 bool
159 WebGLContext::DrawElements_check(GLsizei count, GLenum type,
160 WebGLintptr byteOffset, GLsizei primcount,
161 const char* info, GLuint* out_upperBound)
162 {
163 if (count < 0 || byteOffset < 0) {
164 ErrorInvalidValue("%s: negative count or offset", info);
165 return false;
166 }
168 if (primcount < 0) {
169 ErrorInvalidValue("%s: negative primcount", info);
170 return false;
171 }
173 if (!ValidateStencilParamsForDrawCall()) {
174 return false;
175 }
177 // If count is 0, there's nothing to do.
178 if (count == 0 || primcount == 0) {
179 return false;
180 }
182 CheckedUint32 checked_byteCount;
184 GLsizei first = 0;
186 if (type == LOCAL_GL_UNSIGNED_SHORT) {
187 checked_byteCount = 2 * CheckedUint32(count);
188 if (byteOffset % 2 != 0) {
189 ErrorInvalidOperation("%s: invalid byteOffset for UNSIGNED_SHORT (must be a multiple of 2)", info);
190 return false;
191 }
192 first = byteOffset / 2;
193 }
194 else if (type == LOCAL_GL_UNSIGNED_BYTE) {
195 checked_byteCount = count;
196 first = byteOffset;
197 }
198 else if (type == LOCAL_GL_UNSIGNED_INT && IsExtensionEnabled(WebGLExtensionID::OES_element_index_uint)) {
199 checked_byteCount = 4 * CheckedUint32(count);
200 if (byteOffset % 4 != 0) {
201 ErrorInvalidOperation("%s: invalid byteOffset for UNSIGNED_INT (must be a multiple of 4)", info);
202 return false;
203 }
204 first = byteOffset / 4;
205 }
206 else {
207 ErrorInvalidEnum("%s: type must be UNSIGNED_SHORT or UNSIGNED_BYTE", info);
208 return false;
209 }
211 if (!checked_byteCount.isValid()) {
212 ErrorInvalidValue("%s: overflow in byteCount", info);
213 return false;
214 }
216 // Any checks below this depend on a program being available.
217 if (!mCurrentProgram) {
218 ErrorInvalidOperation("%s: null CURRENT_PROGRAM", info);
219 return false;
220 }
222 if (!mBoundVertexArray->mBoundElementArrayBuffer) {
223 ErrorInvalidOperation("%s: must have element array buffer binding", info);
224 return false;
225 }
227 WebGLBuffer& elemArrayBuffer = *mBoundVertexArray->mBoundElementArrayBuffer;
229 if (!elemArrayBuffer.ByteLength()) {
230 ErrorInvalidOperation("%s: bound element array buffer doesn't have any data", info);
231 return false;
232 }
234 CheckedInt<GLsizei> checked_neededByteCount = checked_byteCount.toChecked<GLsizei>() + byteOffset;
236 if (!checked_neededByteCount.isValid()) {
237 ErrorInvalidOperation("%s: overflow in byteOffset+byteCount", info);
238 return false;
239 }
241 if (uint32_t(checked_neededByteCount.value()) > elemArrayBuffer.ByteLength()) {
242 ErrorInvalidOperation("%s: bound element array buffer is too small for given count and offset", info);
243 return false;
244 }
246 if (!ValidateBufferFetching(info))
247 return false;
249 if (!mMaxFetchedVertices ||
250 !elemArrayBuffer.Validate(type, mMaxFetchedVertices - 1, first, count, out_upperBound))
251 {
252 ErrorInvalidOperation(
253 "%s: bound vertex attribute buffers do not have sufficient "
254 "size for given indices from the bound element array", info);
255 return false;
256 }
258 if (uint32_t(primcount) > mMaxFetchedInstances) {
259 ErrorInvalidOperation("%s: bound instance attribute buffers do not have sufficient size for given primcount", info);
260 return false;
261 }
263 MakeContextCurrent();
265 if (mBoundFramebuffer) {
266 if (!mBoundFramebuffer->CheckAndInitializeAttachments()) {
267 ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info);
268 return false;
269 }
270 } else {
271 ClearBackbufferIfNeeded();
272 }
274 if (!DoFakeVertexAttrib0(mMaxFetchedVertices)) {
275 return false;
276 }
277 BindFakeBlackTextures();
279 return true;
280 }
282 void
283 WebGLContext::DrawElements(GLenum mode, GLsizei count, GLenum type,
284 WebGLintptr byteOffset)
285 {
286 if (IsContextLost())
287 return;
289 if (!ValidateDrawModeEnum(mode, "drawElements: mode"))
290 return;
292 GLuint upperBound = UINT_MAX;
293 if (!DrawElements_check(count, type, byteOffset, 1, "drawElements",
294 &upperBound))
295 {
296 return;
297 }
299 SetupContextLossTimer();
301 if (gl->IsSupported(gl::GLFeature::draw_range_elements)) {
302 gl->fDrawRangeElements(mode, 0, upperBound,
303 count, type, reinterpret_cast<GLvoid*>(byteOffset));
304 } else {
305 gl->fDrawElements(mode, count, type, reinterpret_cast<GLvoid*>(byteOffset));
306 }
308 Draw_cleanup();
309 }
311 void
312 WebGLContext::DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type,
313 WebGLintptr byteOffset, GLsizei primcount)
314 {
315 if (IsContextLost())
316 return;
318 if (!ValidateDrawModeEnum(mode, "drawElementsInstanced: mode"))
319 return;
321 if (!DrawElements_check(count, type, byteOffset, primcount, "drawElementsInstanced"))
322 return;
324 if (!DrawInstanced_check("drawElementsInstanced"))
325 return;
327 SetupContextLossTimer();
328 gl->fDrawElementsInstanced(mode, count, type, reinterpret_cast<GLvoid*>(byteOffset), primcount);
330 Draw_cleanup();
331 }
333 void WebGLContext::Draw_cleanup()
334 {
335 UndoFakeVertexAttrib0();
336 UnbindFakeBlackTextures();
338 if (!mBoundFramebuffer) {
339 Invalidate();
340 mShouldPresent = true;
341 MOZ_ASSERT(!mBackbufferNeedsClear);
342 }
344 if (gl->WorkAroundDriverBugs()) {
345 if (gl->Renderer() == gl::GLRenderer::Tegra) {
346 mDrawCallsSinceLastFlush++;
348 if (mDrawCallsSinceLastFlush >= MAX_DRAW_CALLS_SINCE_FLUSH) {
349 gl->fFlush();
350 mDrawCallsSinceLastFlush = 0;
351 }
352 }
353 }
355 // Let's check the viewport
356 const WebGLRectangleObject* rect = CurValidFBRectObject();
357 if (rect) {
358 if (mViewportWidth > rect->Width() ||
359 mViewportHeight > rect->Height())
360 {
361 if (!mAlreadyWarnedAboutViewportLargerThanDest) {
362 GenerateWarning("Drawing to a destination rect smaller than the viewport rect. "
363 "(This warning will only be given once)");
364 mAlreadyWarnedAboutViewportLargerThanDest = true;
365 }
366 }
367 }
368 }
370 /*
371 * Verify that state is consistent for drawing, and compute max number of elements (maxAllowedCount)
372 * that will be legal to be read from bound VBOs.
373 */
375 bool
376 WebGLContext::ValidateBufferFetching(const char *info)
377 {
378 #ifdef DEBUG
379 GLint currentProgram = 0;
380 MakeContextCurrent();
381 gl->fGetIntegerv(LOCAL_GL_CURRENT_PROGRAM, ¤tProgram);
382 MOZ_ASSERT(GLuint(currentProgram) == mCurrentProgram->GLName(),
383 "WebGL: current program doesn't agree with GL state");
384 #endif
386 if (mBufferFetchingIsVerified) {
387 return true;
388 }
390 bool hasPerVertex = false;
391 uint32_t maxVertices = UINT32_MAX;
392 uint32_t maxInstances = UINT32_MAX;
393 uint32_t attribs = mBoundVertexArray->mAttribs.Length();
395 for (uint32_t i = 0; i < attribs; ++i) {
396 const WebGLVertexAttribData& vd = mBoundVertexArray->mAttribs[i];
398 // If the attrib array isn't enabled, there's nothing to check;
399 // it's a static value.
400 if (!vd.enabled)
401 continue;
403 if (vd.buf == nullptr) {
404 ErrorInvalidOperation("%s: no VBO bound to enabled vertex attrib index %d!", info, i);
405 return false;
406 }
408 // If the attrib is not in use, then we don't have to validate
409 // it, just need to make sure that the binding is non-null.
410 if (!mCurrentProgram->IsAttribInUse(i))
411 continue;
413 // the base offset
414 CheckedUint32 checked_byteLength = CheckedUint32(vd.buf->ByteLength()) - vd.byteOffset;
415 CheckedUint32 checked_sizeOfLastElement = CheckedUint32(vd.componentSize()) * vd.size;
417 if (!checked_byteLength.isValid() ||
418 !checked_sizeOfLastElement.isValid())
419 {
420 ErrorInvalidOperation("%s: integer overflow occured while checking vertex attrib %d", info, i);
421 return false;
422 }
424 if (checked_byteLength.value() < checked_sizeOfLastElement.value()) {
425 maxVertices = 0;
426 maxInstances = 0;
427 break;
428 }
430 CheckedUint32 checked_maxAllowedCount = ((checked_byteLength - checked_sizeOfLastElement) / vd.actualStride()) + 1;
432 if (!checked_maxAllowedCount.isValid()) {
433 ErrorInvalidOperation("%s: integer overflow occured while checking vertex attrib %d", info, i);
434 return false;
435 }
437 if (vd.divisor == 0) {
438 maxVertices = std::min(maxVertices, checked_maxAllowedCount.value());
439 hasPerVertex = true;
440 } else {
441 maxInstances = std::min(maxInstances, checked_maxAllowedCount.value() / vd.divisor);
442 }
443 }
445 mBufferFetchingIsVerified = true;
446 mBufferFetchingHasPerVertex = hasPerVertex;
447 mMaxFetchedVertices = maxVertices;
448 mMaxFetchedInstances = maxInstances;
450 return true;
451 }
453 WebGLVertexAttrib0Status
454 WebGLContext::WhatDoesVertexAttrib0Need()
455 {
456 MOZ_ASSERT(mCurrentProgram);
458 // work around Mac OSX crash, see bug 631420
459 #ifdef XP_MACOSX
460 if (gl->WorkAroundDriverBugs() &&
461 mBoundVertexArray->IsAttribArrayEnabled(0) &&
462 !mCurrentProgram->IsAttribInUse(0))
463 {
464 return WebGLVertexAttrib0Status::EmulatedUninitializedArray;
465 }
466 #endif
468 if (MOZ_LIKELY(gl->IsGLES() ||
469 mBoundVertexArray->IsAttribArrayEnabled(0)))
470 {
471 return WebGLVertexAttrib0Status::Default;
472 }
474 return mCurrentProgram->IsAttribInUse(0)
475 ? WebGLVertexAttrib0Status::EmulatedInitializedArray
476 : WebGLVertexAttrib0Status::EmulatedUninitializedArray;
477 }
479 bool
480 WebGLContext::DoFakeVertexAttrib0(GLuint vertexCount)
481 {
482 WebGLVertexAttrib0Status whatDoesAttrib0Need = WhatDoesVertexAttrib0Need();
484 if (MOZ_LIKELY(whatDoesAttrib0Need == WebGLVertexAttrib0Status::Default))
485 return true;
487 if (!mAlreadyWarnedAboutFakeVertexAttrib0) {
488 GenerateWarning("Drawing without vertex attrib 0 array enabled forces the browser "
489 "to do expensive emulation work when running on desktop OpenGL "
490 "platforms, for example on Mac. It is preferable to always draw "
491 "with vertex attrib 0 array enabled, by using bindAttribLocation "
492 "to bind some always-used attribute to location 0.");
493 mAlreadyWarnedAboutFakeVertexAttrib0 = true;
494 }
496 CheckedUint32 checked_dataSize = CheckedUint32(vertexCount) * 4 * sizeof(GLfloat);
498 if (!checked_dataSize.isValid()) {
499 ErrorOutOfMemory("Integer overflow trying to construct a fake vertex attrib 0 array for a draw-operation "
500 "with %d vertices. Try reducing the number of vertices.", vertexCount);
501 return false;
502 }
504 GLuint dataSize = checked_dataSize.value();
506 if (!mFakeVertexAttrib0BufferObject) {
507 gl->fGenBuffers(1, &mFakeVertexAttrib0BufferObject);
508 }
510 // if the VBO status is already exactly what we need, or if the only difference is that it's initialized and
511 // we don't need it to be, then consider it OK
512 bool vertexAttrib0BufferStatusOK =
513 mFakeVertexAttrib0BufferStatus == whatDoesAttrib0Need ||
514 (mFakeVertexAttrib0BufferStatus == WebGLVertexAttrib0Status::EmulatedInitializedArray &&
515 whatDoesAttrib0Need == WebGLVertexAttrib0Status::EmulatedUninitializedArray);
517 if (!vertexAttrib0BufferStatusOK ||
518 mFakeVertexAttrib0BufferObjectSize < dataSize ||
519 mFakeVertexAttrib0BufferObjectVector[0] != mVertexAttrib0Vector[0] ||
520 mFakeVertexAttrib0BufferObjectVector[1] != mVertexAttrib0Vector[1] ||
521 mFakeVertexAttrib0BufferObjectVector[2] != mVertexAttrib0Vector[2] ||
522 mFakeVertexAttrib0BufferObjectVector[3] != mVertexAttrib0Vector[3])
523 {
524 mFakeVertexAttrib0BufferStatus = whatDoesAttrib0Need;
525 mFakeVertexAttrib0BufferObjectSize = dataSize;
526 mFakeVertexAttrib0BufferObjectVector[0] = mVertexAttrib0Vector[0];
527 mFakeVertexAttrib0BufferObjectVector[1] = mVertexAttrib0Vector[1];
528 mFakeVertexAttrib0BufferObjectVector[2] = mVertexAttrib0Vector[2];
529 mFakeVertexAttrib0BufferObjectVector[3] = mVertexAttrib0Vector[3];
531 gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mFakeVertexAttrib0BufferObject);
533 GetAndFlushUnderlyingGLErrors();
535 if (mFakeVertexAttrib0BufferStatus == WebGLVertexAttrib0Status::EmulatedInitializedArray) {
536 nsAutoArrayPtr<GLfloat> array(new GLfloat[4 * vertexCount]);
537 for(size_t i = 0; i < vertexCount; ++i) {
538 array[4 * i + 0] = mVertexAttrib0Vector[0];
539 array[4 * i + 1] = mVertexAttrib0Vector[1];
540 array[4 * i + 2] = mVertexAttrib0Vector[2];
541 array[4 * i + 3] = mVertexAttrib0Vector[3];
542 }
543 gl->fBufferData(LOCAL_GL_ARRAY_BUFFER, dataSize, array, LOCAL_GL_DYNAMIC_DRAW);
544 } else {
545 gl->fBufferData(LOCAL_GL_ARRAY_BUFFER, dataSize, nullptr, LOCAL_GL_DYNAMIC_DRAW);
546 }
547 GLenum error = GetAndFlushUnderlyingGLErrors();
549 gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mBoundArrayBuffer ? mBoundArrayBuffer->GLName() : 0);
551 // note that we do this error checking and early return AFTER having restored the buffer binding above
552 if (error) {
553 ErrorOutOfMemory("Ran out of memory trying to construct a fake vertex attrib 0 array for a draw-operation "
554 "with %d vertices. Try reducing the number of vertices.", vertexCount);
555 return false;
556 }
557 }
559 gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mFakeVertexAttrib0BufferObject);
560 gl->fVertexAttribPointer(0, 4, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, 0);
562 return true;
563 }
565 void
566 WebGLContext::UndoFakeVertexAttrib0()
567 {
568 WebGLVertexAttrib0Status whatDoesAttrib0Need = WhatDoesVertexAttrib0Need();
570 if (MOZ_LIKELY(whatDoesAttrib0Need == WebGLVertexAttrib0Status::Default))
571 return;
573 if (mBoundVertexArray->HasAttrib(0) && mBoundVertexArray->mAttribs[0].buf) {
574 const WebGLVertexAttribData& attrib0 = mBoundVertexArray->mAttribs[0];
575 gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, attrib0.buf->GLName());
576 gl->fVertexAttribPointer(0,
577 attrib0.size,
578 attrib0.type,
579 attrib0.normalized,
580 attrib0.stride,
581 reinterpret_cast<const GLvoid *>(attrib0.byteOffset));
582 } else {
583 gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
584 }
586 gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mBoundArrayBuffer ? mBoundArrayBuffer->GLName() : 0);
587 }
589 WebGLContextFakeBlackStatus
590 WebGLContext::ResolvedFakeBlackStatus()
591 {
592 // handle this case first, it's the generic case
593 if (MOZ_LIKELY(mFakeBlackStatus == WebGLContextFakeBlackStatus::NotNeeded))
594 return mFakeBlackStatus;
596 if (mFakeBlackStatus == WebGLContextFakeBlackStatus::Needed)
597 return mFakeBlackStatus;
599 for (int32_t i = 0; i < mGLMaxTextureUnits; ++i) {
600 if ((mBound2DTextures[i] && mBound2DTextures[i]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded) ||
601 (mBoundCubeMapTextures[i] && mBoundCubeMapTextures[i]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded))
602 {
603 mFakeBlackStatus = WebGLContextFakeBlackStatus::Needed;
604 return mFakeBlackStatus;
605 }
606 }
608 // we have exhausted all cases where we do need fakeblack, so if the status is still unknown,
609 // that means that we do NOT need it.
610 mFakeBlackStatus = WebGLContextFakeBlackStatus::NotNeeded;
611 return mFakeBlackStatus;
612 }
614 void
615 WebGLContext::BindFakeBlackTexturesHelper(
616 GLenum target,
617 const nsTArray<WebGLRefPtr<WebGLTexture> > & boundTexturesArray,
618 ScopedDeletePtr<FakeBlackTexture> & opaqueTextureScopedPtr,
619 ScopedDeletePtr<FakeBlackTexture> & transparentTextureScopedPtr)
620 {
621 for (int32_t i = 0; i < mGLMaxTextureUnits; ++i) {
622 if (!boundTexturesArray[i]) {
623 continue;
624 }
626 WebGLTextureFakeBlackStatus s = boundTexturesArray[i]->ResolvedFakeBlackStatus();
627 MOZ_ASSERT(s != WebGLTextureFakeBlackStatus::Unknown);
629 if (MOZ_LIKELY(s == WebGLTextureFakeBlackStatus::NotNeeded)) {
630 continue;
631 }
633 bool alpha = s == WebGLTextureFakeBlackStatus::UninitializedImageData &&
634 FormatHasAlpha(boundTexturesArray[i]->ImageInfoBase().WebGLFormat());
635 ScopedDeletePtr<FakeBlackTexture>&
636 blackTexturePtr = alpha
637 ? transparentTextureScopedPtr
638 : opaqueTextureScopedPtr;
640 if (!blackTexturePtr) {
641 GLenum format = alpha ? LOCAL_GL_RGBA : LOCAL_GL_RGB;
642 blackTexturePtr
643 = new FakeBlackTexture(gl, target, format);
644 }
646 gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
647 gl->fBindTexture(target,
648 blackTexturePtr->GLName());
649 }
650 }
652 void
653 WebGLContext::BindFakeBlackTextures()
654 {
655 // this is the generic case: try to return early
656 if (MOZ_LIKELY(ResolvedFakeBlackStatus() == WebGLContextFakeBlackStatus::NotNeeded))
657 return;
659 BindFakeBlackTexturesHelper(LOCAL_GL_TEXTURE_2D,
660 mBound2DTextures,
661 mBlackOpaqueTexture2D,
662 mBlackTransparentTexture2D);
663 BindFakeBlackTexturesHelper(LOCAL_GL_TEXTURE_CUBE_MAP,
664 mBoundCubeMapTextures,
665 mBlackOpaqueTextureCubeMap,
666 mBlackTransparentTextureCubeMap);
667 }
669 void
670 WebGLContext::UnbindFakeBlackTextures()
671 {
672 // this is the generic case: try to return early
673 if (MOZ_LIKELY(ResolvedFakeBlackStatus() == WebGLContextFakeBlackStatus::NotNeeded))
674 return;
676 for (int32_t i = 0; i < mGLMaxTextureUnits; ++i) {
677 if (mBound2DTextures[i] && mBound2DTextures[i]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded) {
678 gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
679 gl->fBindTexture(LOCAL_GL_TEXTURE_2D, mBound2DTextures[i]->GLName());
680 }
681 if (mBoundCubeMapTextures[i] && mBoundCubeMapTextures[i]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded) {
682 gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
683 gl->fBindTexture(LOCAL_GL_TEXTURE_CUBE_MAP, mBoundCubeMapTextures[i]->GLName());
684 }
685 }
687 gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mActiveTexture);
688 }
690 WebGLContext::FakeBlackTexture::FakeBlackTexture(GLContext *gl, GLenum target, GLenum format)
691 : mGL(gl)
692 , mGLName(0)
693 {
694 MOZ_ASSERT(target == LOCAL_GL_TEXTURE_2D || target == LOCAL_GL_TEXTURE_CUBE_MAP);
695 MOZ_ASSERT(format == LOCAL_GL_RGB || format == LOCAL_GL_RGBA);
697 mGL->MakeCurrent();
698 GLuint formerBinding = 0;
699 gl->GetUIntegerv(target == LOCAL_GL_TEXTURE_2D
700 ? LOCAL_GL_TEXTURE_BINDING_2D
701 : LOCAL_GL_TEXTURE_BINDING_CUBE_MAP,
702 &formerBinding);
703 gl->fGenTextures(1, &mGLName);
704 gl->fBindTexture(target, mGLName);
706 // we allocate our zeros on the heap, and we overallocate (16 bytes instead of 4)
707 // to minimize the risk of running into a driver bug in texImage2D, as it is
708 // a bit unusual maybe to create 1x1 textures, and the stack may not have the alignment
709 // that texImage2D expects.
710 void* zeros = calloc(1, 16);
711 if (target == LOCAL_GL_TEXTURE_2D) {
712 gl->fTexImage2D(target, 0, format, 1, 1,
713 0, format, LOCAL_GL_UNSIGNED_BYTE, zeros);
714 } else {
715 for (GLuint i = 0; i < 6; ++i) {
716 gl->fTexImage2D(LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, format, 1, 1,
717 0, format, LOCAL_GL_UNSIGNED_BYTE, zeros);
718 }
719 }
720 free(zeros);
722 gl->fBindTexture(target, formerBinding);
723 }
725 WebGLContext::FakeBlackTexture::~FakeBlackTexture()
726 {
727 if (mGL) {
728 mGL->MakeCurrent();
729 mGL->fDeleteTextures(1, &mGLName);
730 }
731 }