Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/canvas/WebGLProgram.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#include "WebGLProgram.h"
7
8
#include "GLContext.h"
9
#include "mozilla/CheckedInt.h"
10
#include "mozilla/dom/WebGL2RenderingContextBinding.h"
11
#include "mozilla/dom/WebGLRenderingContextBinding.h"
12
#include "mozilla/RefPtr.h"
13
#include "nsPrintfCString.h"
14
#include "WebGLActiveInfo.h"
15
#include "WebGLBuffer.h"
16
#include "WebGLContext.h"
17
#include "WebGLShader.h"
18
#include "WebGLTransformFeedback.h"
19
#include "WebGLUniformLocation.h"
20
#include "WebGLValidateStrings.h"
21
#include "WebGLVertexArray.h"
22
23
namespace mozilla {
24
25
/* If `name`: "foo[3]"
26
 * Then returns true, with
27
 *     `out_baseName`: "foo"
28
 *     `out_isArray`: true
29
 *     `out_index`: 3
30
 *
31
 * If `name`: "foo"
32
 * Then returns true, with
33
 *     `out_baseName`: "foo"
34
 *     `out_isArray`: false
35
 *     `out_index`: 0
36
 */
37
static bool
38
ParseName(const nsCString& name, nsCString* const out_baseName,
39
          bool* const out_isArray, size_t* const out_arrayIndex)
40
0
{
41
0
    int32_t indexEnd = name.RFind("]");
42
0
    if (indexEnd == -1 ||
43
0
        (uint32_t)indexEnd != name.Length() - 1)
44
0
    {
45
0
        *out_baseName = name;
46
0
        *out_isArray = false;
47
0
        *out_arrayIndex = 0;
48
0
        return true;
49
0
    }
50
0
51
0
    int32_t indexOpenBracket = name.RFind("[");
52
0
    if (indexOpenBracket == -1)
53
0
        return false;
54
0
55
0
    uint32_t indexStart = indexOpenBracket + 1;
56
0
    uint32_t indexLen = indexEnd - indexStart;
57
0
    if (indexLen == 0)
58
0
        return false;
59
0
60
0
    const nsAutoCString indexStr(Substring(name, indexStart, indexLen));
61
0
62
0
    nsresult errorcode;
63
0
    int32_t indexNum = indexStr.ToInteger(&errorcode);
64
0
    if (NS_FAILED(errorcode))
65
0
        return false;
66
0
67
0
    if (indexNum < 0)
68
0
        return false;
69
0
70
0
    *out_baseName = StringHead(name, indexOpenBracket);
71
0
    *out_isArray = true;
72
0
    *out_arrayIndex = indexNum;
73
0
    return true;
74
0
}
75
76
static void
77
AssembleName(const nsCString& baseName, bool isArray, size_t arrayIndex,
78
             nsCString* const out_name)
79
0
{
80
0
    *out_name = baseName;
81
0
    if (isArray) {
82
0
        out_name->Append('[');
83
0
        out_name->AppendInt(uint64_t(arrayIndex));
84
0
        out_name->Append(']');
85
0
    }
86
0
}
87
88
////
89
90
static GLenum
91
AttribBaseType(GLenum attribType)
92
0
{
93
0
    switch (attribType) {
94
0
    case LOCAL_GL_FLOAT:
95
0
    case LOCAL_GL_FLOAT_VEC2:
96
0
    case LOCAL_GL_FLOAT_VEC3:
97
0
    case LOCAL_GL_FLOAT_VEC4:
98
0
99
0
    case LOCAL_GL_FLOAT_MAT2:
100
0
    case LOCAL_GL_FLOAT_MAT2x3:
101
0
    case LOCAL_GL_FLOAT_MAT2x4:
102
0
103
0
    case LOCAL_GL_FLOAT_MAT3x2:
104
0
    case LOCAL_GL_FLOAT_MAT3:
105
0
    case LOCAL_GL_FLOAT_MAT3x4:
106
0
107
0
    case LOCAL_GL_FLOAT_MAT4x2:
108
0
    case LOCAL_GL_FLOAT_MAT4x3:
109
0
    case LOCAL_GL_FLOAT_MAT4:
110
0
        return LOCAL_GL_FLOAT;
111
0
112
0
    case LOCAL_GL_INT:
113
0
    case LOCAL_GL_INT_VEC2:
114
0
    case LOCAL_GL_INT_VEC3:
115
0
    case LOCAL_GL_INT_VEC4:
116
0
        return LOCAL_GL_INT;
117
0
118
0
    case LOCAL_GL_UNSIGNED_INT:
119
0
    case LOCAL_GL_UNSIGNED_INT_VEC2:
120
0
    case LOCAL_GL_UNSIGNED_INT_VEC3:
121
0
    case LOCAL_GL_UNSIGNED_INT_VEC4:
122
0
        return LOCAL_GL_UNSIGNED_INT;
123
0
124
0
    default:
125
0
        MOZ_ASSERT(false, "unexpected attrib elemType");
126
0
        return 0;
127
0
    }
128
0
}
129
130
////
131
132
/*static*/ const webgl::UniformInfo::TexListT*
133
webgl::UniformInfo::GetTexList(WebGLActiveInfo* activeInfo)
134
0
{
135
0
    const auto& webgl = activeInfo->mWebGL;
136
0
137
0
    switch (activeInfo->mElemType) {
138
0
    case LOCAL_GL_SAMPLER_2D:
139
0
    case LOCAL_GL_SAMPLER_2D_SHADOW:
140
0
    case LOCAL_GL_INT_SAMPLER_2D:
141
0
    case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
142
0
        return &webgl->mBound2DTextures;
143
0
144
0
    case LOCAL_GL_SAMPLER_CUBE:
145
0
    case LOCAL_GL_SAMPLER_CUBE_SHADOW:
146
0
    case LOCAL_GL_INT_SAMPLER_CUBE:
147
0
    case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
148
0
        return &webgl->mBoundCubeMapTextures;
149
0
150
0
    case LOCAL_GL_SAMPLER_3D:
151
0
    case LOCAL_GL_INT_SAMPLER_3D:
152
0
    case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
153
0
        return &webgl->mBound3DTextures;
154
0
155
0
    case LOCAL_GL_SAMPLER_2D_ARRAY:
156
0
    case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
157
0
    case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
158
0
    case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
159
0
        return &webgl->mBound2DArrayTextures;
160
0
161
0
    default:
162
0
        return nullptr;
163
0
    }
164
0
}
165
166
webgl::UniformInfo::UniformInfo(WebGLActiveInfo* activeInfo)
167
    : mActiveInfo(activeInfo)
168
    , mSamplerTexList(GetTexList(activeInfo))
169
0
{
170
0
    if (mSamplerTexList) {
171
0
        mSamplerValues.assign(mActiveInfo->mElemCount, 0);
172
0
    }
173
0
}
174
175
//////////
176
177
//#define DUMP_SHADERVAR_MAPPINGS
178
179
static already_AddRefed<const webgl::LinkedProgramInfo>
180
QueryProgramInfo(WebGLProgram* prog, gl::GLContext* gl)
181
0
{
182
0
    WebGLContext* const webgl = prog->mContext;
183
0
184
0
    RefPtr<webgl::LinkedProgramInfo> info(new webgl::LinkedProgramInfo(prog));
185
0
186
0
    GLuint maxAttribLenWithNull = 0;
187
0
    gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_ATTRIBUTE_MAX_LENGTH,
188
0
                      (GLint*)&maxAttribLenWithNull);
189
0
    if (maxAttribLenWithNull < 1)
190
0
        maxAttribLenWithNull = 1;
191
0
192
0
    GLuint maxUniformLenWithNull = 0;
193
0
    gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORM_MAX_LENGTH,
194
0
                      (GLint*)&maxUniformLenWithNull);
195
0
    if (maxUniformLenWithNull < 1)
196
0
        maxUniformLenWithNull = 1;
197
0
198
0
    GLuint maxUniformBlockLenWithNull = 0;
199
0
    if (gl->IsSupported(gl::GLFeature::uniform_buffer_object)) {
200
0
        gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH,
201
0
                          (GLint*)&maxUniformBlockLenWithNull);
202
0
        if (maxUniformBlockLenWithNull < 1)
203
0
            maxUniformBlockLenWithNull = 1;
204
0
    }
205
0
206
0
    GLuint maxTransformFeedbackVaryingLenWithNull = 0;
207
0
    if (gl->IsSupported(gl::GLFeature::transform_feedback2)) {
208
0
        gl->fGetProgramiv(prog->mGLName, LOCAL_GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH,
209
0
                          (GLint*)&maxTransformFeedbackVaryingLenWithNull);
210
0
        if (maxTransformFeedbackVaryingLenWithNull < 1)
211
0
            maxTransformFeedbackVaryingLenWithNull = 1;
212
0
    }
213
0
214
0
    // Attribs (can't be arrays)
215
0
216
0
    GLuint numActiveAttribs = 0;
217
0
    gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_ATTRIBUTES,
218
0
                      (GLint*)&numActiveAttribs);
