content/canvas/src/WebGLContextDraw.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/canvas/src/WebGLContextDraw.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,731 @@
     1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "WebGLContext.h"
    1.10 +
    1.11 +#include "GLContext.h"
    1.12 +#include "mozilla/CheckedInt.h"
    1.13 +#include "WebGLBuffer.h"
    1.14 +#include "WebGLContextUtils.h"
    1.15 +#include "WebGLFramebuffer.h"
    1.16 +#include "WebGLProgram.h"
    1.17 +#include "WebGLRenderbuffer.h"
    1.18 +#include "WebGLShader.h"
    1.19 +#include "WebGLTexture.h"
    1.20 +#include "WebGLUniformInfo.h"
    1.21 +#include "WebGLVertexArray.h"
    1.22 +#include "WebGLVertexAttribData.h"
    1.23 +
    1.24 +using namespace mozilla;
    1.25 +using namespace mozilla::dom;
    1.26 +using namespace mozilla::gl;
    1.27 +
    1.28 +// For a Tegra workaround.
    1.29 +static const int MAX_DRAW_CALLS_SINCE_FLUSH = 100;
    1.30 +
    1.31 +bool
    1.32 +WebGLContext::DrawInstanced_check(const char* info)
    1.33 +{
    1.34 +    // This restriction was removed in GLES3, so WebGL2 shouldn't have it.
    1.35 +    if (!IsWebGL2() &&
    1.36 +        IsExtensionEnabled(WebGLExtensionID::ANGLE_instanced_arrays) &&
    1.37 +        !mBufferFetchingHasPerVertex)
    1.38 +    {
    1.39 +        /* http://www.khronos.org/registry/gles/extensions/ANGLE/ANGLE_instanced_arrays.txt
    1.40 +         *  If all of the enabled vertex attribute arrays that are bound to active
    1.41 +         *  generic attributes in the program have a non-zero divisor, the draw
    1.42 +         *  call should return INVALID_OPERATION.
    1.43 +         *
    1.44 +         * NB: This also appears to apply to NV_instanced_arrays, though the
    1.45 +         * INVALID_OPERATION emission is not explicitly stated.
    1.46 +         * ARB_instanced_arrays does not have this restriction.
    1.47 +         */
    1.48 +        ErrorInvalidOperation("%s: at least one vertex attribute divisor should be 0", info);
    1.49 +        return false;
    1.50 +    }
    1.51 +
    1.52 +    return true;
    1.53 +}
    1.54 +
    1.55 +bool WebGLContext::DrawArrays_check(GLint first, GLsizei count, GLsizei primcount, const char* info)
    1.56 +{
    1.57 +    if (first < 0 || count < 0) {
    1.58 +        ErrorInvalidValue("%s: negative first or count", info);
    1.59 +        return false;
    1.60 +    }
    1.61 +
    1.62 +    if (primcount < 0) {
    1.63 +        ErrorInvalidValue("%s: negative primcount", info);
    1.64 +        return false;
    1.65 +    }
    1.66 +
    1.67 +    if (!ValidateStencilParamsForDrawCall()) {
    1.68 +        return false;
    1.69 +    }
    1.70 +
    1.71 +    // If count is 0, there's nothing to do.
    1.72 +    if (count == 0 || primcount == 0) {
    1.73 +        return false;
    1.74 +    }
    1.75 +
    1.76 +    // Any checks below this depend on a program being available.
    1.77 +    if (!mCurrentProgram) {
    1.78 +        ErrorInvalidOperation("%s: null CURRENT_PROGRAM", info);
    1.79 +        return false;
    1.80 +    }
    1.81 +
    1.82 +    if (!ValidateBufferFetching(info)) {
    1.83 +        return false;
    1.84 +    }
    1.85 +
    1.86 +    CheckedInt<GLsizei> checked_firstPlusCount = CheckedInt<GLsizei>(first) + count;
    1.87 +
    1.88 +    if (!checked_firstPlusCount.isValid()) {
    1.89 +        ErrorInvalidOperation("%s: overflow in first+count", info);
    1.90 +        return false;
    1.91 +    }
    1.92 +
    1.93 +    if (uint32_t(checked_firstPlusCount.value()) > mMaxFetchedVertices) {
    1.94 +        ErrorInvalidOperation("%s: bound vertex attribute buffers do not have sufficient size for given first and count", info);
    1.95 +        return false;
    1.96 +    }
    1.97 +
    1.98 +    if (uint32_t(primcount) > mMaxFetchedInstances) {
    1.99 +        ErrorInvalidOperation("%s: bound instance attribute buffers do not have sufficient size for given primcount", info);
   1.100 +        return false;
   1.101 +    }
   1.102 +
   1.103 +    MakeContextCurrent();
   1.104 +
   1.105 +    if (mBoundFramebuffer) {
   1.106 +        if (!mBoundFramebuffer->CheckAndInitializeAttachments()) {
   1.107 +            ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info);
   1.108 +            return false;
   1.109 +        }
   1.110 +    } else {
   1.111 +        ClearBackbufferIfNeeded();
   1.112 +    }
   1.113 +
   1.114 +    if (!DoFakeVertexAttrib0(checked_firstPlusCount.value())) {
   1.115 +        return false;
   1.116 +    }
   1.117 +    BindFakeBlackTextures();
   1.118 +
   1.119 +    return true;
   1.120 +}
   1.121 +
   1.122 +void
   1.123 +WebGLContext::DrawArrays(GLenum mode, GLint first, GLsizei count)
   1.124 +{
   1.125 +    if (IsContextLost())
   1.126 +        return;
   1.127 +
   1.128 +    if (!ValidateDrawModeEnum(mode, "drawArrays: mode"))
   1.129 +        return;
   1.130 +
   1.131 +    if (!DrawArrays_check(first, count, 1, "drawArrays"))
   1.132 +        return;
   1.133 +
   1.134 +    SetupContextLossTimer();
   1.135 +    gl->fDrawArrays(mode, first, count);
   1.136 +
   1.137 +    Draw_cleanup();
   1.138 +}
   1.139 +
   1.140 +void
   1.141 +WebGLContext::DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount)
   1.142 +{
   1.143 +    if (IsContextLost())
   1.144 +        return;
   1.145 +
   1.146 +    if (!ValidateDrawModeEnum(mode, "drawArraysInstanced: mode"))
   1.147 +        return;
   1.148 +
   1.149 +    if (!DrawArrays_check(first, count, primcount, "drawArraysInstanced"))
   1.150 +        return;
   1.151 +
   1.152 +    if (!DrawInstanced_check("drawArraysInstanced"))
   1.153 +        return;
   1.154 +
   1.155 +    SetupContextLossTimer();
   1.156 +    gl->fDrawArraysInstanced(mode, first, count, primcount);
   1.157 +
   1.158 +    Draw_cleanup();
   1.159 +}
   1.160 +
   1.161 +bool
   1.162 +WebGLContext::DrawElements_check(GLsizei count, GLenum type,
   1.163 +                                 WebGLintptr byteOffset, GLsizei primcount,
   1.164 +                                 const char* info, GLuint* out_upperBound)
   1.165 +{
   1.166 +    if (count < 0 || byteOffset < 0) {
   1.167 +        ErrorInvalidValue("%s: negative count or offset", info);
   1.168 +        return false;
   1.169 +    }
   1.170 +
   1.171 +    if (primcount < 0) {
   1.172 +        ErrorInvalidValue("%s: negative primcount", info);
   1.173 +        return false;
   1.174 +    }
   1.175 +
   1.176 +    if (!ValidateStencilParamsForDrawCall()) {
   1.177 +        return false;
   1.178 +    }
   1.179 +
   1.180 +    // If count is 0, there's nothing to do.
   1.181 +    if (count == 0 || primcount == 0) {
   1.182 +        return false;
   1.183 +    }
   1.184 +
   1.185 +    CheckedUint32 checked_byteCount;
   1.186 +
   1.187 +    GLsizei first = 0;
   1.188 +
   1.189 +    if (type == LOCAL_GL_UNSIGNED_SHORT) {
   1.190 +        checked_byteCount = 2 * CheckedUint32(count);
   1.191 +        if (byteOffset % 2 != 0) {
   1.192 +            ErrorInvalidOperation("%s: invalid byteOffset for UNSIGNED_SHORT (must be a multiple of 2)", info);
   1.193 +            return false;
   1.194 +        }
   1.195 +        first = byteOffset / 2;
   1.196 +    }
   1.197 +    else if (type == LOCAL_GL_UNSIGNED_BYTE) {
   1.198 +        checked_byteCount = count;
   1.199 +        first = byteOffset;
   1.200 +    }
   1.201 +    else if (type == LOCAL_GL_UNSIGNED_INT && IsExtensionEnabled(WebGLExtensionID::OES_element_index_uint)) {
   1.202 +        checked_byteCount = 4 * CheckedUint32(count);
   1.203 +        if (byteOffset % 4 != 0) {
   1.204 +            ErrorInvalidOperation("%s: invalid byteOffset for UNSIGNED_INT (must be a multiple of 4)", info);
   1.205 +            return false;
   1.206 +        }
   1.207 +        first = byteOffset / 4;
   1.208 +    }
   1.209 +    else {
   1.210 +        ErrorInvalidEnum("%s: type must be UNSIGNED_SHORT or UNSIGNED_BYTE", info);
   1.211 +        return false;
   1.212 +    }
   1.213 +
   1.214 +    if (!checked_byteCount.isValid()) {
   1.215 +        ErrorInvalidValue("%s: overflow in byteCount", info);
   1.216 +        return false;
   1.217 +    }
   1.218 +
   1.219 +    // Any checks below this depend on a program being available.
   1.220 +    if (!mCurrentProgram) {
   1.221 +        ErrorInvalidOperation("%s: null CURRENT_PROGRAM", info);
   1.222 +        return false;
   1.223 +    }
   1.224 +
   1.225 +    if (!mBoundVertexArray->mBoundElementArrayBuffer) {
   1.226 +        ErrorInvalidOperation("%s: must have element array buffer binding", info);
   1.227 +        return false;
   1.228 +    }
   1.229 +
   1.230 +    WebGLBuffer& elemArrayBuffer = *mBoundVertexArray->mBoundElementArrayBuffer;
   1.231 +
   1.232 +    if (!elemArrayBuffer.ByteLength()) {
   1.233 +        ErrorInvalidOperation("%s: bound element array buffer doesn't have any data", info);
   1.234 +        return false;
   1.235 +    }
   1.236 +
   1.237 +    CheckedInt<GLsizei> checked_neededByteCount = checked_byteCount.toChecked<GLsizei>() + byteOffset;
   1.238 +
   1.239 +    if (!checked_neededByteCount.isValid()) {
   1.240 +        ErrorInvalidOperation("%s: overflow in byteOffset+byteCount", info);
   1.241 +        return false;
   1.242 +    }
   1.243 +
   1.244 +    if (uint32_t(checked_neededByteCount.value()) > elemArrayBuffer.ByteLength()) {
   1.245 +        ErrorInvalidOperation("%s: bound element array buffer is too small for given count and offset", info);
   1.246 +        return false;
   1.247 +    }
   1.248 +
   1.249 +    if (!ValidateBufferFetching(info))
   1.250 +        return false;
   1.251 +
   1.252 +    if (!mMaxFetchedVertices ||
   1.253 +        !elemArrayBuffer.Validate(type, mMaxFetchedVertices - 1, first, count, out_upperBound))
   1.254 +    {
   1.255 +        ErrorInvalidOperation(
   1.256 +                              "%s: bound vertex attribute buffers do not have sufficient "
   1.257 +                              "size for given indices from the bound element array", info);
   1.258 +        return false;
   1.259 +    }
   1.260 +
   1.261 +    if (uint32_t(primcount) > mMaxFetchedInstances) {
   1.262 +        ErrorInvalidOperation("%s: bound instance attribute buffers do not have sufficient size for given primcount", info);
   1.263 +        return false;
   1.264 +    }
   1.265 +
   1.266 +    MakeContextCurrent();
   1.267 +
   1.268 +    if (mBoundFramebuffer) {
   1.269 +        if (!mBoundFramebuffer->CheckAndInitializeAttachments()) {
   1.270 +            ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info);
   1.271 +            return false;
   1.272 +        }
   1.273 +    } else {
   1.274 +        ClearBackbufferIfNeeded();
   1.275 +    }
   1.276 +
   1.277 +    if (!DoFakeVertexAttrib0(mMaxFetchedVertices)) {
   1.278 +        return false;
   1.279 +    }
   1.280 +    BindFakeBlackTextures();
   1.281 +
   1.282 +    return true;
   1.283 +}
   1.284 +
   1.285 +void
   1.286 +WebGLContext::DrawElements(GLenum mode, GLsizei count, GLenum type,
   1.287 +                           WebGLintptr byteOffset)
   1.288 +{
   1.289 +    if (IsContextLost())
   1.290 +        return;
   1.291 +
   1.292 +    if (!ValidateDrawModeEnum(mode, "drawElements: mode"))
   1.293 +        return;
   1.294 +
   1.295 +    GLuint upperBound = UINT_MAX;
   1.296 +    if (!DrawElements_check(count, type, byteOffset, 1, "drawElements",
   1.297 +                            &upperBound))
   1.298 +    {
   1.299 +        return;
   1.300 +    }
   1.301 +
   1.302 +    SetupContextLossTimer();
   1.303 +
   1.304 +    if (gl->IsSupported(gl::GLFeature::draw_range_elements)) {
   1.305 +        gl->fDrawRangeElements(mode, 0, upperBound,
   1.306 +                               count, type, reinterpret_cast<GLvoid*>(byteOffset));
   1.307 +    } else {
   1.308 +        gl->fDrawElements(mode, count, type, reinterpret_cast<GLvoid*>(byteOffset));
   1.309 +    }
   1.310 +
   1.311 +    Draw_cleanup();
   1.312 +}
   1.313 +
   1.314 +void
   1.315 +WebGLContext::DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type,
   1.316 +                                    WebGLintptr byteOffset, GLsizei primcount)
   1.317 +{
   1.318 +    if (IsContextLost())
   1.319 +        return;
   1.320 +
   1.321 +    if (!ValidateDrawModeEnum(mode, "drawElementsInstanced: mode"))
   1.322 +        return;
   1.323 +
   1.324 +    if (!DrawElements_check(count, type, byteOffset, primcount, "drawElementsInstanced"))
   1.325 +        return;
   1.326 +
   1.327 +    if (!DrawInstanced_check("drawElementsInstanced"))
   1.328 +        return;
   1.329 +
   1.330 +    SetupContextLossTimer();
   1.331 +    gl->fDrawElementsInstanced(mode, count, type, reinterpret_cast<GLvoid*>(byteOffset), primcount);
   1.332 +
   1.333 +    Draw_cleanup();
   1.334 +}
   1.335 +
   1.336 +void WebGLContext::Draw_cleanup()
   1.337 +{
   1.338 +    UndoFakeVertexAttrib0();
   1.339 +    UnbindFakeBlackTextures();
   1.340 +
   1.341 +    if (!mBoundFramebuffer) {
   1.342 +        Invalidate();
   1.343 +        mShouldPresent = true;
   1.344 +        MOZ_ASSERT(!mBackbufferNeedsClear);
   1.345 +    }
   1.346 +
   1.347 +    if (gl->WorkAroundDriverBugs()) {
   1.348 +        if (gl->Renderer() == gl::GLRenderer::Tegra) {
   1.349 +            mDrawCallsSinceLastFlush++;
   1.350 +
   1.351 +            if (mDrawCallsSinceLastFlush >= MAX_DRAW_CALLS_SINCE_FLUSH) {
   1.352 +                gl->fFlush();
   1.353 +                mDrawCallsSinceLastFlush = 0;
   1.354 +            }
   1.355 +        }
   1.356 +    }
   1.357 +
   1.358 +    // Let's check the viewport
   1.359 +    const WebGLRectangleObject* rect = CurValidFBRectObject();
   1.360 +    if (rect) {
   1.361 +        if (mViewportWidth > rect->Width() ||
   1.362 +            mViewportHeight > rect->Height())
   1.363 +        {
   1.364 +            if (!mAlreadyWarnedAboutViewportLargerThanDest) {
   1.365 +                GenerateWarning("Drawing to a destination rect smaller than the viewport rect. "
   1.366 +                                "(This warning will only be given once)");
   1.367 +                mAlreadyWarnedAboutViewportLargerThanDest = true;
   1.368 +            }
   1.369 +        }
   1.370 +    }
   1.371 +}
   1.372 +
   1.373 +/*
   1.374 + * Verify that state is consistent for drawing, and compute max number of elements (maxAllowedCount)
   1.375 + * that will be legal to be read from bound VBOs.
   1.376 + */
   1.377 +
   1.378 +bool
   1.379 +WebGLContext::ValidateBufferFetching(const char *info)
   1.380 +{
   1.381 +#ifdef DEBUG
   1.382 +    GLint currentProgram = 0;
   1.383 +    MakeContextCurrent();
   1.384 +    gl->fGetIntegerv(LOCAL_GL_CURRENT_PROGRAM, &currentProgram);
   1.385 +    MOZ_ASSERT(GLuint(currentProgram) == mCurrentProgram->GLName(),
   1.386 +               "WebGL: current program doesn't agree with GL state");
   1.387 +#endif
   1.388 +
   1.389 +    if (mBufferFetchingIsVerified) {
   1.390 +        return true;
   1.391 +    }
   1.392 +
   1.393 +    bool hasPerVertex = false;
   1.394 +    uint32_t maxVertices = UINT32_MAX;
   1.395 +    uint32_t maxInstances = UINT32_MAX;
   1.396 +    uint32_t attribs = mBoundVertexArray->mAttribs.Length();
   1.397 +
   1.398 +    for (uint32_t i = 0; i < attribs; ++i) {
   1.399 +        const WebGLVertexAttribData& vd = mBoundVertexArray->mAttribs[i];
   1.400 +
   1.401 +        // If the attrib array isn't enabled, there's nothing to check;
   1.402 +        // it's a static value.
   1.403 +        if (!vd.enabled)
   1.404 +            continue;
   1.405 +
   1.406 +        if (vd.buf == nullptr) {
   1.407 +            ErrorInvalidOperation("%s: no VBO bound to enabled vertex attrib index %d!", info, i);
   1.408 +            return false;
   1.409 +        }
   1.410 +
   1.411 +        // If the attrib is not in use, then we don't have to validate
   1.412 +        // it, just need to make sure that the binding is non-null.
   1.413 +        if (!mCurrentProgram->IsAttribInUse(i))
   1.414 +            continue;
   1.415 +
   1.416 +        // the base offset
   1.417 +        CheckedUint32 checked_byteLength = CheckedUint32(vd.buf->ByteLength()) - vd.byteOffset;
   1.418 +        CheckedUint32 checked_sizeOfLastElement = CheckedUint32(vd.componentSize()) * vd.size;
   1.419 +
   1.420 +        if (!checked_byteLength.isValid() ||
   1.421 +            !checked_sizeOfLastElement.isValid())
   1.422 +        {
   1.423 +            ErrorInvalidOperation("%s: integer overflow occured while checking vertex attrib %d", info, i);
   1.424 +            return false;
   1.425 +        }
   1.426 +
   1.427 +        if (checked_byteLength.value() < checked_sizeOfLastElement.value()) {
   1.428 +            maxVertices = 0;
   1.429 +            maxInstances = 0;
   1.430 +            break;
   1.431 +        }
   1.432 +
   1.433 +        CheckedUint32 checked_maxAllowedCount = ((checked_byteLength - checked_sizeOfLastElement) / vd.actualStride()) + 1;
   1.434 +
   1.435 +        if (!checked_maxAllowedCount.isValid()) {
   1.436 +            ErrorInvalidOperation("%s: integer overflow occured while checking vertex attrib %d", info, i);
   1.437 +            return false;
   1.438 +        }
   1.439 +
   1.440 +        if (vd.divisor == 0) {
   1.441 +            maxVertices = std::min(maxVertices, checked_maxAllowedCount.value());
   1.442 +            hasPerVertex = true;
   1.443 +        } else {
   1.444 +            maxInstances = std::min(maxInstances, checked_maxAllowedCount.value() / vd.divisor);
   1.445 +        }
   1.446 +    }
   1.447 +
   1.448 +    mBufferFetchingIsVerified = true;
   1.449 +    mBufferFetchingHasPerVertex = hasPerVertex;
   1.450 +    mMaxFetchedVertices = maxVertices;
   1.451 +    mMaxFetchedInstances = maxInstances;
   1.452 +
   1.453 +    return true;
   1.454 +}
   1.455 +
   1.456 +WebGLVertexAttrib0Status
   1.457 +WebGLContext::WhatDoesVertexAttrib0Need()
   1.458 +{
   1.459 +    MOZ_ASSERT(mCurrentProgram);
   1.460 +
   1.461 +    // work around Mac OSX crash, see bug 631420
   1.462 +#ifdef XP_MACOSX
   1.463 +    if (gl->WorkAroundDriverBugs() &&
   1.464 +        mBoundVertexArray->IsAttribArrayEnabled(0) &&
   1.465 +        !mCurrentProgram->IsAttribInUse(0))
   1.466 +    {
   1.467 +        return WebGLVertexAttrib0Status::EmulatedUninitializedArray;
   1.468 +    }
   1.469 +#endif
   1.470 +
   1.471 +    if (MOZ_LIKELY(gl->IsGLES() ||
   1.472 +                   mBoundVertexArray->IsAttribArrayEnabled(0)))
   1.473 +    {
   1.474 +        return WebGLVertexAttrib0Status::Default;
   1.475 +    }
   1.476 +
   1.477 +    return mCurrentProgram->IsAttribInUse(0)
   1.478 +           ? WebGLVertexAttrib0Status::EmulatedInitializedArray
   1.479 +           : WebGLVertexAttrib0Status::EmulatedUninitializedArray;
   1.480 +}
   1.481 +
   1.482 +bool
   1.483 +WebGLContext::DoFakeVertexAttrib0(GLuint vertexCount)
   1.484 +{
   1.485 +    WebGLVertexAttrib0Status whatDoesAttrib0Need = WhatDoesVertexAttrib0Need();
   1.486 +
   1.487 +    if (MOZ_LIKELY(whatDoesAttrib0Need == WebGLVertexAttrib0Status::Default))
   1.488 +        return true;
   1.489 +
   1.490 +    if (!mAlreadyWarnedAboutFakeVertexAttrib0) {
   1.491 +        GenerateWarning("Drawing without vertex attrib 0 array enabled forces the browser "
   1.492 +                        "to do expensive emulation work when running on desktop OpenGL "
   1.493 +                        "platforms, for example on Mac. It is preferable to always draw "
   1.494 +                        "with vertex attrib 0 array enabled, by using bindAttribLocation "
   1.495 +                        "to bind some always-used attribute to location 0.");
   1.496 +        mAlreadyWarnedAboutFakeVertexAttrib0 = true;
   1.497 +    }
   1.498 +
   1.499 +    CheckedUint32 checked_dataSize = CheckedUint32(vertexCount) * 4 * sizeof(GLfloat);
   1.500 +
   1.501 +    if (!checked_dataSize.isValid()) {
   1.502 +        ErrorOutOfMemory("Integer overflow trying to construct a fake vertex attrib 0 array for a draw-operation "
   1.503 +                         "with %d vertices. Try reducing the number of vertices.", vertexCount);
   1.504 +        return false;
   1.505 +    }
   1.506 +
   1.507 +    GLuint dataSize = checked_dataSize.value();
   1.508 +
   1.509 +    if (!mFakeVertexAttrib0BufferObject) {
   1.510 +        gl->fGenBuffers(1, &mFakeVertexAttrib0BufferObject);
   1.511 +    }
   1.512 +
   1.513 +    // if the VBO status is already exactly what we need, or if the only difference is that it's initialized and
   1.514 +    // we don't need it to be, then consider it OK
   1.515 +    bool vertexAttrib0BufferStatusOK =
   1.516 +        mFakeVertexAttrib0BufferStatus == whatDoesAttrib0Need ||
   1.517 +        (mFakeVertexAttrib0BufferStatus == WebGLVertexAttrib0Status::EmulatedInitializedArray &&
   1.518 +         whatDoesAttrib0Need == WebGLVertexAttrib0Status::EmulatedUninitializedArray);
   1.519 +
   1.520 +    if (!vertexAttrib0BufferStatusOK ||
   1.521 +        mFakeVertexAttrib0BufferObjectSize < dataSize ||
   1.522 +        mFakeVertexAttrib0BufferObjectVector[0] != mVertexAttrib0Vector[0] ||
   1.523 +        mFakeVertexAttrib0BufferObjectVector[1] != mVertexAttrib0Vector[1] ||
   1.524 +        mFakeVertexAttrib0BufferObjectVector[2] != mVertexAttrib0Vector[2] ||
   1.525 +        mFakeVertexAttrib0BufferObjectVector[3] != mVertexAttrib0Vector[3])
   1.526 +    {
   1.527 +        mFakeVertexAttrib0BufferStatus = whatDoesAttrib0Need;
   1.528 +        mFakeVertexAttrib0BufferObjectSize = dataSize;
   1.529 +        mFakeVertexAttrib0BufferObjectVector[0] = mVertexAttrib0Vector[0];
   1.530 +        mFakeVertexAttrib0BufferObjectVector[1] = mVertexAttrib0Vector[1];
   1.531 +        mFakeVertexAttrib0BufferObjectVector[2] = mVertexAttrib0Vector[2];
   1.532 +        mFakeVertexAttrib0BufferObjectVector[3] = mVertexAttrib0Vector[3];
   1.533 +
   1.534 +        gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mFakeVertexAttrib0BufferObject);
   1.535 +
   1.536 +        GetAndFlushUnderlyingGLErrors();
   1.537 +
   1.538 +        if (mFakeVertexAttrib0BufferStatus == WebGLVertexAttrib0Status::EmulatedInitializedArray) {
   1.539 +            nsAutoArrayPtr<GLfloat> array(new GLfloat[4 * vertexCount]);
   1.540 +            for(size_t i = 0; i < vertexCount; ++i) {
   1.541 +                array[4 * i + 0] = mVertexAttrib0Vector[0];
   1.542 +                array[4 * i + 1] = mVertexAttrib0Vector[1];
   1.543 +                array[4 * i + 2] = mVertexAttrib0Vector[2];
   1.544 +                array[4 * i + 3] = mVertexAttrib0Vector[3];
   1.545 +            }
   1.546 +            gl->fBufferData(LOCAL_GL_ARRAY_BUFFER, dataSize, array, LOCAL_GL_DYNAMIC_DRAW);
   1.547 +        } else {
   1.548 +            gl->fBufferData(LOCAL_GL_ARRAY_BUFFER, dataSize, nullptr, LOCAL_GL_DYNAMIC_DRAW);
   1.549 +        }
   1.550 +        GLenum error = GetAndFlushUnderlyingGLErrors();
   1.551 +
   1.552 +        gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mBoundArrayBuffer ? mBoundArrayBuffer->GLName() : 0);
   1.553 +
   1.554 +        // note that we do this error checking and early return AFTER having restored the buffer binding above
   1.555 +        if (error) {
   1.556 +            ErrorOutOfMemory("Ran out of memory trying to construct a fake vertex attrib 0 array for a draw-operation "
   1.557 +                             "with %d vertices. Try reducing the number of vertices.", vertexCount);
   1.558 +            return false;
   1.559 +        }
   1.560 +    }
   1.561 +
   1.562 +    gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mFakeVertexAttrib0BufferObject);
   1.563 +    gl->fVertexAttribPointer(0, 4, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, 0);
   1.564 +
   1.565 +    return true;
   1.566 +}
   1.567 +
   1.568 +void
   1.569 +WebGLContext::UndoFakeVertexAttrib0()
   1.570 +{
   1.571 +    WebGLVertexAttrib0Status whatDoesAttrib0Need = WhatDoesVertexAttrib0Need();
   1.572 +
   1.573 +    if (MOZ_LIKELY(whatDoesAttrib0Need == WebGLVertexAttrib0Status::Default))
   1.574 +        return;
   1.575 +
   1.576 +    if (mBoundVertexArray->HasAttrib(0) && mBoundVertexArray->mAttribs[0].buf) {
   1.577 +        const WebGLVertexAttribData& attrib0 = mBoundVertexArray->mAttribs[0];
   1.578 +        gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, attrib0.buf->GLName());
   1.579 +        gl->fVertexAttribPointer(0,
   1.580 +                                 attrib0.size,
   1.581 +                                 attrib0.type,
   1.582 +                                 attrib0.normalized,
   1.583 +                                 attrib0.stride,
   1.584 +                                 reinterpret_cast<const GLvoid *>(attrib0.byteOffset));
   1.585 +    } else {
   1.586 +        gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
   1.587 +    }
   1.588 +
   1.589 +    gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mBoundArrayBuffer ? mBoundArrayBuffer->GLName() : 0);
   1.590 +}
   1.591 +
   1.592 +WebGLContextFakeBlackStatus
   1.593 +WebGLContext::ResolvedFakeBlackStatus()
   1.594 +{
   1.595 +    // handle this case first, it's the generic case
   1.596 +    if (MOZ_LIKELY(mFakeBlackStatus == WebGLContextFakeBlackStatus::NotNeeded))
   1.597 +        return mFakeBlackStatus;
   1.598 +
   1.599 +    if (mFakeBlackStatus == WebGLContextFakeBlackStatus::Needed)
   1.600 +        return mFakeBlackStatus;
   1.601 +
   1.602 +    for (int32_t i = 0; i < mGLMaxTextureUnits; ++i) {
   1.603 +        if ((mBound2DTextures[i] && mBound2DTextures[i]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded) ||
   1.604 +            (mBoundCubeMapTextures[i] && mBoundCubeMapTextures[i]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded))
   1.605 +        {
   1.606 +            mFakeBlackStatus = WebGLContextFakeBlackStatus::Needed;
   1.607 +            return mFakeBlackStatus;
   1.608 +        }
   1.609 +    }
   1.610 +
   1.611 +    // we have exhausted all cases where we do need fakeblack, so if the status is still unknown,
   1.612 +    // that means that we do NOT need it.
   1.613 +    mFakeBlackStatus = WebGLContextFakeBlackStatus::NotNeeded;
   1.614 +    return mFakeBlackStatus;
   1.615 +}
   1.616 +
   1.617 +void
   1.618 +WebGLContext::BindFakeBlackTexturesHelper(
   1.619 +    GLenum target,
   1.620 +    const nsTArray<WebGLRefPtr<WebGLTexture> > & boundTexturesArray,
   1.621 +    ScopedDeletePtr<FakeBlackTexture> & opaqueTextureScopedPtr,
   1.622 +    ScopedDeletePtr<FakeBlackTexture> & transparentTextureScopedPtr)
   1.623 +{
   1.624 +    for (int32_t i = 0; i < mGLMaxTextureUnits; ++i) {
   1.625 +        if (!boundTexturesArray[i]) {
   1.626 +            continue;
   1.627 +        }
   1.628 +
   1.629 +        WebGLTextureFakeBlackStatus s = boundTexturesArray[i]->ResolvedFakeBlackStatus();
   1.630 +        MOZ_ASSERT(s != WebGLTextureFakeBlackStatus::Unknown);
   1.631 +
   1.632 +        if (MOZ_LIKELY(s == WebGLTextureFakeBlackStatus::NotNeeded)) {
   1.633 +            continue;
   1.634 +        }
   1.635 +
   1.636 +        bool alpha = s == WebGLTextureFakeBlackStatus::UninitializedImageData &&
   1.637 +                     FormatHasAlpha(boundTexturesArray[i]->ImageInfoBase().WebGLFormat());
   1.638 +        ScopedDeletePtr<FakeBlackTexture>&
   1.639 +            blackTexturePtr = alpha
   1.640 +                              ? transparentTextureScopedPtr
   1.641 +                              : opaqueTextureScopedPtr;
   1.642 +
   1.643 +        if (!blackTexturePtr) {
   1.644 +            GLenum format = alpha ? LOCAL_GL_RGBA : LOCAL_GL_RGB;
   1.645 +            blackTexturePtr
   1.646 +                = new FakeBlackTexture(gl, target, format);
   1.647 +        }
   1.648 +
   1.649 +        gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
   1.650 +        gl->fBindTexture(target,
   1.651 +                         blackTexturePtr->GLName());
   1.652 +    }
   1.653 +}
   1.654 +
   1.655 +void
   1.656 +WebGLContext::BindFakeBlackTextures()
   1.657 +{
   1.658 +    // this is the generic case: try to return early
   1.659 +    if (MOZ_LIKELY(ResolvedFakeBlackStatus() == WebGLContextFakeBlackStatus::NotNeeded))
   1.660 +        return;
   1.661 +
   1.662 +    BindFakeBlackTexturesHelper(LOCAL_GL_TEXTURE_2D,
   1.663 +                                mBound2DTextures,
   1.664 +                                mBlackOpaqueTexture2D,
   1.665 +                                mBlackTransparentTexture2D);
   1.666 +    BindFakeBlackTexturesHelper(LOCAL_GL_TEXTURE_CUBE_MAP,
   1.667 +                                mBoundCubeMapTextures,
   1.668 +                                mBlackOpaqueTextureCubeMap,
   1.669 +                                mBlackTransparentTextureCubeMap);
   1.670 +}
   1.671 +
   1.672 +void
   1.673 +WebGLContext::UnbindFakeBlackTextures()
   1.674 +{
   1.675 +    // this is the generic case: try to return early
   1.676 +    if (MOZ_LIKELY(ResolvedFakeBlackStatus() == WebGLContextFakeBlackStatus::NotNeeded))
   1.677 +        return;
   1.678 +
   1.679 +    for (int32_t i = 0; i < mGLMaxTextureUnits; ++i) {
   1.680 +        if (mBound2DTextures[i] && mBound2DTextures[i]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded) {
   1.681 +            gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
   1.682 +            gl->fBindTexture(LOCAL_GL_TEXTURE_2D, mBound2DTextures[i]->GLName());
   1.683 +        }
   1.684 +        if (mBoundCubeMapTextures[i] && mBoundCubeMapTextures[i]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded) {
   1.685 +            gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
   1.686 +            gl->fBindTexture(LOCAL_GL_TEXTURE_CUBE_MAP, mBoundCubeMapTextures[i]->GLName());
   1.687 +        }
   1.688 +    }
   1.689 +
   1.690 +    gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mActiveTexture);
   1.691 +}
   1.692 +
   1.693 +WebGLContext::FakeBlackTexture::FakeBlackTexture(GLContext *gl, GLenum target, GLenum format)
   1.694 +    : mGL(gl)
   1.695 +    , mGLName(0)
   1.696 +{
   1.697 +  MOZ_ASSERT(target == LOCAL_GL_TEXTURE_2D || target == LOCAL_GL_TEXTURE_CUBE_MAP);
   1.698 +  MOZ_ASSERT(format == LOCAL_GL_RGB || format == LOCAL_GL_RGBA);
   1.699 +
   1.700 +  mGL->MakeCurrent();
   1.701 +  GLuint formerBinding = 0;
   1.702 +  gl->GetUIntegerv(target == LOCAL_GL_TEXTURE_2D
   1.703 +                   ? LOCAL_GL_TEXTURE_BINDING_2D
   1.704 +                   : LOCAL_GL_TEXTURE_BINDING_CUBE_MAP,
   1.705 +                   &formerBinding);
   1.706 +  gl->fGenTextures(1, &mGLName);
   1.707 +  gl->fBindTexture(target, mGLName);
   1.708 +
   1.709 +  // we allocate our zeros on the heap, and we overallocate (16 bytes instead of 4)
   1.710 +  // to minimize the risk of running into a driver bug in texImage2D, as it is
   1.711 +  // a bit unusual maybe to create 1x1 textures, and the stack may not have the alignment
   1.712 +  // that texImage2D expects.
   1.713 +  void* zeros = calloc(1, 16);
   1.714 +  if (target == LOCAL_GL_TEXTURE_2D) {
   1.715 +      gl->fTexImage2D(target, 0, format, 1, 1,
   1.716 +                      0, format, LOCAL_GL_UNSIGNED_BYTE, zeros);
   1.717 +  } else {
   1.718 +      for (GLuint i = 0; i < 6; ++i) {
   1.719 +          gl->fTexImage2D(LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, format, 1, 1,
   1.720 +                          0, format, LOCAL_GL_UNSIGNED_BYTE, zeros);
   1.721 +      }
   1.722 +  }
   1.723 +  free(zeros);
   1.724 +
   1.725 +  gl->fBindTexture(target, formerBinding);
   1.726 +}
   1.727 +
   1.728 +WebGLContext::FakeBlackTexture::~FakeBlackTexture()
   1.729 +{
   1.730 +  if (mGL) {
   1.731 +      mGL->MakeCurrent();
   1.732 +      mGL->fDeleteTextures(1, &mGLName);
   1.733 +  }
   1.734 +}

mercurial