blob: 15cc4179dcc26095f1f18efcc14d6dcb94be4198 [file] [log] [blame]
Alex Sakhartchouk4a36b452011-04-29 16:49:08 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <GLES2/gl2.h>
18#include <GLES2/gl2ext.h>
19
20#include <rs_hal.h>
21#include <rsContext.h>
22#include <rsProgram.h>
23
Alex Sakhartchouk43850542011-05-05 16:56:27 -070024#include "rsdCore.h"
Jason Sams7e8aae72011-05-26 16:33:01 -070025#include "rsdAllocation.h"
Alex Sakhartchouk4a36b452011-04-29 16:49:08 -070026#include "rsdShader.h"
27#include "rsdShaderCache.h"
28
29using namespace android;
30using namespace android::renderscript;
31
32RsdShader::RsdShader(const Program *p, uint32_t type,
33 const char * shaderText, uint32_t shaderLength) {
34
35 mUserShader.setTo(shaderText, shaderLength);
36 mRSProgram = p;
37 mType = type;
38 initMemberVars();
39 initAttribAndUniformArray();
40 init();
41}
42
43RsdShader::~RsdShader() {
44 if (mShaderID) {
45 glDeleteShader(mShaderID);
46 }
47
48 delete[] mAttribNames;
49 delete[] mUniformNames;
50 delete[] mUniformArraySizes;
51}
52
53void RsdShader::initMemberVars() {
54 mDirty = true;
55 mShaderID = 0;
56 mAttribCount = 0;
57 mUniformCount = 0;
58
59 mAttribNames = NULL;
60 mUniformNames = NULL;
61 mUniformArraySizes = NULL;
62
63 mIsValid = false;
64}
65
66void RsdShader::init() {
67 uint32_t attribCount = 0;
68 uint32_t uniformCount = 0;
69 for (uint32_t ct=0; ct < mRSProgram->mHal.state.inputElementsCount; ct++) {
70 initAddUserElement(mRSProgram->mHal.state.inputElements[ct].get(), mAttribNames, NULL, &attribCount, RS_SHADER_ATTR);
71 }
72 for (uint32_t ct=0; ct < mRSProgram->mHal.state.constantsCount; ct++) {
73 initAddUserElement(mRSProgram->mHal.state.constantTypes[ct]->getElement(), mUniformNames, mUniformArraySizes, &uniformCount, RS_SHADER_UNI);
74 }
75
76 mTextureUniformIndexStart = uniformCount;
77 char buf[256];
78 for (uint32_t ct=0; ct < mRSProgram->mHal.state.texturesCount; ct++) {
79 snprintf(buf, sizeof(buf), "UNI_Tex%i", ct);
80 mUniformNames[uniformCount].setTo(buf);
81 mUniformArraySizes[uniformCount] = 1;
82 uniformCount++;
83 }
84}
85
86String8 RsdShader::getGLSLInputString() const {
87 String8 s;
88 for (uint32_t ct=0; ct < mRSProgram->mHal.state.inputElementsCount; ct++) {
89 const Element *e = mRSProgram->mHal.state.inputElements[ct].get();
90 for (uint32_t field=0; field < e->getFieldCount(); field++) {
91 const Element *f = e->getField(field);
92
93 // Cannot be complex
94 rsAssert(!f->getFieldCount());
95 switch (f->getComponent().getVectorSize()) {
96 case 1: s.append("attribute float ATTRIB_"); break;
97 case 2: s.append("attribute vec2 ATTRIB_"); break;
98 case 3: s.append("attribute vec3 ATTRIB_"); break;
99 case 4: s.append("attribute vec4 ATTRIB_"); break;
100 default:
101 rsAssert(0);
102 }
103
104 s.append(e->getFieldName(field));
105 s.append(";\n");
106 }
107 }
108 return s;
109}
110
111void RsdShader::appendAttributes() {
112 for (uint32_t ct=0; ct < mRSProgram->mHal.state.inputElementsCount; ct++) {
113 const Element *e = mRSProgram->mHal.state.inputElements[ct].get();
114 for (uint32_t field=0; field < e->getFieldCount(); field++) {
115 const Element *f = e->getField(field);
116 const char *fn = e->getFieldName(field);
117
118 if (fn[0] == '#') {
119 continue;
120 }
121
122 // Cannot be complex
123 rsAssert(!f->getFieldCount());
124 switch (f->getComponent().getVectorSize()) {
125 case 1: mShader.append("attribute float ATTRIB_"); break;
126 case 2: mShader.append("attribute vec2 ATTRIB_"); break;
127 case 3: mShader.append("attribute vec3 ATTRIB_"); break;
128 case 4: mShader.append("attribute vec4 ATTRIB_"); break;
129 default:
130 rsAssert(0);
131 }
132
133 mShader.append(fn);
134 mShader.append(";\n");
135 }
136 }
137}
138
139void RsdShader::appendTextures() {
140 char buf[256];
141 for (uint32_t ct=0; ct < mRSProgram->mHal.state.texturesCount; ct++) {
142 if (mRSProgram->mHal.state.textureTargets[ct] == RS_TEXTURE_2D) {
143 snprintf(buf, sizeof(buf), "uniform sampler2D UNI_Tex%i;\n", ct);
144 } else {
145 snprintf(buf, sizeof(buf), "uniform samplerCube UNI_Tex%i;\n", ct);
146 }
147 mShader.append(buf);
148 }
149}
150
151bool RsdShader::createShader() {
152
153 if (mType == GL_FRAGMENT_SHADER) {
154 mShader.append("precision mediump float;\n");
155 }
156 appendUserConstants();
157 appendAttributes();
158 appendTextures();
159
160 mShader.append(mUserShader);
161
162 return true;
163}
164
165bool RsdShader::loadShader(const Context *rsc) {
166 mShaderID = glCreateShader(mType);
167 rsAssert(mShaderID);
168
169 if (rsc->props.mLogShaders) {
170 LOGV("Loading shader type %x, ID %i", mType, mShaderID);
171 LOGV("%s", mShader.string());
172 }
173
174 if (mShaderID) {
175 const char * ss = mShader.string();
176 glShaderSource(mShaderID, 1, &ss, NULL);
177 glCompileShader(mShaderID);
178
179 GLint compiled = 0;
180 glGetShaderiv(mShaderID, GL_COMPILE_STATUS, &compiled);
181 if (!compiled) {
182 GLint infoLen = 0;
183 glGetShaderiv(mShaderID, GL_INFO_LOG_LENGTH, &infoLen);
184 if (infoLen) {
185 char* buf = (char*) malloc(infoLen);
186 if (buf) {
187 glGetShaderInfoLog(mShaderID, infoLen, NULL, buf);
188 LOGE("Could not compile shader \n%s\n", buf);
189 free(buf);
190 }
191 glDeleteShader(mShaderID);
192 mShaderID = 0;
193 rsc->setError(RS_ERROR_BAD_SHADER, "Error returned from GL driver loading shader text,");
194 return false;
195 }
196 }
197 }
198
199 if (rsc->props.mLogShaders) {
200 LOGV("--Shader load result %x ", glGetError());
201 }
202 mIsValid = true;
203 return true;
204}
205
206void RsdShader::appendUserConstants() {
207 for (uint32_t ct=0; ct < mRSProgram->mHal.state.constantsCount; ct++) {
208 const Element *e = mRSProgram->mHal.state.constantTypes[ct]->getElement();
209 for (uint32_t field=0; field < e->getFieldCount(); field++) {
210 const Element *f = e->getField(field);
211 const char *fn = e->getFieldName(field);
212
213 if (fn[0] == '#') {
214 continue;
215 }
216
217 // Cannot be complex
218 rsAssert(!f->getFieldCount());
219 if (f->getType() == RS_TYPE_MATRIX_4X4) {
220 mShader.append("uniform mat4 UNI_");
221 } else if (f->getType() == RS_TYPE_MATRIX_3X3) {
222 mShader.append("uniform mat3 UNI_");
223 } else if (f->getType() == RS_TYPE_MATRIX_2X2) {
224 mShader.append("uniform mat2 UNI_");
225 } else {
226 switch (f->getComponent().getVectorSize()) {
227 case 1: mShader.append("uniform float UNI_"); break;
228 case 2: mShader.append("uniform vec2 UNI_"); break;
229 case 3: mShader.append("uniform vec3 UNI_"); break;
230 case 4: mShader.append("uniform vec4 UNI_"); break;
231 default:
232 rsAssert(0);
233 }
234 }
235
236 mShader.append(fn);
237 if (e->getFieldArraySize(field) > 1) {
238 mShader.appendFormat("[%d]", e->getFieldArraySize(field));
239 }
240 mShader.append(";\n");
241 }
242 }
243}
244
245void RsdShader::logUniform(const Element *field, const float *fd, uint32_t arraySize ) {
246 RsDataType dataType = field->getType();
247 uint32_t elementSize = field->getSizeBytes() / sizeof(float);
248 for (uint32_t i = 0; i < arraySize; i ++) {
249 if (arraySize > 1) {
250 LOGV("Array Element [%u]", i);
251 }
252 if (dataType == RS_TYPE_MATRIX_4X4) {
253 LOGV("Matrix4x4");
254 LOGV("{%f, %f, %f, %f", fd[0], fd[4], fd[8], fd[12]);
255 LOGV(" %f, %f, %f, %f", fd[1], fd[5], fd[9], fd[13]);
256 LOGV(" %f, %f, %f, %f", fd[2], fd[6], fd[10], fd[14]);
257 LOGV(" %f, %f, %f, %f}", fd[3], fd[7], fd[11], fd[15]);
258 } else if (dataType == RS_TYPE_MATRIX_3X3) {
259 LOGV("Matrix3x3");
260 LOGV("{%f, %f, %f", fd[0], fd[3], fd[6]);
261 LOGV(" %f, %f, %f", fd[1], fd[4], fd[7]);
262 LOGV(" %f, %f, %f}", fd[2], fd[5], fd[8]);
263 } else if (dataType == RS_TYPE_MATRIX_2X2) {
264 LOGV("Matrix2x2");
265 LOGV("{%f, %f", fd[0], fd[2]);
266 LOGV(" %f, %f}", fd[1], fd[3]);
267 } else {
268 switch (field->getComponent().getVectorSize()) {
269 case 1:
270 LOGV("Uniform 1 = %f", fd[0]);
271 break;
272 case 2:
273 LOGV("Uniform 2 = %f %f", fd[0], fd[1]);
274 break;
275 case 3:
276 LOGV("Uniform 3 = %f %f %f", fd[0], fd[1], fd[2]);
277 break;
278 case 4:
279 LOGV("Uniform 4 = %f %f %f %f", fd[0], fd[1], fd[2], fd[3]);
280 break;
281 default:
282 rsAssert(0);
283 }
284 }
285 LOGE("Element size %u data=%p", elementSize, fd);
286 fd += elementSize;
287 LOGE("New data=%p", fd);
288 }
289}
290
291void RsdShader::setUniform(const Context *rsc, const Element *field, const float *fd,
292 int32_t slot, uint32_t arraySize ) {
293 RsDataType dataType = field->getType();
294 if (dataType == RS_TYPE_MATRIX_4X4) {
295 glUniformMatrix4fv(slot, arraySize, GL_FALSE, fd);
296 } else if (dataType == RS_TYPE_MATRIX_3X3) {
297 glUniformMatrix3fv(slot, arraySize, GL_FALSE, fd);
298 } else if (dataType == RS_TYPE_MATRIX_2X2) {
299 glUniformMatrix2fv(slot, arraySize, GL_FALSE, fd);
300 } else {
301 switch (field->getComponent().getVectorSize()) {
302 case 1:
303 glUniform1fv(slot, arraySize, fd);
304 break;
305 case 2:
306 glUniform2fv(slot, arraySize, fd);
307 break;
308 case 3:
309 glUniform3fv(slot, arraySize, fd);
310 break;
311 case 4:
312 glUniform4fv(slot, arraySize, fd);
313 break;
314 default:
315 rsAssert(0);
316 }
317 }
318}
319
Alex Sakhartchouk43850542011-05-05 16:56:27 -0700320void RsdShader::setupSampler(const Context *rsc, const Sampler *s, const Allocation *tex) {
321 RsdHal *dc = (RsdHal *)rsc->mHal.drv;
322
323 GLenum trans[] = {
324 GL_NEAREST, //RS_SAMPLER_NEAREST,
325 GL_LINEAR, //RS_SAMPLER_LINEAR,
326 GL_LINEAR_MIPMAP_LINEAR, //RS_SAMPLER_LINEAR_MIP_LINEAR,
327 GL_REPEAT, //RS_SAMPLER_WRAP,
328 GL_CLAMP_TO_EDGE, //RS_SAMPLER_CLAMP
329 GL_LINEAR_MIPMAP_NEAREST, //RS_SAMPLER_LINEAR_MIP_NEAREST
330 };
331
332 GLenum transNP[] = {
333 GL_NEAREST, //RS_SAMPLER_NEAREST,
334 GL_LINEAR, //RS_SAMPLER_LINEAR,
335 GL_LINEAR, //RS_SAMPLER_LINEAR_MIP_LINEAR,
336 GL_CLAMP_TO_EDGE, //RS_SAMPLER_WRAP,
337 GL_CLAMP_TO_EDGE, //RS_SAMPLER_CLAMP
338 GL_LINEAR, //RS_SAMPLER_LINEAR_MIP_NEAREST,
339 };
340
341 // This tells us the correct texture type
Jason Sams7e8aae72011-05-26 16:33:01 -0700342 DrvAllocation *drvTex = (DrvAllocation *)tex->mHal.drv;
343 const GLenum target = drvTex->glTarget;
Alex Sakhartchouk43850542011-05-05 16:56:27 -0700344
345 if (!dc->gl.gl.OES_texture_npot && tex->getType()->getIsNp2()) {
346 if (tex->getHasGraphicsMipmaps() &&
347 (dc->gl.gl.GL_NV_texture_npot_2D_mipmap || dc->gl.gl.GL_IMG_texture_npot)) {
348 if (dc->gl.gl.GL_NV_texture_npot_2D_mipmap) {
349 glTexParameteri(target, GL_TEXTURE_MIN_FILTER, trans[s->mHal.state.minFilter]);
350 } else {
351 switch (trans[s->mHal.state.minFilter]) {
352 case GL_LINEAR_MIPMAP_LINEAR:
353 glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
354 break;
355 default:
356 glTexParameteri(target, GL_TEXTURE_MIN_FILTER, trans[s->mHal.state.minFilter]);
357 break;
358 }
359 }
360 } else {
361 glTexParameteri(target, GL_TEXTURE_MIN_FILTER, transNP[s->mHal.state.minFilter]);
362 }
363 glTexParameteri(target, GL_TEXTURE_MAG_FILTER, transNP[s->mHal.state.magFilter]);
364 glTexParameteri(target, GL_TEXTURE_WRAP_S, transNP[s->mHal.state.wrapS]);
365 glTexParameteri(target, GL_TEXTURE_WRAP_T, transNP[s->mHal.state.wrapT]);
366 } else {
367 if (tex->getHasGraphicsMipmaps()) {
368 glTexParameteri(target, GL_TEXTURE_MIN_FILTER, trans[s->mHal.state.minFilter]);
369 } else {
370 glTexParameteri(target, GL_TEXTURE_MIN_FILTER, transNP[s->mHal.state.minFilter]);
371 }
372 glTexParameteri(target, GL_TEXTURE_MAG_FILTER, trans[s->mHal.state.magFilter]);
373 glTexParameteri(target, GL_TEXTURE_WRAP_S, trans[s->mHal.state.wrapS]);
374 glTexParameteri(target, GL_TEXTURE_WRAP_T, trans[s->mHal.state.wrapT]);
375 }
376
377 float anisoValue = rsMin(dc->gl.gl.EXT_texture_max_aniso, s->mHal.state.aniso);
378 if (dc->gl.gl.EXT_texture_max_aniso > 1.0f) {
379 glTexParameterf(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisoValue);
380 }
381
Alex Sakhartchouk407cae92011-05-06 14:59:45 -0700382 rsdGLCheckError(rsc, "Sampler::setup tex env");
Alex Sakhartchouk43850542011-05-05 16:56:27 -0700383}
384
Alex Sakhartchouk4a36b452011-04-29 16:49:08 -0700385void RsdShader::setupTextures(const Context *rsc, RsdShaderCache *sc) {
386 if (mRSProgram->mHal.state.texturesCount == 0) {
387 return;
388 }
389
Alex Sakhartchouk407cae92011-05-06 14:59:45 -0700390 RsdHal *dc = (RsdHal *)rsc->mHal.drv;
391
Alex Sakhartchouk4a36b452011-04-29 16:49:08 -0700392 uint32_t numTexturesToBind = mRSProgram->mHal.state.texturesCount;
Alex Sakhartchouk407cae92011-05-06 14:59:45 -0700393 uint32_t numTexturesAvailable = dc->gl.gl.maxFragmentTextureImageUnits;
Alex Sakhartchouk4a36b452011-04-29 16:49:08 -0700394 if (numTexturesToBind >= numTexturesAvailable) {
395 LOGE("Attempting to bind %u textures on shader id %u, but only %u are available",
396 mRSProgram->mHal.state.texturesCount, (uint32_t)this, numTexturesAvailable);
397 rsc->setError(RS_ERROR_BAD_SHADER, "Cannot bind more textuers than available");
398 numTexturesToBind = numTexturesAvailable;
399 }
400
401 for (uint32_t ct=0; ct < numTexturesToBind; ct++) {
402 glActiveTexture(GL_TEXTURE0 + ct);
403 if (!mRSProgram->mHal.state.textures[ct].get()) {
404 LOGE("No texture bound for shader id %u, texture unit %u", (uint)this, ct);
405 rsc->setError(RS_ERROR_BAD_SHADER, "No texture bound");
406 continue;
407 }
408
Jason Sams7e8aae72011-05-26 16:33:01 -0700409 DrvAllocation *drvTex = (DrvAllocation *)mRSProgram->mHal.state.textures[ct]->mHal.drv;
410 if (drvTex->glTarget != GL_TEXTURE_2D && drvTex->glTarget != GL_TEXTURE_CUBE_MAP) {
Alex Sakhartchouk4a36b452011-04-29 16:49:08 -0700411 LOGE("Attempting to bind unknown texture to shader id %u, texture unit %u", (uint)this, ct);
412 rsc->setError(RS_ERROR_BAD_SHADER, "Non-texture allocation bound to a shader");
413 }
Jason Sams7e8aae72011-05-26 16:33:01 -0700414 glBindTexture(drvTex->glTarget, drvTex->textureID);
Alex Sakhartchouk407cae92011-05-06 14:59:45 -0700415 rsdGLCheckError(rsc, "ProgramFragment::setup tex bind");
Alex Sakhartchouk4a36b452011-04-29 16:49:08 -0700416 if (mRSProgram->mHal.state.samplers[ct].get()) {
Alex Sakhartchouk43850542011-05-05 16:56:27 -0700417 setupSampler(rsc, mRSProgram->mHal.state.samplers[ct].get(), mRSProgram->mHal.state.textures[ct].get());
Alex Sakhartchouk4a36b452011-04-29 16:49:08 -0700418 } else {
Jason Sams7e8aae72011-05-26 16:33:01 -0700419 glTexParameteri(drvTex->glTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
420 glTexParameteri(drvTex->glTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
421 glTexParameteri(drvTex->glTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
422 glTexParameteri(drvTex->glTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
Alex Sakhartchouk407cae92011-05-06 14:59:45 -0700423 rsdGLCheckError(rsc, "ProgramFragment::setup tex env");
Alex Sakhartchouk4a36b452011-04-29 16:49:08 -0700424 }
425
426 glUniform1i(sc->fragUniformSlot(mTextureUniformIndexStart + ct), ct);
Alex Sakhartchouk407cae92011-05-06 14:59:45 -0700427 rsdGLCheckError(rsc, "ProgramFragment::setup uniforms");
Alex Sakhartchouk4a36b452011-04-29 16:49:08 -0700428 }
429
430 glActiveTexture(GL_TEXTURE0);
431 mDirty = false;
Alex Sakhartchouk407cae92011-05-06 14:59:45 -0700432 rsdGLCheckError(rsc, "ProgramFragment::setup");
Alex Sakhartchouk4a36b452011-04-29 16:49:08 -0700433}
434
435void RsdShader::setupUserConstants(const Context *rsc, RsdShaderCache *sc, bool isFragment) {
436 uint32_t uidx = 0;
437 for (uint32_t ct=0; ct < mRSProgram->mHal.state.constantsCount; ct++) {
438 Allocation *alloc = mRSProgram->mHal.state.constants[ct].get();
439 if (!alloc) {
440 LOGE("Attempting to set constants on shader id %u, but alloc at slot %u is not set", (uint32_t)this, ct);
441 rsc->setError(RS_ERROR_BAD_SHADER, "No constant allocation bound");
442 continue;
443 }
444
445 const uint8_t *data = static_cast<const uint8_t *>(alloc->getPtr());
446 const Element *e = mRSProgram->mHal.state.constantTypes[ct]->getElement();
447 for (uint32_t field=0; field < e->getFieldCount(); field++) {
448 const Element *f = e->getField(field);
449 const char *fieldName = e->getFieldName(field);
450 // If this field is padding, skip it
451 if (fieldName[0] == '#') {
452 continue;
453 }
454
455 uint32_t offset = e->getFieldOffsetBytes(field);
456 const float *fd = reinterpret_cast<const float *>(&data[offset]);
457
458 int32_t slot = -1;
459 uint32_t arraySize = 1;
460 if (!isFragment) {
461 slot = sc->vtxUniformSlot(uidx);
462 arraySize = sc->vtxUniformSize(uidx);
463 } else {
464 slot = sc->fragUniformSlot(uidx);
465 arraySize = sc->fragUniformSize(uidx);
466 }
467 if (rsc->props.mLogShadersUniforms) {
468 LOGV("Uniform slot=%i, offset=%i, constant=%i, field=%i, uidx=%i, name=%s", slot, offset, ct, field, uidx, fieldName);
469 }
470 uidx ++;
471 if (slot < 0) {
472 continue;
473 }
474
475 if (rsc->props.mLogShadersUniforms) {
476 logUniform(f, fd, arraySize);
477 }
478 setUniform(rsc, f, fd, slot, arraySize);
479 }
480 }
481}
482
483void RsdShader::setup(const android::renderscript::Context *rsc, RsdShaderCache *sc) {
484
485 setupUserConstants(rsc, sc, mType == GL_FRAGMENT_SHADER);
486 setupTextures(rsc, sc);
487}
488
489void RsdShader::initAttribAndUniformArray() {
490 mAttribCount = 0;
491 for (uint32_t ct=0; ct < mRSProgram->mHal.state.inputElementsCount; ct++) {
492 const Element *elem = mRSProgram->mHal.state.inputElements[ct].get();
493 for (uint32_t field=0; field < elem->getFieldCount(); field++) {
494 if (elem->getFieldName(field)[0] != '#') {
495 mAttribCount ++;
496 }
497 }
498 }
499
500 mUniformCount = 0;
501 for (uint32_t ct=0; ct < mRSProgram->mHal.state.constantsCount; ct++) {
502 const Element *elem = mRSProgram->mHal.state.constantTypes[ct]->getElement();
503
504 for (uint32_t field=0; field < elem->getFieldCount(); field++) {
505 if (elem->getFieldName(field)[0] != '#') {
506 mUniformCount ++;
507 }
508 }
509 }
510 mUniformCount += mRSProgram->mHal.state.texturesCount;
511
512 if (mAttribCount) {
513 mAttribNames = new String8[mAttribCount];
514 }
515 if (mUniformCount) {
516 mUniformNames = new String8[mUniformCount];
517 mUniformArraySizes = new uint32_t[mUniformCount];
518 }
519}
520
521void RsdShader::initAddUserElement(const Element *e, String8 *names, uint32_t *arrayLengths, uint32_t *count, const char *prefix) {
522 rsAssert(e->getFieldCount());
523 for (uint32_t ct=0; ct < e->getFieldCount(); ct++) {
524 const Element *ce = e->getField(ct);
525 if (ce->getFieldCount()) {
526 initAddUserElement(ce, names, arrayLengths, count, prefix);
527 } else if (e->getFieldName(ct)[0] != '#') {
528 String8 tmp(prefix);
529 tmp.append(e->getFieldName(ct));
530 names[*count].setTo(tmp.string());
531 if (arrayLengths) {
532 arrayLengths[*count] = e->getFieldArraySize(ct);
533 }
534 (*count)++;
535 }
536 }
537}