Beispiel #1
0
bool
WebGLContext::DrawArrays_check(const char* funcName, GLenum mode, GLint first,
                               GLsizei vertCount, GLsizei instanceCount)
{
    if (!ValidateDrawModeEnum(mode, funcName))
        return false;

    if (!ValidateNonNegative(funcName, "first", first) ||
        !ValidateNonNegative(funcName, "vertCount", vertCount) ||
        !ValidateNonNegative(funcName, "instanceCount", instanceCount))
    {
        return false;
    }

    if (!ValidateStencilParamsForDrawCall())
        return false;

    if (IsWebGL2() && !gl->IsSupported(gl::GLFeature::prim_restart_fixed)) {
        MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart));
        if (mPrimRestartTypeBytes != 0) {
            mPrimRestartTypeBytes = 0;

            // OSX appears to have severe perf issues with leaving this enabled.
            gl->fDisable(LOCAL_GL_PRIMITIVE_RESTART);
        }
    }

    if (!vertCount || !instanceCount)
        return false; // No error, just early out.

    if (!ValidateBufferFetching(funcName))
        return false;

    const auto checked_firstPlusCount = CheckedInt<GLsizei>(first) + vertCount;
    if (!checked_firstPlusCount.isValid()) {
        ErrorInvalidOperation("%s: overflow in first+vertCount", funcName);
        return false;
    }

    if (uint32_t(checked_firstPlusCount.value()) > mMaxFetchedVertices) {
        ErrorInvalidOperation("%s: Bound vertex attribute buffers do not have sufficient"
                              " size for given first and count.",
                              funcName);
        return false;
    }

    return true;
}
void
WebGLContext::BufferSubDataImpl(GLenum target, WebGLsizeiptr dstByteOffset,
                                size_t dataLen, const uint8_t* data)
{
    const char funcName[] = "bufferSubData";

    if (!ValidateNonNegative(funcName, "byteOffset", dstByteOffset))
        return;

    const auto& buffer = ValidateBufferSelection(funcName, target);
    if (!buffer)
        return;

    if (!buffer->ValidateRange(funcName, dstByteOffset, dataLen))
        return;

    if (!CheckedInt<GLintptr>(dataLen).isValid()) {
        ErrorOutOfMemory("%s: Size too large.", funcName);
        return;
    }
    const GLintptr glDataLen(dataLen);

    ////

    MakeContextCurrent();
    const ScopedLazyBind lazyBind(gl, target, buffer);

    // Warning: Possibly shared memory.  See bug 1225033.
    gl->fBufferSubData(target, dstByteOffset, glDataLen, data);

    // Warning: Possibly shared memory.  See bug 1225033.
    buffer->ElementArrayCacheBufferSubData(dstByteOffset, data, size_t(glDataLen));
}
Beispiel #3
0
void WebGLContext::BufferSubDataImpl(GLenum target, WebGLsizeiptr dstByteOffset,
                                     uint64_t dataLen, const uint8_t* data) {
  const FuncScope funcScope(*this, "bufferSubData");

  if (!ValidateNonNegative("byteOffset", dstByteOffset)) return;

  const auto& buffer = ValidateBufferSelection(target);
  if (!buffer) return;

  buffer->BufferSubData(target, uint64_t(dstByteOffset), dataLen, data);
}
void
WebGLContext::BufferData(GLenum target, WebGLsizeiptr size, GLenum usage)
{
    const char funcName[] = "bufferData";
    if (IsContextLost())
        return;

    if (!ValidateNonNegative(funcName, "size", size))
        return;

    ////

    const UniqueBuffer zeroBuffer(calloc(size, 1));
    if (!zeroBuffer)
        return ErrorOutOfMemory("%s: Failed to allocate zeros.", funcName);

    BufferDataImpl(target, size_t(size), (const uint8_t*)zeroBuffer.get(), usage);
}
Beispiel #5
0
void WebGLContext::BufferData(GLenum target, WebGLsizeiptr size, GLenum usage) {
  const FuncScope funcScope(*this, "bufferData");
  if (IsContextLost()) return;

  if (!ValidateNonNegative("size", size)) return;

  ////

  const auto checkedSize = CheckedInt<size_t>(size);
  if (!checkedSize.isValid())
    return ErrorOutOfMemory("size too large for platform.");

  const UniqueBuffer zeroBuffer(calloc(checkedSize.value(), 1u));
  if (!zeroBuffer) return ErrorOutOfMemory("Failed to allocate zeros.");

  BufferDataImpl(target, uint64_t{checkedSize.value()},
                 (const uint8_t*)zeroBuffer.get(), usage);
}
Beispiel #6
0
bool
WebGLContext::DrawElements_check(const char* funcName, GLenum mode, GLsizei vertCount,
                                 GLenum type, WebGLintptr byteOffset,
                                 GLsizei instanceCount)
{
    if (!ValidateDrawModeEnum(mode, funcName))
        return false;

    if (mBoundTransformFeedback &&
        mBoundTransformFeedback->mIsActive &&
        !mBoundTransformFeedback->mIsPaused)
    {
        ErrorInvalidOperation("%s: DrawElements* functions are incompatible with"
                              " transform feedback.",
                              funcName);
        return false;
    }

    if (!ValidateNonNegative(funcName, "vertCount", vertCount) ||
        !ValidateNonNegative(funcName, "byteOffset", byteOffset) ||
        !ValidateNonNegative(funcName, "instanceCount", instanceCount))
    {
        return false;
    }

    if (!ValidateStencilParamsForDrawCall())
        return false;

    if (!vertCount || !instanceCount)
        return false; // No error, just early out.

    uint8_t bytesPerElem = 0;
    switch (type) {
    case LOCAL_GL_UNSIGNED_BYTE:
        bytesPerElem = 1;
        break;

    case LOCAL_GL_UNSIGNED_SHORT:
        bytesPerElem = 2;
        break;

    case LOCAL_GL_UNSIGNED_INT:
        if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_element_index_uint)) {
            bytesPerElem = 4;
        }
        break;
    }

    if (!bytesPerElem) {
        ErrorInvalidEnum("%s: Invalid `type`: 0x%04x", funcName, type);
        return false;
    }

    if (byteOffset % bytesPerElem != 0) {
        ErrorInvalidOperation("%s: `byteOffset` must be a multiple of the size of `type`",
                              funcName);
        return false;
    }

    ////

    if (IsWebGL2() && !gl->IsSupported(gl::GLFeature::prim_restart_fixed)) {
        MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart));
        if (mPrimRestartTypeBytes != bytesPerElem) {
            mPrimRestartTypeBytes = bytesPerElem;

            const uint32_t ones = UINT32_MAX >> (32 - 8*mPrimRestartTypeBytes);
            gl->fEnable(LOCAL_GL_PRIMITIVE_RESTART);
            gl->fPrimitiveRestartIndex(ones);
        }
