1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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/. */
6 #include "WebGLShader.h"
8 #include "GLSLANG/ShaderLang.h"
10 #include "mozilla/dom/WebGLRenderingContextBinding.h"
11 #include "mozilla/MemoryReporting.h"
12 #include "nsPrintfCString.h"
15 #include "WebGLContext.h"
16 #include "WebGLObjectModel.h"
17 #include "WebGLShaderValidator.h"
18 #include "WebGLValidateStrings.h"
22 static void PrintLongString(const char* const begin
, const size_t len
) {
23 // Wow - Roll Your Own Foreach-Lines because printf_stderr has a hard-coded
24 // internal size, so long strings are truncated.
26 const size_t chunkSize
= 1000;
27 const UniqueBuffer
buf(moz_xmalloc(chunkSize
+ 1)); // +1 for null-term
28 const auto bufBegin
= (char*)buf
.get();
29 bufBegin
[chunkSize
] = '\0';
31 auto chunkBegin
= begin
;
32 const auto end
= begin
+ len
;
33 while (chunkBegin
+ chunkSize
< end
) {
34 memcpy(bufBegin
, chunkBegin
, chunkSize
);
35 printf_stderr("%s", bufBegin
);
36 chunkBegin
+= chunkSize
;
38 printf_stderr("%s", chunkBegin
);
42 static bool SubstringStartsWith(const std::string
& testStr
, size_t offset
,
43 const char (&refStr
)[N
]) {
44 for (size_t i
= 0; i
< N
- 1; i
++) {
45 if (testStr
[offset
+ i
] != refStr
[i
]) return false;
50 static void GetCompilationStatusAndLog(gl::GLContext
* gl
, GLuint shader
,
51 bool* const out_success
,
52 std::string
* const out_log
) {
53 GLint compileStatus
= LOCAL_GL_FALSE
;
54 gl
->fGetShaderiv(shader
, LOCAL_GL_COMPILE_STATUS
, &compileStatus
);
56 // It's simpler if we always get the log.
57 GLint lenWithNull
= 0;
58 gl
->fGetShaderiv(shader
, LOCAL_GL_INFO_LOG_LENGTH
, &lenWithNull
);
59 if (lenWithNull
< 1) {
62 std::vector
<char> buffer(lenWithNull
);
63 gl
->fGetShaderInfoLog(shader
, buffer
.size(), nullptr, buffer
.data());
64 *out_log
= buffer
.data();
66 *out_success
= (compileStatus
== LOCAL_GL_TRUE
);
69 ////////////////////////////////////////////////////////////////////////////////
71 WebGLShader::WebGLShader(WebGLContext
* webgl
, GLenum type
)
72 : WebGLContextBoundObject(webgl
),
73 mGLName(webgl
->gl
->fCreateShader(type
)),
75 mCompileResults
= std::make_unique
<webgl::ShaderValidatorResults
>();
78 WebGLShader::~WebGLShader() {
79 if (!mContext
) return;
80 mContext
->gl
->fDeleteShader(mGLName
);
83 void WebGLShader::ShaderSource(const std::string
& cleanSource
) {
85 CheckGLSLPreprocString(mContext
->IsWebGL2(), cleanSource
);
88 mSource
= cleanSource
;
91 void WebGLShader::CompileShader() {
92 mCompilationSuccessful
= false;
94 gl::GLContext
* gl
= mContext
->gl
;
96 static const bool kDumpShaders
= PR_GetEnv("MOZ_WEBGL_DUMP_SHADERS");
97 if (MOZ_UNLIKELY(kDumpShaders
)) {
98 printf_stderr("==== begin MOZ_WEBGL_DUMP_SHADERS ====\n");
99 PrintLongString(mSource
.c_str(), mSource
.size());
103 const auto validator
= mContext
->CreateShaderValidator(mType
);
104 MOZ_ASSERT(validator
);
106 mCompileResults
= validator
->ValidateAndTranslate(mSource
.c_str());
109 mCompilationLog
= mCompileResults
->mInfoLog
.c_str();
110 const auto& success
= mCompileResults
->mValid
;
112 if (MOZ_UNLIKELY(kDumpShaders
)) {
113 printf_stderr("\n==== \\/ \\/ \\/ ====\n");
115 const auto& translated
= mCompileResults
->mObjectCode
;
116 PrintLongString(translated
.data(), translated
.size());
118 printf_stderr("Validation failed:\n%s",
119 mCompileResults
->mInfoLog
.c_str());
121 printf_stderr("\n==== end ====\n");
124 if (!success
) return;
126 const std::array
<const char*, 1> parts
= {
127 mCompileResults
->mObjectCode
.c_str()};
128 gl
->fShaderSource(mGLName
, parts
.size(), parts
.data(), nullptr);
130 gl
->fCompileShader(mGLName
);
132 GetCompilationStatusAndLog(gl
, mGLName
, &mCompilationSuccessful
,
136 ////////////////////////////////////////////////////////////////////////////////
138 size_t WebGLShader::CalcNumSamplerUniforms() const {
140 for (const auto& cur
: mCompileResults
->mUniforms
) {
141 const auto& type
= cur
.type
;
142 if (type
== LOCAL_GL_SAMPLER_2D
|| type
== LOCAL_GL_SAMPLER_CUBE
) {
143 accum
+= cur
.getArraySizeProduct();
149 size_t WebGLShader::NumAttributes() const {
150 return mCompileResults
->mAttributes
.size();
153 void WebGLShader::BindAttribLocation(GLuint prog
, const std::string
& userName
,
154 GLuint index
) const {
155 for (const auto& attrib
: mCompileResults
->mAttributes
) {
156 if (attrib
.name
== userName
) {
157 mContext
->gl
->fBindAttribLocation(prog
, index
, attrib
.mappedName
.c_str());
163 void WebGLShader::MapTransformFeedbackVaryings(
164 const std::vector
<std::string
>& varyings
,
165 std::vector
<std::string
>* out_mappedVaryings
) const {
166 MOZ_ASSERT(mType
== LOCAL_GL_VERTEX_SHADER
);
167 MOZ_ASSERT(out_mappedVaryings
);
169 out_mappedVaryings
->clear();
170 out_mappedVaryings
->reserve(varyings
.size());
172 const auto& shaderVaryings
= mCompileResults
->mVaryings
;
174 for (const auto& userName
: varyings
) {
175 const auto* mappedName
= &userName
;
176 for (const auto& shaderVarying
: shaderVaryings
) {
177 if (shaderVarying
.name
== userName
) {
178 mappedName
= &shaderVarying
.mappedName
;
182 out_mappedVaryings
->push_back(*mappedName
);
186 ////////////////////////////////////////////////////////////////////////////////
189 size_t WebGLShader::SizeOfIncludingThis(MallocSizeOf mallocSizeOf
) const {
190 return mallocSizeOf(this) + mSource
.size() + 1 +
191 mCompileResults
->SizeOfIncludingThis(mallocSizeOf
) +
192 mCompilationLog
.size() + 1;
195 } // namespace mozilla