219
0
220
0
    for (GLuint i = 0; i < numActiveAttribs; i++) {
221
0
        nsAutoCString mappedName;
222
0
        mappedName.SetLength(maxAttribLenWithNull - 1);
223
0
224
0
        GLsizei lengthWithoutNull = 0;
225
0
        GLint elemCount = 0; // `size`
226
0
        GLenum elemType = 0; // `type`
227
0
        gl->fGetActiveAttrib(prog->mGLName, i, mappedName.Length()+1, &lengthWithoutNull,
228
0
                             &elemCount, &elemType, mappedName.BeginWriting());
229
0
        GLenum error = gl->fGetError();
230
0
        if (error != LOCAL_GL_NO_ERROR) {
231
0
            gfxCriticalNote << "Failed to do glGetActiveAttrib: " << error;
232
0
        }
233
0
234
0
        mappedName.SetLength(lengthWithoutNull);
235
0
236
0
        ////
237
0
238
0
        nsCString userName;
239
0
        if (!prog->FindAttribUserNameByMappedName(mappedName, &userName)) {
240
0
            userName = mappedName;
241
0
        }
242
0
243
0
        ///////
244
0
245
0
        GLint loc = gl->fGetAttribLocation(prog->mGLName,
246
0
                                           mappedName.BeginReading());
247
0
        if (gl->WorkAroundDriverBugs() &&
248
0
            mappedName.EqualsIgnoreCase("gl_", 3))
249
0
        {
250
0
            // Bug 1328559: Appears problematic on ANGLE and OSX, but not Linux or Win+GL.
251
0
            loc = -1;
252
0
        }
253
#ifdef DUMP_SHADERVAR_MAPPINGS
254
        printf_stderr("[attrib %u/%u] @%i %s->%s\n", i, numActiveAttribs, loc,
255
                      userName.BeginReading(), mappedName.BeginReading());
256
#endif
257
0
        MOZ_ASSERT_IF(mappedName.EqualsIgnoreCase("gl_", 3), loc == -1);
258
0
259
0
        ///////
260
0
261
0
        const bool isArray = false;
262
0
        const RefPtr<WebGLActiveInfo> activeInfo = new WebGLActiveInfo(webgl, elemCount,
263
0
                                                                       elemType, isArray,
264
0
                                                                       userName,
265
0
                                                                       mappedName);
266
0
        const GLenum baseType = AttribBaseType(elemType);
267
0
        const webgl::AttribInfo attrib = {activeInfo, loc, baseType};
268
0
        info->attribs.push_back(attrib);
269
0
270
0
        if (loc == 0) {
271
0
            info->attrib0Active = true;
272
0
        }
273
0
    }
274
0
275
0
    // Uniforms (can be basically anything)
276
0
277
0
    const bool needsCheckForArrays = gl->WorkAroundDriverBugs();
278
0
279
0
    GLuint numActiveUniforms = 0;
280
0
    gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORMS,
281
0
                      (GLint*)&numActiveUniforms);
282
0
283
0
    for (GLuint i = 0; i < numActiveUniforms; i++) {
284
0
        nsAutoCString mappedName;
285
0
        mappedName.SetLength(maxUniformLenWithNull - 1);
286
0
287
0
        GLsizei lengthWithoutNull = 0;
288
0
        GLint elemCount = 0; // `size`
289
0
        GLenum elemType = 0; // `type`
290
0
        gl->fGetActiveUniform(prog->mGLName, i, mappedName.Length()+1, &lengthWithoutNull,
291
0
                              &elemCount, &elemType, mappedName.BeginWriting());
292
0
293
0
        mappedName.SetLength(lengthWithoutNull);
294
0
295
0
        ///////
296
0
297
0
        nsAutoCString baseMappedName;
298
0
        bool isArray;
299
0
        size_t arrayIndex;
300
0
        if (!ParseName(mappedName, &baseMappedName, &isArray, &arrayIndex))
301
0
            MOZ_CRASH("GFX: Failed to parse `mappedName` received from driver.");
302
0
303
0
        // Note that for good drivers, `isArray` should already be correct.
304
0
        // However, if FindUniform succeeds, it will be validator-guaranteed correct.
305
0
306
0
        ///////
307
0
308
0
        nsAutoCString baseUserName;
309
0
        if (!prog->FindUniformByMappedName(baseMappedName, &baseUserName, &isArray)) {
310
0
            // Validator likely missing.
311
0
            baseUserName = baseMappedName;
312
0
313
0
            if (needsCheckForArrays && !isArray) {
314
0
                // By GLES 3, GetUniformLocation("foo[0]") should return -1 if `foo` is
315
0
                // not an array. Our current linux Try slaves return the location of `foo`
316
0
                // anyways, though.
317
0
                std::string mappedNameStr = baseMappedName.BeginReading();
318
0
                mappedNameStr += "[0]";
319
0
320
0
                GLint loc = gl->fGetUniformLocation(prog->mGLName, mappedNameStr.c_str());
321
0
                if (loc != -1)
322
0
                    isArray = true;
323
0
            }
324
0
        }
325
0
326
0
        ///////
327
0
328
#ifdef DUMP_SHADERVAR_MAPPINGS
329
        printf_stderr("[uniform %u/%u] %s->%s\n", i, numActiveUniforms,
330
                      baseUserName.BeginReading(), mappedName.BeginReading());
331
#endif
332
333
0
        ///////
334
0
335
0
        const RefPtr<WebGLActiveInfo> activeInfo = new WebGLActiveInfo(webgl, elemCount,
336
0
                                                                       elemType, isArray,
337
0
                                                                       baseUserName,
338
0
                                                                       baseMappedName);
339
0
340
0
        auto* uniform = new webgl::UniformInfo(activeInfo);
341
0
        info->uniforms.push_back(uniform);
342
0
343
0
        if (uniform->mSamplerTexList) {
344
0
            info->uniformSamplers.push_back(uniform);
345
0
        }
346
0
    }
347
0
348
0
    // Uniform Blocks (can be arrays, but can't contain sampler types)
349
0
350
0
    if (gl->IsSupported(gl::GLFeature::uniform_buffer_object)) {
351
0
        GLuint numActiveUniformBlocks = 0;
352
0
        gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORM_BLOCKS,
353
0
                          (GLint*)&numActiveUniformBlocks);
354
0
355
0
        for (GLuint i = 0; i < numActiveUniformBlocks; i++) {
356
0
            nsAutoCString mappedName;
357
0
            mappedName.SetLength(maxUniformBlockLenWithNull - 1);
358
0
359
0
            GLint lengthWithoutNull;
360
0
            gl->fGetActiveUniformBlockiv(prog->mGLName, i, LOCAL_GL_UNIFORM_BLOCK_NAME_LENGTH, &lengthWithoutNull);
361
0
            gl->fGetActiveUniformBlockName(prog->mGLName, i, maxUniformBlockLenWithNull, &lengthWithoutNull, mappedName.BeginWriting());
362
0
            mappedName.SetLength(lengthWithoutNull);
363
0
364
0
            ////
365
0
366
0
            nsCString userName;
367
0
            if (!prog->UnmapUniformBlockName(mappedName, &userName))
368
0
                continue;
369
0
370
#ifdef DUMP_SHADERVAR_MAPPINGS
371
            printf_stderr("[uniform block %u/%u] %s->%s\n", i, numActiveUniformBlocks,
372
                          userName.BeginReading(), mappedName.BeginReading());
373
#endif
374
375
0
            ////
376
0
377
0
            GLuint dataSize = 0;
378
0
            gl->fGetActiveUniformBlockiv(prog->mGLName, i,
379
0
                                         LOCAL_GL_UNIFORM_BLOCK_DATA_SIZE,
380
0
                                         (GLint*)&dataSize);
381
0
382
0
383
0
            auto* block = new webgl::UniformBlockInfo(webgl, userName, mappedName,
384
0
                                                      dataSize);
385
0
            info->uniformBlocks.push_back(block);
386
0
        }
387
0
    }
388
0
389
0
    // Transform feedback varyings (can be arrays)
390
0
391
0
    if (gl->IsSupported(gl::GLFeature::transform_feedback2)) {
392
0
        GLuint numTransformFeedbackVaryings = 0;
393
0
        gl->fGetProgramiv(prog->mGLName, LOCAL_GL_TRANSFORM_FEEDBACK_VARYINGS,
394
0
                          (GLint*)&numTransformFeedbackVaryings);
395
0
396
0
        for (GLuint i = 0; i < numTransformFeedbackVaryings; i++) {
397
0
            nsAutoCString mappedName;
398
0
            mappedName.SetLength(maxTransformFeedbackVaryingLenWithNull - 1);
399
0
400
0
            GLint lengthWithoutNull;
401
0
            GLsizei elemCount;
402
0
            GLenum elemType;
403
0
            gl->fGetTransformFeedbackVarying(prog->mGLName, i,
404
0
                                             maxTransformFeedbackVaryingLenWithNull,
405
0
                                             &lengthWithoutNull, &elemCount, &elemType,
406
0
                                             mappedName.BeginWriting());
407
0
            mappedName.SetLength(lengthWithoutNull);
408
0
409
0
            ////
410
0
411
0
            nsAutoCString baseMappedName;
412
0
            bool isArray;
413
0
            size_t arrayIndex;
414
0
            if (!ParseName(mappedName, &baseMappedName, &isArray, &arrayIndex))
415
0
                MOZ_CRASH("GFX: Failed to parse `mappedName` received from driver.");
416
0
417
0
            nsAutoCString baseUserName;
418
0
            if (!prog->FindVaryingByMappedName(mappedName, &baseUserName, &isArray)) {
419
0
                baseUserName = baseMappedName;
420
0
            }
421
0
422
0
            ////
423
0
424
#ifdef DUMP_SHADERVAR_MAPPINGS
425
            printf_stderr("[transform feedback varying %u/%u] %s->%s\n", i,
426
                          numTransformFeedbackVaryings, baseUserName.BeginReading(),
427
                          mappedName.BeginReading());
428
#endif
429
430
0
            const RefPtr<WebGLActiveInfo> activeInfo = new WebGLActiveInfo(webgl,
431
0
                                                                           elemCount,
432
0
                                                                           elemType,
433
0
                                                                           isArray,
434
0
                                                                           baseUserName,
435
0
                                                                           mappedName);
436
0
            info->transformFeedbackVaryings.push_back(activeInfo);
437
0
        }
438
0
    }
