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