Skip to content

Commit

Permalink
Support backends with multiple shader languages and precompiled Metal…
Browse files Browse the repository at this point in the history
… libraries (#7769)
  • Loading branch information
bejado committed Apr 22, 2024
1 parent d9cba80 commit 35fa79e
Show file tree
Hide file tree
Showing 11 changed files with 158 additions and 78 deletions.
6 changes: 6 additions & 0 deletions filament/backend/include/backend/Program.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ class Program {
// null terminating character.
Program& shader(ShaderStage shader, void const* data, size_t size);

// sets the language of the shader sources provided with shader() (defaults to ESSL3)
Program& shaderLanguage(ShaderLanguage shaderLanguage);

// Note: This is only needed for GLES3.0 backends, because the layout(binding=) syntax is
// not permitted in glsl. The backend needs a way to associate a uniform block
// to a binding point.
Expand Down Expand Up @@ -136,6 +139,8 @@ class Program {
utils::CString const& getName() const noexcept { return mName; }
utils::CString& getName() noexcept { return mName; }

auto const& getShaderLanguage() const { return mShaderLanguage; }

utils::FixedCapacityVector<SpecializationConstant> const& getSpecializationConstants() const noexcept {
return mSpecializationConstants;
}
Expand All @@ -155,6 +160,7 @@ class Program {
UniformBlockInfo mUniformBlocks = {};
SamplerGroupInfo mSamplerGroups = {};
ShaderSource mShadersSource;
ShaderLanguage mShaderLanguage = ShaderLanguage::ESSL3;
utils::CString mName;
uint64_t mCacheId{};
utils::Invocable<utils::io::ostream&(utils::io::ostream& out)> mLogger;
Expand Down
7 changes: 6 additions & 1 deletion filament/backend/src/Program.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace filament::backend {
using namespace utils;

// We want these in the .cpp file, so they're not inlined (not worth it)
Program::Program() noexcept { // NOLINT(modernize-use-equals-default)
Program::Program() noexcept { // NOLINT(modernize-use-equals-default)
}

Program::Program(Program&& rhs) noexcept = default;
Expand All @@ -47,6 +47,11 @@ Program& Program::shader(ShaderStage shader, void const* data, size_t size) {
return *this;
}

Program& Program::shaderLanguage(ShaderLanguage shaderLanguage) {
mShaderLanguage = shaderLanguage;
return *this;
}

Program& Program::uniformBlockBindings(
FixedCapacityVector<std::pair<utils::CString, uint8_t>> const& uniformBlockBindings) noexcept {
for (auto const& item : uniformBlockBindings) {
Expand Down
53 changes: 34 additions & 19 deletions filament/backend/src/metal/MetalShaderCompiler.mm
Original file line number Diff line number Diff line change
Expand Up @@ -106,26 +106,41 @@ bool isReady() const noexcept {
continue;
}

assert_invariant(source[source.size() - 1] == '\0');

// the shader string is null terminated and the length includes the null character
NSString* objcSource = [[NSString alloc] initWithBytes:source.data()
length:source.size() - 1
encoding:NSUTF8StringEncoding];

// By default, Metal uses the most recent language version.
MTLCompileOptions* options = [MTLCompileOptions new];

// Disable Fast Math optimizations.
// This ensures that operations adhere to IEEE standards for floating-point arithmetic,
// which is crucial for half precision floats in scenarios where fast math optimizations
// lead to inaccuracies, such as in handling special values like NaN or Infinity.
options.fastMathEnabled = NO;

NSError* error = nil;
id<MTLLibrary> library = [device newLibraryWithSource:objcSource
options:options
error:&error];
id<MTLLibrary> library = nil;
switch (program.getShaderLanguage()) {
case ShaderLanguage::MSL: {
// By default, Metal uses the most recent language version.
MTLCompileOptions* options = [MTLCompileOptions new];

// Disable Fast Math optimizations.
// This ensures that operations adhere to IEEE standards for floating-point
// arithmetic, which is crucial for half precision floats in scenarios where fast
// math optimizations lead to inaccuracies, such as in handling special values like
// NaN or Infinity.
options.fastMathEnabled = NO;

assert_invariant(source[source.size() - 1] == '\0');
// the shader string is null terminated and the length includes the null character
NSString* objcSource = [[NSString alloc] initWithBytes:source.data()
length:source.size() - 1
encoding:NSUTF8StringEncoding];
library = [device newLibraryWithSource:objcSource options:options error:&error];
break;
}
case ShaderLanguage::METAL_LIBRARY: {
dispatch_data_t data = dispatch_data_create(source.data(), source.size(),
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
DISPATCH_DATA_DESTRUCTOR_DEFAULT);
library = [device newLibraryWithData:data error:&error];
break;
}
case ShaderLanguage::ESSL1:
case ShaderLanguage::ESSL3:
case ShaderLanguage::SPIRV:
break;
}

if (library == nil) {
NSString* errorMessage = @"unknown error";
if (error) {
Expand Down
15 changes: 15 additions & 0 deletions filament/backend/test/ShaderGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,20 @@ ShaderGenerator::ShaderGenerator(std::string vertex, std::string fragment,
mVertexBlob(transpileShader(ShaderStage::VERTEX, std::move(vertex), backend, isMobile, sib)),
mFragmentBlob(transpileShader(ShaderStage::FRAGMENT, std::move(fragment), backend,
isMobile, sib)) {
switch (backend) {
case Backend::OPENGL:
mShaderLanguage = filament::backend::ShaderLanguage::ESSL3;
break;
case Backend::VULKAN:
mShaderLanguage = filament::backend::ShaderLanguage::SPIRV;
break;
case Backend::METAL:
mShaderLanguage = filament::backend::ShaderLanguage::MSL;
break;
case Backend::NOOP:
mShaderLanguage = filament::backend::ShaderLanguage::ESSL3;
break;
}
}

ShaderGenerator::Blob ShaderGenerator::transpileShader(
Expand Down Expand Up @@ -160,6 +174,7 @@ ShaderGenerator::Blob ShaderGenerator::transpileShader(

Program ShaderGenerator::getProgram(filament::backend::DriverApi&) noexcept {
Program program;
program.shaderLanguage(mShaderLanguage);
program.shader(ShaderStage::VERTEX, mVertexBlob.data(), mVertexBlob.size());
program.shader(ShaderStage::FRAGMENT, mFragmentBlob.data(), mFragmentBlob.size());
return program;
Expand Down
1 change: 1 addition & 0 deletions filament/backend/test/ShaderGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class ShaderGenerator {
Blob mFragmentBlob;
std::string mCompiledVertexShader;
std::string mCompiledFragmentShader;
filament::backend::ShaderLanguage mShaderLanguage;

};

Expand Down
2 changes: 2 additions & 0 deletions filament/backend/test/test_ComputeBasic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ kernel void main0() {}
}

Program program;
program.shaderLanguage(ShaderLanguage::ESSL3);
program.shader(ShaderStage::COMPUTE, shader.data(), shader.size() + 1);

Handle<HwProgram> ph = driver.createProgram(std::move(program));
Expand Down Expand Up @@ -144,6 +145,7 @@ kernel void main0(device Output_data& output_data [[buffer(0)]],
driver.updateBufferObject(input_data, { data.data(), size }, 0);

Program program;
program.shaderLanguage(ShaderLanguage::ESSL3);
program.shader(ShaderStage::COMPUTE, shader.data(), shader.size() + 1);
Handle<HwProgram> ph = driver.createProgram(std::move(program));

Expand Down
70 changes: 43 additions & 27 deletions filament/src/MaterialParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include <utils/CString.h>

#include <stdlib.h>
#include <optional>

using namespace utils;
using namespace filament::backend;
Expand All @@ -42,36 +43,31 @@ using namespace filamat;

namespace filament {

// ------------------------------------------------------------------------------------------------

MaterialParser::MaterialParserDetails::MaterialParserDetails(ShaderLanguage language, const void* data, size_t size)
: mManagedBuffer(data, size),
mChunkContainer(mManagedBuffer.data(), mManagedBuffer.size()),
mMaterialChunk(mChunkContainer) {
constexpr std::pair<ChunkType, ChunkType> shaderLanguageToTags(ShaderLanguage language) {
switch (language) {
case ShaderLanguage::ESSL3:
mMaterialTag = ChunkType::MaterialGlsl;
mDictionaryTag = ChunkType::DictionaryText;
break;
return { ChunkType::MaterialGlsl, ChunkType::DictionaryText };
case ShaderLanguage::ESSL1:
mMaterialTag = ChunkType::MaterialEssl1;
mDictionaryTag = ChunkType::DictionaryText;
break;
return { ChunkType::MaterialEssl1, ChunkType::DictionaryText };
case ShaderLanguage::MSL:
mMaterialTag = ChunkType::MaterialMetal;
mDictionaryTag = ChunkType::DictionaryText;
break;
return { ChunkType::MaterialMetal, ChunkType::DictionaryText };
case ShaderLanguage::SPIRV:
mMaterialTag = ChunkType::MaterialSpirv;
mDictionaryTag = ChunkType::DictionarySpirv;
break;
return { ChunkType::MaterialSpirv, ChunkType::DictionarySpirv };
case ShaderLanguage::METAL_LIBRARY:
mMaterialTag = ChunkType::MaterialMetalLibrary;
mDictionaryTag = ChunkType::DictionaryMetalLibrary;
break;
return { ChunkType::MaterialMetalLibrary, ChunkType::DictionaryMetalLibrary };
}
}

// ------------------------------------------------------------------------------------------------

MaterialParser::MaterialParserDetails::MaterialParserDetails(
const utils::FixedCapacityVector<ShaderLanguage>& preferredLanguages, const void* data,
size_t size)
: mManagedBuffer(data, size),
mChunkContainer(mManagedBuffer.data(), mManagedBuffer.size()),
mPreferredLanguages(preferredLanguages),
mMaterialChunk(mChunkContainer) {}

template<typename T>
UTILS_NOINLINE
bool MaterialParser::MaterialParserDetails::getFromSimpleChunk(
Expand All @@ -87,9 +83,9 @@ bool MaterialParser::MaterialParserDetails::getFromSimpleChunk(

// ------------------------------------------------------------------------------------------------

MaterialParser::MaterialParser(ShaderLanguage language, const void* data, size_t size)
: mImpl(language, data, size) {
}
MaterialParser::MaterialParser(utils::FixedCapacityVector<ShaderLanguage> preferredLanguages,
const void* data, size_t size)
: mImpl(preferredLanguages, data, size) {}

ChunkContainer& MaterialParser::getChunkContainer() noexcept {
return mImpl.mChunkContainer;
Expand All @@ -104,20 +100,40 @@ MaterialParser::ParseResult MaterialParser::parse() noexcept {
if (UTILS_UNLIKELY(!cc.parse())) {
return ParseResult::ERROR_OTHER;
}
const ChunkType matTag = mImpl.mMaterialTag;
const ChunkType dictTag = mImpl.mDictionaryTag;
if (UTILS_UNLIKELY(!cc.hasChunk(matTag) || !cc.hasChunk(dictTag))) {

using MaybeShaderLanguageAndChunks =
std::optional<std::tuple<ShaderLanguage, ChunkType, ChunkType>>;
auto chooseLanguage = [this, &cc]() -> MaybeShaderLanguageAndChunks {
for (auto language : mImpl.mPreferredLanguages) {
const auto [matTag, dictTag] = shaderLanguageToTags(language);
if (cc.hasChunk(matTag) && cc.hasChunk(dictTag)) {
return std::make_tuple(language, matTag, dictTag);
}
}
return {};
};
const auto result = chooseLanguage();

if (!result.has_value()) {
return ParseResult::ERROR_MISSING_BACKEND;
}

const auto [chosenLanguage, matTag, dictTag] = result.value();
if (UTILS_UNLIKELY(!DictionaryReader::unflatten(cc, dictTag, mImpl.mBlobDictionary))) {
return ParseResult::ERROR_OTHER;
}
if (UTILS_UNLIKELY(!mImpl.mMaterialChunk.initialize(matTag))) {
return ParseResult::ERROR_OTHER;
}

mImpl.mChosenLanguage = chosenLanguage;
return ParseResult::SUCCESS;
}

backend::ShaderLanguage MaterialParser::getShaderLanguage() const noexcept {
return mImpl.mChosenLanguage;
}

// Accessors
bool MaterialParser::getMaterialVersion(uint32_t* value) const noexcept {
return mImpl.getFromSimpleChunk(ChunkType::MaterialVersion, value);
Expand Down
16 changes: 11 additions & 5 deletions filament/src/MaterialParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,12 @@
#include <backend/DriverEnums.h>
#include <backend/Program.h>

#include <utils/compiler.h>
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
#include <utils/compiler.h>

#include <inttypes.h>
#include <utility>

namespace filaflat {
class ChunkContainer;
Expand All @@ -48,7 +50,8 @@ struct MaterialConstant;

class MaterialParser {
public:
MaterialParser(backend::ShaderLanguage language, const void* data, size_t size);
MaterialParser(utils::FixedCapacityVector<backend::ShaderLanguage> preferredLanguages,
const void* data, size_t size);

MaterialParser(MaterialParser const& rhs) noexcept = delete;
MaterialParser& operator=(MaterialParser const& rhs) noexcept = delete;
Expand All @@ -60,6 +63,7 @@ class MaterialParser {
};

ParseResult parse() noexcept;
backend::ShaderLanguage getShaderLanguage() const noexcept;

// Accessors
bool getMaterialVersion(uint32_t* value) const noexcept;
Expand Down Expand Up @@ -130,7 +134,9 @@ class MaterialParser {

private:
struct MaterialParserDetails {
MaterialParserDetails(backend::ShaderLanguage language, const void* data, size_t size);
MaterialParserDetails(
const utils::FixedCapacityVector<backend::ShaderLanguage>& preferredLanguages,
const void* data, size_t size);

template<typename T>
bool getFromSimpleChunk(filamat::ChunkType type, T* value) const noexcept;
Expand All @@ -157,12 +163,12 @@ class MaterialParser {

ManagedBuffer mManagedBuffer;
filaflat::ChunkContainer mChunkContainer;
utils::FixedCapacityVector<backend::ShaderLanguage> mPreferredLanguages;
backend::ShaderLanguage mChosenLanguage;

// Keep MaterialChunk alive between calls to getShader to avoid reload the shader index.
filaflat::MaterialChunk mMaterialChunk;
filaflat::BlobDictionary mBlobDictionary;
filamat::ChunkType mMaterialTag = filamat::ChunkType::Unknown;
filamat::ChunkType mDictionaryTag = filamat::ChunkType::Unknown;
};

filaflat::ChunkContainer& getChunkContainer() noexcept;
Expand Down
19 changes: 11 additions & 8 deletions filament/src/details/Engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,11 @@
#include <filament/Texture.h>
#include <filament/VertexBuffer.h>

#include <utils/compiler.h>
#include <utils/Allocator.h>
#include <utils/JobSystem.h>
#include <utils/CountDownLatch.h>
#include <utils/FixedCapacityVector.h>
#include <utils/JobSystem.h>
#include <utils/compiler.h>

#include <chrono>
#include <memory>
Expand Down Expand Up @@ -231,19 +232,21 @@ class FEngine : public Engine {
return mPlatform;
}

backend::ShaderLanguage getShaderLanguage() const noexcept {
// Return a vector of shader languages, in order of preference.
utils::FixedCapacityVector<backend::ShaderLanguage> getShaderLanguage() const noexcept {
switch (mBackend) {
case Backend::DEFAULT:
case Backend::NOOP:
default:
return backend::ShaderLanguage::ESSL3;
return { backend::ShaderLanguage::ESSL3 };
case Backend::OPENGL:
return getDriver().getFeatureLevel() == FeatureLevel::FEATURE_LEVEL_0
? backend::ShaderLanguage::ESSL1 : backend::ShaderLanguage::ESSL3;
return { getDriver().getFeatureLevel() == FeatureLevel::FEATURE_LEVEL_0
? backend::ShaderLanguage::ESSL1
: backend::ShaderLanguage::ESSL3 };
case Backend::VULKAN:
return backend::ShaderLanguage::SPIRV;
return { backend::ShaderLanguage::SPIRV };
case Backend::METAL:
return backend::ShaderLanguage::MSL;
return { backend::ShaderLanguage::METAL_LIBRARY, backend::ShaderLanguage::MSL };
}
}

Expand Down
Loading

0 comments on commit 35fa79e

Please sign in to comment.