439
0
440
0
    // Frag outputs
441
0
442
0
    prog->EnumerateFragOutputs(info->fragDataMap);
443
0
444
0
    return info.forget();
445
0
}
446
447
////////////////////////////////////////////////////////////////////////////////
448
449
webgl::LinkedProgramInfo::LinkedProgramInfo(WebGLProgram* prog)
450
    : prog(prog)
451
    , transformFeedbackBufferMode(prog->mNextLink_TransformFeedbackBufferMode)
452
    , attrib0Active(false)
453
0
{ }
454
455
webgl::LinkedProgramInfo::~LinkedProgramInfo()
456
0
{
457
0
    for (auto& cur : uniforms) {
458
0
        delete cur;
459
0
    }
460
0
    for (auto& cur : uniformBlocks) {
461
0
        delete cur;
462
0
    }
463
0
}
464
465
const webgl::CachedDrawFetchLimits*
466
webgl::LinkedProgramInfo::GetDrawFetchLimits() const
467
0
{
468
0
    const auto& webgl = prog->mContext;
469
0
    const auto& vao = webgl->mBoundVertexArray;
470
0
471
0
    const auto found = mDrawFetchCache.Find(vao);
472
0
    if (found)
473
0
        return found;
474
0
475
0
    std::vector<const CacheMapInvalidator*> cacheDeps;
476
0
    cacheDeps.push_back(vao.get());
477
0
    cacheDeps.push_back(&webgl->mGenericVertexAttribTypeInvalidator);
478
0
479
0
    {
480
0
        // We have to ensure that every enabled attrib array (not just the active ones)
481
0
        // has a non-null buffer.
482
0
        uint32_t i = 0;
483
0
        for (const auto& cur : vao->mAttribs) {
484
0
            if (cur.mEnabled && !cur.mBuf) {
485
0
                webgl->ErrorInvalidOperation("Vertex attrib array %u is enabled but"
486
0
                                             " has no buffer bound.",
487
0
                                             i);
488
0
                return nullptr;
489
0
            }
490
0
        }
491
0
    }
492
0
493
0
    bool hasActiveAttrib = false;
494
0
    bool hasActiveDivisor0 = false;
495
0
    webgl::CachedDrawFetchLimits fetchLimits = { UINT64_MAX, UINT64_MAX };
496
0
497
0
    for (const auto& progAttrib : this->attribs) {
498
0
        const auto& loc = progAttrib.mLoc;
499
0
        if (loc == -1)
500
0
            continue;
501
0
        hasActiveAttrib |= true;
502
0
503
0
        const auto& attribData = vao->mAttribs[loc];
504
0
        hasActiveDivisor0 |= (attribData.mDivisor == 0);
505
0
506
0
        GLenum attribDataBaseType;
507
0
        if (attribData.mEnabled) {
508
0
            MOZ_ASSERT(attribData.mBuf);
509
0
            if (attribData.mBuf->IsBoundForTF()) {
510
0
                webgl->ErrorInvalidOperation("Vertex attrib %u's buffer is bound for"
511
0
                                             " transform feedback.",
512
0
                                              loc);
513
0
                return nullptr;
514
0
            }
515
0
            cacheDeps.push_back(&attribData.mBuf->mFetchInvalidator);
516
0
517
0
            attribDataBaseType = attribData.BaseType();
518
0
519
0
            const size_t availBytes = attribData.mBuf->ByteLength();
520
0
            const auto availElems = AvailGroups(availBytes, attribData.ByteOffset(),
521
0
                                                attribData.BytesPerVertex(),
522
0
                                                attribData.ExplicitStride());
523
0
            if (attribData.mDivisor) {
524
0
                const auto availInstances = CheckedInt<uint64_t>(availElems) * attribData.mDivisor;
525
0
                if (availInstances.isValid()) {
526
0
                    fetchLimits.maxInstances = std::min(fetchLimits.maxInstances,
527
0
                                                        availInstances.value());
528
0
                } // If not valid, it overflowed too large, so we're super safe.
529
0
            } else {
530
0
                fetchLimits.maxVerts = std::min(fetchLimits.maxVerts, availElems);
531
0
            }
532
0
        } else {
533
0
            attribDataBaseType = webgl->mGenericVertexAttribTypes[loc];
534
0
        }
535
0
536
0
        if (attribDataBaseType != progAttrib.mBaseType) {
537
0
            nsCString progType, dataType;
538
0
            WebGLContext::EnumName(progAttrib.mBaseType, &progType);
539
0
            WebGLContext::EnumName(attribDataBaseType, &dataType);
540
0
            webgl->ErrorInvalidOperation("Vertex attrib %u requires data of type %s,"
541
0
                                         " but is being supplied with type %s.",
542
0
                                         loc, progType.BeginReading(),
543
0
                                         dataType.BeginReading());
544
0
            return nullptr;
545
0
        }
546
0
    }
547
0
548
0
    if (hasActiveAttrib && !hasActiveDivisor0) {
549
0
        webgl->ErrorInvalidOperation("One active vertex attrib (if any are active)"
550
0
                                     " must have a divisor of 0.");
551
0
        return nullptr;
552
0
    }
553
0
554
0
    // --
555
0
556
0
    return mDrawFetchCache.Insert(vao.get(), std::move(fetchLimits), std::move(cacheDeps));
557
0
}
558
559
////////////////////////////////////////////////////////////////////////////////
560
// WebGLProgram
561
562
WebGLProgram::WebGLProgram(WebGLContext* webgl)
563
    : WebGLRefCountedObject(webgl)
564
    , mGLName(webgl->gl->fCreateProgram())
565
    , mNumActiveTFOs(0)
566
    , mNextLink_TransformFeedbackBufferMode(LOCAL_GL_INTERLEAVED_ATTRIBS)
