void
WebGL2Context::UniformMatrix2x3fv_base(WebGLUniformLocation* loc, bool transpose,
                                       size_t arrayLength, const GLfloat* data)
{
    GLuint rawLoc;
    GLsizei numElementsToUpload;

    if (!ValidateUniformMatrixArraySetter(loc, 2, 3, LOCAL_GL_FLOAT, arrayLength,
                                          transpose, "uniformMatrix2x3fv",
                                          &rawLoc, &numElementsToUpload))
    {
        return;
    }

    MakeContextCurrent();
    gl->fUniformMatrix2x3fv(rawLoc, numElementsToUpload, transpose, data);
}
void
WebGLContext::BufferData(GLenum target, WebGLsizeiptr size, GLenum usage)
{
    if (IsContextLost())
        return;

    if (!ValidateBufferTarget(target, "bufferData"))
        return;

    WebGLRefPtr<WebGLBuffer>& bufferSlot = GetBufferSlotByTarget(target);

    if (size < 0)
        return ErrorInvalidValue("bufferData: negative size");

    if (!ValidateBufferUsageEnum(usage, "bufferData: usage"))
        return;

    // careful: WebGLsizeiptr is always 64-bit, but GLsizeiptr is like intptr_t.
    if (!CheckedInt<GLsizeiptr>(size).isValid())
        return ErrorOutOfMemory("bufferData: bad size");

    WebGLBuffer* boundBuffer = bufferSlot.get();

    if (!boundBuffer)
        return ErrorInvalidOperation("bufferData: no buffer bound!");

    UniquePtr<uint8_t> zeroBuffer((uint8_t*)calloc(size, 1));
    if (!zeroBuffer)
        return ErrorOutOfMemory("bufferData: out of memory");

    MakeContextCurrent();
    InvalidateBufferFetching();

    GLenum error = CheckedBufferData(target, size, zeroBuffer.get(), usage);

    if (error) {
        GenerateWarning("bufferData generated error %s", ErrorName(error));
        return;
    }

    boundBuffer->SetByteLength(size);
    if (!boundBuffer->ElementArrayCacheBufferData(nullptr, size)) {
        return ErrorOutOfMemory("bufferData: out of memory");
    }
}
void
WebGLContext::BufferSubDataT(GLenum target,
                             WebGLsizeiptr byteOffset,
                             const BufferT& data)
{
    if (IsContextLost())
        return;

    if (!ValidateBufferTarget(target, "bufferSubData"))
        return;

    WebGLRefPtr<WebGLBuffer>& bufferSlot = GetBufferSlotByTarget(target);

    if (byteOffset < 0)
        return ErrorInvalidValue("bufferSubData: negative offset");

    WebGLBuffer* boundBuffer = bufferSlot.get();
    if (!boundBuffer)
        return ErrorInvalidOperation("bufferData: no buffer bound!");

    data.ComputeLengthAndData();

    CheckedInt<WebGLsizeiptr> checked_neededByteLength =
        CheckedInt<WebGLsizeiptr>(byteOffset) + data.Length();

    if (!checked_neededByteLength.isValid()) {
        ErrorInvalidValue("bufferSubData: Integer overflow computing the needed"
                          " byte length.");
        return;
    }

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

    boundBuffer->ElementArrayCacheBufferSubData(byteOffset, data.Data(),
                                                data.Length());

    MakeContextCurrent();
    gl->fBufferSubData(target, byteOffset, data.Length(), data.Data());
}
bool
WebGL2Context::IsSampler(WebGLSampler* sampler)
{
    if (IsContextLost())
        return false;

    if (!sampler)
        return false;

    if (!ValidateObjectAllowDeleted("isSampler", sampler))
        return false;

    if (sampler->IsDeleted())
        return false;

    MakeContextCurrent();
    return gl->fIsSampler(sampler->mGLName);
}
bool
WebGLContext::IsVertexArray(WebGLVertexArray* array)
{
    if (IsContextLost())
        return false;

    if (!array)
        return false;

    if (!ValidateObjectAllowDeleted("isVertexArray", array))
        return false;

    if (array->IsDeleted())
        return false;

    MakeContextCurrent();
    return array->IsVertexArray();
}
void
WebGL2Context::ResumeTransformFeedback()
{
    if (IsContextLost())
        return;

    WebGLTransformFeedback* tf = mBoundTransformFeedback;
    MOZ_ASSERT(tf);
    if (!tf)
        return;

    if (!tf->mIsActive || !tf->mIsPaused)
        return ErrorInvalidOperation("resumeTransformFeedback: transform feedback is not active or is not paused");

    MakeContextCurrent();
    gl->fResumeTransformFeedback();
    tf->mIsPaused = false;
}
void
WebGLContext::BindTexture(GLenum rawTarget, WebGLTexture* newTex)
{
    if (IsContextLost())
        return;

     if (!ValidateObjectAllowDeletedOrNull("bindTexture", newTex))
        return;

    // Need to check rawTarget first before comparing against newTex->Target() as
    // newTex->Target() returns a TexTarget, which will assert on invalid value.
    WebGLRefPtr<WebGLTexture>* currentTexPtr = nullptr;
    switch (rawTarget) {
        case LOCAL_GL_TEXTURE_2D:
            currentTexPtr = &mBound2DTextures[mActiveTexture];
            break;

       case LOCAL_GL_TEXTURE_CUBE_MAP:
            currentTexPtr = &mBoundCubeMapTextures[mActiveTexture];
            break;

       case LOCAL_GL_TEXTURE_3D:
            if (!IsWebGL2())
                return ErrorInvalidEnum("bindTexture: target TEXTURE_3D is only available in WebGL version 2.0 or newer");

            currentTexPtr = &mBound3DTextures[mActiveTexture];
            break;

       default:
            return ErrorInvalidEnumInfo("bindTexture: target", rawTarget);
    }
    const TexTarget texTarget(rawTarget);

    MakeContextCurrent();

    if (newTex && !newTex->BindTexture(texTarget))
        return;

    if (!newTex) {
        gl->fBindTexture(texTarget.get(), 0);
    }

    *currentTexPtr = newTex;
}
void
WebGL2Context::ReadBuffer(GLenum mode)
{
    if (IsContextLost())
        return;

    const bool isColorAttachment = (mode >= LOCAL_GL_COLOR_ATTACHMENT0 &&
                                    mode <= LastColorAttachmentEnum());

    if (mode != LOCAL_GL_NONE && mode != LOCAL_GL_BACK && !isColorAttachment) {
        ErrorInvalidEnum("readBuffer: `mode` must be one of NONE, BACK, or "
                         "COLOR_ATTACHMENTi. Was %s",
                         EnumName(mode));
        return;
    }

    if (mBoundReadFramebuffer) {
        if (mode != LOCAL_GL_NONE &&
            !isColorAttachment)
        {
            ErrorInvalidOperation("readBuffer: If READ_FRAMEBUFFER is non-null, `mode` "
                                  "must be COLOR_ATTACHMENTi or NONE. Was %s",
                                  EnumName(mode));
            return;
        }

        MakeContextCurrent();
        mBoundReadFramebuffer->SetReadBufferMode(mode);
        gl->fReadBuffer(mode);
        return;
    }

    // Operating on the default framebuffer.
    if (mode != LOCAL_GL_NONE &&
        mode != LOCAL_GL_BACK)
    {
        ErrorInvalidOperation("readBuffer: If READ_FRAMEBUFFER is null, `mode`"
                              " must be BACK or NONE. Was %s",
                              EnumName(mode));
        return;
    }

    gl->Screen()->SetReadBuffer(mode);
}
Beispiel #9
0
Window::Window(const int2 & dimensions, const char * title)
{
    if(glfwInit() == GL_FALSE) throw std::runtime_error("glfwInit() failed.");
    window = glfwCreateWindow(dimensions.x, dimensions.y, title, nullptr, nullptr);
    if(window == nullptr) throw std::runtime_error("glfwCreateWindow(...) failed.");

    glfwSetWindowUserPointer(window, this);
    glfwSetKeyCallback(window, OnKey);

    const uint32_t compressedFont[] = {
        0,0x18,0,0,0x660000,0xc000018,0,0,0x36661800,0xc1c003e,0xc30,0x40000000,0x36663c00,0xc360063,0x1818,0x60000000,0x7f243c00,0x6364343,0x1866300c,0x30000000,0x36003c00,0x1c6303,0x183c300c,
        0x18000000,0x36001800,0x6e303e,0x7eff300c,0xc007f00,0x36001800,0x3b1860,0x183c300c,0x6000000,0x7f000000,0x330c61,0x1866300c,0x3000018,0x36001800,0x336663,0x1818,0x1180018,0x36001800,
        0x6e633e,0xc30,0x180018,0,0x18,0,0xc,0,0x18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x3e3e183e,0x7f1c7f30,0x3e3e,0x3e060060,0x63631c63,0x63060338,0x18186363,0x630c0030,0x60601e73,0x6003033c,0x18186363,
        0x63180018,0x6030187b,0x30030336,0x6363,0x30307e0c,0x3c18186f,0x183f3f33,0x7e3e,0x18600006,0x600c1867,0xc63607f,0x6063,0x1830000c,0x60061863,0xc636030,0x18186063,0x187e18,0x63631863,
        0xc636330,0x18183063,0x180c0030,0x3e7f7e3e,0xc3e3e78,0xc001e3e,0x18060060,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x3c3f083e,0x3c7f7f1f,0x67783c63,0x1c63630f,0x66661c63,0x66666636,
        0x66301863,0x36677706,0x43663663,0x43464666,0x36301863,0x636f7f06,0x366637b,0x3161666,0x36301863,0x637f7f06,0x33e637b,0x31e1e66,0x1e30187f,0x637b6b06,0x3667f7b,0x7b161666,0x36301863,
        0x63736306,0x4366633b,0x63064666,0x36331863,0x63636346,0x66666303,0x66066636,0x66331863,0x36636366,0x3c3f633e,0x5c0f7f1f,0x671e3c63,0x1c63637f,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x80000,0,0,0,
        0x1c0000,0x3e3f3e3f,0x6363637e,0x3c7f6663,0x363c01,0x63666366,0x6363637e,0xc636663,0x633003,0x63666366,0x6363635a,0xc316636,0x3007,0x6666366,0x63636318,0xc18661c,0x300e,0x1c3e633e,
        0x6b636318,0xc0c3c1c,0x301c,0x30366b06,0x6b636318,0xc06181c,0x3038,0x63667b06,0x7f366318,0xc431836,0x3070,0x63663e06,0x3e1c6318,0xc631863,0x3060,0x3e67300f,0x36083e3c,0x3c7f3c63,0x3c40,
        0x7000,0,0,0,0,0,0,0xff000000,0,0,0,0,0xc,0,0,0,0xc,0,0,0,0x70018,0x1c0038,0x7601807,0x1c,0x60000,0x360030,0x6601806,0x18,0x60000,0x260030,0x6000006,0x18,0x3e1e1e00,0x6e063e3c,0x66701c36,
        0x3e3b3718,0x63363000,0x330f6336,0x3660186e,0x63667f18,0x3663e00,0x33067f33,0x1e601866,0x63666b18,0x3663300,0x33060333,0x36601866,0x63666b18,0x63663300,0x3e066333,0x66601866,0x63666b18,
        0x3e3e6e00,0x300f3e6e,0x67663c67,0x3e66633c,0,0x33000000,0x660000,0,0,0x1e000000,0x3c0000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x8,0x70000000,0x6e0e18,0,0xc,0x18000000,0x3b1818,0,0xc,0x18000000,
        0x1818,0x3e3b6e3b,0x6366333f,0x187f6363,0x1818,0x636e3366,0x6366330c,0xe336336,0x7000,0xe663366,0x6b66330c,0x1818631c,0x1818,0x38063366,0x6b66330c,0x180c631c,0x1818,0x63063e3e,0x7f3c336c,
        0x18667e36,0x1818,0x3e0f3006,0x36186e38,0x707f6063,0xe18,0x3006,0,0x3000,0,0x780f,0,0x1f00 };
    uint8_t fontPixels[128*128] = {};
    auto out = fontPixels;
    for(auto num : compressedFont)
    {
        for(int i=0; i<32; ++i)
        {
            *out++ = num & (1 << i) ? 255 : 0;
        }
    }

    MakeContextCurrent();
    glGenTextures(1, &fontTexture);
    glBindTexture(GL_TEXTURE_2D, fontTexture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 128, 128, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, fontPixels);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
    glBindTexture(GL_TEXTURE_2D, 0);
}
void
WebGLContext::Enable(GLenum cap)
{
    if (IsContextLost())
        return;

    if (!ValidateCapabilityEnum(cap, "enable"))
        return;

    realGLboolean* trackingSlot = GetStateTrackingSlot(cap);

    if (trackingSlot)
    {
        *trackingSlot = 1;
    }

    MakeContextCurrent();
    gl->fEnable(cap);
}
void
WebGL2Context::EndQuery(GLenum target)
{
    if (IsContextLost())
        return;

    if (!ValidateQueryTarget(target, "endQuery"))
        return;

    WebGLRefPtr<WebGLQuery>& querySlot = GetQuerySlotByTarget(target);
    WebGLQuery* activeQuery = querySlot.get();

    if (!activeQuery || target != activeQuery->mType)
    {
        /* From GLES's EXT_occlusion_query_boolean:
         *     marks the end of the sequence of commands to be tracked for the
         *     query type given by <target>. The active query object for
         *     <target> is updated to indicate that query results are not
         *     available, and the active query object name for <target> is reset
         *     to zero. When the commands issued prior to EndQueryEXT have
         *     completed and a final query result is available, the query object
         *     active when EndQueryEXT is called is updated by the GL. The query
         *     object is updated to indicate that the query results are
         *     available and to contain the query result. If the active query
         *     object name for <target> is zero when EndQueryEXT is called, the
         *     error INVALID_OPERATION is generated.
         */
        ErrorInvalidOperation("endQuery: There is no active query of type %s.",
                              GetQueryTargetEnumString(target));
        return;
    }

    MakeContextCurrent();

    if (target == LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN) {
        gl->fEndQuery(LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
    } else {
        gl->fEndQuery(SimulateOcclusionQueryTarget(gl, target));
    }

    UpdateBoundQuery(target, nullptr);
    NS_DispatchToCurrentThread(new WebGLQuery::AvailableRunnable(activeQuery));
}
void
WebGLContext::DisableVertexAttribArray(GLuint index)
{
    if (IsContextLost())
        return;

    if (!ValidateAttribIndex(index, "disableVertexAttribArray"))
        return;

    MakeContextCurrent();
    InvalidateBufferFetching();

    if (index || !gl->IsCompatibilityProfile()) {
        gl->fDisableVertexAttribArray(index);
    }

    MOZ_ASSERT(mBoundVertexArray);
    mBoundVertexArray->mAttribs[index].mEnabled = false;
}
void
WebGLContext::BufferDataT(GLenum target,
                          const BufferT& data,
                          GLenum usage)
{
    if (IsContextLost())
        return;

    if (!ValidateBufferTarget(target, "bufferData"))
        return;

    const WebGLRefPtr<WebGLBuffer>& bufferSlot = GetBufferSlotByTarget(target);

    data.ComputeLengthAndData();

    // Careful: data.Length() could conceivably be any uint32_t, but GLsizeiptr
    // is like intptr_t.
    if (!CheckedInt<GLsizeiptr>(data.Length()).isValid())
        return ErrorOutOfMemory("bufferData: bad size");

    if (!ValidateBufferUsageEnum(usage, "bufferData: usage"))
        return;

    WebGLBuffer* boundBuffer = bufferSlot.get();

    if (!boundBuffer)
        return ErrorInvalidOperation("bufferData: no buffer bound!");

    MakeContextCurrent();
    InvalidateBufferFetching();

    GLenum error = CheckedBufferData(target, data.Length(), data.Data(), usage);

    if (error) {
        GenerateWarning("bufferData generated error %s", ErrorName(error));
        return;
    }

    boundBuffer->SetByteLength(data.Length());
    if (!boundBuffer->ElementArrayCacheBufferData(data.Data(), data.Length()))
        return ErrorOutOfMemory("bufferData: out of memory");
}
void
WebGL2Context::PauseTransformFeedback()
{
    if (IsContextLost())
        return;

    WebGLTransformFeedback* tf = mBoundTransformFeedback;
    MOZ_ASSERT(tf);
    if (!tf)
        return;

    if (!tf->mIsActive || tf->mIsPaused) {
        return ErrorInvalidOperation("%s: transform feedback is not active or is paused",
                                     "pauseTransformFeedback");
    }

    MakeContextCurrent();
    gl->fPauseTransformFeedback();
    tf->mIsPaused = true;
}
void
WebGLContext::VertexAttribDivisor(GLuint index, GLuint divisor)
{
    if (IsContextLost())
        return;

    if (!ValidateAttribIndex(index, "vertexAttribDivisor"))
        return;

    MOZ_ASSERT(mBoundVertexArray);

    WebGLVertexAttribData& vd = mBoundVertexArray->mAttribs[index];
    vd.mDivisor = divisor;

    InvalidateBufferFetching();

    MakeContextCurrent();

    gl->fVertexAttribDivisor(index, divisor);
}
/* This doesn't belong here. It's part of state querying */
void
WebGL2Context::GetIndexedParameter(GLenum target, GLuint index,
                                   dom::Nullable<dom::OwningWebGLBufferOrLongLong>& retval)
{
    retval.SetNull();
    if (IsContextLost())
        return;

    GLint64 data = 0;

    MakeContextCurrent();

    switch (target) {
    case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_BINDING:
        if (index >= mGLMaxTransformFeedbackSeparateAttribs)
            return ErrorInvalidValue("getIndexedParameter: index should be less than "
                                     "MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS");

        retval.SetValue().SetAsWebGLBuffer() =
            mBoundTransformFeedbackBuffers[index].get();
        return;

    case LOCAL_GL_UNIFORM_BUFFER_BINDING:
        if (index >= mGLMaxUniformBufferBindings)
            return ErrorInvalidValue("getIndexedParameter: index should be than "
                                     "MAX_UNIFORM_BUFFER_BINDINGS");

        retval.SetValue().SetAsWebGLBuffer() = mBoundUniformBuffers[index].get();
        return;

    case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_START:
    case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_SIZE:
    case LOCAL_GL_UNIFORM_BUFFER_START:
    case LOCAL_GL_UNIFORM_BUFFER_SIZE:
        gl->fGetInteger64i_v(target, index, &data);
        retval.SetValue().SetAsLongLong() = data;
        return;
    }

    ErrorInvalidEnumInfo("getIndexedParameter: target", target);
}
void
WebGL2Context::EndTransformFeedback()
{
    if (IsContextLost())
        return;

    WebGLTransformFeedback* tf = mBoundTransformFeedback;
    MOZ_ASSERT(tf);

    if (!tf)
        return;

    if (!tf->mIsActive)
        return ErrorInvalidOperation("%s: transform feedback in not active",
                                     "endTransformFeedback");

    MakeContextCurrent();
    gl->fEndTransformFeedback();
    tf->mIsActive = false;
    tf->mIsPaused = false;
}
void
WebGLContext::Clear(GLbitfield mask)
{
    const char funcName[] = "clear";

    if (IsContextLost())
        return;

    MakeContextCurrent();

    uint32_t m = mask & (LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT | LOCAL_GL_STENCIL_BUFFER_BIT);
    if (mask != m)
        return ErrorInvalidValue("%s: invalid mask bits", funcName);

    if (mask == 0) {
        GenerateWarning("Calling gl.clear(0) has no effect.");
    } else if (mRasterizerDiscardEnabled) {
        GenerateWarning("Calling gl.clear() with RASTERIZER_DISCARD enabled has no effects.");
    }

    if (mBoundDrawFramebuffer) {
        if (!mBoundDrawFramebuffer->ValidateAndInitAttachments(funcName))
            return;

        gl->fClear(mask);
        return;
    } else {
        ClearBackbufferIfNeeded();
    }

    // Ok, we're clearing the default framebuffer/screen.
    {
        ScopedMaskWorkaround autoMask(*this);
        gl->fClear(mask);
    }

    Invalidate();
    mShouldPresent = true;
}
void
WebGLContext::DrawElements(GLenum mode, GLsizei count, GLenum type,
                           WebGLintptr byteOffset)
{
    const char funcName[] = "drawElements";
    if (IsContextLost())
        return;

    if (!ValidateDrawModeEnum(mode, funcName))
        return;

    MakeContextCurrent();

    bool error;
    ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
    if (error)
        return;

    GLuint upperBound = 0;
    if (!DrawElements_check(count, type, byteOffset, 1, funcName, &upperBound))
        return;

    RunContextLossTimer();

    {
        ScopedMaskWorkaround autoMask(*this);

        if (gl->IsSupported(gl::GLFeature::draw_range_elements)) {
            gl->fDrawRangeElements(mode, 0, upperBound, count, type,
                                   reinterpret_cast<GLvoid*>(byteOffset));
        } else {
            gl->fDrawElements(mode, count, type,
                              reinterpret_cast<GLvoid*>(byteOffset));
        }
    }

    Draw_cleanup(funcName);
}
Beispiel #20
0
void
WebGLContext::DrawArraysInstanced(GLenum mode, GLint first, GLsizei vertCount,
                                  GLsizei instanceCount)
{
    const char funcName[] = "drawArraysInstanced";
    if (IsContextLost())
        return;

    MakeContextCurrent();

    bool error = false;
    ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
    if (error)
        return;

    if (!DrawArrays_check(funcName, mode, first, vertCount, instanceCount))
        return;

    if (!DrawInstanced_check(funcName))
        return;

    const ScopedDrawHelper scopedHelper(this, funcName, first, vertCount, instanceCount, &error);
    if (error)
        return;

    const ScopedDrawWithTransformFeedback scopedTF(this, funcName, mode, vertCount,
                                                   instanceCount, &error);
    if (error)
        return;

    {
        ScopedDrawCallWrapper wrapper(*this);
        gl->fDrawArraysInstanced(mode, first, vertCount, instanceCount);
    }

    Draw_cleanup(funcName);
    scopedTF.Advance();
}
void
WebGL2Context::BindTransformFeedback(GLenum target, WebGLTransformFeedback* tf)
{
    const char funcName[] = "bindTransformFeedback";
    if (IsContextLost())
        return;

    if (target != LOCAL_GL_TRANSFORM_FEEDBACK)
        return ErrorInvalidEnum("%s: `target` must be TRANSFORM_FEEDBACK.", funcName);

    if (tf && !ValidateObject(funcName, *tf))
        return;

    if (mBoundTransformFeedback->mIsActive &&
        !mBoundTransformFeedback->mIsPaused)
    {
        ErrorInvalidOperation("%s: Currently bound transform feedback is active and not"
                              " paused.",
                              funcName);
        return;
    }

    ////

    if (mBoundTransformFeedback) {
        mBoundTransformFeedback->AddBufferBindCounts(-1);
    }

    mBoundTransformFeedback = (tf ? tf : mDefaultTransformFeedback);

    MakeContextCurrent();
    gl->fBindTransformFeedback(target, mBoundTransformFeedback->mGLName);

    if (mBoundTransformFeedback) {
        mBoundTransformFeedback->AddBufferBindCounts(+1);
    }
}
void
WebGLContext::DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type,
                                    WebGLintptr byteOffset, GLsizei primcount)
{
    const char funcName[] = "drawElementsInstanced";
    if (IsContextLost())
        return;

    if (!ValidateDrawModeEnum(mode, funcName))
        return;

    MakeContextCurrent();

    bool error;
    ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
    if (error)
        return;

    GLuint upperBound = 0;
    if (!DrawElements_check(count, type, byteOffset, primcount, funcName, &upperBound))
        return;

    if (!DrawInstanced_check(funcName))
        return;

    RunContextLossTimer();

    {
        ScopedMaskWorkaround autoMask(*this);
        gl->fDrawElementsInstanced(mode, count, type,
                                   reinterpret_cast<GLvoid*>(byteOffset),
                                   primcount);
    }

    Draw_cleanup(funcName);
}
void
WebGLContext::ClearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a)
{
    if (IsContextLost())
        return;

    MakeContextCurrent();

    const bool supportsFloatColorBuffers = (IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float) ||
                                            IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float));
    if (!supportsFloatColorBuffers) {
        r = GLClampFloat(r);
        g = GLClampFloat(g);
        b = GLClampFloat(b);
        a = GLClampFloat(a);
    }

    gl->fClearColor(r, g, b, a);

    mColorClearValue[0] = r;
    mColorClearValue[1] = g;
    mColorClearValue[2] = b;
    mColorClearValue[3] = a;
}
void
WebGL2Context::BeginTransformFeedback(GLenum primitiveMode)
{
    if (IsContextLost())
        return;

    WebGLTransformFeedback* tf = mBoundTransformFeedback;
    MOZ_ASSERT(tf);
    if (!tf)
        return;

    if (tf->mIsActive)
        return ErrorInvalidOperation("beginTransformFeedback: transform feedback is active");

    const GLenum mode = tf->mMode;
    if (mode != LOCAL_GL_POINTS && mode != LOCAL_GL_LINES && mode != LOCAL_GL_TRIANGLES)
        return ErrorInvalidEnum("beginTransformFeedback: primitive must be one of POINTS, LINES, or TRIANGLES");

    // TODO:
    // GL_INVALID_OPERATION is generated by glBeginTransformFeedback
    // if any binding point used in transform feedback mode does not
    // have a buffer object bound. In interleaved mode, only the first
    // buffer object binding point is ever written to.

    // GL_INVALID_OPERATION is generated by glBeginTransformFeedback
    // if no binding points would be used, either because no program
    // object is active of because the active program object has
    // specified no varying variables to record.
    if (!mCurrentProgram)
        return ErrorInvalidOperation("beginTransformFeedback: no program is active");

    MakeContextCurrent();
    gl->fBeginTransformFeedback(primitiveMode);
    tf->mIsActive = true;
    tf->mIsPaused = false;
}
JS::Value
WebGLContext::GetVertexAttrib(JSContext* cx, GLuint index, GLenum pname,
                              ErrorResult& rv)
{
    const char funcName[] = "getVertexAttrib";
    if (IsContextLost())
        return JS::NullValue();

    if (!ValidateAttribIndex(index, funcName))
        return JS::NullValue();

    MOZ_ASSERT(mBoundVertexArray);

    MakeContextCurrent();

    switch (pname) {
    case LOCAL_GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:
        return WebGLObjectAsJSValue(cx, mBoundVertexArray->mAttribs[index].mBuf.get(), rv);

    case LOCAL_GL_VERTEX_ATTRIB_ARRAY_STRIDE:
        return JS::Int32Value(mBoundVertexArray->mAttribs[index].Stride());

    case LOCAL_GL_VERTEX_ATTRIB_ARRAY_SIZE:
        return JS::Int32Value(mBoundVertexArray->mAttribs[index].Size());

    case LOCAL_GL_VERTEX_ATTRIB_ARRAY_TYPE:
        return JS::Int32Value(mBoundVertexArray->mAttribs[index].Type());

    case LOCAL_GL_VERTEX_ATTRIB_ARRAY_INTEGER:
        if (IsWebGL2())
            return JS::BooleanValue(mBoundVertexArray->mAttribs[index].IntegerFunc());

        break;

    case LOCAL_GL_VERTEX_ATTRIB_ARRAY_DIVISOR:
        if (IsWebGL2() ||
            IsExtensionEnabled(WebGLExtensionID::ANGLE_instanced_arrays))
        {
            return JS::Int32Value(mBoundVertexArray->mAttribs[index].mDivisor);
        }
        break;

    case LOCAL_GL_CURRENT_VERTEX_ATTRIB:
        {
            JS::RootedObject obj(cx);
            switch (mGenericVertexAttribTypes[index]) {
            case LOCAL_GL_FLOAT:
                obj = GetVertexAttribFloat32Array(cx, index);
                break;

            case LOCAL_GL_INT:
                obj =  GetVertexAttribInt32Array(cx, index);
                break;

            case LOCAL_GL_UNSIGNED_INT:
                obj = GetVertexAttribUint32Array(cx, index);
                break;
            }

            if (!obj)
                rv.Throw(NS_ERROR_OUT_OF_MEMORY);
            return JS::ObjectOrNullValue(obj);
        }

    case LOCAL_GL_VERTEX_ATTRIB_ARRAY_ENABLED:
        return JS::BooleanValue(mBoundVertexArray->mAttribs[index].mEnabled);

    case LOCAL_GL_VERTEX_ATTRIB_ARRAY_NORMALIZED:
        return JS::BooleanValue(mBoundVertexArray->mAttribs[index].Normalized());

    default:
        break;
    }

    ErrorInvalidEnumInfo("getVertexAttrib: parameter", pname);
    return JS::NullValue();
}
bool
WebGLContext::InitAndValidateGL()
{
    if (!gl)
        return false;

    // Unconditionally create a new format usage authority. This is
    // important when restoring contexts and extensions need to add
    // formats back into the authority.
    mFormatUsage = CreateFormatUsage(gl);
    if (!mFormatUsage)
        return false;

    GLenum error = gl->fGetError();
    if (error != LOCAL_GL_NO_ERROR) {
        GenerateWarning("GL error 0x%x occurred during OpenGL context"
                        " initialization, before WebGL initialization!", error);
        return false;
    }

    mMinCapability = gfxPrefs::WebGLMinCapabilityMode();
    mDisableExtensions = gfxPrefs::WebGLDisableExtensions();
    mLoseContextOnMemoryPressure = gfxPrefs::WebGLLoseContextOnMemoryPressure();
    mCanLoseContextInForeground = gfxPrefs::WebGLCanLoseContextInForeground();
    mRestoreWhenVisible = gfxPrefs::WebGLRestoreWhenVisible();

    if (MinCapabilityMode())
        mDisableFragHighP = true;

    // These are the default values, see 6.2 State tables in the
    // OpenGL ES 2.0.25 spec.
    mColorWriteMask[0] = 1;
    mColorWriteMask[1] = 1;
    mColorWriteMask[2] = 1;
    mColorWriteMask[3] = 1;
    mDepthWriteMask = 1;
    mColorClearValue[0] = 0.f;
    mColorClearValue[1] = 0.f;
    mColorClearValue[2] = 0.f;
    mColorClearValue[3] = 0.f;
    mDepthClearValue = 1.f;
    mStencilClearValue = 0;
    mStencilRefFront = 0;
    mStencilRefBack = 0;

    /*
    // Technically, we should be setting mStencil[...] values to
    // `allOnes`, but either ANGLE breaks or the SGX540s on Try break.
    GLuint stencilBits = 0;
    gl->GetUIntegerv(LOCAL_GL_STENCIL_BITS, &stencilBits);
    GLuint allOnes = ~(UINT32_MAX << stencilBits);
    mStencilValueMaskFront = allOnes;
    mStencilValueMaskBack  = allOnes;
    mStencilWriteMaskFront = allOnes;
    mStencilWriteMaskBack  = allOnes;
    */

    gl->GetUIntegerv(LOCAL_GL_STENCIL_VALUE_MASK,      &mStencilValueMaskFront);
    gl->GetUIntegerv(LOCAL_GL_STENCIL_BACK_VALUE_MASK, &mStencilValueMaskBack);
    gl->GetUIntegerv(LOCAL_GL_STENCIL_WRITEMASK,       &mStencilWriteMaskFront);
    gl->GetUIntegerv(LOCAL_GL_STENCIL_BACK_WRITEMASK,  &mStencilWriteMaskBack);

    AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_VALUE_MASK,      mStencilValueMaskFront);
    AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_VALUE_MASK, mStencilValueMaskBack);
    AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_WRITEMASK,       mStencilWriteMaskFront);
    AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_WRITEMASK,  mStencilWriteMaskBack);

    mDitherEnabled = true;
    mRasterizerDiscardEnabled = false;
    mScissorTestEnabled = false;

    // Bindings, etc.
    mActiveTexture = 0;
    mDefaultFB_DrawBuffer0 = LOCAL_GL_BACK;

    mEmitContextLostErrorOnce = true;
    mWebGLError = LOCAL_GL_NO_ERROR;
    mUnderlyingGLError = LOCAL_GL_NO_ERROR;

    mBound2DTextures.Clear();
    mBoundCubeMapTextures.Clear();
    mBound3DTextures.Clear();
    mBound2DArrayTextures.Clear();
    mBoundSamplers.Clear();

    mBoundArrayBuffer = nullptr;
    mBoundTransformFeedbackBuffer = nullptr;
    mCurrentProgram = nullptr;

    mBoundDrawFramebuffer = nullptr;
    mBoundReadFramebuffer = nullptr;
    mBoundRenderbuffer = nullptr;

    MakeContextCurrent();

    // For OpenGL compat. profiles, we always keep vertex attrib 0 array enabled.
    if (gl->IsCompatibilityProfile())
        gl->fEnableVertexAttribArray(0);

    if (MinCapabilityMode())
        mGLMaxVertexAttribs = MINVALUE_GL_MAX_VERTEX_ATTRIBS;
    else
        gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_ATTRIBS, &mGLMaxVertexAttribs);

    if (mGLMaxVertexAttribs < 8) {
        GenerateWarning("GL_MAX_VERTEX_ATTRIBS: %d is < 8!",
                        mGLMaxVertexAttribs);
        return false;
    }

    // Note: GL_MAX_TEXTURE_UNITS is fixed at 4 for most desktop hardware,
    // even though the hardware supports much more.  The
    // GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS value is the accurate value.
    if (MinCapabilityMode())
        mGLMaxTextureUnits = MINVALUE_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS;
    else
        gl->fGetIntegerv(LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &mGLMaxTextureUnits);

    if (mGLMaxTextureUnits < 8) {
        GenerateWarning("GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: %d is < 8!",
                        mGLMaxTextureUnits);
        return false;
    }

    mBound2DTextures.SetLength(mGLMaxTextureUnits);
    mBoundCubeMapTextures.SetLength(mGLMaxTextureUnits);
    mBound3DTextures.SetLength(mGLMaxTextureUnits);
    mBound2DArrayTextures.SetLength(mGLMaxTextureUnits);
    mBoundSamplers.SetLength(mGLMaxTextureUnits);

    ////////////////

    if (MinCapabilityMode()) {
        mImplMaxTextureSize = MINVALUE_GL_MAX_TEXTURE_SIZE;
        mImplMaxCubeMapTextureSize = MINVALUE_GL_MAX_CUBE_MAP_TEXTURE_SIZE;
        mImplMaxRenderbufferSize = MINVALUE_GL_MAX_RENDERBUFFER_SIZE;

        mImplMax3DTextureSize = MINVALUE_GL_MAX_3D_TEXTURE_SIZE;
        mImplMaxArrayTextureLayers = MINVALUE_GL_MAX_ARRAY_TEXTURE_LAYERS;

        mGLMaxTextureImageUnits = MINVALUE_GL_MAX_TEXTURE_IMAGE_UNITS;
        mGLMaxVertexTextureImageUnits = MINVALUE_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS;
    } else {
        gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, (GLint*)&mImplMaxTextureSize);
        gl->fGetIntegerv(LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE, (GLint*)&mImplMaxCubeMapTextureSize);
        gl->fGetIntegerv(LOCAL_GL_MAX_RENDERBUFFER_SIZE, (GLint*)&mImplMaxRenderbufferSize);

        if (!gl->GetPotentialInteger(LOCAL_GL_MAX_3D_TEXTURE_SIZE, (GLint*)&mImplMax3DTextureSize))
            mImplMax3DTextureSize = 0;
        if (!gl->GetPotentialInteger(LOCAL_GL_MAX_ARRAY_TEXTURE_LAYERS, (GLint*)&mImplMaxArrayTextureLayers))
            mImplMaxArrayTextureLayers = 0;

        gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS, &mGLMaxTextureImageUnits);
        gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &mGLMaxVertexTextureImageUnits);
    }

    // If we don't support a target, its max size is 0. We should only floor-to-POT if the
    // value if it's non-zero. (NB log2(0) is -Inf, so zero isn't an integer power-of-two)
    const auto fnFloorPOTIfSupported = [](uint32_t& val) {
        if (val) {
            val = FloorPOT(val);
        }
    };

    fnFloorPOTIfSupported(mImplMaxTextureSize);
    fnFloorPOTIfSupported(mImplMaxCubeMapTextureSize);
    fnFloorPOTIfSupported(mImplMaxRenderbufferSize);

    fnFloorPOTIfSupported(mImplMax3DTextureSize);
    fnFloorPOTIfSupported(mImplMaxArrayTextureLayers);

    ////////////////

    mGLMaxColorAttachments = 1;
    mGLMaxDrawBuffers = 1;
    gl->GetPotentialInteger(LOCAL_GL_MAX_COLOR_ATTACHMENTS,
                            (GLint*)&mGLMaxColorAttachments);
    gl->GetPotentialInteger(LOCAL_GL_MAX_DRAW_BUFFERS, (GLint*)&mGLMaxDrawBuffers);

    if (MinCapabilityMode()) {
        mGLMaxColorAttachments = std::min(mGLMaxColorAttachments,
                                          kMinMaxColorAttachments);
        mGLMaxDrawBuffers = std::min(mGLMaxDrawBuffers, kMinMaxDrawBuffers);
    }

    if (IsWebGL2()) {
        mImplMaxColorAttachments = mGLMaxColorAttachments;
        mImplMaxDrawBuffers = std::min(mGLMaxDrawBuffers, mImplMaxColorAttachments);
    } else {
        mImplMaxColorAttachments = 1;
        mImplMaxDrawBuffers = 1;
    }

    ////////////////

    if (MinCapabilityMode()) {
        mGLMaxFragmentUniformVectors = MINVALUE_GL_MAX_FRAGMENT_UNIFORM_VECTORS;
        mGLMaxVertexUniformVectors = MINVALUE_GL_MAX_VERTEX_UNIFORM_VECTORS;
        mGLMaxVaryingVectors = MINVALUE_GL_MAX_VARYING_VECTORS;
    } else {
        if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) {
            gl->fGetIntegerv(LOCAL_GL_MAX_FRAGMENT_UNIFORM_VECTORS, &mGLMaxFragmentUniformVectors);
            gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_UNIFORM_VECTORS, &mGLMaxVertexUniformVectors);
            gl->fGetIntegerv(LOCAL_GL_MAX_VARYING_VECTORS, &mGLMaxVaryingVectors);
        } else {
            gl->fGetIntegerv(LOCAL_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &mGLMaxFragmentUniformVectors);
            mGLMaxFragmentUniformVectors /= 4;
            gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_UNIFORM_COMPONENTS, &mGLMaxVertexUniformVectors);
            mGLMaxVertexUniformVectors /= 4;

            /* We are now going to try to read GL_MAX_VERTEX_OUTPUT_COMPONENTS
             * and GL_MAX_FRAGMENT_INPUT_COMPONENTS, however these constants
             * only entered the OpenGL standard at OpenGL 3.2. So we will try
             * reading, and check OpenGL error for INVALID_ENUM.
             *
             * On the public_webgl list, "problematic GetParameter pnames"
             * thread, the following formula was given:
             *   maxVaryingVectors = min(GL_MAX_VERTEX_OUTPUT_COMPONENTS,
             *                           GL_MAX_FRAGMENT_INPUT_COMPONENTS) / 4
             */
            GLint maxVertexOutputComponents = 0;
            GLint maxFragmentInputComponents = 0;

            const bool ok = (gl->GetPotentialInteger(LOCAL_GL_MAX_VERTEX_OUTPUT_COMPONENTS,
                                                     &maxVertexOutputComponents) &&
                             gl->GetPotentialInteger(LOCAL_GL_MAX_FRAGMENT_INPUT_COMPONENTS,
                                                     &maxFragmentInputComponents));

            if (ok) {
                mGLMaxVaryingVectors = std::min(maxVertexOutputComponents,
                                                maxFragmentInputComponents) / 4;
            } else {
                mGLMaxVaryingVectors = 16;
                // 16 = 64/4, and 64 is the min value for
                // maxVertexOutputComponents in the OpenGL 3.2 spec.
            }
        }
    }

    if (gl->IsCompatibilityProfile()) {
        // gl_PointSize is always available in ES2 GLSL, but has to be
        // specifically enabled on desktop GLSL.
        gl->fEnable(LOCAL_GL_VERTEX_PROGRAM_POINT_SIZE);

        /* gl_PointCoord is always available in ES2 GLSL and in newer desktop
         * GLSL versions, but apparently not in OpenGL 2 and apparently not (due
         * to a driver bug) on certain NVIDIA setups. See:
         *   http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=261472
         *
         * Note that this used to cause crashes on old ATI drivers... Hopefully
         * not a significant anymore. See bug 602183.
         */
        gl->fEnable(LOCAL_GL_POINT_SPRITE);
    }

