|
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 "WebGLContext.h" |
|
7 #include "WebGLShader.h" |
|
8 #include "WebGLProgram.h" |
|
9 #include "mozilla/dom/WebGLRenderingContextBinding.h" |
|
10 #include "GLContext.h" |
|
11 |
|
12 using namespace mozilla; |
|
13 |
|
14 /** Takes an ASCII string like "foo[i]", turns it into "foo" and returns "[i]" in bracketPart |
|
15 * |
|
16 * \param string input/output: the string to split, becomes the string without the bracket part |
|
17 * \param bracketPart output: gets the bracket part. |
|
18 * |
|
19 * Notice that if there are multiple brackets like "foo[i].bar[j]", only the last bracket is split. |
|
20 */ |
|
21 static bool SplitLastSquareBracket(nsACString& string, nsCString& bracketPart) |
|
22 { |
|
23 MOZ_ASSERT(bracketPart.IsEmpty(), "SplitLastSquareBracket must be called with empty bracketPart string"); |
|
24 |
|
25 if (string.IsEmpty()) |
|
26 return false; |
|
27 |
|
28 char *string_start = string.BeginWriting(); |
|
29 char *s = string_start + string.Length() - 1; |
|
30 |
|
31 if (*s != ']') |
|
32 return false; |
|
33 |
|
34 while (*s != '[' && s != string_start) |
|
35 s--; |
|
36 |
|
37 if (*s != '[') |
|
38 return false; |
|
39 |
|
40 bracketPart.Assign(s); |
|
41 *s = 0; |
|
42 string.EndWriting(); |
|
43 string.SetLength(s - string_start); |
|
44 return true; |
|
45 } |
|
46 |
|
47 JSObject* |
|
48 WebGLProgram::WrapObject(JSContext *cx) { |
|
49 return dom::WebGLProgramBinding::Wrap(cx, this); |
|
50 } |
|
51 |
|
52 WebGLProgram::WebGLProgram(WebGLContext *context) |
|
53 : WebGLContextBoundObject(context) |
|
54 , mLinkStatus(false) |
|
55 , mGeneration(0) |
|
56 , mAttribMaxNameLength(0) |
|
57 { |
|
58 SetIsDOMBinding(); |
|
59 mContext->MakeContextCurrent(); |
|
60 mGLName = mContext->gl->fCreateProgram(); |
|
61 mContext->mPrograms.insertBack(this); |
|
62 } |
|
63 |
|
64 void |
|
65 WebGLProgram::Delete() { |
|
66 DetachShaders(); |
|
67 mContext->MakeContextCurrent(); |
|
68 mContext->gl->fDeleteProgram(mGLName); |
|
69 LinkedListElement<WebGLProgram>::removeFrom(mContext->mPrograms); |
|
70 } |
|
71 |
|
72 bool |
|
73 WebGLProgram::AttachShader(WebGLShader *shader) { |
|
74 if (ContainsShader(shader)) |
|
75 return false; |
|
76 mAttachedShaders.AppendElement(shader); |
|
77 |
|
78 mContext->MakeContextCurrent(); |
|
79 mContext->gl->fAttachShader(GLName(), shader->GLName()); |
|
80 |
|
81 return true; |
|
82 } |
|
83 |
|
84 bool |
|
85 WebGLProgram::DetachShader(WebGLShader *shader) { |
|
86 if (!mAttachedShaders.RemoveElement(shader)) |
|
87 return false; |
|
88 |
|
89 mContext->MakeContextCurrent(); |
|
90 mContext->gl->fDetachShader(GLName(), shader->GLName()); |
|
91 |
|
92 return true; |
|
93 } |
|
94 |
|
95 bool |
|
96 WebGLProgram::HasAttachedShaderOfType(GLenum shaderType) { |
|
97 for (uint32_t i = 0; i < mAttachedShaders.Length(); ++i) { |
|
98 if (mAttachedShaders[i] && mAttachedShaders[i]->ShaderType() == shaderType) { |
|
99 return true; |
|
100 } |
|
101 } |
|
102 return false; |
|
103 } |
|
104 |
|
105 bool |
|
106 WebGLProgram::HasBadShaderAttached() { |
|
107 for (uint32_t i = 0; i < mAttachedShaders.Length(); ++i) { |
|
108 if (mAttachedShaders[i] && !mAttachedShaders[i]->CompileStatus()) { |
|
109 return true; |
|
110 } |
|
111 } |
|
112 return false; |
|
113 } |
|
114 |
|
115 size_t |
|
116 WebGLProgram::UpperBoundNumSamplerUniforms() { |
|
117 size_t numSamplerUniforms = 0; |
|
118 for (size_t i = 0; i < mAttachedShaders.Length(); ++i) { |
|
119 const WebGLShader *shader = mAttachedShaders[i]; |
|
120 if (!shader) |
|
121 continue; |
|
122 for (size_t j = 0; j < shader->mUniformInfos.Length(); ++j) { |
|
123 WebGLUniformInfo u = shader->mUniformInfos[j]; |
|
124 if (u.type == SH_SAMPLER_2D || |
|
125 u.type == SH_SAMPLER_CUBE) |
|
126 { |
|
127 numSamplerUniforms += u.arraySize; |
|
128 } |
|
129 } |
|
130 } |
|
131 return numSamplerUniforms; |
|
132 } |
|
133 |
|
134 void |
|
135 WebGLProgram::MapIdentifier(const nsACString& name, nsCString *mappedName) { |
|
136 if (!mIdentifierMap) { |
|
137 // if the identifier map doesn't exist yet, build it now |
|
138 mIdentifierMap = new CStringMap; |
|
139 for (size_t i = 0; i < mAttachedShaders.Length(); i++) { |
|
140 for (size_t j = 0; j < mAttachedShaders[i]->mAttributes.Length(); j++) { |
|
141 const WebGLMappedIdentifier& attrib = mAttachedShaders[i]->mAttributes[j]; |
|
142 mIdentifierMap->Put(attrib.original, attrib.mapped); |
|
143 } |
|
144 for (size_t j = 0; j < mAttachedShaders[i]->mUniforms.Length(); j++) { |
|
145 const WebGLMappedIdentifier& uniform = mAttachedShaders[i]->mUniforms[j]; |
|
146 mIdentifierMap->Put(uniform.original, uniform.mapped); |
|
147 } |
|
148 } |
|
149 } |
|
150 |
|
151 nsCString mutableName(name); |
|
152 nsCString bracketPart; |
|
153 bool hadBracketPart = SplitLastSquareBracket(mutableName, bracketPart); |
|
154 if (hadBracketPart) |
|
155 mutableName.AppendLiteral("[0]"); |
|
156 |
|
157 if (mIdentifierMap->Get(mutableName, mappedName)) { |
|
158 if (hadBracketPart) { |
|
159 nsCString mappedBracketPart; |
|
160 bool mappedHadBracketPart = SplitLastSquareBracket(*mappedName, mappedBracketPart); |
|
161 if (mappedHadBracketPart) |
|
162 mappedName->Append(bracketPart); |
|
163 } |
|
164 return; |
|
165 } |
|
166 |
|
167 // not found? We might be in the situation we have a uniform array name and the GL's glGetActiveUniform |
|
168 // returned its name without [0], as is allowed by desktop GL but not in ES. Let's then try with [0]. |
|
169 mutableName.AppendLiteral("[0]"); |
|
170 if (mIdentifierMap->Get(mutableName, mappedName)) |
|
171 return; |
|
172 |
|
173 // not found? return name unchanged. This case happens e.g. on bad user input, or when |
|
174 // we're not using identifier mapping, or if we didn't store an identifier in the map because |
|
175 // e.g. its mapping is trivial (as happens for short identifiers) |
|
176 mappedName->Assign(name); |
|
177 } |
|
178 |
|
179 void |
|
180 WebGLProgram::ReverseMapIdentifier(const nsACString& name, nsCString *reverseMappedName) { |
|
181 if (!mIdentifierReverseMap) { |
|
182 // if the identifier reverse map doesn't exist yet, build it now |
|
183 mIdentifierReverseMap = new CStringMap; |
|
184 for (size_t i = 0; i < mAttachedShaders.Length(); i++) { |
|
185 for (size_t j = 0; j < mAttachedShaders[i]->mAttributes.Length(); j++) { |
|
186 const WebGLMappedIdentifier& attrib = mAttachedShaders[i]->mAttributes[j]; |
|
187 mIdentifierReverseMap->Put(attrib.mapped, attrib.original); |
|
188 } |
|
189 for (size_t j = 0; j < mAttachedShaders[i]->mUniforms.Length(); j++) { |
|
190 const WebGLMappedIdentifier& uniform = mAttachedShaders[i]->mUniforms[j]; |
|
191 mIdentifierReverseMap->Put(uniform.mapped, uniform.original); |
|
192 } |
|
193 } |
|
194 } |
|
195 |
|
196 nsCString mutableName(name); |
|
197 nsCString bracketPart; |
|
198 bool hadBracketPart = SplitLastSquareBracket(mutableName, bracketPart); |
|
199 if (hadBracketPart) |
|
200 mutableName.AppendLiteral("[0]"); |
|
201 |
|
202 if (mIdentifierReverseMap->Get(mutableName, reverseMappedName)) { |
|
203 if (hadBracketPart) { |
|
204 nsCString reverseMappedBracketPart; |
|
205 bool reverseMappedHadBracketPart = SplitLastSquareBracket(*reverseMappedName, reverseMappedBracketPart); |
|
206 if (reverseMappedHadBracketPart) |
|
207 reverseMappedName->Append(bracketPart); |
|
208 } |
|
209 return; |
|
210 } |
|
211 |
|
212 // not found? We might be in the situation we have a uniform array name and the GL's glGetActiveUniform |
|
213 // returned its name without [0], as is allowed by desktop GL but not in ES. Let's then try with [0]. |
|
214 mutableName.AppendLiteral("[0]"); |
|
215 if (mIdentifierReverseMap->Get(mutableName, reverseMappedName)) |
|
216 return; |
|
217 |
|
218 // not found? return name unchanged. This case happens e.g. on bad user input, or when |
|
219 // we're not using identifier mapping, or if we didn't store an identifier in the map because |
|
220 // e.g. its mapping is trivial (as happens for short identifiers) |
|
221 reverseMappedName->Assign(name); |
|
222 } |
|
223 |
|
224 WebGLUniformInfo |
|
225 WebGLProgram::GetUniformInfoForMappedIdentifier(const nsACString& name) { |
|
226 nsCString mutableName(name); |
|
227 nsCString bracketPart; |
|
228 bool hadBracketPart = SplitLastSquareBracket(mutableName, bracketPart); |
|
229 // if there is a bracket, we're either an array or an entry in an array. |
|
230 if (hadBracketPart) |
|
231 mutableName.AppendLiteral("[0]"); |
|
232 |
|
233 WebGLUniformInfo info; |
|
234 mUniformInfoMap->Get(mutableName, &info); |
|
235 // we don't check if that Get failed, as if it did, it left info with default values |
|
236 |
|
237 return info; |
|
238 } |
|
239 |
|
240 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(WebGLProgram, mAttachedShaders) |
|
241 |
|
242 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLProgram, AddRef) |
|
243 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLProgram, Release) |