Skip to content

Commit ffba80b

Browse files
hodoulpMorteezaIngthor Hjalmarsson
authored
Adds Metal Shading Language (MSL) Generation support (#1520) (#1557)
* * Adds Metal Shading Language (MSL) Generation support Signed-off-by: Morteza Mostajabodaveh <[email protected]> Co-authored-by: Ingthor Hjalmarsson <[email protected]> Signed-off-by: Morteza Mostajab <[email protected]> * * Fixes warnings triggered due to unused variables. Signed-off-by: Morteza Mostajabodaveh <[email protected]> Signed-off-by: Morteza Mostajab <[email protected]> * * Fixes compilation error on Mac and Windows: - vector of const objects causing compiler errors on windows. - Wrong string was used for Metal language python binding. Signed-off-by: Morteza Mostajab <[email protected]> * * Removes class wrapping interface from OpenColorIO interface and move it to implementation * Adding more tests for metal code path * Proper generated Metal code indentation * Fixes a few coding style inconsistencies and unneeded include files Signed-off-by: Morteza Mostajab <[email protected]> * Remove the unneeded empty line to reset OpenColorIO.h to what it was before the metal change. Signed-off-by: Morteza Mostajab <[email protected]> * * switching from c-like getFunctionParameters function to c++-like one since it is not in the opencolorio interface anymore. Signed-off-by: Morteza Mostajab <[email protected]> * Missing include causing compiler errors on windows Signed-off-by: Morteza Mostajab <[email protected]> * * Fixes and improvements follow up to change that was trying to move metal related code in implementation, and hide them from OCIO interface. Signed-off-by: Morteza Mostajab <[email protected]> * - remove unused variables. Signed-off-by: Morteza Mostajab <[email protected]> * * Adds support for uniform parameters and proper handling of them in metal code. * Adds two new tests for uniforms and helper functions. Signed-off-by: Morteza Mostajab <[email protected]> * * making declaration parsing more C++-like. Functions like sscanf are problematic when it comes to multiple platform support and may not be safe Signed-off-by: Morteza Mostajab <[email protected]> * * Switching from clf/lut1d_half_domain_raw_half_set.clf to clf/lut1d_long.clf. Due to decimal number outputting difference between different platforms, some tests were failing. Signed-off-by: Morteza Mostajab <[email protected]> * * Moving Metal only functionalities to MetalClassWrappingInterface * remove unnecessary functions and data types * code clean up and quality improvement Signed-off-by: Morteza Mostajab <[email protected]> * * Adds support for uniforms that are array * Removing getTextureKeyword() and getTextureDeclaration() as they are not needed. * code clean up and quality improvement Signed-off-by: Morteza Mostajab <[email protected]> * Remove unnecessary changes to GpuShaderUtils_tests.cpp Signed-off-by: Morteza Mostajab <[email protected]> * * Removed unnecessary included files Signed-off-by: Morteza Mostajab <[email protected]> * Removing unnecessary include Signed-off-by: Morteza Mostajab <[email protected]> * * Apply style improvement suggestions Signed-off-by: Morteza Mostajab <[email protected]> * * changed from msl_metal to msl_2 so it matches with `GpuLanguageFromString` function. Signed-off-by: Morteza Mostajab <[email protected]> * Move MetalShaderClassWrappingInterface to a separate file and generalise it Signed-off-by: Morteza Mostajab <[email protected]> * header files inlcudes clean up Signed-off-by: Morteza Mostajab <[email protected]> * Still the else path is needed in case we set language from MSL2 to something else. Signed-off-by: Morteza Mostajab <[email protected]> * [minor] Showing the GpuShaderClassWrapper.h also in the vc15 aftereffect project. Signed-off-by: Morteza Mostajab <[email protected]> * Run ClassWrapper function for all backends. It only produces code in metal shading language code generation case. operator= for GpuShaderClassWrapper Signed-off-by: Morteza Mostajab <[email protected]> * use factory pattern instead of updateClassWrappingInterface Signed-off-by: Morteza Mostajab <[email protected]> * Replace assignment operator with clone function that is more explicit. Signed-off-by: Morteza Mostajab <[email protected]> * use Assignment operator instead of setting members in clone member function Signed-off-by: Morteza Mostajab <[email protected]> * add include guards. Signed-off-by: Morteza Mostajab <[email protected]> * make output of MetalShaderClassWrapper::operator= non-const Signed-off-by: Morteza Mostajab <[email protected]> Co-authored-by: Ingthor Hjalmarsson <[email protected]> Co-authored-by: Patrick Hodoul <[email protected]> Signed-off-by: Patrick Hodoul <[email protected]> Co-authored-by: Morteza Mostajab <[email protected]> Co-authored-by: Ingthor Hjalmarsson <[email protected]>
1 parent 0c2aacb commit ffba80b

File tree

13 files changed

+1505
-24
lines changed

13 files changed

+1505
-24
lines changed

include/OpenColorIO/OpenColorTypes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,7 @@ enum GpuLanguage
449449
LANGUAGE_OSL_1, ///< Open Shading Language
450450
GPU_LANGUAGE_GLSL_ES_1_0, ///< OpenGL ES Shading Language
451451
GPU_LANGUAGE_GLSL_ES_3_0, ///< OpenGL ES Shading Language
452+
GPU_LANGUAGE_MSL_2_0 ///< Metal Shading Language
452453
};
453454

