llmaterialmgr.cpp 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018
  1. /**
  2. * @file llmaterialmgr.cpp
  3. * @brief Material manager
  4. *
  5. * $LicenseInfo:firstyear=2012&license=viewergpl$
  6. *
  7. * Copyright (c) 2012, Linden Research, Inc.
  8. * Copyright (c) 2012-2024, Henri Beauchamp.
  9. *
  10. * Second Life Viewer Source Code
  11. * The source code in this file ("Source Code") is provided by Linden Lab
  12. * to you under the terms of the GNU General Public License, version 2.0
  13. * ("GPL"), unless you have obtained a separate licensing agreement
  14. * ("Other License"), formally executed by you and Linden Lab. Terms of
  15. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  16. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  17. *
  18. * There are special exceptions to the terms and conditions of the GPL as
  19. * it is applied to this Source Code. View the full text of the exception
  20. * in the file doc/FLOSS-exception.txt in this software distribution, or
  21. * online at
  22. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  23. *
  24. * By copying, modifying or distributing this software, you acknowledge
  25. * that you have read and understood your obligations described above,
  26. * and agree to abide by those obligations.
  27. *
  28. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  29. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  30. * COMPLETENESS OR PERFORMANCE.
  31. * $/LicenseInfo$
  32. */
  33. #include "llviewerprecompiledheaders.h"
  34. #include <utility>
  35. #include "llmaterialmgr.h"
  36. #include "llcallbacklist.h"
  37. #include "llcorehttpcommon.h"
  38. #include "llcorehttputil.h"
  39. #include "llfasttimer.h"
  40. #include "llhttpsdhandler.h"
  41. #include "llsdserialize.h"
  42. #include "llsdutil.h"
  43. #include "llagent.h"
  44. #include "llappviewer.h"
  45. #include "llviewerobjectlist.h"
  46. #include "llviewerregion.h"
  47. #include "llworld.h"
  48. // Materials capability parameters
  49. #define MATERIALS_CAPABILITY_NAME "RenderMaterials"
  50. #define MATERIALS_CAP_ZIP_FIELD "Zipped"
  51. #define MATERIALS_CAP_FULL_PER_FACE_FIELD "FullMaterialsPerFace"
  52. #define MATERIALS_CAP_FACE_FIELD "Face"
  53. #define MATERIALS_CAP_MATERIAL_FIELD "Material"
  54. #define MATERIALS_CAP_OBJECT_ID_FIELD "ID"
  55. #define MATERIALS_CAP_MATERIAL_ID_FIELD "MaterialID"
  56. // Network timeouts
  57. #define MATERIALS_GET_TIMEOUT 120.f
  58. #define MATERIALS_POST_TIMEOUT 120.f
  59. ///////////////////////////////////////////////////////////////////////////////
  60. // LLMaterialHttpHandler class
  61. ///////////////////////////////////////////////////////////////////////////////
  62. class LLMaterialHttpHandler final : public LLHttpSDHandler
  63. {
  64. protected:
  65. LOG_CLASS(LLMaterialHttpHandler);
  66. public:
  67. typedef boost::function<void(bool, const LLSD&)> CallbackFunction;
  68. typedef std::shared_ptr<LLMaterialHttpHandler> ptr_t;
  69. LLMaterialHttpHandler(const std::string& method, CallbackFunction cback)
  70. : LLHttpSDHandler(),
  71. mMethod(method),
  72. mCallback(cback)
  73. {
  74. }
  75. ~LLMaterialHttpHandler() override
  76. {
  77. }
  78. protected:
  79. void onSuccess(LLCore::HttpResponse*, const LLSD& content) override
  80. {
  81. if (mCallback)
  82. {
  83. mCallback(true, content);
  84. }
  85. }
  86. void onFailure(LLCore::HttpResponse* response,
  87. LLCore::HttpStatus status) override
  88. {
  89. if (response)
  90. {
  91. llwarns << mMethod << " Error: " << status.toULong()
  92. << " - Cannot access capability: "
  93. << MATERIALS_CAPABILITY_NAME << " - with URL: "
  94. << response->getRequestURL() << " - reason: "
  95. << status.toString() << llendl;
  96. }
  97. mCallback(false, LLSD());
  98. }
  99. private:
  100. std::string mMethod;
  101. CallbackFunction mCallback;
  102. };
  103. ///////////////////////////////////////////////////////////////////////////////
  104. // LLMaterialMgr class
  105. ///////////////////////////////////////////////////////////////////////////////
  106. LLMaterialMgr::LLMaterialMgr()
  107. {
  108. LLAppCoreHttp& app_core_http(gAppViewerp->getAppCoreHttp());
  109. mHttpPolicy = app_core_http.getPolicy(LLAppCoreHttp::AP_MATERIALS);
  110. mHttpRequest = DEFAULT_HTTP_REQUEST;
  111. mHttpHeaders = DEFAULT_HTTP_HEADERS;
  112. mHttpOptions = DEFAULT_HTTP_OPTIONS;
  113. mHttpAdapter =
  114. std::make_shared<LLCoreHttpUtil::HttpCoroutineAdapter>("processGetAllQueue");
  115. mMaterials.emplace(LLMaterialID::null, LLMaterialPtr(NULL));
  116. gIdleCallbacks.addFunction(&LLMaterialMgr::onIdle, NULL);
  117. gWorld.setRegionRemovedCallback(boost::bind(&LLMaterialMgr::onRegionRemoved,
  118. this, _1));
  119. }
  120. LLMaterialMgr::~LLMaterialMgr()
  121. {
  122. gIdleCallbacks.deleteFunction(&LLMaterialMgr::onIdle, NULL);
  123. mHttpAdapter.reset();
  124. mHttpRequest.reset();
  125. mHttpOptions.reset();
  126. mHttpHeaders.reset();
  127. }
  128. bool LLMaterialMgr::isGetPending(const LLUUID& region_id,
  129. const LLMaterialID& material_id) const
  130. {
  131. get_pending_map_t::const_iterator it =
  132. mGetPending.find(RegionMaterialPair(region_id, material_id));
  133. return it != mGetPending.end() &&
  134. LLFrameTimer::getTotalSeconds() < it->second + MATERIALS_POST_TIMEOUT;
  135. }
  136. void LLMaterialMgr::markGetPending(const LLUUID& region_id,
  137. const LLMaterialID& material_id)
  138. {
  139. get_pending_map_t::iterator it =
  140. mGetPending.find(RegionMaterialPair(region_id, material_id));
  141. if (it == mGetPending.end())
  142. {
  143. mGetPending.emplace(RegionMaterialPair(region_id, material_id),
  144. LLFrameTimer::getTotalSeconds());
  145. }
  146. else
  147. {
  148. it->second = LLFrameTimer::getTotalSeconds();
  149. }
  150. }
  151. const LLMaterialPtr LLMaterialMgr::get(const LLUUID& region_id,
  152. const LLMaterialID& material_id)
  153. {
  154. LL_DEBUGS("Materials") << "region: " << region_id << " - material id: "
  155. << material_id << LL_ENDL;
  156. material_map_t::const_iterator mit = mMaterials.find(material_id);
  157. if (mit != mMaterials.end())
  158. {
  159. LL_DEBUGS("Materials") << "Found material " << material_id << LL_ENDL;
  160. return mit->second;
  161. }
  162. if (!isGetPending(region_id, material_id))
  163. {
  164. LL_DEBUGS("Materials") << "Material pending: " << material_id
  165. << LL_ENDL;
  166. get_queue_t::iterator qit = mGetQueue.find(region_id);
  167. if (qit == mGetQueue.end())
  168. {
  169. LL_DEBUGS("Materials") << "mGetQueue add region: " << region_id
  170. << " - pending material: " << material_id
  171. << LL_ENDL;
  172. std::pair<get_queue_t::iterator, bool> ret =
  173. mGetQueue.emplace(region_id, material_queue_t());
  174. qit = ret.first;
  175. }
  176. qit->second.emplace(material_id);
  177. markGetPending(region_id, material_id);
  178. }
  179. LL_DEBUGS("Materials") << "Returning empty material " << LL_ENDL;
  180. return LLMaterialPtr();
  181. }
  182. void LLMaterialMgr::get(const LLUUID& region_id,
  183. const LLMaterialID& material_id,
  184. LLMaterialMgr::get_cb_t::slot_type cb)
  185. {
  186. material_map_t::const_iterator mit = mMaterials.find(material_id);
  187. if (mit != mMaterials.end())
  188. {
  189. LL_DEBUGS("Materials") << "Region " << region_id
  190. << ", found materialid " << material_id
  191. << LL_ENDL;
  192. get_cb_t signal;
  193. signal.connect(cb);
  194. signal(material_id, mit->second);
  195. }
  196. else
  197. {
  198. if (!isGetPending(region_id, material_id))
  199. {
  200. get_queue_t::iterator qit = mGetQueue.find(region_id);
  201. if (qit == mGetQueue.end())
  202. {
  203. LL_DEBUGS("Materials") << "mGetQueue inserting region: "
  204. << region_id << LL_ENDL;
  205. std::pair<get_queue_t::iterator, bool> ret =
  206. mGetQueue.emplace(region_id, material_queue_t());
  207. qit = ret.first;
  208. }
  209. LL_DEBUGS("Materials") << "Adding material id " << material_id
  210. << LL_ENDL;
  211. qit->second.emplace(material_id);
  212. markGetPending(region_id, material_id);
  213. }
  214. get_cb_map_t::iterator cb_it = mGetCallbacks.find(material_id);
  215. if (cb_it == mGetCallbacks.end())
  216. {
  217. std::pair<get_cb_map_t::iterator, bool> ret =
  218. mGetCallbacks.emplace(material_id, new get_cb_t());
  219. cb_it = ret.first;
  220. }
  221. cb_it->second->connect(cb);
  222. }
  223. }
  224. void LLMaterialMgr::getTE(const LLUUID& region_id,
  225. const LLMaterialID& material_id, U32 te,
  226. LLMaterialMgr::get_te_cb_t::slot_type cb)
  227. {
  228. material_map_t::const_iterator mit = mMaterials.find(material_id);
  229. if (mit != mMaterials.end())
  230. {
  231. LL_DEBUGS("Materials") << "Region: " << region_id
  232. << " - Found materialid: " << material_id
  233. << LL_ENDL;
  234. get_te_cb_t signal;
  235. signal.connect(cb);
  236. signal(material_id, mit->second, te);
  237. }
  238. else
  239. {
  240. if (!isGetPending(region_id, material_id))
  241. {
  242. get_queue_t::iterator qit = mGetQueue.find(region_id);
  243. if (mGetQueue.end() == qit)
  244. {
  245. LL_DEBUGS("Materials") << "mGetQueue inserting region: "
  246. << region_id << LL_ENDL;
  247. std::pair<get_queue_t::iterator, bool> ret =
  248. mGetQueue.emplace(region_id, material_queue_t());
  249. qit = ret.first;
  250. }
  251. LL_DEBUGS("Materials") << "Adding material id: " << material_id
  252. << LL_ENDL;
  253. qit->second.emplace(material_id);
  254. markGetPending(region_id, material_id);
  255. }
  256. TEMaterialPair te_mat_pair;
  257. te_mat_pair.mTE = te;
  258. te_mat_pair.mMaterialId = material_id;
  259. get_te_cb_map_t::iterator cb_it = mGetTECallbacks.find(te_mat_pair);
  260. if (cb_it == mGetTECallbacks.end())
  261. {
  262. std::pair<get_te_cb_map_t::iterator, bool> ret =
  263. mGetTECallbacks.emplace(te_mat_pair, new get_te_cb_t());
  264. cb_it = ret.first;
  265. }
  266. cb_it->second->connect(cb);
  267. }
  268. }
  269. bool LLMaterialMgr::isGetAllPending(const LLUUID& region_id) const
  270. {
  271. getall_pending_map_t::const_iterator it = mGetAllPending.find(region_id);
  272. return it != mGetAllPending.end() &&
  273. LLFrameTimer::getTotalSeconds() < it->second + MATERIALS_GET_TIMEOUT;
  274. }
  275. void LLMaterialMgr::getAll(const LLUUID& region_id)
  276. {
  277. if (!isGetAllPending(region_id))
  278. {
  279. LL_DEBUGS("Materials") << "Queuing for region " << region_id
  280. << LL_ENDL;
  281. mGetAllQueue.emplace(region_id);
  282. }
  283. else
  284. {
  285. LL_DEBUGS("Materials") << "Already pending for region " << region_id
  286. << LL_ENDL;
  287. }
  288. }
  289. void LLMaterialMgr::getAll(const LLUUID& region_id,
  290. LLMaterialMgr::get_all_cb_t::slot_type cb)
  291. {
  292. if (!isGetAllPending(region_id))
  293. {
  294. mGetAllQueue.emplace(region_id);
  295. }
  296. get_all_cb_map_t::iterator cb_it = mGetAllCallbacks.find(region_id);
  297. if (cb_it == mGetAllCallbacks.end())
  298. {
  299. std::pair<get_all_cb_map_t::iterator, bool> ret =
  300. mGetAllCallbacks.emplace(region_id, new get_all_cb_t());
  301. cb_it = ret.first;
  302. }
  303. cb_it->second->connect(cb);
  304. }
  305. void LLMaterialMgr::put(const LLUUID& object_id, U8 te, const LLMaterial& mat)
  306. {
  307. put_queue_t::iterator qit = mPutQueue.find(object_id);
  308. if (qit == mPutQueue.end())
  309. {
  310. LL_DEBUGS("Materials") << "mPutQueue insert object " << object_id
  311. << LL_ENDL;
  312. mPutQueue.emplace(object_id, facematerial_map_t());
  313. qit = mPutQueue.find(object_id);
  314. }
  315. facematerial_map_t::iterator fit = qit->second.find(te);
  316. if (fit == qit->second.end())
  317. {
  318. qit->second.emplace(te, mat);
  319. }
  320. else
  321. {
  322. fit->second = mat;
  323. }
  324. }
  325. void LLMaterialMgr::remove(const LLUUID& object_id, U8 te)
  326. {
  327. put(object_id, te, LLMaterial::null);
  328. }
  329. void LLMaterialMgr::setLocalMaterial(const LLUUID& region_id,
  330. LLMaterialPtr material_ptr)
  331. {
  332. LLUUID uuid;
  333. uuid.generate();
  334. LLMaterialID material_id(uuid);
  335. LL_DEBUGS("Materials") << "Created a new local material: " << material_id
  336. << " - region: " << region_id << LL_ENDL;
  337. mMaterials.emplace(material_id, material_ptr);
  338. mGetPending.erase(RegionMaterialPair(region_id, material_id));
  339. }
  340. const LLMaterialPtr LLMaterialMgr::setMaterial(const LLUUID& region_id,
  341. const LLMaterialID& material_id,
  342. const LLSD& material_data)
  343. {
  344. LL_DEBUGS("Materials") << "Region: " << region_id << " - material id: "
  345. << material_id << LL_ENDL;
  346. material_map_t::const_iterator it = mMaterials.find(material_id);
  347. if (it == mMaterials.end())
  348. {
  349. LL_DEBUGS("Materials") << "New material" << LL_ENDL;
  350. LLMaterialPtr new_matp(new LLMaterial(material_data));
  351. it = mMaterials.emplace(material_id, std::move(new_matp)).first;
  352. }
  353. setMaterialCallbacks(material_id, it->second);
  354. mGetPending.erase(RegionMaterialPair(region_id, material_id));
  355. return it->second;
  356. }
  357. void LLMaterialMgr::setMaterialCallbacks(const LLMaterialID& material_id,
  358. const LLMaterialPtr& material_ptr)
  359. {
  360. TEMaterialPair te_mat_pair;
  361. te_mat_pair.mMaterialId = material_id;
  362. U32 i = 0;
  363. while (i < MAX_TES && !mGetTECallbacks.empty())
  364. {
  365. te_mat_pair.mTE = i++;
  366. get_te_cb_map_t::iterator te_it = mGetTECallbacks.find(te_mat_pair);
  367. if (te_it != mGetTECallbacks.end())
  368. {
  369. (*te_it->second)(material_id, material_ptr, te_mat_pair.mTE);
  370. delete te_it->second;
  371. mGetTECallbacks.hmap_erase(te_it);
  372. }
  373. }
  374. get_cb_map_t::iterator cb_it = mGetCallbacks.find(material_id);
  375. if (cb_it != mGetCallbacks.end())
  376. {
  377. (*cb_it->second)(material_id, material_ptr);
  378. delete cb_it->second;
  379. mGetCallbacks.hmap_erase(cb_it);
  380. }
  381. }
  382. void LLMaterialMgr::onGetResponse(bool success, const LLSD& content,
  383. const LLUUID& region_id)
  384. {
  385. if (!success)
  386. {
  387. // *TODO: is there any kind of error handling we can do here ?
  388. llwarns << "Failed in region: " << region_id << llendl;
  389. return;
  390. }
  391. if (!content.isMap() || !content.has(MATERIALS_CAP_ZIP_FIELD) ||
  392. !content[MATERIALS_CAP_ZIP_FIELD].isBinary())
  393. {
  394. llwarns << "Invalid response LLSD in region: " << region_id << llendl;
  395. return;
  396. }
  397. const LLSD::Binary& bin_data = content[MATERIALS_CAP_ZIP_FIELD].asBinary();
  398. LLSD response_data;
  399. if (!unzip_llsd(response_data, bin_data.data(), bin_data.size()))
  400. {
  401. llwarns << "Cannot unzip LLSD binary content in region: " << region_id
  402. << llendl;
  403. return;
  404. }
  405. if (!response_data.isArray())
  406. {
  407. llwarns << "Invalid response data LLSD in region: " << region_id
  408. << llendl;
  409. return;
  410. }
  411. LL_DEBUGS("Materials") << "Response has "<< response_data.size()
  412. << " materials" << LL_ENDL;
  413. for (LLSD::array_const_iterator it = response_data.beginArray(),
  414. end = response_data.endArray();
  415. it != end; ++it)
  416. {
  417. const LLSD& material_data = *it;
  418. if (!material_data.isMap() ||
  419. !material_data.has(MATERIALS_CAP_OBJECT_ID_FIELD) ||
  420. !material_data[MATERIALS_CAP_OBJECT_ID_FIELD].isBinary() ||
  421. !material_data.has(MATERIALS_CAP_MATERIAL_FIELD) ||
  422. !material_data[MATERIALS_CAP_MATERIAL_FIELD].isMap())
  423. {
  424. llwarns << "Invalid material data LLSD in region: " << region_id
  425. << llendl;
  426. continue;
  427. }
  428. const LLSD::Binary& bin_data =
  429. material_data[MATERIALS_CAP_OBJECT_ID_FIELD].asBinary();
  430. if (bin_data.size() != UUID_BYTES)
  431. {
  432. llwarns << "Invalid material Id binary bucket size: "
  433. << bin_data.size() << " (should be "
  434. << UUID_BYTES << ") - Region: " << region_id
  435. << llendl;
  436. continue;
  437. }
  438. LLMaterialID material_id(bin_data);
  439. setMaterial(region_id, material_id,
  440. material_data[MATERIALS_CAP_MATERIAL_FIELD]);
  441. }
  442. }
  443. void LLMaterialMgr::onGetAllResponse(bool success, const LLSD& content,
  444. const LLUUID& region_id)
  445. {
  446. if (!success)
  447. {
  448. // *TODO: is there any kind of error handling we can do here?
  449. llwarns << "Failed in region: " << region_id << llendl;
  450. return;
  451. }
  452. if (!content.isMap() || !content.has(MATERIALS_CAP_ZIP_FIELD) ||
  453. !content[MATERIALS_CAP_ZIP_FIELD].isBinary())
  454. {
  455. llwarns << "Invalid response LLSD in region: " << region_id << llendl;
  456. return;
  457. }
  458. const LLSD::Binary& bin_data = content[MATERIALS_CAP_ZIP_FIELD].asBinary();
  459. LLSD response_data;
  460. if (!unzip_llsd(response_data, bin_data.data(), bin_data.size()))
  461. {
  462. llwarns << "Cannot unzip LLSD binary content in region: " << region_id
  463. << llendl;
  464. return;
  465. }
  466. if (!response_data.isArray())
  467. {
  468. llwarns << "Invalid response data LLSD in region: " << region_id
  469. << llendl;
  470. return;
  471. }
  472. get_queue_t::iterator qit = mGetQueue.find(region_id);
  473. material_map_t materials;
  474. LL_DEBUGS("Materials") << "response has "<< response_data.size()
  475. << " materials" << LL_ENDL;
  476. for (LLSD::array_const_iterator it = response_data.beginArray(),
  477. end = response_data.endArray();
  478. it != end; ++it)
  479. {
  480. const LLSD& material_data = *it;
  481. if (!material_data.isMap() ||
  482. !material_data.has(MATERIALS_CAP_OBJECT_ID_FIELD) ||
  483. !material_data[MATERIALS_CAP_OBJECT_ID_FIELD].isBinary())
  484. {
  485. llwarns << "Invalid material data LLSD (1) in region: "
  486. << region_id << llendl;
  487. continue;
  488. }
  489. const LLSD::Binary& bin_data =
  490. material_data[MATERIALS_CAP_OBJECT_ID_FIELD].asBinary();
  491. if (bin_data.size() != UUID_BYTES)
  492. {
  493. llwarns << "Invalid material Id binary bucket size: "
  494. << bin_data.size() << " (should be "
  495. << UUID_BYTES << ") - Region: " << region_id
  496. << llendl;
  497. continue;
  498. }
  499. LLMaterialID material_id(bin_data);
  500. if (mGetQueue.end() != qit)
  501. {
  502. qit->second.erase(material_id);
  503. }
  504. if (!material_data.has(MATERIALS_CAP_MATERIAL_FIELD) ||
  505. !material_data[MATERIALS_CAP_MATERIAL_FIELD].isMap())
  506. {
  507. llwarns << "Invalid material data LLSD (2) in region: "
  508. << region_id << llendl;
  509. continue;
  510. }
  511. LLMaterialPtr material =
  512. setMaterial(region_id, material_id,
  513. material_data[MATERIALS_CAP_MATERIAL_FIELD]);
  514. materials[material_id] = material;
  515. }
  516. get_all_cb_map_t::iterator cb_it = mGetAllCallbacks.find(region_id);
  517. if (cb_it != mGetAllCallbacks.end() && cb_it->second)
  518. {
  519. (*cb_it->second)(region_id, materials);
  520. delete cb_it->second;
  521. mGetAllCallbacks.hmap_erase(cb_it);
  522. }
  523. if (qit != mGetQueue.end() && qit->second.empty())
  524. {
  525. mGetQueue.hmap_erase(qit);
  526. }
  527. LL_DEBUGS("Materials") << "Recording that getAll has been done for region: "
  528. << region_id << LL_ENDL;
  529. // Prevents subsequent getAll requests for this region
  530. mGetAllRequested.emplace(region_id);
  531. // Invalidates region_id
  532. mGetAllPending.erase(region_id);
  533. }
  534. void LLMaterialMgr::onPutResponse(bool success, const LLSD& content)
  535. {
  536. if (!success)
  537. {
  538. // *TODO: is there any kind of error handling we can do here ?
  539. llwarns << "Failed" << llendl;
  540. return;
  541. }
  542. if (!content.isMap() || !content.has(MATERIALS_CAP_ZIP_FIELD) ||
  543. !content[MATERIALS_CAP_ZIP_FIELD].isBinary())
  544. {
  545. llwarns << "Invalid response LLSD" << llendl;
  546. return;
  547. }
  548. const LLSD::Binary& bin_data = content[MATERIALS_CAP_ZIP_FIELD].asBinary();
  549. LLSD response_data;
  550. if (!unzip_llsd(response_data, bin_data.data(), bin_data.size()))
  551. {
  552. llwarns << "Cannot unzip LLSD binary content" << llendl;
  553. return;
  554. }
  555. if (response_data.isArray())
  556. {
  557. LL_DEBUGS("Materials") << "Response has " << response_data.size()
  558. << " materials" << LL_ENDL;
  559. }
  560. else
  561. {
  562. llwarns << "Invalid response data LLSD" << llendl;
  563. }
  564. }
  565. void LLMaterialMgr::onIdle(void*)
  566. {
  567. LL_FAST_TIMER(FTM_MATERIALS_IDLE);
  568. LLMaterialMgr* self = getInstance();
  569. if (!self->mGetQueue.empty())
  570. {
  571. self->processGetQueue();
  572. }
  573. if (!self->mGetAllQueue.empty())
  574. {
  575. self->processGetAllQueue();
  576. }
  577. if (!self->mPutQueue.empty())
  578. {
  579. self->processPutQueue();
  580. }
  581. self->mHttpRequest->update(0L);
  582. }
  583. void LLMaterialMgr::processGetQueue()
  584. {
  585. get_queue_t::iterator loop_rqit = mGetQueue.begin();
  586. while (loop_rqit != mGetQueue.end())
  587. {
  588. get_queue_t::iterator rqit = loop_rqit++;
  589. LLUUID region_id = rqit->first;
  590. if (isGetAllPending(region_id))
  591. {
  592. continue;
  593. }
  594. LLViewerRegion* regionp = gWorld.getRegionFromID(region_id);
  595. if (!regionp)
  596. {
  597. llwarns << "Unknown region with id " << region_id << llendl;
  598. mGetQueue.hmap_erase(rqit);
  599. continue;
  600. }
  601. else if (!regionp->capabilitiesReceived() ||
  602. regionp->materialsCapThrottled())
  603. {
  604. continue;
  605. }
  606. else if (mGetAllRequested.find(region_id) == mGetAllRequested.end())
  607. {
  608. LL_DEBUGS("Materials") << "Calling getAll for "
  609. << regionp->getName() << LL_ENDL;
  610. getAll(region_id);
  611. continue;
  612. }
  613. const std::string cap_url =
  614. regionp->getCapability(MATERIALS_CAPABILITY_NAME);
  615. if (cap_url.empty())
  616. {
  617. llwarns << "Capability '" << MATERIALS_CAPABILITY_NAME
  618. << "' is not defined on region: "
  619. << regionp->getIdentity() << llendl;
  620. mGetQueue.hmap_erase(rqit);
  621. continue;
  622. }
  623. LLSD mats_data = LLSD::emptyArray();
  624. material_queue_t& materials = rqit->second;
  625. U32 max_entries = regionp->getMaxMaterialsPerTransaction();
  626. material_queue_t::iterator loop_mit = materials.begin();
  627. while (loop_mit != materials.end() &&
  628. (U32)mats_data.size() <= max_entries)
  629. {
  630. material_queue_t::iterator mit = loop_mit++;
  631. mats_data.append(mit->asLLSD());
  632. markGetPending(region_id, *mit);
  633. materials.erase(mit);
  634. }
  635. if (materials.empty())
  636. {
  637. mGetQueue.hmap_erase(rqit);
  638. }
  639. std::string material_str = zip_llsd(mats_data);
  640. S32 mat_size = material_str.size();
  641. if (mat_size <= 0)
  642. {
  643. llwarns << "Could not zip LLSD binary content in region: "
  644. << region_id << llendl;
  645. return;
  646. }
  647. LLSD::Binary material_bin;
  648. material_bin.resize(mat_size);
  649. memcpy(material_bin.data(), material_str.data(), mat_size);
  650. LLSD post_data = LLSD::emptyMap();
  651. post_data[MATERIALS_CAP_ZIP_FIELD] = material_bin;
  652. LLCore::HttpHandler::ptr_t
  653. handler(new LLMaterialHttpHandler("POST",
  654. boost::bind(&LLMaterialMgr::onGetResponse,
  655. this, _1, _2,
  656. region_id)));
  657. LL_DEBUGS("Materials") << "POSTing to region '" << regionp->getName()
  658. << "' at '"<< cap_url << " for "
  659. << mats_data.size() << " materials. Data:\n"
  660. << ll_pretty_print_sd(mats_data) << LL_ENDL;
  661. LLCore::HttpHandle handle;
  662. handle = LLCoreHttpUtil::requestPostWithLLSD(mHttpRequest, mHttpPolicy,
  663. cap_url, post_data,
  664. mHttpOptions, mHttpHeaders,
  665. handler);
  666. if (!instanceExists()) return; // Viewer is being closed down !
  667. if (handle == LLCORE_HTTP_HANDLE_INVALID)
  668. {
  669. LLCore::HttpStatus status = mHttpRequest->getStatus();
  670. llwarns << "Failed to post materials. Status: " << status.toULong()
  671. << " - " << status.toString() << llendl;
  672. }
  673. regionp->resetMaterialsCapThrottle();
  674. }
  675. }
  676. void LLMaterialMgr::processGetAllQueue()
  677. {
  678. uuid_list_t::iterator loop_rit = mGetAllQueue.begin();
  679. while (loop_rit != mGetAllQueue.end())
  680. {
  681. uuid_list_t::iterator rit = loop_rit++;
  682. const LLUUID& region_id = *rit;
  683. LLViewerRegion* regionp = gWorld.getRegionFromID(region_id);
  684. if (!regionp)
  685. {
  686. llwarns << "Unknown region with id " << region_id << llendl;
  687. clearGetQueues(region_id); // Invalidates region_id
  688. continue;
  689. }
  690. else if (!regionp->capabilitiesReceived() ||
  691. regionp->materialsCapThrottled())
  692. {
  693. continue;
  694. }
  695. const std::string& url =
  696. regionp->getCapability(MATERIALS_CAPABILITY_NAME);
  697. if (url.empty())
  698. {
  699. llwarns << "Capability '" << MATERIALS_CAPABILITY_NAME
  700. << "' is not defined for region: "
  701. << regionp->getIdentity() << llendl;
  702. clearGetQueues(region_id); // Invalidates region_id
  703. continue;
  704. }
  705. LL_DEBUGS("Materials") << "GET all for region: " << region_id
  706. << " - url: " << url << LL_ENDL;
  707. gCoros.launch("LLMaterialMgr::processGetAllQueueCoro",
  708. boost::bind(&LLMaterialMgr::processGetAllQueueCoro, this,
  709. url, region_id));
  710. regionp->resetMaterialsCapThrottle();
  711. mGetAllPending[region_id] = LLFrameTimer::getTotalSeconds();
  712. mGetAllQueue.erase(rit); // Invalidates region_id
  713. }
  714. }
  715. void LLMaterialMgr::processGetAllQueueCoro(const std::string& url,
  716. LLUUID region_id)
  717. {
  718. LLSD result = mHttpAdapter->getAndSuspend(url, mHttpOptions, mHttpHeaders);
  719. if (!instanceExists()) return; // Viewer is being closed down !
  720. LLCore::HttpStatus status =
  721. LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
  722. if (status)
  723. {
  724. onGetAllResponse(true, result, region_id);
  725. }
  726. else
  727. {
  728. onGetAllResponse(false, LLSD(), region_id);
  729. }
  730. }
  731. void LLMaterialMgr::processPutQueue()
  732. {
  733. typedef fast_hmap<LLViewerRegion*, LLSD> regionput_req_map_t;
  734. regionput_req_map_t requests;
  735. put_queue_t::iterator loop_qit = mPutQueue.begin();
  736. while (loop_qit != mPutQueue.end())
  737. {
  738. put_queue_t::iterator qit = loop_qit++;
  739. const LLUUID& object_id = qit->first;
  740. const LLViewerObject* objectp = gObjectList.findObject(object_id);
  741. if (!objectp)
  742. {
  743. llwarns << "Cannot find object " << object_id << llendl;
  744. mPutQueue.hmap_erase(qit);
  745. continue;
  746. }
  747. LLViewerRegion* regionp = objectp->getRegion();
  748. if (!regionp)
  749. {
  750. llwarns << "Cannot find region for object " << object_id << llendl;
  751. mPutQueue.hmap_erase(qit);
  752. continue;
  753. }
  754. if (!regionp->capabilitiesReceived() ||
  755. regionp->materialsCapThrottled())
  756. {
  757. continue;
  758. }
  759. LLSD& faces_data = requests[regionp];
  760. facematerial_map_t& face_map = qit->second;
  761. U32 max_entries = regionp->getMaxMaterialsPerTransaction();
  762. facematerial_map_t::iterator fit = face_map.begin();
  763. while (face_map.end() != fit && (U32)faces_data.size() < max_entries)
  764. {
  765. LLSD face_data = LLSD::emptyMap();
  766. face_data[MATERIALS_CAP_FACE_FIELD] = LLSD::Integer(fit->first);
  767. face_data[MATERIALS_CAP_OBJECT_ID_FIELD] =
  768. LLSD::Integer(objectp->getLocalID());
  769. if (!fit->second.isNull())
  770. {
  771. face_data[MATERIALS_CAP_MATERIAL_FIELD] = fit->second.asLLSD();
  772. }
  773. faces_data.append(face_data);
  774. face_map.erase(fit++);
  775. }
  776. if (face_map.empty())
  777. {
  778. mPutQueue.hmap_erase(qit);
  779. }
  780. }
  781. for (regionput_req_map_t::const_iterator it = requests.begin(),
  782. end = requests.end();
  783. it != end; ++it)
  784. {
  785. LLViewerRegion* regionp = it->first;
  786. if (!regionp) continue; // Paranoia
  787. const std::string cap_url =
  788. regionp->getCapability(MATERIALS_CAPABILITY_NAME);
  789. if (cap_url.empty())
  790. {
  791. llwarns << "Capability '" << MATERIALS_CAPABILITY_NAME
  792. << "' is not defined for region: "
  793. << regionp->getIdentity() << llendl;
  794. continue;
  795. }
  796. LLSD mats_data = LLSD::emptyMap();
  797. mats_data[MATERIALS_CAP_FULL_PER_FACE_FIELD] = it->second;
  798. std::string material_str = zip_llsd(mats_data);
  799. S32 mat_size = material_str.size();
  800. if (mat_size <= 0)
  801. {
  802. llwarns << "Could not zip LLSD binary content" << llendl;
  803. continue;
  804. }
  805. LLSD::Binary material_bin;
  806. material_bin.resize(mat_size);
  807. memcpy(material_bin.data(), material_str.data(), mat_size);
  808. LLSD put_data = LLSD::emptyMap();
  809. put_data[MATERIALS_CAP_ZIP_FIELD] = material_bin;
  810. LL_DEBUGS("Materials") << "Put for " << it->second.size()
  811. << " faces to region " << regionp->getIdentity()
  812. << LL_ENDL;
  813. LLCore::HttpHandler::ptr_t
  814. handler(new LLMaterialHttpHandler("PUT",
  815. boost::bind(&LLMaterialMgr::onPutResponse,
  816. this, _1, _2)));
  817. LLCore::HttpHandle handle =
  818. LLCoreHttpUtil::requestPutWithLLSD(mHttpRequest, mHttpPolicy,
  819. cap_url, put_data, mHttpOptions,
  820. mHttpHeaders, handler);
  821. if (!instanceExists()) return; // Viewer is being closed down !
  822. if (handle == LLCORE_HTTP_HANDLE_INVALID)
  823. {
  824. LLCore::HttpStatus status = mHttpRequest->getStatus();
  825. llwarns << "Failed to put materials. Status: " << status.toULong()
  826. << " - " << status.toString() << llendl;
  827. }
  828. regionp->resetMaterialsCapThrottle();
  829. }
  830. }
  831. void LLMaterialMgr::clearGetQueues(const LLUUID& region_id)
  832. {
  833. mGetQueue.erase(region_id);
  834. for (get_pending_map_t::iterator it = mGetPending.begin();
  835. it != mGetPending.end(); )
  836. {
  837. if (region_id == it->first.mRegionId)
  838. {
  839. mGetPending.hmap_erase(it++);
  840. }
  841. else
  842. {
  843. ++it;
  844. }
  845. }
  846. mGetAllQueue.erase(region_id);
  847. mGetAllRequested.erase(region_id);
  848. mGetAllPending.erase(region_id);
  849. mGetAllCallbacks.erase(region_id);
  850. }
  851. void LLMaterialMgr::onRegionRemoved(LLViewerRegion* regionp)
  852. {
  853. clearGetQueues(regionp->getRegionID());
  854. // Put does not need clearing: objects that cannot be found will clean up
  855. // in processPutQueue()
  856. }
  857. //static
  858. void LLMaterialMgr::cleanupClass()
  859. {
  860. if (LLMaterialMgr::instanceExists())
  861. {
  862. llinfos << "Cleaning up the materials manager queues..." << llendl;
  863. LLMaterialMgr* self = LLMaterialMgr::getInstance();
  864. for (get_cb_map_t::iterator it = self->mGetCallbacks.begin(),
  865. end = self->mGetCallbacks.end();
  866. it != end; ++it)
  867. {
  868. delete it->second;
  869. }
  870. self->mGetCallbacks.clear();
  871. for (get_te_cb_map_t::iterator it = self->mGetTECallbacks.begin(),
  872. end = self->mGetTECallbacks.end();
  873. it != end; ++it)
  874. {
  875. delete it->second;
  876. }
  877. self->mGetTECallbacks.clear();
  878. for (get_all_cb_map_t::iterator it = self->mGetAllCallbacks.begin(),
  879. end = self->mGetAllCallbacks.end();
  880. it != end; ++it)
  881. {
  882. delete it->second;
  883. }
  884. self->mGetAllCallbacks.clear();
  885. self->mGetPending.clear();
  886. self->mGetQueue.clear();
  887. self->mPutQueue.clear();
  888. self->mGetAllQueue.clear();
  889. self->mGetAllRequested.clear();
  890. self->mGetAllPending.clear();
  891. self->mMaterials.clear();
  892. llinfos << "Materials manager queues emptied." << llendl;
  893. }
  894. }