content/canvas/src/WebGLContextDraw.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

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, &currentProgram);
   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 }

mercurial