Skip to content

Commit

Permalink
Add Metal shading language compilation to matc (#752)
Browse files Browse the repository at this point in the history
  • Loading branch information
bejado authored Jan 29, 2019
1 parent 836b641 commit 7be3994
Show file tree
Hide file tree
Showing 13 changed files with 296 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ inline constexpr uint8_t getSamplerBindingsStart(driver::Backend api) noexcept {
const uint8_t numUniformBlockBindings = filament::BindingPoints::COUNT;
return numUniformBlockBindings;
}

case driver::Backend::METAL:
// Metal has a separate namespace for uniforms and samplers- collisions aren't an issue.
return 0;
}
}

Expand Down
2 changes: 2 additions & 0 deletions libs/filaflat/include/filaflat/FilaflatDefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ enum UTILS_PUBLIC ChunkType : uint64_t {
MaterialSib = charTo64bitNum("MAT_SIB "),
MaterialGlsl = charTo64bitNum("MAT_GLSL"),
MaterialSpirv = charTo64bitNum("MAT_SPIR"),
MaterialMetal = charTo64bitNum("MAT_METL"),
MaterialShaderModels = charTo64bitNum("MAT_SMDL"),
MaterialSamplerBindings = charTo64bitNum("MAT_SAMP"), // no longer used

Expand Down Expand Up @@ -87,6 +88,7 @@ enum UTILS_PUBLIC ChunkType : uint64_t {

DictionaryGlsl = charTo64bitNum("DIC_GLSL"),
DictionarySpirv = charTo64bitNum("DIC_SPIR"),
DictionaryMetal = charTo64bitNum("DIC_METL")
};

} // namespace filamat
Expand Down
44 changes: 38 additions & 6 deletions libs/filaflat/src/MaterialParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ struct MaterialParserDetails {

bool getGlShader(filament::driver::ShaderModel shaderModel, uint8_t variant,
filament::driver::ShaderType st, ShaderBuilder& shader) noexcept;

bool getMtlShader(filament::driver::ShaderModel shaderModel, uint8_t variant,
filament::driver::ShaderType shaderType, ShaderBuilder& shaderBuilder) noexcept;
};

template<typename T>
Expand Down Expand Up @@ -120,15 +123,16 @@ bool MaterialParser::isShadingMaterial() const noexcept {
cc.hasChunk(MaterialVersion) &&
cc.hasChunk(MaterialUib) &&
cc.hasChunk(MaterialSib) &&
(cc.hasChunk(MaterialGlsl) || cc.hasChunk(MaterialSpirv)) &&
(cc.hasChunk(MaterialGlsl) || cc.hasChunk(MaterialSpirv) || cc.hasChunk(MaterialMetal)) &&
cc.hasChunk(MaterialShaderModels);
}

bool MaterialParser::isPostProcessMaterial() const noexcept {
ChunkContainer const& cc = getChunkContainer();
return cc.hasChunk(PostProcessVersion) &&
((cc.hasChunk(MaterialSpirv) && cc.hasChunk(DictionarySpirv)) ||
(cc.hasChunk(MaterialGlsl) && cc.hasChunk(DictionaryGlsl)));
(cc.hasChunk(MaterialGlsl) && cc.hasChunk(DictionaryGlsl)) ||
(cc.hasChunk(MaterialMetal) && cc.hasChunk(DictionaryMetal)));
}

// Accessors
Expand Down Expand Up @@ -261,9 +265,16 @@ bool MaterialParser::getRequiredAttributes(AttributeBitset* value) const noexcep
bool MaterialParser::getShader(
filament::driver::ShaderModel shaderModel, uint8_t variant, filament::driver::ShaderType st,
ShaderBuilder& shader) noexcept {
return (mImpl->mBackend == filament::driver::Backend::VULKAN) ?
mImpl->getVkShader(shaderModel, variant, st, shader) :
mImpl->getGlShader(shaderModel, variant, st, shader);
if (mImpl->mBackend == filament::driver::Backend::VULKAN) {
return mImpl->getVkShader(shaderModel, variant, st, shader);
}
if (mImpl->mBackend == filament::driver::Backend::OPENGL) {
return mImpl->getGlShader(shaderModel, variant, st, shader);
}
if (mImpl->mBackend == filament::driver::Backend::METAL) {
return mImpl->getMtlShader(shaderModel, variant, st, shader);
}
return false;
}

