diff options
Diffstat (limited to 'indra/newview/llmaterialmgr.cpp')
-rw-r--r-- | indra/newview/llmaterialmgr.cpp | 641 |
1 files changed, 641 insertions, 0 deletions
diff --git a/indra/newview/llmaterialmgr.cpp b/indra/newview/llmaterialmgr.cpp new file mode 100644 index 0000000000..3138bfd4e1 --- /dev/null +++ b/indra/newview/llmaterialmgr.cpp @@ -0,0 +1,641 @@ +/** + * @file llmaterialmgr.cpp + * @brief Material manager + * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llsdserialize.h" +#include "llsdutil.h" + +#include "llagent.h" +#include "llcallbacklist.h" +#include "llmaterialmgr.h" +#include "llviewerobject.h" +#include "llviewerobjectlist.h" +#include "llviewerregion.h" +#include "llworld.h" + +/** + * Materials cap parameters + */ + +#define MATERIALS_CAPABILITY_NAME "RenderMaterials" + +#define MATERIALS_CAP_ZIP_FIELD "Zipped" + +#define MATERIALS_CAP_FULL_PER_FACE_FIELD "FullMaterialsPerFace" +#define MATERIALS_CAP_FACE_FIELD "Face" +#define MATERIALS_CAP_MATERIAL_FIELD "Material" +#define MATERIALS_CAP_OBJECT_ID_FIELD "ID" +#define MATERIALS_CAP_MATERIAL_ID_FIELD "MaterialID" + +#define MATERIALS_GET_MAX_ENTRIES 50 +#define MATERIALS_GET_TIMEOUT (60.f * 20) +#define MATERIALS_POST_MAX_ENTRIES 50 +#define MATERIALS_POST_TIMEOUT (60.f * 5) + +/** + * LLMaterialsResponder helper class + */ + +class LLMaterialsResponder : public LLHTTPClient::Responder +{ +public: + typedef boost::function<void (bool, const LLSD&)> CallbackFunction; + + LLMaterialsResponder(const std::string& pMethod, const std::string& pCapabilityURL, CallbackFunction pCallback); + virtual ~LLMaterialsResponder(); + + virtual void result(const LLSD& pContent); + virtual void error(U32 pStatus, const std::string& pReason); + +private: + std::string mMethod; + std::string mCapabilityURL; + CallbackFunction mCallback; +}; + +LLMaterialsResponder::LLMaterialsResponder(const std::string& pMethod, const std::string& pCapabilityURL, CallbackFunction pCallback) + : LLHTTPClient::Responder() + , mMethod(pMethod) + , mCapabilityURL(pCapabilityURL) + , mCallback(pCallback) +{ +} + +LLMaterialsResponder::~LLMaterialsResponder() +{ +} + +void LLMaterialsResponder::result(const LLSD& pContent) +{ + mCallback(true, pContent); +} + +void LLMaterialsResponder::error(U32 pStatus, const std::string& pReason) +{ + LL_WARNS("debugMaterials") << "--------------------------------------------------------------------------" << LL_ENDL; + LL_WARNS("debugMaterials") << mMethod << " Error[" << pStatus << "] cannot access cap '" << MATERIALS_CAPABILITY_NAME + << "' with url '" << mCapabilityURL << "' because " << pReason << LL_ENDL; + LL_WARNS("debugMaterials") << "--------------------------------------------------------------------------" << LL_ENDL; + + LLSD emptyResult; + mCallback(false, emptyResult); +} + +/** + * LLMaterialMgr class + */ + +LLMaterialMgr::LLMaterialMgr() +{ + gIdleCallbacks.addFunction(&LLMaterialMgr::onIdle, NULL); + LLWorld::instance().setRegionRemovedCallback(boost::bind(&LLMaterialMgr::onRegionRemoved, this, _1)); +} + +LLMaterialMgr::~LLMaterialMgr() +{ + gIdleCallbacks.deleteFunction(&LLMaterialMgr::onIdle, NULL); +} + +bool LLMaterialMgr::isGetPending(const LLUUID& region_id, const LLMaterialID& material_id) +{ + get_pending_map_t::const_iterator itPending = mGetPending.find(pending_material_t(region_id, material_id)); + return (mGetPending.end() != itPending) && (LLFrameTimer::getTotalSeconds() < itPending->second + MATERIALS_POST_TIMEOUT); +} + +const LLMaterialPtr LLMaterialMgr::get(const LLUUID& region_id, const LLMaterialID& material_id) +{ + material_map_t::const_iterator itMaterial = mMaterials.find(material_id); + if (mMaterials.end() != itMaterial) + { + return itMaterial->second; + } + + if (!isGetPending(region_id, material_id)) + { + get_queue_t::iterator itQueue = mGetQueue.find(region_id); + if (mGetQueue.end() == itQueue) + { + std::pair<get_queue_t::iterator, bool> ret = mGetQueue.insert(std::pair<LLUUID, material_queue_t>(region_id, material_queue_t())); + itQueue = ret.first; + } + itQueue->second.insert(material_id); + } + return LLMaterialPtr(); +} + +boost::signals2::connection LLMaterialMgr::get(const LLUUID& region_id, const LLMaterialID& material_id, LLMaterialMgr::get_callback_t::slot_type cb) +{ + material_map_t::const_iterator itMaterial = mMaterials.find(material_id); + if (itMaterial != mMaterials.end()) + { + get_callback_t signal; + signal.connect(cb); + signal(material_id, itMaterial->second); + return boost::signals2::connection(); + } + + if (!isGetPending(region_id, material_id)) + { + get_queue_t::iterator itQueue = mGetQueue.find(region_id); + if (mGetQueue.end() == itQueue) + { + std::pair<get_queue_t::iterator, bool> ret = mGetQueue.insert(std::pair<LLUUID, material_queue_t>(region_id, material_queue_t())); + itQueue = ret.first; + } + itQueue->second.insert(material_id); + } + + get_callback_map_t::iterator itCallback = mGetCallbacks.find(material_id); + if (itCallback == mGetCallbacks.end()) + { + std::pair<get_callback_map_t::iterator, bool> ret = mGetCallbacks.insert(std::pair<LLMaterialID, get_callback_t*>(material_id, new get_callback_t())); + itCallback = ret.first; + } + return itCallback->second->connect(cb);; +} + +bool LLMaterialMgr::isGetAllPending(const LLUUID& region_id) +{ + getall_pending_map_t::const_iterator itPending = mGetAllPending.find(region_id); + return (mGetAllPending.end() != itPending) && (LLFrameTimer::getTotalSeconds() < itPending->second + MATERIALS_GET_TIMEOUT); +} + +void LLMaterialMgr::getAll(const LLUUID& region_id) +{ + if (!isGetAllPending(region_id)) + { + mGetAllQueue.insert(region_id); + } +} + +boost::signals2::connection LLMaterialMgr::getAll(const LLUUID& region_id, LLMaterialMgr::getall_callback_t::slot_type cb) +{ + if (!isGetAllPending(region_id)) + { + mGetAllQueue.insert(region_id); + } + + getall_callback_map_t::iterator itCallback = mGetAllCallbacks.find(region_id); + if (mGetAllCallbacks.end() == itCallback) + { + std::pair<getall_callback_map_t::iterator, bool> ret = mGetAllCallbacks.insert(std::pair<LLUUID, getall_callback_t*>(region_id, new getall_callback_t())); + itCallback = ret.first; + } + return itCallback->second->connect(cb);; +} + +void LLMaterialMgr::put(const LLUUID& object_id, const U8 te, const LLMaterial& material) +{ + put_queue_t::iterator itQueue = mPutQueue.find(object_id); + if (mPutQueue.end() == itQueue) + { + mPutQueue.insert(std::pair<LLUUID, facematerial_map_t>(object_id, facematerial_map_t())); + itQueue = mPutQueue.find(object_id); + } + + facematerial_map_t::iterator itFace = itQueue->second.find(te); + if (itQueue->second.end() == itFace) + { + itQueue->second.insert(std::pair<U8, LLMaterial>(te, material)); + } + else + { + itFace->second = material; + } +} + +const LLMaterialPtr LLMaterialMgr::setMaterial(const LLUUID& region_id, const LLMaterialID& material_id, const LLSD& material_data) +{ + material_map_t::const_iterator itMaterial = mMaterials.find(material_id); + if (mMaterials.end() == itMaterial) + { + LLMaterialPtr newMaterial(new LLMaterial(material_data)); + std::pair<material_map_t::const_iterator, bool> ret = mMaterials.insert(std::pair<LLMaterialID, LLMaterialPtr>(material_id, newMaterial)); + itMaterial = ret.first; + } + + mGetPending.erase(pending_material_t(region_id, material_id)); + + get_callback_map_t::iterator itCallback = mGetCallbacks.find(material_id); + if (itCallback != mGetCallbacks.end()) + { + (*itCallback->second)(material_id, itMaterial->second); + + delete itCallback->second; + mGetCallbacks.erase(itCallback); + } + + return itMaterial->second; +} + +void LLMaterialMgr::onGetResponse(bool success, const LLSD& content, const LLUUID& region_id) +{ + if (!success) + { + // *TODO: is there any kind of error handling we can do here? + return; + } + + llassert(content.isMap()); + llassert(content.has(MATERIALS_CAP_ZIP_FIELD)); + llassert(content[MATERIALS_CAP_ZIP_FIELD].isBinary()); + + LLSD::Binary content_binary = content[MATERIALS_CAP_ZIP_FIELD].asBinary(); + std::string content_string(reinterpret_cast<const char*>(content_binary.data()), content_binary.size()); + std::istringstream content_stream(content_string); + + LLSD response_data; + if (!unzip_llsd(response_data, content_stream, content_binary.size())) + { + LL_ERRS("debugMaterials") << "Cannot unzip LLSD binary content" << LL_ENDL; + return; + } + + llassert(response_data.isArray()); + for (LLSD::array_const_iterator itMaterial = response_data.beginArray(); itMaterial != response_data.endArray(); ++itMaterial) + { + const LLSD& material_data = *itMaterial; + llassert(material_data.isMap()); + + llassert(material_data.has(MATERIALS_CAP_OBJECT_ID_FIELD)); + llassert(material_data[MATERIALS_CAP_OBJECT_ID_FIELD].isBinary()); + LLMaterialID material_id(material_data[MATERIALS_CAP_OBJECT_ID_FIELD].asBinary()); + + llassert(material_data.has(MATERIALS_CAP_MATERIAL_FIELD)); + llassert(material_data[MATERIALS_CAP_MATERIAL_FIELD].isMap()); + + setMaterial(region_id, material_id, material_data[MATERIALS_CAP_MATERIAL_FIELD]); + } +} + +void LLMaterialMgr::onGetAllResponse(bool success, const LLSD& content, const LLUUID& region_id) +{ + if (!success) + { + // *TODO: is there any kind of error handling we can do here? + return; + } + + llassert(content.isMap()); + llassert(content.has(MATERIALS_CAP_ZIP_FIELD)); + llassert(content[MATERIALS_CAP_ZIP_FIELD].isBinary()); + + LLSD::Binary content_binary = content[MATERIALS_CAP_ZIP_FIELD].asBinary(); + std::string content_string(reinterpret_cast<const char*>(content_binary.data()), content_binary.size()); + std::istringstream content_stream(content_string); + + LLSD response_data; + if (!unzip_llsd(response_data, content_stream, content_binary.size())) + { + LL_ERRS("debugMaterials") << "Cannot unzip LLSD binary content" << LL_ENDL; + return; + } + + get_queue_t::iterator itQueue = mGetQueue.find(region_id); + material_map_t materials; + + llassert(response_data.isArray()); + for (LLSD::array_const_iterator itMaterial = response_data.beginArray(); itMaterial != response_data.endArray(); ++itMaterial) + { + const LLSD& material_data = *itMaterial; + llassert(material_data.isMap()); + + llassert(material_data.has(MATERIALS_CAP_OBJECT_ID_FIELD)); + llassert(material_data[MATERIALS_CAP_OBJECT_ID_FIELD].isBinary()); + LLMaterialID material_id(material_data[MATERIALS_CAP_OBJECT_ID_FIELD].asBinary()); + if (mGetQueue.end() != itQueue) + { + itQueue->second.erase(material_id); + } + + llassert(material_data.has(MATERIALS_CAP_MATERIAL_FIELD)); + llassert(material_data[MATERIALS_CAP_MATERIAL_FIELD].isMap()); + LLMaterialPtr material = setMaterial(region_id, material_id, material_data[MATERIALS_CAP_MATERIAL_FIELD]); + + materials[material_id] = material; + } + + getall_callback_map_t::iterator itCallback = mGetAllCallbacks.find(region_id); + if (itCallback != mGetAllCallbacks.end()) + { + (*itCallback->second)(region_id, materials); + + delete itCallback->second; + mGetAllCallbacks.erase(itCallback); + } + + if ( (mGetQueue.end() != itQueue) && (itQueue->second.empty()) ) + { + mGetQueue.erase(itQueue); + } + mGetAllRequested.insert(region_id); + mGetAllPending.erase(region_id); // Invalidates region_id +} + +void LLMaterialMgr::onPutResponse(bool success, const LLSD& content) +{ + if (!success) + { + // *TODO: is there any kind of error handling we can do here? + return; + } + + llassert(content.isMap()); + llassert(content.has(MATERIALS_CAP_ZIP_FIELD)); + llassert(content[MATERIALS_CAP_ZIP_FIELD].isBinary()); + + LLSD::Binary content_binary = content[MATERIALS_CAP_ZIP_FIELD].asBinary(); + std::string content_string(reinterpret_cast<const char*>(content_binary.data()), content_binary.size()); + std::istringstream content_stream(content_string); + + LLSD response_data; + if (!unzip_llsd(response_data, content_stream, content_binary.size())) + { + LL_ERRS("debugMaterials") << "Cannot unzip LLSD binary content" << LL_ENDL; + return; + } + else + { + llassert(response_data.isArray()); + + for (LLSD::array_const_iterator faceIter = response_data.beginArray(); faceIter != response_data.endArray(); ++faceIter) + { +# ifndef LL_RELEASE_FOR_DOWNLOAD + const LLSD& face_data = *faceIter; // conditional to avoid unused variable warning +# endif + llassert(face_data.isMap()); + + llassert(face_data.has(MATERIALS_CAP_OBJECT_ID_FIELD)); + llassert(face_data[MATERIALS_CAP_OBJECT_ID_FIELD].isInteger()); + // U32 local_id = face_data[MATERIALS_CAP_OBJECT_ID_FIELD].asInteger(); + + llassert(face_data.has(MATERIALS_CAP_FACE_FIELD)); + llassert(face_data[MATERIALS_CAP_FACE_FIELD].isInteger()); + // S32 te = face_data[MATERIALS_CAP_FACE_FIELD].asInteger(); + + llassert(face_data.has(MATERIALS_CAP_MATERIAL_ID_FIELD)); + llassert(face_data[MATERIALS_CAP_MATERIAL_ID_FIELD].isBinary()); + // LLMaterialID material_id(face_data[MATERIALS_CAP_MATERIAL_ID_FIELD].asBinary()); + + // *TODO: do we really still need to process this? + } + } +} + +static LLFastTimer::DeclareTimer FTM_MATERIALS_IDLE("Materials"); + +void LLMaterialMgr::onIdle(void*) +{ + LLFastTimer t(FTM_MATERIALS_IDLE); + + LLMaterialMgr* instancep = LLMaterialMgr::getInstance(); + + if (!instancep->mGetQueue.empty()) + { + instancep->processGetQueue(); + } + + if (!instancep->mGetAllQueue.empty()) + { + instancep->processGetAllQueue(); + } + + if (!instancep->mPutQueue.empty()) + { + instancep->processPutQueue(); + } +} + +void LLMaterialMgr::processGetQueue() +{ + get_queue_t::iterator loopRegionQueue = mGetQueue.begin(); + while (mGetQueue.end() != loopRegionQueue) + { + get_queue_t::iterator itRegionQueue = loopRegionQueue++; + + const LLUUID& region_id = itRegionQueue->first; + if (isGetAllPending(region_id)) + { + continue; + } + + const LLViewerRegion* regionp = LLWorld::instance().getRegionFromID(region_id); + if (!regionp) + { + LL_WARNS("debugMaterials") << "Unknown region with id " << region_id.asString() << LL_ENDL; + mGetQueue.erase(itRegionQueue); + continue; + } + else if (!regionp->capabilitiesReceived()) + { + continue; + } + else if (mGetAllRequested.end() == mGetAllRequested.find(region_id)) + { + getAll(region_id); + continue; + } + + const std::string capURL = regionp->getCapability(MATERIALS_CAPABILITY_NAME); + if (capURL.empty()) + { + LL_WARNS("debugMaterials") << "Capability '" << MATERIALS_CAPABILITY_NAME + << "' is not defined on region '" << regionp->getName() << "'" << LL_ENDL; + mGetQueue.erase(itRegionQueue); + continue; + } + + LLSD materialsData = LLSD::emptyArray(); + + material_queue_t& materials = itRegionQueue->second; + material_queue_t::iterator loopMaterial = materials.begin(); + while ( (materials.end() != loopMaterial) && (materialsData.size() <= MATERIALS_GET_MAX_ENTRIES) ) + { + material_queue_t::iterator itMaterial = loopMaterial++; + materialsData.append((*itMaterial).asLLSD()); + materials.erase(itMaterial); + mGetPending.insert(std::pair<pending_material_t, F64>(pending_material_t(region_id, *itMaterial), LLFrameTimer::getTotalSeconds())); + } + + std::string materialString = zip_llsd(materialsData); + + S32 materialSize = materialString.size(); + if (materialSize <= 0) + { + LL_ERRS("debugMaterials") << "cannot zip LLSD binary content" << LL_ENDL; + return; + } + + LLSD::Binary materialBinary; + materialBinary.resize(materialSize); + memcpy(materialBinary.data(), materialString.data(), materialSize); + + LLSD postData = LLSD::emptyMap(); + postData[MATERIALS_CAP_ZIP_FIELD] = materialBinary; + + LLHTTPClient::ResponderPtr materialsResponder = new LLMaterialsResponder("POST", capURL, boost::bind(&LLMaterialMgr::onGetResponse, this, _1, _2, region_id)); + LLHTTPClient::post(capURL, postData, materialsResponder); + } +} + +void LLMaterialMgr::processGetAllQueue() +{ + getall_queue_t::iterator loopRegion = mGetAllQueue.begin(); + while (mGetAllQueue.end() != loopRegion) + { + getall_queue_t::iterator itRegion = loopRegion++; + + const LLUUID& region_id = *itRegion; + LLViewerRegion* regionp = LLWorld::instance().getRegionFromID(region_id); + if (regionp == NULL) + { + LL_WARNS("debugMaterials") << "Unknown region with id " << region_id.asString() << LL_ENDL; + mGetAllQueue.erase(itRegion); + continue; + } + else if (!regionp->capabilitiesReceived()) + { + continue; + } + + std::string capURL = regionp->getCapability(MATERIALS_CAPABILITY_NAME); + if (capURL.empty()) + { + LL_WARNS("debugMaterials") << "Capability '" << MATERIALS_CAPABILITY_NAME + << "' is not defined on the current region '" << regionp->getName() << "'" << LL_ENDL; + mGetAllQueue.erase(itRegion); + continue; + } + + LLHTTPClient::ResponderPtr materialsResponder = new LLMaterialsResponder("GET", capURL, boost::bind(&LLMaterialMgr::onGetAllResponse, this, _1, _2, *itRegion)); + LLHTTPClient::get(capURL, materialsResponder); + mGetAllPending.insert(std::pair<LLUUID, F64>(region_id, LLFrameTimer::getTotalSeconds())); + mGetAllQueue.erase(itRegion); // Invalidates region_id + } +} + +void LLMaterialMgr::processPutQueue() +{ + put_queue_t::iterator loopQueue = mPutQueue.begin(); + while (mPutQueue.end() != loopQueue) + { + put_queue_t::iterator itQueue = loopQueue++; + + const LLUUID& object_id = itQueue->first; + const LLViewerObject* objectp = gObjectList.findObject(object_id); + if ( (!objectp) || (!objectp->getRegion()) ) + { + LL_WARNS("debugMaterials") << "Object or object region is NULL" << LL_ENDL; + + mPutQueue.erase(itQueue); + continue; + } + + const LLViewerRegion* regionp = objectp->getRegion(); + if (!regionp->capabilitiesReceived()) + { + continue; + } + + std::string capURL = regionp->getCapability(MATERIALS_CAPABILITY_NAME); + if (capURL.empty()) + { + LL_WARNS("debugMaterials") << "Capability '" << MATERIALS_CAPABILITY_NAME + << "' is not defined on region '" << regionp->getName() << "'" << LL_ENDL; + + mPutQueue.erase(itQueue); + continue; + } + + LLSD facesData = LLSD::emptyArray(); + for (facematerial_map_t::const_iterator itFace = itQueue->second.begin(); itFace != itQueue->second.end(); ++itFace) + { + LLSD faceData = LLSD::emptyMap(); + faceData[MATERIALS_CAP_FACE_FIELD] = static_cast<LLSD::Integer>(itFace->first); + faceData[MATERIALS_CAP_OBJECT_ID_FIELD] = static_cast<LLSD::Integer>(objectp->getLocalID()); + if (!itFace->second.isNull()) + { + faceData[MATERIALS_CAP_MATERIAL_FIELD] = itFace->second.asLLSD(); + } + facesData.append(faceData); + } + + LLSD materialsData = LLSD::emptyMap(); + materialsData[MATERIALS_CAP_FULL_PER_FACE_FIELD] = facesData; + + std::string materialString = zip_llsd(materialsData); + + S32 materialSize = materialString.size(); + if (materialSize <= 0) + { + LL_ERRS("debugMaterials") << "cannot zip LLSD binary content" << LL_ENDL; + + mPutQueue.erase(itQueue); + continue; + } + else + { + LLSD::Binary materialBinary; + materialBinary.resize(materialSize); + memcpy(materialBinary.data(), materialString.data(), materialSize); + + LLSD putData = LLSD::emptyMap(); + putData[MATERIALS_CAP_ZIP_FIELD] = materialBinary; + + LLHTTPClient::ResponderPtr materialsResponder = new LLMaterialsResponder("PUT", capURL, boost::bind(&LLMaterialMgr::onPutResponse, this, _1, _2)); + LLHTTPClient::put(capURL, putData, materialsResponder); + } + } +} + +void LLMaterialMgr::onRegionRemoved(LLViewerRegion* regionp) +{ + const LLUUID& region_id = regionp->getRegionID(); + + // Get + mGetQueue.erase(region_id); + for (get_pending_map_t::iterator itPending = mGetPending.begin(); itPending != mGetPending.end();) + { + if (region_id == itPending->first.first) + { + mGetPending.erase(itPending++); + } + else + { + ++itPending; + } + } + + // Get all + mGetAllQueue.erase(region_id); + mGetAllRequested.erase(region_id); + mGetAllPending.erase(region_id); + mGetAllCallbacks.erase(region_id); + + // Put +// mPutQueue.erase(region_id); +} |