Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/canvas/WebGLShader.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 "WebGLShader.h"
7
8
#include "GLSLANG/ShaderLang.h"
9
#include "GLContext.h"
10
#include "mozilla/dom/WebGLRenderingContextBinding.h"
11
#include "mozilla/MemoryReporting.h"
12
#include "nsPrintfCString.h"
13
#include "nsString.h"
14
#include "prenv.h"
15
#include "WebGLContext.h"
16
#include "WebGLObjectModel.h"
17
#include "WebGLShaderValidator.h"
18
#include "WebGLValidateStrings.h"
19
20
namespace mozilla {
21
22
static void
23
PrintLongString(const char* const begin, const size_t len)
24
0
{
25
0
    // Wow - Roll Your Own Foreach-Lines because printf_stderr has a hard-coded
26
0
    // internal size, so long strings are truncated.
27
0
28
0
    const size_t chunkSize = 1000;
29
0
    const UniqueBuffer buf(moz_xmalloc(chunkSize+1)); // +1 for null-term
30
0
    const auto bufBegin = (char*)buf.get();
31
0
    bufBegin[chunkSize] = '\0';
32
0
33
0
    auto chunkBegin = begin;
34
0
    const auto end = begin + len;
35
0
    while (chunkBegin + chunkSize < end) {
36
0
        memcpy(bufBegin, chunkBegin, chunkSize);
37
0
        printf_stderr("%s", bufBegin);
38
0
        chunkBegin += chunkSize;
39
0
    }
40
0
    printf_stderr("%s", chunkBegin);
41
0
}
42
43
// On success, writes to out_validator and out_translatedSource.
44
// On failure, writes to out_translationLog.
45
static bool
46
Translate(const nsACString& source, webgl::ShaderValidator* validator,
47
          nsACString* const out_translationLog, nsACString* const out_translatedSource)
48
0
{
49
0
    if (!validator->ValidateAndTranslate(source.BeginReading())) {
50
0
        validator->GetInfoLog(out_translationLog);
51
0
        return false;
52
0
    }
53
0
54
0
    // Success
55
0
    validator->GetOutput(out_translatedSource);
56
0
    return true;
57
0
}
58
59
template<size_t N>
60
static bool
61
SubstringStartsWith(const std::string& testStr, size_t offset, const char (& refStr)[N])
62
0
{
63
0
    for (size_t i = 0; i < N-1; i++) {
64
0
        if (testStr[offset + i] != refStr[i])
65
0
            return false;
66
0
    }
67
0
    return true;
68
0
}
Unexecuted instantiation: Unified_cpp_dom_canvas5.cpp:bool mozilla::SubstringStartsWith<17ul>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long, char const (&) [17ul])
Unexecuted instantiation: Unified_cpp_dom_canvas5.cpp:bool mozilla::SubstringStartsWith<14ul>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long, char const (&) [14ul])
69
70
/* On success, writes to out_translatedSource.
71
 * On failure, writes to out_translationLog.
72
 *
73
 * Requirements:
74
 *   #version is either omitted, `#version 100`, or `version 300 es`.
75
 */
76
static bool
77
TranslateWithoutValidation(const nsACString& sourceNS, bool isWebGL2,
78
                           nsACString* const out_translationLog,
79
                           nsACString* const out_translatedSource)
80
0
{
81
0
    std::string source = sourceNS.BeginReading();
82
0
83
0
    size_t versionStrStart = source.find("#version");
84
0
    size_t versionStrLen;
85
0
    uint32_t glesslVersion;
86
0
87
0
    if (versionStrStart != std::string::npos) {
88
0
        static const char versionStr100[] = "#version 100\n";
89
0
        static const char versionStr300es[] = "#version 300 es\n";
90
0
91
0
        if (isWebGL2 && SubstringStartsWith(source, versionStrStart, versionStr300es)) {
92
0
            glesslVersion = 300;
93
0
            versionStrLen = strlen(versionStr300es);
94
0
95
0
        } else if (SubstringStartsWith(source, versionStrStart, versionStr100)) {
96
0
            glesslVersion = 100;
97
0
            versionStrLen = strlen(versionStr100);
98
0
99
0
        } else {
100
0
            nsPrintfCString error("#version, if declared, must be %s.",
101
0
                                  isWebGL2 ? "`100` or `300 es`"
102
0
                                           : "`100`");
103
0
            *out_translationLog = error;
104
0
            return false;
105
0
        }
106
0
    } else {
107
0
        versionStrStart = 0;
108
0
        versionStrLen = 0;
109
0
        glesslVersion = 100;
110
0
    }
111
0
112
0
    std::string reversionedSource = source;
113
0
    reversionedSource.erase(versionStrStart, versionStrLen);
114
0
115
0
    switch (glesslVersion) {
116
0
    case 100:
117
0
        /* According to ARB_ES2_compatibility extension glsl
118
0
         * should accept #version 100 for ES 2 shaders. */
119
0
        reversionedSource.insert(versionStrStart, "#version 100\n");
120
0
        break;
121
0
    case 300:
122
0
        reversionedSource.insert(versionStrStart, "#version 330\n");
123
0
        break;
124
0
    default:
125
0
        MOZ_CRASH("GFX: Bad `glesslVersion`.");
126
0
    }
127
0
128
0
    out_translatedSource->Assign(reversionedSource.c_str(),
129
0
                                 reversionedSource.length());
130
0
    return true;
131
0
}
132
133
static void
134
GetCompilationStatusAndLog(gl::GLContext* gl, GLuint shader, bool* const out_success,
135
                           nsACString* const out_log)