454455
enum EnvironmentMode

src/OpenColorIO/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ set(SOURCES
5454
GPUProcessor.cpp
5555
GpuShader.cpp
5656
GpuShaderDesc.cpp
57+
GpuShaderClassWrapper.cpp
5758
GpuShaderUtils.cpp
5859
HashUtils.cpp
5960
ImageDesc.cpp
Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
// Copyright Contributors to the OpenColorIO Project.
3+
4+
#include <OpenColorIO/OpenColorIO.h>
5+
6+
#include "GpuShaderClassWrapper.h"
7+
8+
namespace OCIO_NAMESPACE
9+
{
10+
11+
std::string MetalShaderClassWrapper::generateClassWrapperHeader(GpuShaderText& kw) const
12+
{
13+
if(m_className.empty())
14+
{
15+
throw Exception("Struct name must include at least 1 character");
16+
}
17+
if(std::isdigit(m_className[0]))
18+
{
19+
throw Exception(("Struct name must not start with a digit. Invalid className passed in: " + m_className).c_str());
20+
}
21+
22+
kw.newLine() << "struct " << m_className;
23+
kw.newLine() << "{";
24+
kw.newLine() << m_className <<"(";
25+
kw.indent();
26+
27+
std::string separator = "";
28+
for(const auto& param : m_functionParameters)
29+
{
30+
kw.newLine() << separator << param.type << " " << param.name;
31+
separator = ", ";
32+
}
33+
kw.dedent();
34+
kw.newLine() << ")";
35+
kw.newLine() << "{";
36+
37+
kw.indent();
38+
for(const auto& param : m_functionParameters)
39+
{
40+
size_t openAngledBracketPos = param.name.find('[');
41+
bool isArray = openAngledBracketPos != std::string::npos;
42+
if(!isArray)
43+
kw.newLine() << "this->" << param.name << " = " << param.name << ";";
44+
else
45+
{
46+
size_t closeAngledBracketPos = param.name.find(']');
47+
std::string variableName = param.name.substr(0, openAngledBracketPos);
48+
49+
kw.newLine() << "for(int i = 0; i < "
50+
<< param.name.substr(openAngledBracketPos+1, closeAngledBracketPos-openAngledBracketPos-1)
51+
<< "; ++i)";
52+
kw.indent();
53+
kw.newLine() << "this->" << variableName << "[i] = " << variableName << "[i];";
54+
kw.dedent();
55+
}
56+
}
57+
kw.dedent();
58+
kw.newLine() <<"}";
59+
return kw.string();
60+
}
61+
62+
std::string MetalShaderClassWrapper::generateClassWrapperFooter(GpuShaderText& kw, const std::string &ocioFunctionName) const
63+
{
64+
if(m_className.empty())
65+
{
66+
throw Exception("Struct name must include at least 1 character");
67+
}
68+
if(std::isdigit(m_className[0]))
69+
{
70+
throw Exception(("Struct name must not start with a digit. Invalid className passed in: " + m_className).c_str());
71+
}
72+
73+
kw.newLine() << "};";
74+
75+
kw.newLine() << kw.float4Keyword() << " " << ocioFunctionName<< "(";
76+
std::string texParamOut;
77+
78+
kw.indent();
79+
std::string separator = "";
80+
for(const auto& param : m_functionParameters)
81+
{
82+
kw.newLine() << separator << param.type << " " << param.name;
83+
separator = ", ";
84+
}
85+
kw.newLine() << separator << kw.float4Keyword() << " inPixel)";
86+
kw.dedent();
87+
kw.newLine() << "{";
88+
kw.indent();
89+
kw.newLine() << "return " << m_className << "(";
90+
91+
kw.indent();
92+
separator = "";
93+
for(const auto& param : m_functionParameters)
94+
{
95+
size_t openAngledBracketPos = param.name.find('[');
96+
bool isArray = openAngledBracketPos != std::string::npos;
97+
98+
if(!isArray)
99+
kw.newLine() << separator << param.name;
100+
else
101+
kw.newLine() << separator << param.name.substr(0, openAngledBracketPos);
102+
separator = ", ";
103+
}
104+
kw.dedent();
105+
106+
kw.newLine() << ")." << ocioFunctionName << "(inPixel);";
107+
kw.dedent();
108+
kw.newLine() << "}";
109+
110+
return kw.string();
111+
}
112+
113+
std::string MetalShaderClassWrapper::getClassWrapperName(const std::string &resourcePrefix, const std::string& functionName)
114+
{
115+
return (resourcePrefix.length() == 0 ? "OCIO_" : resourcePrefix) + functionName;
116+
}
117+
118+
void MetalShaderClassWrapper::extractFunctionParameters(const std::string& declaration)
119+
{
120+
// We want the caller to always pass 3d luts first with their samplers, and then pass other luts.
121+
std::vector<std::tuple<std::string, std::string, std::string>> lut3DTextures;
122+
std::vector<std::tuple<std::string, std::string, std::string>> lutTextures;
123+
std::vector<std::pair <std::string, std::string >> uniforms;
124+
125+
m_functionParameters.clear();
126+
127+
std::string lineBuffer;
128+
129+
std::istringstream is(declaration);
130+
while(!is.eof())
131+
{
132+
std::getline(is, lineBuffer);
133+
134+
if(lineBuffer.empty())
135+
continue;
136+
137+
size_t i = 0;
138+
139+
// Skip spaces
140+
while(std::isspace(lineBuffer[i])) ++i;
141+
142+
// if the line was all skippable characters
143+
if(i >= lineBuffer.size())
144+
continue;
145+
146+
// if the line is a comment
147+
if(lineBuffer[i + 0] == '/' && lineBuffer[i + 1] == '/')
148+
continue;
149+
150+
if(lineBuffer.compare(i, 7, "texture") == 0)
151+
{
152+
int textureDim = static_cast<int>(lineBuffer[i+7] - '0');
153+
154+
size_t endTextureType = lineBuffer.find('>');
155+
std::string textureType = lineBuffer.substr(i, (endTextureType - i + 1));
156+
157+
i = endTextureType + 1;
158+
while(std::isspace(lineBuffer[i])) ++i;
159+
160+
size_t endTextureName = lineBuffer.find_first_of(" \t;", i);
161+
std::string textureName = lineBuffer.substr(i, (endTextureName - i));
162+
163+
std::getline(is, lineBuffer);
164+
165+
i = lineBuffer.find("sampler") + 7;
166+
while(std::isspace(lineBuffer[i])) ++i;
167+
size_t endSamplerName = lineBuffer.find_first_of(" \t;", i);
168+
std::string samplerName = lineBuffer.substr(i, endSamplerName - i);
169+
170+
if(textureDim == 3)
171+
lut3DTextures.emplace_back(textureType, textureName, samplerName);
172+
else
173+
lutTextures.emplace_back(textureType, textureName, samplerName);
174+
}
175+
else
176+
{
177+
size_t endTypeName = lineBuffer.find_first_of(" \t", i);
178+
std::string variableType = lineBuffer.substr(i, (endTypeName - i));
179+
180+
i = endTypeName + 1;
181+
while(std::isspace(lineBuffer[i])) ++i;
182+
183+
size_t endVariableName = lineBuffer.find_first_of(" \t;", i);
184+
std::string variableName = lineBuffer.substr(i, (endVariableName - i));
185+
uniforms.emplace_back(variableType, variableName);
186+
}
187+
}
188+
189+
for(const auto& lut3D : lut3DTextures)
190+
{
191+
m_functionParameters.emplace_back(std::get<0>(lut3D).c_str(), std::get<1>(lut3D).c_str());
192+
m_functionParameters.emplace_back("sampler", std::get<2>(lut3D).c_str());
193+
}
194+
195+
for(const auto& lut : lutTextures)
196+
{
197+
m_functionParameters.emplace_back(std::get<0>(lut).c_str(), std::get<1>(lut).c_str());
198+
m_functionParameters.emplace_back("sampler", std::get<2>(lut).c_str());
199+
}
200+
201+
for(const auto& uniform : uniforms)
202+
{
203+
m_functionParameters.emplace_back(uniform.first, uniform.second);
204+
}
205+
}
206+
207+
void MetalShaderClassWrapper::prepareClassWrapper(const std::string& resourcePrefix, const std::string& functionName, const std::string& originalHeader)
208+
{
209+
m_functionName = functionName;
210+
m_className = getClassWrapperName(resourcePrefix, functionName);
211+
extractFunctionParameters(originalHeader);
212+
}
213+
214+
std::string MetalShaderClassWrapper::getClassWrapperHeader(const std::string& originalHeader)
215+
{
216+
GpuShaderText st(GPU_LANGUAGE_MSL_2_0);
217+
218+
generateClassWrapperHeader(st);
219+
st.newLine();
220+
221+
std::string classWrapHeader = "\n// Declaration of class wrapper\n\n";
222+
classWrapHeader += st.string();
223+
224+
return classWrapHeader + originalHeader;
225+
}
226+
227+
std::string MetalShaderClassWrapper::getClassWrapperFooter(const std::string& originalFooter)
228+
{
229+
GpuShaderText st(GPU_LANGUAGE_MSL_2_0);
230+
231+
st.newLine();
232+
generateClassWrapperFooter(st, m_functionName);
233+
234+
std::string classWrapFooter = "\n// Close class wrapper\n\n";
235+
classWrapFooter += st.string();
236+
237+
return originalFooter + classWrapFooter;
238+
}
239+
240+
std::unique_ptr<GpuShaderClassWrapper> MetalShaderClassWrapper::clone() const
241+
{
242+
std::unique_ptr<MetalShaderClassWrapper> clonedWrapper = std::unique_ptr<MetalShaderClassWrapper>(new MetalShaderClassWrapper);
243+
*clonedWrapper = *this;
244+
return clonedWrapper;
245+
}
246+
247+
MetalShaderClassWrapper& MetalShaderClassWrapper::operator=(const MetalShaderClassWrapper& rhs)
248+
{
249+
this->m_className = rhs.m_className;
250+
this->m_functionName = rhs.m_functionName;
251+
this->m_functionParameters = rhs.m_functionParameters;
252+
return *this;
253+
}
254+
255+
} // namespace OCIO_NAMESPACE
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
// Copyright Contributors to the OpenColorIO Project.
3+
4+
#ifndef INCLUDED_OCIO_GPUSHADERCLASSWRAPPER_H
5+
#define INCLUDED_OCIO_GPUSHADERCLASSWRAPPER_H
6+
7+
#include <sstream>
8+
#include <utility>
9+
#include <vector>
10+
#include <memory>
11+
12+
#include <OpenColorIO/OpenColorIO.h>
13+
14+
#include "GpuShaderUtils.h"
15+
16+
namespace OCIO_NAMESPACE
17+
{
18+
19+
class GpuShaderClassWrapper
20+
{
21+
public:
22+
virtual void prepareClassWrapper(const std::string& resourcePrefix,
23+
const std::string& functionName,
24+
const std::string& originalHeader) = 0;
25+
virtual std::string getClassWrapperHeader(const std::string& originalHeader) = 0;
26+
virtual std::string getClassWrapperFooter(const std::string& originalFooter) = 0;
27+
28+
virtual std::unique_ptr<GpuShaderClassWrapper> clone() const = 0;
29+
30+
virtual ~GpuShaderClassWrapper() = default;
31+
};
32+
33+
class NullGpuShaderClassWrapper : public GpuShaderClassWrapper
34+
{
35+
public:
36+
void prepareClassWrapper(const std::string& /*resourcePrefix*/,
37+
const std::string& /*functionName*/,
38+
const std::string& /*originalHeader*/) final {}
39+
std::string getClassWrapperHeader(const std::string& originalHeader) final { return originalHeader; }
40+
std::string getClassWrapperFooter(const std::string& originalFooter) final { return originalFooter; }
41+
42+
std::unique_ptr<GpuShaderClassWrapper> clone() const final
43+
{
44+
return std::unique_ptr<NullGpuShaderClassWrapper>(new NullGpuShaderClassWrapper());
45+
}
46+
};
47+
48+
class MetalShaderClassWrapper : public GpuShaderClassWrapper
49+
{
50+
public:
51+
void prepareClassWrapper(const std::string& resourcePrefix,
52+
const std::string& functionName,
53+
const std::string& originalHeader) final;
54+
std::string getClassWrapperHeader(const std::string& originalHeader) final;
55+
std::string getClassWrapperFooter(const std::string& originalFooter) final;
56+
57+
std::unique_ptr<GpuShaderClassWrapper> clone() const final;
58+
MetalShaderClassWrapper& operator=(const MetalShaderClassWrapper& rhs);
59+
60+
private:
61+
struct FunctionParam
62+
{
63+
FunctionParam(const std::string& type, const std::string& name) :
64+
type(type),
65+
name(name)
66+
{
67+
}
68+
69+
std::string type;
70+
std::string name;
71+
};
72+
73+
std::string getClassWrapperName(const std::string &resourcePrefix, const std::string& functionName);
74+
void extractFunctionParameters(const std::string& declaration);
75+
std::string generateClassWrapperHeader(GpuShaderText& st) const;
76+
std::string generateClassWrapperFooter(GpuShaderText& st, const std::string &ocioFunctionName) const;
77+
78+
std::string m_className;
79+
std::string m_functionName;
80+
std::vector<FunctionParam> m_functionParameters;
81+
};
82+
83+
} // namespace OCIO_NAMESPACE
84+
85+
#endif

0 commit comments

Comments
 (0)