567
0
{
568
0
    mContext->mPrograms.insertBack(this);
569
0
}
570
571
WebGLProgram::~WebGLProgram()
572
0
{
573
0
    DeleteOnce();
574
0
}
575
576
void
577
WebGLProgram::Delete()
578
0
{
579
0
    gl::GLContext* gl = mContext->GL();
580
0
    gl->fDeleteProgram(mGLName);
581
0
582
0
    mVertShader = nullptr;
583
0
    mFragShader = nullptr;
584
0
585
0
    mMostRecentLinkInfo = nullptr;
586
0
587
0
    LinkedListElement<WebGLProgram>::removeFrom(mContext->mPrograms);
588
0
}
589
590
////////////////////////////////////////////////////////////////////////////////
591
// GL funcs
592
593
void
594
WebGLProgram::AttachShader(WebGLShader* shader)
595
0
{
596
0
    WebGLRefPtr<WebGLShader>* shaderSlot;
597
0
    switch (shader->mType) {
598
0
    case LOCAL_GL_VERTEX_SHADER:
599
0
        shaderSlot = &mVertShader;
600
0
        break;
601
0
    case LOCAL_GL_FRAGMENT_SHADER:
602
0
        shaderSlot = &mFragShader;
603
0
        break;
604
0
    default:
605
0
        mContext->ErrorInvalidOperation("attachShader: Bad type for shader.");
606
0
        return;
607
0
    }
608
0
609
0
    if (*shaderSlot) {
610
0
        if (shader == *shaderSlot) {
611
0
            mContext->ErrorInvalidOperation("attachShader: `shader` is already attached.");
612
0
        } else {
613
0
            mContext->ErrorInvalidOperation("attachShader: Only one of each type of"
614
0
                                            " shader may be attached to a program.");
615
0
        }
616
0
        return;
617
0
    }
618
0
619
0
    *shaderSlot = shader;
620
0
621
0
    mContext->gl->fAttachShader(mGLName, shader->mGLName);
622
0
}
623
624
void
625
WebGLProgram::BindAttribLocation(GLuint loc, const nsAString& name)
626
0
{
627
0
    if (!ValidateGLSLVariableName(name, mContext))
628
0
        return;
629
0
630
0
    if (loc >= mContext->MaxVertexAttribs()) {
631
0
        mContext->ErrorInvalidValue("`location` must be less than"
632
0
                                    " MAX_VERTEX_ATTRIBS.");
633
0
        return;
634
0
    }
635
0
636
0
    if (StringBeginsWith(name, NS_LITERAL_STRING("gl_"))) {
637
0
        mContext->ErrorInvalidOperation("Can't set the location of a"
638
0
                                        " name that starts with 'gl_'.");
639
0
        return;
640
0
    }
641
0
642
0
    NS_LossyConvertUTF16toASCII asciiName(name);
643
0
644
0
    auto res = mNextLink_BoundAttribLocs.insert({asciiName, loc});
645
0
646
0
    const bool wasInserted = res.second;
647
0
    if (!wasInserted) {
648
0
        auto itr = res.first;
649
0
        itr->second = loc;
650
0
    }
651
0
}
652
653
void
654
WebGLProgram::DetachShader(const WebGLShader* shader)
655
0
{
656
0
    MOZ_ASSERT(shader);
657
0
658
0
    WebGLRefPtr<WebGLShader>* shaderSlot;
659
0
    switch (shader->mType) {
660
0
    case LOCAL_GL_VERTEX_SHADER:
661
0
        shaderSlot = &mVertShader;
662
0
        break;
663
0
    case LOCAL_GL_FRAGMENT_SHADER:
664
0
        shaderSlot = &mFragShader;
665
0
        break;
666
0
    default:
667
0
        mContext->ErrorInvalidOperation("attachShader: Bad type for shader.");
668
0
        return;
669
0
    }
670
0
671
0
    if (*shaderSlot != shader) {
672
0
        mContext->ErrorInvalidOperation("detachShader: `shader` is not attached.");
673
0
        return;
674
0
    }
675
0
676
0
    *shaderSlot = nullptr;
677
0
678
0
    mContext->gl->fDetachShader(mGLName, shader->mGLName);
679
0
}
680
681
already_AddRefed<WebGLActiveInfo>
682
WebGLProgram::GetActiveAttrib(GLuint index) const
683
0
{
684
0
    if (!mMostRecentLinkInfo) {
685
0
        RefPtr<WebGLActiveInfo> ret = WebGLActiveInfo::CreateInvalid(mContext);
686
0
        return ret.forget();
687
0
    }
688
0
689
0
    const auto& attribs = mMostRecentLinkInfo->attribs;
690
0
691
0
    if (index >= attribs.size()) {
692
0
        mContext->ErrorInvalidValue("`index` (%i) must be less than %s (%zu).",
693
0
                                    index, "ACTIVE_ATTRIBS", attribs.size());
694
0
        return nullptr;
695
0
    }
696
0
697
0
    RefPtr<WebGLActiveInfo> ret = attribs[index].mActiveInfo;
698
0
    return ret.forget();
699
0
}
700
701
already_AddRefed<WebGLActiveInfo>
702
WebGLProgram::GetActiveUniform(GLuint index) const
703
0
{
704
0
    if (!mMostRecentLinkInfo) {
705
0
        // According to the spec, this can return null.
706
0
        RefPtr<WebGLActiveInfo> ret = WebGLActiveInfo::CreateInvalid(mContext);
707
0
        return ret.forget();
708
0
    }
709
0
710
0
    const auto& uniforms = mMostRecentLinkInfo->uniforms;
711
0
712
0
    if (index >= uniforms.size()) {
713
0
        mContext->ErrorInvalidValue("`index` (%i) must be less than %s (%zu).",
714
0
                                    index, "ACTIVE_UNIFORMS", uniforms.size());
715
0
        return nullptr;
716
0
    }
717
0
718
0
    RefPtr<WebGLActiveInfo> ret = uniforms[index]->mActiveInfo;
719
0
    return ret.forget();
720
0
}
721
722
void
723
WebGLProgram::GetAttachedShaders(nsTArray<RefPtr<WebGLShader>>* const out) const
724
0
{
725
0
    out->TruncateLength(0);
726
0
727
0
    if (mVertShader)
728
0
        out->AppendElement(mVertShader);
729
0
730
0
    if (mFragShader)
731
0
        out->AppendElement(mFragShader);
732
0
}
733
734
GLint
735
WebGLProgram::GetAttribLocation(const nsAString& userName_wide) const
736
0
{
737
0
    if (!ValidateGLSLVariableName(userName_wide, mContext))
738
0
        return -1;
739
0
740
0
    if (!IsLinked()) {
741
0
        mContext->ErrorInvalidOperation("`program` must be linked.");
742
0
        return -1;
743
0
    }
744
0
745
0
    const NS_LossyConvertUTF16toASCII userName(userName_wide);
746
0
747
0
    const webgl::AttribInfo* info;
748
0
    if (!LinkInfo()->FindAttrib(userName, &info))
749
0
        return -1;
750
0
751
0
    return GLint(info->mLoc);
752
0
}
753
754
static GLint
755
GetFragDataByUserName(const WebGLProgram* prog,
756
                      const nsCString& userName)
757
0
{
758
0
    nsCString mappedName;
759
0
    if (!prog->LinkInfo()->MapFragDataName(userName, &mappedName))
760
0
        return -1;
761
0
762
0
    return prog->mContext->gl->fGetFragDataLocation(prog->mGLName, mappedName.BeginReading());
763
0
}
764
765
GLint
766
WebGLProgram::GetFragDataLocation(const nsAString& userName_wide) const
767
0
{
768
0
    if (!ValidateGLSLVariableName(userName_wide, mContext))
769
0
        return -1;
770
0
771
0
    if (!IsLinked()) {
772
0
        mContext->ErrorInvalidOperation("`program` must be linked.");
773
0
        return -1;
774
0
    }
775
0
776
0
    const NS_LossyConvertUTF16toASCII userName(userName_wide);
777
#ifdef XP_MACOSX
778
    const auto& gl = mContext->gl;
779
    if (gl->WorkAroundDriverBugs()) {
780
        // OSX doesn't return locs for indexed names, just the base names.
781
        // Indicated by failure in: conformance2/programs/gl-get-frag-data-location.html
782
        bool isArray;
783
        size_t arrayIndex;
784
        nsCString baseUserName;
785
        if (!ParseName(userName, &baseUserName, &isArray, &arrayIndex))
786
            return -1;
787
788
        if (arrayIndex >= mContext->mGLMaxDrawBuffers)
789
            return -1;
790
791
        const auto baseLoc = GetFragDataByUserName(this, baseUserName);
792
        const auto loc = baseLoc + GLint(arrayIndex);
793
        return loc;
794
    }
795
#endif
796
    return GetFragDataByUserName(this, userName);
797
0
}
798
799
void
800
WebGLProgram::GetProgramInfoLog(nsAString* const out) const
801
0
{
802
0
    CopyASCIItoUTF16(mLinkLog, *out);
803
0
}
804
805
static GLint
806
GetProgramiv(gl::GLContext* gl, GLuint program, GLenum pname)
807
0
{
808
0
    GLint ret = 0;
809
0
    gl->fGetProgramiv(program, pname, &ret);
810
0
    return ret;
811
0
}
812
813
JS::Value
814
WebGLProgram::GetProgramParameter(GLenum pname) const
815
0
{
816
0
    gl::GLContext* gl = mContext->gl;
817
0
818
0
    if (mContext->IsWebGL2()) {
819
0
        switch (pname) {
820
0
        case LOCAL_GL_ACTIVE_UNIFORM_BLOCKS:
821
0
            if (!IsLinked())
822
0
                return JS::NumberValue(0);
823
0
            return JS::NumberValue(LinkInfo()->uniformBlocks.size());
824
0
825
0
        case LOCAL_GL_TRANSFORM_FEEDBACK_VARYINGS:
826
0
            if (!IsLinked())
827
0
                return JS::NumberValue(0);
828
0
            return JS::NumberValue(LinkInfo()->transformFeedbackVaryings.size());
829
0
830
0
        case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_MODE:
831
0
            if (!IsLinked())
832
0
                return JS::NumberValue(LOCAL_GL_INTERLEAVED_ATTRIBS);
833
0
            return JS::NumberValue(LinkInfo()->transformFeedbackBufferMode);
834
0
       }
835
0
    }
836
0
837
0
    switch (pname) {
838
0
    case LOCAL_GL_ATTACHED_SHADERS:
839
0
        return JS::NumberValue( int(bool(mVertShader.get())) + int(bool(mFragShader)) );
840
0
841
0
    case LOCAL_GL_ACTIVE_UNIFORMS:
842
0
        if (!IsLinked())
843
0
            return JS::NumberValue(0);
844
0
        return JS::NumberValue(LinkInfo()->uniforms.size());
845
0
846
0
    case LOCAL_GL_ACTIVE_ATTRIBUTES:
847
0
        if (!IsLinked())
848
0
            return JS::NumberValue(0);
849
0
        return JS::NumberValue(LinkInfo()->attribs.size());
850
0
851
0
    case LOCAL_GL_DELETE_STATUS:
852
0
        return JS::BooleanValue(IsDeleteRequested());
853
0
854
0
    case LOCAL_GL_LINK_STATUS:
855
0
        return JS::BooleanValue(IsLinked());
856
0
857
0
    case LOCAL_GL_VALIDATE_STATUS:
858
#ifdef XP_MACOSX
859
        // See comment in ValidateProgram.
860
        if (gl->WorkAroundDriverBugs())
861
            return JS::BooleanValue(true);
862
#endif
863
        // Todo: Implement this in our code.
864
0
        return JS::BooleanValue(bool(GetProgramiv(gl, mGLName, pname)));
865
0
866
0
    default:
867
0
        mContext->ErrorInvalidEnumInfo("pname", pname);
868
0
        return JS::NullValue();
869
0
    }
870
0
}
871
872
GLuint
873
WebGLProgram::GetUniformBlockIndex(const nsAString& userName_wide) const
874
0
{
875
0
    if (!ValidateGLSLVariableName(userName_wide, mContext))
876
0
        return LOCAL_GL_INVALID_INDEX;
877
0
878
0
    if (!IsLinked()) {
879
0
        mContext->ErrorInvalidOperation("`program` must be linked.");
880
0
        return LOCAL_GL_INVALID_INDEX;
881
0
    }
882
0
883
0
    const NS_LossyConvertUTF16toASCII userName(userName_wide);
884
0
885
0
    const webgl::UniformBlockInfo* info = nullptr;
886
0
    for (const auto& cur : LinkInfo()->uniformBlocks) {
887
0
        if (cur->mUserName == userName) {
888
0
            info = cur;
889
0
            break;
890
0
        }
891
0
    }
892
0
    if (!info)
893
0
        return LOCAL_GL_INVALID_INDEX;
894
0
895
0
    const auto& mappedName = info->mMappedName;
896
0
897
0
    gl::GLContext* gl = mContext->GL();
898
0
    return gl->fGetUniformBlockIndex(mGLName, mappedName.BeginReading());
899
0
}
900
901
void
902
WebGLProgram::GetActiveUniformBlockName(GLuint uniformBlockIndex, nsAString& retval) const
903
0
{
904
0
    if (!IsLinked()) {
905
0
        mContext->ErrorInvalidOperation("`program` must be linked.");
906
0
        return;
907
0
    }
908
0
909
0
    const webgl::LinkedProgramInfo* linkInfo = LinkInfo();
910
0
    GLuint uniformBlockCount = (GLuint) linkInfo->uniformBlocks.size();
911
0
    if (uniformBlockIndex >= uniformBlockCount) {
912
0
        mContext->ErrorInvalidValue("index %u invalid.", uniformBlockIndex);
913
0
        return;
914
0
    }
915
0
916
0
    const auto& blockInfo = linkInfo->uniformBlocks[uniformBlockIndex];
917
0
    retval.Assign(NS_ConvertASCIItoUTF16(blockInfo->mUserName));
918
0
}
919
920
JS::Value
921
WebGLProgram::GetActiveUniformBlockParam(GLuint uniformBlockIndex, GLenum pname) const
922
0
{
923
0
    if (!IsLinked()) {
924
0
        mContext->ErrorInvalidOperation("`program` must be linked.");
925
0
        return JS::NullValue();
926
0
    }
927
0
928
0
    const webgl::LinkedProgramInfo* linkInfo = LinkInfo();
929
0
    GLuint uniformBlockCount = (GLuint)linkInfo->uniformBlocks.size();
930
0
    if (uniformBlockIndex >= uniformBlockCount) {
931
0
        mContext->ErrorInvalidValue("Index %u invalid.", uniformBlockIndex);
932
0
        return JS::NullValue();
933
0
    }
934
0
935
0
    gl::GLContext* gl = mContext->GL();
936
0
    GLint param = 0;
937
0
938
0
    switch (pname) {
939
0
    case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER:
940
0
    case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER:
941
0
        gl->fGetActiveUniformBlockiv(mGLName, uniformBlockIndex, pname, &param);
942
0
        return JS::BooleanValue(bool(param));
943
0
944
0
    case LOCAL_GL_UNIFORM_BLOCK_BINDING:
945
0
    case LOCAL_GL_UNIFORM_BLOCK_DATA_SIZE:
946
0
    case LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS:
947
0
        gl->fGetActiveUniformBlockiv(mGLName, uniformBlockIndex, pname, &param);
948
0
        return JS::NumberValue(param);
949
0
950
0
    default:
951
0
        MOZ_CRASH("bad `pname`.");
952
0
    }
953
0
}
954
955
JS::Value
956
WebGLProgram::GetActiveUniformBlockActiveUniforms(JSContext* cx, GLuint uniformBlockIndex,
957
                                                  ErrorResult* const out_error) const
