BsLightProbeVolume.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "Renderer/BsLightProbeVolume.h"
  4. #include "RTTI/BsLightProbeVolumeRTTI.h"
  5. #include "Allocators/BsFrameAlloc.h"
  6. #include "Renderer/BsRenderer.h"
  7. #include "Renderer/BsLight.h"
  8. #include "RenderAPI/BsGpuBuffer.h"
  9. #include "Image/BsTexture.h"
  10. #include "Renderer/BsIBLUtility.h"
  11. #include "Scene/BsSceneObject.h"
  12. namespace bs
  13. {
  14. LightProbeVolumeBase::LightProbeVolumeBase()
  15. : mPosition(BsZero), mRotation(BsIdentity), mIsActive(true)
  16. { }
  17. LightProbeVolume::LightProbeVolume()
  18. : mVolume(AABox::UNIT_BOX), mCellCount { 1, 1, 1 }, mLastUpdateHash(0)
  19. { }
  20. LightProbeVolume::LightProbeVolume(const AABox& volume, const Vector3I& cellCount)
  21. :mVolume(volume), mCellCount(cellCount), mLastUpdateHash(0)
  22. {
  23. reset();
  24. }
  25. LightProbeVolume::~LightProbeVolume()
  26. {
  27. if (mRendererTask)
  28. mRendererTask->cancel();
  29. }
  30. UINT32 LightProbeVolume::addProbe(const Vector3& position)
  31. {
  32. UINT32 handle = mNextProbeId++;
  33. mProbes[handle] = ProbeInfo(LightProbeFlags::Clean, position);
  34. _markCoreDirty();
  35. return handle;
  36. }
  37. void LightProbeVolume::removeProbe(UINT32 handle)
  38. {
  39. auto iterFind = mProbes.find(handle);
  40. if (iterFind != mProbes.end() && mProbes.size() > 4)
  41. {
  42. iterFind->second.flags = LightProbeFlags::Removed;
  43. _markCoreDirty();
  44. }
  45. }
  46. void LightProbeVolume::setProbePosition(UINT32 handle, const Vector3& position)
  47. {
  48. auto iterFind = mProbes.find(handle);
  49. if (iterFind != mProbes.end())
  50. {
  51. iterFind->second.position = position;
  52. _markCoreDirty();
  53. }
  54. }
  55. Vector3 LightProbeVolume::getProbePosition(UINT32 handle) const
  56. {
  57. auto iterFind = mProbes.find(handle);
  58. if (iterFind != mProbes.end())
  59. return iterFind->second.position;
  60. return Vector3::ZERO;
  61. }
  62. Vector<LightProbeInfo> LightProbeVolume::getProbes() const
  63. {
  64. Vector<LightProbeInfo> output;
  65. for(auto& entry : mProbes)
  66. {
  67. if (entry.second.flags == LightProbeFlags::Removed || entry.second.flags == LightProbeFlags::Empty)
  68. continue;
  69. LightProbeInfo info;
  70. info.position = entry.second.position;
  71. info.handle = entry.first;
  72. info.shCoefficients = entry.second.coefficients;
  73. output.push_back(info);
  74. }
  75. return output;
  76. }
  77. void LightProbeVolume::resize(const AABox& volume, const Vector3I& cellCount)
  78. {
  79. UINT32 numProbesX = std::max(1, mCellCount.v[0]) + 1;
  80. UINT32 numProbesY = std::max(1, mCellCount.v[1]) + 1;
  81. UINT32 numProbesZ = std::max(1, mCellCount.v[2]) + 1;
  82. Vector3 size = mVolume.getSize();
  83. for(UINT32 z = 0; z < numProbesZ; ++z)
  84. {
  85. for(UINT32 y = 0; y < numProbesY; ++y)
  86. {
  87. for(UINT32 x = 0; x < numProbesX; ++x)
  88. {
  89. Vector3 position = mVolume.getMin();
  90. position.x += size.x * (x / (float)numProbesX);
  91. position.y += size.y * (y / (float)numProbesY);
  92. position.z += size.z * (z / (float)numProbesZ);
  93. if (mVolume.contains(position))
  94. continue;
  95. addProbe(position);
  96. }
  97. }
  98. }
  99. mVolume = volume;
  100. mCellCount = cellCount;
  101. _markCoreDirty();
  102. }
  103. void LightProbeVolume::reset()
  104. {
  105. UINT32 numProbesX = std::max(1, mCellCount.v[0]) + 1;
  106. UINT32 numProbesY = std::max(1, mCellCount.v[1]) + 1;
  107. UINT32 numProbesZ = std::max(1, mCellCount.v[2]) + 1;
  108. UINT32 numProbes = numProbesX * numProbesY * numProbesZ;
  109. // Make sure there are adequate number of probes to fill the volume
  110. while((UINT32)mProbes.size() < numProbes)
  111. addProbe(Vector3::ZERO);
  112. UINT32 idx = 0;
  113. UINT32 rowPitch = numProbesX;
  114. UINT32 slicePitch = numProbesX * numProbesY;
  115. Vector3 size = mVolume.getSize();
  116. auto iter = mProbes.begin();
  117. while (iter != mProbes.end())
  118. {
  119. UINT32 x = idx % numProbesX;
  120. UINT32 y = (idx / rowPitch) % numProbesY;
  121. UINT32 z = (idx / slicePitch);
  122. Vector3 position = mVolume.getMin();
  123. position.x += size.x * (x / (float)(numProbesX - 1));
  124. position.y += size.y * (y / (float)(numProbesY - 1));
  125. position.z += size.z * (z / (float)(numProbesZ - 1));
  126. iter->second.position = position;
  127. iter->second.flags = LightProbeFlags::Clean;
  128. ++idx;
  129. ++iter;
  130. if (idx >= numProbes)
  131. break;
  132. }
  133. // Set remaining probes to removed state
  134. while(iter != mProbes.end())
  135. {
  136. iter->second.flags = LightProbeFlags::Removed;
  137. ++iter;
  138. }
  139. _markCoreDirty();
  140. }
  141. void LightProbeVolume::clip()
  142. {
  143. for (auto& entry : mProbes)
  144. {
  145. if (!mVolume.contains(entry.second.position))
  146. entry.second.flags = LightProbeFlags::Removed;
  147. }
  148. _markCoreDirty();
  149. }
  150. void LightProbeVolume::renderProbe(UINT32 handle)
  151. {
  152. auto iterFind = mProbes.find(handle);
  153. if (iterFind != mProbes.end())
  154. {
  155. if (iterFind->second.flags == LightProbeFlags::Clean)
  156. {
  157. iterFind->second.flags = LightProbeFlags::Dirty;
  158. _markCoreDirty();
  159. runRenderProbeTask();
  160. }
  161. }
  162. }
  163. void LightProbeVolume::renderProbes()
  164. {
  165. bool anyModified = false;
  166. for(auto& entry : mProbes)
  167. {
  168. if (entry.second.flags == LightProbeFlags::Clean)
  169. {
  170. entry.second.flags = LightProbeFlags::Dirty;
  171. anyModified = true;
  172. }
  173. }
  174. if (anyModified)
  175. {
  176. _markCoreDirty();
  177. runRenderProbeTask();
  178. }
  179. }
  180. void LightProbeVolume::_updateTransform(const HSceneObject& so, bool force)
  181. {
  182. UINT32 curHash = so->getTransformHash();
  183. if (curHash != _getLastModifiedHash() || force)
  184. {
  185. mPosition = so->getWorldPosition();
  186. mRotation = so->getWorldRotation();
  187. _markCoreDirty();
  188. _setLastModifiedHash(curHash);
  189. }
  190. }
  191. void LightProbeVolume::runRenderProbeTask()
  192. {
  193. // If a task is already running cancel it
  194. // Note: If the task is just about to start processing, cancelling it will skip the update this frame
  195. // (which might be fine if we just changed positions of dirty probes it was about to update, but it might also
  196. // waste a frame if those positions needed to be updated anyway). For now I'm ignoring it as it seems like a rare
  197. // enough situation, plus it's one that will only happen during development time.
  198. if (mRendererTask)
  199. mRendererTask->cancel();
  200. auto renderComplete = [this]()
  201. {
  202. mRendererTask = nullptr;
  203. };
  204. SPtr<ct::LightProbeVolume> coreProbeVolume = getCore();
  205. auto renderProbes = [coreProbeVolume]()
  206. {
  207. return coreProbeVolume->renderProbes(3);
  208. };
  209. mRendererTask = ct::RendererTask::create("RenderLightProbes", renderProbes);
  210. mRendererTask->onComplete.connect(renderComplete);
  211. ct::gRenderer()->addTask(mRendererTask);
  212. }
  213. void LightProbeVolume::updateCoefficients()
  214. {
  215. // Ensure all light probe coefficients are generated
  216. if (mRendererTask)
  217. mRendererTask->wait();
  218. ct::LightProbeVolume* coreVolume = getCore().get();
  219. Vector<LightProbeCoefficientInfo> coeffInfo;
  220. auto getSaveData = [coreVolume, &coeffInfo]()
  221. {
  222. coreVolume->getProbeCoefficients(coeffInfo);
  223. };
  224. gCoreThread().queueCommand(getSaveData);
  225. gCoreThread().submit(true);
  226. for(auto& entry : coeffInfo)
  227. {
  228. auto iterFind = mProbes.find(entry.handle);
  229. if (iterFind == mProbes.end())
  230. continue;
  231. iterFind->second.coefficients = entry.coefficients;
  232. }
  233. }
  234. SPtr<ct::LightProbeVolume> LightProbeVolume::getCore() const
  235. {
  236. return std::static_pointer_cast<ct::LightProbeVolume>(mCoreSpecific);
  237. }
  238. SPtr<LightProbeVolume> LightProbeVolume::create(const AABox& volume, const Vector3I& cellCount)
  239. {
  240. LightProbeVolume* probeVolume = new (bs_alloc<LightProbeVolume>()) LightProbeVolume(volume, cellCount);
  241. SPtr<LightProbeVolume> probeVolumePtr = bs_core_ptr<LightProbeVolume>(probeVolume);
  242. probeVolumePtr->_setThisPtr(probeVolumePtr);
  243. probeVolumePtr->initialize();
  244. return probeVolumePtr;
  245. }
  246. SPtr<LightProbeVolume> LightProbeVolume::createEmpty()
  247. {
  248. LightProbeVolume* probeVolume = new (bs_alloc<LightProbeVolume>()) LightProbeVolume();
  249. SPtr<LightProbeVolume> probleVolumePtr = bs_core_ptr<LightProbeVolume>(probeVolume);
  250. probleVolumePtr->_setThisPtr(probleVolumePtr);
  251. return probleVolumePtr;
  252. }
  253. SPtr<ct::CoreObject> LightProbeVolume::createCore() const
  254. {
  255. ct::LightProbeVolume* handler = new (bs_alloc<ct::LightProbeVolume>()) ct::LightProbeVolume(mProbes);
  256. SPtr<ct::LightProbeVolume> handlerPtr = bs_shared_ptr<ct::LightProbeVolume>(handler);
  257. handlerPtr->_setThisPtr(handlerPtr);
  258. return handlerPtr;
  259. }
  260. CoreSyncData LightProbeVolume::syncToCore(FrameAlloc* allocator)
  261. {
  262. UINT32 size = 0;
  263. UINT8* buffer = nullptr;
  264. bs_frame_mark();
  265. {
  266. FrameVector<std::pair<UINT32, ProbeInfo>> dirtyProbes;
  267. FrameVector<UINT32> removedProbes;
  268. for (auto& probe : mProbes)
  269. {
  270. if (probe.second.flags == LightProbeFlags::Dirty)
  271. {
  272. dirtyProbes.push_back(std::make_pair(probe.first, probe.second));
  273. probe.second.flags = LightProbeFlags::Clean;
  274. }
  275. else if (probe.second.flags == LightProbeFlags::Removed)
  276. {
  277. removedProbes.push_back(probe.first);
  278. probe.second.flags = LightProbeFlags::Empty;
  279. }
  280. }
  281. for (auto& probe : removedProbes)
  282. mProbes.erase(probe);
  283. UINT32 numDirtyProbes = (UINT32)dirtyProbes.size();
  284. UINT32 numRemovedProbes = (UINT32)removedProbes.size();
  285. size += rttiGetElemSize(mPosition);
  286. size += rttiGetElemSize(mRotation);
  287. size += rttiGetElemSize(mIsActive);
  288. size += rttiGetElemSize(numDirtyProbes);
  289. size += rttiGetElemSize(numRemovedProbes);
  290. size += (sizeof(UINT32) + sizeof(Vector3) + sizeof(LightProbeFlags)) * numDirtyProbes;
  291. size += sizeof(UINT32) * numRemovedProbes;
  292. buffer = allocator->alloc(size);
  293. char* dataPtr = (char*)buffer;
  294. dataPtr = rttiWriteElem(mPosition, dataPtr);
  295. dataPtr = rttiWriteElem(mRotation, dataPtr);
  296. dataPtr = rttiWriteElem(mIsActive, dataPtr);
  297. dataPtr = rttiWriteElem(numDirtyProbes, dataPtr);
  298. dataPtr = rttiWriteElem(numRemovedProbes, dataPtr);
  299. for (auto& entry : dirtyProbes)
  300. {
  301. dataPtr = rttiWriteElem(entry.first, dataPtr);
  302. dataPtr = rttiWriteElem(entry.second.position, dataPtr);
  303. dataPtr = rttiWriteElem(entry.second.flags, dataPtr);
  304. }
  305. for(auto& entry : removedProbes)
  306. dataPtr = rttiWriteElem(entry, dataPtr);
  307. }
  308. bs_frame_clear();
  309. return CoreSyncData(buffer, size);
  310. }
  311. void LightProbeVolume::_markCoreDirty()
  312. {
  313. markCoreDirty();
  314. }
  315. RTTITypeBase* LightProbeVolume::getRTTIStatic()
  316. {
  317. return LightProbeVolumeRTTI::instance();
  318. }
  319. RTTITypeBase* LightProbeVolume::getRTTI() const
  320. {
  321. return LightProbeVolume::getRTTIStatic();
  322. }
  323. namespace ct
  324. {
  325. LightProbeVolume::LightProbeVolume(const UnorderedMap<UINT32, bs::LightProbeVolume::ProbeInfo>& probes)
  326. {
  327. mInitCoefficients.resize(probes.size());
  328. mProbePositions.resize(probes.size());
  329. mProbeInfos.resize(probes.size());
  330. UINT32 probeIdx = 0;
  331. for(auto& entry : probes)
  332. {
  333. mProbeMap[entry.first] = probeIdx;
  334. mProbePositions[probeIdx] = entry.second.position;
  335. LightProbeInfo probeInfo;
  336. probeInfo.flags = LightProbeFlags::Dirty;
  337. probeInfo.bufferIdx = probeIdx;
  338. probeInfo.handle = entry.first;
  339. mProbeInfos[probeIdx] = probeInfo;
  340. mInitCoefficients[probeIdx] = entry.second.coefficients;
  341. probeIdx++;
  342. }
  343. }
  344. LightProbeVolume::~LightProbeVolume()
  345. {
  346. gRenderer()->notifyLightProbeVolumeRemoved(this);
  347. }
  348. void LightProbeVolume::initialize()
  349. {
  350. // Set SH coefficients loaded from the file
  351. UINT32 numCoefficients = (UINT32)mInitCoefficients.size();
  352. assert(mInitCoefficients.size() == mProbeMap.size());
  353. resizeCoefficientBuffer(std::max(32U, numCoefficients));
  354. mCoefficients->writeData(0, sizeof(LightProbeSHCoefficients) * numCoefficients, mInitCoefficients.data());
  355. mInitCoefficients.clear();
  356. gRenderer()->notifyLightProbeVolumeAdded(this);
  357. CoreObject::initialize();
  358. }
  359. bool LightProbeVolume::renderProbes(UINT32 maxProbes)
  360. {
  361. // Probe map only contains active probes
  362. UINT32 numUsedProbes = (UINT32)mProbeMap.size();
  363. if(numUsedProbes > mCoeffBufferSize)
  364. resizeCoefficientBuffer(std::max(32U, numUsedProbes * 2));
  365. UINT32 numProbeUpdates = 0;
  366. for (; mFirstDirtyProbe < (UINT32)mProbeInfos.size(); ++mFirstDirtyProbe)
  367. {
  368. LightProbeInfo& probeInfo = mProbeInfos[mFirstDirtyProbe];
  369. if(probeInfo.flags == LightProbeFlags::Dirty)
  370. {
  371. TEXTURE_DESC cubemapDesc;
  372. cubemapDesc.type = TEX_TYPE_CUBE_MAP;
  373. cubemapDesc.format = PF_RGBA16F;
  374. cubemapDesc.width = 256; // Note: Test different sizes and their effect on quality
  375. cubemapDesc.height = 256;
  376. cubemapDesc.usage = TU_STATIC | TU_RENDERTARGET;
  377. SPtr<Texture> cubemap = Texture::create(cubemapDesc);
  378. Vector3 localPos = mProbePositions[mFirstDirtyProbe];
  379. Vector3 transformedPos = mRotation.rotate(localPos) + mPosition;
  380. gRenderer()->captureSceneCubeMap(cubemap, transformedPos, CaptureSettings());
  381. gIBLUtility().filterCubemapForIrradiance(cubemap, mCoefficients, probeInfo.bufferIdx);
  382. probeInfo.flags = LightProbeFlags::Clean;
  383. numProbeUpdates++;
  384. }
  385. if (maxProbes != 0 && numProbeUpdates >= maxProbes)
  386. break;
  387. }
  388. gRenderer()->notifyLightProbeVolumeUpdated(this);
  389. return mFirstDirtyProbe == (UINT32)mProbeInfos.size();
  390. }
  391. void LightProbeVolume::syncToCore(const CoreSyncData& data)
  392. {
  393. char* dataPtr = (char*)data.getBuffer();
  394. bool oldIsActive = mIsActive;
  395. dataPtr = rttiReadElem(mPosition, dataPtr);
  396. dataPtr = rttiReadElem(mRotation, dataPtr);
  397. dataPtr = rttiReadElem(mIsActive, dataPtr);
  398. UINT32 numDirtyProbes, numRemovedProbes;
  399. dataPtr = rttiReadElem(numDirtyProbes, dataPtr);
  400. dataPtr = rttiReadElem(numRemovedProbes, dataPtr);
  401. for (UINT32 i = 0; i < numDirtyProbes; ++i)
  402. {
  403. UINT32 handle;
  404. dataPtr = rttiReadElem(handle, dataPtr);
  405. Vector3 position;
  406. dataPtr = rttiReadElem(position, dataPtr);
  407. LightProbeFlags flags;
  408. dataPtr = rttiReadElem(flags, dataPtr);
  409. auto iterFind = mProbeMap.find(handle);
  410. if(iterFind != mProbeMap.end())
  411. {
  412. // Update existing probe information
  413. UINT32 compactIdx = iterFind->second;
  414. mProbeInfos[compactIdx].flags = LightProbeFlags::Dirty;
  415. mProbePositions[compactIdx] = position;
  416. mFirstDirtyProbe = std::min(compactIdx, mFirstDirtyProbe);
  417. }
  418. else // Add a new probe
  419. {
  420. // Empty slots always start at a specific index because we always move them to the back of the array
  421. UINT32 emptyProbeStartIdx = (UINT32)mProbeMap.size();
  422. UINT32 numProbes = (UINT32)mProbeInfos.size();
  423. // Find an empty slot to place the probe information at
  424. UINT32 compactIdx = -1;
  425. for(UINT32 j = emptyProbeStartIdx; j < numProbes; ++j)
  426. {
  427. if(mProbeInfos[j].flags == LightProbeFlags::Empty)
  428. {
  429. compactIdx = j;
  430. break;
  431. }
  432. }
  433. // Found an empty slot
  434. if (compactIdx == -1)
  435. {
  436. compactIdx = (UINT32)mProbeInfos.size();
  437. LightProbeInfo info;
  438. info.flags = LightProbeFlags::Dirty;
  439. info.bufferIdx = compactIdx;
  440. info.handle = handle;
  441. mProbeInfos.push_back(info);
  442. mProbePositions.push_back(position);
  443. }
  444. else // No empty slot, add a new one
  445. {
  446. LightProbeInfo& info = mProbeInfos[compactIdx];
  447. info.flags = LightProbeFlags::Dirty;
  448. info.handle = handle;
  449. mProbePositions[compactIdx] = position;
  450. }
  451. mProbeMap[handle] = compactIdx;
  452. mFirstDirtyProbe = std::min(compactIdx, mFirstDirtyProbe);
  453. }
  454. }
  455. // Mark slots for removed probes as empty, and move them back to the end of the array
  456. for (UINT32 i = 0; i < numRemovedProbes; ++i)
  457. {
  458. UINT32 idx;
  459. dataPtr = rttiReadElem(idx, dataPtr);
  460. auto iterFind = mProbeMap.find(idx);
  461. if(iterFind != mProbeMap.end())
  462. {
  463. UINT32 compactIdx = iterFind->second;
  464. LightProbeInfo& info = mProbeInfos[compactIdx];
  465. info.flags = LightProbeFlags::Empty;
  466. // Move the empty info to the back of the array so all non-empty probes are contiguous
  467. // Search from back to current index, and find first non-empty probe to switch switch
  468. UINT32 lastSearchIdx = (UINT32)mProbeInfos.size() - 1;
  469. while (lastSearchIdx >= (INT32)compactIdx)
  470. {
  471. LightProbeFlags flags = mProbeInfos[lastSearchIdx].flags;
  472. if (flags != LightProbeFlags::Empty)
  473. {
  474. std::swap(mProbeInfos[i], mProbeInfos[lastSearchIdx]);
  475. std::swap(mProbePositions[i], mProbePositions[lastSearchIdx]);
  476. mProbeMap[mProbeInfos[lastSearchIdx].handle] = i;
  477. break;
  478. }
  479. lastSearchIdx--;
  480. }
  481. mProbeMap.erase(iterFind);
  482. }
  483. }
  484. if (oldIsActive != mIsActive)
  485. {
  486. if (mIsActive)
  487. gRenderer()->notifyLightProbeVolumeAdded(this);
  488. else
  489. gRenderer()->notifyLightProbeVolumeRemoved(this);
  490. }
  491. }
  492. void LightProbeVolume::getProbeCoefficients(Vector<LightProbeCoefficientInfo>& output) const
  493. {
  494. UINT32 numActiveProbes = (UINT32)mProbeMap.size();
  495. if (numActiveProbes == 0)
  496. return;
  497. output.resize(numActiveProbes);
  498. LightProbeSHCoefficients* coefficients = bs_stack_alloc<LightProbeSHCoefficients>(numActiveProbes);
  499. mCoefficients->readData(0, sizeof(LightProbeSHCoefficients) * numActiveProbes, coefficients);
  500. for(UINT32 i = 0; i < numActiveProbes; ++i)
  501. {
  502. output[i].coefficients = coefficients[mProbeInfos[i].bufferIdx];
  503. output[i].handle = mProbeInfos[i].handle;
  504. }
  505. bs_stack_free(coefficients);
  506. }
  507. void LightProbeVolume::resizeCoefficientBuffer(UINT32 count)
  508. {
  509. GPU_BUFFER_DESC desc;
  510. desc.type = GBT_STRUCTURED;
  511. desc.elementSize = sizeof(LightProbeSHCoefficients);
  512. desc.elementCount = count;
  513. desc.usage = GBU_STATIC;
  514. desc.format = BF_UNKNOWN;
  515. desc.randomGpuWrite = true;
  516. SPtr<GpuBuffer> newBuffer = GpuBuffer::create(desc);
  517. if (mCoefficients)
  518. newBuffer->copyData(*mCoefficients, 0, 0, mCoefficients->getSize(), true);
  519. mCoefficients = newBuffer;
  520. mCoeffBufferSize = count;
  521. }
  522. }}