Skip to content

Commit

Permalink
Merge pull request #2579 from Autodesk/t_gamaj/MAYA-122706/remove_glo…
Browse files Browse the repository at this point in the history
…bal_variable_usage

Fix MaterialX compilation on M1 Macs
  • Loading branch information
seando-adsk authored Sep 7, 2022
2 parents 2e54853 + 6c948fa commit 53e5597
Showing 1 changed file with 72 additions and 45 deletions.
117 changes: 72 additions & 45 deletions lib/mayaUsd/render/MaterialXGenOgsXml/GlslFragmentGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#include <MaterialXGenGlsl/GlslShaderGenerator.h>
#include <MaterialXGenShader/Shader.h>

#include <regex>

MATERIALX_NAMESPACE_BEGIN

namespace Stage {
Expand All @@ -25,6 +27,74 @@ const string LIGHT_LOOP_RESULT = "lightLoopResult";
const string MAYA_ENV_IRRADIANCE_SAMPLE = "diffuseI";
const string MAYA_ENV_RADIANCE_SAMPLE = "specularI";
const string MAYA_ENV_ROUGHNESS = "roughness";

// The Apple shader compiler found on M1/M2 machines does not allow using a global variable as a
// temporary buffer. We will need to handle these inputs differently.
void fixupVertexDataInstance(ShaderStage& stage)
{
// All the strings start with $. This is an anchor character in regex, so skip it.
auto d = [](auto const& s) { return s.substr(1); };

// Find keywords: (as text)
//
// vec3 HW::T_POSITION_WORLD vec3 $normalWorld
//
// And replace with:
//
// vec3 unused_T_POSITION_WORLD vec3 unused_normalWorld
//

static const std::string paramSource = "vec3 [$](" + d(HW::T_POSITION_WORLD) + "|"
+ d(HW::T_POSITION_OBJECT) + "|" + d(HW::T_NORMAL_WORLD) + "|" + d(HW::T_NORMAL_OBJECT)
+ "|" + d(HW::T_TANGENT_WORLD) + "|" + d(HW::T_TANGENT_OBJECT) + "|"
+ d(HW::T_BITANGENT_WORLD) + "|" + d(HW::T_BITANGENT_OBJECT) + ")";

static const std::regex paramRegex(paramSource.c_str());

// Find keywords: (as text)
//
// HW::T_VERTEX_DATA_INSTANCE.HW::T_POSITION_WORLD $vd.$normalWorld
//
// And replace with:
//
// HW::T_POSITION_WORLD(PIX_IN.HW::T_POSITION_WORLD) $normalWorld( PIX_IN.$normalWorld )
//

static const std::string vtxSource = "[$]" + d(HW::T_VERTEX_DATA_INSTANCE) + "[.][$]("
+ d(HW::T_POSITION_WORLD) + "|" + d(HW::T_POSITION_OBJECT) + "|" + d(HW::T_NORMAL_WORLD)
+ "|" + d(HW::T_NORMAL_OBJECT) + "|" + d(HW::T_TANGENT_WORLD) + "|"
+ d(HW::T_TANGENT_OBJECT) + "|" + d(HW::T_BITANGENT_WORLD) + "|" + d(HW::T_BITANGENT_OBJECT)
+ ")";

static const std::regex vtxRegex(vtxSource.c_str());

// Find keywords: (as text)
//
// HW::T_VERTEX_DATA_INSTANCE. $vd.$inGeomprop_st
//
// And replace with:
//
// (nothing) $inGeomprop_st
//

static const std::string vdCleanupSource = "[$]" + d(HW::T_VERTEX_DATA_INSTANCE) + "[.]";

static const std::regex vdCleanupRegex(vdCleanupSource.c_str());

std::string code = stage.getSourceCode();
code = std::regex_replace(code, paramRegex, "vec3 unused_$1");
code = std::regex_replace(code, vtxRegex, "$$$1( PIX_IN.$$$1 )");
code = std::regex_replace(code, vdCleanupRegex, "");

#if MX_COMBINED_VERSION >= 13804
stage.setSourceCode(code);
#else
// setSourceCode was added in 1.38.4. Use const casting in previous versions to modify the
// member variable directly (since getSourceCode returns by reference).
std::string& ncCode(const_cast<std::string&>(stage.getSourceCode()));
ncCode = code;
#endif
}
} // namespace