958
0
{
959
0
    if (!IsLinked()) {
960
0
        mContext->ErrorInvalidOperation("`program` must be linked.");
961
0
        return JS::NullValue();
962
0
    }
963
0
964
0
    const webgl::LinkedProgramInfo* linkInfo = LinkInfo();
965
0
    GLuint uniformBlockCount = (GLuint)linkInfo->uniformBlocks.size();
966
0
    if (uniformBlockIndex >= uniformBlockCount) {
967
0
        mContext->ErrorInvalidValue("Index %u invalid.", uniformBlockIndex);
968
0
        return JS::NullValue();
969
0
    }
970
0
971
0
    gl::GLContext* gl = mContext->GL();
972
0
    GLint activeUniformCount = 0;
973
0
    gl->fGetActiveUniformBlockiv(mGLName, uniformBlockIndex,
974
0
                                 LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS,
975
0
                                 &activeUniformCount);
976
0
    JS::RootedObject obj(cx, dom::Uint32Array::Create(cx, mContext, activeUniformCount,
977
0
                                                      nullptr));
978
0
    if (!obj) {
979
0
        *out_error = NS_ERROR_OUT_OF_MEMORY;
980
0
        return JS::NullValue();
981
0
    }
982
0
983
0
    dom::Uint32Array result;
984
0
    DebugOnly<bool> inited = result.Init(obj);
985
0
    MOZ_ASSERT(inited);
986
0
    result.ComputeLengthAndData();
987
0
    gl->fGetActiveUniformBlockiv(mGLName, uniformBlockIndex,
988
0
                                 LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES,
989
0
                                 (GLint*)result.Data());
990
0
991
0
    return JS::ObjectValue(*obj);
992
0
}
993
994
already_AddRefed<WebGLUniformLocation>
995
WebGLProgram::GetUniformLocation(const nsAString& userName_wide) const
996
0
{
997
0
    if (!ValidateGLSLVariableName(userName_wide, mContext))
998
0
        return nullptr;
999
0
1000
0
    if (!IsLinked()) {
1001
0
        mContext->ErrorInvalidOperation("`program` must be linked.");
1002
0
        return nullptr;
1003
0
    }
1004
0
1005
0
    const NS_LossyConvertUTF16toASCII userName(userName_wide);
1006
0
1007
0
    // GLES 2.0.25, Section 2.10, p35
1008
0
    // If the the uniform location is an array, then the location of the first
1009
0
    // element of that array can be retrieved by either using the name of the
1010
0
    // uniform array, or the name of the uniform array appended with "[0]".
1011
0
    nsCString mappedName;
1012
0
    size_t arrayIndex;
1013
0
    webgl::UniformInfo* info;
1014
0
    if (!LinkInfo()->FindUniform(userName, &mappedName, &arrayIndex, &info))
1015
0
        return nullptr;
1016
0
1017
0
    gl::GLContext* gl = mContext->GL();
1018
0
1019
0
    GLint loc = gl->fGetUniformLocation(mGLName, mappedName.BeginReading());
1020
0
    if (loc == -1)
1021
0
        return nullptr;
1022
0
1023
0
    RefPtr<WebGLUniformLocation> locObj = new WebGLUniformLocation(mContext, LinkInfo(),
1024
0
                                                                   info, loc, arrayIndex);
1025
0
    return locObj.forget();
1026
0
}
1027
1028
void
1029
WebGLProgram::GetUniformIndices(const dom::Sequence<nsString>& uniformNames,
1030
                                dom::Nullable< nsTArray<GLuint> >& retval) const
1031
0
{
1032
0
    if (!IsLinked()) {
1033
0
        mContext->ErrorInvalidOperation("`program` must be linked.");
1034
0
        return;
1035
0
    }
1036
0
1037
0
    size_t count = uniformNames.Length();
1038
0
    nsTArray<GLuint>& arr = retval.SetValue();
1039
0
1040
0
    gl::GLContext* gl = mContext->GL();
1041
0
1042
0
    for (size_t i = 0; i < count; i++) {
1043
0
        const NS_LossyConvertUTF16toASCII userName(uniformNames[i]);
1044
0
1045
0
        nsCString mappedName;
1046
0
        size_t arrayIndex;
1047
0
        webgl::UniformInfo* info;
1048
0
        if (!LinkInfo()->FindUniform(userName, &mappedName, &arrayIndex, &info)) {
1049
0
            arr.AppendElement(LOCAL_GL_INVALID_INDEX);
1050
0
            continue;
1051
0
        }
1052
0
1053
0
        const GLchar* const mappedNameBegin = mappedName.get();
1054
0
1055
0
        GLuint index = LOCAL_GL_INVALID_INDEX;
1056
0
        gl->fGetUniformIndices(mGLName, 1, &mappedNameBegin, &index);
1057
0
        arr.AppendElement(index);
1058
0
    }
1059
0
}
1060
1061
void
1062
WebGLProgram::UniformBlockBinding(GLuint uniformBlockIndex,
1063
                                  GLuint uniformBlockBinding) const
