From 02223dcaabb59178f7de907bad29d80f02dea770 Mon Sep 17 00:00:00 2001 From: Ajmal Kunnummal Date: Thu, 6 Mar 2025 10:42:24 -0800 Subject: [PATCH] Add a way to specify an additional uniform name along with a sampler param for the associated transform matrix (#8490) BUGS=[399959254] --- NEW_RELEASE_NOTES.md | 1 + docs/Materials.md.html | 7 ++-- filament/src/MaterialParser.cpp | 6 ++++ .../private/filament/BufferInterfaceBlock.h | 33 ++++++++++++++++--- libs/filabridge/src/BufferInterfaceBlock.cpp | 21 ++++++++++-- .../filamat/include/filamat/MaterialBuilder.h | 8 +++-- libs/filamat/src/MaterialBuilder.cpp | 14 +++++--- .../src/eiff/MaterialInterfaceBlockChunk.cpp | 1 + tools/matc/src/matc/ParametersProcessor.cpp | 20 ++++++++++- 9 files changed, 93 insertions(+), 18 deletions(-) diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index 4a1a9c7fa7..63090f0235 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -7,3 +7,4 @@ for next branch cut* header. appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). ## Release notes for next branch cut +- materials: add an optional `transformName` field to sampler material parameters. [⚠️ **New Material Version**] diff --git a/docs/Materials.md.html b/docs/Materials.md.html index 753e3a6b2f..fae697661c 100644 --- a/docs/Materials.md.html +++ b/docs/Materials.md.html @@ -991,10 +991,13 @@ Type Value : Each entry is an object with the properties `name` and `type`, both of `string` type. The - name must be a valid GLSL identifier. Entries also have an optional `precision`, which can be + name must be a valid GLSL identifier. Entries have an optional `precision`, which can be one of `default` (best precision for the platform, typically `high` on desktop, `medium` on mobile), `low`, `medium`, `high`. The type must be one of the types described in - table [materialParamsTypes]. + table [materialParamsTypes]. For Android external textures, entries also have an optional + transformName parameter to specify the name of the material parameter that will be + used to expose the transform matrix associated with the external sampler. In iOS and Vulkan, + this will always be identity. Type | Description :----------------------|:--------------------------------- diff --git a/filament/src/MaterialParser.cpp b/filament/src/MaterialParser.cpp index 5c5873726c..400e8e18d0 100644 --- a/filament/src/MaterialParser.cpp +++ b/filament/src/MaterialParser.cpp @@ -421,6 +421,7 @@ bool ChunkUniformInterfaceBlock::unflatten(Unflattener& unflattener, uint64_t fieldSize = 0; uint8_t fieldType = 0; uint8_t fieldPrecision = 0; + uint8_t fieldAssociatedSampler = 0; if (!unflattener.read(&fieldName)) { return false; @@ -438,8 +439,13 @@ bool ChunkUniformInterfaceBlock::unflatten(Unflattener& unflattener, return false; } + if (!unflattener.read(&fieldAssociatedSampler)) { + return false; + } + // a size of 1 means not an array builder.add({{{ fieldName.data(), fieldName.size() }, + fieldAssociatedSampler, uint32_t(fieldSize == 1 ? 0 : fieldSize), BufferInterfaceBlock::Type(fieldType), BufferInterfaceBlock::Precision(fieldPrecision) }}); diff --git a/libs/filabridge/include/private/filament/BufferInterfaceBlock.h b/libs/filabridge/include/private/filament/BufferInterfaceBlock.h index c47193208f..093326fd7a 100644 --- a/libs/filabridge/include/private/filament/BufferInterfaceBlock.h +++ b/libs/filabridge/include/private/filament/BufferInterfaceBlock.h @@ -40,11 +40,28 @@ public: std::string_view name; uint32_t size; backend::UniformType type; - backend::Precision precision{}; - backend::FeatureLevel minFeatureLevel = backend::FeatureLevel::FEATURE_LEVEL_1; - std::string_view structName{}; - uint32_t stride{}; - std::string_view sizeName{}; + backend::Precision precision; + uint8_t associatedSampler = 0; + backend::FeatureLevel minFeatureLevel; + std::string_view structName; + uint32_t stride; + std::string_view sizeName; + + InterfaceBlockEntry() = default; + InterfaceBlockEntry(std::string_view name, uint32_t size, backend::UniformType type, + backend::Precision precision = {}, + backend::FeatureLevel minFeatureLevel = backend::FeatureLevel::FEATURE_LEVEL_1, std::string_view structName = {}, + uint32_t stride = {}, std::string_view sizeName = {}) noexcept + : name(name), size(size), type(type), precision(precision), + associatedSampler(0), minFeatureLevel(minFeatureLevel), + structName(structName), stride(stride), sizeName(sizeName) {} + InterfaceBlockEntry(std::string_view name, uint8_t associatedSampler, uint32_t size, backend::UniformType type, + backend::Precision precision = {}, + backend::FeatureLevel minFeatureLevel = backend::FeatureLevel::FEATURE_LEVEL_1, std::string_view structName = {}, + uint32_t stride = {}, std::string_view sizeName = {}) noexcept + : name(name), size(size), type(type), precision(precision), + associatedSampler(associatedSampler), minFeatureLevel(minFeatureLevel), + structName(structName), stride(stride), sizeName(sizeName) {} }; BufferInterfaceBlock(); @@ -68,6 +85,7 @@ public: bool isArray; // true if the field is an array uint32_t size; // size of the array in elements, or 0 if not an array Precision precision; // precision of this field + uint8_t associatedSampler; // sampler associated with this field backend::FeatureLevel minFeatureLevel; // below this feature level, this field is not needed utils::CString structName; // name of this field structure if type is STRUCT utils::CString sizeName; // name of the size parameter in the shader @@ -152,6 +170,10 @@ public: // negative value if name doesn't exist or Panic if exceptions are enabled ssize_t getFieldOffset(std::string_view name, size_t index) const; + // returns offset in bytes of the transform matrix for the given external texture binding + // returns -1 if the field doesn't exist + ssize_t getTransformFieldOffset(uint8_t binding) const; + FieldInfo const* getFieldInfo(std::string_view name) const; bool hasField(std::string_view name) const noexcept { @@ -179,6 +201,7 @@ private: utils::CString mName; utils::FixedCapacityVector mFieldInfoList; std::unordered_map mInfoMap; + std::unordered_map mTransformOffsetMap; uint32_t mSize = 0; // size in bytes rounded to multiple of 4 Alignment mAlignment = Alignment::std140; Target mTarget = Target::UNIFORM; diff --git a/libs/filabridge/src/BufferInterfaceBlock.cpp b/libs/filabridge/src/BufferInterfaceBlock.cpp index 7b907baceb..046ea49557 100644 --- a/libs/filabridge/src/BufferInterfaceBlock.cpp +++ b/libs/filabridge/src/BufferInterfaceBlock.cpp @@ -58,7 +58,7 @@ BufferInterfaceBlock::Builder& BufferInterfaceBlock::Builder::add( for (auto const& item : list) { mEntries.push_back({ { item.name.data(), item.name.size() }, - 0, uint8_t(item.stride), item.type, item.size > 0, item.size, item.precision, item.minFeatureLevel, + 0, uint8_t(item.stride), item.type, item.size > 0, item.size, item.precision, item.associatedSampler, item.minFeatureLevel, { item.structName.data(), item.structName.size() }, { item.sizeName.data(), item.sizeName.size() } }); @@ -71,7 +71,7 @@ BufferInterfaceBlock::Builder& BufferInterfaceBlock::Builder::addVariableSizedAr mHasVariableSizeArray = true; mEntries.push_back({ { item.name.data(), item.name.size() }, - 0, uint8_t(item.stride), item.type, true, 0, item.precision, item.minFeatureLevel, + 0, uint8_t(item.stride), item.type, true, 0, item.precision, item.associatedSampler, item.minFeatureLevel, { item.structName.data(), item.structName.size() }, { item.sizeName.data(), item.sizeName.size() } }); @@ -122,6 +122,8 @@ BufferInterfaceBlock::BufferInterfaceBlock(Builder const& builder) noexcept auto& uniformsInfoList = mFieldInfoList; + auto& transformOffsetMap = mTransformOffsetMap; + uint32_t i = 0; uint16_t offset = 0; for (auto const& e : builder.mEntries) { @@ -143,11 +145,16 @@ BufferInterfaceBlock::BufferInterfaceBlock(Builder const& builder) noexcept FieldInfo& info = uniformsInfoList[i]; info = { e.name, offset, uint8_t(stride), e.type, e.isArray, e.size, - e.precision, e.minFeatureLevel, e.structName, e.sizeName }; + e.precision, e.associatedSampler, e.minFeatureLevel, e.structName, e.sizeName }; // record this uniform info infoMap[{ info.name.data(), info.name.size() }] = i; + // record the transform offset if this is an external sampler + if (e.associatedSampler != 0) { + transformOffsetMap[e.associatedSampler] = offset; + } + // advance offset to next slot offset += stride * std::max(1u, e.size); ++i; @@ -163,6 +170,14 @@ ssize_t BufferInterfaceBlock::getFieldOffset(std::string_view name, size_t index return (ssize_t)info->getBufferOffset(index); } +ssize_t BufferInterfaceBlock::getTransformFieldOffset(uint8_t binding) const { + auto pos = mTransformOffsetMap.find(binding); + if (pos == mTransformOffsetMap.end()) { + return -1; + } + return (ssize_t)pos->second; +} + BufferInterfaceBlock::FieldInfo const* BufferInterfaceBlock::getFieldInfo( std::string_view name) const { auto pos = mInfoMap.find(name); diff --git a/libs/filamat/include/filamat/MaterialBuilder.h b/libs/filamat/include/filamat/MaterialBuilder.h index 774c96f07d..c9ff548880 100644 --- a/libs/filamat/include/filamat/MaterialBuilder.h +++ b/libs/filamat/include/filamat/MaterialBuilder.h @@ -318,7 +318,8 @@ public: MaterialBuilder& parameter(const char* name, SamplerType samplerType, SamplerFormat format = SamplerFormat::FLOAT, ParameterPrecision precision = ParameterPrecision::DEFAULT, - bool multisample = false) noexcept; + bool multisample = false, + const char* transformName = "") noexcept; MaterialBuilder& buffer(filament::BufferInterfaceBlock bib) noexcept; @@ -650,8 +651,8 @@ public: Parameter() noexcept: parameterType(INVALID) {} // Sampler - Parameter(const char* paramName, SamplerType t, SamplerFormat f, ParameterPrecision p, bool ms) - : name(paramName), size(1), precision(p), samplerType(t), format(f), parameterType(SAMPLER), multisample(ms) { } + Parameter(const char* paramName, SamplerType t, SamplerFormat f, ParameterPrecision p, bool ms, const char* tn) + : name(paramName), size(1), precision(p), samplerType(t), format(f), parameterType(SAMPLER), multisample(ms), transformName(tn) { } // Uniform Parameter(const char* paramName, UniformType t, size_t typeSize, ParameterPrecision p) @@ -669,6 +670,7 @@ public: SubpassType subpassType; SamplerFormat format; bool multisample; + utils::CString transformName; enum { INVALID, UNIFORM, diff --git a/libs/filamat/src/MaterialBuilder.cpp b/libs/filamat/src/MaterialBuilder.cpp index df745a33b8..15fba4b08b 100644 --- a/libs/filamat/src/MaterialBuilder.cpp +++ b/libs/filamat/src/MaterialBuilder.cpp @@ -279,7 +279,7 @@ MaterialBuilder& MaterialBuilder::parameter(const char* name, UniformType type, MaterialBuilder& MaterialBuilder::parameter(const char* name, SamplerType samplerType, - SamplerFormat format, ParameterPrecision precision, bool multisample) noexcept { + SamplerFormat format, ParameterPrecision precision, bool multisample, const char* transformName) noexcept { FILAMENT_CHECK_PRECONDITION(!multisample || (format != SamplerFormat::SHADOW && (samplerType == SamplerType::SAMPLER_2D || @@ -288,7 +288,7 @@ MaterialBuilder& MaterialBuilder::parameter(const char* name, SamplerType sample " as long as type is not SHADOW"; FILAMENT_CHECK_POSTCONDITION(mParameterCount < MAX_PARAMETERS_COUNT) << "Too many parameters"; - mParameters[mParameterCount++] = { name, samplerType, format, precision, multisample }; + mParameters[mParameterCount++] = { name, samplerType, format, precision, multisample, transformName }; return *this; } @@ -609,12 +609,18 @@ void MaterialBuilder::prepareToBuild(MaterialInfo& info) noexcept { SamplerInterfaceBlock::Builder sbb; BufferInterfaceBlock::Builder ibb; // sampler bindings start at 1, 0 is the ubo - for (size_t i = 0, binding = 1, c = mParameterCount; i < c; i++) { + uint16_t binding = 1; + for (size_t i = 0, c = mParameterCount; i < c; i++) { auto const& param = mParameters[i]; assert_invariant(!param.isSubpass()); if (param.isSampler()) { sbb.add({ param.name.data(), param.name.size() }, - binding++, param.samplerType, param.format, param.precision, param.multisample); + binding, param.samplerType, param.format, param.precision, param.multisample); + if (!param.transformName.empty()) { + ibb.add({{{ param.transformName.data(), param.transformName.size() }, uint8_t(binding), + 0, UniformType::MAT3, Precision::DEFAULT, FeatureLevel::FEATURE_LEVEL_0 }}); + } + binding++; } else if (param.isUniform()) { ibb.add({{{ param.name.data(), param.name.size() }, uint32_t(param.size == 1u ? 0u : param.size), param.uniformType, diff --git a/libs/filamat/src/eiff/MaterialInterfaceBlockChunk.cpp b/libs/filamat/src/eiff/MaterialInterfaceBlockChunk.cpp index 2e86dca9d0..838e76d810 100644 --- a/libs/filamat/src/eiff/MaterialInterfaceBlockChunk.cpp +++ b/libs/filamat/src/eiff/MaterialInterfaceBlockChunk.cpp @@ -52,6 +52,7 @@ void MaterialUniformInterfaceBlockChunk::flatten(Flattener& f) { f.writeUint64(uInfo.size); f.writeUint8(static_cast(uInfo.type)); f.writeUint8(static_cast(uInfo.precision)); + f.writeUint8(static_cast(uInfo.associatedSampler)); } } diff --git a/tools/matc/src/matc/ParametersProcessor.cpp b/tools/matc/src/matc/ParametersProcessor.cpp index 34bbc2d72a..c9b0cfbb10 100644 --- a/tools/matc/src/matc/ParametersProcessor.cpp +++ b/tools/matc/src/matc/ParametersProcessor.cpp @@ -153,6 +153,12 @@ static bool processParameter(MaterialBuilder& builder, const JsonishObject& json std::cerr << "parameters: name value must be STRING." << std::endl; return false; } + + const JsonishValue* transformNameValue = jsonObject.getValue("transformName"); + if (transformNameValue && transformNameValue->getType() != JsonishValue::STRING) { + std::cerr << "parameters: transformName value must be STRING." << std::endl; + return false; + } const JsonishValue* precisionValue = jsonObject.getValue("precision"); if (precisionValue) { @@ -221,7 +227,19 @@ static bool processParameter(MaterialBuilder& builder, const JsonishObject& json auto multisample = multiSampleValue ? multiSampleValue->toJsonBool()->getBool() : false; - builder.parameter(nameString.c_str(), type, format, precision, multisample); + if (transformNameValue) { + if (type != MaterialBuilder::SamplerType::SAMPLER_EXTERNAL) { + std::cerr << "parameters: the parameter with name '" << nameString << "'" + << " is a sampler of type " << typeString << " and has a transformName." + << " Transform names are only supported for external samplers." + << std::endl; + return false; + } + auto transformName = transformNameValue->toJsonString()->getString(); + builder.parameter(nameString.c_str(), type, format, precision, multisample, transformName.c_str()); + } else { + builder.parameter(nameString.c_str(), type, format, precision, multisample); + } } else { std::cerr << "parameters: the type '" << typeString