136
0
{
137
0
    GLint compileStatus = LOCAL_GL_FALSE;
138
0
    gl->fGetShaderiv(shader, LOCAL_GL_COMPILE_STATUS, &compileStatus);
139
0
140
0
    // It's simpler if we always get the log.
141
0
    GLint lenWithNull = 0;
142
0
    gl->fGetShaderiv(shader, LOCAL_GL_INFO_LOG_LENGTH, &lenWithNull);
143
0
144
0
    if (lenWithNull > 1) {
145
0
        // SetLength takes the length without the null.
146
0
        out_log->SetLength(lenWithNull - 1);
147
0
        gl->fGetShaderInfoLog(shader, lenWithNull, nullptr, out_log->BeginWriting());
148
0
    } else {
149
0
        out_log->SetLength(0);
150
0
    }
151
0
152
0
    *out_success = (compileStatus == LOCAL_GL_TRUE);
153
0
}
154
155
////////////////////////////////////////////////////////////////////////////////
156
157
WebGLShader::WebGLShader(WebGLContext* webgl, GLenum type)
158
    : WebGLRefCountedObject(webgl)
159
    , mGLName(webgl->gl->fCreateShader(type))
160
    , mType(type)
161
    , mTranslationSuccessful(false)
162
    , mCompilationSuccessful(false)