1064
0
{
1065
0
    if (!IsLinked()) {
1066
0
        mContext->ErrorInvalidOperation("`program` must be linked.");
1067
0
        return;
1068
0
    }
1069
0
1070
0
    const auto& uniformBlocks = LinkInfo()->uniformBlocks;
1071
0
    if (uniformBlockIndex >= uniformBlocks.size()) {
1072
0
        mContext->ErrorInvalidValue("Index %u invalid.", uniformBlockIndex);
1073
0
        return;
1074
0
    }
1075
0
    const auto& uniformBlock = uniformBlocks[uniformBlockIndex];
1076
0
1077
0
    const auto& indexedBindings = mContext->mIndexedUniformBufferBindings;
1078
0
    if (uniformBlockBinding >= indexedBindings.size()) {
1079
0
        mContext->ErrorInvalidValue("Binding %u invalid.", uniformBlockBinding);
1080
0
        return;
1081
0
    }
1082
0
    const auto& indexedBinding = indexedBindings[uniformBlockBinding];
1083
0
1084
0
    ////
1085
0
1086
0
    gl::GLContext* gl = mContext->GL();
1087
0
    gl->fUniformBlockBinding(mGLName, uniformBlockIndex, uniformBlockBinding);
1088
0
1089
0
    ////
1090
0
1091
0
    uniformBlock->mBinding = &indexedBinding;
1092
0
}
1093
1094
bool
1095
WebGLProgram::ValidateForLink()
1096
0
{
1097
0
    if (!mVertShader || !mVertShader->IsCompiled()) {
1098
0
        mLinkLog.AssignLiteral("Must have a compiled vertex shader attached.");
1099
0
        return false;
1100
0
    }
1101
0
1102
0
    if (!mFragShader || !mFragShader->IsCompiled()) {
1103
0
        mLinkLog.AssignLiteral("Must have an compiled fragment shader attached.");
1104
0
        return false;
1105
0
    }
1106
0
1107
0
    if (!mFragShader->CanLinkTo(mVertShader, &mLinkLog))
1108
0
        return false;
1109
0
1110
0
    const auto& gl = mContext->gl;
1111
0
1112
0
    if (gl->WorkAroundDriverBugs() &&
1113
0
        mContext->mIsMesa)
1114
0
    {
1115
0
        // Bug 777028: Mesa can't handle more than 16 samplers per program,
1116
0
        // counting each array entry.
1117
0
        size_t numSamplerUniforms_upperBound = mVertShader->CalcNumSamplerUniforms() +
1118
0
                                               mFragShader->CalcNumSamplerUniforms();
1119
0
        if (numSamplerUniforms_upperBound > 16) {
1120
0
            mLinkLog.AssignLiteral("Programs with more than 16 samplers are disallowed on"
1121
0
                                   " Mesa drivers to avoid crashing.");
1122
0
            return false;
1123
0
        }
1124
0
1125
0
        // Bug 1203135: Mesa crashes internally if we exceed the reported maximum attribute count.
1126
0
        if (mVertShader->NumAttributes() > mContext->MaxVertexAttribs()) {
1127
0
            mLinkLog.AssignLiteral("Number of attributes exceeds Mesa's reported max"
1128
0
                                   " attribute count.");
1129
0
            return false;
1130
0
        }
1131
0
    }
1132
0
1133
0
    return true;
1134
0
}
1135
1136
void
1137
WebGLProgram::LinkProgram()
1138
0
{
1139
0
    if (mNumActiveTFOs) {
1140
0
        mContext->ErrorInvalidOperation("Program is in-use by one or more active"
1141
0
                                        " transform feedback objects.");
1142
0
        return;
1143
0
    }
1144
0
1145
0
    // as some of the validation changes program state
1146
0
1147
0
    mLinkLog.Truncate();
1148
0
    mMostRecentLinkInfo = nullptr;
1149
0
1150
0
    if (!ValidateForLink()) {
1151
0
        mContext->GenerateWarning("%s", mLinkLog.BeginReading());
1152
0
        return;
1153
0
    }
1154
0
1155
0
    // Bind the attrib locations.
1156
0
    // This can't be done trivially, because we have to deal with mapped attrib names.
1157
0
    for (const auto& pair : mNextLink_BoundAttribLocs) {
1158
0
        const auto& name = pair.first;
1159
0
        const auto& index = pair.second;
1160
0
1161
0
        mVertShader->BindAttribLocation(mGLName, name, index);
1162
0
    }
1163
0
1164
0
    // Storage for transform feedback varyings before link.
1165
0
    // (Work around for bug seen on nVidia drivers.)
1166
0
    std::vector<std::string> scopedMappedTFVaryings;
1167
0
1168
0
    if (mContext->IsWebGL2()) {
1169
0
        mVertShader->MapTransformFeedbackVaryings(mNextLink_TransformFeedbackVaryings,
1170
0
                                                  &scopedMappedTFVaryings);
1171
0
1172
0
        std::vector<const char*> driverVaryings;
1173
0
        driverVaryings.reserve(scopedMappedTFVaryings.size());
1174
0
        for (const auto& cur : scopedMappedTFVaryings) {
1175
0
            driverVaryings.push_back(cur.c_str());
1176
0
        }
1177
0
1178
0
        mContext->gl->fTransformFeedbackVaryings(mGLName, driverVaryings.size(),
1179
0
                                                 driverVaryings.data(),
1180
0
                                                 mNextLink_TransformFeedbackBufferMode);
1181
0
    }
1182
0
1183
0
    LinkAndUpdate();
1184
0
1185
0
    if (mMostRecentLinkInfo) {
1186
0
        nsCString postLinkLog;
1187
0
        if (ValidateAfterTentativeLink(&postLinkLog))
1188
0
            return;
1189
0
1190
0
        mMostRecentLinkInfo = nullptr;
1191
0
        mLinkLog = postLinkLog;
1192
0
    }
1193
0
1194
0
    // Failed link.
1195
0
    if (mContext->ShouldGenerateWarnings()) {
1196
0
        // report shader/program infoLogs as warnings.
1197
0
        // note that shader compilation errors can be deferred to linkProgram,
1198
0
        // which is why we can't do anything in compileShader. In practice we could
1199
0
        // report in compileShader the translation errors generated by ANGLE,
1200
0
        // but it seems saner to keep a single way of obtaining shader infologs.
1201
0
        if (!mLinkLog.IsEmpty()) {
1202
0
            mContext->GenerateWarning("Failed to link, leaving the following"
1203
0
                                      " log:\n%s\n",
1204
0
                                      mLinkLog.BeginReading());
1205
0
        }
1206
0
    }
1207
0
}
1208
1209
static uint8_t
1210
NumUsedLocationsByElemType(GLenum elemType)
1211
0
{
1212
0
    // GLES 3.0.4 p55
1213
0
1214
0
    switch (elemType) {
1215
0
    case LOCAL_GL_FLOAT_MAT2:
1216
0
    case LOCAL_GL_FLOAT_MAT2x3:
1217
0
    case LOCAL_GL_FLOAT_MAT2x4:
1218
0
        return 2;
1219
0
1220
0
    case LOCAL_GL_FLOAT_MAT3x2:
1221
0
    case LOCAL_GL_FLOAT_MAT3:
1222
0
    case LOCAL_GL_FLOAT_MAT3x4:
1223
0
        return 3;
1224
0
1225
0
    case LOCAL_GL_FLOAT_MAT4x2:
1226
0
    case LOCAL_GL_FLOAT_MAT4x3:
1227
0
    case LOCAL_GL_FLOAT_MAT4:
1228
0
        return 4;
1229
0
1230
0
    default:
1231
0
        return 1;
1232
0
    }
1233
0
}
1234
1235
static uint8_t
1236
NumComponents(GLenum elemType)
1237
0
{
1238
0
    switch (elemType) {
1239
0
    case LOCAL_GL_FLOAT:
1240
0
    case LOCAL_GL_INT:
1241
0
    case LOCAL_GL_UNSIGNED_INT:
1242
0
    case LOCAL_GL_BOOL:
1243
0
        return 1;
1244
0
1245
0
    case LOCAL_GL_FLOAT_VEC2:
1246
0
    case LOCAL_GL_INT_VEC2:
1247
0
    case LOCAL_GL_UNSIGNED_INT_VEC2:
1248
0
    case LOCAL_GL_BOOL_VEC2:
1249
0
        return 2;
1250
0
1251
0
    case LOCAL_GL_FLOAT_VEC3:
1252
0
    case LOCAL_GL_INT_VEC3:
1253
0
    case LOCAL_GL_UNSIGNED_INT_VEC3:
1254
0
    case LOCAL_GL_BOOL_VEC3:
1255
0
        return 3;
1256
0
1257
0
    case LOCAL_GL_FLOAT_VEC4:
1258
0
    case LOCAL_GL_INT_VEC4:
1259
0
    case LOCAL_GL_UNSIGNED_INT_VEC4:
1260
0
    case LOCAL_GL_BOOL_VEC4:
1261
0
    case LOCAL_GL_FLOAT_MAT2:
1262
0
        return 4;
1263
0
1264
0
    case LOCAL_GL_FLOAT_MAT2x3:
1265
0
    case LOCAL_GL_FLOAT_MAT3x2:
1266
0
        return 6;
1267
0
1268
0
    case LOCAL_GL_FLOAT_MAT2x4:
1269
0
    case LOCAL_GL_FLOAT_MAT4x2:
1270
0
        return 8;
1271
0
1272
0
    case LOCAL_GL_FLOAT_MAT3:
1273
0
        return 9;
1274
0
1275
0
    case LOCAL_GL_FLOAT_MAT3x4:
1276
0
    case LOCAL_GL_FLOAT_MAT4x3:
1277
0
        return 12;
1278
0
1279
0
    case LOCAL_GL_FLOAT_MAT4:
1280
0
        return 16;
1281
0
1282
0
    default:
1283
0
        MOZ_CRASH("`elemType`");
1284
0
    }
1285
0
}
1286
1287
bool
1288
WebGLProgram::ValidateAfterTentativeLink(nsCString* const out_linkLog) const
1289
0
{
1290
0
    const auto& linkInfo = mMostRecentLinkInfo;
1291
0
    const auto& gl = mContext->gl;
1292
0
1293
0
    // Check if the attrib name conflicting to uniform name
1294
0
    for (const auto& attrib : linkInfo->attribs) {
1295
0
        const auto& attribName = attrib.mActiveInfo->mBaseUserName;
1296
0
1297
0
        for (const auto& uniform : linkInfo->uniforms) {
1298
0
            const auto& uniformName = uniform->mActiveInfo->mBaseUserName;
1299
0
            if (attribName == uniformName) {
1300
0
                *out_linkLog = nsPrintfCString("Attrib name conflicts with uniform name:"
1301
0
                                               " %s",
1302
0
                                               attribName.BeginReading());
1303
0
                return false;
1304
0
            }
1305
0
        }
1306
0
    }
1307
0
1308
0
    std::map<uint32_t, const webgl::AttribInfo*> attribsByLoc;
1309
0
    for (const auto& attrib : linkInfo->attribs) {
1310
0
        if (attrib.mLoc == -1)
1311
0
            continue;
1312
0
1313
0
        const auto& elemType = attrib.mActiveInfo->mElemType;
1314
0
        const auto numUsedLocs = NumUsedLocationsByElemType(elemType);
1315
0
        for (uint32_t i = 0; i < numUsedLocs; i++) {
1316
0
            const uint32_t usedLoc = attrib.mLoc + i;
1317
0
1318
0
            const auto res = attribsByLoc.insert({usedLoc, &attrib});
1319
0
            const bool& didInsert = res.second;
1320
0
            if (!didInsert) {
1321
0
                const auto& aliasingName = attrib.mActiveInfo->mBaseUserName;
1322
0
                const auto& itrExisting = res.first;
1323
0
                const auto& existingInfo = itrExisting->second;
1324
0
                const auto& existingName = existingInfo->mActiveInfo->mBaseUserName;
1325
0
                *out_linkLog = nsPrintfCString("Attrib \"%s\" aliases locations used by"
1326
0
                                               " attrib \"%s\".",
1327
0
                                               aliasingName.BeginReading(),
1328
0
                                               existingName.BeginReading());
1329
0
                return false;
1330
0
            }
1331
0
        }
1332
0
    }
1333
0
1334
0
    // Forbid:
1335
0
    // * Unrecognized varying name
1336
0
    // * Duplicate varying name
1337
0
    // * Too many components for specified buffer mode
1338
0
    if (!mNextLink_TransformFeedbackVaryings.empty()) {
1339
0
        GLuint maxComponentsPerIndex = 0;
1340
0
        switch (mNextLink_TransformFeedbackBufferMode) {
1341
0
        case LOCAL_GL_INTERLEAVED_ATTRIBS:
1342
0
            gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS,
1343
0
                             &maxComponentsPerIndex);
1344
0
            break;
1345
0
1346
0
        case LOCAL_GL_SEPARATE_ATTRIBS:
1347
0
            gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS,
1348
0
                             &maxComponentsPerIndex);
1349
0
            break;
1350
0
1351
0
        default:
1352
0
            MOZ_CRASH("`bufferMode`");
1353
0
        }
