From 53f506670cfb8f0fbfc760fee4bea79c53ca5a93 Mon Sep 17 00:00:00 2001 From: yein Date: Mon, 22 Dec 2025 13:52:32 -0800 Subject: [PATCH] Add apiLevel and add parsing logic (#9535) --- filament/test/filament_test_material.cpp | 36 +++++++++++++++++++ .../include/filament/MaterialEnums.h | 9 +++++ .../filamat/include/filamat/MaterialBuilder.h | 6 ++++ libs/filamat/src/MaterialBuilder.cpp | 11 ++++++ .../filament-matp/src/ParametersProcessor.cpp | 6 ++++ libs/filament-matp/tests/test_matp.cpp | 16 ++++++++- 6 files changed, 83 insertions(+), 1 deletion(-) diff --git a/filament/test/filament_test_material.cpp b/filament/test/filament_test_material.cpp index ea43cf63b5..d74b3e9bc3 100644 --- a/filament/test/filament_test_material.cpp +++ b/filament/test/filament_test_material.cpp @@ -131,3 +131,39 @@ TEST(Material, MaterialWithoutSourceMaterialReturnsEmptySource) { engine->destroy(material); Engine::destroy(engine); } + +TEST(Material, MaterialSettingValidApiLevelReturnsAnValidPackage) { + Engine* engine = Engine::create(Engine::Backend::NOOP); + + filamat::MaterialBuilder builder; + builder.init(); + builder.setApiLevel(filament::RELEASED_MATERIAL_API_LEVEL); + filamat::Package result = builder.build(engine->getJobSystem()); + ASSERT_TRUE(result.isValid()); + + builder.init(); + builder.setApiLevel(filament::UNSTABLE_MATERIAL_API_LEVEL); + result = builder.build(engine->getJobSystem()); + ASSERT_TRUE(result.isValid()); + + Engine::destroy(engine); +} + +TEST(Material, MaterialSettingInvalidApiLevelReturnsAnInvalidPackage) { + Engine* engine = Engine::create(Engine::Backend::NOOP); + + filamat::MaterialBuilder builder; + builder.init(); + // Set the API level higher than unstable, which is illegal. + builder.setApiLevel(filament::UNSTABLE_MATERIAL_API_LEVEL + 1); + filamat::Package result = builder.build(engine->getJobSystem()); + ASSERT_FALSE(result.isValid()); + + builder.init(); + // Set the API level lower than the minimum. + builder.setApiLevel(0); + result = builder.build(engine->getJobSystem()); + ASSERT_FALSE(result.isValid()); + + Engine::destroy(engine); +} diff --git a/libs/filabridge/include/filament/MaterialEnums.h b/libs/filabridge/include/filament/MaterialEnums.h index f13673e0a9..fb2bea7986 100644 --- a/libs/filabridge/include/filament/MaterialEnums.h +++ b/libs/filabridge/include/filament/MaterialEnums.h @@ -30,6 +30,15 @@ namespace filament { // update this when a new version of filament wouldn't work with older materials static constexpr size_t MATERIAL_VERSION = 68; +// Those are the api levels that are used in the source material file (.mat) +// +// The level used for the apis that are already public and stable. Any released api supports +// backward compatibility (i.e. No breaking changes will be introduced for those apis.) +static constexpr uint32_t RELEASED_MATERIAL_API_LEVEL = 1; +// The level used for the apis that are currently under development or development has completed +// but may introduce breaking changes. +static constexpr uint32_t UNSTABLE_MATERIAL_API_LEVEL = 2; + /** * Supported shading models */ diff --git a/libs/filamat/include/filamat/MaterialBuilder.h b/libs/filamat/include/filamat/MaterialBuilder.h index 5ca507f0ea..e8b3ea7811 100644 --- a/libs/filamat/include/filamat/MaterialBuilder.h +++ b/libs/filamat/include/filamat/MaterialBuilder.h @@ -666,6 +666,9 @@ public: */ MaterialBuilder& materialSource(std::string_view source) noexcept; + //! Set the (client requested) api level that the material is supposed to be compiled against. + MaterialBuilder& setApiLevel(uint32_t apiLevel) noexcept; + /** * Build the material. If you are using the Filament engine with this library, you should use * the job system provided by Engine. @@ -990,6 +993,9 @@ private: bool mNoSamplerValidation = false; bool mUseDefaultDepthVariant = false; + + // Default api level is always 1. + uint32_t mApiLevel = 1; }; } // namespace filamat diff --git a/libs/filamat/src/MaterialBuilder.cpp b/libs/filamat/src/MaterialBuilder.cpp index 7a99175f8e..c970b6f46a 100644 --- a/libs/filamat/src/MaterialBuilder.cpp +++ b/libs/filamat/src/MaterialBuilder.cpp @@ -1257,6 +1257,11 @@ MaterialBuilder& MaterialBuilder::materialSource(std::string_view source) noexce return *this; } +MaterialBuilder& MaterialBuilder::setApiLevel(uint32_t apiLevel) noexcept { + mApiLevel = apiLevel; + return *this; +} + Package MaterialBuilder::build(JobSystem& jobSystem) { if (materialBuilderClients == 0) { slog.e << "Error: MaterialBuilder::init() must be called before build()." << io::endl; @@ -1293,6 +1298,12 @@ error: goto error; } + if (mApiLevel < 1 || mApiLevel > filament::UNSTABLE_MATERIAL_API_LEVEL) { + slog.e << "Error: api level can't be set below 1 or above unstable material level(" << + filament::UNSTABLE_MATERIAL_API_LEVEL << ")" << io::endl; + goto error; + } + // prepareToBuild must be called first, to populate mCodeGenPermutations. MaterialInfo info{}; prepareToBuild(info); diff --git a/libs/filament-matp/src/ParametersProcessor.cpp b/libs/filament-matp/src/ParametersProcessor.cpp index 0fbaa49a64..9025f55b82 100644 --- a/libs/filament-matp/src/ParametersProcessor.cpp +++ b/libs/filament-matp/src/ParametersProcessor.cpp @@ -79,6 +79,11 @@ static utils::Status processName(MaterialBuilder& builder, const JsonishValue& v return utils::Status::ok(); } +static utils::Status processApiLevel(MaterialBuilder& builder, const JsonishValue& value) { + builder.setApiLevel(value.toJsonNumber()->getFloat()); + return utils::Status::ok(); +} + static utils::Status processInterpolation(MaterialBuilder& builder, const JsonishValue& value) { static const std::unordered_map strToEnum { { "smooth", MaterialBuilder::Interpolation::SMOOTH }, @@ -1406,6 +1411,7 @@ ParametersProcessor::ParametersProcessor() { mParameters["useDefaultDepthVariant"] = { &processUseDefaultDepthVariant, Type::BOOL }; mParameters["linearFog"] = { &processLinearFog, Type::BOOL }; mParameters["shadowFarAttenuation"] = { &processShadowFarAttenuation, Type::BOOL }; + mParameters["apiLevel"] = { &processApiLevel, Type::NUMBER }; } utils::Status ParametersProcessor::process(MaterialBuilder& builder, const JsonishObject& jsonObject) { diff --git a/libs/filament-matp/tests/test_matp.cpp b/libs/filament-matp/tests/test_matp.cpp index d9262877cb..68512beaa1 100644 --- a/libs/filament-matp/tests/test_matp.cpp +++ b/libs/filament-matp/tests/test_matp.cpp @@ -112,7 +112,7 @@ TEST_F(MaterialLexer, MaterialParserWithToolSection) { matp::MaterialParser parser; TestMaterialParser testParser(parser); filamat::MaterialBuilder unused; - utils::Status result =testParser.parseMaterial( + utils::Status result = testParser.parseMaterial( materialSourceWithTool.c_str(), materialSourceWithTool.size(), unused); EXPECT_EQ(result.getCode(), utils::StatusCode::OK); } @@ -174,6 +174,20 @@ TEST_F(MaterialLexer, MaterialParserSyntaxError) { EXPECT_EQ(result.getCode(), utils::StatusCode::INVALID_ARGUMENT); } +TEST_F(MaterialLexer, MaterialParserCanParseApiLevel) { + static std::string sourceWithApiLevel(R"( + material { + apiLevel: 1, + } + )"); + matp::MaterialParser parser; + TestMaterialParser testParser(parser); + filamat::MaterialBuilder unused; + utils::Status result = + testParser.parseMaterial(sourceWithApiLevel.c_str(), sourceWithApiLevel.size(), unused); + EXPECT_EQ(result.getCode(), utils::StatusCode::OK); +} + static std::string jsonMaterialSource(R"( { "fragment": "void material(inout MaterialInputs material) {