163
0
{
164
0
    mContext->mShaders.insertBack(this);
165
0
}
166
167
WebGLShader::~WebGLShader()
168
0
{
169
0
    DeleteOnce();
170
0
}
171
172
void
173
WebGLShader::ShaderSource(const nsAString& source)
174
0
{
175
0
    nsString sourceWithoutComments;
176
0
    if (!TruncateComments(source, &sourceWithoutComments)) {
177
0
        mContext->ErrorOutOfMemory("Failed to alloc for empting comment contents.");
178
0
        return;
179
0
    }
180
0
181
0
    if (!ValidateGLSLPreprocString(mContext, sourceWithoutComments))
182
0
        return;
183
0
184
0
    // We checked that the source stripped of comments is in the
185
0
    // 7-bit ASCII range, so we can skip the NS_IsAscii() check.
186
0
    const NS_LossyConvertUTF16toASCII cleanSource(sourceWithoutComments);
187
0
188
0
    mSource = source;
189
0
    mCleanSource = cleanSource;
190
0
}
191
192
void
193
WebGLShader::CompileShader()
194
0
{
195
0
    mValidator = nullptr;
196
0
    mTranslationSuccessful = false;
197
0
    mCompilationSuccessful = false;
198
0
199
0
    gl::GLContext* gl = mContext->gl;
200
0
201
0
    mValidator.reset(mContext->CreateShaderValidator(mType));
202
0
203
0
    static const bool kDumpShaders = PR_GetEnv("MOZ_WEBGL_DUMP_SHADERS");
204
0
    if (MOZ_UNLIKELY(kDumpShaders)) {
205
0
        printf_stderr("==== begin MOZ_WEBGL_DUMP_SHADERS ====\n");
206
0
        PrintLongString(mCleanSource.BeginReading(), mCleanSource.Length());
207
0
    }
208
0
209
0
    bool success;
210
0
    if (mValidator) {
211
0
        success = Translate(mCleanSource, mValidator.get(), &mValidationLog,
212
0
                            &mTranslatedSource);
213
0
    } else {
214
0
        success = TranslateWithoutValidation(mCleanSource, mContext->IsWebGL2(),
215
0
                                             &mValidationLog, &mTranslatedSource);
216
0
    }
217
0
218
0
    if (MOZ_UNLIKELY(kDumpShaders)) {
219
0
        printf_stderr("\n==== \\/ \\/ \\/ ====\n");
220
0
        if (success) {
221
0
            PrintLongString(mTranslatedSource.BeginReading(), mTranslatedSource.Length());
222
0
        } else {
223
0
            printf_stderr("Validation failed:\n%s", mValidationLog.BeginReading());
224
0
        }
225
0
        printf_stderr("\n==== end ====\n");
226
0
    }
227
0
228
0
    if (!success)
229
0
        return;
230
0
231
0
    mTranslationSuccessful = true;
232
0
233
0
    const char* const parts[] = {
234
0
        mTranslatedSource.BeginReading()
235
0
    };
236
0
    gl->fShaderSource(mGLName, ArrayLength(parts), parts, nullptr);
237
0
238
0
    gl->fCompileShader(mGLName);
239
0
240
0
    GetCompilationStatusAndLog(gl, mGLName, &mCompilationSuccessful, &mCompilationLog);
241
0
}
242
243
void
244
WebGLShader::GetShaderInfoLog(nsAString* out) const
245
0
{
246
0
    const nsCString& log = !mTranslationSuccessful ? mValidationLog
247
0
                                                   : mCompilationLog;
248
0
    CopyASCIItoUTF16(log, *out);
249
0
}
250
251
JS::Value
252
WebGLShader::GetShaderParameter(GLenum pname) const
253
0
{
254
0
    switch (pname) {
255
0
    case LOCAL_GL_SHADER_TYPE:
256
0
        return JS::NumberValue(mType);
257
0
258
0
    case LOCAL_GL_DELETE_STATUS:
259
0
        return JS::BooleanValue(IsDeleteRequested());
260
0
261
0
    case LOCAL_GL_COMPILE_STATUS:
262
0
        return JS::BooleanValue(mCompilationSuccessful);
263
0
264
0
    default:
265
0
        mContext->ErrorInvalidEnumInfo("getShaderParameter: `pname`", pname);
266
0
        return JS::NullValue();
267
0
    }
268
0
}
269
270
void
271
WebGLShader::GetShaderSource(nsAString* out) const
272
0
{
273
0
    out->SetIsVoid(false);
274
0
    *out = mSource;
275
0
}
276
277
void
278
WebGLShader::GetShaderTranslatedSource(nsAString* out) const
279
0
{
280
0
    out->SetIsVoid(false);
281
0
    CopyASCIItoUTF16(mTranslatedSource, *out);
282
0
}
283
284
////////////////////////////////////////////////////////////////////////////////
285
286
bool
287
WebGLShader::CanLinkTo(const WebGLShader* prev, nsCString* const out_log) const
288
0
{
289
0
    if (!mValidator)
290
0
        return true;
291
0
292
0
    return mValidator->CanLinkTo(prev->mValidator.get(), out_log);
293
0
}
294
295
size_t
296
WebGLShader::CalcNumSamplerUniforms() const
297
0
{
298
0
    if (mValidator)
299
0
        return mValidator->CalcNumSamplerUniforms();
300
0
301
0
    // TODO
302
0
    return 0;
303
0
}
304
305
size_t
306
WebGLShader::NumAttributes() const
307
0
{
308
0
    if (mValidator)
309
0
        return mValidator->NumAttributes();
310
0
311
0
    // TODO
312
0
    return 0;
313
0
}
314
315
void
316
WebGLShader::BindAttribLocation(GLuint prog, const nsCString& userName,
317
                                GLuint index) const
318
0
{
319
0
    std::string userNameStr(userName.BeginReading());
320
0
321
0
    const std::string* mappedNameStr = &userNameStr;
322
0
    if (mValidator)
323
0
        mValidator->FindAttribMappedNameByUserName(userNameStr, &mappedNameStr);
324
0
325
0
    mContext->gl->fBindAttribLocation(prog, index, mappedNameStr->c_str());
326
0
}
327
328
bool
329
WebGLShader::FindAttribUserNameByMappedName(const nsACString& mappedName,
330
                                            nsCString* const out_userName) const
331
0
{
332
0
    if (!mValidator)
333
0
        return false;
334
0
335
0
    const std::string mappedNameStr(mappedName.BeginReading());
336
0
    const std::string* userNameStr;
337
0
    if (!mValidator->FindAttribUserNameByMappedName(mappedNameStr, &userNameStr))
338
0
        return false;
339
0
340
0
    *out_userName = userNameStr->c_str();
341
0
    return true;
342
0
}
343
344
bool
345
WebGLShader::FindVaryingByMappedName(const nsACString& mappedName,
346
                                     nsCString* const out_userName,
347
                                     bool* const out_isArray) const