1354
0
1355
0
        std::vector<size_t> componentsPerVert;
1356
0
        std::set<const WebGLActiveInfo*> alreadyUsed;
1357
0
        for (const auto& wideUserName : mNextLink_TransformFeedbackVaryings) {
1358
0
            if (componentsPerVert.empty() ||
1359
0
                mNextLink_TransformFeedbackBufferMode == LOCAL_GL_SEPARATE_ATTRIBS)
1360
0
            {
1361
0
                componentsPerVert.push_back(0);
1362
0
            }
1363
0
1364
0
            ////
1365
0
1366
0
            const WebGLActiveInfo* curInfo = nullptr;
1367
0
            for (const auto& info : linkInfo->transformFeedbackVaryings) {
1368
0
                const NS_ConvertASCIItoUTF16 info_wideUserName(info->mBaseUserName);
1369
0
                if (info_wideUserName == wideUserName) {
1370
0
                    curInfo = info.get();
1371
0
                    break;
1372
0
                }
1373
0
            }
1374
0
1375
0
            if (!curInfo) {
1376
0
                const NS_LossyConvertUTF16toASCII asciiUserName(wideUserName);
1377
0
                *out_linkLog = nsPrintfCString("Transform feedback varying \"%s\" not"
1378
0
                                               " found.",
1379
0
                                               asciiUserName.BeginReading());
1380
0
                return false;
1381
0
            }
1382
0
1383
0
            const auto insertResPair = alreadyUsed.insert(curInfo);
1384
0
            const auto& didInsert = insertResPair.second;
1385
0
            if (!didInsert) {
1386
0
                const NS_LossyConvertUTF16toASCII asciiUserName(wideUserName);
1387
0
                *out_linkLog = nsPrintfCString("Transform feedback varying \"%s\""
1388
0
                                               " specified twice.",
1389
0
                                               asciiUserName.BeginReading());
1390
0
                return false;
1391
0
            }
1392
0
1393
0
            ////
1394
0
1395
0
            size_t varyingComponents = NumComponents(curInfo->mElemType);
1396
0
            varyingComponents *= curInfo->mElemCount;
1397
0
1398
0
            auto& totalComponentsForIndex = *(componentsPerVert.rbegin());
1399
0
            totalComponentsForIndex += varyingComponents;
1400
0
1401
0
            if (totalComponentsForIndex > maxComponentsPerIndex) {
1402
0
                const NS_LossyConvertUTF16toASCII asciiUserName(wideUserName);
1403
0
                *out_linkLog = nsPrintfCString("Transform feedback varying \"%s\""
1404
0
                                               " pushed `componentsForIndex` over the"
1405
0
                                               " limit of %u.",
1406
0
                                               asciiUserName.BeginReading(),
1407
0
                                               maxComponentsPerIndex);
1408
0
                return false;
1409
0
            }
1410
0
        }
1411
0
1412
0
        linkInfo->componentsPerTFVert.swap(componentsPerVert);
1413
0
    }
1414
0
1415
0
    return true;
1416
0
}
1417
1418
bool
1419
WebGLProgram::UseProgram() const
1420
0
{
1421
0
    if (!mMostRecentLinkInfo) {
1422
0
        mContext->ErrorInvalidOperation("Program has not been successfully linked.");
1423
0
        return false;
1424
0
    }
1425
0
1426
0
    if (mContext->mBoundTransformFeedback &&
1427
0
        mContext->mBoundTransformFeedback->mIsActive &&
1428
0
        !mContext->mBoundTransformFeedback->mIsPaused)
1429
0
    {
1430
0
        mContext->ErrorInvalidOperation("Transform feedback active and not paused.");
1431
0
        return false;
1432
0
    }
1433
0
1434
0
    mContext->gl->fUseProgram(mGLName);
1435
0
    return true;
1436
0
}
1437
1438
void
1439
WebGLProgram::ValidateProgram() const
1440
0
{
1441
0
    gl::GLContext* gl = mContext->gl;
1442
0
1443
#ifdef XP_MACOSX
1444
    // See bug 593867 for NVIDIA and bug 657201 for ATI. The latter is confirmed
1445
    // with Mac OS 10.6.7.
1446
    if (gl->WorkAroundDriverBugs()) {
1447
        mContext->GenerateWarning("Implemented as a no-op on"
1448
                                  " Mac to work around crashes.");
1449
        return;
1450
    }
1451
#endif
1452
1453
0
    gl->fValidateProgram(mGLName);
1454
0
}
1455
1456
1457
////////////////////////////////////////////////////////////////////////////////
1458
1459
void
1460
WebGLProgram::LinkAndUpdate()
1461
0
{
1462
0
    mMostRecentLinkInfo = nullptr;
1463
0
1464
0
    gl::GLContext* gl = mContext->gl;
1465
0
    gl->fLinkProgram(mGLName);
1466
0
1467
0
    // Grab the program log.
1468
0
    GLuint logLenWithNull = 0;
1469
0
    gl->fGetProgramiv(mGLName, LOCAL_GL_INFO_LOG_LENGTH, (GLint*)&logLenWithNull);
1470
0
    if (logLenWithNull > 1) {
1471
0
        mLinkLog.SetLength(logLenWithNull - 1);
1472
0
        gl->fGetProgramInfoLog(mGLName, logLenWithNull, nullptr, mLinkLog.BeginWriting());
1473
0
    } else {
1474
0
        mLinkLog.SetLength(0);
1475
0
    }
1476
0
1477
0
    GLint ok = 0;
1478
0
    gl->fGetProgramiv(mGLName, LOCAL_GL_LINK_STATUS, &ok);
1479
0
    if (!ok)
1480
0
        return;
1481
0
1482
0
    mMostRecentLinkInfo = QueryProgramInfo(this, gl);
1483
0
    MOZ_RELEASE_ASSERT(mMostRecentLinkInfo, "GFX: most recent link info not set.");
1484
0
}
1485
1486
bool
1487
WebGLProgram::FindAttribUserNameByMappedName(const nsACString& mappedName,
1488
                                             nsCString* const out_userName) const
