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 21template <typename T> 22static 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 30BufferData::BufferData() : m_size(0), m_usage(0), m_mapped(false) {}; 31BufferData::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 ****/ 39ProgramData::ProgramData() : m_numIndexes(0), 40 m_initialized(false), 41 m_locShiftWAR(false) 42{ 43 m_Indexes = NULL; 44} 45 46void 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 55bool ProgramData::isInitialized() 56{ 57 return m_initialized; 58} 59 60ProgramData::~ProgramData() 61{ 62 delete[] m_Indexes; 63 m_Indexes = NULL; 64} 65 66void 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 85void ProgramData::setIndexFlags(GLuint index, GLuint flags) 86{ 87 if (index >= m_numIndexes) 88 return; 89 m_Indexes[index].flags |= flags; 90} 91 92GLuint 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 108GLenum 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 117void 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 131GLint 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 146GLint 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 160GLint 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 178bool 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 199bool 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 213bool 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 227GLSharedGroup::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 235GLSharedGroup::~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 245bool 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 253BufferData * GLSharedGroup::getBufferData(GLuint bufferId) 254{ 255 android::AutoMutex _lock(m_lock); 256 return m_buffers.valueFor(bufferId); 257} 258 259SharedTextureDataMap* GLSharedGroup::getTextureData() { 260 return &m_textureRecs; 261} 262 263void 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 269void 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 281void 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 289void GLSharedGroup::setBufferMapped(GLuint bufferId, bool mapped) { 290 BufferData * buf = m_buffers.valueFor(bufferId); 291 if (!buf) return; 292 buf->m_mapped = mapped; 293} 294 295GLenum GLSharedGroup::getBufferUsage(GLuint bufferId) { 296 BufferData * buf = m_buffers.valueFor(bufferId); 297 if (!buf) return 0; 298 return buf->m_usage; 299} 300 301bool GLSharedGroup::isBufferMapped(GLuint bufferId) { 302 BufferData * buf = m_buffers.valueFor(bufferId); 303 if (!buf) return false; 304 return buf->m_mapped; 305} 306 307GLenum 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 320void 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 330void 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 343void 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 353bool 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 369void 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. 388void 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 400void 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. 413void 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 441GLenum 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 457bool 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 468void 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 475GLint 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 486GLint 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 497bool 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 508GLint 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 519bool 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 530bool GLSharedGroup::isShader(GLuint shader) 531{ 532 android::AutoMutex _lock(m_lock); 533 ShaderData* pData = m_shaders.valueFor(shader); 534 return (pData!=NULL); 535} 536 537bool 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 551ShaderData* GLSharedGroup::getShaderData(GLuint shader) 552{ 553 android::AutoMutex _lock(m_lock); 554 return m_shaders.valueFor(shader); 555} 556 557void 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 566void 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 573void 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 583uint32_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 593void GLSharedGroup::associateGLShaderProgram(GLuint shaderProgramName, uint32_t shaderProgramId) { 594 android::AutoMutex _lock(m_lock); 595 m_shaderProgramIdMap[shaderProgramName] = shaderProgramId; 596} 597 598ShaderProgramData* 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 605ShaderProgramData* GLSharedGroup::getShaderProgramData(GLuint shaderProgramName) { 606 android::AutoMutex _lock(m_lock); 607 return m_shaderPrograms.valueFor(m_shaderProgramIdMap[shaderProgramName]); 608} 609 610void 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 618void 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 627void GLSharedGroup::initShaderProgramData(GLuint shaderProgram, GLuint numIndices) { 628 ShaderProgramData* spData = getShaderProgramData(shaderProgram); 629 spData->programData->initProgramData(numIndices); 630} 631 632void 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 655void GLSharedGroup::setupShaderProgramLocationShiftWAR(GLuint shaderProgram) { 656 ShaderProgramData* spData = getShaderProgramData(shaderProgram); 657 spData->programData->setupLocationShiftWAR(); 658} 659