/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 |