1489
0
{
1490
0
    if (mVertShader->FindAttribUserNameByMappedName(mappedName, out_userName))
1491
0
        return true;
1492
0
1493
0
    return false;
1494
0
}
1495
1496
bool
1497
WebGLProgram::FindVaryingByMappedName(const nsACString& mappedName,
1498
                                              nsCString* const out_userName,
1499
                                              bool* const out_isArray) const
1500
0
{
1501
0
    if (mVertShader->FindVaryingByMappedName(mappedName, out_userName, out_isArray))
1502
0
        return true;
1503
0
1504
0
    return false;
1505
0
}
1506
1507
1508
bool
1509
WebGLProgram::FindUniformByMappedName(const nsACString& mappedName,
1510
                                      nsCString* const out_userName,
1511
                                      bool* const out_isArray) const
1512
0
{
1513
0
    if (mVertShader->FindUniformByMappedName(mappedName, out_userName, out_isArray))
1514
0
        return true;
1515
0
1516
0
    if (mFragShader->FindUniformByMappedName(mappedName, out_userName, out_isArray))
1517
0
        return true;
1518
0
1519
0
    return false;
1520
0
}
1521
1522
void
1523
WebGLProgram::TransformFeedbackVaryings(const dom::Sequence<nsString>& varyings,
1524
                                        GLenum bufferMode)
1525
0
{
1526
0
    const auto& gl = mContext->gl;
1527
0
1528
0
    switch (bufferMode) {
1529
0
    case LOCAL_GL_INTERLEAVED_ATTRIBS:
1530
0
        break;
1531
0
1532
0
    case LOCAL_GL_SEPARATE_ATTRIBS:
1533
0
        {
1534
0
            GLuint maxAttribs = 0;
1535
0
            gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS,
1536
0
                             &maxAttribs);
1537
0
            if (varyings.Length() > maxAttribs) {
1538
0
                mContext->ErrorInvalidValue("Length of `varyings` exceeds %s.",
1539
0
                                            "TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS");
1540
0
                return;
1541
0
            }
1542
0
        }
1543
0
        break;
1544
0
1545
0
    default:
1546
0
        mContext->ErrorInvalidEnumInfo("bufferMode", bufferMode);
1547
0
        return;
1548
0
    }
1549
0
1550
0
    ////
1551
0
1552
0
    mNextLink_TransformFeedbackVaryings.assign(varyings.Elements(),
1553
0
                                               varyings.Elements() + varyings.Length());
1554
0
    mNextLink_TransformFeedbackBufferMode = bufferMode;
1555
0
}
1556
1557
already_AddRefed<WebGLActiveInfo>
1558
WebGLProgram::GetTransformFeedbackVarying(GLuint index) const
1559
0
{
1560
0
    // No docs in the WebGL 2 spec for this function. Taking the language for
1561
0
    // getActiveAttrib, which states that the function returns null on any error.
1562
0
    if (!IsLinked()) {
1563
0
        mContext->ErrorInvalidOperation("`program` must be linked.");
1564
0
        return nullptr;
1565
0
    }
1566
0
1567
0
    if (index >= LinkInfo()->transformFeedbackVaryings.size()) {
1568
0
        mContext->ErrorInvalidValue("`index` is greater or "
1569
0
                                    "equal to TRANSFORM_FEEDBACK_VARYINGS.");
1570
0
        return nullptr;
1571
0
    }
1572
0
1573
0
    RefPtr<WebGLActiveInfo> ret = LinkInfo()->transformFeedbackVaryings[index];
1574
0
    return ret.forget();
1575
0
}
1576
1577
bool
1578
WebGLProgram::UnmapUniformBlockName(const nsCString& mappedName,
1579
                                    nsCString* const out_userName) const
1580
0
{
1581
0
    nsCString baseMappedName;
1582
0
    bool isArray;
1583
0
    size_t arrayIndex;
1584
0
    if (!ParseName(mappedName, &baseMappedName, &isArray, &arrayIndex))
1585
0
        return false;
1586
0
1587
0
    nsCString baseUserName;
1588
0
    if (!mVertShader->UnmapUniformBlockName(baseMappedName, &baseUserName) &&
1589
0
        !mFragShader->UnmapUniformBlockName(baseMappedName, &baseUserName))
1590
0
    {
1591
0
        return false;
1592
0
    }
1593
0
1594
0
    AssembleName(baseUserName, isArray, arrayIndex, out_userName);
1595
0
    return true;
1596
0
}
1597
1598
void
1599
WebGLProgram::EnumerateFragOutputs(std::map<nsCString, const nsCString> &out_FragOutputs) const
1600
0
{
1601
0
    MOZ_ASSERT(mFragShader);
1602
0
1603
0
    mFragShader->EnumerateFragOutputs(out_FragOutputs);
1604
0
}
1605
1606
////////////////////////////////////////////////////////////////////////////////
1607
1608
bool
1609
IsBaseName(const nsCString& name)
1610
0
{
1611
0
    if (!name.Length())
1612
0
        return true;
1613
0
1614
0
    return name[name.Length() - 1] != ']'; // Doesn't end in ']'.
1615
0
}
1616
1617
bool
1618
webgl::LinkedProgramInfo::FindAttrib(const nsCString& userName,
1619
                                     const webgl::AttribInfo** const out) const
1620
0
{
1621
0
    // VS inputs cannot be arrays or structures.
1622
0
    // `userName` is thus always `baseUserName`.
1623
0
    for (const auto& attrib : attribs) {
1624
0
        if (attrib.mActiveInfo->mBaseUserName == userName) {
1625
0
            *out = &attrib;
1626
0
            return true;
1627
0
        }
1628
0
    }
1629
0
1630
0
    return false;
1631
0
}
1632
1633
bool
1634
webgl::LinkedProgramInfo::FindUniform(const nsCString& userName,
1635
                                      nsCString* const out_mappedName,
1636
                                      size_t* const out_arrayIndex,
1637
                                      webgl::UniformInfo** const out_info) const
1638
0
{
1639
0
    nsCString baseUserName;
1640
0
    bool isArray;
1641
0
    size_t arrayIndex;
1642
0
    if (!ParseName(userName, &baseUserName, &isArray, &arrayIndex))
1643
0
        return false;
1644
0
1645
0
    webgl::UniformInfo* info = nullptr;
1646
0
    for (const auto& uniform : uniforms) {
1647
0
        if (uniform->mActiveInfo->mBaseUserName == baseUserName) {
1648
0
            info = uniform;
1649
0
            break;
1650
0
        }
1651
0
    }
1652
0
    if (!info)
1653
0
        return false;
1654
0
1655
0
    const auto& baseMappedName = info->mActiveInfo->mBaseMappedName;
1656
0
    AssembleName(baseMappedName, isArray, arrayIndex, out_mappedName);
1657
0
1658
0
    *out_arrayIndex = arrayIndex;
1659
0
    *out_info = info;
1660
0
    return true;
1661
0
}
1662
1663
bool
1664
webgl::LinkedProgramInfo::MapFragDataName(const nsCString& userName,
1665
                                          nsCString* const out_mappedName) const
1666
0
{
1667
0
    // FS outputs can be arrays, but not structures.
1668
0
1669
0
    if (fragDataMap.empty()) {
1670
0
        // No mappings map from validation, so just forward it.
1671
0
        *out_mappedName = userName;
1672
0
        return true;
1673
0
    }
1674
0
1675
0
    nsCString baseUserName;
1676
0
    bool isArray;
1677
0
    size_t arrayIndex;
1678
0
    if (!ParseName(userName, &baseUserName, &isArray, &arrayIndex))
1679
0
        return false;
1680
0
1681
0
    const auto itr = fragDataMap.find(baseUserName);
1682
0
    if (itr == fragDataMap.end())
1683
0
        return false;
1684
0
1685
0
    const auto& baseMappedName = itr->second;
1686
0
    AssembleName(baseMappedName, isArray, arrayIndex, out_mappedName);
1687
0
    return true;
1688
0
}
1689
1690
////////////////////////////////////////////////////////////////////////////////
1691
1692
JSObject*
1693
WebGLProgram::WrapObject(JSContext* js, JS::Handle<JSObject*> givenProto)
1694
0
{
1695
0
    return dom::WebGLProgram_Binding::Wrap(js, this, givenProto);
1696
0
}
1697
1698
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLProgram, mVertShader, mFragShader)
1699
1700
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLProgram, AddRef)
1701
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLProgram, Release)
1702
1703
} // namespace mozilla