Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 0 additions & 16 deletions lib/usd/ui/renderSetup/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,20 +1,4 @@

#
# Copyright 2026 Autodesk
#
# 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.
#

add_subdirectory(rendering)

target_sources(${PROJECT_NAME}
Expand Down
260 changes: 223 additions & 37 deletions lib/usd/ui/renderSetup/mayaRendererProvider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,55 +16,46 @@

#include "mayaRendererProvider.h"

#ifdef MAYA_HAS_USD_SETTINGS_NODES
#include <mayaUsd/nodes/sceneRenderDescription.h>
#endif

#include <pxr/imaging/glf/contextCaps.h>
#include <pxr/imaging/hd/rendererPlugin.h>
#include <pxr/imaging/hd/rendererPluginRegistry.h>

#include <maya/MCommandResult.h>
#include <maya/MFnDependencyNode.h>
#include <maya/MGlobal.h>
#include <maya/MObject.h>
#include <maya/MPlug.h>
#include <maya/MSelectionList.h>
#include <maya/MStatus.h>
#include <maya/MStringArray.h>

namespace MayaUsdRenderSetup {

namespace {

//! \return the label Maya's own Render Settings dialog shows for `rendererName`.
MString rendererUIName(const MString& rendererName)
{
MString cmd;
cmd.format("renderer -query -rendererUIName \"^1s\"", rendererName);
MString uiName;
MGlobal::executeCommand(cmd, uiName);
return uiName;
}
} // namespace

std::vector<AdskUsdRenderSetup::RendererInfo> MayaRendererProvider::availableRenderers() const
{
MStringArray allRenderers;
MStatus status
= MGlobal::executeCommand("renderer -query -namesOfAvailableRenderers", allRenderers);
if (!status) {
MGlobal::displayWarning("Unable to retrieve available renderers.");
return {};
}
MString uiName;
MStatus status = MGlobal::executeCommand(cmd, uiName);
if (status != MS::kSuccess)
return rendererName;

std::vector<AdskUsdRenderSetup::RendererInfo> renderers;
renderers.reserve(allRenderers.length());
for (const auto& rendererName : allRenderers) {
AdskUsdRenderSetup::RendererInfo info;
info.name = rendererName.asChar();
info.displayName = rendererUIName(rendererName).asChar();
info.isHydra = isHydraCapable(info.name);
renderers.push_back(info);
}
return renderers;
}
if (uiName.length() == 0)
return rendererName;

std::string MayaRendererProvider::currentRenderer() const
{
MString result;
MGlobal::executeCommand("currentRenderer()", result);
return result.asChar();
return uiName;
}

bool MayaRendererProvider::isHydraCapable(const std::string& rendererName) const
//! \return true if the named renderer is capable of rendering through Hydra.
bool isHydraCapable(const std::string& rendererName)
{
MString cmd;
cmd.format("renderer -query -capability \"isHydra\" \"^1s\"", MString(rendererName.c_str()));
Expand All @@ -80,18 +71,213 @@ bool MayaRendererProvider::isHydraCapable(const std::string& rendererName) const
return false;
}

switch (result.resultType()) {
case MCommandResult::kString: {
MString value;
result.getResult(value);
return value == "true" || value == "1";
if (result.resultType() != MCommandResult::kString)
return false;

MString value;
result.getResult(value);
return value == "true" || value == "1";
Comment thread
seando-adsk marked this conversation as resolved.
}

//! \brief RAII wrapper for HdRendererPlugin pointers.
class RendererPluginPointer
{
public:
RendererPluginPointer(
PXR_NS::HdRendererPluginRegistry& registry,
const PXR_NS::TfToken& rendererId)
: _registry(registry)
, _plugin(registry.GetRendererPlugin(rendererId))
{
}

~RendererPluginPointer()
{
if (_plugin) {
_registry.ReleasePlugin(_plugin);
}
}

PXR_NS::HdRendererPlugin* get() const { return _plugin; }

private:
PXR_NS::HdRendererPluginRegistry& _registry;
PXR_NS::HdRendererPlugin* _plugin;
};

//! \brief RAII wrapper for HdRenderDelegate pointers.
class RenderDelegatePointer
{
public:
RenderDelegatePointer(PXR_NS::HdRendererPlugin* plugin)
: _plugin(plugin)
, _delegate(plugin ? plugin->CreateRenderDelegate() : nullptr)
{
}

~RenderDelegatePointer()
{
if (_plugin && _delegate) {
_plugin->DeleteRenderDelegate(_delegate);
}
}
default: return false;

PXR_NS::HdRenderDelegate* get() const { return _delegate; }

private:
PXR_NS::HdRendererPlugin* _plugin;
PXR_NS::HdRenderDelegate* _delegate;
};

//! \return true if the named renderer is available and capable of rendering through Hydra.
bool isHydraRendererAvailable(
PXR_NS::HdRendererPluginRegistry& registry,
const PXR_NS::TfToken& rendererId)
{
RendererPluginPointer plugin(registry, rendererId);
if (!plugin.get())
return false;

// As of 22.02, this needs to be called for Storm
if (rendererId == PXR_NS::TfToken("HdStormRendererPlugin")) {
PXR_NS::GlfContextCaps::InitInstance();
}

if (!plugin.get()->IsSupported())
return false;

RenderDelegatePointer delegate(plugin.get());
if (!delegate.get())
return false;

return true;
}

//! \return a map of every renderer Maya currently knows about, Hydra-capable or not.
std::map<std::string, AdskUsdRenderSetup::RendererInfo> getRenderersMap()
{
std::map<std::string, AdskUsdRenderSetup::RendererInfo> renderers;

{
MStringArray mayaRenderers;
MStatus status
= MGlobal::executeCommand("renderer -query -namesOfAvailableRenderers", mayaRenderers);
if (!status) {
MGlobal::displayWarning("Unable to retrieve available renderers.");
} else {
for (const auto& rendererName : mayaRenderers) {
AdskUsdRenderSetup::RendererInfo info;
info.name = rendererName.asChar();
info.displayName = rendererUIName(rendererName).asChar();
info.isHydra = isHydraCapable(info.name);
renderers[info.name] = std::move(info);
}
}
}

{
PXR_NS::HdRendererPluginRegistry& registry
= PXR_NS::HdRendererPluginRegistry::GetInstance();
std::vector<PXR_NS::HfPluginDesc> hydraDescs;
registry.GetPluginDescs(&hydraDescs);

for (const auto& desc : hydraDescs) {
const PXR_NS::TfToken rendererId = desc.id;
if (!isHydraRendererAvailable(registry, rendererId)) {
continue;
}

// Note: some Hydra renderers may also be registered in Maya's legacy renderer registry,
// so we need to merge the two lists.
//
// The display-name registered with Maya is usually better than the one registered
// with Hydra, so we prefer that one if it exists. In that case we only update the
// isHydra flag, since the `isHydraCapable` query might not be reliable for some
// renderers.
const std::string name = rendererId.GetText();
auto it = renderers.find(name);
if (it != renderers.end()) {
it->second.isHydra = true;
} else {
renderers[name] = AdskUsdRenderSetup::RendererInfo { name, desc.displayName, true };
}
}
}

return renderers;
}

} // namespace

std::vector<AdskUsdRenderSetup::RendererInfo> MayaRendererProvider::availableRenderers() const
{
std::vector<AdskUsdRenderSetup::RendererInfo> renderers;

auto renderersMap = getRenderersMap();
renderers.reserve(renderersMap.size());
for (const auto& pair : renderersMap) {
renderers.push_back(pair.second);
}

return renderers;
}

std::string MayaRendererProvider::currentRenderer() const
{
MSelectionList slist;
slist.add("defaultRenderGlobals");
MObject defaultRenderGlobalsObj;
if (slist.length() > 0 && slist.getDependNode(0, defaultRenderGlobalsObj)) {
MFnDependencyNode depNode(defaultRenderGlobalsObj);
MPlug currentRendererPlug = depNode.findPlug("currentRenderer", false);
if (!currentRendererPlug.isNull()) {
MString value;
currentRendererPlug.getValue(value);
if (value.length() > 0) {
return value.asChar();
}
}
}

#ifdef MAYA_HAS_USD_SETTINGS_NODES
std::string hydraRenderer = MAYAUSD_NS_DEF::SceneRenderDescription::getCurrentRenderer();
if (!hydraRenderer.empty()) {
return hydraRenderer;
}
#endif

return std::string();
}

void MayaRendererProvider::switchRenderer(const std::string& next)
{
// TODO: should we allow setting an empty renderer?
if (next.empty()) {
return;
}

// This validates that the renderer is available and avoid
// using some arbitrary string in the MEL command.
{
const auto avail = getRenderersMap();
const auto it = avail.find(next);
if (it == avail.end())
return;

#ifdef MAYA_HAS_USD_SETTINGS_NODES
if (it->second.isHydra) {
MAYAUSD_NS_DEF::SceneRenderDescription::setCurrentRenderer(next);
} else {
// TODO: do we need to clear the SceneRenderDescription current renderer when the
// renderer is switched to a legacy renderer?
MAYAUSD_NS_DEF::SceneRenderDescription::setCurrentRenderer("");
}
#endif
}

// Note: other code watch the "currentRenderer" attribute of the defaultRenderGlobals node,
// so we change it last after we already updated the `SceneRenderDescription` current
// renderer.
MString cmd;
cmd.format("setCurrentRenderer(\"^1s\")", MString(next.c_str()));
MGlobal::executeCommand(cmd);
Expand Down
33 changes: 25 additions & 8 deletions lib/usd/ui/renderSetup/mayaRendererProvider.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,28 @@
#ifndef MAYAUSDUI_USD_RENDERSETUP_MAYARENDERERPROVIDER_H
#define MAYAUSDUI_USD_RENDERSETUP_MAYARENDERERPROVIDER_H

#include <mayaUsdUI/ui/api.h>

#include <AdskUsdRenderSetup/IRendererProvider.h>

#include <string>
#include <vector>

namespace MayaUsdRenderSetup {

//! MayaUSD implementation of AdskUsdRenderSetup::IRendererProvider for the Render Setup UI.
//! Lists every renderer Maya knows about and reads/writes Maya's global current renderer
//! (defaultRenderGlobals.currentRenderer).
class MayaRendererProvider : public AdskUsdRenderSetup::IRendererProvider
//! Reports available renderers from Maya's legacy renderer registry (mayaSoftware, arnold, etc.,
//! marked isHydra=false) and from Hydra's HdRendererPluginRegistry (marked isHydra=true).
//! Reads and writes the current renderer from the appropriate store:
//! - Hydra renderers: UsdSettingsNode::currentRenderer attribute
//! - Legacy renderers: defaultRenderGlobals.currentRenderer attribute
#if defined(_MSC_VER)
// AdskUsdRenderSetup::IRendererProvider is a header-only interface (all its methods are
// inline), so it needs no dll-interface of its own for MayaRendererProvider to export safely.
#pragma warning(push)
#pragma warning(disable : 4275)
#endif
class MAYAUSD_UI_PUBLIC MayaRendererProvider : public AdskUsdRenderSetup::IRendererProvider
{
public:
//! \return every renderer Maya currently knows about, Hydra-capable or not.
Expand All @@ -34,13 +48,16 @@ class MayaRendererProvider : public AdskUsdRenderSetup::IRendererProvider
std::string currentRenderer() const override;

protected:
//! Switches Maya's current renderer via the setCurrentRenderer MEL proc.
//! Requests a switch to the named renderer. Leaves currentRenderer() unchanged
//! (and thus switchRenderer is implicitly declined) if the name is unknown.
//! For Hydra renderers: writes UsdSettingsNode::currentRenderer.
//! For legacy renderers: writes defaultRenderGlobals.currentRenderer and clears
//! UsdSettingsNode.
void switchRenderer(const std::string& next) override;

private:
//! \return true if `rendererName` reports the "isHydra" capability.
bool isHydraCapable(const std::string& rendererName) const;
};
#if defined(_MSC_VER)
#pragma warning(pop)
#endif

} // namespace MayaUsdRenderSetup

Expand Down
1 change: 1 addition & 0 deletions test/lib/usd/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
add_subdirectory(plugin)
add_subdirectory(pxrUsdPreviewSurface)
add_subdirectory(schemas)
add_subdirectory(ui)
add_subdirectory(utils)
add_subdirectory(translators)
19 changes: 19 additions & 0 deletions test/lib/usd/ui/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#
# Copyright 2026 Autodesk
#
# 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.
#
Comment thread
seando-adsk marked this conversation as resolved.

if (AdskUsdRenderSetup_FOUND)
add_subdirectory(renderSetup)
endif()
Loading
Loading