Files
Mujoco_WASM/src/user/user_cache.h
T
Sam Haves 922aebbeed Provide explicit argument for asset cache ID.
This will allow assets to cache compiled results with different parameters. For example, we could cache both an RGB version of a texture and a Mono version.

PiperOrigin-RevId: 811341377
Change-Id: Ifd6a4ff88661a2b38be47c02d6a666de2c202885
2025-09-25 07:24:12 -07:00

186 lines
6.2 KiB
C++

// Copyright 2024 DeepMind Technologies Limited
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef MUJOCO_SRC_USER_CACHE_H_
#define MUJOCO_SRC_USER_CACHE_H_
#include <cstddef>
#include <cstdlib>
#include <functional>
#include <set>
#include <string>
#include <memory>
#include <mutex>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <mujoco/mjplugin.h>
typedef std::function<bool(const void*)> mjCDataFunc;
typedef void (*mjCDeallocFunc)(const void*);
// A class container for a thread-safe asset cache
//
// Each mjCAsset is used to store raw and/or processed data loaded from a
// resource and is defined by a unique ID (usually the full filename of the
// asset).
class mjCAsset {
friend class mjCCache;
public:
mjCAsset(std::string modelname, std::string id, const mjResource* resource,
std::shared_ptr<const void> data, std::size_t size) :
id_(id), timestamp_(resource->timestamp),
size_(size), data_(std::move(data)) {
AddReference(modelname);
}
// move and copy constructors
mjCAsset(mjCAsset&& other) = default;
mjCAsset& operator=(mjCAsset&& other) = default;
mjCAsset(const mjCAsset& other) = default;
mjCAsset& operator=(const mjCAsset& other) = default;
const std::string& Timestamp() const { return timestamp_; }
const std::string& Id() const { return id_; }
std::size_t InsertNum() const { return insert_num_; }
std::size_t AccessCount() const { return access_count_; }
// pass data in the cache to the given function, return true if data was copied
bool PopulateData(mjCDataFunc fn) const {
return fn(data_.get());
}
private:
mjCAsset() = default;
// helpers for managing models referencing this asset
void AddReference(std::string xml_file) { references_.insert(xml_file); }
void RemoveReference(const std::string& xml_file) {
references_.erase(xml_file);
}
void ReplaceData(const mjCAsset& other) {
data_ = other.data_;
size_ = other.size_;
}
bool HasReferences() const { return !references_.empty(); }
void IncrementAccess() { access_count_++; }
// makes a copy for user (strip unnecessary references)
static mjCAsset Copy(const mjCAsset& other);
// setters
void SetInsertNum(std::size_t num) { insert_num_ = num; }
void SetTimestamp(std::string timestamp) { timestamp_ = timestamp; }
// accessors
std::size_t BytesCount() const { return size_; }
const void* Data() const {
return data_.get();
}
const std::set<std::string>& References() const { return references_; }
std::string id_; // unique id associated with asset
std::string timestamp_; // opaque timestamp of asset
std::size_t insert_num_; // number when asset was inserted
std::size_t access_count_ = 0; // incremented when getting 0th block
std::size_t size_ = 0; // how many bytes taken up by the asset
std::shared_ptr<const void> data_; // actual data of the asset
// list of models referencing this asset
std::set<std::string> references_;
};
struct mjCAssetCompare {
bool operator()(const mjCAsset* e1, const mjCAsset* e2) const {
if (e1->AccessCount() != e2->AccessCount()) {
return e1->AccessCount() < e2->AccessCount();
}
return e1->InsertNum() < e2->InsertNum();
}
};
// the class container for a thread-safe asset cache
class mjCCache {
public:
explicit mjCCache(std::size_t size) : capacity_(size) {}
// move only
mjCCache(mjCCache&& other) = delete;
mjCCache& operator=(mjCCache&& other) = delete;
mjCCache(const mjCCache& other) = delete;
mjCCache& operator=(const mjCCache& other) = delete;
// sets the capacity of the cache in bytes
// low-priority cached assets will be dropped to make the new memory
// requirement
void SetCapacity(std::size_t size);
// returns the corresponding timestamp, if the given asset is stored in
// the cache
const std::string* HasAsset(const std::string& id);
// inserts an asset into the cache, if asset is already in the cache, its data
// is updated only if the timestamps disagree
bool Insert(const std::string& modelname, const std::string& id, const mjResource *resource,
std::shared_ptr<const void> data, std::size_t size);
// populate data from the cache into the given function
bool PopulateData(const std::string& id, const mjResource* resource, mjCDataFunc fn);
// deletes the asset from the cache with the given id
void DeleteAsset(const std::string& id);
// removes model from the cache, assets only referenced by the model will be
// deleted
void RemoveModel(const std::string& filename);
// Wipes out all assets from the cache for the given model
void Reset(const std::string& filename);
// Wipes out all internal data
void Reset();
// accessors
std::size_t Capacity() const;
std::size_t Size() const;
private:
void Delete(mjCAsset* asset);
void Delete(mjCAsset* asset, const std::string& skip);
void Trim();
// TODO(kylebayes): We should consider a shared mutex like in
// engine/engine_plugin.cc as some of these methods don't need to be fully
// locked.
mutable std::mutex mutex_;
std::size_t insert_num_ = 0; // a running counter of assets being inserted
std::size_t size_ = 0; // current size of the cache in bytes
std::size_t capacity_ = 0; // capacity of the cache in bytes
// internal constant look up table for assets
std::unordered_map<std::string, mjCAsset> lookup_;
// internal priority queue for the cache
std::set<mjCAsset*, mjCAssetCompare> entries_;
// models using the cache along with the assets they reference
std::unordered_map<std::string, std::unordered_set<mjCAsset*>> models_;
};
#endif // MUJOCO_SRC_USER_CACHE_H_