Add renderer info query API
This commit is contained in:
@@ -117,6 +117,13 @@ typedef struct mjrRect_ { // OpenGL rectangle
|
||||
} mjrRect;
|
||||
|
||||
|
||||
typedef struct mjrRendererInfo_ { // active renderer identity
|
||||
const char* renderer; // renderer family: classic, filament, noop
|
||||
const char* backend; // graphics backend: opengl, vulkan; empty if uninitialized
|
||||
const char* backend_version; // backend version string, empty if unknown
|
||||
} mjrRendererInfo;
|
||||
|
||||
|
||||
typedef struct mjrVertexAttribute_ { // vertex attribute format specification
|
||||
const void* bytes; // vertex data
|
||||
int usage; // position, normal, etc [mjrVertexAttributeUsage]
|
||||
|
||||
@@ -104,6 +104,9 @@ mjrfContext* mjrf_createContext(const mjrfContextConfig* config);
|
||||
// Destroys the filament rendering context.
|
||||
void mjrf_destroyContext(mjrfContext* ctx);
|
||||
|
||||
// Gets active renderer information for the given filament context.
|
||||
void mjrf_getRendererInfo(mjrfContext* ctx, mjrRendererInfo* info);
|
||||
|
||||
typedef enum mjrDrawMode_ { // how to draw objects in the scene
|
||||
mjDRAW_MODE_DEFAULT, // default colors and lighting
|
||||
mjDRAW_MODE_DEFAULT_NO_TEXTURES, // default, but without textures
|
||||
|
||||
@@ -855,6 +855,12 @@ MJAPI void mjv_cameraFrustum(float zver[2], float zhor[2], float zclip[2], cons
|
||||
// Set default mjrContext.
|
||||
MJAPI void mjr_defaultContext(mjrContext* con);
|
||||
|
||||
// Set default mjrRendererInfo.
|
||||
MJAPI void mjr_defaultRendererInfo(mjrRendererInfo* info);
|
||||
|
||||
// Get active renderer information.
|
||||
MJAPI void mjr_getRendererInfo(mjrRendererInfo* info);
|
||||
|
||||
// Allocate resources in custom OpenGL context; fontscale is mjtFontScale.
|
||||
MJAPI void mjr_makeContext(const mjModel* m, mjrContext* con, int fontscale);
|
||||
|
||||
|
||||
@@ -125,6 +125,14 @@ class MuJoCoBindingsTest(parameterized.TestCase):
|
||||
self.model: mujoco.MjModel = mujoco.MjModel.from_xml_string(TEST_XML)
|
||||
self.data = mujoco.MjData(self.model)
|
||||
|
||||
def test_renderer_info_binding(self):
|
||||
info = mujoco.MjrRendererInfo()
|
||||
mujoco.mjr_getRendererInfo(info)
|
||||
|
||||
self.assertIn(info.renderer, ('classic', 'filament', 'noop'))
|
||||
self.assertIn(info.backend, ('', 'opengl', 'vulkan', 'unknown'))
|
||||
self.assertIsInstance(info.backend_version, str)
|
||||
|
||||
def test_load_xml_can_handle_name_clash(self):
|
||||
xml_1 = r"""
|
||||
<mujoco>
|
||||
|
||||
@@ -5480,6 +5480,34 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([
|
||||
),
|
||||
doc='Set default mjrContext.',
|
||||
)),
|
||||
('mjr_defaultRendererInfo',
|
||||
FunctionDecl(
|
||||
name='mjr_defaultRendererInfo',
|
||||
return_type=ValueType(name='void'),
|
||||
parameters=(
|
||||
FunctionParameterDecl(
|
||||
name='info',
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='mjrRendererInfo'),
|
||||
),
|
||||
),
|
||||
),
|
||||
doc='Set default mjrRendererInfo.',
|
||||
)),
|
||||
('mjr_getRendererInfo',
|
||||
FunctionDecl(
|
||||
name='mjr_getRendererInfo',
|
||||
return_type=ValueType(name='void'),
|
||||
parameters=(
|
||||
FunctionParameterDecl(
|
||||
name='info',
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='mjrRendererInfo'),
|
||||
),
|
||||
),
|
||||
),
|
||||
doc='Get active renderer information.',
|
||||
)),
|
||||
('mjr_makeContext',
|
||||
FunctionDecl(
|
||||
name='mjr_makeContext',
|
||||
|
||||
@@ -10995,6 +10995,34 @@ STRUCTS: Mapping[str, StructDecl] = dict([
|
||||
),
|
||||
),
|
||||
)),
|
||||
('mjrRendererInfo',
|
||||
StructDecl(
|
||||
name='mjrRendererInfo',
|
||||
declname='struct mjrRendererInfo_',
|
||||
fields=(
|
||||
StructFieldDecl(
|
||||
name='renderer',
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='char', is_const=True),
|
||||
),
|
||||
doc='renderer family: classic, filament, noop',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='backend',
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='char', is_const=True),
|
||||
),
|
||||
doc='graphics backend: opengl, vulkan; empty if uninitialized',
|
||||
),
|
||||
StructFieldDecl(
|
||||
name='backend_version',
|
||||
type=PointerType(
|
||||
inner_type=ValueType(name='char', is_const=True),
|
||||
),
|
||||
doc='backend version string, empty if unknown',
|
||||
),
|
||||
),
|
||||
)),
|
||||
('mjrVertexAttribute',
|
||||
StructDecl(
|
||||
name='mjrVertexAttribute',
|
||||
|
||||
@@ -79,6 +79,7 @@ using MjLogMessage = ::mjLogMessage;
|
||||
|
||||
// From mjrender.h
|
||||
using MjrRect = ::mjrRect;
|
||||
using MjrRendererInfo = ::mjrRendererInfo;
|
||||
using MjrContext = ::mjrContext;
|
||||
using MjrVertexAttribute = ::mjrVertexAttribute;
|
||||
|
||||
|
||||
@@ -245,6 +245,8 @@ PYBIND11_MODULE(_render, pymodule, pybind11::mod_gil_not_used()) {
|
||||
using EigenFloatVectorX = Eigen::Vector<float, Eigen::Dynamic>;
|
||||
|
||||
// Skipped: mjr_defaultContext (have MjrContext.__init__)
|
||||
Def<traits::mjr_defaultRendererInfo>(pymodule);
|
||||
Def<traits::mjr_getRendererInfo>(pymodule);
|
||||
// Skipped: mjr_makeContext (have MjrContext.__init__)
|
||||
Def<traits::mjr_changeFont>(pymodule);
|
||||
Def<traits::mjr_addAux>(pymodule);
|
||||
|
||||
@@ -1049,6 +1049,36 @@ This is useful for example when the MJB is not available as a file on disk.)"));
|
||||
X(height);
|
||||
#undef X
|
||||
|
||||
// ==================== MJRRENDERERINFO ======================================
|
||||
py::class_<raw::MjrRendererInfo> mjrRendererInfo(m, "MjrRendererInfo");
|
||||
mjrRendererInfo.def(py::init([]() {
|
||||
raw::MjrRendererInfo info;
|
||||
mjr_defaultRendererInfo(&info);
|
||||
return info;
|
||||
}));
|
||||
mjrRendererInfo.def("__copy__", [](const raw::MjrRendererInfo& other) {
|
||||
return raw::MjrRendererInfo(other);
|
||||
});
|
||||
mjrRendererInfo.def("__deepcopy__",
|
||||
[](const raw::MjrRendererInfo& other, py::dict) {
|
||||
return raw::MjrRendererInfo(other);
|
||||
});
|
||||
DefineStructFunctions(mjrRendererInfo);
|
||||
mjrRendererInfo.def_property_readonly("renderer",
|
||||
[](const raw::MjrRendererInfo& info) {
|
||||
return info.renderer ? info.renderer
|
||||
: "";
|
||||
});
|
||||
mjrRendererInfo.def_property_readonly("backend",
|
||||
[](const raw::MjrRendererInfo& info) {
|
||||
return info.backend ? info.backend
|
||||
: "";
|
||||
});
|
||||
mjrRendererInfo.def_property_readonly(
|
||||
"backend_version", [](const raw::MjrRendererInfo& info) {
|
||||
return info.backend_version ? info.backend_version : "";
|
||||
});
|
||||
|
||||
// ==================== MJRVERTEXATTRIBUTE ===================================
|
||||
py::class_<raw::MjrVertexAttribute> mjrVertexAttribute(m,
|
||||
"MjrVertexAttribute");
|
||||
|
||||
@@ -57,6 +57,8 @@ class CompatContext {
|
||||
scene_bridge_->UploadHeightField(model, id);
|
||||
}
|
||||
|
||||
mjrfContext* Context() const { return context_.get(); }
|
||||
|
||||
private:
|
||||
mjrDrawMode draw_mode_ = mjDRAW_MODE_DEFAULT;
|
||||
UniquePtr<mjrfContext> context_{nullptr, nullptr};
|
||||
@@ -189,6 +191,20 @@ void mjr_defaultContext(mjrContext* con) {
|
||||
memset(con, 0, sizeof(mjrContext));
|
||||
}
|
||||
|
||||
void mjr_defaultRendererInfo(mjrRendererInfo* info) {
|
||||
memset(info, 0, sizeof(mjrRendererInfo));
|
||||
info->renderer = "filament";
|
||||
info->backend = "";
|
||||
info->backend_version = "";
|
||||
}
|
||||
|
||||
void mjr_getRendererInfo(mjrRendererInfo* info) {
|
||||
mjr_defaultRendererInfo(info);
|
||||
if (g_context) {
|
||||
mjrf_getRendererInfo(g_context->Context(), info);
|
||||
}
|
||||
}
|
||||
|
||||
void mjr_makeFilamentContext(const mjModel* m, const mjrfContextConfig* cfg,
|
||||
mjrContext* con) {
|
||||
if (g_context != nullptr) {
|
||||
|
||||
@@ -55,6 +55,30 @@ void mjr_defaultContext(mjrContext* con) {
|
||||
memset(con, 0, sizeof(mjrContext));
|
||||
}
|
||||
|
||||
static int context_count = 0;
|
||||
|
||||
static int contextHasResources(const mjrContext* con) {
|
||||
return con->ntexture || con->offColor || con->offDepthStencil || con->offFBO ||
|
||||
con->shadowTex || con->shadowFBO || con->rangePlane || con->rangeMesh ||
|
||||
con->rangeHField || con->rangeBuiltin || con->rangeFont || con->nskin;
|
||||
}
|
||||
|
||||
// set default mjrRendererInfo
|
||||
void mjr_defaultRendererInfo(mjrRendererInfo* info) {
|
||||
memset(info, 0, sizeof(mjrRendererInfo));
|
||||
info->renderer = "classic";
|
||||
info->backend = "";
|
||||
info->backend_version = "";
|
||||
}
|
||||
|
||||
// get active renderer information
|
||||
void mjr_getRendererInfo(mjrRendererInfo* info) {
|
||||
mjr_defaultRendererInfo(info);
|
||||
if (context_count > 0) {
|
||||
info->backend = "opengl";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// allocate lists
|
||||
@@ -1608,6 +1632,7 @@ void mjr_makeContext_offSize(const mjModel* m, mjrContext* con, int fontscale,
|
||||
// try to bind window (bind offscreen if no window)
|
||||
mjr_setBuffer(mjFB_WINDOW, con);
|
||||
|
||||
context_count++;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1667,6 +1692,7 @@ void mjr_makeContext_offSize(const mjModel* m, mjrContext* con, int fontscale,
|
||||
|
||||
// set default depth mapping for mjr_readPixels
|
||||
con->readDepthMap = mjDEPTH_ZERONEAR;
|
||||
context_count++;
|
||||
}
|
||||
|
||||
|
||||
@@ -1811,6 +1837,8 @@ void mjr_addAux(int index, int width, int height, int samples, mjrContext* con)
|
||||
|
||||
// free resources in custom OpenGL context
|
||||
void mjr_freeContext(mjrContext* con) {
|
||||
int had_resources = contextHasResources(con);
|
||||
|
||||
// save flags
|
||||
int glInitialized = con->glInitialized;
|
||||
int windowAvailable = con->windowAvailable;
|
||||
@@ -1867,6 +1895,10 @@ void mjr_freeContext(mjrContext* con) {
|
||||
con->windowSamples = windowSamples;
|
||||
con->windowStereo = windowStereo;
|
||||
con->windowDoublebuffer = windowDoublebuffer;
|
||||
|
||||
if (had_resources && context_count > 0) {
|
||||
context_count--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ namespace mujoco {
|
||||
FilamentContext::FilamentContext(const mjrfContextConfig* config)
|
||||
: config_(*config) {
|
||||
FilamentPlatformSetup setup = CreateFilamentPlatform(config_);
|
||||
backend_ = setup.backend;
|
||||
platform_ = std::move(setup.platform);
|
||||
|
||||
filament::Engine::Config engine_config;
|
||||
@@ -58,7 +59,7 @@ FilamentContext::FilamentContext(const mjrfContextConfig* config)
|
||||
|
||||
filament::Engine::Builder engine_builder;
|
||||
engine_builder.config(&engine_config);
|
||||
engine_builder.backend(setup.backend);
|
||||
engine_builder.backend(backend_);
|
||||
engine_builder.platform(platform_.get());
|
||||
engine_builder.feature("backend.disable_parallel_shader_compile",
|
||||
setup.disable_parallel_shader_compile);
|
||||
|
||||
@@ -59,6 +59,8 @@ class FilamentContext : public mjrfContext {
|
||||
|
||||
filament::Engine* GetEngine() const { return engine_; }
|
||||
|
||||
filament::Engine::Backend GetBackend() const { return backend_; }
|
||||
|
||||
ObjectManager* GetObjectManager() const { return object_manager_.get(); }
|
||||
|
||||
MaterialManager* GetMaterialManager() const {
|
||||
@@ -76,6 +78,7 @@ class FilamentContext : public mjrfContext {
|
||||
void ValidateSwapChains(std::span<const mjrfRenderRequest> render_requests);
|
||||
|
||||
mjrfContextConfig config_;
|
||||
filament::Engine::Backend backend_;
|
||||
filament::Engine* engine_ = nullptr;
|
||||
filament::Renderer* renderer_ = nullptr;
|
||||
filament::SwapChain* window_swap_chain_ = nullptr;
|
||||
|
||||
@@ -37,6 +37,17 @@ static void setf(float (&arr)[N], const std::array<float, N>& values) {
|
||||
}
|
||||
}
|
||||
|
||||
static const char* BackendName(filament::Engine::Backend backend) {
|
||||
switch (backend) {
|
||||
case filament::Engine::Backend::OPENGL:
|
||||
return "opengl";
|
||||
case filament::Engine::Backend::VULKAN:
|
||||
return "vulkan";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
void mjrf_defaultContextConfig(mjrfContextConfig* config) {
|
||||
@@ -124,6 +135,13 @@ void mjrf_destroyContext(mjrfContext* ctx) {
|
||||
delete mujoco::FilamentContext::downcast(ctx);
|
||||
}
|
||||
|
||||
void mjrf_getRendererInfo(mjrfContext* ctx, mjrRendererInfo* info) {
|
||||
memset(info, 0, sizeof(mjrRendererInfo));
|
||||
info->renderer = "filament";
|
||||
info->backend = ctx ? BackendName(mujoco::FilamentContext::downcast(ctx)->GetBackend()) : "";
|
||||
info->backend_version = "";
|
||||
}
|
||||
|
||||
mjrfTexture* mjrf_createTexture(mjrfContext* ctx,
|
||||
const mjrfTextureConfig* config) {
|
||||
return new mujoco::Texture(
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
#include <mujoco/mujoco.h>
|
||||
#include <string.h>
|
||||
|
||||
// This library implements the entirety of mujoco's mjr API as fast-fail stubs.
|
||||
// You can link this library with your application (instead of standard renderer
|
||||
@@ -22,6 +23,15 @@
|
||||
void mjr_defaultContext(mjrContext* con) {
|
||||
mju_error("mjr_defaultContext not implemented.");
|
||||
}
|
||||
void mjr_defaultRendererInfo(mjrRendererInfo* info) {
|
||||
memset(info, 0, sizeof(mjrRendererInfo));
|
||||
info->renderer = "noop";
|
||||
info->backend = "";
|
||||
info->backend_version = "";
|
||||
}
|
||||
void mjr_getRendererInfo(mjrRendererInfo* info) {
|
||||
mjr_defaultRendererInfo(info);
|
||||
}
|
||||
void mjr_makeContext(const mjModel* m, mjrContext* con, int fontscale) {
|
||||
mju_error("mjr_makeContext not implemented.");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user