1 /* 2 * Copyright (C) 2011-2012 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 <rs_hal.h> 18 #include <rsContext.h> 19 20 #include "rsdShader.h" 21 #include "rsdShaderCache.h" 22 #include "rsdGL.h" 23 24 #include <GLES/gl.h> 25 #include <GLES2/gl2.h> 26 27 using namespace android; 28 using namespace android::renderscript; 29 30 31 RsdShaderCache::RsdShaderCache() { 32 mEntries.setCapacity(16); 33 mVertexDirty = true; 34 mFragmentDirty = true; 35 } 36 37 RsdShaderCache::~RsdShaderCache() { 38 cleanupAll(); 39 } 40 41 void RsdShaderCache::updateUniformArrayData(const Context *rsc, RsdShader *prog, uint32_t linkedID, 42 UniformData *data, const char* logTag, 43 UniformQueryData **uniformList, uint32_t uniListSize) { 44 45 for (uint32_t ct=0; ct < prog->getUniformCount(); ct++) { 46 if (data[ct].slot >= 0 && data[ct].arraySize > 1) { 47 //Iterate over the list of active GL uniforms and find highest array index 48 for (uint32_t ui = 0; ui < uniListSize; ui ++) { 49 if (prog->getUniformName(ct) == uniformList[ui]->name) { 50 data[ct].arraySize = (uint32_t)uniformList[ui]->arraySize; 51 break; 52 } 53 } 54 } 55 56 if (rsc->props.mLogShaders) { 57 ALOGV("%s U, %s = %d, arraySize = %d\n", logTag, 58 prog->getUniformName(ct).string(), data[ct].slot, data[ct].arraySize); 59 } 60 } 61 } 62 63 void RsdShaderCache::populateUniformData(RsdShader *prog, uint32_t linkedID, UniformData *data) { 64 for (uint32_t ct=0; ct < prog->getUniformCount(); ct++) { 65 data[ct].slot = glGetUniformLocation(linkedID, prog->getUniformName(ct)); 66 data[ct].arraySize = prog->getUniformArraySize(ct); 67 } 68 } 69 70 bool RsdShaderCache::hasArrayUniforms(RsdShader *vtx, RsdShader *frag) { 71 UniformData *data = mCurrent->vtxUniforms; 72 for (uint32_t ct=0; ct < vtx->getUniformCount(); ct++) { 73 if (data[ct].slot >= 0 && data[ct].arraySize > 1) { 74 return true; 75 } 76 } 77 data = mCurrent->fragUniforms; 78 for (uint32_t ct=0; ct < frag->getUniformCount(); ct++) { 79 if (data[ct].slot >= 0 && data[ct].arraySize > 1) { 80 return true; 81 } 82 } 83 return false; 84 } 85 86 bool RsdShaderCache::setup(const Context *rsc) { 87 if (!mVertexDirty && !mFragmentDirty) { 88 return true; 89 } 90 91 if (!link(rsc)) { 92 return false; 93 } 94 95 if (mFragmentDirty) { 96 mFragment->setup(rsc, this); 97 mFragmentDirty = false; 98 } 99 if (mVertexDirty) { 100 mVertex->setup(rsc, this); 101 mVertexDirty = false; 102 } 103 104 return true; 105 } 106 107 bool RsdShaderCache::link(const Context *rsc) { 108 109 RsdShader *vtx = mVertex; 110 RsdShader *frag = mFragment; 111 112 uint32_t vID = vtx->getStateBasedShaderID(rsc); 113 uint32_t fID = frag->getStateBasedShaderID(rsc); 114 115 // Don't try to cache if shaders failed to load 116 if (!vID || !fID) { 117 return false; 118 } 119 uint32_t entryCount = mEntries.size(); 120 for (uint32_t ct = 0; ct < entryCount; ct ++) { 121 if ((mEntries[ct]->vtx == vID) && (mEntries[ct]->frag == fID)) { 122 123 //ALOGV("SC using program %i", mEntries[ct]->program); 124 glUseProgram(mEntries[ct]->program); 125 mCurrent = mEntries[ct]; 126 //ALOGV("RsdShaderCache hit, using %i", ct); 127 rsdGLCheckError(rsc, "RsdShaderCache::link (hit)"); 128 return true; 129 } 130 } 131 132 ProgramEntry *e = new ProgramEntry(vtx->getAttribCount(), 133 vtx->getUniformCount(), 134 frag->getUniformCount()); 135 mEntries.push(e); 136 mCurrent = e; 137 e->vtx = vID; 138 e->frag = fID; 139 e->program = glCreateProgram(); 140 if (e->program) { 141 GLuint pgm = e->program; 142 glAttachShader(pgm, vID); 143 //ALOGE("e1 %x", glGetError()); 144 glAttachShader(pgm, fID); 145 146 glBindAttribLocation(pgm, 0, "ATTRIB_position"); 147 glBindAttribLocation(pgm, 1, "ATTRIB_color"); 148 glBindAttribLocation(pgm, 2, "ATTRIB_normal"); 149 glBindAttribLocation(pgm, 3, "ATTRIB_texture0"); 150 151 //ALOGE("e2 %x", glGetError()); 152 glLinkProgram(pgm); 153 //ALOGE("e3 %x", glGetError()); 154 GLint linkStatus = GL_FALSE; 155 glGetProgramiv(pgm, GL_LINK_STATUS, &linkStatus); 156 if (linkStatus != GL_TRUE) { 157 GLint bufLength = 0; 158 glGetProgramiv(pgm, GL_INFO_LOG_LENGTH, &bufLength); 159 if (bufLength) { 160 char* buf = (char*) malloc(bufLength); 161 if (buf) { 162 glGetProgramInfoLog(pgm, bufLength, NULL, buf); 163 rsc->setError(RS_ERROR_FATAL_PROGRAM_LINK, buf); 164 free(buf); 165 } 166 } 167 glDeleteProgram(pgm); 168 return false; 169 } 170 171 for (uint32_t ct=0; ct < e->vtxAttrCount; ct++) { 172 e->vtxAttrs[ct].slot = glGetAttribLocation(pgm, vtx->getAttribName(ct)); 173 e->vtxAttrs[ct].name = vtx->getAttribName(ct).string(); 174 if (rsc->props.mLogShaders) { 175 ALOGV("vtx A %i, %s = %d\n", ct, vtx->getAttribName(ct).string(), e->vtxAttrs[ct].slot); 176 } 177 } 178 179 populateUniformData(vtx, pgm, e->vtxUniforms); 180 populateUniformData(frag, pgm, e->fragUniforms); 181 182 // Only populate this list if we have arrays in our uniforms 183 UniformQueryData **uniformList = NULL; 184 GLint numUniforms = 0; 185 bool hasArrays = hasArrayUniforms(vtx, frag); 186 if (hasArrays) { 187 // Get the number of active uniforms and the length of the longest name 188 glGetProgramiv(pgm, GL_ACTIVE_UNIFORMS, &numUniforms); 189 GLint maxNameLength = 0; 190 glGetProgramiv(pgm, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxNameLength); 191 if (numUniforms > 0 && maxNameLength > 0) { 192 uniformList = new UniformQueryData*[numUniforms]; 193 // Iterate over all the uniforms and build the list we 194 // can later use to match our uniforms to 195 for (uint32_t ct = 0; ct < (uint32_t)numUniforms; ct++) { 196 uniformList[ct] = new UniformQueryData(maxNameLength); 197 glGetActiveUniform(pgm, ct, maxNameLength, &uniformList[ct]->writtenLength, 198 &uniformList[ct]->arraySize, &uniformList[ct]->type, 199 uniformList[ct]->name); 200 //ALOGE("GL UNI idx=%u, arraySize=%u, name=%s", ct, 201 // uniformList[ct]->arraySize, uniformList[ct]->name); 202 } 203 } 204 } 205 206 // We now know the highest index of all of the array uniforms 207 // and we need to update our cache to reflect that 208 // we may have declared [n], but only m < n elements are used 209 updateUniformArrayData(rsc, vtx, pgm, e->vtxUniforms, "vtx", 210 uniformList, (uint32_t)numUniforms); 211 updateUniformArrayData(rsc, frag, pgm, e->fragUniforms, "frag", 212 uniformList, (uint32_t)numUniforms); 213 214 // Clean up the uniform data from GL 215 if (uniformList != NULL) { 216 for (uint32_t ct = 0; ct < (uint32_t)numUniforms; ct++) { 217 delete uniformList[ct]; 218 } 219 delete[] uniformList; 220 uniformList = NULL; 221 } 222 } 223 224 //ALOGV("SC made program %i", e->program); 225 glUseProgram(e->program); 226 rsdGLCheckError(rsc, "RsdShaderCache::link (miss)"); 227 228 return true; 229 } 230 231 int32_t RsdShaderCache::vtxAttribSlot(const String8 &attrName) const { 232 for (uint32_t ct=0; ct < mCurrent->vtxAttrCount; ct++) { 233 if (attrName == mCurrent->vtxAttrs[ct].name) { 234 return mCurrent->vtxAttrs[ct].slot; 235 } 236 } 237 return -1; 238 } 239 240 void RsdShaderCache::cleanupVertex(RsdShader *s) { 241 int32_t numEntries = (int32_t)mEntries.size(); 242 uint32_t numShaderIDs = s->getStateBasedIDCount(); 243 for (uint32_t sId = 0; sId < numShaderIDs; sId ++) { 244 uint32_t id = s->getStateBasedID(sId); 245 for (int32_t ct = 0; ct < numEntries; ct ++) { 246 if (mEntries[ct]->vtx == id) { 247 glDeleteProgram(mEntries[ct]->program); 248 249 delete mEntries[ct]; 250 mEntries.removeAt(ct); 251 numEntries = (int32_t)mEntries.size(); 252 ct --; 253 } 254 } 255 } 256 } 257 258 void RsdShaderCache::cleanupFragment(RsdShader *s) { 259 int32_t numEntries = (int32_t)mEntries.size(); 260 uint32_t numShaderIDs = s->getStateBasedIDCount(); 261 for (uint32_t sId = 0; sId < numShaderIDs; sId ++) { 262 uint32_t id = s->getStateBasedID(sId); 263 for (int32_t ct = 0; ct < numEntries; ct ++) { 264 if (mEntries[ct]->frag == id) { 265 glDeleteProgram(mEntries[ct]->program); 266 267 delete mEntries[ct]; 268 mEntries.removeAt(ct); 269 numEntries = (int32_t)mEntries.size(); 270 ct --; 271 } 272 } 273 } 274 } 275 276 void RsdShaderCache::cleanupAll() { 277 for (uint32_t ct=0; ct < mEntries.size(); ct++) { 278 glDeleteProgram(mEntries[ct]->program); 279 free(mEntries[ct]); 280 } 281 mEntries.clear(); 282 } 283 284