Enable multithreading for texture compilation, use new user ThreadPool for compilation.
PiperOrigin-RevId: 864449279 Change-Id: I5dd053ab0b1f14d81e1f68296eadc59cd5b8d698
This commit is contained in:
committed by
Copybara-Service
parent
f0d5ed73b2
commit
49b4790877
+127
-78
@@ -15,9 +15,9 @@
|
|||||||
#include "user/user_model.h"
|
#include "user/user_model.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <array>
|
|
||||||
#include <csetjmp>
|
#include <csetjmp>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
@@ -32,16 +32,17 @@
|
|||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <unordered_set>
|
|
||||||
|
|
||||||
#include <mujoco/mjdata.h>
|
#include <mujoco/mjdata.h>
|
||||||
#include <mujoco/mjmacro.h>
|
#include <mujoco/mjmacro.h>
|
||||||
#include <mujoco/mjmodel.h>
|
#include <mujoco/mjmodel.h>
|
||||||
|
#include <mujoco/mjplugin.h>
|
||||||
#include <mujoco/mjspec.h>
|
#include <mujoco/mjspec.h>
|
||||||
#include <mujoco/mjtnum.h>
|
#include <mujoco/mjtnum.h>
|
||||||
#include <mujoco/mjplugin.h>
|
#include <mujoco/mujoco.h>
|
||||||
#include "cc/array_safety.h"
|
#include "cc/array_safety.h"
|
||||||
#include "engine/engine_forward.h"
|
#include "engine/engine_forward.h"
|
||||||
#include "engine/engine_io.h"
|
#include "engine/engine_io.h"
|
||||||
@@ -53,15 +54,13 @@
|
|||||||
#include "engine/engine_util_misc.h"
|
#include "engine/engine_util_misc.h"
|
||||||
#include "user/user_api.h"
|
#include "user/user_api.h"
|
||||||
#include "user/user_objects.h"
|
#include "user/user_objects.h"
|
||||||
|
#include "user/user_threadpool.h"
|
||||||
#include "user/user_util.h"
|
#include "user/user_util.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
namespace mju = ::mujoco::util;
|
namespace mju = ::mujoco::util;
|
||||||
using std::string;
|
using std::string;
|
||||||
using std::vector;
|
using std::vector;
|
||||||
constexpr int kMaxCompilerThreads = 16;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---------------------------------- LOCAL UTILITY FUNCTIONS ---------------------------------------
|
//---------------------------------- LOCAL UTILITY FUNCTIONS ---------------------------------------
|
||||||
|
|
||||||
@@ -76,6 +75,18 @@ bool IsSameVec(const T pos1[3], const T pos2[3]) {
|
|||||||
std::abs(pos1[2] - pos2[2]) < kFrameEps;
|
std::abs(pos1[2] - pos2[2]) < kFrameEps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned int NumCompilerThreads(int upper_bound = -1) {
|
||||||
|
// Use at most half the available threads to avoid
|
||||||
|
// overloading hyperthreaded CPUs.
|
||||||
|
// Compilation is largely compute-bound so we want to give each
|
||||||
|
// physical core a chance without too much L1/L2 cache thrashing.
|
||||||
|
unsigned int nthreads = std::thread::hardware_concurrency() / 2;
|
||||||
|
if (upper_bound > 0) {
|
||||||
|
nthreads = std::min(nthreads, static_cast<unsigned int>(upper_bound));
|
||||||
|
}
|
||||||
|
return std::max(static_cast<unsigned int>(1), nthreads);
|
||||||
|
}
|
||||||
|
|
||||||
// return true if two quaternions are element-wise less than kFrameEps apart, including double-cover
|
// return true if two quaternions are element-wise less than kFrameEps apart, including double-cover
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool IsSameQuat(const T quat1[4], const T quat2[4]) {
|
bool IsSameQuat(const T quat1[4], const T quat2[4]) {
|
||||||
@@ -2389,10 +2400,6 @@ void mjCModel::LengthRange(mjModel* m, mjData* data) {
|
|||||||
m->opt.timestep = compiler.LRopt.timestep;
|
m->opt.timestep = compiler.LRopt.timestep;
|
||||||
}
|
}
|
||||||
|
|
||||||
// number of threads available
|
|
||||||
int hardware_threads = std::thread::hardware_concurrency();
|
|
||||||
const int nthread = mjMAX(1, mjMIN(kMaxCompilerThreads, hardware_threads/2));
|
|
||||||
|
|
||||||
// count actuators that need computation
|
// count actuators that need computation
|
||||||
int cnt = 0;
|
int cnt = 0;
|
||||||
for (int i=0; i < m->nu; i++) {
|
for (int i=0; i < m->nu; i++) {
|
||||||
@@ -2417,6 +2424,8 @@ void mjCModel::LengthRange(mjModel* m, mjData* data) {
|
|||||||
cnt++;
|
cnt++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto nthread = NumCompilerThreads(cnt);
|
||||||
|
|
||||||
// single thread
|
// single thread
|
||||||
if (!compiler.usethread || cnt < 2 || nthread < 2) {
|
if (!compiler.usethread || cnt < 2 || nthread < 2) {
|
||||||
char err[200];
|
char err[200];
|
||||||
@@ -2430,8 +2439,11 @@ void mjCModel::LengthRange(mjModel* m, mjData* data) {
|
|||||||
// multiple threads
|
// multiple threads
|
||||||
else {
|
else {
|
||||||
// allocate mjData for each thread
|
// allocate mjData for each thread
|
||||||
char err[kMaxCompilerThreads][200];
|
// using vectors to allow arbitrary number of threads without stack overflow
|
||||||
mjData* pdata[kMaxCompilerThreads] = {data};
|
std::vector<std::vector<char>> err(nthread, std::vector<char>(200));
|
||||||
|
std::vector<mjData*> pdata(nthread);
|
||||||
|
pdata[0] = data;
|
||||||
|
|
||||||
for (int i=1; i < nthread; i++) {
|
for (int i=1; i < nthread; i++) {
|
||||||
pdata[i] = mj_makeData(m);
|
pdata[i] = mj_makeData(m);
|
||||||
}
|
}
|
||||||
@@ -2442,18 +2454,20 @@ void mjCModel::LengthRange(mjModel* m, mjData* data) {
|
|||||||
num++;
|
num++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepare thread function arguments, clear errors
|
// prepare thread function arguments
|
||||||
LRThreadArg arg[kMaxCompilerThreads];
|
std::vector<LRThreadArg> arg(nthread);
|
||||||
for (int i=0; i < nthread; i++) {
|
for (int i=0; i < nthread; i++) {
|
||||||
LRThreadArg temp = {m, pdata[i], i*num, num, &compiler.LRopt, err[i], 200};
|
LRThreadArg temp = {
|
||||||
|
m, pdata[i], i * num, num, &compiler.LRopt, err[i].data(), 200};
|
||||||
arg[i] = temp;
|
arg[i] = temp;
|
||||||
err[i][0] = 0;
|
err[i][0] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// launch threads
|
// launch threads
|
||||||
std::thread th[kMaxCompilerThreads];
|
std::vector<std::thread> th;
|
||||||
|
th.reserve(nthread);
|
||||||
for (int i=0; i < nthread; i++) {
|
for (int i=0; i < nthread; i++) {
|
||||||
th[i] = std::thread(LRfunc, arg+i);
|
th.emplace_back(LRfunc, &arg[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait for threads to finish
|
// wait for threads to finish
|
||||||
@@ -2469,7 +2483,7 @@ void mjCModel::LengthRange(mjModel* m, mjData* data) {
|
|||||||
// report first error
|
// report first error
|
||||||
for (int i=0; i < nthread; i++) {
|
for (int i=0; i < nthread; i++) {
|
||||||
if (err[i][0]) {
|
if (err[i][0]) {
|
||||||
throw mjCError(0, "%s", err[i]);
|
throw mjCError(0, "%s", err[i].data());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4485,7 +4499,9 @@ mjModel* mjCModel::Compile(const mjVFS* vfs, mjModel** m) {
|
|||||||
}
|
}
|
||||||
throw mjCError(0, "engine error: %s", error_msg.c_str());
|
throw mjCError(0, "engine error: %s", error_msg.c_str());
|
||||||
}
|
}
|
||||||
TryCompile(*const_cast<mjModel**>(&model), *const_cast<mjData**>(&data), vfs);
|
|
||||||
|
TryCompile(*const_cast<mjModel**>(&model), *const_cast<mjData**>(&data),
|
||||||
|
vfs);
|
||||||
} catch (mjCError err) {
|
} catch (mjCError err) {
|
||||||
// deallocate everything allocated in Compile
|
// deallocate everything allocated in Compile
|
||||||
mj_deleteModel(model);
|
mj_deleteModel(model);
|
||||||
@@ -4515,16 +4531,14 @@ mjModel* mjCModel::Compile(const mjVFS* vfs, mjModel** m) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Helper function for mesh compilation used by both serial and parallel paths
|
||||||
// mesh compilation function to be used in threads
|
static void CompileMesh(mjCMesh* mesh, const mjVFS* vfs,
|
||||||
void CompileMesh(mjCMesh* mesh, const mjVFS* vfs, std::exception_ptr& exception,
|
std::exception_ptr& exception, std::mutex& exception_mutex,
|
||||||
std::mutex& exception_mutex, std::string* warningtext) {
|
std::string* warningtext) {
|
||||||
// set warning text buffer to this local thread
|
|
||||||
local_warningtext_ptr = warningtext;
|
local_warningtext_ptr = warningtext;
|
||||||
auto previous_handler = _mjPRIVATE__get_tls_warning_fn();
|
auto previous_handler = _mjPRIVATE__get_tls_warning_fn();
|
||||||
_mjPRIVATE__set_tls_warning_fn(warninghandler);
|
_mjPRIVATE__set_tls_warning_fn(warninghandler);
|
||||||
|
|
||||||
// compile the mesh, catch exception to be rethrown later
|
|
||||||
try {
|
try {
|
||||||
mesh->Compile(vfs);
|
mesh->Compile(vfs);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
@@ -4534,47 +4548,84 @@ void CompileMesh(mjCMesh* mesh, const mjVFS* vfs, std::exception_ptr& exception,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// restore warning handler to top-level
|
|
||||||
_mjPRIVATE__set_tls_warning_fn(previous_handler);
|
_mjPRIVATE__set_tls_warning_fn(previous_handler);
|
||||||
local_warningtext_ptr = nullptr;
|
local_warningtext_ptr = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function for texture compilation used by both serial and parallel paths
|
||||||
|
static void CompileTexture(mjCTexture* texture, const mjVFS* vfs,
|
||||||
|
std::exception_ptr& exception,
|
||||||
|
std::mutex& exception_mutex, std::string* warningtext) {
|
||||||
|
local_warningtext_ptr = warningtext;
|
||||||
|
auto previous_handler = _mjPRIVATE__get_tls_warning_fn();
|
||||||
|
_mjPRIVATE__set_tls_warning_fn(warninghandler);
|
||||||
|
|
||||||
|
try {
|
||||||
// multi-threaded mesh compilation
|
texture->Compile(vfs);
|
||||||
void mjCModel::CompileMeshes(const mjVFS* vfs) {
|
} catch (...) {
|
||||||
std::vector<std::thread> threads;
|
std::lock_guard<std::mutex> lock(exception_mutex);
|
||||||
int nmesh = meshes_.size();
|
if (!exception) {
|
||||||
int hardware_threads = std::thread::hardware_concurrency() / 2;
|
exception = std::current_exception();
|
||||||
int maxthread = std::min(nmesh, std::min(kMaxCompilerThreads, hardware_threads));
|
|
||||||
int nthread = std::max(1, maxthread);
|
|
||||||
|
|
||||||
threads.reserve(nthread);
|
|
||||||
|
|
||||||
// holds an exception thrown by a worker thread
|
|
||||||
std::exception_ptr exception;
|
|
||||||
std::mutex except_mutex;
|
|
||||||
|
|
||||||
std::atomic_int next_mesh = 0;
|
|
||||||
std::vector<std::string> mesh_warningtext(nmesh);
|
|
||||||
for (int i = 0; i < nthread; ++i) {
|
|
||||||
threads.emplace_back([&] {
|
|
||||||
for (int meshid = next_mesh++; meshid < nmesh; meshid = next_mesh++) {
|
|
||||||
auto& mesh = meshes_[meshid];
|
|
||||||
CompileMesh(mesh, vfs, exception, except_mutex,
|
|
||||||
&mesh_warningtext[meshid]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// join threads
|
|
||||||
for (auto& thread : threads) {
|
|
||||||
if (thread.joinable()) {
|
|
||||||
thread.join();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// concatenate all warnings from threads, copy into warningtext
|
_mjPRIVATE__set_tls_warning_fn(previous_handler);
|
||||||
|
local_warningtext_ptr = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// multi-threaded mesh and texture compilation with shared threadpool
|
||||||
|
void mjCModel::CompileMeshesAndTextures(const mjVFS* vfs) {
|
||||||
|
int nmesh = meshes_.size();
|
||||||
|
int ntexture = textures_.size();
|
||||||
|
int total_tasks = nmesh + ntexture;
|
||||||
|
|
||||||
|
// holds exceptions thrown by worker threads
|
||||||
|
std::exception_ptr mesh_exception;
|
||||||
|
std::mutex mesh_except_mutex;
|
||||||
|
std::exception_ptr texture_exception;
|
||||||
|
std::mutex texture_except_mutex;
|
||||||
|
|
||||||
|
std::vector<std::string> mesh_warningtext(nmesh);
|
||||||
|
std::vector<std::string> texture_warningtext(ntexture);
|
||||||
|
|
||||||
|
// If no pool provided or too few total tasks, run serially
|
||||||
|
if (!compiler.usethread || total_tasks < 2) {
|
||||||
|
// Compile meshes serially
|
||||||
|
for (int i = 0; i < nmesh; i++) {
|
||||||
|
CompileMesh(meshes_[i], vfs, mesh_exception, mesh_except_mutex,
|
||||||
|
&mesh_warningtext[i]);
|
||||||
|
}
|
||||||
|
// Compile textures serially
|
||||||
|
for (int i = 0; i < ntexture; i++) {
|
||||||
|
CompileTexture(textures_[i], vfs, texture_exception,
|
||||||
|
texture_except_mutex, &texture_warningtext[i]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mujoco::user::ThreadPool pool(NumCompilerThreads(total_tasks));
|
||||||
|
|
||||||
|
// Enqueue mesh tasks
|
||||||
|
for (int i = 0; i < nmesh; ++i) {
|
||||||
|
pool.Schedule([mesh = meshes_[i], vfs, &mesh_exception,
|
||||||
|
&mesh_except_mutex, warningtext = &mesh_warningtext[i]]() {
|
||||||
|
CompileMesh(mesh, vfs, mesh_exception, mesh_except_mutex, warningtext);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enqueue texture tasks
|
||||||
|
for (int i = 0; i < ntexture; ++i) {
|
||||||
|
pool.Schedule([texture = textures_[i], vfs, &texture_exception,
|
||||||
|
&texture_except_mutex,
|
||||||
|
warningtext = &texture_warningtext[i]]() {
|
||||||
|
CompileTexture(texture, vfs, texture_exception, texture_except_mutex,
|
||||||
|
warningtext);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for all tasks to complete
|
||||||
|
pool.WaitCount(total_tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
// concatenate all mesh warnings, copy into warningtext
|
||||||
std::string concatenated_warnings;
|
std::string concatenated_warnings;
|
||||||
bool has_warning = false;
|
bool has_warning = false;
|
||||||
for (int i = 0; i < nmesh; i++) {
|
for (int i = 0; i < nmesh; i++) {
|
||||||
@@ -4588,14 +4639,24 @@ void mjCModel::CompileMeshes(const mjVFS* vfs) {
|
|||||||
}
|
}
|
||||||
mju::strcpy_arr(warningtext, concatenated_warnings.c_str());
|
mju::strcpy_arr(warningtext, concatenated_warnings.c_str());
|
||||||
|
|
||||||
// if exception was caught, rethrow it
|
// aggregate texture warnings
|
||||||
if (exception) {
|
for (int i = 0; i < ntexture; ++i) {
|
||||||
std::rethrow_exception(exception);
|
if (!texture_warningtext[i].empty()) {
|
||||||
|
if (has_warning) mju::strcat_arr(warningtext, "\n");
|
||||||
|
mju::strcat_arr(warningtext, texture_warningtext[i].c_str());
|
||||||
|
has_warning = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if exceptions were caught, rethrow the first one
|
||||||
|
if (mesh_exception) {
|
||||||
|
std::rethrow_exception(mesh_exception);
|
||||||
|
}
|
||||||
|
if (texture_exception) {
|
||||||
|
std::rethrow_exception(texture_exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// compute qpos0
|
// compute qpos0
|
||||||
void mjCModel::ComputeReference() {
|
void mjCModel::ComputeReference() {
|
||||||
int b = 0;
|
int b = 0;
|
||||||
@@ -4720,8 +4781,6 @@ void mjCModel::ResolveKeyframes(const mjModel* m) {
|
|||||||
key_pending_.clear();
|
key_pending_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void mjCModel::TryCompile(mjModel*& m, mjData*& d, const mjVFS* vfs) {
|
void mjCModel::TryCompile(mjModel*& m, mjData*& d, const mjVFS* vfs) {
|
||||||
// check if nan test works
|
// check if nan test works
|
||||||
double test = mjNAN;
|
double test = mjNAN;
|
||||||
@@ -4823,16 +4882,8 @@ void mjCModel::TryCompile(mjModel*& m, mjData*& d, const mjVFS* vfs) {
|
|||||||
// automatically set nuser fields
|
// automatically set nuser fields
|
||||||
SetNuser();
|
SetNuser();
|
||||||
|
|
||||||
// compile meshes (needed for geom compilation)
|
// compile meshes and textures (needed for geom compilation)
|
||||||
if (compiler.usethread && meshes_.size() > 1) {
|
CompileMeshesAndTextures(vfs);
|
||||||
// multi-threaded mesh compile
|
|
||||||
CompileMeshes(vfs);
|
|
||||||
} else {
|
|
||||||
// single-threaded mesh compile
|
|
||||||
for (int i=0; i < meshes_.size(); i++) {
|
|
||||||
meshes_[i]->Compile(vfs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// compile objects in kinematic tree
|
// compile objects in kinematic tree
|
||||||
for (int i=0; i < bodies_.size(); i++) {
|
for (int i=0; i < bodies_.size(); i++) {
|
||||||
@@ -4854,7 +4905,7 @@ void mjCModel::TryCompile(mjModel*& m, mjData*& d, const mjVFS* vfs) {
|
|||||||
for (auto flex : flexes_) flex->Compile(vfs);
|
for (auto flex : flexes_) flex->Compile(vfs);
|
||||||
for (auto skin : skins_) skin->Compile(vfs);
|
for (auto skin : skins_) skin->Compile(vfs);
|
||||||
for (auto hfield : hfields_) hfield->Compile(vfs);
|
for (auto hfield : hfields_) hfield->Compile(vfs);
|
||||||
for (auto texture : textures_) texture->Compile(vfs);
|
|
||||||
for (auto material : materials_) material->Compile();
|
for (auto material : materials_) material->Compile();
|
||||||
for (auto pair : pairs_) pair->Compile();
|
for (auto pair : pairs_) pair->Compile();
|
||||||
for (auto exclude : excludes_) exclude->Compile();
|
for (auto exclude : excludes_) exclude->Compile();
|
||||||
@@ -5117,8 +5168,6 @@ void mjCModel::TryCompile(mjModel*& m, mjData*& d, const mjVFS* vfs) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void PrintIndent(std::stringstream& ss, int depth) {
|
static void PrintIndent(std::stringstream& ss, int depth) {
|
||||||
// A static string of spaces, created only once during the program's lifetime.
|
// A static string of spaces, created only once during the program's lifetime.
|
||||||
static const std::string spaces(1024, ' ');
|
static const std::string spaces(1024, ' ');
|
||||||
|
|||||||
@@ -29,8 +29,8 @@
|
|||||||
#include <mujoco/mjdata.h>
|
#include <mujoco/mjdata.h>
|
||||||
#include <mujoco/mjmodel.h>
|
#include <mujoco/mjmodel.h>
|
||||||
#include <mujoco/mjplugin.h>
|
#include <mujoco/mjplugin.h>
|
||||||
#include <mujoco/mjtnum.h>
|
|
||||||
#include <mujoco/mjspec.h>
|
#include <mujoco/mjspec.h>
|
||||||
|
#include <mujoco/mjtnum.h>
|
||||||
#include "user/user_objects.h"
|
#include "user/user_objects.h"
|
||||||
|
|
||||||
typedef std::map<std::string, int, std::less<> > mjKeyMap;
|
typedef std::map<std::string, int, std::less<> > mjKeyMap;
|
||||||
@@ -296,9 +296,6 @@ class mjCModel : public mjCModel_, private mjSpec {
|
|||||||
// clear objects allocated by Compile
|
// clear objects allocated by Compile
|
||||||
void Clear();
|
void Clear();
|
||||||
|
|
||||||
// multi-threaded mesh compilation
|
|
||||||
void CompileMeshes(const mjVFS* vfs);
|
|
||||||
|
|
||||||
// delete material from object
|
// delete material from object
|
||||||
template <class T> void DeleteMaterial(std::vector<T*>& list,
|
template <class T> void DeleteMaterial(std::vector<T*>& list,
|
||||||
std::string_view name = "");
|
std::string_view name = "");
|
||||||
@@ -354,6 +351,8 @@ class mjCModel : public mjCModel_, private mjSpec {
|
|||||||
|
|
||||||
// compile phases
|
// compile phases
|
||||||
void TryCompile(mjModel*& m, mjData*& d, const mjVFS* vfs);
|
void TryCompile(mjModel*& m, mjData*& d, const mjVFS* vfs);
|
||||||
|
void CompileMeshesAndTextures(const mjVFS* vfs);
|
||||||
|
|
||||||
void SetNuser(); // set nuser fields
|
void SetNuser(); // set nuser fields
|
||||||
void IndexAssets(bool discard); // convert asset names into indices
|
void IndexAssets(bool discard); // convert asset names into indices
|
||||||
void CheckEmptyNames(); // check empty names
|
void CheckEmptyNames(); // check empty names
|
||||||
|
|||||||
@@ -1441,14 +1441,13 @@ class mjCTexture : public mjCTexture_, private mjsTexture {
|
|||||||
void CopyFromSpec(void);
|
void CopyFromSpec(void);
|
||||||
void PointToLocal(void);
|
void PointToLocal(void);
|
||||||
void NameSpace(const mjCModel* m);
|
void NameSpace(const mjCModel* m);
|
||||||
|
void Compile(const mjVFS* vfs);
|
||||||
|
|
||||||
std::string File() const { return file_; }
|
std::string File() const { return file_; }
|
||||||
std::string get_content_type() const { return content_type_; }
|
std::string get_content_type() const { return content_type_; }
|
||||||
std::vector<std::string> get_cubefiles() const { return cubefiles_; }
|
std::vector<std::string> get_cubefiles() const { return cubefiles_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Compile(const mjVFS* vfs); // compiler
|
|
||||||
|
|
||||||
// store texture into asset cache
|
// store texture into asset cache
|
||||||
std::string GetCacheId(const mjResource* resource, const std::string& asset_type);
|
std::string GetCacheId(const mjResource* resource, const std::string& asset_type);
|
||||||
void Builtin2D(void); // make builtin 2D
|
void Builtin2D(void); // make builtin 2D
|
||||||
|
|||||||
Reference in New Issue
Block a user