const string& HwSpecularEnvironmentSamples::name()
Expand Down Expand Up @@ -67,8 +137,6 @@ GlslFragmentGenerator::GlslFragmentGenerator()
_tokenSubstitutions[HW::T_TANGENT_OBJECT] = "Tm";
_tokenSubstitutions[HW::T_BITANGENT_WORLD] = "Bw";
_tokenSubstitutions[HW::T_BITANGENT_OBJECT] = "Bm";
_tokenSubstitutions[HW::T_VERTEX_DATA_INSTANCE]
= "g_mxVertexData"; // name of a global non-const variable
if (OgsXmlGenerator::useLightAPI() >= 2) {
// Use a Maya 2022.1-aware surface node implementation.
registerImplementation(
Expand Down Expand Up @@ -126,34 +194,6 @@ ShaderPtr GlslFragmentGenerator::generate(
ScopedFloatFormatting floatFormatting(Value::FloatFormatFixed);

const VariableBlock& vertexData = pixelStage.getInputBlock(HW::VERTEX_DATA);
if (!vertexData.empty()) {
// GLSL shader input blocks are essentially global constants accessible
// from any function in the shader, not just the entry point. The
// MaterialX GLSL generator makes use of this global access pattern.
// We could make it work for VP2 GLSL fragments by referencing the
// PIX_IN input block generated by VP2 in the final effect but this
// approach has two problems:
// 1. VP2 pre-processes some inputs (e.g. Nw) before passing them as
// arguments to the fragment's root function. These are the correct
// values to use for shading, not the original ones from PIX_IN.
// 2. This pattern is not portable to HLSL where shader stage inputs
// are passed as arguments to the entry point function and have to be
// passed to other functions explicitly.
//
// Since passing vertex inputs to all functions as arguments would be
// too intrusive in GLSL codegen, we instead define a non-const global
// struct variable which gets populated from the function arguments in
// the fragment's root function and is read from in all other functions

emitLine("struct", pixelStage, false);
emitScopeBegin(pixelStage);
emitVariableDeclarations(
vertexData, EMPTY_STRING, Syntax::SEMICOLON, context, pixelStage, false);
emitScopeEnd(pixelStage, false, false);
emitString(" " + HW::T_VERTEX_DATA_INSTANCE, pixelStage);
emitLineEnd(pixelStage, true);
emitLineBreak(pixelStage);
}

// Add global constants and type definitions
const unsigned int maxLights = std::max(1u, context.getOptions().hwMaxActiveLightSources);
Expand Down Expand Up @@ -328,21 +368,6 @@ ShaderPtr GlslFragmentGenerator::generate(
// to a surface shader, so just output black.
emitLine("return vec3(0.0)", pixelStage);
} else {
// Copy vertex data values from the root function's arguments to a
// global struct variable accessible from all other functions.
//
for (size_t i = 0; i < vertexData.size(); ++i) {
const string& name = vertexData[i]->getVariable();

emitLineBegin(pixelStage);
emitString(HW::T_VERTEX_DATA_INSTANCE, pixelStage);
emitString(".", pixelStage);
emitString(name, pixelStage);
emitString(" = ", pixelStage);
emitString(name, pixelStage);
emitLineEnd(pixelStage, true);
}

if (lighting && OgsXmlGenerator::useLightAPI() < 2) {
// Store environment samples from light rig:
emitLine(
Expand Down Expand Up @@ -424,6 +449,8 @@ ShaderPtr GlslFragmentGenerator::generate(
// End function
emitScopeEnd(pixelStage);

fixupVertexDataInstance(pixelStage);

// Replace all tokens with real identifier names
replaceTokens(_tokenSubstitutions, pixelStage);

Expand Down

0 comments on commit 53e5597

Please sign in to comment.