1 /* 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 "GLSharedGroup.h" 18 19 /**** KeyedVector utilities ****/ 20 21 template <typename T> 22 static void clearObjectMap(android::DefaultKeyedVector<GLuint, T>& v) { 23 for (size_t i = 0; i < v.size(); i++) 24 delete v.valueAt(i); 25 v.clear(); 26 } 27 28 /**** BufferData ****/ 29 30 BufferData::BufferData() : m_size(0), m_usage(0), m_mapped(false) {}; 31 BufferData::BufferData(GLsizeiptr size, void * data) : m_size(size), m_usage(0), m_mapped(false) 32 { 33 void * buffer = NULL; 34 if (size>0) buffer = m_fixedBuffer.alloc(size); 35 if (data) memcpy(buffer, data, size); 36 } 37 38 /**** ProgramData ****/ 39 ProgramData::ProgramData() : m_numIndexes(0), 40 m_initialized(false), 41 m_locShiftWAR(false) 42 { 43 m_Indexes = NULL; 44 } 45 46 void ProgramData::initProgramData(GLuint numIndexes) 47 { 48 m_initialized = true; 49 m_numIndexes = numIndexes; 50 delete[] m_Indexes; 51 m_Indexes = new IndexInfo[numIndexes]; 52 m_locShiftWAR = false; 53 } 54 55 bool ProgramData::isInitialized() 56 { 57 return m_initialized; 58 } 59 60 ProgramData::~ProgramData() 61 { 62 delete[] m_Indexes; 63 m_Indexes = NULL; 64 } 65 66 void ProgramData::setIndexInfo(GLuint index, GLint base, GLint size, GLenum type) 67 { 68 if (index>=m_numIndexes) 69 return; 70 m_Indexes[index].base = base; 71 m_Indexes[index].size = size; 72 m_Indexes[index].type = type; 73 if (index > 0) { 74 m_Indexes[index].appBase = m_Indexes[index-1].appBase + 75 m_Indexes[index-1].size; 76 } 77 else { 78 m_Indexes[index].appBase = 0; 79 } 80 m_Indexes[index].hostLocsPerElement = 1; 81 m_Indexes[index].flags = 0; 82 m_Indexes[index].samplerValue = 0; 83 } 84 85 void ProgramData::setIndexFlags(GLuint index, GLuint flags) 86 { 87 if (index >= m_numIndexes) 88 return; 89 m_Indexes[index].flags |= flags; 90 } 91 92 GLuint ProgramData::getIndexForLocation(GLint location) 93 { 94 GLuint index = m_numIndexes; 95 GLint minDist = -1; 96 for (GLuint i=0;i<m_numIndexes;++i) 97 { 98 GLint dist = location - m_Indexes[i].base; 99 if (dist >= 0 && 100 (minDist < 0 || dist < minDist)) { 101 index = i; 102 minDist = dist; 103 } 104 } 105 return index; 106 } 107 108 GLenum ProgramData::getTypeForLocation(GLint location) 109 { 110 GLuint index = getIndexForLocation(location); 111 if (index<m_numIndexes) { 112 return m_Indexes[index].type; 113 } 114 return 0; 115 } 116 117 void ProgramData::setupLocationShiftWAR() 118 { 119 m_locShiftWAR = false; 120 for (GLuint i=0; i<m_numIndexes; i++) { 121 if (0 != (m_Indexes[i].base & 0xffff)) { 122 return; 123 } 124 } 125 // if we have one uniform at location 0, we do not need the WAR. 126 if (m_numIndexes > 1) { 127 m_locShiftWAR = true; 128 } 129 } 130 131 GLint ProgramData::locationWARHostToApp(GLint hostLoc, GLint arrIndex) 132 { 133 if (!m_locShiftWAR) return hostLoc; 134 135 GLuint index = getIndexForLocation(hostLoc); 136 if (index<m_numIndexes) { 137 if (arrIndex > 0) { 138 m_Indexes[index].hostLocsPerElement = 139 (hostLoc - m_Indexes[index].base) / arrIndex; 140 } 141 return m_Indexes[index].appBase + arrIndex; 142 } 143 return -1; 144 } 145 146 GLint ProgramData::locationWARAppToHost(GLint appLoc) 147 { 148 if (!m_locShiftWAR) return appLoc; 149 150 for(GLuint i=0; i<m_numIndexes; i++) { 151 GLint elemIndex = appLoc - m_Indexes[i].appBase; 152 if (elemIndex >= 0 && elemIndex < m_Indexes[i].size) { 153 return m_Indexes[i].base + 154 elemIndex * m_Indexes[i].hostLocsPerElement; 155 } 156 } 157 return -1; 158 } 159 160 GLint ProgramData::getNextSamplerUniform(GLint index, GLint* val, GLenum* target) 161 { 162 for (GLint i = index + 1; i >= 0 && i < (GLint)m_numIndexes; i++) { 163 if (m_Indexes[i].type == GL_SAMPLER_2D) { 164 if (val) *val = m_Indexes[i].samplerValue; 165 if (target) { 166 if (m_Indexes[i].flags & INDEX_FLAG_SAMPLER_EXTERNAL) { 167 *target = GL_TEXTURE_EXTERNAL_OES; 168 } else { 169 *target = GL_TEXTURE_2D; 170 } 171 } 172 return i; 173 } 174 } 175 return -1; 176 } 177 178 bool ProgramData::setSamplerUniform(GLint appLoc, GLint val, GLenum* target) 179 { 180 for (GLuint i = 0; i < m_numIndexes; i++) { 181 GLint elemIndex = appLoc - m_Indexes[i].appBase; 182 if (elemIndex >= 0 && elemIndex < m_Indexes[i].size) { 183 if (m_Indexes[i].type == GL_TEXTURE_2D) { 184 m_Indexes[i].samplerValue = val; 185 if (target) { 186 if (m_Indexes[i].flags & INDEX_FLAG_SAMPLER_EXTERNAL) { 187 *target = GL_TEXTURE_EXTERNAL_OES; 188 } else { 189 *target = GL_TEXTURE_2D; 190 } 191 } 192 return true; 193 } 194 } 195 } 196 return false; 197 } 198 199 bool ProgramData::attachShader(GLuint shader) 200 { 201 size_t n = m_shaders.size(); 202 for (size_t i = 0; i < n; i++) { 203 if (m_shaders[i] == shader) { 204 return false; 205 } 206 } 207 // AKA m_shaders.push_back(), but that has an ambiguous call to insertAt() 208 // due to the default parameters. This is the desired insertAt() overload. 209 m_shaders.insertAt(shader, m_shaders.size(), 1); 210 return true; 211 } 212 213 bool ProgramData::detachShader(GLuint shader) 214 { 215 size_t n = m_shaders.size(); 216 for (size_t i = 0; i < n; i++) { 217 if (m_shaders[i] == shader) { 218 m_shaders.removeAt(i); 219 return true; 220 } 221 } 222 return false; 223 } 224 225 /***** GLSharedGroup ****/ 226 227 GLSharedGroup::GLSharedGroup() : 228 m_buffers(android::DefaultKeyedVector<GLuint, BufferData*>(NULL)), 229 m_programs(android::DefaultKeyedVector<GLuint, ProgramData*>(NULL)), 230 m_shaders(android::DefaultKeyedVector<GLuint, ShaderData*>(NULL)), 231 m_shaderPrograms(android::DefaultKeyedVector<GLuint, ShaderProgramData*>(NULL)) 232 { 233 } 234 235 GLSharedGroup::~GLSharedGroup() 236 { 237 m_buffers.clear(); 238 m_programs.clear(); 239 clearObjectMap(m_buffers); 240 clearObjectMap(m_programs); 241 clearObjectMap(m_shaders); 242 clearObjectMap(m_shaderPrograms); 243 } 244 245 bool GLSharedGroup::isShaderOrProgramObject(GLuint obj) 246 { 247 android::AutoMutex _lock(m_lock); 248 return ((m_shaders.valueFor(obj)!=NULL) || 249 (m_programs.valueFor(obj)!=NULL) || 250 (m_shaderPrograms.valueFor(m_shaderProgramIdMap[obj]) !=NULL)); 251 } 252 253 BufferData * GLSharedGroup::getBufferData(GLuint bufferId) 254 { 255 android::AutoMutex _lock(m_lock); 256 return m_buffers.valueFor(bufferId); 257 } 258 259 SharedTextureDataMap* GLSharedGroup::getTextureData() { 260 return &m_textureRecs; 261 } 262 263 void GLSharedGroup::addBufferData(GLuint bufferId, GLsizeiptr size, void * data) 264 { 265 android::AutoMutex _lock(m_lock); 266 m_buffers.add(bufferId, new BufferData(size, data)); 267 } 268 269 void GLSharedGroup::updateBufferData(GLuint bufferId, GLsizeiptr size, void * data) 270 { 271 android::AutoMutex _lock(m_lock); 272 ssize_t idx = m_buffers.indexOfKey(bufferId); 273 if (idx >= 0) { 274 delete m_buffers.valueAt(idx); 275 m_buffers.editValueAt(idx) = new BufferData(size, data); 276 } else { 277 m_buffers.add(bufferId, new BufferData(size, data)); 278 } 279 } 280 281 void GLSharedGroup::setBufferUsage(GLuint bufferId, GLenum usage) { 282 android::AutoMutex _lock(m_lock); 283 ssize_t idx = m_buffers.indexOfKey(bufferId); 284 if (idx >= 0) { 285 m_buffers.editValueAt(idx)->m_usage = usage; 286 } 287 } 288 289 void GLSharedGroup::setBufferMapped(GLuint bufferId, bool mapped) { 290 BufferData * buf = m_buffers.valueFor(bufferId); 291 if (!buf) return; 292 buf->m_mapped = mapped; 293 } 294 295 GLenum GLSharedGroup::getBufferUsage(GLuint bufferId) { 296 BufferData * buf = m_buffers.valueFor(bufferId); 297 if (!buf) return 0; 298 return buf->m_usage; 299 } 300 301 bool GLSharedGroup::isBufferMapped(GLuint bufferId) { 302 BufferData * buf = m_buffers.valueFor(bufferId); 303 if (!buf) return false; 304 return buf->m_mapped; 305 } 306 307 GLenum GLSharedGroup::subUpdateBufferData(GLuint bufferId, GLintptr offset, GLsizeiptr size, void * data) 308 { 309 android::AutoMutex _lock(m_lock); 310 BufferData * buf = m_buffers.valueFor(bufferId); 311 if ((!buf) || (buf->m_size < offset+size) || (offset < 0) || (size<0)) return GL_INVALID_VALUE; 312 313 //it's safe to update now 314 memcpy((char*)buf->m_fixedBuffer.ptr() + offset, data, size); 315 316 buf->m_indexRangeCache.invalidateRange((size_t)offset, (size_t)size); 317 return GL_NO_ERROR; 318 } 319 320 void GLSharedGroup::deleteBufferData(GLuint bufferId) 321 { 322 android::AutoMutex _lock(m_lock); 323 ssize_t idx = m_buffers.indexOfKey(bufferId); 324 if (idx >= 0) { 325 delete m_buffers.valueAt(idx); 326 m_buffers.removeItemsAt(idx); 327 } 328 } 329 330 void GLSharedGroup::addProgramData(GLuint program) 331 { 332 android::AutoMutex _lock(m_lock); 333 ProgramData *pData = m_programs.valueFor(program); 334 if (pData) 335 { 336 m_programs.removeItem(program); 337 delete pData; 338 } 339 340 m_programs.add(program,new ProgramData()); 341 } 342 343 void GLSharedGroup::initProgramData(GLuint program, GLuint numIndexes) 344 { 345 android::AutoMutex _lock(m_lock); 346 ProgramData *pData = m_programs.valueFor(program); 347 if (pData) 348 { 349 pData->initProgramData(numIndexes); 350 } 351 } 352 353 bool GLSharedGroup::isProgramInitialized(GLuint program) 354 { 355 android::AutoMutex _lock(m_lock); 356 ProgramData* pData = m_programs.valueFor(program); 357 if (pData) 358 { 359 return pData->isInitialized(); 360 } 361 if (m_shaderProgramIdMap.find(program) == m_shaderProgramIdMap.end()) return false; 362 ShaderProgramData* spData = m_shaderPrograms.valueFor(m_shaderProgramIdMap[program]); 363 if (spData) { 364 return spData->programData->isInitialized(); 365 } 366 return false; 367 } 368 369 void GLSharedGroup::deleteProgramData(GLuint program) 370 { 371 android::AutoMutex _lock(m_lock); 372 ProgramData *pData = m_programs.valueFor(program); 373 if (pData) { 374 delete pData; 375 } 376 m_programs.removeItem(program); 377 378 if (m_shaderProgramIdMap.find(program) == m_shaderProgramIdMap.end()) return; 379 ShaderProgramData* spData = m_shaderPrograms.valueFor(m_shaderProgramIdMap[program]); 380 if (spData) { 381 delete spData; 382 } 383 m_shaderPrograms.removeItem(m_shaderProgramIdMap[program]); 384 m_shaderProgramIdMap.erase(program); 385 } 386 387 // No such thing for separable shader programs. 388 void GLSharedGroup::attachShader(GLuint program, GLuint shader) 389 { 390 android::AutoMutex _lock(m_lock); 391 ProgramData* programData = m_programs.valueFor(program); 392 ssize_t idx = m_shaders.indexOfKey(shader); 393 if (programData && idx >= 0) { 394 if (programData->attachShader(shader)) { 395 refShaderDataLocked(idx); 396 } 397 } 398 } 399 400 void GLSharedGroup::detachShader(GLuint program, GLuint shader) 401 { 402 android::AutoMutex _lock(m_lock); 403 ProgramData* programData = m_programs.valueFor(program); 404 ssize_t idx = m_shaders.indexOfKey(shader); 405 if (programData && idx >= 0) { 406 if (programData->detachShader(shader)) { 407 unrefShaderDataLocked(idx); 408 } 409 } 410 } 411 412 // Not needed/used for separate shader programs. 413 void GLSharedGroup::setProgramIndexInfo(GLuint program, GLuint index, GLint base, GLint size, GLenum type, const char* name) 414 { 415 android::AutoMutex _lock(m_lock); 416 ProgramData* pData = m_programs.valueFor(program); 417 if (pData) 418 { 419 pData->setIndexInfo(index,base,size,type); 420 421 if (type == GL_SAMPLER_2D) { 422 size_t n = pData->getNumShaders(); 423 for (size_t i = 0; i < n; i++) { 424 GLuint shaderId = pData->getShader(i); 425 ShaderData* shader = m_shaders.valueFor(shaderId); 426 if (!shader) continue; 427 ShaderData::StringList::iterator nameIter = shader->samplerExternalNames.begin(); 428 ShaderData::StringList::iterator nameEnd = shader->samplerExternalNames.end(); 429 while (nameIter != nameEnd) { 430 if (*nameIter == name) { 431 pData->setIndexFlags(index, ProgramData::INDEX_FLAG_SAMPLER_EXTERNAL); 432 break; 433 } 434 ++nameIter; 435 } 436 } 437 } 438 } 439 } 440 441 GLenum GLSharedGroup::getProgramUniformType(GLuint program, GLint location) 442 { 443 android::AutoMutex _lock(m_lock); 444 ProgramData* pData = m_programs.valueFor(program); 445 GLenum type=0; 446 if (pData) { 447 type = pData->getTypeForLocation(location); 448 } 449 if (m_shaderProgramIdMap.find(program) == m_shaderProgramIdMap.end()) return type; 450 ShaderProgramData* spData = m_shaderPrograms.valueFor(m_shaderProgramIdMap[program]); 451 if (spData) { 452 type = spData->programData->getTypeForLocation(location); 453 } 454 return type; 455 } 456 457 bool GLSharedGroup::isProgram(GLuint program) 458 { 459 android::AutoMutex _lock(m_lock); 460 ProgramData* pData = m_programs.valueFor(program); 461 if (pData) return true; 462 if (m_shaderProgramIdMap.find(program) == m_shaderProgramIdMap.end()) return false; 463 ShaderProgramData* spData = m_shaderPrograms.valueFor(m_shaderProgramIdMap[program]); 464 if (spData) return true; 465 return false; 466 } 467 468 void GLSharedGroup::setupLocationShiftWAR(GLuint program) 469 { 470 android::AutoMutex _lock(m_lock); 471 ProgramData* pData = m_programs.valueFor(program); 472 if (pData) pData->setupLocationShiftWAR(); 473 } 474 475 GLint GLSharedGroup::locationWARHostToApp(GLuint program, GLint hostLoc, GLint arrIndex) 476 { 477 android::AutoMutex _lock(m_lock); 478 ProgramData* pData = m_programs.valueFor(program); 479 if (pData) return pData->locationWARHostToApp(hostLoc, arrIndex); 480 if (m_shaderProgramIdMap.find(program) == m_shaderProgramIdMap.end()) return hostLoc; 481 ShaderProgramData* spData = m_shaderPrograms.valueFor(m_shaderProgramIdMap[program]); 482 if (spData) return spData->programData->locationWARHostToApp(hostLoc, arrIndex); 483 return hostLoc; 484 } 485 486 GLint GLSharedGroup::locationWARAppToHost(GLuint program, GLint appLoc) 487 { 488 android::AutoMutex _lock(m_lock); 489 ProgramData* pData = m_programs.valueFor(program); 490 if (pData) return pData->locationWARAppToHost(appLoc); 491 if (m_shaderProgramIdMap.find(program) == m_shaderProgramIdMap.end()) return appLoc; 492 ShaderProgramData* spData = m_shaderPrograms.valueFor(m_shaderProgramIdMap[program]); 493 if (spData) return spData->programData->locationWARAppToHost(appLoc); 494 return appLoc; 495 } 496 497 bool GLSharedGroup::needUniformLocationWAR(GLuint program) 498 { 499 android::AutoMutex _lock(m_lock); 500 ProgramData* pData = m_programs.valueFor(program); 501 if (pData) return pData->needUniformLocationWAR(); 502 if (m_shaderProgramIdMap.find(program) == m_shaderProgramIdMap.end()) return false; 503 ShaderProgramData* spData = m_shaderPrograms.valueFor(m_shaderProgramIdMap[program]); 504 if (spData) return spData->programData->needUniformLocationWAR(); 505 return false; 506 } 507 508 GLint GLSharedGroup::getNextSamplerUniform(GLuint program, GLint index, GLint* val, GLenum* target) const 509 { 510 android::AutoMutex _lock(m_lock); 511 ProgramData* pData = m_programs.valueFor(program); 512 if (pData) return pData->getNextSamplerUniform(index, val, target); 513 if (m_shaderProgramIdMap.find(program) == m_shaderProgramIdMap.end()) return -1; 514 ShaderProgramData* spData = m_shaderPrograms.valueFor(m_shaderProgramIdMap.find(program)->second); 515 if (spData) return spData->programData->getNextSamplerUniform(index, val, target); 516 return -1; 517 } 518 519 bool GLSharedGroup::setSamplerUniform(GLuint program, GLint appLoc, GLint val, GLenum* target) 520 { 521 android::AutoMutex _lock(m_lock); 522 ProgramData* pData = m_programs.valueFor(program); 523 if (pData) return pData->setSamplerUniform(appLoc, val, target); 524 if (m_shaderProgramIdMap.find(program) == m_shaderProgramIdMap.end()) return false; 525 ShaderProgramData* spData = m_shaderPrograms.valueFor(m_shaderProgramIdMap[program]); 526 if (spData) return spData->programData->setSamplerUniform(appLoc, val, target); 527 return false; 528 } 529 530 bool GLSharedGroup::isShader(GLuint shader) 531 { 532 android::AutoMutex _lock(m_lock); 533 ShaderData* pData = m_shaders.valueFor(shader); 534 return (pData!=NULL); 535 } 536 537 bool GLSharedGroup::addShaderData(GLuint shader) 538 { 539 android::AutoMutex _lock(m_lock); 540 ShaderData* data = new ShaderData; 541 if (data) { 542 if (m_shaders.add(shader, data) < 0) { 543 delete data; 544 data = NULL; 545 } 546 data->refcount = 1; 547 } 548 return data != NULL; 549 } 550 551 ShaderData* GLSharedGroup::getShaderData(GLuint shader) 552 { 553 android::AutoMutex _lock(m_lock); 554 return m_shaders.valueFor(shader); 555 } 556 557 void GLSharedGroup::unrefShaderData(GLuint shader) 558 { 559 android::AutoMutex _lock(m_lock); 560 ssize_t idx = m_shaders.indexOfKey(shader); 561 if (idx >= 0) { 562 unrefShaderDataLocked(idx); 563 } 564 } 565 566 void GLSharedGroup::refShaderDataLocked(ssize_t shaderIdx) 567 { 568 assert(shaderIdx >= 0 && shaderIdx <= m_shaders.size()); 569 ShaderData* data = m_shaders.valueAt(shaderIdx); 570 data->refcount++; 571 } 572 573 void GLSharedGroup::unrefShaderDataLocked(ssize_t shaderIdx) 574 { 575 assert(shaderIdx >= 0 && shaderIdx <= m_shaders.size()); 576 ShaderData* data = m_shaders.valueAt(shaderIdx); 577 if (--data->refcount == 0) { 578 delete data; 579 m_shaders.removeItemsAt(shaderIdx); 580 } 581 } 582 583 uint32_t GLSharedGroup::addNewShaderProgramData() { 584 android::AutoMutex _lock(m_lock); 585 ShaderProgramData* data = new ShaderProgramData; 586 uint32_t currId = m_shaderProgramId; 587 ALOGD("%s: new data %p id %u", __FUNCTION__, data, currId); 588 m_shaderPrograms.add(currId, data); 589 m_shaderProgramId++; 590 return currId; 591 } 592 593 void GLSharedGroup::associateGLShaderProgram(GLuint shaderProgramName, uint32_t shaderProgramId) { 594 android::AutoMutex _lock(m_lock); 595 m_shaderProgramIdMap[shaderProgramName] = shaderProgramId; 596 } 597 598 ShaderProgramData* GLSharedGroup::getShaderProgramDataById(uint32_t id) { 599 android::AutoMutex _lock(m_lock); 600 ShaderProgramData* res = m_shaderPrograms.valueFor(id); 601 ALOGD("%s: id=%u res=%p", __FUNCTION__, id, res); 602 return res; 603 } 604 605 ShaderProgramData* GLSharedGroup::getShaderProgramData(GLuint shaderProgramName) { 606 android::AutoMutex _lock(m_lock); 607 return m_shaderPrograms.valueFor(m_shaderProgramIdMap[shaderProgramName]); 608 } 609 610 void GLSharedGroup::deleteShaderProgramDataById(uint32_t id) { 611 android::AutoMutex _lock(m_lock); 612 ShaderProgramData* data = m_shaderPrograms.valueFor(id); 613 delete data; 614 m_shaderPrograms.removeItemsAt(id); 615 } 616 617 618 void GLSharedGroup::deleteShaderProgramData(GLuint shaderProgramName) { 619 android::AutoMutex _lock(m_lock); 620 uint32_t id = m_shaderProgramIdMap[shaderProgramName]; 621 ShaderProgramData* data = m_shaderPrograms.valueFor(id); 622 delete data; 623 m_shaderPrograms.removeItemsAt(id); 624 m_shaderProgramIdMap.erase(shaderProgramName); 625 } 626 627 void GLSharedGroup::initShaderProgramData(GLuint shaderProgram, GLuint numIndices) { 628 ShaderProgramData* spData = getShaderProgramData(shaderProgram); 629 spData->programData->initProgramData(numIndices); 630 } 631 632 void GLSharedGroup::setShaderProgramIndexInfo(GLuint shaderProgram, GLuint index, GLint base, GLint size, GLenum type, const char* name) { 633 ShaderProgramData* spData = getShaderProgramData(shaderProgram); 634 ProgramData* pData = spData->programData; 635 ShaderData* sData = spData->shaderData; 636 637 if (pData) 638 { 639 pData->setIndexInfo(index, base, size, type); 640 641 if (type == GL_SAMPLER_2D) { 642 ShaderData::StringList::iterator nameIter = sData->samplerExternalNames.begin(); 643 ShaderData::StringList::iterator nameEnd = sData->samplerExternalNames.end(); 644 while (nameIter != nameEnd) { 645 if (*nameIter == name) { 646 pData->setIndexFlags(index, ProgramData::INDEX_FLAG_SAMPLER_EXTERNAL); 647 break; 648 } 649 ++nameIter; 650 } 651 } 652 } 653 } 654 655 void GLSharedGroup::setupShaderProgramLocationShiftWAR(GLuint shaderProgram) { 656 ShaderProgramData* spData = getShaderProgramData(shaderProgram); 657 spData->programData->setupLocationShiftWAR(); 658 } 659