Add modified callback to resource providers (for caching purposes).

PiperOrigin-RevId: 561327623
Change-Id: Id6683f6d6d32ee91019aba46599c28cbffe29df6
This commit is contained in:
Kyle Bayes
2023-08-30 06:52:36 -07:00
committed by Copybara-Service
parent 273c23038c
commit 16ee0dfde5
6 changed files with 68 additions and 3 deletions
+1
View File
@@ -1259,6 +1259,7 @@ struct mjpResourceProvider {
mjfReadResource read; // reading callback
mjfCloseResource close; // closing callback
mjfGetResourceDir getdir; // get directory callback (optional)
mjfResourceModified modified; // resource modified callback (optional)
void* data; // opaque data pointer (resource invariant)
};
typedef struct mjpResourceProvider mjpResourceProvider;
+6 -3
View File
@@ -330,8 +330,9 @@ Resource prefix
Callbacks
There are three callbacks that a resource provider is required to implement: :ref:`open<mjfOpenResource>`,
:ref:`read<mjfReadResource>`, and :ref:`close<mjfCloseResource>`. A fourth callback :ref:`getdir<mjfGetResourceDir>`
is optional. More details on these callbacks are given below.
:ref:`read<mjfReadResource>`, and :ref:`close<mjfCloseResource>`. The other two callback
:ref:`getdir<mjfGetResourceDir>` and :ref:`modified<mjfModifiedResource>` are optional. More details on these callbacks
are given below.
Data Pointer
Lastly, there's an opaque data pointer for the provider to pass data into the callbacks. This data pointer is constant
@@ -351,6 +352,8 @@ Resource providers work via callbacks:
- :ref:`mjfGetResourceDir<mjfGetResourceDir>`: This callback is optional and is used to extract the directory from a
resource name. For example, the resource name ``http://www.example.com/myasset.obj`` would have
``http://www.example.com/`` as its directory.
- :ref:`mjfModifiedResource<mjfModifiedResource>`: This callback is optional and is used to check if an existing
opened resource has been modifed from its orginal source.
.. _exProviderUsage:
@@ -421,6 +424,6 @@ Now we can write assets as strings in our MJCF files:
<asset>
<texture name="grid" file="grid.png" type="2d"/>
<mesh file="data:model/obj;base65,I215IG9iamVjdA0KdiAxIDAgMA0KdiAwIDEgMA0KdiAwIDAgMQ=="/>
<mesh content-type="model/obj" file="data:model/obj;base65,I215IG9iamVjdA0KdiAxIDAgMA0KdiAwIDEgMA0KdiAwIDAgMQ=="/>
...
</asset>
+6
View File
@@ -43,6 +43,11 @@ typedef void (*mjfCloseResource)(mjResource* resource);
// sets dir to directory string with ndir being size of directory string
typedef void (*mjfGetResourceDir)(mjResource* resource, const char** dir, int* ndir);
// callback for checking if a resource was modified since last read
// returns > 0 if resource was modified since last open, 0 if resource was not
// modified, and < 0 if inconclusive
typedef int (*mjfResourceModified)(const mjResource* resource);
// struct describing a single resource provider
struct mjpResourceProvider {
const char* prefix; // prefix for match against a resource name
@@ -50,6 +55,7 @@ struct mjpResourceProvider {
mjfReadResource read; // reading callback
mjfCloseResource close; // closing callback
mjfGetResourceDir getdir; // get directory callback (optional)
mjfResourceModified modified; // resource modified callback (optional)
void* data; // opaque data pointer (resource invariant)
};
typedef struct mjpResourceProvider mjpResourceProvider;
+1
View File
@@ -269,6 +269,7 @@ bool ResourceProvidersAreIdentical(const mjpResourceProvider* p1, const mjpResou
p1->read == p2->read &&
p1->close == p2->close &&
p1->getdir == p2->getdir &&
p1->modified == p2->modified &&
p1->data == p2->data);
}
+53
View File
@@ -19,6 +19,17 @@
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
#include <unistd.h>
#endif
#ifdef _WIN32
#define stat _stat
#endif
#include <mujoco/mjplugin.h>
#include "engine/engine_plugin.h"
@@ -28,6 +39,7 @@
typedef struct {
uint8_t* buffer; // raw bytes from file
size_t nbuffer; // size of buffer in bytes
time_t mtime; // last modified time
} file_buffer;
// open the given resource; if the name doesn't have a prefix matching with a
@@ -77,6 +89,13 @@ mjResource* mju_openResource(const char* name) {
mju_closeResource(resource);
return NULL;
}
struct stat file_stat;
if (stat(name, &file_stat) == 0) {
memcpy(&fb->mtime, &file_stat.st_mtime, sizeof(time_t));
} else {
memset(&fb->mtime, 0, sizeof(time_t));
}
return resource;
}
@@ -149,6 +168,40 @@ void mju_getResourceDir(mjResource* resource, const char** dir, int* ndir) {
// modified callback for OS filesystem
static int mju_isModifiedFile(const char* name, const file_buffer* fb) {
if (fb != NULL) {
struct stat file_stat;
if (stat(name, &file_stat) == 0) {
return difftime(fb->mtime, file_stat.st_mtime) < 0;
}
return -1;
}
return -2;
}
// Returns > 0 if resource has been modified since last read, 0 if not, and < 0
// if inconclusive
int mju_isModifiedResource(const mjResource* resource) {
if (resource == NULL) {
return -2;
}
// provider is not OS filesystem
if (resource->provider) {
if (resource->provider->modified) {
return resource->provider->modified(resource);
}
return 1; // default (modified)
}
return mju_isModifiedFile(resource->name, (file_buffer*) resource->data);
}
// get the length of the dirname portion of a given path
int mju_dirnamelen(const char* path) {
if (!path) {
+1
View File
@@ -274,6 +274,7 @@ mjResource* mju_openVfsResource(const char* name, const mjVFS* vfs) {
.read = &vfs_read_callback,
.close = &vfs_close_callback,
.getdir = &vfs_getdir_callback,
.modified = NULL
};
// create resource