void
WebGLContext::BindBufferRange(GLenum target, GLuint index, WebGLBuffer* buffer,
                              WebGLintptr offset, WebGLsizeiptr size)
{
    const char funcName[] = "bindBufferRange";
    if (IsContextLost())
        return;

    if (!ValidateObjectAllowDeletedOrNull(funcName, buffer))
        return;

    if (buffer && buffer->IsDeleted())
        return ErrorInvalidOperation("%s: Cannot bind a deleted object.", funcName);

    if (!ValidateNonNegative(funcName, "offset", offset) ||
        !ValidateNonNegative(funcName, "size", size))
    {
        return;
    }

    WebGLRefPtr<WebGLBuffer>* genericBinding;
    IndexedBufferBinding* indexedBinding;
    if (!ValidateIndexedBufferBinding(funcName, target, index, &genericBinding,
                                      &indexedBinding))
    {
        return;
    }

    if (buffer && !buffer->ValidateCanBindToTarget(funcName, target))
        return;

    ////

    gl->MakeCurrent();

    switch (target) {
    case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
        if (offset % 4 != 0 || size % 4 != 0) {
            ErrorInvalidValue("%s: For %s, `offset` and `size` must be multiples of 4.",
                              funcName, "TRANSFORM_FEEDBACK_BUFFER");
            return;
        }
        break;

    case LOCAL_GL_UNIFORM_BUFFER:
        {
            GLuint offsetAlignment = 0;
            gl->GetUIntegerv(LOCAL_GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &offsetAlignment);
            if (offset % offsetAlignment != 0) {
                ErrorInvalidValue("%s: For %s, `offset` must be a multiple of %s.",
                                  funcName, "UNIFORM_BUFFER",
                                  "UNIFORM_BUFFER_OFFSET_ALIGNMENT");
                return;
            }
        }
        break;
    }

    ////

#ifdef XP_MACOSX
    if (buffer && buffer->Content() == WebGLBuffer::Kind::Undefined &&
        gl->WorkAroundDriverBugs())
    {
        // BindBufferRange will fail if the buffer's contents is undefined.
        // Bind so driver initializes the buffer.
        gl->fBindBuffer(target, buffer->mGLName);
    }
#endif

    gl->fBindBufferRange(target, index, buffer ? buffer->mGLName : 0, offset, size);

    ////

    *genericBinding = buffer;
    indexedBinding->mBufferBinding = buffer;
    indexedBinding->mRangeStart = offset;
    indexedBinding->mRangeSize = size;

    if (buffer) {
        buffer->SetContentAfterBind(target);
    }

    switch (target) {
    case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
        mBoundTransformFeedback->OnIndexedBindingsChanged();
        break;
    case LOCAL_GL_UNIFORM:
        OnUBIndexedBindingsChanged();
        break;
    }
}
void
WebGL2Context::GetBufferSubData(GLenum target, GLintptr offset,
                                const dom::ArrayBufferView& data)
{
    const char funcName[] = "getBufferSubData";
    if (IsContextLost())
        return;

    if (!ValidateNonNegative(funcName, "offset", offset))
        return;

    const auto& buffer = ValidateBufferSelection(funcName, target);
    if (!buffer)
        return;

    ////

    // If offset + returnedData.byteLength would extend beyond the end
    // of the buffer an INVALID_VALUE error is generated.
    data.ComputeLengthAndData();

    const auto neededByteLength = CheckedInt<size_t>(offset) + data.LengthAllowShared();
    if (!neededByteLength.isValid()) {
        ErrorInvalidValue("%s: Integer overflow computing the needed byte length.",
                          funcName);
        return;
    }

    if (neededByteLength.value() > buffer->ByteLength()) {
        ErrorInvalidValue("%s: Not enough data. Operation requires %d bytes, but buffer"
                          " only has %d bytes.",
                          funcName, neededByteLength.value(), buffer->ByteLength());
        return;
    }

    ////

    if (buffer->mNumActiveTFOs) {
        ErrorInvalidOperation("%s: Buffer is bound to an active transform feedback"
                              " object.",
                              funcName);
        return;
    }

    if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER &&
        mBoundTransformFeedback->mIsActive)
    {
        ErrorInvalidOperation("%s: Currently bound transform feedback is active.",
                              funcName);
        return;
    }

    ////

    gl->MakeCurrent();

    const auto ptr = gl->fMapBufferRange(target, offset, data.LengthAllowShared(),
                                         LOCAL_GL_MAP_READ_BIT);
    // Warning: Possibly shared memory.  See bug 1225033.
    memcpy(data.DataAllowShared(), ptr, data.LengthAllowShared());
    gl->fUnmapBuffer(target);
}
void
WebGL2Context::CopyBufferSubData(GLenum readTarget, GLenum writeTarget,
                                 GLintptr readOffset, GLintptr writeOffset,
                                 GLsizeiptr size)
{
    const char funcName[] = "copyBufferSubData";
    if (IsContextLost())
        return;

    const auto& readBuffer = ValidateBufferSelection(funcName, readTarget);
    if (!readBuffer)
        return;

    const auto& writeBuffer = ValidateBufferSelection(funcName, writeTarget);
    if (!writeBuffer)
        return;

    if (readBuffer->mNumActiveTFOs ||
        writeBuffer->mNumActiveTFOs)
    {
        ErrorInvalidOperation("%s: Buffer is bound to an active transform feedback"
                              " object.",
                              funcName);
        return;
    }

    if (!ValidateNonNegative(funcName, "readOffset", readOffset) ||
        !ValidateNonNegative(funcName, "writeOffset", writeOffset) ||
        !ValidateNonNegative(funcName, "size", size))
    {
        return;
    }

    const auto fnValidateOffsetSize = [&](const char* info, GLintptr offset,
                                          const WebGLBuffer* buffer)
    {
        const auto neededBytes = CheckedInt<size_t>(offset) + size;
        if (!neededBytes.isValid() || neededBytes.value() > buffer->ByteLength()) {
            ErrorInvalidValue("%s: Invalid %s range.", funcName, info);
            return false;
        }
        return true;
    };

    if (!fnValidateOffsetSize("read", readOffset, readBuffer) ||
        !fnValidateOffsetSize("write", writeOffset, writeBuffer))
    {
        return;
    }

    if (readBuffer == writeBuffer &&
        !ValidateDataRanges(readOffset, writeOffset, size, funcName))
    {
        return;
    }

    const auto& readType = readBuffer->Content();
    const auto& writeType = writeBuffer->Content();
    MOZ_ASSERT(readType != WebGLBuffer::Kind::Undefined);
    MOZ_ASSERT(writeType != WebGLBuffer::Kind::Undefined);
    if (writeType != readType) {
        ErrorInvalidOperation("%s: Can't copy %s data to %s data.",
                              funcName,
                              (readType == WebGLBuffer::Kind::OtherData) ? "other"
                                                                         : "element",
                              (writeType == WebGLBuffer::Kind::OtherData) ? "other"
                                                                          : "element");
        return;
    }

    gl->MakeCurrent();
    gl->fCopyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size);
}
Beispiel #10
0
void WebGLContext::BindBufferRange(GLenum target, GLuint index,
                                   WebGLBuffer* buffer, WebGLintptr offset,
                                   WebGLsizeiptr size) {
  const FuncScope funcScope(*this, "bindBufferRange");
  if (IsContextLost()) return;

  if (buffer && !ValidateObject("buffer", *buffer)) return;

  if (!ValidateNonNegative("offset", offset) ||
      !ValidateNonNegative("size", size)) {
    return;
  }

  WebGLRefPtr<WebGLBuffer>* genericBinding;
  IndexedBufferBinding* indexedBinding;
  if (!ValidateIndexedBufferBinding(target, index, &genericBinding,
                                    &indexedBinding)) {
    return;
  }

  if (buffer && !buffer->ValidateCanBindToTarget(target)) return;

  if (buffer && !size) {
    ErrorInvalidValue("Size must be non-zero for non-null buffer.");
    return;
  }

  ////

  switch (target) {
    case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
      if (offset % 4 != 0 || size % 4 != 0) {
        ErrorInvalidValue("For %s, `offset` and `size` must be multiples of 4.",
                          "TRANSFORM_FEEDBACK_BUFFER");
        return;
      }
      break;

    case LOCAL_GL_UNIFORM_BUFFER: {
      GLuint offsetAlignment = 0;
      gl->GetUIntegerv(LOCAL_GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT,
                       &offsetAlignment);
      if (offset % offsetAlignment != 0) {
        ErrorInvalidValue("For %s, `offset` must be a multiple of %s.",
                          "UNIFORM_BUFFER", "UNIFORM_BUFFER_OFFSET_ALIGNMENT");
        return;
      }
    } break;
  }

    ////

#ifdef XP_MACOSX
  if (buffer && buffer->Content() == WebGLBuffer::Kind::Undefined &&
      gl->WorkAroundDriverBugs()) {
    // BindBufferRange will fail if the buffer's contents is undefined.
    // Bind so driver initializes the buffer.
    gl->fBindBuffer(target, buffer->mGLName);
  }
#endif

  gl->fBindBufferRange(target, index, buffer ? buffer->mGLName : 0, offset,
                       size);

  ////

  WebGLBuffer::SetSlot(target, buffer, genericBinding);
  WebGLBuffer::SetSlot(target, buffer, &indexedBinding->mBufferBinding);
  indexedBinding->mRangeStart = offset;
  indexedBinding->mRangeSize = size;

  if (buffer) {
    buffer->SetContentAfterBind(target);
  }
}