summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
Diffstat (limited to 'indra')
-rw-r--r--indra/newview/CMakeLists.txt8
-rwxr-xr-xindra/newview/llviewerregion.cpp12
-rw-r--r--indra/newview/llvocache.cpp184
-rw-r--r--indra/newview/llvocache.h3
-rw-r--r--indra/newview/tests/lldir_stub.cpp30
-rw-r--r--indra/newview/tests/llvieweroctree_stub.cpp86
-rw-r--r--indra/newview/tests/llvocache_test.cpp146
7 files changed, 435 insertions, 34 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 1588f16f07..3271de72c9 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -2383,6 +2383,7 @@ if (LL_TESTS)
# llremoteparcelrequest.cpp
llviewerhelputil.cpp
llversioninfo.cpp
+ llvocache.cpp
llworldmap.cpp
llworldmipmap.cpp
)
@@ -2396,6 +2397,13 @@ if (LL_TESTS)
#llviewertexturelist.cpp
)
+ set_source_files_properties(
+ llvocache.cpp
+ PROPERTIES
+ LL_TEST_ADDITIONAL_SOURCE_FILES
+ ../llmessage/lldatapacker.cpp
+ )
+
set(test_libs
${LLCOMMON_LIBRARIES}
${JSONCPP_LIBRARIES}
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index e3ac1767fb..afe48f01b5 100755
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -785,7 +785,7 @@ void LLViewerRegion::loadObjectCache()
if(LLVOCache::instanceExists())
{
LLVOCache & vocache = LLVOCache::instance();
- vocache.readFromCache(mHandle, mImpl->mCacheID, mImpl->mCacheMap) ;
+ vocache.readFromCache(mHandle, mImpl->mCacheID, mImpl->mCacheMap);
vocache.readGenericExtrasFromCache(mHandle, mImpl->mCacheID, mImpl->mGLTFOverridesJson);
if (mImpl->mCacheMap.empty())
@@ -814,7 +814,8 @@ void LLViewerRegion::saveObjectCache()
bool removal_enabled = sVOCacheCullingEnabled && (mRegionTimer.getElapsedTimeF32() > start_time_threshold); //allow to remove invalid objects from object cache file.
LLVOCache & instance = LLVOCache::instance();
- instance.writeToCache(mHandle, mImpl->mCacheID, mImpl->mCacheMap, mCacheDirty, removal_enabled) ;
+
+ instance.writeToCache(mHandle, mImpl->mCacheID, mImpl->mCacheMap, mCacheDirty, removal_enabled);
instance.writeGenericExtrasToCache(mHandle, mImpl->mCacheID, mImpl->mGLTFOverridesJson, mCacheDirty, removal_enabled);
mCacheDirty = FALSE;
}
@@ -822,6 +823,7 @@ void LLViewerRegion::saveObjectCache()
// Map of LLVOCacheEntry takes time to release, store map for cleanup on idle
sRegionCacheCleanup.insert(mImpl->mCacheMap.begin(), mImpl->mCacheMap.end());
mImpl->mCacheMap.clear();
+ // TODO - probably need to do the same for overrides cache
}
void LLViewerRegion::sendMessage()
@@ -2646,6 +2648,8 @@ void LLViewerRegion::cacheFullUpdateGLTFOverride(const LLGLTFOverrideCacheEntry
LLViewerObject * obj = gObjectList.findObject(object_id);
if (obj != nullptr)
{
+ llassert(obj->getRegion() == this);
+
U32 local_id = obj->getLocalID();
mImpl->mGLTFOverridesJson[local_id] = override_data;
@@ -3547,4 +3551,8 @@ void LLViewerRegion::loadCacheMiscExtras(U32 local_id, LLVOCacheEntry * entry, U
{
LLGLTFMaterialList::loadCacheOverrides(iter->second);
}
+ else
+ {
+ LL_DEBUGS("GLTF") << "cache miss for handle: " << mHandle << " local_id:" << local_id << LL_ENDL;
+ }
}
diff --git a/indra/newview/llvocache.cpp b/indra/newview/llvocache.cpp
index cb75426cce..21a6a2950e 100644
--- a/indra/newview/llvocache.cpp
+++ b/indra/newview/llvocache.cpp
@@ -26,15 +26,13 @@
#include "llviewerprecompiledheaders.h"
#include "llvocache.h"
-#include "llerror.h"
#include "llregionhandle.h"
#include "llviewercontrol.h"
#include "llviewerobjectlist.h"
#include "lldrawable.h"
#include "llviewerregion.h"
-#include "pipeline.h"
#include "llagentcamera.h"
-#include "llmemory.h"
+#include "llsdserialize.h"
//static variables
U32 LLVOCacheEntry::sMinFrameRange = 0;
@@ -65,12 +63,12 @@ bool LLGLTFOverrideCacheEntry::fromLLSD(const LLSD& data)
return false;
}
- if (data.has("region_handle_low") && data.has("region_handle_high"))
+ if (data.has("region_handle_x") && data.has("region_handle_y"))
{
// TODO start requiring this once server sends this for all messages
- U64 region_handle_low = data["region_handle_low"].asInteger();
- U64 region_handle_high = data["region_handle_high"].asInteger();
- mRegionHandle = (region_handle_low & 0x00000000ffffffffUL) || (region_handle_high << 32);
+ U32 region_handle_y = data["region_handle_y"].asInteger();
+ U32 region_handle_x = data["region_handle_x"].asInteger();
+ mRegionHandle = to_region_handle(region_handle_x, region_handle_y);
mHasRegionHandle = true;
}
else
@@ -99,29 +97,31 @@ bool LLGLTFOverrideCacheEntry::fromLLSD(const LLSD& data)
mSides[side_idx] = gltf_json[i].asString();
}
}
+ else
+ {
+ LL_WARNS_IF(sides.size() != 0, "GLTF") << "broken override cache entry" << LL_ENDL;
+ }
}
return true;
}
-LLSD LLGLTFOverrideCacheEntry::toLLSD()
+LLSD LLGLTFOverrideCacheEntry::toLLSD() const
{
- llassert(false); // "Function not tested!!!
-
LLSD data;
if (mHasRegionHandle)
{
- data["region_handle_low"] = LLSD::Integer(mRegionHandle & 0x00000000ffffffffUL);
- data["region_handle_high"] = LLSD::Integer(mRegionHandle >> 32);
+ U32 region_handle_x, region_handle_y;
+ from_region_handle(mRegionHandle, &region_handle_x, &region_handle_y);
+ data["region_handle_y"] = LLSD::Integer(region_handle_y);
+ data["region_handle_x"] = LLSD::Integer(region_handle_x);
}
data["object_id"] = mObjectId;
- std::map<S32, std::string>::const_iterator iter = mSides.begin();
- std::map<S32, std::string>::const_iterator end = mSides.end();
- while (iter != end)
+ for (auto const & side : mSides)
{
- data["sides"].append(LLSD::Integer(iter->first));
- data["sides"].append(iter->second);
+ data["sides"].append(LLSD::Integer(side.first));
+ data["gltf_json"].append(side.second);
}
return data;
@@ -425,6 +425,7 @@ S32 LLVOCacheEntry::writeToBuffer(U8 *data_buffer) const
return ENTRY_HEADER_SIZE + size;
}
+#ifndef LL_TEST
//static
void LLVOCacheEntry::updateDebugSettings()
{
@@ -477,6 +478,7 @@ void LLVOCacheEntry::updateDebugSettings()
const U32 clamped_frames = inv_obj_time ? llclamp((U32) inv_obj_time, MIN_FRAMES, MAX_FRAMES) : MAX_FRAMES; // [10, 64], with zero => 64
sMinFrameRange = MIN_FRAMES + ((clamped_frames - MIN_FRAMES) * adjust_factor);
}
+#endif // LL_TEST
//static
F32 LLVOCacheEntry::getSquaredPixelThreshold(bool is_front)
@@ -948,6 +950,7 @@ void LLVOCachePartition::selectBackObjects(LLCamera &camera, F32 pixel_threshold
return;
}
+#ifndef LL_TEST
S32 LLVOCachePartition::cull(LLCamera &camera, bool do_occlusion)
{
static LLCachedControl<bool> use_object_cache_occlusion(gSavedSettings,"UseObjectCacheOcclusion");
@@ -1014,6 +1017,7 @@ S32 LLVOCachePartition::cull(LLCamera &camera, bool do_occlusion)
}
return 1;
}
+#endif // LL_TEST
void LLVOCachePartition::setCullHistory(BOOL has_new_object)
{
@@ -1087,8 +1091,9 @@ void LLVOCachePartition::removeOccluder(LLVOCacheGroup* group)
//-------------------------------------------------------------------
//LLVOCache
//-------------------------------------------------------------------
-// Format string used to construct filename for the object cache
+// Format strings used to construct filename for the object cache
static const char OBJECT_CACHE_FILENAME[] = "objects_%d_%d.slc";
+static const char OBJECT_CACHE_EXTRAS_FILENAME[] = "objects_%d_%d_extras.slec";
const U32 MAX_NUM_OBJECT_ENTRIES = 128 ;
const U32 MIN_ENTRIES_TO_PURGE = 16 ;
@@ -1101,9 +1106,12 @@ LLVOCache::LLVOCache(bool read_only) :
mInitialized(false),
mReadOnly(read_only),
mNumEntries(0),
- mCacheSize(1)
+ mCacheSize(1),
+ mEnabled(true)
{
+#ifndef LL_TEST
mEnabled = gSavedSettings.getBOOL("ObjectCacheEnabled");
+#endif
mLocalAPRFilePoolp = new LLVolatileAPRPool() ;
}
@@ -1280,6 +1288,15 @@ void LLVOCache::getObjectCacheFilename(U64 handle, std::string& filename)
return ;
}
+std::string LLVOCache::getObjectCacheExtrasFilename(U64 handle)
+{
+ U32 region_x, region_y;
+
+ grid_from_region_handle(handle, &region_x, &region_y);
+ return gDirUtilp->getExpandedFilename(LL_PATH_CACHE, object_cache_dirname,
+ llformat(OBJECT_CACHE_EXTRAS_FILENAME, region_x, region_y));
+}
+
void LLVOCache::removeFromCache(HeaderEntryInfo* entry)
{
if(mReadOnly)
@@ -1507,7 +1524,78 @@ void LLVOCache::readFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::voca
void LLVOCache::readGenericExtrasFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::vocache_gltf_overrides_map_t& cache_extras_entry_map)
{
- LL_DEBUGS() << "TODO" << LL_ENDL;
+ if(!mEnabled)
+ {
+ LL_WARNS() << "Not reading cache for handle " << handle << "): Cache is currently disabled." << LL_ENDL;
+ return ;
+ }
+ llassert_always(mInitialized);
+
+ handle_entry_map_t::iterator iter = mHandleEntryMap.find(handle) ;
+ if(iter == mHandleEntryMap.end()) //no cache
+ {
+ LL_WARNS() << "No handle map entry for " << handle << LL_ENDL;
+ return;
+ }
+
+ std::string filename(getObjectCacheExtrasFilename(handle));
+ llifstream in(filename, std::ios::in | std::ios::binary);
+
+ std::string line;
+ std::getline(in, line);
+ if(!in.good()) {
+ LL_WARNS() << "Failed reading extras cache for handle " << handle << LL_ENDL;
+ return;
+ }
+
+ if(!LLUUID::validate(line))
+ {
+ LL_WARNS() << "Failed reading extras cache for handle" << handle << ". invalid uuid line: '" << line << "'" << LL_ENDL;
+ return;
+ }
+
+ LLUUID cache_id(line);
+ if(cache_id != id)
+ {
+ LL_INFOS() << "Cache ID doesn't match for this region, discarding" << LL_ENDL;
+ return;
+ }
+
+ U32 num_entries; // if removal was enabled during write num_entries might be wrong
+ std::getline(in, line);
+ if(!in.good()) {
+ LL_WARNS() << "Failed reading extras cache for handle " << handle << LL_ENDL;
+ return;
+ }
+ try {
+ num_entries = std::stol(line);
+ }
+ catch(std::logic_error &excp) // either invalid_argument or out_of_range
+ {
+ LL_WARNS() << "Failed reading extras cache for handle " << handle << ". unreadable num_entries" << LL_ENDL;
+ return;
+ }
+
+ LL_DEBUGS("GLTF") << "Beginning reading extras cache for handle " << handle << ", " << num_entries << " entries" << LL_ENDL;
+
+ LLSD entry_llsd;
+ for (U32 i = 0; i < num_entries && !in.eof(); i++)
+ {
+ static const U32 max_size = 4096;
+ bool success = LLSDSerialize::deserialize(entry_llsd, in, max_size);
+ // check bool(in) this time since eof is not a failure condition here
+ if(!success || !in) {
+ LL_WARNS() << "Failed reading extras cache for handle " << handle << ", entry number " << i << LL_ENDL;
+ return;
+ }
+
+ LLGLTFOverrideCacheEntry entry;
+ entry.fromLLSD(entry_llsd);
+ U32 local_id = entry_llsd["local_id"].asInteger();
+ cache_extras_entry_map[local_id] = entry;
+ }
+
+ LL_DEBUGS("GLTF") << "Completed reading extras cache for handle " << handle << ", " << num_entries << " entries" << LL_ENDL;
}
void LLVOCache::purgeEntries(U32 size)
@@ -1520,6 +1608,7 @@ void LLVOCache::purgeEntries(U32 size)
mHeaderEntryQueue.erase(iter) ;
removeFromCache(entry) ;
delete entry;
+ // TODO also delete extras
}
mNumEntries = mHandleEntryMap.size() ;
}
@@ -1649,4 +1738,59 @@ void LLVOCache::writeToCache(U64 handle, const LLUUID& id, const LLVOCacheEntry:
void LLVOCache::writeGenericExtrasToCache(U64 handle, const LLUUID& id, const LLVOCacheEntry::vocache_gltf_overrides_map_t& cache_extras_entry_map, BOOL dirty_cache, bool removal_enabled)
{
+ if(!mEnabled)
+ {
+ LL_WARNS() << "Not writing extras cache for handle " << handle << "): Cache is currently disabled." << LL_ENDL;
+ return;
+ }
+ llassert_always(mInitialized);
+
+ if(mReadOnly)
+ {
+ LL_WARNS() << "Not writing extras cache for handle " << handle << "): Cache is currently in read-only mode." << LL_ENDL;
+ return;
+ }
+
+ std::string filename(getObjectCacheExtrasFilename(handle));
+ llofstream out(filename, std::ios::out | std::ios::binary);
+ if(!out.good())
+ {
+ LL_WARNS() << "Failed writing extras cache for handle " << handle << LL_ENDL;
+ return;
+ // TODO - clean up broken cache file
+ }
+
+ out << id << '\n';
+ if(!out.good())
+ {
+ LL_WARNS() << "Failed writing extras cache for handle " << handle << LL_ENDL;
+ return;
+ // TODO - clean up broken cache file
+ }
+
+ U32 num_entries = cache_extras_entry_map.size();
+ out << num_entries << '\n';
+ if(!out.good())
+ {
+ LL_WARNS() << "Failed writing extras cache for handle " << handle << LL_ENDL;
+ return;
+ // TODO - clean up broken cache file
+ }
+
+ for (auto const & entry : cache_extras_entry_map)
+ {
+ S32 local_id = entry.first;
+ LLSD entry_llsd = entry.second.toLLSD();
+ entry_llsd["local_id"] = local_id;
+ LLSDSerialize::serialize(entry_llsd, out, LLSDSerialize::LLSD_XML);
+ out << '\n';
+ if(!out.good())
+ {
+ LL_WARNS() << "Failed writing extras cache for handle " << handle << LL_ENDL;
+ return;
+ // TODO - clean up broken cache file
+ }
+ }
+
+ LL_DEBUGS("GLTF") << "Completed writing extras cache for handle " << handle << ", " << num_entries << " entries" << LL_ENDL;
}
diff --git a/indra/newview/llvocache.h b/indra/newview/llvocache.h
index f41e9301ca..dcc8d37c5c 100644
--- a/indra/newview/llvocache.h
+++ b/indra/newview/llvocache.h
@@ -43,7 +43,7 @@ class LLGLTFOverrideCacheEntry
{
public:
bool fromLLSD(const LLSD& data);
- LLSD toLLSD();
+ LLSD toLLSD() const;
LLUUID mObjectId;
std::map<S32, std::string> mSides; //json per side
@@ -301,6 +301,7 @@ private:
void setDirNames(ELLPath location);
// determine the cache filename for the region from the region handle
void getObjectCacheFilename(U64 handle, std::string& filename);
+ std::string getObjectCacheExtrasFilename(U64 handle);
void removeFromCache(HeaderEntryInfo* entry);
void readCacheHeader();
void writeCacheHeader();
diff --git a/indra/newview/tests/lldir_stub.cpp b/indra/newview/tests/lldir_stub.cpp
index 2bc6772d86..911e9334dd 100644
--- a/indra/newview/tests/lldir_stub.cpp
+++ b/indra/newview/tests/lldir_stub.cpp
@@ -30,25 +30,29 @@ LLDir::LLDir() {}
LLDir::~LLDir() {}
BOOL LLDir::deleteFilesInDir(const std::string &dirname, const std::string &mask) { return true; }
void LLDir::setChatLogsDir(const std::string &path) {}
-void LLDir::setPerAccountChatLogsDir(const std::string &first, const std::string &last) {}
-void LLDir::setLindenUserDir(const std::string &first, const std::string &last) {}
+void LLDir::setPerAccountChatLogsDir(const std::string &) {}
+void LLDir::updatePerAccountChatLogsDir() {}
+void LLDir::setLindenUserDir(const std::string &) {}
void LLDir::setSkinFolder(const std::string &skin_folder, const std::string& language) {}
bool LLDir::setCacheDir(const std::string &path) { return true; }
-void LLDir::dumpCurrentDirectories() {}
+void LLDir::dumpCurrentDirectories(LLError::ELevel) {}
+std::string LLDir::getSkinFolder() const { return ""; }
+std::string LLDir::getLanguage() const { return ""; }
+
class LLDir_stub : public LLDir
{
public:
- LLDir_stub() {}
- ~LLDir_stub() {}
+ LLDir_stub() = default;
+ ~LLDir_stub() = default;
+
+ void initAppDirs(const std::string &app_name, const std::string &) override {}
- /*virtual*/ void initAppDirs(const std::string &app_name) {}
+ std::string getCurPath() override { return "CUR_PATH_FROM_LLDIR"; }
+ bool fileExists(const std::string &filename) const override { return false; }
- /*virtual*/ std::string getCurPath() { return "CUR_PATH_FROM_LLDIR"; }
- /*virtual*/ U32 countFilesInDir(const std::string &dirname, const std::string &mask) { return 42; }
- /*virtual*/ BOOL getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap) { fname = fname + "_NEXT"; return false; }
- /*virtual*/ void getRandomFileInDir(const std::string &dirname, const std::string &mask, std::string &fname) { fname = "RANDOM_FILE"; }
- /*virtual*/ bool fileExists(const std::string &filename) const { return false; }
+ std::string getLLPluginLauncher() override { return ""; }
+ std::string getLLPluginFilename(std::string base_name) override { return ""; }
};
LLDir_stub gDirUtil;
@@ -60,3 +64,7 @@ std::string LLDir::getExpandedFilename(ELLPath loc, const std::string& subdir, c
return subdir + " --- " + filename + " --- expanded!";
}
+std::string LLDir::getExpandedFilename(ELLPath location, const std::string &filename) const
+{
+ return filename + " --- expanded!";
+}
diff --git a/indra/newview/tests/llvieweroctree_stub.cpp b/indra/newview/tests/llvieweroctree_stub.cpp
new file mode 100644
index 0000000000..ab180c49a3
--- /dev/null
+++ b/indra/newview/tests/llvieweroctree_stub.cpp
@@ -0,0 +1,86 @@
+/**
+ * @file llvieweroctree_stub.cpp
+ * @brief stub implementations to allow unit testing
+ *
+ * $LicenseInfo:firstyear=2023&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, 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$
+ */
+
+S32 AABBSphereIntersect(const LLVector4a& min, const LLVector4a& max, const LLVector3 &origin, const F32 &rad) { return 0; }
+
+void LLViewerOctreeCull::traverse(const LLOctreeNode<LLViewerOctreeEntry, LLPointer<LLViewerOctreeEntry> >* node) { }
+void LLViewerOctreeCull::visit(const LLOctreeNode<LLViewerOctreeEntry, LLPointer<LLViewerOctreeEntry> >* node) { }
+void LLViewerOctreeCull::preprocess(LLViewerOctreeGroup* group) {}
+bool LLViewerOctreeCull::earlyFail(LLViewerOctreeGroup* group) { return false; }
+bool LLViewerOctreeCull::checkProjectionArea(const LLVector4a& center, const LLVector4a& size, const LLVector3& shift, F32 pixel_threshold, F32 near_radius) { return false; }
+bool LLViewerOctreeCull::checkObjects(const OctreeNode* branch, const LLViewerOctreeGroup* group) { return false; }
+void LLViewerOctreeCull::processGroup(LLViewerOctreeGroup* group) {}
+
+
+bool LLViewerOctreeGroup::boundObjects(BOOL empty, LLVector4a& minOut, LLVector4a& maxOut) { return false; }
+void LLViewerOctreeGroup::unbound() {}
+void LLViewerOctreeGroup::rebound() {}
+void LLViewerOctreeGroup::handleInsertion(const TreeNode* node, LLViewerOctreeEntry* obj) {}
+void LLViewerOctreeGroup::handleRemoval(const TreeNode* node, LLViewerOctreeEntry* obj) {}
+void LLViewerOctreeGroup::handleDestruction(const TreeNode* node) {}
+void LLViewerOctreeGroup::handleStateChange(const TreeNode* node) {}
+void LLViewerOctreeGroup::handleChildAddition(const OctreeNode* parent, OctreeNode* child) {}
+void LLViewerOctreeGroup::handleChildRemoval(const OctreeNode* parent, const OctreeNode* child) {}
+
+LLOcclusionCullingGroup::LLOcclusionCullingGroup(OctreeNode* node, LLViewerOctreePartition* part) :
+ LLViewerOctreeGroup(node),
+ mSpatialPartition(part)
+{
+}
+LLOcclusionCullingGroup::~LLOcclusionCullingGroup() = default;
+void LLOcclusionCullingGroup::doOcclusion(LLCamera* camera, const LLVector4a* shift) {}
+void LLOcclusionCullingGroup::setOcclusionState(U32 state, S32 mode) {}
+void LLOcclusionCullingGroup::clearOcclusionState(U32 state, S32 mode) {}
+void LLOcclusionCullingGroup::handleChildAddition(const OctreeNode *parent, OctreeNode *child) {}
+BOOL LLOcclusionCullingGroup::isRecentlyVisible() const { return FALSE; }
+BOOL LLOcclusionCullingGroup::isAnyRecentlyVisible() const { return FALSE; }
+
+
+LLViewerOctreeGroup::LLViewerOctreeGroup(OctreeNode* node) : mOctreeNode(node) {}
+LLViewerOctreeGroup::~LLViewerOctreeGroup() = default;
+
+LLViewerOctreeEntryData::LLViewerOctreeEntryData(LLViewerOctreeEntry::eEntryDataType_t) {}
+LLViewerOctreeEntryData::~LLViewerOctreeEntryData() = default;
+bool LLViewerOctreeEntryData::isVisible() const { return false; }
+bool LLViewerOctreeEntryData::isRecentlyVisible() const { return false; }
+void LLViewerOctreeEntryData::setVisible() const {}
+void LLViewerOctreeEntryData::resetVisible() const {}
+const LLVector4a& LLViewerOctreeEntryData::getPositionGroup() const { return LLVector4a::getZero(); }
+const LLVector4a* LLViewerOctreeEntryData::getSpatialExtents() const { return nullptr; }
+LLViewerOctreeGroup* LLViewerOctreeEntryData::getGroup() const { return nullptr; }
+void LLViewerOctreeEntryData::setSpatialExtents(const LLVector4a& min, const LLVector4a& max) {}
+void LLViewerOctreeEntryData::setPositionGroup(const LLVector4a& pos) {}
+void LLViewerOctreeEntryData::setGroup(LLViewerOctreeGroup* group) {}
+void LLViewerOctreeEntryData::setOctreeEntry(LLViewerOctreeEntry* entry) {}
+U32 LLViewerOctreeEntryData::sCurVisible{};
+
+LLViewerOctreePartition::LLViewerOctreePartition() = default;
+LLViewerOctreePartition::~LLViewerOctreePartition() = default;
+void LLViewerOctreePartition::cleanup() {}
+
+BOOL LLViewerOctreeGroup::isRecentlyVisible() const { return FALSE; }
+
+
diff --git a/indra/newview/tests/llvocache_test.cpp b/indra/newview/tests/llvocache_test.cpp
new file mode 100644
index 0000000000..ea6abe254e
--- /dev/null
+++ b/indra/newview/tests/llvocache_test.cpp
@@ -0,0 +1,146 @@
+/**
+ * @file llvocache_.cpp
+ * @author Brad
+ * @date 2023-03-01
+ * @brief Test Viewer Object Cache functionality
+ *
+ * $LicenseInfo:firstyear=2023&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2014, 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 "../test/lltut.h"
+
+#include "llvocache.h"
+
+#include "lldir.h"
+#include "llhudobject.h"
+#include "llregionhandle.h"
+#include "llsdutil.h"
+#include "llsdserialize.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+
+#include "lldir_stub.cpp"
+#include "llvieweroctree_stub.cpp"
+
+namespace
+{
+
+}
+
+
+//----------------------------------------------------------------------------
+// Mock objects for the dependencies of the code we're testing
+S32 LLVOCachePartition::cull(LLCamera &camera, bool do_occlusion) { return 0; }
+
+LLViewerObjectList::LLViewerObjectList() = default;
+LLViewerObjectList::~LLViewerObjectList() = default;
+LLDebugBeacon::~LLDebugBeacon() = default;
+LLViewerObjectList gObjectList{};
+LLViewerCamera::eCameraID LLViewerCamera::sCurCameraID{};
+void LLViewerObject::unpackUUID(LLDataPackerBinaryBuffer *dp, LLUUID &value, std::string name) {}
+
+bool LLViewerRegion::addVisibleGroup(LLViewerOctreeGroup*) { return false; }
+U32 LLViewerRegion::getNumOfVisibleGroups() const { return 0; }
+LLVector3 LLViewerRegion::getOriginAgent() const { return LLVector3::zero; }
+S32 LLViewerRegion::sLastCameraUpdated{};
+
+// -------------------------------------------------------------------------------------------
+// TUT
+// -------------------------------------------------------------------------------------------
+namespace tut
+{
+ // Test wrapper declaration
+ struct vocacheTest
+ {
+ vocacheTest() = default;
+ ~vocacheTest() = default;
+
+ static const std::string override_llsd_text[];
+ };
+
+ const std::string vocacheTest::override_llsd_text[]{
+ // sample override contents captured from traffic
+ R"(<? llsd/notation ?>
+ {'gltf_json':['{"asset":{"version":"2.0"},"images":[{"uri":"00000000-0000-0000-0000-000000000000"},{"uri":"00000000-0000-0000-0000-000000000000"},{"uri":"00000000-0000-0000-0000-000000000000"},{"uri":"00000000-0000-0000-0000-000000000000"},{"uri":"00000000-0000-0000-0000-000000000000"}],"materials":[{"emissiveTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":3},"normalTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":1},"occlusionTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":4},"pbrMetallicRoughness":{"baseColorTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":0},"metallicRoughnessTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":2}}}],"textures":[{"source":0},{"source":1},{"source":2},{"source":3},{"source":4}]}\n','{"asset":{"version":"2.0"},"images":[{"uri":"00000000-0000-0000-0000-000000000000"},{"uri":"00000000-0000-0000-0000-000000000000"},{"uri":"00000000-0000-0000-0000-000000000000"},{"uri":"00000000-0000-0000-0000-000000000000"},{"uri":"00000000-0000-0000-0000-000000000000"}],"materials":[{"emissiveTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":3},"normalTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":1},"occlusionTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":4},"pbrMetallicRoughness":{"baseColorTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":0},"metallicRoughnessTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":2}}}],"textures":[{"source":0},{"source":1},{"source":2},{"source":3},{"source":4}]}\n','{"asset":{"version":"2.0"},"images":[{"uri":"00000000-0000-0000-0000-000000000000"},{"uri":"00000000-0000-0000-0000-000000000000"},{"uri":"00000000-0000-0000-0000-000000000000"},{"uri":"00000000-0000-0000-0000-000000000000"},{"uri":"00000000-0000-0000-0000-000000000000"}],"materials":[{"emissiveTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":3},"normalTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":1},"occlusionTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":4},"pbrMetallicRoughness":{"baseColorTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":0},"metallicRoughnessTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":2}}}],"textures":[{"source":0},{"source":1},{"source":2},{"source":3},{"source":4}]}\n','{"asset":{"version":"2.0"},"images":[{"uri":"00000000-0000-0000-0000-000000000000"},{"uri":"00000000-0000-0000-0000-000000000000"},{"uri":"00000000-0000-0000-0000-000000000000"},{"uri":"00000000-0000-0000-0000-000000000000"},{"uri":"00000000-0000-0000-0000-000000000000"}],"materials":[{"emissiveTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":3},"normalTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":1},"occlusionTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":4},"pbrMetallicRoughness":{"baseColorTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":0},"metallicRoughnessTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":2}}}],"textures":[{"source":0},{"source":1},{"source":2},{"source":3},{"source":4}]}\n','{"asset":{"version":"2.0"},"images":[{"uri":"00000000-0000-0000-0000-000000000000"},{"uri":"00000000-0000-0000-0000-000000000000"},{"uri":"00000000-0000-0000-0000-000000000000"},{"uri":"00000000-0000-0000-0000-000000000000"},{"uri":"00000000-0000-0000-0000-000000000000"}],"materials":[{"emissiveTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":3},"normalTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":1},"occlusionTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":4},"pbrMetallicRoughness":{"baseColorTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":0},"metallicRoughnessTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":2}}}],"textures":[{"source":0},{"source":1},{"source":2},{"source":3},{"source":4}]}\n','{"asset":{"version":"2.0"},"images":[{"uri":"00000000-0000-0000-0000-000000000000"},{"uri":"00000000-0000-0000-0000-000000000000"},{"uri":"00000000-0000-0000-0000-000000000000"},{"uri":"00000000-0000-0000-0000-000000000000"},{"uri":"00000000-0000-0000-0000-000000000000"}],"materials":[{"emissiveTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":3},"normalTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":1},"occlusionTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":4},"pbrMetallicRoughness":{"baseColorTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":0},"metallicRoughnessTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":2}}}],"textures":[{"source":0},{"source":1},{"source":2},{"source":3},{"source":4}]}\n','{"asset":{"version":"2.0"},"images":[{"uri":"00000000-0000-0000-0000-000000000000"},{"uri":"00000000-0000-0000-0000-000000000000"},{"uri":"00000000-0000-0000-0000-000000000000"},{"uri":"00000000-0000-0000-0000-000000000000"},{"uri":"00000000-0000-0000-0000-000000000000"}],"materials":[{"emissiveTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":3},"normalTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":1},"occlusionTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":4},"pbrMetallicRoughness":{"baseColorTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":0},"metallicRoughnessTexture":{"extensions":{"KHR_texture_transform":{"offset":[0.0,0.0],"rotation":0.0,"scale":[2.0,2.0]}},"index":2}}}],"textures":[{"source":0},{"source":1},{"source":2},{"source":3},{"source":4}]}\n'],'object_id':u8b357110-4845-1d40-1cf0-cc4a2c22a4f1,'sides':[i0,i1,i2,i3,i4,i5,i6]})"
+ };
+
+ // Tut templating thingamagic: test group, object and test instance
+ struct vocacheTestFactory : public test_group<vocacheTest>
+ {
+ vocacheTestFactory()
+ : test_group<vocacheTest>("LLVOCache")
+ {
+ ll_init_apr();
+
+ const bool READ_ONLY = false;
+ const U32 INDRA_OBJECT_CACHE_VERSION = 15; // see LLAppViewer::getObjectCacheVersion()
+ const U32 CACHE_NUMBER_OF_REGIONS = 128; // see setting CacheNumberOfRegionsForObjects
+
+ LLVOCache &instance = LLVOCache::initParamSingleton(READ_ONLY);
+ instance.initCache(LL_PATH_CACHE, CACHE_NUMBER_OF_REGIONS, INDRA_OBJECT_CACHE_VERSION);
+ }
+
+ ~vocacheTestFactory()
+ {
+ LLVOCache::deleteSingleton();
+ ll_cleanup_apr();
+ }
+
+ };
+
+ typedef vocacheTestFactory::object vocacheTestObject;
+ tut::vocacheTestFactory tut_test;
+
+ // ---------------------------------------------------------------------------------------
+ // Test functions
+ // ---------------------------------------------------------------------------------------
+
+ template<> template<>
+ void vocacheTestObject::test<1>()
+ {
+ LLGLTFOverrideCacheEntry entry{};
+ LLSD entry_llsd;
+
+ std::istringstream in_llsd(override_llsd_text[0]);
+ bool success = LLSDSerialize::deserialize(entry_llsd, in_llsd, override_llsd_text[0].length());
+
+ ensure("llsd deserialize succeeds", success);
+
+ entry.fromLLSD(entry_llsd);
+ ensure_equals("entry object_id match", entry.mObjectId, LLUUID("8b357110-4845-1d40-1cf0-cc4a2c22a4f1"));
+ ensure_equals("sides count", entry.mSides.size(), 7);
+
+ //std::cout << other << std::endl;
+ ensure_equals("toLLSD() match", entry.toLLSD(), entry_llsd);
+ }
+
+ template<> template<>
+ void vocacheTestObject::test<2>()
+ {
+ LLVOCacheEntry::vocache_gltf_overrides_map_t extras;
+
+ U64 region_handle = to_region_handle(140, 81);
+ LLUUID region_id = LLUUID::generateNewID();
+
+ LLVOCache::instance().readGenericExtrasFromCache(region_handle, region_id, extras);
+ }
+}