From cddd22da28d0bb81d60eb248a12970f1b6e711b4 Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Fri, 12 Apr 2024 00:42:10 -0400 Subject: [PATCH] Introduce a new precompiled Metal material chunk type (#7749) This change introduces a new chunk type to material files for precompiled Metal libraries. Previously, SPIR-V was the only binary type, so there's also a couple of refactor commits present here. Nothing is changed in Filament or matc yet. BUGS=[333547148] --- .../backend/include/backend/DriverEnums.h | 8 ++- filament/src/MaterialParser.cpp | 4 ++ .../include/filament/MaterialChunkType.h | 2 + .../filaflat/include/filaflat/MaterialChunk.h | 2 +- libs/filaflat/src/DictionaryReader.cpp | 19 +++++ libs/filaflat/src/MaterialChunk.cpp | 5 +- libs/filamat/CMakeLists.txt | 6 +- libs/filamat/src/MaterialBuilder.cpp | 15 ++-- .../src/eiff/DictionaryMetalLibraryChunk.cpp | 33 +++++++++ .../src/eiff/DictionaryMetalLibraryChunk.h | 43 ++++++++++++ ...SpirvChunk.cpp => MaterialBinaryChunk.cpp} | 13 ++-- ...rialSpirvChunk.h => MaterialBinaryChunk.h} | 14 ++-- libs/filamat/src/eiff/ShaderEntry.h | 8 +-- libs/matdbg/src/ShaderExtractor.cpp | 4 ++ libs/matdbg/src/ShaderReplacer.cpp | 10 +-- libs/matdbg/src/TextWriter.cpp | 6 ++ tools/matinfo/src/main.cpp | 70 +++++++++++++------ 17 files changed, 206 insertions(+), 56 deletions(-) create mode 100644 libs/filamat/src/eiff/DictionaryMetalLibraryChunk.cpp create mode 100644 libs/filamat/src/eiff/DictionaryMetalLibraryChunk.h rename libs/filamat/src/eiff/{MaterialSpirvChunk.cpp => MaterialBinaryChunk.cpp} (75%) rename libs/filamat/src/eiff/{MaterialSpirvChunk.h => MaterialBinaryChunk.h} (68%) diff --git a/filament/backend/include/backend/DriverEnums.h b/filament/backend/include/backend/DriverEnums.h index 6b0424ca06..7a9f94169b 100644 --- a/filament/backend/include/backend/DriverEnums.h +++ b/filament/backend/include/backend/DriverEnums.h @@ -158,14 +158,16 @@ static constexpr const char* backendToString(Backend backend) { } /** - * Defines the shader language. Similar to the above backend enum, but the OpenGL backend can select - * between two shader languages: ESSL 1.0 and ESSL 3.0. + * Defines the shader language. Similar to the above backend enum, with some differences: + * - The OpenGL backend can select between two shader languages: ESSL 1.0 and ESSL 3.0. + * - The Metal backend can prefer precompiled Metal libraries, while falling back to MSL. */ enum class ShaderLanguage { ESSL1 = 0, ESSL3 = 1, SPIRV = 2, MSL = 3, + METAL_LIBRARY = 4, }; static constexpr const char* shaderLanguageToString(ShaderLanguage shaderLanguage) { @@ -178,6 +180,8 @@ static constexpr const char* shaderLanguageToString(ShaderLanguage shaderLanguag return "SPIR-V"; case ShaderLanguage::MSL: return "MSL"; + case ShaderLanguage::METAL_LIBRARY: + return "Metal precompiled library"; } } diff --git a/filament/src/MaterialParser.cpp b/filament/src/MaterialParser.cpp index 5427835a28..d0992ec4eb 100644 --- a/filament/src/MaterialParser.cpp +++ b/filament/src/MaterialParser.cpp @@ -65,6 +65,10 @@ MaterialParser::MaterialParserDetails::MaterialParserDetails(ShaderLanguage lang mMaterialTag = ChunkType::MaterialSpirv; mDictionaryTag = ChunkType::DictionarySpirv; break; + case ShaderLanguage::METAL_LIBRARY: + mMaterialTag = ChunkType::MaterialMetalLibrary; + mDictionaryTag = ChunkType::DictionaryMetalLibrary; + break; } } diff --git a/libs/filabridge/include/filament/MaterialChunkType.h b/libs/filabridge/include/filament/MaterialChunkType.h index 27d50c1683..c80ac7d8c9 100644 --- a/libs/filabridge/include/filament/MaterialChunkType.h +++ b/libs/filabridge/include/filament/MaterialChunkType.h @@ -45,6 +45,7 @@ enum UTILS_PUBLIC ChunkType : uint64_t { MaterialEssl1 = charTo64bitNum("MAT_ESS1"), MaterialSpirv = charTo64bitNum("MAT_SPIR"), MaterialMetal = charTo64bitNum("MAT_METL"), + MaterialMetalLibrary = charTo64bitNum("MAT_MLIB"), MaterialShaderModels = charTo64bitNum("MAT_SMDL"), MaterialSamplerBindings = charTo64bitNum("MAT_SAMP"), MaterialUniformBindings = charTo64bitNum("MAT_UNIF"), @@ -93,6 +94,7 @@ enum UTILS_PUBLIC ChunkType : uint64_t { DictionaryText = charTo64bitNum("DIC_TEXT"), DictionarySpirv = charTo64bitNum("DIC_SPIR"), + DictionaryMetalLibrary = charTo64bitNum("DIC_MLIB"), }; } // namespace filamat diff --git a/libs/filaflat/include/filaflat/MaterialChunk.h b/libs/filaflat/include/filaflat/MaterialChunk.h index 2b7ceda7fa..304819769e 100644 --- a/libs/filaflat/include/filaflat/MaterialChunk.h +++ b/libs/filaflat/include/filaflat/MaterialChunk.h @@ -74,7 +74,7 @@ private: BlobDictionary const& dictionary, ShaderContent& shaderContent, ShaderModel shaderModel, filament::Variant variant, ShaderStage shaderStage); - bool getSpirvShader( + bool getBinaryShader( BlobDictionary const& dictionary, ShaderContent& shaderContent, ShaderModel shaderModel, filament::Variant variant, ShaderStage shaderStage); }; diff --git a/libs/filaflat/src/DictionaryReader.cpp b/libs/filaflat/src/DictionaryReader.cpp index 87c8d9a2e0..6a84ca34b6 100644 --- a/libs/filaflat/src/DictionaryReader.cpp +++ b/libs/filaflat/src/DictionaryReader.cpp @@ -78,6 +78,25 @@ bool DictionaryReader::unflatten(ChunkContainer const& container, } return true; + } else if (dictionaryTag == ChunkType::DictionaryMetalLibrary) { + uint32_t blobCount; + if (!unflattener.read(&blobCount)) { + return false; + } + + dictionary.reserve(blobCount); + for (uint32_t i = 0; i < blobCount; i++) { + unflattener.skipAlignmentPadding(); + + const char* data; + size_t dataSize; + if (!unflattener.read(&data, &dataSize)) { + return false; + } + dictionary.emplace_back(dataSize); + memcpy(dictionary.back().data(), data, dictionary.back().size()); + } + return true; } else if (dictionaryTag == ChunkType::DictionaryText) { uint32_t stringCount = 0; if (!unflattener.read(&stringCount)) { diff --git a/libs/filaflat/src/MaterialChunk.cpp b/libs/filaflat/src/MaterialChunk.cpp index a7798aa6e0..b69af9abd0 100644 --- a/libs/filaflat/src/MaterialChunk.cpp +++ b/libs/filaflat/src/MaterialChunk.cpp @@ -153,7 +153,7 @@ bool MaterialChunk::getTextShader(Unflattener unflattener, return true; } -bool MaterialChunk::getSpirvShader(BlobDictionary const& dictionary, +bool MaterialChunk::getBinaryShader(BlobDictionary const& dictionary, ShaderContent& shaderContent, ShaderModel shaderModel, filament::Variant variant, ShaderStage shaderStage) { if (mBase == nullptr) { @@ -186,7 +186,8 @@ bool MaterialChunk::getShader(ShaderContent& shaderContent, BlobDictionary const case filamat::ChunkType::MaterialMetal: return getTextShader(mUnflattener, dictionary, shaderContent, shaderModel, variant, stage); case filamat::ChunkType::MaterialSpirv: - return getSpirvShader(dictionary, shaderContent, shaderModel, variant, stage); + case filamat::ChunkType::MaterialMetalLibrary: + return getBinaryShader(dictionary, shaderContent, shaderModel, variant, stage); default: return false; } diff --git a/libs/filamat/CMakeLists.txt b/libs/filamat/CMakeLists.txt index cf1e529a6e..4e97538f72 100644 --- a/libs/filamat/CMakeLists.txt +++ b/libs/filamat/CMakeLists.txt @@ -50,7 +50,8 @@ set(PRIVATE_HDRS ${COMMON_PRIVATE_HDRS} src/eiff/BlobDictionary.h src/eiff/DictionarySpirvChunk.h - src/eiff/MaterialSpirvChunk.h + src/eiff/DictionaryMetalLibraryChunk.h + src/eiff/MaterialBinaryChunk.h src/GLSLPostProcessor.h src/MetalArgumentBuffer.h src/ShaderMinifier.h @@ -63,7 +64,8 @@ set(SRCS ${COMMON_SRCS} src/eiff/BlobDictionary.cpp src/eiff/DictionarySpirvChunk.cpp - src/eiff/MaterialSpirvChunk.cpp + src/eiff/DictionaryMetalLibraryChunk.cpp + src/eiff/MaterialBinaryChunk.cpp src/MetalArgumentBuffer.cpp src/sca/ASTHelpers.cpp src/sca/GLSLTools.cpp diff --git a/libs/filamat/src/MaterialBuilder.cpp b/libs/filamat/src/MaterialBuilder.cpp index 109d6e43da..d6c56b882f 100644 --- a/libs/filamat/src/MaterialBuilder.cpp +++ b/libs/filamat/src/MaterialBuilder.cpp @@ -33,11 +33,12 @@ #include "eiff/LineDictionary.h" #include "eiff/MaterialInterfaceBlockChunk.h" #include "eiff/MaterialTextChunk.h" -#include "eiff/MaterialSpirvChunk.h" +#include "eiff/MaterialBinaryChunk.h" #include "eiff/ChunkContainer.h" #include "eiff/SimpleFieldChunk.h" #include "eiff/DictionaryTextChunk.h" #include "eiff/DictionarySpirvChunk.h" +#include "eiff/DictionaryMetalLibraryChunk.h" #include #include @@ -821,7 +822,7 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector glslEntries; std::vector essl1Entries; - std::vector spirvEntries; + std::vector spirvEntries; std::vector metalEntries; LineDictionary textDictionary; BlobDictionary spirvDictionary; @@ -872,7 +873,7 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector spirv = std::move(s.spirv); + std::vector spirv = std::move(s.data); s.dictionaryIndex = spirvDictionary.addBlob(spirv); } for (const auto& s : metalEntries) { @@ -1041,11 +1042,11 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector(std::move(spirvDictionary), stripInfo); - container.push(std::move(spirvEntries)); + container.push(std::move(spirvEntries), ChunkType::MaterialSpirv); } // Emit Metal chunk (MaterialTextChunk). diff --git a/libs/filamat/src/eiff/DictionaryMetalLibraryChunk.cpp b/libs/filamat/src/eiff/DictionaryMetalLibraryChunk.cpp new file mode 100644 index 0000000000..fb701aab98 --- /dev/null +++ b/libs/filamat/src/eiff/DictionaryMetalLibraryChunk.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * 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. + */ + +#include "DictionaryMetalLibraryChunk.h" + +namespace filamat { + +DictionaryMetalLibraryChunk::DictionaryMetalLibraryChunk(BlobDictionary&& dictionary) + : Chunk(ChunkType::DictionaryMetalLibrary), mDictionary(std::move(dictionary)) {} + +void DictionaryMetalLibraryChunk::flatten(Flattener& f) { + f.writeUint32(mDictionary.getBlobCount()); + for (size_t i = 0 ; i < mDictionary.getBlobCount() ; i++) { + std::string_view blob = mDictionary.getBlob(i); + f.writeAlignmentPadding(); + f.writeBlob((const char*) blob.data(), blob.size()); + } +} + +} // namespace filamat diff --git a/libs/filamat/src/eiff/DictionaryMetalLibraryChunk.h b/libs/filamat/src/eiff/DictionaryMetalLibraryChunk.h new file mode 100644 index 0000000000..a129974961 --- /dev/null +++ b/libs/filamat/src/eiff/DictionaryMetalLibraryChunk.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * 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. + */ + +#ifndef TNT_FILAMAT_DIC_METAL_LIBRARY_CHUNK_H +#define TNT_FILAMAT_DIC_METAL_LIBRARY_CHUNK_H + +#include +#include + +#include "Chunk.h" +#include "Flattener.h" +#include "BlobDictionary.h" + +namespace filamat { + +class DictionaryMetalLibraryChunk final : public Chunk { +public: + explicit DictionaryMetalLibraryChunk(BlobDictionary&& dictionary); + ~DictionaryMetalLibraryChunk() = default; + +private: + void flatten(Flattener& f) override; + + BlobDictionary mDictionary; + bool mStripDebugInfo; +}; + +} // namespace filamat + +#endif // TNT_FILAMAT_DIC_METAL_LIBRARY_CHUNK_H diff --git a/libs/filamat/src/eiff/MaterialSpirvChunk.cpp b/libs/filamat/src/eiff/MaterialBinaryChunk.cpp similarity index 75% rename from libs/filamat/src/eiff/MaterialSpirvChunk.cpp rename to libs/filamat/src/eiff/MaterialBinaryChunk.cpp index 8003ff2398..bc097e0423 100644 --- a/libs/filamat/src/eiff/MaterialSpirvChunk.cpp +++ b/libs/filamat/src/eiff/MaterialBinaryChunk.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 2018 The Android Open Source Project - *DictionaryGlsl + * * 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 @@ -14,16 +14,17 @@ * limitations under the License. */ -#include "MaterialSpirvChunk.h" +#include "MaterialBinaryChunk.h" namespace filamat { -MaterialSpirvChunk::MaterialSpirvChunk(const std::vector&& entries) : - Chunk(ChunkType::MaterialSpirv), mEntries(entries) {} +MaterialBinaryChunk::MaterialBinaryChunk( + const std::vector&& entries, ChunkType chunkType) + : Chunk(chunkType), mEntries(entries) {} -void MaterialSpirvChunk::flatten(Flattener &f) { +void MaterialBinaryChunk::flatten(Flattener &f) { f.writeUint64(mEntries.size()); - for (const SpirvEntry& entry : mEntries) { + for (const BinaryEntry& entry : mEntries) { f.writeUint8(uint8_t(entry.shaderModel)); f.writeUint8(entry.variant.key); f.writeUint8(uint8_t(entry.stage)); diff --git a/libs/filamat/src/eiff/MaterialSpirvChunk.h b/libs/filamat/src/eiff/MaterialBinaryChunk.h similarity index 68% rename from libs/filamat/src/eiff/MaterialSpirvChunk.h rename to libs/filamat/src/eiff/MaterialBinaryChunk.h index 42aefdc245..8c12194724 100644 --- a/libs/filamat/src/eiff/MaterialSpirvChunk.h +++ b/libs/filamat/src/eiff/MaterialBinaryChunk.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef TNT_FILAMAT_MATERIAL_SPIRV_CHUNK_H -#define TNT_FILAMAT_MATERIAL_SPIRV_CHUNK_H +#ifndef TNT_FILAMAT_MATERIAL_BINARY_CHUNK_H +#define TNT_FILAMAT_MATERIAL_BINARY_CHUNK_H #include "Chunk.h" #include "ShaderEntry.h" @@ -24,17 +24,17 @@ namespace filamat { -class MaterialSpirvChunk final : public Chunk { +class MaterialBinaryChunk final : public Chunk { public: - explicit MaterialSpirvChunk(const std::vector&& entries); - ~MaterialSpirvChunk() = default; + explicit MaterialBinaryChunk(const std::vector&& entries, ChunkType type); + ~MaterialBinaryChunk() = default; private: void flatten(Flattener& f) override; - const std::vector mEntries; + const std::vector mEntries; }; } // namespace filamat -#endif // TNT_FILAMAT_MATERIAL_SPIRV_CHUNK_H +#endif // TNT_FILAMAT_MATERIAL_BINARY_CHUNK_H diff --git a/libs/filamat/src/eiff/ShaderEntry.h b/libs/filamat/src/eiff/ShaderEntry.h index f29b7f14a1..6a015e458f 100644 --- a/libs/filamat/src/eiff/ShaderEntry.h +++ b/libs/filamat/src/eiff/ShaderEntry.h @@ -34,14 +34,14 @@ struct TextEntry { std::string shader; }; -struct SpirvEntry { +struct BinaryEntry { filament::backend::ShaderModel shaderModel; filament::Variant variant; filament::backend::ShaderStage stage; - size_t dictionaryIndex; + size_t dictionaryIndex; // maps to an index in the blob dictionary - // temporarily holds this entry's spirv until added to the dictionary - std::vector spirv; + // temporarily holds this entry's binary data until added to the dictionary + std::vector data; }; } // namespace filamat diff --git a/libs/matdbg/src/ShaderExtractor.cpp b/libs/matdbg/src/ShaderExtractor.cpp index f9dbe0ea1e..22ba82c211 100644 --- a/libs/matdbg/src/ShaderExtractor.cpp +++ b/libs/matdbg/src/ShaderExtractor.cpp @@ -53,6 +53,10 @@ ShaderExtractor::ShaderExtractor(backend::ShaderLanguage target, const void* dat mMaterialTag = ChunkType::MaterialMetal; mDictionaryTag = ChunkType::DictionaryText; break; + case backend::ShaderLanguage::METAL_LIBRARY: + mMaterialTag = ChunkType::MaterialMetalLibrary; + mDictionaryTag = ChunkType::DictionaryMetalLibrary; + break; case backend::ShaderLanguage::SPIRV: mMaterialTag = ChunkType::MaterialSpirv; mDictionaryTag = ChunkType::DictionarySpirv; diff --git a/libs/matdbg/src/ShaderReplacer.cpp b/libs/matdbg/src/ShaderReplacer.cpp index 8eb3497eb4..215328d868 100644 --- a/libs/matdbg/src/ShaderReplacer.cpp +++ b/libs/matdbg/src/ShaderReplacer.cpp @@ -35,7 +35,7 @@ #include "eiff/ChunkContainer.h" #include "eiff/DictionarySpirvChunk.h" #include "eiff/DictionaryTextChunk.h" -#include "eiff/MaterialSpirvChunk.h" +#include "eiff/MaterialBinaryChunk.h" #include "eiff/MaterialTextChunk.h" #include "eiff/LineDictionary.h" @@ -73,7 +73,7 @@ private: vector mShaderRecords; }; -// Tiny database of data blobs that can import / export MaterialSpirvChunk and DictionarySpirvChunk. +// Tiny database of data blobs that can import / export MaterialBinaryChunk and DictionarySpirvChunk. // The blobs are stored *after* they have been compressed by SMOL-V. class BlobIndex { public: @@ -90,7 +90,7 @@ public: private: const ChunkType mDictTag; const ChunkType mMatTag; - vector mShaderRecords; + vector mShaderRecords; filaflat::BlobDictionary mDataBlobs; }; @@ -364,7 +364,7 @@ BlobIndex::BlobIndex(ChunkType dictTag, ChunkType matTag, const filaflat::ChunkC const auto& offsets = matChunk.getOffsets(); mShaderRecords.reserve(offsets.size()); for (auto [key, offset] : offsets) { - SpirvEntry info; + BinaryEntry info; filaflat::MaterialChunk::decodeKey(key, &info.shaderModel, &info.variant, &info.stage); info.dictionaryIndex = offset; mShaderRecords.emplace_back(info); @@ -391,7 +391,7 @@ void BlobIndex::writeChunks(ostream& stream) { // Apply SMOL-V compression and write out the results. filamat::ChunkContainer cc; - cc.push(std::move(mShaderRecords)); + cc.push(std::move(mShaderRecords), ChunkType::MaterialSpirv); cc.push(std::move(blobs), false); Flattener prepass = Flattener::getDryRunner(); diff --git a/libs/matdbg/src/TextWriter.cpp b/libs/matdbg/src/TextWriter.cpp index b74596f6a4..fca5bb0ca1 100644 --- a/libs/matdbg/src/TextWriter.cpp +++ b/libs/matdbg/src/TextWriter.cpp @@ -421,6 +421,9 @@ static bool printShaderInfo(ostream& text, const ChunkContainer& container, Chun case ChunkType::MaterialMetal: text << "Metal shaders:" << endl; break; + case ChunkType::MaterialMetalLibrary: + text << "Metal precompiled shader libraries:" << endl; + break; default: assert(false && "Invalid shader ChunkType"); break; @@ -455,6 +458,9 @@ bool TextWriter::writeMaterialInfo(const filaflat::ChunkContainer& container) { if (!printShaderInfo(text, container, ChunkType::MaterialMetal)) { return false; } + if (!printShaderInfo(text, container, ChunkType::MaterialMetalLibrary)) { + return false; + } printChunks(text, container); diff --git a/tools/matinfo/src/main.cpp b/tools/matinfo/src/main.cpp index d592002372..ea2d1a0141 100644 --- a/tools/matinfo/src/main.cpp +++ b/tools/matinfo/src/main.cpp @@ -96,8 +96,10 @@ static void printUsage(const char* name) { " Print the Vulkan dictionary\n\n" " --web-server=[port], -w\n" " Serve a web page at the given port (e.g. 8080)\n\n" - " --dump-binary=[index], -b\n" + " --dump-spirv-binary=[index], -b\n" " Dump binary SPIRV for the nth Vulkan shader to 'out.spv'\n\n" + " --dump-metal-library=[index]\n" + " Dump binary Metal library for the nth Metal precompiled library to 'out.metallib'\n\n" " --license\n" " Print copyright and license information\n\n" " --analyze-spirv=[index], -a\n" @@ -124,21 +126,24 @@ static void license() { static int handleArguments(int argc, char* argv[], Config* config) { static constexpr const char* OPTSTR = "hla:g:G:s:v:b:m:b:w:Xxyz"; + constexpr int DUMP_METAL_LIBRARY_OPTION = 1000; static const struct option OPTIONS[] = { - { "help", no_argument, nullptr, 'h' }, - { "license", no_argument, nullptr, 'l' }, - { "analyze-spirv", required_argument, nullptr, 'a' }, - { "print-glsl", required_argument, nullptr, 'g' }, - { "print-essl1", required_argument, nullptr, 'G' }, - { "print-spirv", required_argument, nullptr, 's' }, - { "print-vkglsl", required_argument, nullptr, 'v' }, - { "print-metal", required_argument, nullptr, 'm' }, - { "print-dic-glsl", no_argument, nullptr, 'x' }, - { "print-dic-essl1", no_argument, nullptr, 'X' }, - { "print-dic-metal", no_argument, nullptr, 'y' }, - { "print-dic-vk", no_argument, nullptr, 'z' }, - { "dump-binary", required_argument, nullptr, 'b' }, - { "web-server", required_argument, nullptr, 'w' }, + { "help", no_argument, nullptr, 'h' }, + { "license", no_argument, nullptr, 'l' }, + { "analyze-spirv", required_argument, nullptr, 'a' }, + { "print-glsl", required_argument, nullptr, 'g' }, + { "print-essl1", required_argument, nullptr, 'G' }, + { "print-spirv", required_argument, nullptr, 's' }, + { "print-vkglsl", required_argument, nullptr, 'v' }, + { "print-metal", required_argument, nullptr, 'm' }, + { "print-dic-glsl", no_argument, nullptr, 'x' }, + { "print-dic-essl1", no_argument, nullptr, 'X' }, + { "print-dic-metal", no_argument, nullptr, 'y' }, + { "print-dic-vk", no_argument, nullptr, 'z' }, + { "dump-binary", required_argument, nullptr, 'b' }, // backwards compatibility + { "dump-spirv-binary", required_argument, nullptr, 'b' }, + { "dump-metal-library", required_argument, nullptr, DUMP_METAL_LIBRARY_OPTION }, + { "web-server", required_argument, nullptr, 'w' }, { nullptr, 0, nullptr, 0 } // termination of the option list }; @@ -185,6 +190,7 @@ static int handleArguments(int argc, char* argv[], Config* config) { case 'm': config->printMetal = true; config->shaderIndex = static_cast(std::stoi(arg)); + config->binary = false; break; case 'w': config->serverPort = std::stoi(arg); @@ -201,6 +207,11 @@ static int handleArguments(int argc, char* argv[], Config* config) { case 'z': config->printDictionarySPIRV = true; break; + case DUMP_METAL_LIBRARY_OPTION: + config->printMetal = true; + config->shaderIndex = static_cast(std::stoi(arg)); + config->binary = true; + break; } } @@ -397,6 +408,11 @@ static void dumpSpirvBinary(const std::vector& spirv, const std::strin std::cout << "Binary SPIR-V dumped to " << filename << std::endl; } +static void dumpBinary(const uint8_t* data, size_t size, const std::string& filename) { + std::ofstream out(filename, std::ofstream::binary); + out.write(reinterpret_cast(data), size); +} + static bool parseChunks(Config config, void* data, size_t size) { using namespace filament::matdbg; ChunkContainer container(data, size); @@ -517,13 +533,20 @@ static bool parseChunks(Config config, void* data, size_t size) { } if (config.printMetal) { - ShaderExtractor parser(filament::backend::ShaderLanguage::MSL, data, size); + const filament::backend::ShaderLanguage language = config.binary + ? filament::backend::ShaderLanguage::METAL_LIBRARY + : filament::backend::ShaderLanguage::MSL; + ShaderExtractor parser(language, data, size); if (!parser.parse()) { return false; } - info.resize(getShaderCount(container, filamat::ChunkType::MaterialMetal)); - if (!getShaderInfo(container, info.data(), filamat::ChunkType::MaterialMetal)) { + const filamat::ChunkType chunkType = config.binary + ? filamat::ChunkType::MaterialMetalLibrary + : filamat::ChunkType::MaterialMetal; + size_t shaderCount = getShaderCount(container, chunkType); + info.resize(shaderCount); + if (!getShaderInfo(container, info.data(), chunkType)) { std::cerr << "Failed to parse Metal chunk." << std::endl; return false; } @@ -534,8 +557,15 @@ static bool parseChunks(Config config, void* data, size_t size) { } const auto& item = info[config.shaderIndex]; - parser.getShader(item.shaderModel, item.variant, item.pipelineStage, content); - std::cout << (const char*) content.data(); + if (!parser.getShader(item.shaderModel, item.variant, item.pipelineStage, content)) { + return false; + } + + if (config.binary) { + dumpBinary(content.data(), content.size(), "out.metallib"); + } else { + std::cout << (const char*) content.data(); + } return true; }