#ifdef XP_MACOSX
    if (gl->WorkAroundDriverBugs() &&
        gl->Vendor() == gl::GLVendor::ATI &&
        !nsCocoaFeatures::IsAtLeastVersion(10,9))
    {
        // The Mac ATI driver, in all known OSX version up to and including
        // 10.8, renders points sprites upside-down. (Apple bug 11778921)
        gl->fPointParameterf(LOCAL_GL_POINT_SPRITE_COORD_ORIGIN,
                             LOCAL_GL_LOWER_LEFT);
    }
#endif

    if (gl->IsSupported(gl::GLFeature::seamless_cube_map_opt_in)) {
        gl->fEnable(LOCAL_GL_TEXTURE_CUBE_MAP_SEAMLESS);
    }

    // Check the shader validator pref
    mBypassShaderValidation = gfxPrefs::WebGLBypassShaderValidator();

    // initialize shader translator
    if (!ShInitialize()) {
        GenerateWarning("GLSL translator initialization failed!");
        return false;
    }

    // Mesa can only be detected with the GL_VERSION string, of the form
    // "2.1 Mesa 7.11.0"
    const char* versionStr = (const char*)(gl->fGetString(LOCAL_GL_VERSION));
    mIsMesa = strstr(versionStr, "Mesa");

    // Notice that the point of calling fGetError here is not only to check for
    // errors, but also to reset the error flags so that a subsequent WebGL
    // getError call will give the correct result.
    error = gl->fGetError();
    if (error != LOCAL_GL_NO_ERROR) {
        GenerateWarning("GL error 0x%x occurred during WebGL context"
                        " initialization!", error);
        return false;
    }

    if (IsWebGL2() &&
        !InitWebGL2())
    {
        // Todo: Bug 898404: Only allow WebGL2 on GL>=3.0 on desktop GL.
        return false;
    }

    // Default value for all disabled vertex attributes is [0, 0, 0, 1]
    mVertexAttribType = MakeUnique<GLenum[]>(mGLMaxVertexAttribs);
    for (int32_t index = 0; index < mGLMaxVertexAttribs; ++index) {
        mVertexAttribType[index] = LOCAL_GL_FLOAT;
        VertexAttrib4f(index, 0, 0, 0, 1);
    }

    mDefaultVertexArray = WebGLVertexArray::Create(this);
    mDefaultVertexArray->mAttribs.SetLength(mGLMaxVertexAttribs);
    mBoundVertexArray = mDefaultVertexArray;

    // OpenGL core profiles remove the default VAO object from version
    // 4.0.0. We create a default VAO for all core profiles,
    // regardless of version.
    //
    // GL Spec 4.0.0:
    // (https://www.opengl.org/registry/doc/glspec40.core.20100311.pdf)
    // in Section E.2.2 "Removed Features", pg 397: "[...] The default
    // vertex array object (the name zero) is also deprecated. [...]"

    if (gl->IsCoreProfile()) {
        MakeContextCurrent();
        mDefaultVertexArray->GenVertexArray();
        mDefaultVertexArray->BindVertexArray();
    }

    mPixelStore_FlipY = false;
    mPixelStore_PremultiplyAlpha = false;
    mPixelStore_ColorspaceConversion = BROWSER_DEFAULT_WEBGL;

    // GLES 3.0.4, p259:
    mPixelStore_UnpackImageHeight = 0;
    mPixelStore_UnpackSkipImages = 0;
    mPixelStore_UnpackRowLength = 0;
    mPixelStore_UnpackSkipRows = 0;
    mPixelStore_UnpackSkipPixels = 0;
    mPixelStore_UnpackAlignment = 4;
    mPixelStore_PackRowLength = 0;
    mPixelStore_PackSkipRows = 0;
    mPixelStore_PackSkipPixels = 0;
    mPixelStore_PackAlignment = 4;

    return true;
}
bool
WebGL2Context::ValidateClearBuffer(const char* funcName, GLenum buffer, GLint drawBuffer,
                                   size_t availElemCount, GLuint elemOffset,
                                   GLenum funcType)
{
    if (elemOffset > availElemCount) {
        ErrorInvalidValue("%s: Offset too big for list.", funcName);
        return false;
    }
    availElemCount -= elemOffset;

    ////

    size_t requiredElements;
    GLint maxDrawBuffer;
    switch (buffer) {
    case LOCAL_GL_COLOR:
          requiredElements = 4;
          maxDrawBuffer = mGLMaxDrawBuffers - 1;
          break;

    case LOCAL_GL_DEPTH:
    case LOCAL_GL_STENCIL:
          requiredElements = 1;
          maxDrawBuffer = 0;
          break;

    case LOCAL_GL_DEPTH_STENCIL:
          requiredElements = 2;
          maxDrawBuffer = 0;
          break;

    default:
          ErrorInvalidEnumInfo(funcName, buffer);
          return false;
    }

    if (drawBuffer < 0 || drawBuffer > maxDrawBuffer) {
        ErrorInvalidValue("%s: Invalid drawbuffer %d. This buffer only supports"
                          " `drawbuffer` values between 0 and %u.",
                          funcName, drawBuffer, maxDrawBuffer);
        return false;
    }

    if (availElemCount < requiredElements) {
        ErrorInvalidValue("%s: Not enough elements. Require %u. Given %u.",
                          funcName, requiredElements, availElemCount);
        return false;
    }

    ////

    MakeContextCurrent();

    const auto& fb = mBoundDrawFramebuffer;
    if (fb) {
        if (!fb->ValidateAndInitAttachments(funcName))
            return false;

        if (!fb->ValidateClearBufferType(funcName, buffer, drawBuffer, funcType))
            return false;
    } else if (buffer == LOCAL_GL_COLOR) {
        if (drawBuffer != 0)
            return true;

        if (mDefaultFB_DrawBuffer0 == LOCAL_GL_NONE)
            return true;

        if (funcType != LOCAL_GL_FLOAT) {
            ErrorInvalidOperation("%s: For default framebuffer, COLOR is always of type"
                                  " FLOAT.",
                                  funcName);
            return false;
        }
    }

    return true;
}
Beispiel #28
0
_X_EXPORT Bool
glXMakeCurrent(Display * dpy, GLXDrawable draw, GLXContext gc)
{
   return MakeContextCurrent(dpy, draw, draw, gc);
}
JS::Value
WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
{
    if (IsContextLost())
        return JS::NullValue();

    MakeContextCurrent();

    if (MinCapabilityMode()) {
        switch(pname) {
            ////////////////////////////
            // Single-value params

            // int
            case LOCAL_GL_MAX_VERTEX_ATTRIBS:
                return JS::Int32Value(MINVALUE_GL_MAX_VERTEX_ATTRIBS);

            case LOCAL_GL_MAX_FRAGMENT_UNIFORM_VECTORS:
                return JS::Int32Value(MINVALUE_GL_MAX_FRAGMENT_UNIFORM_VECTORS);

            case LOCAL_GL_MAX_VERTEX_UNIFORM_VECTORS:
                return JS::Int32Value(MINVALUE_GL_MAX_VERTEX_UNIFORM_VECTORS);

            case LOCAL_GL_MAX_VARYING_VECTORS:
                return JS::Int32Value(MINVALUE_GL_MAX_VARYING_VECTORS);

            case LOCAL_GL_MAX_TEXTURE_SIZE:
                return JS::Int32Value(MINVALUE_GL_MAX_TEXTURE_SIZE);

            case LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE:
                return JS::Int32Value(MINVALUE_GL_MAX_CUBE_MAP_TEXTURE_SIZE);

            case LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS:
                return JS::Int32Value(MINVALUE_GL_MAX_TEXTURE_IMAGE_UNITS);

            case LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS:
                return JS::Int32Value(MINVALUE_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS);

            case LOCAL_GL_MAX_RENDERBUFFER_SIZE:
                return JS::Int32Value(MINVALUE_GL_MAX_RENDERBUFFER_SIZE);

            default:
                // Return the real value; we're not overriding this one
                break;
        }
    }

    if (IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers)) {
        if (pname == LOCAL_GL_MAX_COLOR_ATTACHMENTS) {
            return JS::Int32Value(mGLMaxColorAttachments);

        } else if (pname == LOCAL_GL_MAX_DRAW_BUFFERS) {
            return JS::Int32Value(mGLMaxDrawBuffers);

        } else if (pname >= LOCAL_GL_DRAW_BUFFER0 &&
                   pname < GLenum(LOCAL_GL_DRAW_BUFFER0 + mGLMaxDrawBuffers))
        {
            GLint iv = 0;
            gl->fGetIntegerv(pname, &iv);

            if (mBoundDrawFramebuffer)
                return JS::Int32Value(iv);

            const GLint index = (pname - LOCAL_GL_DRAW_BUFFER0);
            if (iv == LOCAL_GL_COLOR_ATTACHMENT0 + index)
                return JS::Int32Value(LOCAL_GL_BACK);

            return JS::Int32Value(LOCAL_GL_NONE);
        }
    }

    if (IsExtensionEnabled(WebGLExtensionID::OES_vertex_array_object)) {
        if (pname == LOCAL_GL_VERTEX_ARRAY_BINDING) {
            WebGLVertexArray* vao =
                (mBoundVertexArray != mDefaultVertexArray) ? mBoundVertexArray.get() : nullptr;
            return WebGLObjectAsJSValue(cx, vao, rv);
        }
    }

    if (IsExtensionEnabled(WebGLExtensionID::EXT_disjoint_timer_query)) {
        if (pname == LOCAL_GL_TIMESTAMP_EXT) {
            GLuint64 iv = 0;
            gl->fGetInteger64v(pname, (GLint64*) &iv);
            // TODO: JS doesn't support 64-bit integers. Be lossy and
            // cast to double (53 bits)
            return JS::NumberValue(static_cast<double>(iv));
        } else if (pname == LOCAL_GL_GPU_DISJOINT_EXT) {
            // When disjoint isn't supported, leave as false.
            realGLboolean disjoint = LOCAL_GL_FALSE;
            if (gl->IsExtensionSupported(gl::GLContext::EXT_disjoint_timer_query)) {
                gl->fGetBooleanv(pname, &disjoint);
            }
            return JS::BooleanValue(bool(disjoint));
        }
    }

    // Privileged string params exposed by WEBGL_debug_renderer_info.
    // The privilege check is done in WebGLContext::IsExtensionSupported.
    // So here we just have to check that the extension is enabled.
    if (IsExtensionEnabled(WebGLExtensionID::WEBGL_debug_renderer_info)) {
        switch (pname) {
        case UNMASKED_VENDOR_WEBGL:
        case UNMASKED_RENDERER_WEBGL:
            {
                const char* overridePref = nullptr;
                GLenum driverEnum = LOCAL_GL_NONE;

                switch (pname) {
                case UNMASKED_RENDERER_WEBGL:
                    overridePref = "webgl.renderer-string-override";
                    driverEnum = LOCAL_GL_RENDERER;
                    break;
                case UNMASKED_VENDOR_WEBGL:
                    overridePref = "webgl.vendor-string-override";
                    driverEnum = LOCAL_GL_VENDOR;
                    break;
                default:
                    MOZ_CRASH("bad `pname`");
                }

                bool hasRetVal = false;

                nsAutoString ret;
                if (overridePref) {
                    nsresult res = Preferences::GetString(overridePref, &ret);
                    if (NS_SUCCEEDED(res) && ret.Length() > 0)
                        hasRetVal = true;
                }

                if (!hasRetVal) {
                    const char* chars = reinterpret_cast<const char*>(gl->fGetString(driverEnum));
                    ret = NS_ConvertASCIItoUTF16(chars);
                    hasRetVal = true;
                }

                return StringValue(cx, ret, rv);
            }
        }
    }

    if (IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives)) {
        if (pname == LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT) {
            GLint i = 0;
            gl->fGetIntegerv(pname, &i);
            return JS::Int32Value(i);
        }
    }

    if (IsExtensionEnabled(WebGLExtensionID::EXT_texture_filter_anisotropic)) {
        if (pname == LOCAL_GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT) {
            GLfloat f = 0.f;
            gl->fGetFloatv(pname, &f);
            return JS::NumberValue(f);
        }
    }

    switch (pname) {
        //
        // String params
        //
        case LOCAL_GL_VENDOR:
        case LOCAL_GL_RENDERER:
            return StringValue(cx, "Mozilla", rv);
        case LOCAL_GL_VERSION:
            return StringValue(cx, "WebGL 1.0", rv);
        case LOCAL_GL_SHADING_LANGUAGE_VERSION:
            return StringValue(cx, "WebGL GLSL ES 1.0", rv);

        ////////////////////////////////
        // Single-value params

        // unsigned int
        case LOCAL_GL_CULL_FACE_MODE:
        case LOCAL_GL_FRONT_FACE:
        case LOCAL_GL_ACTIVE_TEXTURE:
        case LOCAL_GL_STENCIL_FUNC:
        case LOCAL_GL_STENCIL_FAIL:
        case LOCAL_GL_STENCIL_PASS_DEPTH_FAIL:
        case LOCAL_GL_STENCIL_PASS_DEPTH_PASS:
        case LOCAL_GL_STENCIL_BACK_FUNC:
        case LOCAL_GL_STENCIL_BACK_FAIL:
        case LOCAL_GL_STENCIL_BACK_PASS_DEPTH_FAIL:
        case LOCAL_GL_STENCIL_BACK_PASS_DEPTH_PASS:
        case LOCAL_GL_DEPTH_FUNC:
        case LOCAL_GL_BLEND_SRC_RGB:
        case LOCAL_GL_BLEND_SRC_ALPHA:
        case LOCAL_GL_BLEND_DST_RGB:
        case LOCAL_GL_BLEND_DST_ALPHA:
        case LOCAL_GL_BLEND_EQUATION_RGB:
        case LOCAL_GL_BLEND_EQUATION_ALPHA:
        case LOCAL_GL_GENERATE_MIPMAP_HINT: {
            GLint i = 0;
            gl->fGetIntegerv(pname, &i);
            return JS::NumberValue(uint32_t(i));
        }
        case LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE: {
            if (mBoundReadFramebuffer) {
                FBStatus status = mBoundReadFramebuffer->CheckFramebufferStatus();
                if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
                    ErrorInvalidOperation("getParameter: Read framebuffer must be"
                                          " complete before querying"
                                          " IMPLEMENTATION_COLOR_READ_TYPE.");
                    return JS::NullValue();
                }
            }

            GLint i = 0;
            if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) {
                gl->fGetIntegerv(pname, &i);
            } else {
                i = LOCAL_GL_UNSIGNED_BYTE;
            }
            return JS::NumberValue(uint32_t(i));
        }
        case LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT: {
            if (mBoundReadFramebuffer) {
                FBStatus status = mBoundReadFramebuffer->CheckFramebufferStatus();
                if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
                    ErrorInvalidOperation("getParameter: Read framebuffer must be"
                                          " complete before querying"
                                          " IMPLEMENTATION_COLOR_READ_FORMAT.");
                    return JS::NullValue();
                }
            }

            GLint i = 0;
            if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) {
                gl->fGetIntegerv(pname, &i);
            } else {
                i = LOCAL_GL_RGBA;
            }
            return JS::NumberValue(uint32_t(i));
        }
        // int
        case LOCAL_GL_STENCIL_REF:
        case LOCAL_GL_STENCIL_BACK_REF: {
            GLint stencilBits = 0;
            if (!GetStencilBits(&stencilBits))
                return JS::NullValue();

            // Assuming stencils have 8 bits
            const GLint stencilMask = (1 << stencilBits) - 1;

            GLint refValue = 0;
            gl->fGetIntegerv(pname, &refValue);

            return JS::Int32Value(refValue & stencilMask);
        }
        case LOCAL_GL_STENCIL_BITS: {
            GLint stencilBits = 0;
            GetStencilBits(&stencilBits);
            return JS::Int32Value(stencilBits);
        }
        case LOCAL_GL_STENCIL_CLEAR_VALUE:
        case LOCAL_GL_UNPACK_ALIGNMENT:
        case LOCAL_GL_PACK_ALIGNMENT:
        case LOCAL_GL_SUBPIXEL_BITS:
        case LOCAL_GL_SAMPLE_BUFFERS:
        case LOCAL_GL_SAMPLES:
        case LOCAL_GL_MAX_VERTEX_ATTRIBS:
        case LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS:
        case LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS:
        case LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS:
        case LOCAL_GL_RED_BITS:
        case LOCAL_GL_GREEN_BITS:
        case LOCAL_GL_BLUE_BITS:
        case LOCAL_GL_DEPTH_BITS: {
            GLint i = 0;
            gl->fGetIntegerv(pname, &i);
            return JS::Int32Value(i);
        }
        case LOCAL_GL_ALPHA_BITS: {
            GLint i = 0;
            if (!mNeedsFakeNoAlpha) {
                gl->fGetIntegerv(pname, &i);
            }
            return JS::Int32Value(i);
        }
        case LOCAL_GL_MAX_TEXTURE_SIZE:
            return JS::Int32Value(mGLMaxTextureSize);

        case LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE:
            return JS::Int32Value(mGLMaxCubeMapTextureSize);

        case LOCAL_GL_MAX_RENDERBUFFER_SIZE:
            return JS::Int32Value(mGLMaxRenderbufferSize);

        case LOCAL_GL_MAX_VERTEX_UNIFORM_VECTORS:
            return JS::Int32Value(mGLMaxVertexUniformVectors);

        case LOCAL_GL_MAX_FRAGMENT_UNIFORM_VECTORS:
            return JS::Int32Value(mGLMaxFragmentUniformVectors);

        case LOCAL_GL_MAX_VARYING_VECTORS:
            return JS::Int32Value(mGLMaxVaryingVectors);

        case LOCAL_GL_NUM_COMPRESSED_TEXTURE_FORMATS:
            return JS::Int32Value(0);
        case LOCAL_GL_COMPRESSED_TEXTURE_FORMATS: {
            uint32_t length = mCompressedTextureFormats.Length();
            JSObject* obj = dom::Uint32Array::Create(cx, this, length,
                                                     mCompressedTextureFormats.Elements());
            if (!obj) {
                rv = NS_ERROR_OUT_OF_MEMORY;
            }
            return JS::ObjectOrNullValue(obj);
        }

        // unsigned int. here we may have to return very large values like 2^32-1 that can't be represented as
        // javascript integer values. We just return them as doubles and javascript doesn't care.
        case LOCAL_GL_STENCIL_BACK_VALUE_MASK:
            return JS::DoubleValue(mStencilValueMaskBack); // pass as FP value to allow large values such as 2^32-1.

        case LOCAL_GL_STENCIL_BACK_WRITEMASK:
            return JS::DoubleValue(mStencilWriteMaskBack);

        case LOCAL_GL_STENCIL_VALUE_MASK:
            return JS::DoubleValue(mStencilValueMaskFront);

        case LOCAL_GL_STENCIL_WRITEMASK:
            return JS::DoubleValue(mStencilWriteMaskFront);

        // float
        case LOCAL_GL_DEPTH_CLEAR_VALUE:
        case LOCAL_GL_LINE_WIDTH:
        case LOCAL_GL_POLYGON_OFFSET_FACTOR:
        case LOCAL_GL_POLYGON_OFFSET_UNITS:
        case LOCAL_GL_SAMPLE_COVERAGE_VALUE: {
            GLfloat f = 0.f;
            gl->fGetFloatv(pname, &f);
            return JS::DoubleValue(f);
        }

        // bool
        case LOCAL_GL_BLEND:
        case LOCAL_GL_DEPTH_TEST:
        case LOCAL_GL_STENCIL_TEST:
        case LOCAL_GL_CULL_FACE:
        case LOCAL_GL_DITHER:
        case LOCAL_GL_POLYGON_OFFSET_FILL:
        case LOCAL_GL_SCISSOR_TEST:
        case LOCAL_GL_SAMPLE_COVERAGE_INVERT:
        case LOCAL_GL_DEPTH_WRITEMASK: {
            realGLboolean b = 0;
            gl->fGetBooleanv(pname, &b);
            return JS::BooleanValue(bool(b));
        }

        // bool, WebGL-specific
        case UNPACK_FLIP_Y_WEBGL:
            return JS::BooleanValue(mPixelStoreFlipY);
        case UNPACK_PREMULTIPLY_ALPHA_WEBGL:
            return JS::BooleanValue(mPixelStorePremultiplyAlpha);

        // uint, WebGL-specific
        case UNPACK_COLORSPACE_CONVERSION_WEBGL:
            return JS::NumberValue(uint32_t(mPixelStoreColorspaceConversion));

        ////////////////////////////////
        // Complex values

        // 2 floats
        case LOCAL_GL_DEPTH_RANGE:
        case LOCAL_GL_ALIASED_POINT_SIZE_RANGE:
        case LOCAL_GL_ALIASED_LINE_WIDTH_RANGE: {
            GLfloat fv[2] = { 0 };
            gl->fGetFloatv(pname, fv);
            JSObject* obj = dom::Float32Array::Create(cx, this, 2, fv);
            if (!obj) {
                rv = NS_ERROR_OUT_OF_MEMORY;
            }
            return JS::ObjectOrNullValue(obj);
        }

        // 4 floats
        case LOCAL_GL_COLOR_CLEAR_VALUE:
        case LOCAL_GL_BLEND_COLOR: {
            GLfloat fv[4] = { 0 };
            gl->fGetFloatv(pname, fv);
            JSObject* obj = dom::Float32Array::Create(cx, this, 4, fv);
            if (!obj) {
                rv = NS_ERROR_OUT_OF_MEMORY;
            }
            return JS::ObjectOrNullValue(obj);
        }

        // 2 ints
        case LOCAL_GL_MAX_VIEWPORT_DIMS: {
            GLint iv[2] = { 0 };
            gl->fGetIntegerv(pname, iv);
            JSObject* obj = dom::Int32Array::Create(cx, this, 2, iv);
            if (!obj) {
                rv = NS_ERROR_OUT_OF_MEMORY;
            }
            return JS::ObjectOrNullValue(obj);
        }

        // 4 ints
        case LOCAL_GL_SCISSOR_BOX:
        case LOCAL_GL_VIEWPORT: {
            GLint iv[4] = { 0 };
            gl->fGetIntegerv(pname, iv);
            JSObject* obj = dom::Int32Array::Create(cx, this, 4, iv);
            if (!obj) {
                rv = NS_ERROR_OUT_OF_MEMORY;
            }
            return JS::ObjectOrNullValue(obj);
        }

        // 4 bools
        case LOCAL_GL_COLOR_WRITEMASK: {
            realGLboolean gl_bv[4] = { 0 };
            gl->fGetBooleanv(pname, gl_bv);
            bool vals[4] = { bool(gl_bv[0]), bool(gl_bv[1]),
                             bool(gl_bv[2]), bool(gl_bv[3]) };
            JS::Rooted<JS::Value> arr(cx);
            if (!dom::ToJSValue(cx, vals, &arr)) {
                rv = NS_ERROR_OUT_OF_MEMORY;
            }
            return arr;
        }

        case LOCAL_GL_ARRAY_BUFFER_BINDING: {
            return WebGLObjectAsJSValue(cx, mBoundArrayBuffer.get(), rv);
        }

        case LOCAL_GL_ELEMENT_ARRAY_BUFFER_BINDING: {
            return WebGLObjectAsJSValue(cx, mBoundVertexArray->mElementArrayBuffer.get(), rv);
        }

        case LOCAL_GL_RENDERBUFFER_BINDING: {
            return WebGLObjectAsJSValue(cx, mBoundRenderbuffer.get(), rv);
        }

        // DRAW_FRAMEBUFFER_BINDING is the same as FRAMEBUFFER_BINDING.
        case LOCAL_GL_FRAMEBUFFER_BINDING: {
            return WebGLObjectAsJSValue(cx, mBoundDrawFramebuffer.get(), rv);
        }

        case LOCAL_GL_CURRENT_PROGRAM: {
            return WebGLObjectAsJSValue(cx, mCurrentProgram.get(), rv);
        }

        case LOCAL_GL_TEXTURE_BINDING_2D: {
            return WebGLObjectAsJSValue(cx, mBound2DTextures[mActiveTexture].get(), rv);
        }

        case LOCAL_GL_TEXTURE_BINDING_CUBE_MAP: {
            return WebGLObjectAsJSValue(cx, mBoundCubeMapTextures[mActiveTexture].get(), rv);
        }

        default:
            break;
    }

    ErrorInvalidEnumInfo("getParameter: parameter", pname);
    return JS::NullValue();
}
void
WebGL2Context::GetActiveUniforms(JSContext* cx, const WebGLProgram& program,
                                 const dom::Sequence<GLuint>& uniformIndices,
                                 GLenum pname, JS::MutableHandleValue retval)
{
    const char funcName[] = "getActiveUniforms";
    retval.setNull();
    if (IsContextLost())
        return;

    if (!ValidateUniformEnum(this, pname, funcName))
        return;

    if (!ValidateObject("getActiveUniforms: program", program))
        return;

    const auto& numActiveUniforms = program.LinkInfo()->uniforms.size();
    for (const auto& curIndex : uniformIndices) {
        if (curIndex >= numActiveUniforms) {
            ErrorInvalidValue("%s: Too-large active uniform index queried.", funcName);
            return;
        }
    }

    const auto& count = uniformIndices.Length();

    JS::Rooted<JSObject*> array(cx, JS_NewArrayObject(cx, count));
    UniquePtr<GLint[]> samples(new GLint[count]);
    if (!array || !samples) {
        ErrorOutOfMemory("%s: Failed to allocate buffers.", funcName);
        return;
    }
    retval.setObject(*array);

    MakeContextCurrent();
    gl->fGetActiveUniformsiv(program.mGLName, count, uniformIndices.Elements(), pname,
                             samples.get());

    switch (pname) {
    case LOCAL_GL_UNIFORM_TYPE:
    case LOCAL_GL_UNIFORM_SIZE:
    case LOCAL_GL_UNIFORM_BLOCK_INDEX:
    case LOCAL_GL_UNIFORM_OFFSET:
    case LOCAL_GL_UNIFORM_ARRAY_STRIDE:
    case LOCAL_GL_UNIFORM_MATRIX_STRIDE:
        for (size_t i = 0; i < count; ++i) {
            JS::RootedValue value(cx);
            value = JS::Int32Value(samples[i]);
            if (!JS_DefineElement(cx, array, i, value, JSPROP_ENUMERATE))
                return;
        }
        break;
    case LOCAL_GL_UNIFORM_IS_ROW_MAJOR:
        for (size_t i = 0; i < count; ++i) {
            JS::RootedValue value(cx);
            value = JS::BooleanValue(samples[i]);
            if (!JS_DefineElement(cx, array, i, value, JSPROP_ENUMERATE))
                return;
        }
        break;

    default:
        MOZ_CRASH("Invalid pname");
    }
}