bool MaterialParserDetails::getVkShader(filament::driver::ShaderModel shaderModel, uint8_t variant,
Expand Down Expand Up @@ -296,7 +307,8 @@ bool MaterialParserDetails::getGlShader(filament::driver::ShaderModel shaderMode

// Read the dictionary only if it has not been read yet.
if (UTILS_UNLIKELY(mBlobDictionary.isEmpty())) {
if (!TextDictionaryReader::unflatten(container, mBlobDictionary)) {
if (!TextDictionaryReader::unflatten(container, mBlobDictionary,
filamat::ChunkType::DictionaryGlsl)) {
return false;
}
}
Expand All @@ -305,4 +317,24 @@ bool MaterialParserDetails::getGlShader(filament::driver::ShaderModel shaderMode
return mMaterialChunk.getTextShader(unflattener, mBlobDictionary, shader, shaderModel, variant, st);
}

bool MaterialParserDetails::getMtlShader(filament::driver::ShaderModel shaderModel, uint8_t variant,
filament::driver::ShaderType st, ShaderBuilder& shader) noexcept {
ChunkContainer const& container = mChunkContainer;
if (!container.hasChunk(ChunkType::MaterialMetal) ||
!container.hasChunk(ChunkType::DictionaryMetal)) {
return false;
}

// Read the dictionary only if it has not been read yet.
if (UTILS_UNLIKELY(mBlobDictionary.isEmpty())) {
if (!TextDictionaryReader::unflatten(container, mBlobDictionary,
filamat::ChunkType::DictionaryMetal)) {
return false;
}
}

Unflattener unflattener(container, ChunkType::MaterialMetal);
return mMaterialChunk.getTextShader(unflattener, mBlobDictionary, shader, shaderModel, variant, st);
}

} // namespace filaflat
5 changes: 3 additions & 2 deletions libs/filaflat/src/TextDictionaryReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ namespace filaflat {
struct TextDictionaryReader {
bool unflatten(Unflattener& unflattener, BlobDictionary& dictionary);

static bool unflatten(ChunkContainer const& container, BlobDictionary& blobDictionary) {
Unflattener dictionaryUnflattener(container, filamat::ChunkType::DictionaryGlsl);
static bool unflatten(ChunkContainer const& container, BlobDictionary& blobDictionary,
filamat::ChunkType chunkType) {
Unflattener dictionaryUnflattener(container, chunkType);
TextDictionaryReader dictionary;
return dictionary.unflatten(dictionaryUnflattener, blobDictionary);
}
Expand Down
1 change: 1 addition & 0 deletions libs/filamat/include/filamat/MaterialBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class UTILS_PUBLIC MaterialBuilderBase {
ALL,
OPENGL,
VULKAN,
METAL
};

enum class Optimization {
Expand Down
23 changes: 22 additions & 1 deletion libs/filamat/src/GLSLPostProcessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <localintermediate.h>

#include <spirv_glsl.hpp>
#include <spirv_msl.hpp>

#include "sca/builtinResource.h"
#include "sca/GLSLTools.h"
Expand Down Expand Up @@ -127,9 +128,17 @@ static std::string shrinkString(const std::string& s) {
return r;
}

void SpvToMsl(const SpirvBlob* spirv, std::string* outMsl) {
CompilerMSL mslCompiler(*spirv);
mslCompiler.set_common_options(CompilerGLSL::Options {
.vertex.fixup_clipspace = true
});
*outMsl = mslCompiler.compile();
}

bool GLSLPostProcessor::process(const std::string& inputShader,
filament::driver::ShaderType shaderType, filament::driver::ShaderModel shaderModel,
std::string* outputGlsl, SpirvBlob* outputSpirv) {
std::string* outputGlsl, SpirvBlob* outputSpirv, std::string* outputMsl) {

// If TargetApi is Vulkan, then we need post-processing even if there's no optimization.
using TargetApi = MaterialBuilder::TargetApi;
Expand All @@ -144,6 +153,7 @@ bool GLSLPostProcessor::process(const std::string& inputShader,

mGlslOutput = outputGlsl;
mSpirvOutput = outputSpirv;
mMslOutput = outputMsl;

if (shaderType == filament::driver::VERTEX) {
mShLang = EShLangVertex;
Expand Down Expand Up @@ -182,6 +192,9 @@ bool GLSLPostProcessor::process(const std::string& inputShader,
case MaterialBuilder::Optimization::NONE:
if (mSpirvOutput) {
GlslangToSpv(*program.getIntermediate(mShLang), *mSpirvOutput);
if (mMslOutput) {
SpvToMsl(mSpirvOutput, mMslOutput);
}
} else {
utils::slog.e << "GLSL post-processor invoked with optimization level NONE"
<< utils::io::endl;
Expand Down Expand Up @@ -240,6 +253,10 @@ void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader,
}
}

if (mMslOutput) {
SpvToMsl(mSpirvOutput, mMslOutput);
}

if (mGlslOutput) {
*mGlslOutput = glsl;
}
Expand Down Expand Up @@ -280,6 +297,10 @@ void GLSLPostProcessor::fullOptimization(const TShader& tShader,
*mSpirvOutput = spirv;
}

if (mMslOutput) {
SpvToMsl(mSpirvOutput, mMslOutput);
}

// Transpile back to GLSL
if (mGlslOutput) {
CompilerGLSL::Options glslOptions;
Expand Down
8 changes: 5 additions & 3 deletions libs/filamat/src/GLSLPostProcessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,20 @@

namespace filamat {

using SpirvBlob = std::vector<uint32_t>;

class GLSLPostProcessor {
public:
GLSLPostProcessor(MaterialBuilder::Optimization optimization, bool printShaders);

~GLSLPostProcessor();

using SpirvBlob = std::vector<uint32_t>;

bool process(const std::string& inputShader, filament::driver::ShaderType shaderType,
filament::driver::ShaderModel shaderModel, std::string* outputGlsl,
SpirvBlob* outputSpirv);
SpirvBlob* outputSpirv, std::string* outputMsl);

private:

void fullOptimization(const glslang::TShader& tShader,
filament::driver::ShaderModel shaderModel) const;
void preprocessOptimization(glslang::TShader& tShader,
Expand All @@ -55,6 +56,7 @@ class GLSLPostProcessor {
const bool mPrintShaders;
std::string* mGlslOutput = nullptr;
SpirvBlob* mSpirvOutput = nullptr;
std::string* mMslOutput = nullptr;
EShLanguage mShLang = EShLangFragment;
int mLangVersion = 0;
};
Expand Down
57 changes: 53 additions & 4 deletions libs/filamat/src/MaterialBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,15 @@ void MaterialBuilderBase::prepare() {
case TargetApi::ALL:
mCodeGenPermutations.push_back({i, TargetApi::OPENGL, glCodeGenTargetApi});
mCodeGenPermutations.push_back({i, TargetApi::VULKAN, TargetApi::VULKAN});
mCodeGenPermutations.push_back({i, TargetApi::METAL, TargetApi::VULKAN});
break;
case TargetApi::OPENGL:
mCodeGenPermutations.push_back({i, TargetApi::OPENGL, glCodeGenTargetApi});
break;
case TargetApi::VULKAN:
mCodeGenPermutations.push_back({i, TargetApi::VULKAN, TargetApi::VULKAN});
case TargetApi::METAL:
mCodeGenPermutations.push_back({i, TargetApi::METAL, TargetApi::VULKAN});
break;
}
}
Expand Down Expand Up @@ -453,9 +456,12 @@ Package MaterialBuilder::build() noexcept {
// Generate all shaders.
std::vector<TextEntry> glslEntries;
std::vector<SpirvEntry> spirvEntries;
std::vector<TextEntry> metalEntries;
LineDictionary glslDictionary;
BlobDictionary spirvDictionary;
LineDictionary metalDictionary;
std::vector<uint32_t> spirv;
std::string msl;

ShaderGenerator sg(mProperties, mVariables,
mMaterialCode, mMaterialLineOffset, mMaterialVertexCode, mMaterialVertexLineOffset);
Expand All @@ -470,7 +476,6 @@ Package MaterialBuilder::build() noexcept {
const ShaderModel shaderModel = ShaderModel(params.shaderModel);
const TargetApi targetApi = params.targetApi;
const TargetApi codeGenTargetApi = params.codeGenTargetApi;
std::vector<uint32_t>* pSpirv = (targetApi == TargetApi::VULKAN) ? &spirv : nullptr;

// Re-populate the set of sampler bindings for this API.
filament::SamplerBindingMap map;
Expand All @@ -479,11 +484,20 @@ Package MaterialBuilder::build() noexcept {
map.populate(offset, &info.sib, mMaterialName.c_str());
info.samplerBindings = std::move(map);

// Metal Shading Language is cross-compiled from Vulkan.
const bool targetApiNeedsSpirv =
(targetApi == TargetApi::VULKAN || targetApi == TargetApi::METAL);
const bool targetApiNeedsMsl = targetApi == TargetApi::METAL;
std::vector<uint32_t>* pSpirv = targetApiNeedsSpirv ? &spirv : nullptr;
std::string* pMsl = targetApiNeedsMsl ? &msl : nullptr;

TextEntry glslEntry{0};
SpirvEntry spirvEntry{0};
TextEntry metalEntry{0};

glslEntry.shaderModel = static_cast<uint8_t>(params.shaderModel);
spirvEntry.shaderModel = static_cast<uint8_t>(params.shaderModel);
metalEntry.shaderModel = static_cast<uint8_t>(params.shaderModel);

// apply custom variants filters
uint8_t variantMask = ~mVariantFilter;
Expand All @@ -496,6 +510,7 @@ Package MaterialBuilder::build() noexcept {

glslEntry.variant = k;
spirvEntry.variant = k;
metalEntry.variant = k;

// Remove variants for unlit materials
uint8_t v = filament::Variant::filterVariant(
Expand All @@ -506,9 +521,8 @@ Package MaterialBuilder::build() noexcept {
std::string vs = sg.createVertexProgram(
shaderModel, targetApi, codeGenTargetApi, info, k,
mInterpolation, mVertexDomain);

bool ok = postProcessor.process(vs, filament::driver::ShaderType::VERTEX,
shaderModel, &vs, pSpirv);
shaderModel, &vs, pSpirv, pMsl);
if (!ok) {
showErrorMessage(mMaterialName.c_str_safe(), k, targetApi,
filament::driver::ShaderType::VERTEX, vs);
Expand Down Expand Up @@ -536,6 +550,18 @@ Package MaterialBuilder::build() noexcept {
spirv.clear();
spirvEntries.push_back(spirvEntry);
}
if (targetApi == TargetApi::METAL) {
assert(spirv.size() > 0);
assert(msl.length() > 0);
metalEntry.stage = filament::driver::ShaderType::VERTEX;
metalEntry.shaderSize = msl.length();
metalEntry.shader = (char*)malloc(metalEntry.shaderSize + 1);
strcpy(metalEntry.shader, msl.c_str());
spirv.clear();
msl.clear();
metalDictionary.addText(metalEntry.shader);
metalEntries.push_back(metalEntry);
}
}

if (filament::Variant::filterVariantFragment(v) == k) {
Expand All @@ -544,7 +570,7 @@ Package MaterialBuilder::build() noexcept {
shaderModel, targetApi, codeGenTargetApi, info, k, mInterpolation);

bool ok = postProcessor.process(fs, filament::driver::ShaderType::FRAGMENT,
shaderModel, &fs, pSpirv);
shaderModel, &fs, pSpirv, pMsl);
if (!ok) {
showErrorMessage(mMaterialName.c_str_safe(), k, targetApi,
filament::driver::ShaderType::FRAGMENT, fs);
Expand Down Expand Up @@ -572,6 +598,18 @@ Package MaterialBuilder::build() noexcept {
spirv.clear();
spirvEntries.push_back(spirvEntry);
}
if (targetApi == TargetApi::METAL) {
assert(spirv.size() > 0);
assert(msl.length() > 0);
metalEntry.stage = filament::driver::ShaderType::FRAGMENT;
metalEntry.shaderSize = msl.length();
metalEntry.shader = (char*)malloc(metalEntry.shaderSize + 1);
strcpy(metalEntry.shader, msl.c_str());
spirv.clear();
msl.clear();
metalDictionary.addText(metalEntry.shader);
metalEntries.push_back(metalEntry);
}
}
}
}
Expand All @@ -592,6 +630,14 @@ Package MaterialBuilder::build() noexcept {
container.addChild(&spirvChunk);
}

// Emit Metal chunks (MetalDictionaryReader and MaterialMetalChunk).
filamat::DictionaryTextChunk dicMetalChunk(metalDictionary, ChunkType::DictionaryMetal);
MaterialTextChunk metalChunk(metalEntries, metalDictionary, ChunkType::MaterialMetal);
if (!metalEntries.empty()) {
container.addChild(&dicMetalChunk);
container.addChild(&metalChunk);
}

// Flatten all chunks in the container into a Package.
size_t packageSize = container.getSize();
Package package(packageSize);
Expand All @@ -603,6 +649,9 @@ Package MaterialBuilder::build() noexcept {
for (TextEntry entry : glslEntries) {
free(entry.shader);
}
for (TextEntry entry : metalEntries) {
free(entry.shader);
}
return package;
}

Expand Down
Loading

0 comments on commit 7be3994

Please sign in to comment.