348
0
{
349
0
    if (!mValidator)
350
0
        return false;
351
0
352
0
    const std::string mappedNameStr(mappedName.BeginReading());
353
0
    std::string userNameStr;
354
0
    if (!mValidator->FindVaryingByMappedName(mappedNameStr, &userNameStr, out_isArray))
355
0
        return false;
356
0
357
0
    *out_userName = userNameStr.c_str();
358
0
    return true;
359
0
}
360
361
bool
362
WebGLShader::FindUniformByMappedName(const nsACString& mappedName,
363
                                     nsCString* const out_userName,
364
                                     bool* const out_isArray) const
365
0
{
366
0
    if (!mValidator)
367
0
        return false;
368
0
369
0
    const std::string mappedNameStr(mappedName.BeginReading(), mappedName.Length());
370
0
    std::string userNameStr;
371
0
    if (!mValidator->FindUniformByMappedName(mappedNameStr, &userNameStr, out_isArray))
372
0
        return false;
373
0
374
0
    *out_userName = userNameStr.c_str();
375
0
    return true;
376
0
}
377
378
bool
379
WebGLShader::UnmapUniformBlockName(const nsACString& baseMappedName,
380
                                   nsCString* const out_baseUserName) const
381
0
{
382
0
    if (!mValidator) {
383
0
        *out_baseUserName = baseMappedName;
384
0
        return true;
385
0
    }
386
0
387
0
    return mValidator->UnmapUniformBlockName(baseMappedName, out_baseUserName);
388
0
}
389
390
void
391
WebGLShader::EnumerateFragOutputs(std::map<nsCString, const nsCString> &out_FragOutputs) const
392
0
{
393
0
    out_FragOutputs.clear();
394
0
395
0
    if (!mValidator) {
396
0
        return;
397
0
    }
398
0
    mValidator->EnumerateFragOutputs(out_FragOutputs);
399
0
}
400
401
void
402
WebGLShader::MapTransformFeedbackVaryings(const std::vector<nsString>& varyings,
403
                                          std::vector<std::string>* out_mappedVaryings) const
404
0
{
405
0
    MOZ_ASSERT(mType == LOCAL_GL_VERTEX_SHADER);
406
0
    MOZ_ASSERT(out_mappedVaryings);
407
0
408
0
    out_mappedVaryings->clear();
409
0
    out_mappedVaryings->reserve(varyings.size());
410
0
411
0
    for (const auto& wideUserName : varyings) {
412
0
        const NS_LossyConvertUTF16toASCII mozUserName(wideUserName); // Don't validate here.
413
0
        const std::string userName(mozUserName.BeginReading(), mozUserName.Length());
414
0
        const std::string* pMappedName = &userName;
415
0
        if (mValidator) {
416
0
            mValidator->FindVaryingMappedNameByUserName(userName, &pMappedName);
417
0
        }
418
0
        out_mappedVaryings->push_back(*pMappedName);
419
0
    }
420
0
}
421
422
////////////////////////////////////////////////////////////////////////////////
423
// Boilerplate
424
425
JSObject*
426
WebGLShader::WrapObject(JSContext* js, JS::Handle<JSObject*> givenProto)
427
0
{
428
0
    return dom::WebGLShader_Binding::Wrap(js, this, givenProto);
429
0
}
430
431
size_t
432
WebGLShader::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const
433
0
{
434
0
    size_t validatorSize = mValidator ? mallocSizeOf(mValidator.get())
435
0
                                      : 0;
436
0
    return mallocSizeOf(this) +
437
0
           mSource.SizeOfExcludingThisIfUnshared(mallocSizeOf) +
438
0
           mCleanSource.SizeOfExcludingThisIfUnshared(mallocSizeOf) +
439
0
           validatorSize +
440
0
           mValidationLog.SizeOfExcludingThisIfUnshared(mallocSizeOf) +
441
0
           mTranslatedSource.SizeOfExcludingThisIfUnshared(mallocSizeOf) +
442
0
           mCompilationLog.SizeOfExcludingThisIfUnshared(mallocSizeOf);
443
0
}
444
445
void
446
WebGLShader::Delete()
447
0
{
448
0
    gl::GLContext* gl = mContext->GL();
449
0
450
0
    gl->fDeleteShader(mGLName);
451
0
452
0
    LinkedListElement<WebGLShader>::removeFrom(mContext->mShaders);
453
0
}
454
455
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLShader)
456
457
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLShader, AddRef)
458
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLShader, Release)
459
460
} // namespace mozilla